手軽にマーチンゲール法をEAに取り入れる事ができる関数

スーツ姿のデグーマウスがMT4 EAのコードを指しながら解説しているイラスト。ぷろぐらむFXのデフォルトアイキャッチ。
EAサンプル・ノウハウ
記事内に商品プロモーションを含む場合があります
スポンサーリンク

今回はMT4の自動売買用EAサンプルソース(FX用)を、プログラミング初心者の方向けに解説しています。

はじめに

※本記事は、マーチンゲール手法をEAロジックとして実装するための プログラミング教材・検証記録です。
長期運用や資金管理を保証するものではありません。

前回、マーチンゲール法を使ったEAを作成したところ、
想定以上に興味深い検証結果が得られました。

そこで、手軽に展開する事ができるようにマーチンゲール法の関数を作りました。

ソースに設定、変数、関数をコピペするだけで簡単にマーチンゲール法が実現できるようになります。

マーチンゲール法を試してみたい方は是非ご活用下さい。
※ナンピン&マーチン型ではなく1つのポジションで過去の履歴をみて次回トレードに反映させる型です

マーチンゲール法を使った結果の例

マーチンゲール法を使うとあまり全然成績の良くないEAが右肩上がりのEAに化けます。こちらのマーチンゲール法適用前と適用後のバックテスト結果をご覧ください。

  • 手軽にマーチンゲール法をEAに取り入れる事ができる関数
  • 手軽にマーチンゲール法をEAに取り入れる事ができる関数

勝率も33%のEAで最大10Lot縛りですが、こんな感じで変わってきます。

⚠️ 注意:この関数は「マーチンゲールの仕組み」を示すためのサンプルです

マーチンゲールは、
ロット制御・段数制限・相場条件を適切に設計しなければ、 EAとしては簡単に破綻します。

「なぜマーチンEAが壊れるのか」 「成立させるには何を設計すべきか」については、 以下の記事で検証結果ベースで詳しく解説しています。

※本記事の関数は、上記の設計思想を理解した上で ロジックの一部として組み込むことを前提としています。

追加するソースコード

取引履歴からマーチンゲールを適用したロット数を計算する関数です。

① 追加するパラメータ(上部に追記)

//=== マーチン設定 ===//
input bool   UseMartingale = true;   // マーチン使用
input double MartinFactor  = 2.0;    // ロット倍率
input int    MaxMartinStep = 5;      // 最大連敗数

② マーチン用の管理変数を追加(上部に追記 ※グローバル)

int martinStep = 0;             // 現在の連敗数
int lastHistoryTicket = -1;     // 最後に見たチケット

③ 「前回トレード結果」を判定する関数を追加(末尾に追記)

//+------------------------------------------------------------------+
//| 前回トレード結果からマーチン段数を更新                           |
//+------------------------------------------------------------------+
void UpdateMartingaleStep()
{
   for(int i = OrdersHistoryTotal() - 1; i >= 0; i--)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) continue;
      if(OrderSymbol() != Symbol()) continue;
      if(OrderMagicNumber() != 777) continue;

      // 既に処理済みなら何もしない
      if(OrderTicket() == lastHistoryTicket)
         return;

      lastHistoryTicket = OrderTicket();

      if(OrderProfit() > 0)
         martinStep = 0;
      else
         martinStep++;

      if(martinStep > MaxMartinStep)
         martinStep = MaxMartinStep;

      return;
   }
}

※本サンプルでは
「OrderProfit() が 0 以下の場合はすべて負け扱い」
としてマーチン段数を進めています。

実運用では、
・手数料
・スワップ
・部分決済
などを考慮した判定に調整してください。

④ ロット計算用関数を追加(末尾に追記)

//+------------------------------------------------------------------+
//| マーチン後ロット計算                                             |
//+------------------------------------------------------------------+
double GetTradeLots()
{
   if(!UseMartingale) return Lots;

   double tradeLots = Lots * MathPow(MartinFactor, martinStep);

   // ブローカー最小ロット対策
   double minLot = MarketInfo(Symbol(), MODE_MINLOT);
   if(tradeLots < minLot) tradeLots = minLot;

   return NormalizeDouble(tradeLots, 2);
}

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

