EAで経済指標日に取引しないようにする関数とサンプルソース

EA
スポンサーリンク

概要

最近、(特に重要な)経済指標発表時は値動きが簡単に50~100pipsほど動くので怖い思いをしています。EAを止めるといいのですが、バックテストではEAを止めていないだけに経済指標でEAを止めるのはちょっと迷います。

そこで今回、経済指標発表時等の特定の日程においてEAが取引しないようにする関数を作成しました。

FOMCや雇用統計等の取引停止したい日程をCSV形式のファイルからEAに取り込んで、その日(の特定時間帯)はEAで取引しないような感じです。
※日にちを直接プログラムに書くと今後の日を追加する度にEAプログラムをリコンパイルは大変なので・・・

今回はグローバル変数とか関数が複数あったり外部CSVファイルがあったりと初心者の方には少し難しいかもしれないですが、実際に私が公開しているZigZagのEAに入れ込んだサンプルソースもありますので、参考にしてみてください。

CSV形式のファイルとは?

よくある?経済指標時に取引をしない処理は、外部サイトから情報をひっぱって参照している形式がありますが、今回私が作ったのはCSV形式のファイルを参照します。このCSVファイルが経済指標を回避するための肝になるデータです。

このCSVファイルですが、カンマ区切りで区切られたファイルで誰でも簡単にメンテナンス可能です。様々なデータを取り込むのに適した形式といえます。

フォーマットはこんな感じ。シンプルに必要なデータだけとしました。

Excelで見た場合

メモ帳で見た場合

特に変わった感じではありません。要は経済指標の日付(トレードしたくない日)、時間、イベント名称をカンマ区切りで1行毎に書いていったファイルです。

この雇用統計(2012年~2022年)とFOMC(2012年~2023年)の日付が書かれたCSVファイルも参考に配布します。

“経済指標CSV” をダウンロード

ks_usdjpy.csv – 590 回のダウンロード – 6.21 KB

ダウンロードしたら下記を参考にファイルを置いてください。

この『ks_usdjpy.csv』ファイルの置き場所ですがバックテストと実際にリアルタイムで使う場合とで置く場所が違うので注意してください。

バックテストで使う場合は、MT4画面の左上「ファイル」から「データフォルダを開く」をクリックし『tester\files\』フォルダの直下においてください。

リアルタイムで動かす場合は、同じくMT4画面の左上「ファイル」から「データフォルダを開く」をクリックし『\MQL4\Files\』フォルダの直下においてください。

下準備は以上です。

変数と関数のソースコード

では、本題のソースコードです。

今回はグローバルの変数の追加と、関数を2つ追加します。

グローバル変数の追加

パラメータ設定として、読み込むCSVのファイル名称を追加しています。あとは配列や実際の判別処理で使う用の変数です。

input string csvFileName = "ks_usdjpy.csv";     // CSVファイル名

string DateArray[];   // 読み込んだデータを格納する配列(日程)
string TimeArray[];   // 読み込んだデータを格納する配列(時間)
string EventArray[];  // 読み込んだデータを格納する配列(イベント名称)

string TodayEvent = "";    //当日のイベント名称格納用
int dayBuff = 0;           //1日1回チェック用

EAのソースコードの上の方にコピペで貼り付けてください。

関数の追加

CSVファイルから(経済指標)データを読み込む関数

まず、CSVファイル内の日付データを配列に取り込みます。

プログラミングでファイル読み込みする時に、1行目を読み込むと2行目に目標が変わります。これを(ファイルの終わりまで又は)レコード数分ループすると1ファイル丸々読めるっていう流れが基本になるので、この形を覚えておくと便利です!
//+------------------------------------------------------------------+
//|【関数】CSVファイルから経済指標データを読み込む
//|
//|【引数】 なし
//|
//|【戻値】True:成功 False:失敗
//|
//|
bool funcSihyouDataSet()
{

bool kekka=false;
string dataBuff[2];   // 読み込んだデータを格納する配列(カンマ分割時)

   // ファイルを開く
   int fileHandle = FileOpen(csvFileName, FILE_READ|FILE_CSV);
   if (fileHandle == INVALID_HANDLE) {
       Print("ファイルを開けませんでした: ", csvFileName);
       return kekka;
   }
   // ファイルの行数を取得
   int lines = 0;
   while (!FileIsEnding(fileHandle)) {
       lines++;
       FileReadString(fileHandle);
   }
   FileClose(fileHandle);
   
   //ファイルを開く
   fileHandle = FileOpen(csvFileName, FILE_READ|FILE_CSV);
   
   //1行目はヘッダー部のため読み飛ばす
   FileReadString(fileHandle);
   lines = lines-1;
   
   //格納用配列のリサイズ(サイズ決めておかないと、値入れた時に空白になっちゃう)
   ArrayResize(DateArray,lines);
   ArrayResize(TimeArray,lines);
   ArrayResize(EventArray,lines);

   
   for (int i = 0; i < lines; i++) {
      //1行読む  
      string line = FileReadString(fileHandle);
       
      int a = StringSplit(line, ',', dataBuff);
       
      DateArray[i] = dataBuff[0];
      TimeArray[i] = dataBuff[1];
      EventArray[i] = dataBuff[2];

   }
   FileClose(fileHandle);

   kekka=true;
   Print("ファイルを読み込みました: ", csvFileName);
   
   return kekka;   
}

