【MQL4・MT4】EAで使う新規・変更・決済注文のサンプル関数|リトライ&エラー処理付き

EAサンプル・ノウハウ
記事内に商品プロモーションを含む場合があります
スポンサーリンク

はじめに

EA開発において 「新規注文・変更注文・決済注文」 の処理は必ず必要になります。
しかし、実際に書いてみると「エラーで通らなかった」「スプレッド拡大中に約定してしまった」といった問題に直面する方も多いのではないでしょうか。

この記事では、私がリアル口座でも利用している エラーリトライ対応の注文処理関数 をサンプルとして紹介します。
当サイトに掲載している移動平均線やZIGZAG等の各EAにも、売買処理部分にそのまま流用していますので、ぜひ参考にしてください。


📜 ソースコード

以下は、新規注文・変更注文・決済注文をまとめた自作関数です。
(関数単体で使えるようになっているので、必要なEAにそのままコピペ可能です)

//+------------------------------------------------------------------+
//|【関数】新規注文関数
//|
//|【引数】 orderType:売買(0=BUY, 1=SELL)
//|【引数】 sl:損切り価格  tp:利確価格
//|【引数】 lots:取引ロット数
//|【引数】 orderComment:オーダーコメント
//|【引数】 magic:マジックNo
//|【引数】 maxSpreadPips: 許容スプレッド[Point] (0=制限なし)
//|
//|【戻値】true=成功 false=失敗
//+------------------------------------------------------------------+
bool funcOrder_Send(int orderType, double sl, double tp, double lots,
                    string orderComment, int magic, int maxSpreadPoints)
{
   int    ticket = -1;
   double price  = 0;
   color  clr    = clrNONE;
   double spread = 0;

   // 最大10回リトライ
   for(int retry=0; retry<10; retry++)
   {
      RefreshRates();
      
      // スプレッド取得(MT4表示と同じポイント単位)
      spread = MarketInfo(Symbol(), MODE_SPREAD);

      // スプレッドチェック(maxSpreadPoints=0 の場合はスキップ)
      if(maxSpreadPoints > 0 && spread > maxSpreadPoints)
      {
         Print("スプレッド拡大中(", spread,
               "ポイント) → 待機中 (リトライ=", retry+1, ")");
         Sleep(2000);
         continue;
      }

      // エントリー価格と色を決定
      if(orderType == OP_BUY){
         price = Ask;
         clr   = clrBlue;
      }else if(orderType == OP_SELL){
         price = Bid;
         clr   = clrRed;
      }

      ticket = OrderSend(Symbol(), orderType, lots, price,
                         20, sl, tp, orderComment, magic, 0, clr);

      if(ticket > 0)
      {
         Print("新規注文成功! チケットNo=", ticket,
               " レート=", price, " Lots=", lots,
               " スプレッド=", spread, "ポイント");
         return true;
      }
      else
      {
         int err = GetLastError();
         Print("新規注文失敗 エラーNo=", err,
               " リトライ回数=", retry+1);
         Sleep(2000);
      }
   }

   return false;
}
 
//+------------------------------------------------------------------+
//|【関数】変更注文関数                                               |
//|                                                                  |
//|【引数】 ticket: 修正対象のチケットNo                              |
//|【引数】 sl: 新しい損切り価格                                      |
//|【引数】 tp: 新しい利確価格                                        |
//|【引数】 maxSpreadPoints: 許容スプレッド[Point] (0=制限なし)        |
//|                                                                  |
//|【戻値】true=成功 false=失敗                                       |
//+------------------------------------------------------------------+
bool funcOrder_Modify(int ticket, double sl, double tp, int maxSpreadPoints)
{
   double spread = 0;
   bool   result = false;

   // 注文選択
   if(!OrderSelect(ticket, SELECT_BY_TICKET)){
      Print("funcOrder_Modify: OrderSelect失敗 ticket=", ticket,
            " エラー=", GetLastError());
      return false;
   }
   //通貨が違う場合は変更しない
   if(OrderSymbol() != Symbol()){
       Print("funcOrder_Modify: シンボル不一致 ", OrderSymbol(), "≠", Symbol());
       return false;
   }

   // 最大10回リトライ
   for(int retry=0; retry<10; retry++)
   {
      RefreshRates();

      // スプレッド取得(MT4表示と同じポイント単位)
      spread = MarketInfo(Symbol(), MODE_SPREAD);

      // スプレッドチェック(maxSpreadPoints=0 の場合はスキップ)
      if(maxSpreadPoints > 0 && spread > maxSpreadPoints){
         Print("スプレッド拡大中(", spread,
               "ポイント) → 修正待機中 (リトライ=", retry+1, ")");
         Sleep(2000);
         continue;
      }

      // 注文修正
      result = OrderModify(ticket,
                           OrderOpenPrice(),  // 価格は変更しない
                           sl,                // 新しい損切り
                           tp,                // 新しい利確
                           0,                 // 有効期限(変更しない)
                           clrNONE);

      if(result){
         Print("変更注文成功! チケットNo=", ticket,
               " 新SL=", sl, " 新TP=", tp,
               " スプレッド=", spread, "ポイント");
         return true;
      }else{
         int err = GetLastError();
         Print("変更注文失敗 ticket=", ticket,
               " エラーNo=", err,
               " リトライ回数=", retry+1);
         Sleep(2000);
      }
   }

   return false;
}
    