スポンサーリンク

修正するソースコード

では、現在私が公開している以下記事のアリゲーターのソースコードに今回のマーチンゲール法を適用してみます。

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

まずは、「追加するソースコード」を追加した後、以下の修正を加えていきます。

①エントリー前に「マーチン段数更新」を入れる

// ポジションがない場合はエントリー判定
if(total == 0){

   // ★ここで追加
   UpdateMartingaleStep();

②呼び出し元でロットを決定

      if(ea_order_Type > -1){

         double tradeLots = Lots;

         if(UseMartingale){
            tradeLots = Lots * MathPow(MartinFactor, martinStep);
         }
         funcOrder_Send(ea_order_Type,ea_order_stop_price,ea_order_good_price,tradeLots,Ea_Name,777,0);
      }

修正は以上です。

ソースコード全体

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

//+------------------------------------------------------------------+
//|                                                 05_Alligator.mq4 |
//|                                Copyright © 2020-2025 ぷろぐらむFX |
//|                                             https://fx-prog.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2020-2025 ぷろぐらむFX"
#property link      "https://fx-prog.com/"
#property version   "1.00"
#property strict


input double Lots = 0.01;              //ロット
input int TP = 100;                    //利確(pips)
input int SL = 100;                    //損切り(pips)
//***【パラメータ設定↓】アリゲーター***//
input int Jaw_Period   = 13;           // ジョーズ(青線)期間
input int Jaw_Shift    = 8;            // ジョーズ(青線)シフト

input int Teeth_Period = 8;            // ティース(赤線)期間
input int Teeth_Shift  = 5;            // ティース(赤線)シフト

input int Lips_Period  = 5;            // リップス(緑線)期間
input int Lips_Shift   = 3;            // リップス(緑線)シフト
//***【パラメータ設定↑】アリゲーター***//
//=== マーチン設定 ===//
input bool   UseMartingale = true;   // マーチン使用
input double MartinFactor  = 2.0;    // ロット倍率
input int    MaxMartinStep = 5;      // 最大連敗数

int martinStep = 0;             // 現在の連敗数
int lastHistoryTicket = -1;     // 最後に見たチケット


string Ea_Name = "Alligator";          //オーダーコメントに使用

double PipValue;                       //ブローカー桁数に対応した Point 補正
datetime prevtime;                     //ローソク足時間保持用
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   //デジットチェック
   if (Digits == 3 || Digits == 5){
      PipValue = Point * 10;   // 例: EURUSD 1.23456 → 1pip = 0.00010
   }else{
      PipValue = Point;        // 例: USDJPY 123.45 → 1pip = 0.01
   }

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

}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   int total = OrdersTotal();    //ポジショントータル数をセット  
   int ea_order_Type = -1; //0:OP_BUY  1:OP_SELL
   double ea_order_stop_price = 0; //ストップロスレート
   double ea_order_good_price = 0; //利確レート

   //新しい足ができた時だけやる
   if(Time[0] != prevtime){
      prevtime = Time[0];
   }else{
      return;
   }

   //ポジションがない場合はエントリー判定
   if(total == 0){

      // ★ここで追加
      UpdateMartingaleStep();

      bool openBuy  = false;    //買い新規注文判断
      bool openSell = false;    //売り新規注文判断
      
//***【新規注文判断↓】***//

      bool isBullAlligator_S = false;
      bool isBearAlligator_S = false;

      //ジョーズライン
      double JAW_ATAI_1 = iAlligator(NULL, 0, Jaw_Period, Jaw_Shift, Teeth_Period, Teeth_Shift, Lips_Period ,Lips_Shift, MODE_SMMA, PRICE_MEDIAN, MODE_GATORJAW, 1);
      double JAW_ATAI_2 = iAlligator(NULL, 0, Jaw_Period, Jaw_Shift, Teeth_Period, Teeth_Shift, Lips_Period ,Lips_Shift, MODE_SMMA, PRICE_MEDIAN, MODE_GATORJAW, 2);
      
      //ティースライン
      double TEETH_ATAI_1 = iAlligator(NULL, 0, Jaw_Period, Jaw_Shift, Teeth_Period, Teeth_Shift, Lips_Period ,Lips_Shift, MODE_SMMA, PRICE_MEDIAN, MODE_GATORTEETH, 1);
      double TEETH_ATAI_2 = iAlligator(NULL, 0, Jaw_Period, Jaw_Shift, Teeth_Period, Teeth_Shift, Lips_Period ,Lips_Shift, MODE_SMMA, PRICE_MEDIAN, MODE_GATORTEETH, 2);
      
      //リップスライン
      double LIPS_ATAI_1 = iAlligator(NULL, 0, Jaw_Period, Jaw_Shift, Teeth_Period, Teeth_Shift, Lips_Period ,Lips_Shift, MODE_SMMA, PRICE_MEDIAN, MODE_GATORLIPS, 1);
      double LIPS_ATAI_2 = iAlligator(NULL, 0, Jaw_Period, Jaw_Shift, Teeth_Period, Teeth_Shift, Lips_Period ,Lips_Shift, MODE_SMMA, PRICE_MEDIAN, MODE_GATORLIPS, 2);
      
      //ローソク足の1つ前のクローズ値情報を取得しておく
      double rosokuValueCloseP_Alli = iClose(NULL, 0, 1);
   
      //=== 開き具合の計算 ===//
      double lipsTeethDiff = MathAbs(LIPS_ATAI_1 - TEETH_ATAI_1);
      double teethJawDiff  = MathAbs(TEETH_ATAI_1 - JAW_ATAI_1);
      bool isOpenedUp = (LIPS_ATAI_1 > TEETH_ATAI_1 && TEETH_ATAI_1 > JAW_ATAI_1);
      
      //=== 上昇トレンドで落ち着いた状態 ===//
      if(isOpenedUp && lipsTeethDiff > Point*10 && teethJawDiff > Point*10 
         && rosokuValueCloseP_Alli > LIPS_ATAI_1)
      {
         isBullAlligator_S = true;
      }
      
      //=== 下降トレンドで落ち着いた状態 ===//
      bool isOpenedDown = (LIPS_ATAI_1 < TEETH_ATAI_1 && TEETH_ATAI_1 < JAW_ATAI_1);
      if(isOpenedDown && lipsTeethDiff > Point*10 && teethJawDiff > Point*10 
         && rosokuValueCloseP_Alli < LIPS_ATAI_1)
      {
         isBearAlligator_S = true;
      }

//***【新規注文判断↑】***//

      //参照するサンプルEAの買いエントリーフラグをセットしてください
      if(isBullAlligator_S){
         openBuy =true;
         ea_order_Type = OP_BUY;
      //参照するサンプルEAの売りエントリーフラグをセットしてください      
      }else if(isBearAlligator_S){
         openSell =true;
         ea_order_Type = OP_SELL;
      }

//***【TP/SL判断↓】***//
      if(openBuy){
         ea_order_stop_price = Ask - SL * PipValue;
         ea_order_good_price = Ask + TP * PipValue;
      }else if(openSell){
         ea_order_stop_price = Bid + SL * PipValue;  
         ea_order_good_price = Bid - TP * PipValue;   
      }
//***【TP/SL判断↑】***//

      if(ea_order_Type > -1){

         double tradeLots = Lots;

         if(UseMartingale){
            tradeLots = Lots * MathPow(MartinFactor, martinStep);
         }
         funcOrder_Send(ea_order_Type,ea_order_stop_price,ea_order_good_price,tradeLots,Ea_Name,777,0);
      }
      
   //ポジションがある場合は決済判定   
   }else{
   
      bool closeBuy  = false;    //買いポジション決済判断
      bool closeSell = false;    //売りポジション決済判断
      bool kekka = OrderSelect(0,SELECT_BY_POS,MODE_TRADES);      //ポジション確認

//***【決済注文判断↓】***//

      bool isBullAlligator_C = false;
      bool isBearAlligator_C = false;

      //ジョーズライン
      double JAW_ATAI_1 = iAlligator(NULL, 0, Jaw_Period, Jaw_Shift, Teeth_Period, Teeth_Shift, Lips_Period ,Lips_Shift, MODE_SMMA, PRICE_MEDIAN, MODE_GATORJAW, 1);
      double JAW_ATAI_2 = iAlligator(NULL, 0, Jaw_Period, Jaw_Shift, Teeth_Period, Teeth_Shift, Lips_Period ,Lips_Shift, MODE_SMMA, PRICE_MEDIAN, MODE_GATORJAW, 2);
      
      //ティースライン
      double TEETH_ATAI_1 = iAlligator(NULL, 0, Jaw_Period, Jaw_Shift, Teeth_Period, Teeth_Shift, Lips_Period ,Lips_Shift, MODE_SMMA, PRICE_MEDIAN, MODE_GATORTEETH, 1);
      double TEETH_ATAI_2 = iAlligator(NULL, 0, Jaw_Period, Jaw_Shift, Teeth_Period, Teeth_Shift, Lips_Period ,Lips_Shift, MODE_SMMA, PRICE_MEDIAN, MODE_GATORTEETH, 2);
      
      //リップスライン
      double LIPS_ATAI_1 = iAlligator(NULL, 0, Jaw_Period, Jaw_Shift, Teeth_Period, Teeth_Shift, Lips_Period ,Lips_Shift, MODE_SMMA, PRICE_MEDIAN, MODE_GATORLIPS, 1);
      double LIPS_ATAI_2 = iAlligator(NULL, 0, Jaw_Period, Jaw_Shift, Teeth_Period, Teeth_Shift, Lips_Period ,Lips_Shift, MODE_SMMA, PRICE_MEDIAN, MODE_GATORLIPS, 2);
      
      //ローソク足の1つ前のクローズ値情報を取得しておく
      double rosokuValueCloseP_Alli = iClose(NULL, 0, 1);
      
      //=== 開き具合の計算 ===//
      double lipsTeethDiff = MathAbs(LIPS_ATAI_1 - TEETH_ATAI_1);
      double teethJawDiff  = MathAbs(TEETH_ATAI_1 - JAW_ATAI_1);
      bool isOpenedUp = (LIPS_ATAI_1 > TEETH_ATAI_1 && TEETH_ATAI_1 > JAW_ATAI_1);
      
      // ポジションがBUYの場合
      if(OrderType() == OP_BUY){
         if(LIPS_ATAI_1 < TEETH_ATAI_1 || lipsTeethDiff < Point*5 || teethJawDiff < Point*5){
            isBullAlligator_C = true;  // 決済条件
         }
      }
      // ポジションがSELLの場合      
      else if(OrderType() == OP_SELL){
         if(LIPS_ATAI_1 > TEETH_ATAI_1 || lipsTeethDiff < Point*5 || teethJawDiff < Point*5){
            isBearAlligator_C = true;  // 決済条件
         }
      }

//***【決済注文判断↑】***//
   
      //参照するサンプルEAの買いポジション決済フラグをセットしてください
      if(isBullAlligator_C){
         closeBuy =true;
         ea_order_Type = OP_BUY;
      //参照するサンプルEAの売りポジション決済フラグをセットしてください
      }else if(isBearAlligator_C){
         closeSell =true;
         ea_order_Type = OP_SELL;
      }
      
      if(ea_order_Type > -1){
         funcOrder_CloseAll(ea_order_Type, -1, 0);
      }
      
   }

   return;
}


