このEAのサンプルコードをそのままMetaEditorに貼り付けると、実はコンパイルの時点でエラーが出て動かせません。コンパイルが通る状態に直しても、ポジションの重複防止と損切り設定が抜けているため、そのままでは実運用できない状態でした。

この記事では、経済指標の発表が集中しやすい月曜・金曜の夜にオーダーを止めるEAを例に、コンパイルエラーの直し方から、実際にMT4で動かせる状態までを解説します。

月曜日と金曜日の夜にオーダーをストップさせるEA

よくトレーダーで経済指標を気にする方がいます。

重要な経済指標というのはやはり米国のものが多いです。

今回は、

  • 月曜日と金曜日
  • 21時以降

という条件で米国時間の指標の前にオーダーストップするEAを作成したいと思います。

メタエディタ(MetaEditor)を立ち上げる

MT4を起動し、ツールバーの黄色いボタン(またはツール→MetaQuotes Language Editor)からメタエディタ(MetaEditor)を立ち上げましょう。

新しいEAを作るので、メタエディタの「ファイル」→「新規作成」を選び、ウィザードで「Expert Advisor(テンプレートを使用)」を選択して「次へ」を押します。

名前を入力する画面が出るので、今回は「Week-Time-EA」と入力します。あとの画面はイベントハンドラのチェックを何も入れず、そのまま「次へ」を数回押して「完了」まで進めてください。

パラメーターを記述する

パラメーターを記述しましょう。

サーバー時間で計算されています。

サーバー時間はMT4の業者によって異なります。XMであれば、

  • 夏:日本時間から7時間前
  • 冬:日本時間から6時間前

今回は21時以降は停止させたいので、夏であれば「14」で冬であれば「15」となります。XM以外の業者を使っている場合は、MT4の「表示」→「気配値表示」を右クリックし、チャートに表示される時刻とご自身の時計を見比べれば、サーバー時間と日本時間の差を確認できます。

/*パラメーター*/
input int    TakeProfit         = 5;//利確(pips)
input int    StopLoss           = 10;//損切り(pips)
input double Lots               = 0.01;//ロット数
input int    MagicNumber        = 12345;//マジックナンバー
input int    Slippage           = 3;//スリッページ
input bool   Weekend_Stop       = true;//trueならStopする
input int    Stop_Day_Of_Week1  = 1;//(0:日曜日 1:月曜日 2:火曜日 3:水曜日 4:木曜日 5:金曜日 6:土曜日)
input int    Stop_Day_Of_Week2  = 5;//(0:日曜日 1:月曜日 2:火曜日 3:水曜日 4:木曜日 5:金曜日 6:土曜日)
input int    Stop_Hours         = 14;//何時からストップさせるか。指定時間はサーバー時刻
Copy

StopLossとSlippageは、後述する理由でこの記事から新しく追加したパラメーターです。

OnTick()関数を記述する

今回は過去に作成したゴールデンクロス・デッドクロスのロジックを活用しましょう。

     double kakoMA50,kakoMA100;//過去MA50,過去MA100
     double genMA50,genMA100;//現在MA50,現在MA100
     
     kakoMA50 = iMA(NULL,0,50,0,MODE_SMA,PRICE_CLOSE,1);//1つ前の50日線
     kakoMA100 = iMA(NULL,0,100,0,MODE_SMA,PRICE_CLOSE,1);//1つ前の100MA
     genMA50 = iMA(NULL,0,50,0,MODE_SMA,PRICE_CLOSE,0);//現在の50MA
     genMA100= iMA(NULL,0,100,0,MODE_SMA,PRICE_CLOSE,0);//現在の100MA

     int NowTime = TimeHour(TimeCurrent());//現在の時刻
     bool OrderStop = false;
     if(Weekend_Stop==true && NowTime>=Stop_Hours)//Weekend_StopがONで指定時刻以降である場合
     {
     	if(DayOfWeek()== Stop_Day_Of_Week1 || DayOfWeek()== Stop_Day_Of_Week2)//月曜日か金曜日
     	{
     		OrderStop = true;//オーダーストップ
     	}
     }

