Building a Martingale Grid Trading Strategy in MQL5 (Part 1)

Building a Martingale Grid Trading Strategy in MQL5 (Part 1)

6 June 2025, 13:17
George Liviu Geambasu
0
64

Building a Martingale Grid Trading Strategy in MQL5


Summary: This article presents a complete implementation of a Martingale-based grid trading strategy in MQL5. We'll examine the code structure, risk management features, and potential enhancements for this controversial but powerful trading approach.

1. Introduction to Martingale Grid Trading

The Martingale strategy is a well-known (and often controversial) trading approach where position sizes are increased after losses, with the expectation that eventual wins will recover previous losses. When applied as a grid strategy, it involves placing orders at progressively better prices as the market moves against your initial position.

How Grid Martingale Works:

  • An initial position is opened at the current market price
  • Additional positions are added at predetermined intervals as price moves against you
  • Each subsequent position is larger than the previous (typically 1.5-2x)
  • All positions are closed when the average break-even price is reached

Key Risks to Consider:

  • Margin depletion: Exponential position growth can quickly consume available margin
  • No guaranteed recovery: Strong trends can continue beyond your grid levels
  • Psychological stress: Requires discipline to manage growing losses

2. The MQL5 Implementation

Let's examine the complete code structure with detailed explanations for each component.

2.1. Includes and Initial Setup

#include <Trade/Trade.mqh>
CTrade trade;

#include <Trade\AccountInfo.mqh>
static CAccountInfo accountInfo;

Explanation:

  • CTrade class provides simplified trade execution methods (market orders, position management)
  • CAccountInfo gives access to account-related functions (margin checks, balance information)

2.2. Grid Trading Parameters

// Grid parameters
double initialLotSize = 0.01;
double lotMultiplier = 1.5;
int gridStepPoints = 300;
double gridStep = gridStepPoints * _Point;
int breakEvenTPPoints = 100;
double breakEvenTP = breakEvenTPPoints * _Point;
int digits_number;

// Grid tracking variable
double currentBuyGridStep = 0;

Parameter Breakdown:

Parameter Description Default Value
initialLotSize Starting position size 0.01 lots
lotMultiplier Position size multiplier for each new grid level 1.5x
gridStepPoints Distance between grid levels in points 300 points
breakEvenTPPoints Take-profit distance in points for break-even exit 100 points

2.3. Initialization Function

void InitializeVariables() {
    digits_number = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
    
    if (currentBuyGridStep == 0) {
        currentBuyGridStep = gridStep;
    }
}

Key Functions:

  • SymbolInfoInteger(_Symbol, SYMBOL_DIGITS) - Gets the number of decimal places for the current symbol
  • Initializes currentBuyGridStep if not already set

3. Core Trading Logic

3.1. Main Execution Flow

void OnTick() {
    InitializeVariables();
    RunTradingStrategy();
}

void RunTradingStrategy() {
   int nr_buy_positions = CountBuyPositions();
   double askPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   // Open initial buy position if no buy positions exist
   if (nr_buy_positions == 0) {
      OpenInitialBuyPosition();
   }

   CheckBuyGridLevels();
   SetBreakEvenTP();
}

Process Flow:

  1. Initialize necessary variables on each tick
  2. Count existing buy positions
  3. Open initial position if none exists
  4. Check if new grid levels should be triggered
  5. Monitor for break-even exit conditions

3.2. Position Counting Function

int CountBuyPositions() {
   int count = 0;
   for (int i = 0; i < PositionsTotal(); i++) {
      ulong ticket = PositionGetTicket(i);
      if (PositionSelectByTicket(ticket) && 
          PositionGetString(POSITION_SYMBOL) == _Symbol &&
          PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
         count++;
      }
   }
   return count;
}

Key Points:

  • Iterates through all open positions
  • Filters positions for current symbol and buy type
  • Returns count of matching positions

4. Grid Position Management

4.1. Opening the Initial Position

