恥は/dev/nullへ by 初心者

プログラミング素人がのろのろと学んだことをつづっています♪

MQLで書いたVLDMIの関数コード(MT4用)

 

追記: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 です。