この処理はEAを立ち上げた時に1度だけ呼ばれるだけでOKなのでOnInit()等で最初に1度だけ呼び出す感じです。

経済指標データから当日のイベント日をチェックする関数

取り込んだ日付データの配列を見て、当日に該当する日があるかをチェックします。
※日程、時間、イベント名配列を作りましたが、今回時間は使いませんでした

//+------------------------------------------------------------------+
//|【関数】経済指標データから当日のイベント日をチェック
//|
//|【引数】 なし
//|
//|【戻値】True:今日はイベント日 False:今日はイベント日ではない
//|
//|
bool funcSihyouCheck()
{
string dateBuff[2];
string timeBuff[2];
bool kekka;

   kekka =false;

   // 配列サイズ分ループ
   for (int i = 0; i < ArraySize(DateArray); i++) {
   
      //日程を年月日で分割する
      StringSplit(DateArray[i], '.', dateBuff);
      
      if(StringToInteger(dateBuff[0])==Year() && StringToInteger(dateBuff[1])==Month() && StringToInteger(dateBuff[2])==Day()){

         //時間を時分秒で分割する(今の所、時間は未使用)
         StringSplit(TimeArray[i], ':', timeBuff);
      
         //イベント名称をセット
         TodayEvent = EventArray[i];
         
         kekka = true;
         break;
      }
          
   }


   return kekka;   
}

この関数も1日に1度だけ呼ばれればOKなので制御してあげる必要があります。
※値動きの度にこの処理をするとパフォーマンス悪いので

上記の関数2つをEAのソースコードの一番下等にコピペで貼り付けてください。

問題無くコンパイルできれば成功です。

関数の呼び出し方

上記2つの関数の呼び出し方についてです。

funcSihyouDataSet()関数

funcSihyouDataSet()関数は、前述した通り初回だけ実行されるinit()でやってしまいましょう。

int init()
  {
//---
   //CSVファイルから経済指標データを読み込む
   if(funcSihyouDataSet() == false){
      return(INIT_FAILED);
   }
         
//---
   return(INIT_SUCCEEDED);
  }

これで取引したくない日の一覧(CSVのデータ)がDateArray[]配列に格納されているので、あとは好きに使っていく感じです。

funcSihyouCheck()関数

funcSihyouDataSet()関数は、1日に1回だけ呼び出すだけでOKなのでこんな感じで制御してあげます。