//★★★★★★★★★★★★★★★★★★★★ ここから下は共通関数 ★★★★★★★★★★★★★★★★★★★★
//+------------------------------------------------------------------+
//|【関数】新規注文関数
//|
//|【引数】 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;
}
 
    
//+------------------------------------------------------------------+
//|【関数】全決済関数                                                 |
//|                                                                  |
//|【引数】 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;
}

//+------------------------------------------------------------------+
//| 前回トレード結果からマーチン段数を更新                           |
//+------------------------------------------------------------------+
void UpdateMartingaleStep()
{
   for(int i = OrdersHistoryTotal() - 1; i >= 0; i--)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) continue;
      if(OrderSymbol() != Symbol()) continue;
      if(OrderMagicNumber() != 777) continue;

      // 既に処理済みなら何もしない
      if(OrderTicket() == lastHistoryTicket)
         return;

      lastHistoryTicket = OrderTicket();

      if(OrderProfit() > 0)
         martinStep = 0;
      else
         martinStep++;

      if(martinStep > MaxMartinStep)
         martinStep = MaxMartinStep;

      return;
   }
}
//+------------------------------------------------------------------+
//| マーチン後ロット計算                                             |
//+------------------------------------------------------------------+
double GetTradeLots()
{
   if(!UseMartingale) return Lots;

   double tradeLots = Lots * MathPow(MartinFactor, martinStep);

   // ブローカー最小ロット対策
   double minLot = MarketInfo(Symbol(), MODE_MINLOT);
   if(tradeLots < minLot) tradeLots = minLot;

   return NormalizeDouble(tradeLots, 2);
}