void OpenInitialBuyPosition() {
   double askPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK); // Get current ask price
   double lotSize = NormalizeLotSize(initialLotSize);
   double requiredMargin = accountInfo.MarginCheck(_Symbol, ORDER_TYPE_BUY, lotSize, askPrice);
   
   if(requiredMargin > accountInfo.FreeMargin()) {
      Print("Not enough margin for initial buy! Required: ", requiredMargin, " Free: ", accountInfo.FreeMargin());
      return;
   }

   if (!trade.Buy(lotSize, _Symbol, askPrice, 0, 0, "Initial Buy")) {
      Print("Initial buy error: ", GetLastError());
   } else {
      Print("Initial buy position opened at ", askPrice, " with lot size ", lotSize);
   }
}

Safety Features:

  • Lot size normalization to comply with broker restrictions
  • Margin check before opening position
  • Error handling and logging

4.2. Grid Level Monitoring

void CheckBuyGridLevels() {
   double minBuyPrice = DBL_MAX;
   int nr_buy_positions = 0;

   for (int i = 0; i < PositionsTotal(); i++) {
      ulong ticket = PositionGetTicket(i);
      if (PositionSelectByTicket(ticket) && PositionGetString(POSITION_SYMBOL) == _Symbol) {
         if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
            double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            if (entryPrice < minBuyPrice) {
               minBuyPrice = entryPrice;
            }
            nr_buy_positions++;
         }
      }
   }

   if (nr_buy_positions > 0) {
      double nextGridBuyPrice = NormalizeDouble(minBuyPrice - currentBuyGridStep, digits_number);
      double currentAsk = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), digits_number);

      if (currentAsk <= nextGridBuyPrice) {
         double newLotSize = NormalizeLotSize(initialLotSize * MathPow(lotMultiplier, nr_buy_positions));
         double requiredMargin = accountInfo.MarginCheck(_Symbol, ORDER_TYPE_BUY, newLotSize, currentAsk);
         
         if(requiredMargin > accountInfo.FreeMargin()) {
            Print("Not enough margin for grid buy!");
            return;
         }

         if (!trade.Buy(newLotSize, _Symbol, currentAsk, 0, 0, "Grid Buy")) {
            Print("Grid buy error: ", GetLastError());
         }
      }
   }
}

Grid Logic Explained:

  1. Finds the lowest existing buy position price
  2. Calculates next grid level price (current lowest - grid step)
  3. Checks if current ask price has reached the next grid level
  4. Calculates new position size using exponential growth
  5. Performs margin check before opening new position

5. Break-Even Exit Strategy

void SetBreakEvenTP() {
   double totalBuyVolume = 0;
   double totalBuyCost = 0;

   for (int i = 0; i < PositionsTotal(); i++) {
      ulong ticket = PositionGetTicket(i);
      if (PositionSelectByTicket(ticket) && PositionGetString(POSITION_SYMBOL) == _Symbol) {
         if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
            double volume = PositionGetDouble(POSITION_VOLUME);
            double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            totalBuyVolume += volume;
            totalBuyCost += entryPrice * volume;
         }
      }
   }

   if (totalBuyVolume > 0) {
      double breakEvenBuyPrice = NormalizeDouble((totalBuyCost / totalBuyVolume) + breakEvenTP, digits_number);
      double currentBid = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), digits_number);

      if (currentBid >= breakEvenBuyPrice) {
         CloseAllBuyPositions();
      }
   }
}

Exit Logic:

  • Calculates volume-weighted average entry price
  • Adds the break-even TP distance to determine exit price
  • Monitors current bid price against target exit price
  • Closes all positions when target is reached

6. Risk Management and Enhancements

6.1. Current Safety Features

  • Margin checks before each trade
  • Lot size normalization
  • Break-even exit mechanism

6.2. Potential Improvements

Enhancement Description Implementation Difficulty
Dynamic Grid Spacing Adjust step size based on ATR or volatility Medium
Max Drawdown Limit Stop trading after certain loss threshold Easy
Trailing Stop Protect profits during favorable moves Medium
Time-Based Exit Close positions after certain time period Easy

7. Conclusion

This Martingale grid strategy implementation provides a solid foundation for automated grid trading in MQL5. While the approach can be profitable in ranging markets, it carries significant risk during strong trends. Always:

  • Thoroughly backtest with historical data
  • Implement strict risk management rules
  • Monitor performance in demo accounts before live trading

In future articles, we'll explore enhancements like:

  • Sell-side grid implementation
  • Multi-currency support
  • Advanced risk controls

Note: The complete source code for this EA is available in the attached .mq5 file. Always test thoroughly in a demo environment before considering live trading.