恥は/dev/nullへ by 初心者

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

MT4でCSVファイル出力 and 修正版VLDMI関数

計算結果をファイルに出力して検証したかったので、CSVファイルを出力する方法を調べました。FileOpen関数、FileWrite関数、FileClose関数を使います。

 

この記事内のコードで使われているeVLDMI関数

このブログの過去記事にもeVLDMI関数が登場しますが、それとは少し仕様が異なります。


以下のコードに登場するeVLDMI関数は、自分が所属しているチームで活用してもらうために少し内容を修正したものです。修正点は次の2つです。

・引数の順番(自分用のeVLDMI関数では、引数3 と 引数4 が逆)
・VL期間は基準期間の「2分の1 ~ 2倍」の範囲内

 

コード

// CSVファイルを出力するScript
// CSVファイルの出力先はMT4の「MQL4 → Files」フォルダ

#property copyright "Copyright 2020. PHILOJUAN"
#property link      "https://uhoho.hatenablog.jp/"
#property version   "1.00"
#property strict

void OnStart()
{
   int limit = Bars - 28;

   double vlValue[];            // VLDMIの値を格納する配列を用意
   ArrayResize(vlValue, limit); // 配列の要素数をセット

   for(int i = 0; i < limit; i++)
   {
      vlValue[i] = eVLDMI(NULL , 14, 5, 3, i);
   }

   // eVLDMI.csvというファイルをオープン(作成)
   int fHandle = FileOpen("eVLDMI.csv", FILE_READ | FILE_WRITE | FILE_CSV, ",");

   //<失敗例>
   // int fHandle = FileOpen("eVLDMI.csv", FILE_WRITE | FILE_CSV, ",");
   // FILE_READ を記述しなかったら、CSVファイルの中身が1行だけになった(汗)

   FileWrite(fHandle, "シフト", "VLDMIの値");  // CSVファイルの見出し行を作成

   for(int k = 0; k < limit; k++)     // CSVファイルにvlValue配列の中身を出力
   {
      // FileSeek(fHandle, 0, SEEK_END);
      // 上のFileSeek行は無くても問題なさそうだが、将来ハマった時のヒント
      // になるかもしれないので残してある。

      FileWrite(fHandle, IntegerToString(k) + "本目", vlValue[k]);
   }

   // ファイルを閉じる
   FileClose(fHandle);
}

//------------------------------------------------------------------
// 某チーム用 eVLDMI関数
//------------------------------------------------------------------
//  引数1 timeframe     時間軸(PERIOD_M5等)
//  引数2 base_period   基準期間
//  引数3 M_period      標準偏差を単純平均する期間(1以上の整数値)
//  引数4 N_period      標準偏差計算期間          (1以上の整数値)
//  引数5 shift         バーシフト
//
// 【自分用eVLDMI関数との相違点】
//
// ・引数の順番を変えてある(自分用では、引数3 と 引数4 が逆)
// ・VL期間が、基準期間の「2分の1 ~ 2倍」の範囲に収まるようにしてある

double eVLDMI(int timeframe, int base_period, int M_period, int N_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 k = i; k < i + N_period; k++)
      {
         double diff = iClose(NULL, timeframe, k) - 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;
   else res1 = base_period * sd_ave / sd_0;

   // res1 を基準期間(base_period)の「2分の1 ~ 2倍」の範囲に収める
   if(res1 > base_period * 2)   res1 = base_period * 2;
   if(res1 < base_period / 2.0) res1 = base_period / 2.0;

   int VL_period = (int)res1;    // 小数点以下を切捨てて、VL_period に代入

   res2 = iRSI(NULL, timeframe, VL_period, PRICE_CLOSE, shift);

   return res2;
}