//+------------------------------------------------------------------+
//|【関数】全決済関数                                                 |
//|                                                                  |
//|【引数】 orderType : -1=すべて  0=BUYのみ  1=SELLのみ             |
//|【引数】 magic: 対象マジック番号 (-1=全マジック対象)               |
//|【引数】 maxSpreadPoints: 許容スプレッド[Point] (0=制限なし)       |
//|                                                                  |
//|【戻値】true=全成功 false=一部失敗あり                             |
//+------------------------------------------------------------------+
bool funcOrder_CloseAll(int orderType, int magic, int maxSpreadPoints)
{
   bool allSuccess = true;
   int totalOrders = OrdersTotal();
   double spread, closePrice, lots;
   int type, ticket;
   color  clr    = clrNONE;

   for(int i = totalOrders-1; i >= 0; i--)
   {
      //オーダーセレクト失敗でスキップ
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
      
      // 同一シンボル以外はスキップ
      if(OrderSymbol() != Symbol()) continue;

      // マジック番号指定がある場合は一致しないものをスキップ
      if(magic != -1 && OrderMagicNumber() != magic) continue;

      type   = OrderType();
      ticket = OrderTicket();
      lots   = OrderLots();

      // orderType指定でフィルタ
      if(orderType == 0 && type != OP_BUY) continue;
      if(orderType == 1 && type != OP_SELL) continue;

      // 最大10回リトライ
      bool result = false;
      for(int retry=0; retry<10; retry++)
      {
         RefreshRates();

         // スプレッド取得
         spread = MarketInfo(Symbol(), MODE_SPREAD);

         if(maxSpreadPoints > 0 && spread > maxSpreadPoints)
         {
            Print("スプレッド拡大中(", spread,
                  "ポイント) → クローズ待機中 ticket=", ticket,
                  " リトライ=", retry+1);
            Sleep(2000);
            continue;
         }

         // クローズ価格決定
         if(type == OP_BUY){
            closePrice = Bid;
            clr   = clrBlue;
         }else if(type == OP_SELL){
            closePrice = Ask;
            clr   = clrRed;
         }

         result = OrderClose(ticket, lots, closePrice, 20, clr);

         if(result){
            Print("クローズ成功! チケットNo=", ticket,
                  " ロット=", lots, " クローズ価格=", closePrice,
                  " スプレッド=", spread, "ポイント");
            break;
         }else{
            int err = GetLastError();
            Print("クローズ失敗 ticket=", ticket,
                  " エラーNo=", err,
                  " リトライ=", retry+1);
            Sleep(2000);
         }
      }

      if(!result) allSuccess = false;
   }

   return allSuccess;
}

スリッページは20固定にしています。

各関数の詳細はこの後に解説していきます。

スポンサーリンク

funcOrder_Send()関数 ― 新規注文

【使用例】
bool Kekka = funcOrder_Send(OP_BUY,109.500,109.700,0.01,"テストオーダー",777,20);

引数

  • orderType : 注文種別 [OP_BUY / OP_SELL]
  • sl : 損切値
  • tp : 利確値
  • lots : ロット
  • orderComment : コメント
  • magic : マジックナンバー
  • maxSpreadPoints : 許容スプレッド