既存のソースコードをほぼほぼ変更せずにマーチンゲール法が適用されましたね!

手軽にマーチンゲール法をEAに取り入れる事ができる関数

さいごに

勝率の高いEAにマーチンゲールを組み合わせると、 短期的な成績は安定して見えるケースもあります。
一方で、連敗時のリスク増大という本質的な課題もあるため、 本記事では「ロジック実装の考え方」に重点を置いて解説しました。

勝率が90%とかだと10連敗する確率は0.1*0.1*0.1*・・・=0.00000001%なので0.01Lot開始のトレードで10lotまでいく事は滅多にないと考え出すと危ないですが夢がありますね。
※ただし、勝率は相場環境によって簡単に崩れるため、「想定勝率を前提にロット設計を固定するのは非常に危険」です。

※マーチン・ナンピン手法のリスク管理や設計思想については、 以下の記事でより詳しく整理しています。


✅ 今回のロジックをベースにしたEAサンプルも多数公開中

今回紹介したようなEAの売買ロジック・考え方をベースに、
当サイトではさまざまなFX自動売買EAのサンプルコードを公開しています。

ロジックの違いや設計の考え方を比較しながら、
自分に合ったEA構成を探したい方はぜひチェックしてみてください。


📊 EA運用・検証フェーズに進みたい方へ

