エラー/リトライを考慮した新規・変更・決済関数(処理)

EA
スポンサーリンク

MT4のEAで、エラーやリトライをある程度考慮した新規・変更・決済注文関数を作りました。

私がFXでEA運用しているリアル口座でも使っているものなので、ポジションを複数持たないEAであればこの関数でほぼほぼ問題無くトレードできます。

あと、新規・決済注文時にはスプレッドの開きも確認しています。

ご自身のFX口座の仕様に合わせる必要があるかと思いますが、もしよかったら使ってみてください。
※EAのかなり大事な部分の処理なので、ご利用は自己責任でお願いします!

主な使用関数

MarketInfo();

マーケット情報を返す。

MarketInfo(Symbol(),MODE_SPREAD)で取引通貨のスプレッドを返します。

OrderSend(); OrderModify(); OrderClose();

言わずと知れた、新規注文、変更注文、決済注文の関数を使用しています。

ソースコード

新規注文、変更注文、決済注文の自作関数です。

あと、エントリーや決済に矢印がチャート上に付きますが、ロングは青系、ショートは赤系の矢印、SL/TPに引っかかった場合は黄色系の矢印にしています。

//+------------------------------------------------------------------+
//|【関数】新規注文関数
//|
//|【引数】 ea_order_entry_Type:売買(0:買 1:売)
//|【引数】 ea_order_stop_price:損切値  ea_order_good_price:利確値
//|【引数】 orderComment:オーダーコメント    ea_order_MagicNo:マジックNo
//|
//|【戻値】True:成功 False:失敗
//|
//|
bool funcOrder_Send(int ea_order_entry_Type, double ea_order_stop_price, double ea_order_good_price,string orderComment,int ea_order_MagicNo,int entrySpread)
{
    
   int order_resend_num;        // エントリー試行回数
   int ea_ticket_res;           // チケットNo
   double ea_order_entry_price; // エントリーレート
   color order_Color;
   bool kekka;
    
   kekka=false;
    
/////////ロット数確認領域/////////
    
   double Lot = 1;   // ロット[0.01単位]
   //double Lot = funcLotGet(200,10),  // ロット[0.01単位](証拠金200%維持、FXTFは最大10Lot)
/////////ロット数計算領域/////////
    
    
   for( order_resend_num = 0; order_resend_num < 10; order_resend_num++ ) {    // エントリー試行回数上限:10回
    
      //スプレッドがentrySpread未満の場合のみ新規注文する(2銭未満にしたい場合は20にする)
      if(MarketInfo(NULL,MODE_SPREAD) < entrySpread){
    
         if(ea_order_entry_Type == OP_BUY){   
            ea_order_entry_price = Ask;               // 現在の買値でエントリー
            order_Color = clrBlue;
         }else if(ea_order_entry_Type == OP_SELL){        
            ea_order_entry_price = Bid;               // 現在の売値でエントリー
            order_Color = clrRed;            
         }
    
         ea_ticket_res = OrderSend(   // 新規エントリー注文
            NULL,                     // 通貨ペア
            ea_order_entry_Type,      // オーダータイプ[OP_BUY / OP_SELL]
            Lot,                      // ロット数
            ea_order_entry_price,     // オーダープライスレート
            20,                       // スリッページ
            ea_order_stop_price,      // ストップレート
            ea_order_good_price,      // リミットレート
            orderComment,             // オーダーコメント
            ea_order_MagicNo,         // マジックナンバー
            0,                        // オーダーリミット時間
            order_Color               // オーダーアイコンカラー
            );
             
         if ( ea_ticket_res == -1) { 
            Print("エントリー失敗 エラーNo=",GetLastError()," リトライ回数=",order_resend_num+1);
                
            Sleep(5000);                                           // msec待ち
            RefreshRates();                                        // レート更新
       
         } else {    // 注文約定
            Print("新規注文約定。 レート:",ea_order_entry_price," チケットNo=",ea_ticket_res," オーダーマジックNo=",ea_order_MagicNo);
                
            Sleep(500);                                           // 500msec待ち
            kekka=true;
            break;
         }
    
      }else{
          Print("スプレッド拡大中(銭)=",MarketInfo(NULL,MODE_SPREAD) / 10," リトライ回数=",order_resend_num+1);
              
          Sleep(5000);                                           // msec待ち
          RefreshRates();                                        // レート更新
      }
    
   }
   return kekka;   
}
//+------------------------------------------------------------------+
//|【関数】トレール(変更注文)関数
//|
//|【引数】 ea_order_stop_price:損切値  ea_order_good_price:利確値
//|
//|【戻値】True:成功 False:失敗
//|
//|    
bool funcOrder_Modify(double ea_order_stop_price, double ea_order_good_price)
{
   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(0,SELECT_BY_POS,MODE_TRADES)==true){
         OrderKekka = OrderModify(OrderTicket(),OrderOpenPrice(),ea_order_stop_price,ea_order_good_price,0,clrYellow);
    
         if(OrderKekka==false){
            errorcode = GetLastError();
             
            Print("変更注文失敗 エラーNo=",errorcode," リトライ回数=",order_resend_num+1," SL=",ea_order_stop_price,"TP=",ea_order_good_price);
                
                
            //損切り・利確値が不正な場合は微調整する
            if(errorcode==ERR_INVALID_STOPS){
               if(OrderType() == OP_BUY){
                  ea_order_stop_price = ea_order_stop_price - 10 * Point;
                  ea_order_good_price = ea_order_good_price + 10 * Point;
               }else if(OrderType() == OP_SELL){
                  ea_order_stop_price = ea_order_stop_price + 10 * Point;
                  ea_order_good_price = ea_order_good_price - 10 * Point;            
               }
            }
                
            RefreshRates();
            Sleep(5000);
         }else{
            Print("変更注文完了。 SL=",ea_order_stop_price," TP=",ea_order_good_price);
             
            kekka=true;
            break;
         }
      }
   }
       
   return kekka;   
}
    