許容スプレッドについては、以下赤枠の数値で考えて下さい。赤枠の数値 > maxSpreadPointsで注文します。
※以降、変更注文/決済注文も同様です

また、maxSpreadPointsを0にすることでスプレッドをチェックしなくなります。

戻り値

  • True: 成功
  • False: 失敗

処理内容

引数で渡した情報を元に新規注文を行います。

  • 最大10回リトライ・・・for(int retry=0; retry<10; retry++)
  • スプレッドがmaxSpreadPointsを超えている場合や注文失敗時にリトライ
  • 2秒間隔で再試行・・・Sleep(2000)

funcOrder_Modify()関数 ― 変更注文

【使用例】
// 例: 現在の通貨ペアの全注文を取得してSL/TP変更
for(int i = 0; i < OrdersTotal(); i++)
{
// 注文を選択(位置指定)
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
// 自EAのポジションだけに絞る場合はマジック番号をチェック
// if(OrderMagicNumber() != MAGIC_NUMBER) continue;

// 買いポジションだけに絞る場合は種別をチェック
// if(OrderType() != OP_BUY) continue;

int ticket = OrderTicket();
double newSL = OrderOpenPrice() - 50 * Point; // 例: 現在価格から50ポイント下に損切り
double newTP = OrderOpenPrice() + 100 * Point; // 例: 現在価格から100ポイント上に利確
int maxSpread = 0; // スプレッドチェックはしない

// 変更関数呼び出し
bool result = funcOrder_Modify(ticket, newSL, newTP, maxSpread);

if(result)
Print("チケットNo ", ticket, " のSL/TP変更成功");
else
Print("チケットNo ", ticket, " のSL/TP変更失敗");
}
}

対象マジックナンバー(パラメータ設定:MAGIC_NUMBER) チェックや、買い注文のみの参考コードの入れていますので、使用したい場合はコメントを解除してご利用ください。

引数

  • ticket: 修正対象のチケットNo
  • sl: 新しい損切り価格
  • tp: 新しい利確価格
  • maxSpreadPoints : 許容スプレッド

戻り値

  • True: 成功
  • False: 失敗

処理内容

引数で渡した情報を元に、TP/SLの変更を行います。

  • 最大10回リトライ・・・for(int retry=0; retry<10; retry++)
  • スプレッドがmaxSpreadPointsを超えている場合や注文失敗時にリトライ
  • 2秒間隔で再試行・・・Sleep(2000)

funcOrder_Close()関数 ― 決済注文

【使用例】
bool Kekka = funcOrder_Close(-1,-1,20);

引数

  • orderType : -1=すべて 0=BUYのみ 1=SELLのみ
  • magic : マジックナンバー
  • maxSpreadPoints: 許容スプレッド

orderTypeについては、-1で全てのポジションを決済、0で買いポジションのみ、1で売りポジションのみとなります。

magicについても、-1で全てのポジション対象、番号指定でそのマジックナンバーのポジションのみ決済となります。

戻り値

  • True: 成功
  • False: 失敗

処理内容

orderTypemagicを元に、ポジションの決済を行います。

  • 最大10回リトライ・・・for(int retry=0; retry<10; retry++)
  • スプレッドがmaxSpreadPointsを超えている場合や注文失敗時にリトライ
  • 2秒間隔で再試行・・・Sleep(2000)

🛠️ 主な使用関数

EA開発でよく使う注文関連関数の詳細です。引数や戻り値、注意点もまとめています。

