ネットで見つけたパーフェクトオーダーEAのサンプルコードをそのままMetaEditorに貼り付けてコンパイルしようとしたら、「’MA_Short_Bars’ – undeclared identifier」というエラーが出た——あるいはコンパイルは通ったのに、他のEAや裁量トレードと同じ口座で動かしたら全くエントリーしなくなった。MQLでEAを自作し始めた人がよくつまずくポイントです。

原因の多くは、コードの一部(本文の断片コード)と完成版のコードで変数名が食い違っていたり、ポジションの数え方が自分のEA以外の建玉まで含めてしまっていたりすることです。この記事では、短期・中期・長期の移動平均線が同じ順序に並ぶ「パーフェクトオーダー」でエントリーするEAを例に、パラメーターの設定からOnTick()関数の中身、そして見落としがちな「ポジションの重複防止」と「損切り設定」まで、実際にMetaEditorでコンパイルしてMT4で動かせる状態のコードを最後まで解説します。

パーフェクトオーダー時にエントリーするEA

パーフェクトオーダー時にエントリーするEAを作成したいと思います。

パーフェクトオーダーとは、短期・中期・長期の移動平均線が「短期>中期>長期」(上昇トレンド)または「短期<中期<長期」(下降トレンド)の順序に並んでいる状態のことです。3本の移動平均線が同じ方向を向いているため、トレンドの継続を狙ったエントリー根拠として使われます。

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

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

「ファイル」→「新規作成」からウィザードで「Expert Advisor(テンプレートを使用)」を選び、「次へ」を押します。名前の入力画面が出るので、今回は「Perfect-Order-ea」と入力します(作者名・リンク・バージョンは空欄のままで問題ありません)。あとの画面はイベントハンドラのチェックを何も入れず「次へ」を数回押して「完了」まで進めてください。OnInit()・OnDeinit()・OnTick()の3つの関数だけが書かれた空のひな形が開くので、ここに以下のコードを追記していきます。

パラメーターを記述する

パラメーターを記述しましょう。利確・ロット数・マジックナンバーと、3本の移動平均線の期間に加えて、損切り(StopLoss)とスリッページ(Slippage)も用意します。

input int    TakeProfit      = 5;//利確(pips)
input int    StopLoss        = 10;//損切り(pips)
input double Lots            = 0.01;//ロット数
input int    MagicNumber     = 12345;//マジックナンバー
input int    Slippage        = 3;//スリッページ
input int    MA_Short_Period = 25;//短期足MA
input int    MA_Middle_Period = 50;//中期足MA
input int    MA_Long_Period  = 200;//長期足MA

損切り(StopLoss)とスリッページ(Slippage)は、後述する理由でこの記事から新しく追加したパラメーターです。

OnTick()関数を記述する

OnTick()関数を記述しましょう。まず、変数を3つ用意します。

double Short_MA,Middle_MA,Long_MA;

次にそれぞれの変数にiMA関数の値を代入していきます。ここで使う変数名は、さきほどパラメーターで定義した名前(MA_Short_Period・MA_Middle_Period・MA_Long_Period)と正確に一致させてください。名前が1文字でも違うと「undeclared identifier」というコンパイルエラーになります。

   Short_MA = iMA(NULL,0,MA_Short_Period,0,MODE_SMA,PRICE_CLOSE,1);//短期足MA
   Middle_MA = iMA(NULL,0,MA_Middle_Period,0,MODE_SMA,PRICE_CLOSE,1);//中期足MA
   Long_MA = iMA(NULL,0,MA_Long_Period,0,MODE_SMA,PRICE_CLOSE,1);//長期足MA

上昇パーフェクトオーダー(短期>中期>長期)で、かつ1本前の終値が短期移動平均線より上にある場合に、

if(Short_MA > Middle_MA && Middle_MA > Long_MA && Close[1] > Short_MA)

ロングのポジションを持ちます。「Close[1] > Short_MA」の条件は、3本のMAが並んだ直後の一瞬だけでなく、価格自体もそのトレンド方向にいることを確認するための追加条件です。

 OrderSend(Symbol(),OP_BUY,Lots,Ask,Slippage,Ask-StopLoss*Point*10,Ask+TakeProfit*Point*10,"Order Long",MagicNumber,0,Red);

ショートも同様に、下降パーフェクトオーダーで、かつ1本前の終値が短期移動平均線より下にある場合に、

if(Short_MA < Middle_MA && Middle_MA < Long_MA && Close[1] < Short_MA)

ショートのポジションを持ちます。