int start()
{
  
//---
   //1日に1回チェックする
   if(Day() != dayBuff){
      dayBuff = Day();
      
      // 経済指標イベントを確認
      if(funcSihyouCheck() ==false){
         TodayEvent ="";
      }
   }

・・・

funcSihyouDataSet()関数は、当日が動かしたくない日(雇用統計やFOMCのイベント日)であればtrueを返します。

falseの場合、TodayEvent変数をクリアしていますがこの理由は次で説明します。

動かしたくない日の判別

やっとメインの動かしたくない日を判別する処理です。

前半部は多分皆さんで処理が結構違うと思います。私の場合は、orderPtn=0でエントリーしない・1以上でエントリーとしています。

なのでエントリーするかどうかの処理をした後、動かしたくない日を判別し該当する場合はorderPtn=0にしてエントリーしないようにする処理を追加しています。

   //高値切り上げ安値切り上げでロング
   if(ZigTop[0] > ZigTop[1] && ZigTop[1] > ZigTop[2] && 
      ZigBottom[0] > ZigBottom[1] && ZigBottom[1] > ZigBottom[2] && BottomPoint==1)
   {
      orderPtn=1;
   }
   //高値切り下げ安値切り下げでショート 
   else if(ZigTop[0] < ZigTop[1] && ZigTop[1] < ZigTop[2] &&
      ZigBottom[0] < ZigBottom[1] && ZigBottom[1] < ZigBottom[2] && TopPoint==1)
   {
      orderPtn=2;
   }
   else
   {
      orderPtn=0;
   }

   if(TodayEvent!=""){
      orderPtn=0;
      Print("今日は" + TodayEvent + "なのでトレードしません");
            
   }

18行目でTodayEvent変数が空白ではなかった場合(当日が動かしたくない日の場合)、orderPtn=0にして問答無用でその日はエントリーをしない日にしています。

こんな感じでTodayEvent変数で動かしたくない日かどうかを判別しているので、もし前日が動かしたくない日で、当日が動かしてもいい日だった場合はTodayEvent変数はクリアする必要があります。

なので、funcSihyouDataSet()関数でfalseだった場合はTodayEvent変数をクリアしています。

動かしたくない日でかつ時間も見たい

動かしたくない日だと結構アバウトなのでさらに踏み込んで時間まで見たい場合はこんな感じにします。

   //高値切り上げ安値切り上げでロング
   if(ZigTop[0] > ZigTop[1] && ZigTop[1] > ZigTop[2] && 
      ZigBottom[0] > ZigBottom[1] && ZigBottom[1] > ZigBottom[2] && BottomPoint==1)
   {
      orderPtn=1;
   }
   //高値切り下げ安値切り下げでショート 
   else if(ZigTop[0] < ZigTop[1] && ZigTop[1] < ZigTop[2] &&
      ZigBottom[0] < ZigBottom[1] && ZigBottom[1] < ZigBottom[2] && TopPoint==1)
   {
      orderPtn=2;
   }
   else
   {
      orderPtn=0;
   }

   if(TodayEvent!=""){
      if(TodayEvent=="雇用統計"){
         //雇用統計の場合、事前8時間と事後1時間30分はトレードしない
         if(Hour() >= 7 && Hour() <= 16){
            orderPtn=0;
            Print("7時から17時までは" + TodayEvent + "なのでトレードしません");
            
         }
      }

      if(TodayEvent=="FOMC"){
         //FOMCの場合、事前8時間と事後2時間はトレードしない
         if(Hour() >= 13 && Hour() <= 22){
            orderPtn=0;
            Print("13時から23時までは" + TodayEvent + "なのでトレードしません");
         }
      }      
   }

雇用統計は(国内FX業者の)MT4上では15:30に発表されるため、事前8時間前と事後1時間半はトレードしないとする場合、こんな感じにします。

FOMCは(国内FX業者の)MT4上では21:00に発表されるため、事前8時間前と事後2時間はトレードしないとイベント毎に分けたりも出来ます。

修正の具体例

では、現在私が公開している以下記事のZigZag-MAXのソースコードに今回の処理を適用してみます。

関数の追加の仕方とか、あまり修正内容がわからない方は参考にしてみてください。

修正箇所①

まずは、ZigZag-MAXのソースコードの上の方に変数を追加します。

//+------------------------------------------------------------------+
//|                                             ZigZag_NanpinLot.mq4 |
//|                                    Copyright 2020, mef Software. |
//|                                             https://fx-prog.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, mef Software."
#property link      "https://fx-prog.com/"
#property version   "1.01"
          
input int E_Depth = 16;
input int E_Deviation = 5;
input int E_Backstep = 3;
       
input int STOP_LOSS = 400;
input int TP = 100;
input int A_SPREAD = 10;
  
input double Lots = 0.01;
input string csvFileName = "ks_usdjpy.csv";     // CSVファイル名

string DateArray[];   // 読み込んだデータを格納する配列(日程)
string TimeArray[];   // 読み込んだデータを格納する配列(時間)
string EventArray[];  // 読み込んだデータを格納する配列(イベント名称)

string TodayEvent = "";    //当日のイベント名称格納用
int dayBuff = 0;           //1日1回チェック用

double ZigTop[5];     //ジグザグの山保存用
double ZigBottom[5];  //ジグザグの谷保存用
int TopPoint;
int BottomPoint;
       

修正箇所②

次に、ソースコードの一番下に今回の関数2つを追加します。

   //目視確認用コメント
   Comment(
          "│ ZigTOP0=", ZigTop[0], "│ ZigBTM0=", ZigBottom[0], "|\n",
          "│ ZigTOP1=", ZigTop[1], "│ ZigBTM1=", ZigBottom[1], "|\n",
          "│ ZigTOP2=", ZigTop[2], "│ ZigBTM2=", ZigBottom[2], "|\n",
          "│ ZigTOP3=", ZigTop[3], "│ ZigBTM3=", ZigBottom[3], "|\n",
          "│ TopP=", TopPoint, "│ BottomP=", BottomPoint, "|\n"
   );
             
          
   return(kekka);   
         
}

