日本時間を使ったEAを作ろうとしたらMT4時間の時差やサマータイムでハマってしまった。結論を言うと日本時間に調整する関数を挟んで何とかした。ほとんどが参考プログラムなので手間はほとんどかかっていない。簡単なテストでは動いていたのでこれで日本時間はなんとかなるだろう。
しかし、ひとつ気がかりなことがある。それはヒストリカルデータのGMTバグ。以下のサイトで2013年だけデータ違うといった情報があった。まだ調べ切れていないのでなんとも言えないがこのデータのバックテストを信用して実弾は使えない。FXDDのヒストリカルデータについては別途調査したいと思う。
調査した結果からは、冬時間に該当する期間で、GMT+3のデータに変換されているのは、2013/11~の2014/3のみで、他の冬時間に該当する期間はGMT+3に変換されずそのままGMT+2のデータになっていることが想像されます。
FXDD(MT4)とFXFT(MT4)とXM(MT5)を使って2013年の過去データを確認した。前述のサイトで指摘されている通り2013年のデータ時間がズレている可能性が高いことが確認できた。MT5のデータを比較すると明らかに時間が一致していない。無料でダウンロードするヒストリカルデータに時間ズレがある可能性があるため使用する前にデータが正しいか検証した方がいい。日本時間を使ってEAの場合はこの時間ズレで結果が変わってしまう可能性が高い。
本記事はFXの自動売買に使用されるプログラムについて解説していますが内容を保証するものではありません。金銭にかかわる内容であるためご注意ください。参考にする場合は自己責任でお願いします。
サマータイムへの切り替えのタイミングはサーバー(証券会社)によって異なりますのでご注意ください。サマータイムへの切り替えタイミングは対象の証券会社のサイトで確認してください。
本記事ではMT4を使用する。
FXDD – MetaTrader
・Version: 4.00 build 1322
・8Feb 2021
MetaEditor
・Version 5.00 build 2375
・7 Jan 2021
MT4と日本時間
MT4は夏季がGMT+3で冬季がGMT+2、日本時間は常にGMT+9である。
MT4は日本時間で表示されない
MT4を起動すると気づくと思うが時間がズレている。これはMT4で適用されている時間が「GMT+2サマータイム」になっているため。日本時間と6時間もしくは7時間ズレている。サマータイムのルールがあるため1年を通して時間のズレが異なり単純な足し算だけで計算できない。
サマータイム
FXでは3月の第2日曜日から11月の第1日曜日が夏時間となりプラス6時間、それ以外の冬時間はプラス7時間となる。
サマータイムについての詳しい説明はこちら。
【EA開発ガイド】Part 2 EA開発実践 – Chapter 6 サーマータイムとヒストリカルデータ
日本時間の取得
サマータイムを考慮しMT4の時刻を日本時間に変更する。
(1) 日本時間取得関数
サマータイム用の関数が無かったので他サイトのコードを参考に日本時間を取得する関数を作成。処理は3月の第2日曜日から11月の第1日曜日であれば夏時間として取得時刻にプラス6時間する。それ以外はプラス7時間に補正して日本時間とする。とりあえず20年分くらい使えればいいのでこの方法で進める。
以下は参考にさせていただいたサイト。
参考
MT4のEAバックテストでサマータイムを正確に計算するトレードステーションと株・FX自動売買で暮らす
参考
【MQL】サマータイムに対応した日本時間を出力する【EA】ろん|note
common-function.mqh
//+------------------------------------------------------------------+ //| common-function.mqh | //| Copyright 2021, zerokara-blog. | //| https://zerokara-blog.com/ | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, zerokara-blog." #property link "https://zerokara-blog.com/" #property strict // 日本時間の取得 datetime getJapanTime() { datetime present = TimeCurrent(); datetime summer = present + 21600; datetime winter = present + 25200; switch(Year()) { case 2000: if(StringToTime("2000.3.12")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2000.11.5"))return summer; break; case 2001: if(StringToTime("2001.3.11")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2001.11.4"))return summer; break; case 2002: if(StringToTime("2002.3.10")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2002.11.3"))return summer; break; case 2003: if(StringToTime("2003.3.9") <=TimeCurrent()&&TimeCurrent()<=StringToTime("2003.11.2"))return summer; break; case 2004: if(StringToTime("2004.3.14")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2004.11.7"))return summer; break; case 2005: if(StringToTime("2005.3.13")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2005.11.6"))return summer; break; case 2006: if(StringToTime("2006.3.12")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2006.11.5"))return summer; break; case 2007: if(StringToTime("2007.3.11")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2007.11.4"))return summer; break; case 2008: if(StringToTime("2008.3.9") <=TimeCurrent()&&TimeCurrent()<=StringToTime("2008.11.2"))return summer; break; case 2009: if(StringToTime("2009.3.8") <=TimeCurrent()&&TimeCurrent()<=StringToTime("2009.11.1"))return summer; break; case 2010: if(StringToTime("2010.3.14")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2010.11.7"))return summer; break; case 2011: if(StringToTime("2011.3.13")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2011.11.6"))return summer; break; case 2012: if(StringToTime("2012.3.11")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2012.11.4"))return summer; break; case 2013: if(StringToTime("2013.3.10")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2013.11.3"))return summer; break; case 2014: if(StringToTime("2014.3.9") <=TimeCurrent()&&TimeCurrent()<=StringToTime("2014.11.2"))return summer; break; case 2015: if(StringToTime("2015.3.8") <=TimeCurrent()&&TimeCurrent()<=StringToTime("2015.11.1"))return summer; break; case 2016: if(StringToTime("2016.3.13")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2016.11.6"))return summer; break; case 2017: if(StringToTime("2017.3.12")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2017.11.5"))return summer; break; case 2018: if(StringToTime("2018.3.11")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2018.11.4"))return summer; break; case 2019: if(StringToTime("2019.3.10")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2019.11.3"))return summer; break; case 2020: if(StringToTime("2020.3.8") <=TimeCurrent()&&TimeCurrent()<=StringToTime("2020.11.1"))return summer; break; case 2021: if(StringToTime("2021.3.14")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2021.11.7"))return summer; break; case 2022: if(StringToTime("2022.3.13")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2022.11.6"))return summer; break; case 2023: if(StringToTime("2023.3.12")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2023.11.5"))return summer; break; case 2024: if(StringToTime("2024.3.10")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2024.11.3"))return summer; break; case 2025: if(StringToTime("2025.3.9") <=TimeCurrent()&&TimeCurrent()<=StringToTime("2025.11.2"))return summer; break; case 2026: if(StringToTime("2026.3.8") <=TimeCurrent()&&TimeCurrent()<=StringToTime("2026.11.1"))return summer; break; case 2027: if(StringToTime("2027.3.14")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2027.11.7"))return summer; break; case 2028: if(StringToTime("2028.3.12")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2028.11.5"))return summer; break; case 2029: if(StringToTime("2029.3.11")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2029.11.4"))return summer; break; case 2030: if(StringToTime("2030.3.10")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2030.11.3"))return summer; break; case 2031: if(StringToTime("2031.3.9") <=TimeCurrent()&&TimeCurrent()<=StringToTime("2031.11.2"))return summer; break; case 2032: if(StringToTime("2032.3.14")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2032.11.7"))return summer; break; case 2033: if(StringToTime("2033.3.13")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2033.11.6"))return summer; break; case 2034: if(StringToTime("2034.3.12")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2034.11.5"))return summer; break; case 2035: if(StringToTime("2035.3.11")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2035.11.4"))return summer; break; case 2036: if(StringToTime("2036.3.9") <=TimeCurrent()&&TimeCurrent()<=StringToTime("2036.11.2"))return summer; break; case 2037: if(StringToTime("2037.3.8") <=TimeCurrent()&&TimeCurrent()<=StringToTime("2037.11.1"))return summer; break; case 2038: if(StringToTime("2038.3.14")<=TimeCurrent()&&TimeCurrent()<=StringToTime("2038.11.7"))return summer; break; } return winter; }
(2) 呼び出し処理
先ほど作成した関数をEAで呼び出す。この関数を1回だけ呼び出しMT4時間と変換した日本時間を表示するプログラム。
別ファイルの関数を呼び出す方法はこちら。
【EA開発ガイド】Part 3 MQL4開発基本 – Chapter 2 MQHを使って共通処理を別ファイルで管理する
function-tester.mq4
//+------------------------------------------------------------------+ //| function-tester.mq4 | //| Copyright 2021, zerokara-blog. | //| https://zerokara-blog.com/ | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, zerokara-blog." #property link "https://zerokara-blog.com/" #property version "1.00" #property strict #include <common-function.mqh> bool flg; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- flg = true; //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- if(flg) { // 日本時間取得関数のテスト // MT4時間を出力 Print(TimeCurrent()); // 日本時間を出力 Print(getJapanTime()); // 1回だけ実行 flg = false; } } //+------------------------------------------------------------------+
(3) 実行結果の確認
8月と1月で日本時間が異なるか確認する。
2020年8月5日
夏時間なので時差が6時間になる。
テスト期間を2020年8月5日から2020年8月6日に設定。
テスターの使い方についてはこちら。
【EA開発ガイド】Part 3 MQL4開発基本 – Chapter 1 EAでHelloWorldを実行する
MT4と日本時間の時差が6時間。
2021年1月5日
テスト期間を2021年1月5日から2021年1月6日に設定。
MT4との時差が7時間あることが確認できる。5分になっているのは0分にティックの変更がないため。
これでサマータイムに対応した時刻を取得できるようになった。
まとめ
久しぶりのC言語ライクのプログラミングでちょっと怪しいが何とか動いた。気になるのがバックテスト時のヒストリカルデータの信頼性。ものによってはGMTがズレているって話もあるので注意した方がいいかもしれない。
- この関数は3月の第2日曜日から11月の第1日曜日が夏時間であることが前提
- ヒストリカルデータが冬時間GMT+2、夏時間GMT+3の前提
- 関数の対応年数は2000年から2038年まで
システムのコストなどを考えるとサマータイムってマイナスでしかないですよね。