OrderSend(Symbol(),OP_SELL,Lots,Bid,Slippage,Bid+StopLoss*Point*10,Bid-TakeProfit*Point*10,"Order Short",MagicNumber,0,Blue);

これでエントリーのロジック自体はできましたが、このまま動かすと2つの問題が残っています。次の2つのセクションで直していきます。

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

このEAをそのまま「OrdersTotal()==0のときだけエントリーする」という条件で書いてしまうと、口座全体の注文数を見ることになります。つまり、このEAを他のEAや裁量トレードと同じ口座で同時に動かした場合、自分以外のポジションが1つでも残っていると、このEAは新規エントリーの判定自体を行いません。逆にEA単体で動かしている場合は問題になりませんが、「動いているはずなのにエントリーしない」という原因不明のトラブルにつながりやすいポイントです。

これを避けるため、口座全体の注文数ではなく、自分(このマジックナンバー)のポジションを持っているかどうかを判定する関数を用意し、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);
  }

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

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

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

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

利確だけでなく損切りも必ず設定しましょう。パラメーターに追加したStopLossを使って、エントリー方向と逆側に損切りラインを計算します(買いは Ask-StopLoss*Point*10、売りは Bid+StopLoss*Point*10)。

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

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

コンパイルする

コードを保存したら、メタエディタの「コンパイル」(Ctrl+F7)をクリックします。「0 error(s), 0 warning(s)」と表示されればコンパイル成功です。エラーが出た場合は、変数名の綴りやセミコロンの付け忘れがないか確認しましょう。

ソースコード全体

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

//+------------------------------------------------------------------+
//|                                             Perfect-Order-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 int    MA_Short_Period = 25;//短期足MA
input int    MA_Middle_Period = 50;//中期足MA
input int    MA_Long_Period  = 200;//長期足MA

//+------------------------------------------------------------------+
//| この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 Short_MA,Middle_MA,Long_MA;//変数

   Short_MA = iMA(NULL,0,MA_Short_Period,0,MODE_SMA,PRICE_CLOSE,1);//短期足MA
   Middle_MA = iMA(NULL,0,MA_Middle_Period,0,MODE_SMA,PRICE_CLOSE,1);//中期足MA
   Long_MA = iMA(NULL,0,MA_Long_Period,0,MODE_SMA,PRICE_CLOSE,1);//長期足MA

   int ticket;

   if(Short_MA > Middle_MA && Middle_MA > Long_MA && Close[1] > Short_MA)//上昇パーフェクトオーダー時
     {
      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(Short_MA < Middle_MA && Middle_MA < Long_MA && Close[1] < Short_MA)//下降パーフェクトオーダー時
     {
      ticket = OrderSend(Symbol(),OP_SELL,Lots,Bid,Slippage,Bid+StopLoss*Point*10,Bid-TakeProfit*Point*10,"Order Short",MagicNumber,0,Blue);//ショートエントリー
      if(ticket < 0)
         Print("売り注文に失敗しました。エラーコード=",GetLastError());
     }
  }
//+------------------------------------------------------------------+

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

MT4の「表示」→「ストラテジーテスター」を開き、Expert AdvisorにPerfect-Order-eaを選択、通貨ペアと期間を指定して、モデルを「全ティック(最も正確な値)」にしてスタートすると、過去データでの動作を確認できます。

修正前のコードでは、他のEAや裁量ポジションが残っている状態だとエントリーが発生しません。修正後は、自分(このマジックナンバー)のポジションの有無だけで判定されることを確認してください。

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

このEAは3本の移動平均線の並び順だけで売買する、学習用にシンプルへ作った例です。実際の口座で稼働させる前に、次の点を確認してください。

  • 3本のMAが並んだ直後にトレンドが反転すると、損切りが連続して発生しやすい
  • ロット数(Lots)は口座残高に対して過大にならないよう調整する
  • バックテストの結果は過去データに基づくものであり、将来の相場でも同じ成績になるとは限らない

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

あわせて読みたい: 【MQL】パーフェクトオーダーとMACDを組み合わせたインジケーター【上級編】

トレードでの使い方: FXの移動平均線を複数活用した最強のパーフェクトオーダーの設定・メリット・デメリットについてわかりやすく解説してみた

まとめ

これでパーフェクトオーダー時でトレードできるEAが完成しましたね。エントリーのロジックだけでなく、ポジションの重複防止と損切り設定まで含めて初めて、実運用を検討できる形になります。

さらにここに移動平均線の角度や本数を増やすことで精度が上がったりします。

関連記事