//+------------------------------------------------------------------+
//|【関数】決済関数
//|
//|【引数】 kessaiSpread:許容する決済時のスプレッド
//|
//|【戻値】True:成功 False:失敗
//|
//|
bool funcOrder_Close(int kessaiSpread)
{
   bool kekka,OrderKekka;
   int total,order_resend_num;
   double ea_order_entry_price; // エントリーレート
   color order_Color;
       
   kekka=false;
    
   for(order_resend_num = 0; order_resend_num < 100; order_resend_num++ ) {
      if(MarketInfo(NULL,MODE_SPREAD) < kessaiSpread){
    
         total=OrdersTotal();
             
         if(total >0 && OrderSelect(0,SELECT_BY_POS,MODE_TRADES)==true){
    
            if(OrderType() == OP_BUY){   
               ea_order_entry_price = Bid;               // 現在の売値で決済
               order_Color = clrLightBlue;
            }else if(OrderType() == OP_SELL){
               ea_order_entry_price = Ask;               // 現在の買値で決済
               order_Color = clrLightPink;            
            }
    
            OrderKekka = OrderClose(OrderTicket(),OrderLots(),ea_order_entry_price,10,order_Color);
    
            if(OrderKekka==false){
               Print("決済失敗 エラーNo=",GetLastError()," リトライ回数=",order_resend_num+1);
                   
               RefreshRates();
               Sleep(1000);
            }else{
               Print("決済注文約定。 レート=",ea_order_entry_price);
                
               kekka=true;
               break;
            }
         }
      }else{
          Print("スプレッド拡大中(銭)=",MarketInfo(NULL,MODE_SPREAD) / 10," リトライ回数=",order_resend_num+1);
              
          Sleep(5000);                                           // msec待ち
          RefreshRates();                                        // レート更新
      }
   }
   return kekka;   
}

解説

funcOrder_Send()関数

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

引数

引数1(int:ea_order_entry_Type)は買い注文か、売り注文かです。[OP_BUY / OP_SELL]を渡す形でOKです。

引数2(double:ea_order_stop_price)は損切値です。

引数3(double:ea_order_good_price)は利確値です。変数名はTake Profitだとなんかしっくりこないので、goodにしています。

引数4(string:orderComment)はコメントです。

引数5(int:ea_order_MagicNo)はマジックナンバーです。

引数6(int:entrySpread)は許容するエントリー時のスプレッドです。20とした場合、スプレッド幅が2銭未満であればエントリーします。雇用統計時等5銭以上スプレッド幅が開く時のエントリーを回避できます。

戻り値

戻り値は、True:成功 False:失敗になります。

処理内容

最大10回のリトライを行い、新規注文を行います。

リトライが発生するのは、スプレッドが引数のentrySpread値を超えている場合 または 新規注文に失敗した場合です。リトライは5秒間隔で行います。

あと、ロット数も引数で渡すように考えたのですがロット数変えるのはここだけ!というのを決めたかったので関数内(///ロット数確認領域///)にしています。

ロット数の所で、1ヵ所コメント化されていますが、以下の記事の内容です。証拠金維持率に応じたロット数になるよう制御したい人向けです。

funcOrder_Modify()関数

【使用例】
bool Kekka = funcOrder_Modify(109.600,109.800);

引数

引数1(double:ea_order_stop_price)は損切値です。

引数2(double:ea_order_good_price)は利確値です。

戻り値

戻り値は、True:成功 False:失敗になります。

処理内容

最大20回のリトライを行い、変更注文を行います。

リトライが発生するのは、変更注文に失敗した場合です。リトライは5秒間隔で行い、失敗時のエラーコードが130だった場合、損切り・利確値ともに0.1銭広げます。

funcOrder_Close()関数

【使用例】
bool Kekka = funcOrder_Close(10);

引数

引数1(int:kessaiSpread)は許容する決済時のスプレッドです。20とした場合、スプレッド幅が2銭未満であれば決済します。雇用統計時等5銭以上スプレッド幅が開く時の決済を回避できます。

戻り値

戻り値は、True:成功 False:失敗になります。

処理内容

最大100回のリトライを行い、決済注文を行います。

リトライが発生するのは、スプレッドが引数のkessaiSpread値を超えている場合 または 決済注文に失敗した場合です。スプレッドが引数の値を超えていた場合は5秒間隔でリトライを行います。また、決済注文に失敗した場合は1秒間隔でリトライを行います。

さいごに

以上で、MT4のEAで、エラーやリトライをある程度考慮した新規・変更・決済注文関数の説明を終わります。

どのEAでも使いそうな処理は関数化して、一元管理しておくと便利ですね。

あと、最終的にインクルードで別ファイル(mqh)ファイルで管理したらEAのソースが整理されるのでソースの可視化に繋がります!以下の記事に詳細を記載しています。



※ オススメ記事(EAが使える国内FX業者の一覧)

コメント

  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という感じでグローバル変数とローカル変数が被ってますみたいな注意メッセージが出ると思います。

      • トシ より:

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