MT5でチャート情報をCSV出力する

　個人利用するために書いた、とりあえず動作する程度で、スマートなコードじゃないかもしれませんが予めご了承ください。もっとこういう書き方すればいいよというアドバイスがある方は是非ご教授ください。

# 本題
　ExcelやPythonなどを使って相場分析するためにチャート情報を取得する必要がありますが、必要なデータを手軽にCSV出力できるようにMT5のスクリプトファイルを作っておくと便利です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1882169/5a5bf772-5128-4ebb-4046-90788bd71751.png)


　データはMT5で表示されているチャートをそのままCSVに変換したようなものです。時間データは __年/月/日/曜日/時/分__ と分けておくとアノマリー分析等がやりやすいかなと個人的に思います。

　また、データ分析でよく使用する対数差収益率も一緒に出力しておくとあとから計算する手間が省けますね。(PythonだったらPython側でやっても手間は変わらないけど)

# スクリプトのソースコード

```.cpp
//+------------------------------------------------------------------+
//|
//|   CVSファイルに出力
//|
//+------------------------------------------------------------------+

#property script_show_inputs //実行前にパラメーター入力ウィンドウ表示

input datetime Start = D'2020.01.01';           // ここから…
input datetime Stop  = D'2022.01.01';           // ここまで出力する
input ENUM_TIMEFRAMES TF = PERIOD_CURRENT;      // 時間軸

void OnStart()
{
//--- input変数のチェック
  if(Start >= Stop) {
    MessageBox("出力開始日時と出力終了日時が間違ってます。", "input変数のエラー", MB_OK);
    return;
  }

  int start_index = iBarShift(_Symbol, TF, Start, false);
  int stop_index = iBarShift(_Symbol, TF, Stop, false);
  string filename, timeflame;
  datetime time;
  double r;
  MqlDateTime dt;

//--- ファイル名
  if(TF == PERIOD_CURRENT)
    timeflame = EnumToString(_Period);
  else
    timeflame = EnumToString(TF);
  filename = "ChartData_" + _Symbol + "_" + timeflame + "_" + TimeToString(Stop, TIME_DATE) + "_" + TimeToString(Start, TIME_DATE);
  StringReplace(filename, ".", "");
  StringReplace(filename, " ", "");
  StringReplace(filename, ":", "");
  filename += ".csv";

//--- ファイルハンドルの取得
  int handle = FileOpen(filename, FILE_WRITE | FILE_CSV);
  if( handle == INVALID_HANDLE ) {
    Print("ハンドル取得に失敗：", GetLastError());
    return;
  }

//--- カラム名を書き込む
  FileWrite(handle, "年", "月", "日", "曜日", "時", "分", "始値", "高値", "安値", "終値", "対数差収益率", "ティックボリューム");
  
//--- データの書き込み
  for(int i = stop_index; i < start_index; i++) {
    time = iTime(_Symbol, TF, i);
    TimeToStruct(time, dt);
    r = log(iClose(_Symbol, TF, i)) - log(iClose(_Symbol, TF, i + 1));
    
    FileWrite(handle, dt.year,
                      dt.mon,
                      dt.day,
                      dt.day_of_week,
                      dt.hour,
                      dt.min,
                      iOpen(_Symbol, TF, i),
                      iHigh(_Symbol, TF, i),
                      iLow(_Symbol, TF, i),
                      iClose(_Symbol, TF, i),
                      r,
                      iTickVolume(_Symbol, TF, i));
  }

  FileClose(handle);
  MessageBox("CSV出力が完了しました。", "完了", MB_OK);
}
```
　これをコンパイル&実行すると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1882169/1e258d01-c286-9867-5760-2cf837d8a299.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1882169/daf7e294-c9d8-975c-24f5-ddd003bdee50.png)
　こんな感じでメッセージが表示されるので出力されたファイルを確認していきます。

　保存場所ですがMT5の仕様で、CSV出力するときは
__ファイルパス： \MetaQuotes\Terminal\　(MT5アカウントディレクトリ) \MQL5\Files__
ここに保存されるようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1882169/446748d8-3243-c75b-9b3d-388fdf08c4aa.png)
↓ファイルの中身
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1882169/c55ee009-529f-e5be-7abd-d80bff98fc48.png)
曜日は、0が日曜、1が月曜……　6が土曜です。

# サマータイム情報もほしい場合
　MT5では多くのブローカーでGMT+2、サマータイムは米国に従っていると思います。そこでCSVの時間情報の中にサマータイム中かどうかを追加します。

　サマータイムかどうかの判定をする関数は、こちらのブログがとても参考になったのでリンクを貼っておきます。

https://uhoho.hatenablog.jp/entry/2021/06/13/104718

リンク先の関数を使わせていただきまして、NewYorkIsSummerTime()関数を上のコードに追加します。

「カラム名を書き込む」「データを書き込む」ところを下のように書き換え…
```.cpp
//--- カラム名を書き込む
  FileWrite(handle, "年", "月", "日", "曜日", "時", "分", "夏時間", "始値", "高値", "安値", "終値", "対数差収益率", "ティックボリューム");
  
//--- データの書き込み
  for(int i = stop_index; i < start_index; i++) {
    time = iTime(_Symbol, TF, i);
    TimeToStruct(time, dt);
    r = log(iClose(_Symbol, TF, i)) - log(iClose(_Symbol, TF, i + 1));
    
    FileWrite(handle, dt.year,
                      dt.mon,
                      dt.day,
                      dt.day_of_week,
                      dt.hour,
                      dt.min,
                      (int)NewYorkIsSummerTime(time, 2),
                      iOpen(_Symbol, TF, i),
                      iHigh(_Symbol, TF, i),
                      iLow(_Symbol, TF, i),
                      iClose(_Symbol, TF, i),
                      r,
                      iTickVolume(_Symbol, TF, i));
  }

```
　CSVファイルを出力すると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1882169/9c1352e4-a639-bdb1-28ad-99642170b4c1.png)
冬時間であれば0、夏時間であれば1
となる情報を追加できました。