これでオーダーを止めるかどうかの判定はできましたが、このまま動かすと3つの問題が残っています。次のセクションで順番に直していきます。

未定義の識別子を直す

元のコードにはbool OderStop = False;という行がありました。MQL4は大文字と小文字を区別する言語で、真偽値を表す予約語は小文字のtruefalseだけです。先頭が大文字のFalseは未定義の識別子として扱われ、コンパイル時に「’False’ – undeclared identifier」というエラーになり、この時点でコンパイルが通りません。

Falsefalseに直しています。あわせて、変数名のOderStopも本来は「Order(注文)Stop」のつもりだったはずなので、誤字を直してOrderStopとしています。

ポジションを重複して持たないようにする

ゴールデンクロス・デッドクロスの条件は、条件が成立している間(次にクロスするまでの数時間〜数日)ずっと真のままです。ポジションを持っているかどうかのチェックがないと、その間ずっと新規エントリーを送り続けてしまい、証拠金が尽きるまで同じ方向のポジションを増やし続ける結果になります。

これを防ぐため、まず自分(このマジックナンバー)のポジションをすでに持っているかどうかを判定する関数を用意し、OnTick()の先頭でチェックします。

bool HasPosition()
  {
   for(int i=OrdersTotal()-1; i>=0; i--)
     {
      if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) continue;
      if(OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber)
         return(true);
     }
   return(false);
  }
Copy

OnTick()の先頭に以下の1行を追加するだけで、ポジションを持っている間は新規エントリーの判定自体をスキップできます。

if(HasPosition())
   return;//既にポジションがあれば新規エントリーしない

損切り(ストップロス)を設定する

元のOrderSend()は、6番目の引数(損切り価格)が0になっていました。0を指定すると損切りは設定されず、逆行しても決済されないまま含み損が膨らみ続けます。

利確だけでなく損切りも必ず設定しましょう。パラメーターに追加したStopLossを使って、エントリー方向と逆側に損切りラインを計算します。

//買いの場合
Ask-StopLoss*Point*10

//売りの場合
Bid+StopLoss*Point*10

※Pointは通貨ペアの最小価格単位です。多くのFX業者が採用している5桁表示のブローカーでは1pip=10Pointになるため、pips単位で指定したTakeProfit・StopLossに10を掛けています。4桁表示のブローカーを使う場合は、この「×10」を外してください。

※このEAをそのまま動かして「エラーコード130(Invalid stops)」がログに出た場合、注文が失敗する原因のほとんどはStopLoss・TakeProfitの値が近すぎることです。ブローカーごとに決まっている最小ストップ距離(ストップレベル)より値が小さいと発注に失敗するので、エラー130が出た場合はまずTakeProfit・StopLossの値を大きくしてみてください。

コンパイルする

すべて書き終えたら、メニューの「コンパイル」(Ctrl+F7)を実行しましょう。「0 error(s), 0 warning(s)」と表示されれば成功です。動きを確認できたら完了です。

ソースコード全体

ここまでの修正をすべて反映した、実際にMetaEditorでコンパイルしてそのまま動かせる状態のコードです。

//+------------------------------------------------------------------+
//|                                            Week-Time-Stop-EA.mq4 |
//|                     Copyright 2020, FX-EA System Project Creator |
//|                        https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, FX-EA System Project Creator"
#property link      "https://creator.fx-ea-system-project.com/"
#property version   "1.01"
#property strict