関数名概要引数戻り値使用例注意点・補足
OrderSend()新規注文を出す symbol : 通貨ペア
cmd : 注文種別(OP_BUY / OP_SELL)
volume : ロット数
price : 注文価格(Buy=Ask, Sell=Bid)
slippage : 許容スリッページ(ポイント)
stoploss : 損切価格
takeprofit : 利確価格
comment : 注文コメント
magic : マジック番号
expiration : 有効期限(0=無期限)
arrow_color : 注文矢印色
成功: チケット番号
失敗: -1
int ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 20, 109.500, 109.700, "テスト", 777, 0, clrBlue);
失敗時は GetLastError() で原因取得。スプレッドが広い場合は注文が通らないことがある
OrderModify()既存注文を修正する ticket : 対象チケット番号
price : 新規価格(通常はOrderOpenPrice())
stoploss : 新しい損切価格
takeprofit : 新しい利確価格
expiration : 有効期限
arrow_color : 矢印色
true: 成功
false: 失敗
bool result = OrderModify(ticket, OrderOpenPrice(), newSL, newTP, 0, clrNONE);
OrderSelect()でチケット選択済みが前提。失敗時はGetLastError()で確認
OrderClose()ポジションを決済する ticket : 決済対象チケット番号
lots : ロット数
price : 決済価格(Buy=Bid, Sell=Ask)
slippage : 許容スリッページ
color : 矢印色
true: 成功
false: 失敗
bool ok = OrderClose(ticket, 0.1, Bid, 20, clrRed);
複数ポジションを閉じる場合はOrdersTotal()でループ。失敗時はリトライ処理が望ましい
MarketInfo()通貨ペアの情報取得 symbol : 通貨ペア
mode : 情報種別(例: MODE_SPREAD, MODE_BID, MODE_ASKなど)
情報の数値
double spread = MarketInfo(Symbol(), MODE_SPREAD);
スプレッドチェックや価格取得時に利用
OrderSelect()注文を選択する index : チケット番号または配列インデックス
select : 選択方法(SELECT_BY_TICKET / SELECT_BY_POS)
pool : 取得対象(MODE_TRADES=現在ポジション, MODE_HISTORY=履歴)
true: 成功
false: 失敗
if(OrderSelect(ticket, SELECT_BY_TICKET)) { … }
選択に失敗するとOrderModifyやOrderCloseがエラーになるので必須
GetLastError()エラー番号取得なしエラー番号(int)
int err = GetLastError();
注文失敗時やリトライ処理時に活用

この関数を利用したEAサンプル記事

本記事の注文処理関数は、当サイト全てのEAサンプル記事で利用しています。
EAロジック部分と組み合わせて使うことで、実戦的なEAを構築可能です。


さいごに

以上、MT4のEAで使える 新規注文・変更注文・決済注文のリトライ対応関数 を紹介しました。

EA開発では、売買ロジック(インジケータ判定)よりも、この「注文処理の安定化」が最初の壁になることが多いです。
どのEAでも共通で利用できる部分は関数化して一元管理しておくと効率的です。

さらに整理するなら、.mqhファイルにまとめてインクルード管理するのがおすすめです。

安定した注文処理を実装しつつ、オリジナルEA開発にぜひ活用してみてください。


✅ 他にも多数のEAサンプルを公開中!

今回紹介した内容以外にも、当サイトではさまざまなFX自動売買EAのサンプルコードを提供しています。

自分に合った戦略のEAを見つけたい方は、ぜひチェックしてみてください。


EA開発初心者向けに、今後も実践的なMQL4関数を紹介していきます。
気になる機能やロジックがあれば、ぜひ他の記事もあわせてご覧ください!

コメント

  1. トシ より:

    はじめまして、初心者です、サンプルプログラムで
    マジックナンバーを外部パラメーターに表示させるには、
    どうしたらいいて゜しょうか、ただ(input int ea_order_MagicNo = 1234;)を追加すると
    グローバル変数がどうとかメッセージが出てしまいます、直接書き込むには
    (ea_order_MagicNo=1234, // マジックナンバー(識別用)) のみで良いのでしょうか、
    ご教授ください。

    • りょう りょう より:

      はじめまして!以下のようにしてみるのはどうでしょうか?

      ①まず宣言部で名前を変える
      input int ea_MagicNo = 1234;

      ②funcOrder_Send関数のマジックナンバーの所はこんな感じ。
      funcOrder_Send(orderPtn – 1,ea_order_stop_price,ea_order_good_price,ea_MagicNo,0);

      変数の名前が同じだとdeclaration of ‘ea_order_MagicNo’ hides global variableという感じでグローバル変数とローカル変数が被ってますみたいな注意メッセージが出ると思います。

      • トシ より:

        こんなに速く教えていただきとても感謝しています、
        内容が理解できました、なるべくりょう様に甘えないようにしますが、
        ここが一番のサイトなのでまたどうしてもの時よろしくお願いします、
        ありがとうございました。