//+------------------------------------------------------------------+
//|【関数】CSVファイルから経済指標データを読み込む
//|
//|【引数】 なし
//|
//|【戻値】True:成功 False:失敗
//|
//|
bool funcSihyouDataSet()
{

bool kekka=false;
string dataBuff[2];   // 読み込んだデータを格納する配列(カンマ分割時)

   // ファイルを開く
   int fileHandle = FileOpen(csvFileName, FILE_READ|FILE_CSV);
   if (fileHandle == INVALID_HANDLE) {
       Print("ファイルを開けませんでした: ", csvFileName);
       return kekka;
   }
   // ファイルの行数を取得
   int lines = 0;
   while (!FileIsEnding(fileHandle)) {
       lines++;
       FileReadString(fileHandle);
   }
   FileClose(fileHandle);
   
   //ファイルを開く
   fileHandle = FileOpen(csvFileName, FILE_READ|FILE_CSV);
   
   //1行目はヘッダー部のため読み飛ばす
   FileReadString(fileHandle);
   lines = lines-1;
   
   //格納用配列のリサイズ(サイズ決めておかないと、値入れた時に空白になっちゃう)
   ArrayResize(DateArray,lines);
   ArrayResize(TimeArray,lines);
   ArrayResize(EventArray,lines);

   
   for (int i = 0; i < lines; i++) {
      //1行読む  
      string line = FileReadString(fileHandle);
       
      int a = StringSplit(line, ',', dataBuff);
       
      DateArray[i] = dataBuff[0];
      TimeArray[i] = dataBuff[1];
      EventArray[i] = dataBuff[2];

   }
   FileClose(fileHandle);

   kekka=true;
   Print("ファイルを読み込みました: ", csvFileName);
   
   return kekka;   
}

//+------------------------------------------------------------------+
//|【関数】経済指標データから当日のイベント日をチェック
//|
//|【引数】 なし
//|
//|【戻値】True:今日はイベント日 False:今日はイベント日ではない
//|
//|
bool funcSihyouCheck()
{
string dateBuff[2];
string timeBuff[2];
bool kekka;

   kekka =false;

   // 配列サイズ分ループ
   for (int i = 0; i < ArraySize(DateArray); i++) {
   
      //日程を年月日で分割する
      StringSplit(DateArray[i], '.', dateBuff);
      
      if(StringToInteger(dateBuff[0])==Year() && StringToInteger(dateBuff[1])==Month() && StringToInteger(dateBuff[2])==Day()){

         //時間を時分秒で分割する(今の所、時間は未使用)
         StringSplit(TimeArray[i], ':', timeBuff);
      
         //イベント名称をセット
         TodayEvent = EventArray[i];
         
         kekka = true;
         break;
      }
          
   }


   return kekka;   
}

この状態で1度コンパイルをしてみましょう。

問題無くコンパイルできるはずなのでコンパイルできなかった場合はコピペ等が正常に出来てない可能性があるので見直しましょう。

修正箇所③

次に、ソースコードの上の方『int init()』の所に処理を追加します。

double maisuu=0.01;
datetime prevtime;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
//---
   //CSVファイルから経済指標データを読み込む
   if(funcSihyouDataSet() == false){
      return(INIT_FAILED);
   }
