今回はバックテストを取る中で日本時間を考慮していく際に避けていけないMT4の使用の一つGMT時間にあるサマータイムによる時差をコード上で考慮していく方法を説明していきます。
サマータイムに関しては色んな書き方があると思いますのであくまでも一例です。(色々考えてなるべく短くなるように書いてます)
※もっといい方法あるよという場合は是非教えて下さい。
少し面倒なことをしている感じはありますが、毎年打込む方式のものはプログラミングとしてどうだ?という点がありましたので。
サマータイムとは?
日本ではあまり馴染みのないことですが、欧州を中心に、日の出時刻が早まる時期(3月~11月)に時計の針を一時間進め、太陽の出ている時間帯を有効活用することを目的とされているものです。
このシステムは多くのMT4・MT5にも適応されており、日本時間の勝率を確認する際、この時間を考慮した上で変換する必要があります。
またサマータイムの変換方法によってはアメリカやイギリスなど国や州、地域によって異なりますが、今回は
アメリカのサマータイム
≪開始日≫3月の第2日曜日午前2時
≪終了日≫11月の第1日曜日午前2時
を基準に考えます。
また取引の関係で日曜はもともと存在しないので表現としては
上記の時間を超えた月曜日からという書き方になります。
今回の内容
今回行う内容は
- サマータイムの変換を確認する(確認はアロー)
サマータイムのサンプルコード
まずはいつも通り全体のサンプルコードから
#property copyright "TAKULOG"
#property version "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1 clrBlue
#property indicator_color2 clrRed
#property indicator_width1 1
#property indicator_width2 1
////// 【バッファー用意】 //////////////////////////////////////
double ArrowDo[]; // 下矢印用バッファー
double ArrowUp[]; // 上矢印用バッファー
int TimeFlag=0;
bool summer = false;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit(){//--- indicator buffers mapping
SetIndexBuffer(0,ArrowDo) ;Arrow(0,238,"arrow_Do");// >>下矢印
SetIndexBuffer(1,ArrowUp) ;Arrow(1,236,"arrow_Up"); // >>上矢印
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated,
const datetime &time[],const double &open[],
const double &high[], const double &low[],
const double &close[], const long &tick_volume[],
const long &volume[], const int &spread[])
{//---
int limit = rates_total - prev_calculated;
int n = 2; //array out of range対処
if (limit > 1) {limit = rates_total - n - 1;}
//////////////////////////////// <<アロー用forループ開始>> /////////////////////////////////////////////
for(int i=limit; i>=0; i--){//for開始
if(i+1 >= Bars-1){continue;}
int TimeH = (int)TimeHour(iTime(NULL,0,i)); //時間を取得
int Timecount;
//誤差計算
if(TimeH+Summerflag(i)==24){Timecount=0;}
else{Timecount=TimeH+Summerflag(i);}
if(Timecount)==1){
if(Open[i]<Close[i]){
ArrowDo[i]=High[i]+Point*5;
}
else if(Open[i]>Close[i]){
ArrowUp[i]=Low[i]-Point*5;
}
else{
ArrowDo[i]=0; ArrowUp[i] =0;
}
}
}
//--- return value of prev_calculated for next call
return(rates_total);
}
// >>自作関数
// >>---------<< アロー関数 >>--------------------------------------------------------------------<<
void Arrow(int number,int code,string name){
SetIndexStyle(number,DRAW_ARROW); //矢印を出す
SetIndexArrow(number,code); //矢印の種類設定
SetIndexLabel(number,name); //ラベル(名前)を決める
SetIndexEmptyValue(number,0.0);
}
// >>---------<< サマータイム関数 >>--------------------------------------------------------------------<<
int Summerflag(int shift){ // TimeFlag と summer はグローバル関数
int B=0;
int CanM = (int)TimeMonth(iTime(NULL,0,shift)); //月取得
int CanD = (int)TimeDay(iTime(NULL,0,shift)); //日取得
int CanW = (int)TimeDayOfWeek(iTime(NULL,0,shift));//曜日取得
if(TimeFlag!=CanD){ //>>日が変わった際に計算
if(CanM>=3&&CanM<=11){ //------------------------------------------- 3月から11月範囲計算開始
if(CanM==3){ //------------------------------------------- 3月の計算(月曜日が○日だったら夏時間)
if(CanD<=8) { summer = false;}
if(CanD==9) { if(CanW==1){summer = true;}else{summer = false;} }// 9日の月曜日が第3月曜日の最小日(第2日曜の最小が8日の為)
if(CanD==10){ if(CanW<=2){summer = true;}else{summer = false;} }// 10日が火曜以下であれば,第3月曜日を迎えた週
if(CanD==11){ if(CanW<=3){summer = true;}else{summer = false;} }// 11日が水曜以下であれば,第3月曜日を迎えた週
if(CanD==12){ if(CanW<=4){summer = true;}else{summer = false;} }// 12日が木曜以下であれば,第3月曜日を迎えた週
if(CanD>=13){ summer = true; } // 13日以降は上の条件のいずれかが必ず満たされる
}
if(CanM==11){ //------------------------------------------ 11月の計算(月曜日が○日だったら冬時間)
if(CanD==1){ summer = true; }
if(CanD==2){ if(CanW==1){summer = false;}else{summer = true;} }// 2日の月曜日が第2月曜日の最小日(第1日曜の最小が1日の為)
if(CanD==3){ if(CanW<=2){summer = false;}else{summer = true;} }// 3日が火曜以下であれば,第2月曜日を迎えた週
if(CanD==4){ if(CanW<=3){summer = false;}else{summer = true;} }// 4日が水曜以下であれば,第2月曜日を迎えた週
if(CanD==5){ if(CanW<=4){summer = false;}else{summer = true;} }// 5日が木曜以下であれば,第2月曜日を迎えた週
if(CanD==6){ if(CanW<=5){summer = false;}else{summer = true;} }// 6日が金曜以下であれば,第2月曜日を迎えた週
if(CanD>=7){ summer = false; } // 7日以降が何曜日に来ても第2月曜日を迎えている(7日が日なら迎えていないが8日で迎える)
}
if(CanM!=3&&CanM!=11)summer = true;// 4月~10月は無条件で夏時間
} //--------------------------------------------------------------- 3月から11月範囲計算終了
else{summer = false;}//12月~2月は無条件で冬時間
TimeFlag=CanD;
} if(summer == true){B=0;}else{B=1;}
return(B);
}
コンパイルしたもの
サマータイムのコードの理屈
サマータイムのコードを書く上でカレンダーの性質を利用していきます。
サマータイムは3月の第2日曜日と11月の第1日曜日とどちらも日曜日を基準に変わりますので日曜日の最小、最大日時を考えて
その他の曜日を考慮し、コードを書くといい訳なのでまず、最小と最大を見てみます。
上記の画像からわかるように
第二日曜の最小は8日、最大は14日
第一日曜の最小は1日、最大は15日
となるのでこのことを考慮すれば表現はできます。
MT4で取得できる情報とサマータイムをコード化する上での注意点
【MQL4:サインツール編③】指定した時間足や指定時間のみにサインを出す方法
でも紹介していますが、MT4で取得できる時間関係の情報は
TimeDayOfWeek() 指定した日付の曜日を数値(0:日曜日)で返します。
TimeDayOfYear() 指定した日付の経過日数(1年の経過日数)を返します
TimeYear() 指定した日付の年(年月日の'年'のみ)を返します
TimeMonth() 指定した日付の月(年月日の'月'のみ)を返します
TimeDay() 指定した日付の日(年月日の'日')を返します
TimeHour() 指定した時間の時(時分秒の'時'のみ)を返します。
TimeMinute() 指定した時間の分(時分秒の'分'のみ)を返します。
TimeSeconds() 指定した時間の秒(時分秒の'秒'のみ)を返します。
です。日時と曜日が分かれば今回は充分ですので
TimeDayOfWeek() 指定した日付の曜日を数値(0:日曜日)で返します。
TimeMonth() 指定した日付の月(年月日の'月'のみ)を返します
TimeDay() 指定した日付の日(年月日の'日')を返します
この3つのコードを今回は使用しています。
サンプルコードの三月の計算を例にするのですが
if(CanD==9) { if(CanW==1){summer = true;}else{summer = false;} }// 9日の月曜日が第3月曜日の最小日(第2日曜の最小が8日の為)
if(CanD==10){ if(CanW<=2){summer = true;}else{summer = false;} }// 10日が火曜以下であれば,第3月曜日を迎えた週
if(CanD==11){ if(CanW<=3){summer = true;}else{summer = false;} }// 11日が水曜以下であれば,第3月曜日を迎えた週
if(CanD==12){ if(CanW<=4){summer = true;}else{summer = false;} }// 12日が木曜以下であれば,第3月曜日を迎えた週
if(CanD>=13){ summer = true; } // 13日以降は上の条件のいずれかが必ず満たされる
上記で最小と最大を考慮するとという話はしていたのですが、ここでの注意点がMT4で取得した日がサマータイムであるか?ないか?の判定をする必要がありますので
処理的に
○日が△曜日以内だったらという処理をしています。
第二日曜の最小が8日であることから、第二月曜から土曜までの最小も自ずと決まっているので
取得した日が○曜日以下であればという書き方で判定すれば判定できます。
判定で取得した状態を使用する例
今回のサマータイムのサンプルコードでの関数内の処理に
夏時間と判定した場合ture、それ以外をfalseとしています。
Bという変数に1と0を返すようにしていますが(実際はture,falseで1.0の値取れるので省略可ですが、その場合ture、falseを反対にする必要あります。)
MT4上の時間の誤差を埋めるのが目的な為ですので夏か冬どちらかを基準にして統一すればいいという考えです。
今回は夏時間を基準に冬に足し上げるという形を取っていますのでイメージとしては下記の画像のようなイメージです。
実際にコードで処理している部分は
int TimeH = (int)TimeHour(iTime(NULL,0,i)); // 時間を取得
int Timecount;
//誤差計算
if(TimeH+Summerflag(i)==24){Timecount=0;}
else{Timecount=TimeH+Summerflag(i);}
if(Timecount)==1){
}
の部分になります。冬時間は1を足し上げているので冬時間のみ24という数字になってしまいますが、MT4上に存在しない時間の数字になりますので24になった場合だけ
0にするようにしてあげる処理をしています。
本コードの余談
説明が難しい