/*パラメーター*/
input int    TakeProfit         = 5;//利確(pips)
input int    StopLoss           = 10;//損切り(pips)
input double Lots               = 0.01;//ロット数
input int    MagicNumber        = 12345;//マジックナンバー
input int    Slippage           = 3;//スリッページ
input bool   Weekend_Stop       = true;//trueならStopする
input int    Stop_Day_Of_Week1  = 1;//(0:日曜日 1:月曜日 2:火曜日 3:水曜日 4:木曜日 5:金曜日 6:土曜日)
input int    Stop_Day_Of_Week2  = 5;//(0:日曜日 1:月曜日 2:火曜日 3:水曜日 4:木曜日 5:金曜日 6:土曜日)
input int    Stop_Hours         = 14;//何時からストップさせるか。指定時間はサーバー時刻

//+------------------------------------------------------------------+
//| このEA(マジックナンバー)のポジションを保有しているか判定する      |
//+------------------------------------------------------------------+
bool HasPosition()
  {
   for(int i=OrdersTotal()-1; i>=0; i--)
     {
      if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) continue;
      if(OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber)
         return(true);
     }
   return(false);
  }

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   //既にポジションを持っている場合は新規エントリーしない
   if(HasPosition())
      return;

   double kakoMA50,kakoMA100;//過去MA50,過去MA100
   double genMA50,genMA100;//現在MA50,現在MA100

   kakoMA50 = iMA(NULL,0,50,0,MODE_SMA,PRICE_CLOSE,1);//1つ前の50日線
   kakoMA100 = iMA(NULL,0,100,0,MODE_SMA,PRICE_CLOSE,1);//1つ前の100MA
   genMA50 = iMA(NULL,0,50,0,MODE_SMA,PRICE_CLOSE,0);//現在の50MA
   genMA100= iMA(NULL,0,100,0,MODE_SMA,PRICE_CLOSE,0);//現在の100MA

   int NowTime = TimeHour(TimeCurrent());//現在の時刻
   bool OrderStop = false;
   if(Weekend_Stop==true && NowTime>=Stop_Hours)//Weekend_StopがONで指定時刻以降である場合
     {
      if(DayOfWeek()==Stop_Day_Of_Week1 || DayOfWeek()==Stop_Day_Of_Week2)//月曜日か金曜日
        {
         OrderStop = true;//オーダーストップ
        }
     }

   int ticket;

   if(kakoMA50<=kakoMA100 && genMA50>genMA100 && OrderStop==false)//ゴールデンクロス時にOrderStopがfalseの場合
     {
      ticket = OrderSend(Symbol(),OP_BUY,Lots,Ask,Slippage,Ask-StopLoss*Point*10,Ask+TakeProfit*Point*10,"Order Long",MagicNumber,0,Red);//ロングエントリー
      if(ticket < 0)
         Print("買い注文に失敗しました。エラーコード=",GetLastError());
     }

   if(kakoMA50>=kakoMA100 && genMA50Copy

バックテストで動作を確認する

コードを保存したら、MetaEditorの「コンパイル」(Ctrl+F7)で「0 error(s), 0 warning(s)」になることを確認します。

そのあとMT4の「表示」→「ストラテジーテスター」を開き、Expert Advisorに作成したWeek-Time-EAを選択、通貨ペアと期間を指定して、モデルを「全ティック(最も正確な値)」にしてスタートすると、過去データでの動作を確認できます。月曜・金曜の指定時刻以降にエントリーが発生していないことを確認してください。

実運用する前に確認しておきたいこと

このEAを実際の口座で稼働させる前に、次の点を確認してください。

  • Stop_Hoursはサーバー時刻基準です。使っているFX業者のサーバー時刻と、夏時間・冬時間の切り替えを確認してから値を設定してください(値が合っていないと、止めたい時間帯にオーダーが止まりません)
  • ロット数(Lots)は口座残高に対して過大にならないよう調整する
  • バックテストの結果は過去データに基づくものであり、将来の相場でも同じ成績になるとは限らない

まずはデモ口座、または少額のロットでのフォワードテスト(リアルタイムでの動作確認)を行ってから、本番運用を検討してください。

まとめ

このソースコードを改良すれば、どのようなロジックにでも指標の時間帯はエントリーしないようになります。

とても応用が効くソースコードなのでぜひ改良して活用してみてください。

関連記事