PR

【MQL】指定した日が祝日なのか判定する方法【ソースコードあり】

オリジナル関数

この記事では、MQL4、またはMQL5言語を用いて、特定の日付が日本の祝日かどうかを確認する手順を詳しく解説します。
実用的なソースコードの例と共に、日本の祝日を判定するためのアルゴリズムの構築方法を学べます。FXトレーディングや自動売買システムを、開発する際に役立つ情報が満載です


前回の記事で、日本の祝日をEA内に取り込んで使えるようになりましたので、その実用編になります。

WAN
WAN

日本市場が休場の時に、別のイベントが重なると急変動となりやすいんだ。
今日紹介するコードを用いて、日本が祝日なのか判定できるよ

You
You

祝日は身構えちゃいますよね。
逆にブレイクアウトにも応用できたりして!

WAN
WAN

無茶なことはやめておけ!!
スプレッドが上下に開いて終了だw

祝日の判定に使用する、オリジナル関数の作成

オリジナル関数を作成する際には、その関数の目的と必要なデータを整理し、それらを組み込むロジックを作成します。

オリジナル関数 int HolidayFilterLogic(datetime search_time)の概要

この関数は、引数で受け取った、datetime型の変数(search_time)が、祝日情報の配列の何番目にあるかを返します。どこにも一致しない(祝日じゃない)場合は、-1を返します。

例えば、search_timeが配列の3番目の祝日と一致する場合、関数は「3」という値を返します。

祝日の判定に必要な情報(時刻の年月日)を抽出して修正

祝日情報の配列は、年月日(yyyy.mm.dd)の形式で格納されており、時分秒(hh:mm:ss)は含まれていません。そのため、もし引数として渡される変数 (search_time) に、時分秒の情報が含まれている場合、正確な照合ができなくなってしまいます

そこで、受け取った変数(search_time)の、年月日部分だけを次の方法で残します。

search_time = StringToTime(TimeToString(search_time, TIME_DATE));

TimeToString 関数で、日付け部分を文字列に変換し、StringToTime 関数で再び時刻型に戻します。

WAN
WAN

いってこい、ってやつね
面倒ですが結構使います(‘ω’)

情報配列を使えるようにする

ここで言う情報配列とは、前回作成した祝日のデータが格納されている配列です。
動的配列のデータを参照するには、配列の大きさを確認する必要があります。

祝日の情報が格納されている配列の大きさを確認する

祝日の情報が入った、HolidayArray[][2]という多次元配列の大きさを調べる方法について説明します。

この配列には、前回の記事でCSVから取り込まれた祝日のデータが格納されています。
配列の「1次元目」は、CSVファイルの行に対応していますので、行の数を知るためには1次元目(0番目)のサイズを調べれば良いことになります。

特定次元のサイズを調べるには、ArrayRange関数を使います。
さらに、得られた数字をcsv_sizeに代入しています。

int csv_size = ArrayRange(HolidayArray, 0);

ArrayRange関数の第二引数の(0)は、配列の1次元目を表しています。

多次元配列(配列[①][②])は、①が1次元目ですが、これは配列の0番目(インデックス0)に該当します。②は2次元目ですが、1番目(インデックス1)に該当します。

情報配列をチェックして、祝日かどうか判定する方法

次に、祝日の情報が入った配列を確認する、for文の使い方を詳しく解説します。

このHolidayArray[][2]配列には、祝日の日付と名称が格納されています。
日付は配列の0番目(1次元目の動的配列)、名称は1番目(2次元目の静的配列)に入っています。
これらは、両方とも文字列型で格納されています。

for文を使用して、配列内のデータをチェックする

for文で、HolidayArrayの各行を一つずつチェックします。
その際、配列の0列目にある日付を StringToTime 関数で時刻型に変換し、それが引数で渡された search_time と一致するかどうかを確認します

for(int i = 0; i < csv_size; i++)
 {
 datetime csvtime = StringToTime(HolidayArray[i][0]);//祝日配列の日付部分
 if(csvtime == search_time)
  return(i);
 }

この配列にない時刻の場合は、for文を抜けて-1を返してあげます。

完成したソースコードがこちら↓↓

//+------------------------------------------------------------------+
//|  引数で受け取った時刻が祝日CSVの何番目に該当するかを返す関数  ワンダフルFXライフ
//+------------------------------------------------------------------+
int HolidayFilterLogic(datetime search_time)
  {
//年月日に修正
   search_time = StringToTime(TimeToString(search_time, TIME_DATE));
   int csv_size = ArrayRange(HolidayArray, 0);
   for(int i = 0; i < csv_size; i++)
     {
      datetime csvtime = StringToTime(HolidayArray[i][0]);//祝日配列の日付部分
      if(csvtime == search_time)
         return(i);
     }
   return(-1);
  }
プログラミングの考え方

配列やリスト内の検索において、該当する要素が存在しない場合、負の数(多くの場合は -1)を返すことがあります。これは、一般的なプログラミングの慣例の一種です。