今回のような仕組みを理解したうえで、
「実際にどのEAが安定しているのか」、「検証データではどんな差が出ているのか」
を確認したい方は、以下の記事も参考になります。


EA開発初心者向けに、今後も実践的に使えるMQL4関数や実装例を紹介していきます。
気になる機能やロジックがあれば、用途別に整理した関連記事もぜひあわせてご覧ください。

コメント

  1. 新さんのアイコン より:

    りょうさんが公開しているソースコードのどれかに、今回のマーチン関数を入れたソースを例として掲載して欲しいです。

    初歩的な事もままならない私にとって、今回の記事の内容を試しましたがコンパイル時にエラーが出てしまい、解決方法がわからなかったため、具体的な例をいただきたいです。

    宜しくお願い致します。

  2. ラドローさんのアイコン ラドロー より:

    はじめまして
    1つ教えて頂きたい事があります。
    マーチン倍数部分 total = total*2;
    をロットを0.1負けたら次0.2負けたら0.3
    と+1ずつ足す方法教えて頂けるとありがたいです。
    よろしくお願い致します。

    • りょう りょう より:

      ラドローさん

      初めまして。コメント有難うございます。
      +1ずつ足す方法ですが242行目の、
      『total = total*2;』の箇所を『total = total+Lots;』と書き換えて頂ければ対応可能です。

タイトルとURLをコピーしました