見出し画像

【MQL】経済指標スケジュールを取得し、チャートに表示させるコードの書き方例

2023/11/05 

お知らせ

  • なし

はじめに

 経済指標発表のスケジュールをサイトから取得して、チャートに表示させるコードの書き方例を掲載します。今回のコードは経済指標発表スケジュールをみんかぶから取得しています。
 自分でトレードしているときに、重要経済指標の予定のチェックをしていなかったばっかりに、利益の出ていたポジションが一瞬でストップロスとなり、後悔したことがきっかけで、このインジケータを作成しました。

注意事項

ここで説明するサンプルコードは、記事作成時点では問題なく動作していますが、対象サイトの変更により動作しなくなる可能性があります。
以前はYahooから取得しているEAが多くありましたが、Yahoo側の変更によりデータ取得が困難になりました。記事作成時点では、みんかぶの他には、外貨exなどから取得するコードを書くことができます。

※Windows版MT4/MT5でのみ動作します。Windows版以外では動作しません。

パラメータの説明

パラメータ

パラメータ

・文字の色: 表示する文字の色
・強調表示の色: ☆の色の強調表示色
・終了したイベントの色: 終了した指標発表の文字色
・強調表示する☆の数: 星の数がここで指定した数以上の場合に強調
・表示する指標の☆の数: 星の数が指定した個数以上の指標のみを表示
・イベントの日付を更新する時刻: 指定した時刻に当日のデータに更新
・終了したイベントは消去する: 終了した指標発表は指定時間後に消去
・消去するまでの時間: 終了したイベントを消去するまでの時間
・最大表示行数: 最大の表示行数
・翌日のイベントを表示する: 当日の続きに翌日分の指標発表予定を表示

表示イメージ

パラメータ初期値の場合、終了したイベントは、グレーに変化し、1時間後に消去されます。発表時刻の確定していない指標については、時刻は空白とし、対象日の間は設定にかかわらず常時表示するようにしました。

インジケータ表示例

コードの説明

処理のポイントとなる箇所を順に説明していき、全体のコードについては、最後に記載します。

サイトへの接続に使用するAPI

