追記:2020年7月14日の記事に修正版eVLDMI関数を記載しています。
(1)VLDMIの計算式
FXなどで使われるVLDMIというテクニカル指標をコード化してみたくて計算式を探したのですが、ネット上にはVLDMIの計算式について情報が余りありませんでした。
どうにか見つけたのが以下のサイトです。
https://fx-tradesite.com/vldmi-870
このサイトによると計算式は以下の通りです。
VL期間 = 基準期間 ÷ (終値のN日標準偏差 ÷ 終値のN日標準偏差のM日単純平均) VLDMI = VL期間内の前日比プラスの総和 ÷ (VL期間内の前日比プラスの総和 + VL期間内の前日比マイナスの総和) 基準期間は14 N=10 M=10
(2)VLDMIのコード(MQL4版)
上述の計算式を元に書いたのが以下のコードです。
なお、計算式の最後の方で iRSI関数を使っている理由についてはコードの前半に書いた説明をご覧ください。
/*------------------------------------------------------------------------------- 【製作者】 philojuan 【関数名】 eVLDMI 最初の e は エセ の エ(あれ?) 【引数】 timeframe 時間軸(PERIOD_M5等) base_period 基準期間 N_period 標準偏差計算期間 (1以上の整数値でなければエラーとなる) M_period 標準偏差を単純平均する期間(1以上の整数値でなければエラーとなる) shift バーシフト 【参考URL】 https://fx-tradesite.com/vldmi-870 このコードは参考URLの計算式を元にしている。 【計算方法】 VL期間 = 基準期間 ÷ (終値のN日標準偏差 ÷ 終値のN日標準偏差のM日単純平均) VLDMIの値 = VL期間をRSIの期間として採用して得た値 【iRSI関数を用いている理由】 参考URLによるとVLDMIの計算式は以下のようになっている。 「VL期間内の前日比プラスの総和 ÷ (VL期間内の前日比プラスの総和 + VL期間内の前日比マイナスの総和)」 この計算式から「VL」の2文字を取ると・・・ 「期間内の前日比プラスの総和 ÷ (期間内の前日比プラスの総和 + 期間内の前日比マイナスの総和)」 これはRSIの計算式と全く同じである。 つまり、RSIを計算する日数としてVL期間を用いているだけである。 よって、VL期間を計算し、その値を整数値にしたものをiRSI関数に入れればVLDMIを計算 したことになる。 -------------------------------------------------------------------------------*/ double eVLDMI(int timeframe, int base_period, int N_period, int M_period, int shift) { // N_period や M_periodが 0 の場合、エラーメッセージを出して終了 // 戻り値は -1 となる if(!N_period || !M_period) { printf("eVLDMI function: Zero Division Error"); Alert("eVLDMI: Error, Zero Division"); return -1.0; } double sd_0 = 0.0; //「shift時点の」終値のN日間標準偏差 double sd_total = 0.0; // 過去 M 日間の標準偏差の合計 double sd_ave = 0.0; // 終値の N 日間標準偏差の M 日単純平均 double res1 = 0.0; // VL期間(int型にキャストする前の値) double res2 = 0.0; // 戻り値 for(int i = shift; i < shift + M_period; i++) { double c_sum = 0.0; // 終値の N 日間合計 // N 日間の終値を合計 for(int j = i; j < i + N_period; j++) { c_sum += iClose(NULL, timeframe, j); } double c_ave = c_sum / N_period; // 終値の N 日間単純平均 double d_sum = 0.0; // (各日の終値 - 平均)の2乗を N 日間分だけ合計 for(int j = i; j < i + N_period; j++) { double diff = iClose(NULL, timeframe, j) - c_ave; d_sum += diff * diff; } // N 日間標準偏差を M 日分だけ合計 sd_total += MathSqrt(d_sum / N_period); // i == shift のときの標準偏差を sd_0 に代入 if(i == shift) sd_0 = sd_total; // shift時点の N 日間標準偏差 } // 終値の N 日間標準偏差の M 日単純平均 sd_ave = sd_total / M_period; // VL期間の計算 if(!sd_0) res1 = base_period; // 0割りを回避(コード末尾の [メモ] を参照) else res1 = base_period * sd_ave / sd_0; int VL_period = (int)res1; // 小数点以下を切捨てて、VL_period に代入 res2 = iRSI(NULL, timeframe, VL_period, PRICE_CLOSE, shift); return res2; } /* [メモ] N_period が小さい数の場合(たとえば 2 や 3 )、終値が同じ日が数日続けば sd_0 が 0 になることがあり得る。その場合、0割りが発生しプログラムが止まる。仮に sd_0 を 限りなく小さい値と見立てると、VL期間 が極端に大きな値になってしまう。 急激な数値変化を避けるために、妥協案として sd_0 の値に平均値を採用する。 この場合、base_period * sd_ave / sd_ave となるので、base_period * 1 である。 こうした考えから sd_0 が 0 の場合は res1 = base_period とした。 */
「VL期間の計算」という部分が冗長に感じる場合は、変数 res1 を使わずに、まとめてしまってもよろしいかと思います。(コードを書いた時点では、int型にキャストする前の値を調べたかったので res1 を使っています。)
(3)使い方
上述のサイトでは「基準期間は14 N=10 M=10」となっていたので、それを当てはめると以下のようになります。
double omusubi = eVLDMI(PERIOD_M5, 14, 10, 10, 2);
この例では、変数omusubi に VLDMI の値を代入しています。
(引数の説明)
第1引数は 時間軸
第2引数は 基準期間
第3引数は 標準偏差を求める期間 (上述の計算式の N 日)
第4引数は 標準偏差を平均する期間(上述の計算式の M 日)
第5引数は バーシフト(何本目のローソク足か)
(引数のルール)
第1引数は PERIOD_M5など
第2引数は 1 以上の整数値
第3引数は 1 以上の整数値
第4引数は 1 以上の整数値
第5引数は 0 以上の整数値
(4)エラーになるケース
第3引数や第4引数に0を入れるとエラーになります。
この場合の戻り値は -1 です。