2次元配列のイメージ

データチェックにおける注意点

なぜ日付を文字列で比較しないのか?
日付を文字列として比較すると、形式の違いで同じ日付でも異なるものと判断されてしまう可能性があります。
例えば、「2020.1.1」と「2020.01.01」は見た目が異なるため、文字列としては異なるものとみなされます。これを避けるために、日付の文字列を時刻型に変換して比較します。

WAN
WAN

数字なのに文字列っていう考え方が初心者にはわかりずらいんだよねぇ

数字や時刻型の01や+1は全部1だけど、
文字列の”01″や”+1″は”1″ではないんだよね(^^;)

まとめ

  • 日付けは文字ではなく、時刻型で比較する
  • 多次元配列の各次元のサイズの取得は、ArrayRange関数
  • 引数の時刻が配列に該当した場合は、該当番目、該当しない場合は-1を返す

以下は完成したコード(前回の記事のコードも含みます)です。EAとして、動作を確認することができます。

//+------------------------------------------------------------------+
//|  テストコード  ワンダフルFXライフ
//+------------------------------------------------------------------+
int OnInit()
  {
  //祝日情報を取り込む
   HolidayRead();
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//|  テストコード  ワンダフルFXライフ
//+------------------------------------------------------------------+
void OnTick()
  {
  //現在のサーバー時刻が祝日CSVの何番目にあるかを検索
   int holiday = HolidayFilterLogic(TimeCurrent());
   if(holiday == -1) //-1の場合は祝日じゃない
      Comment("本日は祝日ではありません。");
   else
      Comment("本日は", HolidayArray[holiday][1], "です。");
  }

//+------------------------------------------------------------------+
//|  引数で受け取った時刻が祝日CSVの何番目に該当するかを返す関数  ワンダフルFXライフ
//+------------------------------------------------------------------+
int HolidayFilterLogic(datetime search_time)
  {
//年月日に修正
   search_time = StringToTime(TimeToString(search_time, TIME_DATE));
   int csv_size = ArrayRange(HolidayArray, 0);
   for(int i = 0; i < csv_size; i++)
     {
      datetime csvtime = StringToTime(HolidayArray[i][0]);//祝日配列の日付部分
      if(csvtime == search_time)
         return(i);
     }
   return(-1);
  }

//+------------------------------------------------------------------+
//| Webページを読み込んでstringに代入する関数  ワンダフルFXライフ
//+------------------------------------------------------------------+
#import "wininet.dll"
int InternetOpenW(string agent, int accessType, string proxyName, string proxyByPass, int flags);
int InternetOpenUrlW(int internetSession, string url, string headers, int headersLength, int flags, int context);
int InternetReadFile(int handle, uchar &buffer[], int numberOfBytesToRead, int &numberOfBytesRead);
int InternetCloseHandle(int handle);
#import
string ReadWebPage(string url)
  {
   string text = "";
   if(TerminalInfoInteger(TERMINAL_DLLS_ALLOWED) == false)
     {
      Comment("DLLの使用を許可してください");
      return(text);
     }
   int inet = InternetOpenW("MetaTrader 5 Terminal", 0, NULL, NULL, 0);
   if(inet == 0)
     {
      Comment("InternetOpenW failed.");
      return(text);
     }
   int handle = InternetOpenUrlW(inet, url, NULL, 0, 0, 0);
   if(handle == 0)
     {
      Comment("InternetOpenUrlW failed.");
      InternetCloseHandle(inet);
      return(text);
     }
   int    byteSize = 0;
   uchar  receive[1024];//1キロバイトずつが最適
   while(InternetReadFile(handle, receive, ArraySize(receive), byteSize))
     {
      if(byteSize <= 0)
         break;
      text += CharArrayToString(receive, 0, byteSize);
     }
   InternetCloseHandle(handle);
   InternetCloseHandle(inet);
   return(text);
  }
//+------------------------------------------------------------------+
//|  国民の祝日CSVを読み込んで配列に格納する関数  ワンダフルFXライフ
//+------------------------------------------------------------------+
string HolidayArray[][2];//日付、名称
const string HolidayURL = "https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv";
void HolidayRead()
  {
   string holiday_txt = ReadWebPage(HolidayURL);
   string result[];
   int rows = StringSplit(holiday_txt, '\n', result);
   ArrayResize(HolidayArray, rows - 1); // ヘッダー行を除く行数で配列をリサイズ
   for(int i = 1; i < rows; i++)
     {
      string columns[];
      StringSplit(result[i], ',', columns); // カンマで列を分割
      if(ArraySize(columns) >= 2)
        {
         string holiday = columns[0];
         StringReplace(holiday, "/", "."); // 日付のフォーマットを変更
         HolidayArray[i - 1][0] = holiday; // 日付
         HolidayArray[i - 1][1] = columns[1]; // 名称
        }
     }
  }
//+------------------------------------------------------------------+

出力結果は、以下のようになります。


タイトルとURLをコピーしました