🚀 AI One-Click Generation of mql4 Strategy Code

How to Code a Martingale Money Management Strategy in Mql4?

mql4 | Published on: 2025/11/26
The following content is generated by EasyQuant .

Problem Description

Please write code that doubles the trade size after a losing trade.

Solution

Here is the complete MQL4 code for an Expert Advisor (EA) that implements a Martingale strategy. This code checks the result of the last closed trade and doubles the lot size if that trade was a loss.

MQL4 Code

//+------------------------------------------------------------------+
//|                                            MartingaleExample.mq4 |
//|                        Copyright 2023, Quantitative Trading AI   |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative Trading AI"
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

//--- Input Parameters
input double InpBaseLot     = 0.1;    // Starting Lot Size
input double InpMultiplier  = 2.0;    // Martingale Multiplier (2.0 = Double)
input int    InpStopLoss    = 50;     // Stop Loss in points
input int    InpTakeProfit  = 50;     // Take Profit in points
input int    InpMagicNumber = 123456; // Magic Number to identify orders
input int    InpSlippage    = 3;      // Maximum slippage in points

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   // Basic validation
   if(InpBaseLot <= 0 || InpMultiplier < 1.0)
   {
      Print("Error: Invalid Input Parameters.");
      return(INIT_FAILED);
   }
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   // 1. Check if there are currently open orders for this symbol/magic number
   if(CountOpenOrders() > 0) return; // Wait for the current trade to close

   // 2. Calculate the Lot Size based on the last closed trade
   double tradeLotSize = GetMartingaleLots();

   // 3. Entry Logic (Placeholder: Simple Random Entry for demonstration)
   // REPLACE THIS BLOCK WITH YOUR ACTUAL STRATEGY LOGIC (RSI, MA, etc.)
   int ticket = -1;
   
   // Example: Always enter BUY if no trades exist (for testing logic)
   double sl = Ask - InpStopLoss * Point;
   double tp = Ask + InpTakeProfit * Point;
   
   // Normalize prices
   sl = NormalizeDouble(sl, Digits);
   tp = NormalizeDouble(tp, Digits);

   ticket = OrderSend(Symbol(), OP_BUY, tradeLotSize, Ask, InpSlippage, sl, tp, "Martingale Buy", InpMagicNumber, 0, clrGreen);

   if(ticket < 0)
   {
      Print("OrderSend failed with error #", GetLastError());
   }
}

//+------------------------------------------------------------------+
//| Function to calculate Lot Size based on history                  |
//+------------------------------------------------------------------+
double GetMartingaleLots()
{
   // Iterate through closed orders from the most recent to the oldest
   for(int i = OrdersHistoryTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
      {
         // Filter by Symbol and Magic Number
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNumber)
         {
            // Check only Buy or Sell orders (ignore cancelled pending orders)
            if(OrderType() <= OP_SELL)
            {
               // If the last trade was a LOSS (Profit < 0)
               if(OrderProfit() < 0)
               {
                  double newLot = OrderLots() * InpMultiplier;
                  
                  // Verify lot size against broker limits
                  return VerifyLotSize(newLot);
               }
               // If the last trade was a WIN (Profit >= 0)
               else
               {
                  return InpBaseLot; // Reset to base lot
               }
            }
         }
      }
   }
   
   // If no history is found (first trade), return base lot
   return InpBaseLot;
}

//+------------------------------------------------------------------+
//| Helper: Verify and Normalize Lot Size                            |
//+------------------------------------------------------------------+
double VerifyLotSize(double lot)
{
   double minLot  = MarketInfo(Symbol(), MODE_MINLOT);
   double maxLot  = MarketInfo(Symbol(), MODE_MAXLOT);
   double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);

   // 1. Round the lot to the nearest step
   if(lotStep > 0)
      lot = MathFloor(lot / lotStep) * lotStep;

   // 2. Clamp between Min and Max
   if(lot < minLot) lot = minLot;
   if(lot > maxLot) lot = maxLot;

   return lot;
}

//+------------------------------------------------------------------+
//| Helper: Count Open Orders                                        |
//+------------------------------------------------------------------+
int CountOpenOrders()
{
   int count = 0;
   for(int i = 0; i < OrdersTotal(); i++)
   {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      {
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagicNumber)
         {
            count++;
         }
      }
   }
   return count;
}

Code Explanation

  1. GetMartingaleLots() Function:

    • This is the core logic. It loops through the account history using OrdersHistoryTotal().
    • It iterates backwards (i >= 0) to find the most recent closed trade first.
    • It filters trades using OrderSymbol() and OrderMagicNumber() to ensure it only looks at trades generated by this specific EA.
    • Logic: If OrderProfit() < 0 (Loss), it multiplies the previous lot size by InpMultiplier (default 2.0). If the profit is positive, it resets to InpBaseLot.
  2. VerifyLotSize() Function:

    • Calculated lot sizes can sometimes be invalid (e.g., 0.1 * 2 = 0.2 is fine, but specific multipliers might result in 0.13333).
    • This function retrieves the broker's MODE_MINLOT, MODE_MAXLOT, and MODE_LOTSTEP using MarketInfo.
    • It normalizes the calculated lot to match the broker's requirements, preventing OrderSend Error 131 (Invalid Trade Volume).
  3. OnTick() Execution:

    • The EA first checks if a trade is already open using CountOpenOrders(). It waits for the current trade to close before calculating the next lot size.
    • Note: I have included a placeholder OrderSend command that simply buys. You should replace the entry logic comment with your specific strategy (e.g., Moving Average crossover or RSI).

Risk Warning

Martingale strategies carry a high risk of ruining the account. Because the lot size doubles exponentially ($0.1, 0.2, 0.4, 0.8, 1.6, 3.2...$), a consecutive losing streak can quickly hit the broker's maximum lot limit or trigger a Margin Call. Ensure you test this in the Strategy Tester before using it on a live account.

Q&A

Q: How do I change the multiplier to 1.5 instead of doubling?
A: Change the input parameter InpMultiplier from 2.0 to 1.5 in the EA settings window.

Q: Will this work if I restart the terminal?
A: Yes. Because the code uses MODE_HISTORY to look at past closed trades, it will remember the last trade result even if the terminal is restarted, provided the account history is loaded.

Q: Why does the EA reset to the base lot size after a win?
A: This is standard Martingale logic to recover losses and then return to the baseline risk. If you wish to keep the lot size high, you would need to modify the else block inside GetMartingaleLots.