みんかぶのサイトに接続して、ページを読み込む手段としては、Windows標準DLL ”wininet.dll"のAPIを使用します。MQLのWebRequsetはインジケータでは使用できません。MT4とMT5では、32ビットと64ビットという違いがあり、使用する関数の引数や復帰値の型が異なる部分があります。
下のコードでは、最初の型定義(#defineの部分)がMT4とMT4で異なっています。

  • MT4用

#define HINTERNET int

#define BOOL int

#define DWORD int

#define DWORD_PTR int

#define LPCTSTR string&

#import "wininet.dll"
	HINTERNET InternetOpenW(LPCTSTR lpszAgent, DWORD dwAccessType, LPCTSTR lpszProxyName, LPCTSTR lpszProxyBypass, DWORD dwFlags);
	HINTERNET HttpOpenRequestW(HINTERNET hConnect, LPCTSTR lpszVerb, LPCTSTR lpszObjectName, LPCTSTR lpszVersion, LPCTSTR lpszReferer, int /*LPCTSTR* */ lplpszAcceptTypes, uint/*DWORD*/ dwFlags, DWORD_PTR dwContext);
	HINTERNET InternetOpenUrlW(HINTERNET hInternet, LPCTSTR lpszUrl, LPCTSTR lpszHeaders, DWORD dwHeadersLength, uint/*DWORD*/ dwFlags, DWORD_PTR dwContext);
	BOOL InternetReadFile(HINTERNET hFile, LPVOID arr[], DWORD, LPDWORD byte);
    BOOL InternetCloseHandle(HINTERNET hInternet);
#import
  • MT5用

#define HINTERNET long

#define BOOL int

#define DWORD int

#define DWORD_PTR long

#define LPCTSTR string&

#import "wininet.dll"
	HINTERNET InternetOpenW(LPCTSTR lpszAgent, DWORD dwAccessType, LPCTSTR lpszProxyName, LPCTSTR lpszProxyBypass, DWORD dwFlags);
	HINTERNET HttpOpenRequestW(HINTERNET hConnect, LPCTSTR lpszVerb, LPCTSTR lpszObjectName, LPCTSTR lpszVersion, LPCTSTR lpszReferer, int /*LPCTSTR* */ lplpszAcceptTypes, uint/*DWORD*/ dwFlags, DWORD_PTR dwContext);
	HINTERNET InternetOpenUrlW(HINTERNET hInternet, LPCTSTR lpszUrl, LPCTSTR lpszHeaders, DWORD dwHeadersLength, uint/*DWORD*/ dwFlags, DWORD_PTR dwContext);
	int InternetReadFile(HINTERNET, uchar &arr[], int, int &byte);                  
	BOOL InternetCloseHandle(HINTERNET hInternet);
#import

サイトからの情報取得

サイトからの情報取得は、GetEventInfo 関数を作成して、引数で発表日時、国名、指標名、重要度(★の数)、時刻未定フラグ を返却するようにしています。

bool GetEventInfo(string _url, datetime _targetDate, datetime& _eventDt[], string& _country[], string& _eventNm[], string& _eventNm2[], int& _stars[], bool& _eventNoSch[])

引数
 ・string _url:対象サイトのURL
 ・datetime _targetDate:取得対象の日付
 ・datetime& _eventDt[ ]:取得した指標発表日時(配列)
 ・string& _country[ ]:取得した国名(配列)
 ・string& _eventNm[ ]:取得した指標名(配列)
 ・int& _stars[ ]:取得した指標重要度(★の数)(配列)
 ・bool& _eventNoSch[ ]:発表時刻未定のときtrueを返却
復帰値
 ・true(取得成功) または false(取得失敗)

みんかぶの場合は、URLに日付のパラメータを付与することにより、指定した日付のスケジュールのページを開くことができます。
https://fx.minkabu.jp/indicators?date=2023-03-21

発表時刻の確定していない発表に関しては、仮の時刻として9時を返却し、_eventoNoschにtrueを設定するようにしてチャートへの表示時に判別できるようにしています。

ブラウザでF12キー押下で開くDevToolsを使って、HTMLの構成を確認しながら文字列の切り出し方法を考えて作っていきました。切り出しが必要な項目の直前の文字列を検索して項目の先頭を検出し、次に項目の後に続く文字列を検索して、対象項目の終了位置を取得して、対象項目の文字列を切り出す方法で切り出しを行っています。
では、コードに説明のコメントを記述しましたので、ご確認ください。

bool GetEventInfo(string _url, datetime _targetDate, datetime& _eventDt[], string& _country[], string& _eventNm[], string& _eventNm2[], int& _stars[], bool& _eventNoSch[]){
   bool ret = true;
   
    // wininet の初期化
	int hInternetSession = InternetOpenW(UserAgent,0,nill,nill,0);                         
  	if(hInternetSession <= 0)
  	{
    	Print("Opening Internet FAILED...");
    	return(false);         
  	}
  	string temp;
  	int iBuffer[256];
  	int dwBytesRead;
  	
    // URLに引数で指定された日付をパラメータとして連結
  	MqlDateTime date_struct;
    TimeToStruct(_targetDate, date_struct);
  	string url = _url + "?date=" + StringFormat("%4d-%02d-%02d", date_struct.year, date_struct.mon, date_struct.day);

    // URLに接続
  	int hURL = InternetOpenUrlW(hInternetSession, url, nill,0,INTERNET_FLAG_PRAGMA_NOCACHE,0); 
  	string toStr = "";
  	if(hURL >0)
  	{
         uchar ch[1000];
         
         int dwBytes;
         // URLのページを1000バイトずつ読み込む
         while(InternetReadFile(hURL, ch, 1000, dwBytes)){
            if(dwBytes <= 0) break;
            //stringに変換
            toStr += CharArrayToString(ch, 0, dwBytes, CP_UTF8);
         }
	}
    //この時点で、みんかぶのページのデータ(HTML)が、toStrに格納されています。

    // 接続を解除
	InternetCloseHandle(hInternetSession);

	int offset;
	int off;
	int off2;
	int end;
	string tmpStr;
	string strDt;
	
	datetime dtTarget[];
	string country[];
	string event[];
	int stars[];
	
	//不必要な部分を適当に取り除く
    //文字列"l-main"以前の部分を除外(必須の処理ではない)
	offset = StringFind(toStr, "l-main", 0);
	tmpStr = StringSubstr(toStr, offset);
	//------------------------------------

    //最初に年月日を取得します。
	offset = StringFind(tmpStr, "年", 0);
    if(offset >= 0){
      offset = offset - 4;
      end = offset + 5 + 6;
      strDt = StringSubstr(tmpStr, offset, end - offset - 1);
      StringReplace(strDt, "年", ".");
      StringReplace(strDt, "月", ".");
      StringReplace(strDt, "日", "");
	}
	
	//特定日の表示部分を切り出し(tableの内側)
	offset = StringFind(tmpStr, "<table", 0);
	tmpStr = StringSubstr(tmpStr, offset);
	offset = StringFind(tmpStr, "<tr", 0);
	tmpStr = StringSubstr(tmpStr, offset);
	offset = StringFind(tmpStr, "</table>", 0);
	tmpStr = StringSubstr(tmpStr, 0, offset);
	int ix = ArraySize(_eventDt);
	offset = 0;

    // 各指標の情報を順に取得
	while(true){
       // trタグが無くなればループを抜ける
	   offset = StringFind(tmpStr, "<tr", offset);
	   if(offset < 0){
	      break;
	   }
	   
	   end = StringFind(tmpStr, "</tr>", offset);
	   string w = StringSubstr(tmpStr, offset, end - offset);
		   
	   //時刻を取得
	   int s;
	   if((s = StringFind(w, ":", 0)) >= 0){
	      ArrayResize(_eventDt, ix + 1);
	      ArrayResize(_country, ix + 1);
	      ArrayResize(_eventNm, ix + 1);
	      ArrayResize(_eventNm2, ix + 1);
	      ArrayResize(_stars, ix + 1);
	      ArrayResize(_eventNoSch, ix + 1);
	      
   	   s = s - 2;
   	   string tm = StringSubstr(w, s, 5);
   	   string strDt2 = strDt + " " + tm;
   	   _eventDt[ix] = StringToTime(strDt2);
   	   _eventNoSch[ix] = false;
   	}else if(StringFind(w, "未定", 0) >= 0){
	      ArrayResize(_eventDt, ix + 1);
	      ArrayResize(_country, ix + 1);
	      ArrayResize(_eventNm, ix + 1);
	      ArrayResize(_eventNm2, ix + 1);
	      ArrayResize(_stars, ix + 1);
 	      ArrayResize(_eventNoSch, ix + 1);


   	   string strDt2 = strDt + " " + "09:00";  //時刻が未定のイベントは9時にする
   	   _eventDt[ix] = StringToTime(strDt2);
   	   _eventNoSch[ix] = true;
   	}else{
         offset = offset + 1;
         continue;
 	   }
   	
   	   //国を取得
	   off = StringFind(w, "alt=", 0);
	   off2 = StringFind(w, "\"", off+5);
	   string t = StringSubstr(w, off+5, off2 - off - 5);
	   _country[ix] = t;
	   
	   //指標名を取得
	   off = StringFind(w, "fbd\">", 0);
	   off2 = StringFind(w, "</p>", off+5);
	   t = StringSubstr(w, off+5, off2 - off - 5);
	   _eventNm[ix] = t;
	   
	   //指標の補足を取得(前年比等)
	   off = StringFind(w, "nowrap\">", 0);
	   if(off >= 0){
   	   off2 = StringFind(w, "</p>", off+8);
   	   t = StringSubstr(w, off+8, off2 - off - 8);
   	   _eventNm2[ix] = t;
   	}else{
   	   _eventNm2[ix] = "";
   	}
	   
	   //星の数を取得
	   off = 0;
   	   _stars[ix] = 0;
   	   for(int i=0; i<5; i++){
   	      off = StringFind(w, "star-fill", off);
   	      if(off >= 0){
   	         _stars[ix]++;
   	         off++;
   	      }else{
   	         break;
   	      }
   	   }
       offset = offset + 1;
	   ix++;
	}
   return ret;

}

チャートへの表示

次の関数 showMsgLabelでは、チャートに文字列を表示する処理を行っています。日時、国名、★、指標名をそれぞれ別のラベルでチャートに表示しています。別のラベルにしているのは、1つのラベルでは、表示文字数に制限があるようで、1ラベルでは1行分の表示ができないのと、各項目の先頭位置を揃えるのに都合がよいからです。

void showMsgLabel(int textNo, string text, color clr, int lineNo, int xDistance){
   string objNm = "IndMsg" + IntegerToString(textNo);
   ObjectCreate(0, objNm+IntegerToString(lineNo),OBJ_LABEL,0,0,0);
   ObjectSetString(0, objNm+IntegerToString(lineNo), OBJPROP_TEXT, text);
   ObjectSetString(0, objNm+IntegerToString(lineNo), OBJPROP_FONT, "MS ゴシック");
   ObjectSetInteger(0, objNm+IntegerToString(lineNo), OBJPROP_FONTSIZE, 10);
   ObjectSetInteger(0, objNm+IntegerToString(lineNo), OBJPROP_COLOR, clr);
   ObjectSetInteger(0, objNm+IntegerToString(lineNo), OBJPROP_CORNER, 0);
   ObjectSetInteger(0, objNm+IntegerToString(lineNo), OBJPROP_XDISTANCE, xDistance);
   ObjectSetInteger(0, objNm+IntegerToString(lineNo), OBJPROP_YDISTANCE, 20 * lineNo);

}

次のコードでは、サイトから取得した指標情報の配列を入力として、終了したイベントの色分け、時刻未定の時刻の除外、★の編集等を行い、showMsgLabel関数により項目毎に別のラベルとしてチャートに表示しています。

//---------------------------------------
// イベント情報をチャートに表示する
//---------------------------------------
void showEventMsg(datetime& _eventDt[], string& _country[], string& _eventNm[], string& _eventNm2[], int& _stars[], bool& _eventNoSch[]){

   int y = 10;
   for(int i=0; i<ArraySize(_eventDt); i++){
   
      //文字の色
      color msgColor = MsgColor;
      if(_eventDt[i] < TimeLocal()){
         //終了したイベントの色
         msgColor = FinishedEvwntColor;
      }
      
      //日時
      string dt = TimeToString(_eventDt[i]);
      if(_eventNoSch[i]){
         //時刻が設定されていないイベント
         dt = StringSubstr(dt,0, 10);
      }else{
         dt = StringSubstr(dt,0, 16);
      }
      
      //星
      string stars = "";
      for(int j=0; j<_stars[i]; j++){
         stars = stars + "☆";
      }
      
      //イベント名
      //イベント名の先頭についている国名を除外し、補足名を後ろに付与
      string msg[];
      StringSplit(_eventNm[i], '・', msg);
      msg[1] = msg[1] + " " + _eventNm2[i];
      
      //星の色
      color starColor;
      if(_eventDt[i] < TimeLocal()){
         starColor = msgColor;
      }else{
         if(_stars[i] >= ImportantStarsNum){
            //強調色
            starColor = ImportantColor;
         }else{
            starColor = msgColor;
         }
      }
      
      //項目毎に表示(ラベルオブジェクトは表示長に限度があるため、項目毎にラベルオブジェクトで表示)
      showMsgLabel(1, dt, msgColor, i+1, 10);
      showMsgLabel(2, _country[i], msgColor, i+1, 130);
      showMsgLabel(3, stars, starColor, i+1, 220);
      showMsgLabel(4, msg[1], msgColor, i+1, 290);
      
      //最大表示件数まで
      if(i+1 >= MaxLine){
         break;
      } 
      
   }
}

コード全体

サイトからのデータ取得は、頻繁に行うのは良くないので、インジケータをチャートに適用したときと、一日に一度だけ取得するようにしています。

ここから先は

13,604字 / 2ファイル

¥ 3,000

この記事が気に入ったらチップで応援してみませんか?