//---
   return(INIT_SUCCEEDED);
  }
           
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
int start()
{

ここでもコンパイルは成功するのでコンパイルしてみましょう。コンパイルできなかったら見直しましょう。

修正箇所④

次に、ソースコードの真ん中らへんの『int start()』に処理を追加します。

int start()
{
int orderPtn=0; //0:何もしない 1:買い 2:売り
int total=0;
           
double ea_order_stop_price=0,ea_order_good_price=0; //ストップロスレート,利確レート,エントリーレート
bool OrderKekka;
          
//---
   //1日に1回チェックする
   if(Day() != dayBuff){
      dayBuff = Day();
      
      // 経済指標イベントを確認
      if(funcSihyouCheck() ==false){
         TodayEvent ="";
      }
   }

   //新しい足ができた時だけやる
   if(Time[0] != prevtime){
      prevtime = Time[0];
   }else{
      return(0);
   }
           
//***売買判断箇所***//
          
   //チェック用変数の初期化
   TopPoint=0;
   BottomPoint=0;   
       
   //ZigZagの高値安値を構造体に再セット
   getZigZag(1);
       

ここでもコンパイルをして確認しましょう!

修正箇所⑤

最後に、先ほど修正した『int start()』のもう少し下に処理を追加します。

int start()
{
int orderPtn=0; //0:何もしない 1:買い 2:売り
int total=0;
           
double ea_order_stop_price=0,ea_order_good_price=0; //ストップロスレート,利確レート,エントリーレート
bool OrderKekka;

//---
   //1日に1回チェックする
   if(Day() != dayBuff){
      dayBuff = Day();
      
      // 経済指標イベントを確認
      if(funcSihyouCheck() ==false){
         TodayEvent ="";
      }
   }

   //新しい足ができた時だけやる
   if(Time[0] != prevtime){
      prevtime = Time[0];
   }else{
      return(0);
   }
           
//***売買判断箇所***//
          
   //チェック用変数の初期化
   TopPoint=0;
   BottomPoint=0;   
       
   //ZigZagの高値安値を構造体に再セット
   getZigZag(1);
       
       
   //高値切り上げ安値切り上げでロング
   if(ZigTop[0] > ZigTop[1] && ZigTop[1] > ZigTop[2] && 
      ZigBottom[0] > ZigBottom[1] && ZigBottom[1] > ZigBottom[2] && BottomPoint==1)
   {
      orderPtn=1;
   }
   //高値切り下げ安値切り下げでショート 
   else if(ZigTop[0] < ZigTop[1] && ZigTop[1] < ZigTop[2] &&
      ZigBottom[0] < ZigBottom[1] && ZigBottom[1] < ZigBottom[2] && TopPoint==1)
   {
      orderPtn=2;
   }
   else
   {
      orderPtn=0;
   }

   if(TodayEvent!=""){
      if(TodayEvent=="雇用統計"){
         //雇用統計の場合、事前8時間と事後1時間30分はトレードしない
         if(Hour() >= 7 && Hour() <= 16){
            orderPtn=0;
            Print("7時から17時までは" + TodayEvent + "なのでトレードしません");
            
         }
      }

      if(TodayEvent=="FOMC"){
         //FOMCの場合、事前8時間と事後2時間はトレードしない
         if(Hour() >= 13 && Hour() <= 22){
            orderPtn=0;
            Print("13時から23時までは" + TodayEvent + "なのでトレードしません");
         }
      }      
   }

//***売買判断箇所***//
           
       
//***決済判断箇所***//
   total=OrdersTotal();
   maisuu = Lots;

コンパイルが成功したら完成です!

修正は以上です。上記の修正を適用した全体ソースコードはこんな感じです。

//+------------------------------------------------------------------+
//|                                             ZigZag_NanpinLot.mq4 |
//|                                    Copyright 2020, mef Software. |
//|                                             https://fx-prog.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, mef Software."
#property link      "https://fx-prog.com/"
#property version   "1.01"
          
input int E_Depth = 16;
input int E_Deviation = 5;
input int E_Backstep = 3;
       
input int STOP_LOSS = 400;
input int TP = 100;
input int A_SPREAD = 10;
  
input double Lots = 0.01;
input string csvFileName = "ks_usdjpy.csv";     // CSVファイル名

string DateArray[];   // 読み込んだデータを格納する配列(日程)
string TimeArray[];   // 読み込んだデータを格納する配列(時間)
string EventArray[];  // 読み込んだデータを格納する配列(イベント名称)

string TodayEvent = "";    //当日のイベント名称格納用
int dayBuff = 0;           //1日1回チェック用
       
double ZigTop[5];     //ジグザグの山保存用
double ZigBottom[5];  //ジグザグの谷保存用
int TopPoint;
int BottomPoint;
       
double maisuu=0.01;
datetime prevtime;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
//---
   //CSVファイルから経済指標データを読み込む
   if(funcSihyouDataSet() == false){
      return(INIT_FAILED);
   }
//---
   return(INIT_SUCCEEDED);
  }
           
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
int start()
{
int orderPtn=0; //0:何もしない 1:買い 2:売り
int total=0;
           
double ea_order_stop_price=0,ea_order_good_price=0; //ストップロスレート,利確レート,エントリーレート
bool OrderKekka;

//---
   //1日に1回チェックする
   if(Day() != dayBuff){
      dayBuff = Day();
      
      // 経済指標イベントを確認
      if(funcSihyouCheck() ==false){
         TodayEvent ="";
      }
   }

   //新しい足ができた時だけやる
   if(Time[0] != prevtime){
      prevtime = Time[0];
   }else{
      return(0);
   }
           
//***売買判断箇所***//
          
   //チェック用変数の初期化
   TopPoint=0;
   BottomPoint=0;   
       
   //ZigZagの高値安値を構造体に再セット
   getZigZag(1);
       
       
   //高値切り上げ安値切り上げでロング
   if(ZigTop[0] > ZigTop[1] && ZigTop[1] > ZigTop[2] && 
      ZigBottom[0] > ZigBottom[1] && ZigBottom[1] > ZigBottom[2] && BottomPoint==1)
   {
      orderPtn=1;
   }
   //高値切り下げ安値切り下げでショート 
   else if(ZigTop[0] < ZigTop[1] && ZigTop[1] < ZigTop[2] &&
      ZigBottom[0] < ZigBottom[1] && ZigBottom[1] < ZigBottom[2] && TopPoint==1)
   {
      orderPtn=2;
   }
   else
   {
      orderPtn=0;
   }

   if(TodayEvent!=""){
      if(TodayEvent=="雇用統計"){
         //雇用統計の場合、事前8時間と事後1時間30分はトレードしない
         if(Hour() >= 7 && Hour() <= 16){
            orderPtn=0;
            Print("7時から17時までは" + TodayEvent + "なのでトレードしません");
            
         }
      }

      if(TodayEvent=="FOMC"){
         //FOMCの場合、事前8時間と事後2時間はトレードしない
         if(Hour() >= 13 && Hour() <= 22){
            orderPtn=0;
            Print("13時から23時までは" + TodayEvent + "なのでトレードしません");
         }
      }      
   }

//***売買判断箇所***//
           
       
//***決済判断箇所***//
   total=OrdersTotal();
   maisuu = Lots;
   
   //ナンピン処理
   if(total > 0 && orderPtn > 0){ 
      OrderKekka = OrderSelect(total-1,SELECT_BY_POS,MODE_TRADES);
      if(OrderType()==OP_BUY && orderPtn == 2){
         orderPtn =0;
      }else if(OrderType()==OP_SELL && orderPtn == 1){
         orderPtn =0;
      }
      if(OrderType()==OP_BUY && (OrderOpenPrice() - 40 * Point < Ask)){
         orderPtn =0;
      }else if(OrderType()==OP_SELL && (OrderOpenPrice() + 40 * Point  > Bid)){
         orderPtn =0;
      } 
    
      //既にナンピンを4回している場合は追加ナンピンしない
      if(total>3){
         orderPtn =0;
      }else{
         if(orderPtn !=0){
            maisuu = (Lots + Lots) * OrdersTotal();
            if(maisuu==(Lots + Lots) * 3){
   
               //3つ目のポジションのSLの値と建値の値を取得する
               OrderKekka = OrderSelect(2,SELECT_BY_POS,MODE_TRADES);              
               //2つ目のポジションのSLとTPを変更する(SL:3つ目のポジションのSLに変更、 TP:3つ目のポジションの建値に変更)                          
               OrderKekka = funcOrder_Modify(OrderStopLoss(),OrderOpenPrice(),1);  
   
   
               //再度3つ目のポジションのSLの値と建値の値を取得する
               OrderKekka = OrderSelect(2,SELECT_BY_POS,MODE_TRADES);
               //3つ目のポジションのSLとTPを変更する(SL:3つ目のポジションのSLに変更、 TP:3つ目のポジションの建値に変更)           
               OrderKekka = funcOrder_Modify(OrderStopLoss(),OrderOpenPrice(),2);
   
                  
            }
         }
       }
   }
    
   if(orderPtn > 0)   
   {
      if(orderPtn == 1)
      {
         ea_order_stop_price = Ask - STOP_LOSS * Point;
         ea_order_good_price = Ask + TP * Point;          
      }
      else if(orderPtn == 2)
      {
         ea_order_stop_price = Bid + STOP_LOSS * Point;  
         ea_order_good_price = Bid - TP * Point;  
      }   
          
      //新規注文
      OrderKekka = funcOrder_Send(orderPtn - 1,ea_order_stop_price,ea_order_good_price,0,0);
          
   }
             
   return(0);
}
          
//+------------------------------------------------------------------+
//|【関数】新規注文関数                                                 |
//|                                                                  |
//|【引数】 ea_order_entry_Type:売買(0:買 1:売)                         |
//|【引数】 ea_order_stop_price:損切値  ea_order_good_price:利確値      |
//|【引数】 orderComment:オーダーコメント    ea_order_MagicNo:マジックNo       |
//|                                                                  |
//|【戻値】True:成功                                                   |
//|                                                                  |
//| 
bool funcOrder_Send(int ea_order_entry_Type, double ea_order_stop_price, double ea_order_good_price,int orderComment,int ea_order_MagicNo)
{
           
   int order_resend_num;        // エントリー試行回数
   int ea_ticket_res;           // チケットNo
   int errorcode;               // エラーコード
   double ea_order_entry_price; // エントリーレート
   color order_Color;
   bool kekka;
              
   for( order_resend_num = 0; order_resend_num < 10; order_resend_num++ ) {    // エントリー試行回数上限:10回
           
      if(MarketInfo(NULL,MODE_SPREAD) < A_SPREAD ){
           
         if(ea_order_entry_Type == OP_BUY){   
            ea_order_entry_price = Ask;               // 現在の買値でエントリー
            order_Color = clrBlue;
            Print(Ask+"でロングするよ!");            
         }else if(ea_order_entry_Type == OP_SELL){        
            ea_order_entry_price = Bid;               // 現在の売値でエントリー
            order_Color = clrRed;
            Print(Bid+"でショートするよ!");                       
         }
           
         // FXCMでは新規エントリー時にストップ/リミットを設定出来ない。
         ea_ticket_res = OrderSend(   // 新規エントリー注文
            NULL,                 // 通貨ペア
            ea_order_entry_Type,      // オーダータイプ[OP_BUY / OP_SELL]
            maisuu ,                    // ロット[0.01単位](FXTFは1=10Lot)
            ea_order_entry_price,     // オーダープライスレート
            20,                       // スリップ上限    (int)[分解能 0.1pips]
            ea_order_stop_price,      // ストップレート
            ea_order_good_price,      // リミットレート
            orderComment,             // オーダーコメント
            ea_order_MagicNo,         // マジックナンバー(識別用)
            0,                        // オーダーリミット時間
            order_Color               // オーダーアイコンカラー
            );
  
         Print("結果:"+ea_ticket_res);
  
         if ( ea_ticket_res == -1) {            // オーダーエラー
            errorcode = GetLastError();      // エラーコード取得
              
            if( errorcode != ERR_NO_ERROR) { // エラー発生
               printf("エラー");
            }
              
            Sleep(2000);                                           // 1000msec待ち
            RefreshRates();                                        // レート更新
              
            printf("再エントリー要求回数:%d, 更新エントリーレート:%g",order_resend_num+1 ,ea_order_entry_price);
              
         } else {    // 注文約定
            Print("新規注文約定。 チケットNo=",ea_ticket_res," レート:",ea_order_entry_price);
            Sleep(300);                                           // 300msec待ち(オーダー要求頻度が多過ぎるとエラーになる為)
                          
            break;
         }
  
              
              
      }else{
         Print("スプレッドNG 許容スプレッド:"+A_SPREAD+"pips未満 環境スプレッド:"+MarketInfo(NULL,MODE_SPREAD)+"pips");
         Sleep(2000);                                           // 2000msec待ち
         RefreshRates();     
      }
                       
  
   }
   return kekka;   
}
//+------------------------------------------------------------------+
//|【関数】トレール(変更注文)関数
//|
//|【引数】 order_stop_price:損切値  order_good_price:利確値  cnt:何個目のポジションを変更するかの値
//|
//|【戻値】True:成功 False:失敗
//|
//|    
bool funcOrder_Modify(double order_stop_price, double order_good_price,int cnt)
{
   bool kekka,OrderKekka;
   int total,order_resend_num,errorcode;
   
   kekka=false;
      
   for( order_resend_num = 0; order_resend_num < 20; order_resend_num++ ) {   
      
      total=OrdersTotal();
         
      if(total >0 && OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES)==true){
         OrderKekka = OrderModify(OrderTicket(),OrderOpenPrice(),order_stop_price,order_good_price,0,clrYellow);
   
         if(OrderKekka==false){
            errorcode = GetLastError();
            
            Print("変更注文失敗 エラーNo=",errorcode," リトライ回数=",order_resend_num+1," SL=",order_stop_price,"TP=",order_good_price);
               
               
            //損切り・利確値が不正な場合は微調整する
            if(errorcode==ERR_NO_RESULT || errorcode==ERR_INVALID_STOPS){
               if(OrderType() == OP_BUY){
                  order_stop_price = order_stop_price - 5 * Point;
                  order_good_price = order_good_price + 5 * Point;
               }else if(OrderType() == OP_SELL){
                  order_stop_price = order_stop_price + 5 * Point;
                  order_good_price = order_good_price - 5 * Point;            
               }
            }
               
            RefreshRates();
            Sleep(5000);
         }else{
            Print("変更注文完了。 SL=",order_stop_price," TP=",order_good_price);
            
            kekka=true;
            break;
         }
      }
   }
      
   return kekka;   
}
   
//+------------------------------------------------------------------+
//|【関数】ジグザグの値をセット
//|
//|
//|【備考】なし
//+------------------------------------------------------------------+
bool getZigZag(int i)
{
       
int n,m,t;
       
bool kekka;
       
   m=0;
   t=0;
       
          
   kekka=false;
       
   for(n=i;n<=i+200;n++)
   {
      //ZigZagの値を取得
      double Zg=NormalizeDouble(iCustom(NULL,0,"ZigZag",E_Depth,E_Deviation,E_Backstep,0,n),5);
       
      //ZigZagの値と最高値が同じ場合、頂点なのでZigTopにセット      
      if(Zg!=0 && Zg==NormalizeDouble(High[n],5))
      {
         if(n==1){
            TopPoint=1;
         }
                
         ZigTop[m++]=Zg;
         if(m>=4)break;
      }
             
      //ZigZagの値と最安値が同じ場合、底なのでZigBottomにセット            
      if(Zg!=0 && Zg==NormalizeDouble(Low[n],5))
      {
         if(n==1){
            BottomPoint=1;
         }
                      
         ZigBottom[t++]=Zg;
         if(t>=4)break;
      }
   }
       
   kekka=true;
          
       
   //目視確認用コメント
   Comment(
          "│ ZigTOP0=", ZigTop[0], "│ ZigBTM0=", ZigBottom[0], "|\n",
          "│ ZigTOP1=", ZigTop[1], "│ ZigBTM1=", ZigBottom[1], "|\n",
          "│ ZigTOP2=", ZigTop[2], "│ ZigBTM2=", ZigBottom[2], "|\n",
          "│ ZigTOP3=", ZigTop[3], "│ ZigBTM3=", ZigBottom[3], "|\n",
          "│ TopP=", TopPoint, "│ BottomP=", BottomPoint, "|\n"
   );
             
          
   return(kekka);   
         
}

//+------------------------------------------------------------------+
//|【関数】CSVファイルから経済指標データを読み込む
//|
//|【引数】 なし
//|
//|【戻値】True:成功 False:失敗
//|
//|
bool funcSihyouDataSet()
{

bool kekka=false;
string dataBuff[2];   // 読み込んだデータを格納する配列(カンマ分割時)

   // ファイルを開く
   int fileHandle = FileOpen(csvFileName, FILE_READ|FILE_CSV);
   if (fileHandle == INVALID_HANDLE) {
       Print("ファイルを開けませんでした: ", csvFileName);
       return kekka;
   }
   // ファイルの行数を取得
   int lines = 0;
   while (!FileIsEnding(fileHandle)) {
       lines++;
       FileReadString(fileHandle);
   }
   FileClose(fileHandle);
   
   //ファイルを開く
   fileHandle = FileOpen(csvFileName, FILE_READ|FILE_CSV);
   
   //1行目はヘッダー部のため読み飛ばす
   FileReadString(fileHandle);
   lines = lines-1;
   
   //格納用配列のリサイズ(サイズ決めておかないと、値入れた時に空白になっちゃう)
   ArrayResize(DateArray,lines);
   ArrayResize(TimeArray,lines);
   ArrayResize(EventArray,lines);

   
   for (int i = 0; i < lines; i++) {
      //1行読む  
      string line = FileReadString(fileHandle);
       
      int a = StringSplit(line, ',', dataBuff);
       
      DateArray[i] = dataBuff[0];
      TimeArray[i] = dataBuff[1];
      EventArray[i] = dataBuff[2];

   }
   FileClose(fileHandle);

   kekka=true;
   Print("ファイルを読み込みました: ", csvFileName);
   
   return kekka;   
}

//+------------------------------------------------------------------+
//|【関数】経済指標データから当日のイベント日をチェック
//|
//|【引数】 なし
//|
//|【戻値】True:今日はイベント日 False:今日はイベント日ではない
//|
//|
bool funcSihyouCheck()
{
string dateBuff[2];
string timeBuff[2];
bool kekka;

   kekka =false;

   // 配列サイズ分ループ
   for (int i = 0; i < ArraySize(DateArray); i++) {
   
      //日程を年月日で分割する
      StringSplit(DateArray[i], '.', dateBuff);
      
      if(StringToInteger(dateBuff[0])==Year() && StringToInteger(dateBuff[1])==Month() && StringToInteger(dateBuff[2])==Day()){

         //時間を時分秒で分割する(今の所、時間は未使用)
         StringSplit(TimeArray[i], ':', timeBuff);
      
         //イベント名称をセット
         TodayEvent = EventArray[i];
         
         kekka = true;
         break;
      }
          
   }


   return kekka;   
}

これで経済指標発表に手動で止めたりしなくて良くなります!

実際に動かしてみる

実際に動かしてみます。まずしっかりCSVファイルが読み込まれているか確認します。

以下のような感じでファイルが開けなかった旨のメッセージが出ているとCSVファイルは読み込まれていないので、『ファイルが適切な場所にあるか』『ファイル名称が合っているか』『Excelで開きっぱなしにしてロックされていないか』等を確認しEAの再起動が必要です。

こんな感じでメッセージが出ていればCSVファイルの読み込みは成功です。

読み込みが成功すると、当該時間になった場合はトレードしない旨のメッセージを表示しています。

さいごに

以上が、『EAで経済指標日に取引しないようにする関数とサンプルソース』です。

冒頭でも説明した通り、最近の経済指標は恐ろしいので経済指標系の処理を作ってみました。ちなみに、CSVデータ取り込みの処理はChatGPTさんに手伝ってもらいました。

今回ダウンロードできるCSVは2012年~2023年3月までの雇用統計とFOMCの日程しか書いていないので、これ以降の未来のイベントや他にも経済指標を除外したい場合はお好みでCSVに追加してみてください。

※ MT4・EAが使えるFX会社のおすすめ

※ EAのサンプルソースを一覧表にまとめました


コメント