Summary: Professional MT4 EA combining Stochastic oscillator signals with controlled Martingale position sizing. Features maximum position limits, breakeven management, spread control, and configurable multiplier. Complete source code included for educational purposes.




# Stochastic Martingale EA - Complete MQL4 Source Code

This article provides a fully functional Expert Advisor that combines Stochastic oscillator technical signals with a controlled Martingale position sizing approach. Unlike pure Martingale systems that blindly double after losses, this EA only adds positions when Stochastic confirms oversold/overbought conditions, providing a technical filter for entry decisions.

Strategy Logic



The EA uses the Stochastic oscillator as the primary signal generator. When the Stochastic %K line falls below the oversold level (default 20) and crosses back above it, a BUY signal is generated. When it rises above the overbought level (default 80) and crosses back below it, a SELL signal is generated. The Martingale component applies only to losing positions, with configurable multiplier and maximum position limits to prevent exponential risk growth.

Complete MQL4 Code



```mql4
//+------------------------------------------------------------------+
//| StochasticMartingaleEA.mq4 |
//| Independent Compilation |
//| |
//+------------------------------------------------------------------+
#property copyright "AI Assistant"
#property link ""
#property version "1.00"
#property strict

//--- Input Parameters
input double InitialLot = 0.01; // Initial lot size
input double MartingaleMultiplier = 2.0; // Lot multiplier after loss
input int MaxPositions = 5; // Maximum Martingale positions
input double MaxLot = 2.0; // Maximum allowed lot size
input int StochasticK = 5; // Stochastic %K period
input int StochasticD = 3; // Stochastic %D period
input int StochasticSlowing = 3; // Stochastic slowing
input int Overbought = 80; // Overbought level
input int Oversold = 20; // Oversold level
input int StopLoss = 50; // Stop loss in pips
input int TakeProfit = 100; // Take profit in pips
input int BreakEvenPips = 30; // Pips to trigger breakeven (0=off)
input int MaxSpread = 35; // Maximum spread in pips
input bool UseTimeFilter = false; // Enable time filter
input int StartHour = 8; // Trading start hour
input int EndHour = 20; // Trading end hour
input int Slippage = 10; // Maximum slippage
input int MagicNumber = 202413; // EA magic number
input bool CloseOpposite = true; // Close opposite positions on new signal

//--- Global variables
double stochMain = 0, stochSignal = 0;
double stochMainPrev = 0, stochSignalPrev = 0;
int pointMultiplier = 10;
bool isBusy = false;

//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Detect broker digit format
if(Digits == 3 || Digits == 5)
pointMultiplier = 10;
else if(Digits == 2 || Digits == 4)
pointMultiplier = 1;

if(StochasticK < 1 || StochasticD < 1)
{
Print("Error: Invalid Stochastic parameters");
return(INIT_PARAMETERS_INCORRECT);
}
return(INIT_SUCCEEDED);
}

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

//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
if(isBusy) return;
isBusy = true;

// Time filter check
if(!IsTradingTime())
{
isBusy = false;
return;
}

// Spread check
if(!IsSpreadOK())
{
isBusy = false;
return;
}

// Calculate Stochastic values
CalculateStochastic();

// Manage existing positions (breakeven)
ManageBreakeven();

// Count existing positions for this EA
int currentPositions = CountPositions();

// Signal detection - only enter if we haven't reached max positions
if(currentPositions < MaxPositions)
{
if(IsBuySignal())
{
if(CloseOpposite) CloseSellPositions();
if(CountBuyPositions() == 0)
{
double lot = CalculateLotSize(currentPositions);
OpenOrder(OP_BUY, lot);
}
}
else if(IsSellSignal())
{
if(CloseOpposite) CloseBuyPositions();
if(CountSellPositions() == 0)
{
double lot = CalculateLotSize(currentPositions);
OpenOrder(OP_SELL, lot);
}
}
}

isBusy = false;
}

//+------------------------------------------------------------------+
//| Calculate Stochastic oscillator values |
//+------------------------------------------------------------------+
void CalculateStochastic()
{
stochMain = iStochastic(Symbol(), 0, StochasticK, StochasticD, StochasticSlowing, MODE_SMA, 0, MODE_MAIN, 0);
stochSignal = iStochastic(Symbol(), 0, StochasticK, StochasticD, StochasticSlowing, MODE_SMA, 0, MODE_SIGNAL, 0);
stochMainPrev = iStochastic(Symbol(), 0, StochasticK, StochasticD, StochasticSlowing, MODE_SMA, 0, MODE_MAIN, 1);
stochSignalPrev = iStochastic(Symbol(), 0, StochasticK, StochasticD, StochasticSlowing, MODE_SMA, 0, MODE_SIGNAL, 1);
}

//+------------------------------------------------------------------+
//| Check for buy signal - Stochastic crosses above oversold |
//+------------------------------------------------------------------+
bool IsBuySignal()
{
if(stochMain == EMPTY_VALUE || stochMainPrev == EMPTY_VALUE)
return false;

// Main line was below oversold and now above oversold
// Also require signal line confirmation
bool oversoldCrossover = (stochMainPrev <= Oversold && stochMain > Oversold);
bool signalConfirmation = (stochMain > stochSignal);

return (oversoldCrossover && signalConfirmation);
}

//+------------------------------------------------------------------+
//| Check for sell signal - Stochastic crosses below overbought |
//+------------------------------------------------------------------+
bool IsSellSignal()
{
if(stochMain == EMPTY_VALUE || stochMainPrev == EMPTY_VALUE)
return false;

// Main line was above overbought and now below overbought
bool overboughtCrossunder = (stochMainPrev >= Overbought && stochMain < Overbought);
bool signalConfirmation = (stochMain < stochSignal);

return (overboughtCrossunder && signalConfirmation);
}

//+------------------------------------------------------------------+
//| Calculate lot size based on Martingale progression |
//+------------------------------------------------------------------+
double CalculateLotSize(int currentLossStreak)
{
double lot = InitialLot;

// Apply Martingale multiplier for each losing position in sequence
for(int i = 0; i < currentLossStreak; i++)
{
lot = lot * MartingaleMultiplier;
}

// Cap at maximum lot size
if(lot > MaxLot)
lot = MaxLot;

// Round to allowed lot step
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
if(lotStep > 0)
lot = MathFloor(lot / lotStep) * lotStep;

double minLot = MarketInfo(Symbol(), MODE_MINLOT);
if(lot < minLot)
lot = minLot;

return NormalizeDouble(lot, 2);
}

//+------------------------------------------------------------------+
//| Open market order |
//+------------------------------------------------------------------+
void OpenOrder(int cmd, double lot)
{
double price = (cmd == OP_BUY) ? Ask : Bid;
double sl = 0, tp = 0;

if(StopLoss > 0)
{
if(cmd == OP_BUY)
sl = price - StopLoss * Point * pointMultiplier;
else
sl = price + StopLoss * Point * pointMultiplier;
}

if(TakeProfit > 0)
{
if(cmd == OP_BUY)
tp = price + TakeProfit * Point * pointMultiplier;
else
tp = price - TakeProfit * Point * pointMultiplier;
}

int ticket = OrderSend(Symbol(), cmd, lot, price, Slippage, sl, tp, "StochMartingale", MagicNumber, 0, clrNONE);

if(ticket < 0)
Print("OrderSend failed. Error: ", GetLastError());
else
Print("Order opened. Ticket: ", ticket, " Lot: ", lot);
}

//+------------------------------------------------------------------+
//| Manage breakeven stop loss |
//+------------------------------------------------------------------+
void ManageBreakeven()
{
if(BreakEvenPips <= 0) return;

for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
double breakevenPrice = OrderOpenPrice();
double currentProfit = 0;

if(OrderType() == OP_BUY)
{
currentProfit = (Bid - OrderOpenPrice()) / Point / pointMultiplier;
if(currentProfit >= BreakEvenPips && OrderStopLoss() < breakevenPrice)
{
OrderModify(OrderTicket(), OrderOpenPrice(), breakevenPrice, OrderTakeProfit(), 0, clrNONE);
Print("Breakeven set for BUY #", OrderTicket());
}
}
else if(OrderType() == OP_SELL)
{
currentProfit = (OrderOpenPrice() - Ask) / Point / pointMultiplier;
if(currentProfit >= BreakEvenPips && (OrderStopLoss() > breakevenPrice || OrderStopLoss() == 0))
{
OrderModify(OrderTicket(), OrderOpenPrice(), breakevenPrice, OrderTakeProfit(), 0, clrNONE);
Print("Breakeven set for SELL #", OrderTicket());
}
}
}
}
}
}

//+------------------------------------------------------------------+
//| Check if spread is within acceptable limits |
//+------------------------------------------------------------------+
bool IsSpreadOK()
{
if(MaxSpread <= 0) return true;

int currentSpread = (int)((Ask - Bid) / Point / pointMultiplier);
return (currentSpread <= MaxSpread);
}

//+------------------------------------------------------------------+
//| Check if current time is within trading hours |
//+------------------------------------------------------------------+
bool IsTradingTime()
{
if(!UseTimeFilter) return true;

datetime now = TimeCurrent();
MqlDateTime dt;
TimeToStruct(now, dt);

int hour = dt.hour;
return (hour >= StartHour && hour < EndHour);
}

//+------------------------------------------------------------------+
//| Count total positions for this EA |
//+------------------------------------------------------------------+
int CountPositions()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
count++;
}
}
return count;
}

//+------------------------------------------------------------------+
//| Count buy positions only |
//+------------------------------------------------------------------+
int CountBuyPositions()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_BUY)
count++;
}
}
return count;
}

//+------------------------------------------------------------------+
//| Count sell positions only |
//+------------------------------------------------------------------+
int CountSellPositions()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_SELL)
count++;
}
}
return count;
}

//+------------------------------------------------------------------+
//| Close all buy positions |
//+------------------------------------------------------------------+
void CloseBuyPositions()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_BUY)
{
OrderClose(OrderTicket(), OrderLots(), Bid, Slippage, clrNONE);
}
}
}
}

//+------------------------------------------------------------------+
//| Close all sell positions |
//+------------------------------------------------------------------+
void CloseSellPositions()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_SELL)
{
OrderClose(OrderTicket(), OrderLots(), Ask, Slippage, clrNONE);
}
}
}
}
//+------------------------------------------------------------------+
```

Parameter Explanation



| Parameter | Description | Recommended |
|-----------|-------------|-------------|
| InitialLot | Starting position size | 0.01 |
| MartingaleMultiplier | Lot multiplier after a losing trade | 1.5-2.0 |
| MaxPositions | Maximum consecutive Martingale positions | 3-5 |
| MaxLot | Absolute maximum lot size cap | 1.0-2.0 |
| StochasticK | %K period (fast line) | 5-14 |
| StochasticD | %D period (slow line/signal) | 3-5 |
| Overbought | Overbought threshold | 70-80 |
| Oversold | Oversold threshold | 20-30 |
| StopLoss | Stop loss distance in pips | 40-60 |
| TakeProfit | Take profit distance in pips | 80-120 |
| BreakEvenPips | Pips needed to move stop to entry | 25-40 |
| MaxSpread | Maximum allowed spread | 20-35 |
| MaxPositions | Maximum number of positions in sequence | 3-5 |

Installation Instructions



1. Copy the code into MetaEditor (press F4 in MT4)
2. Create a new Expert Advisor (File > New > Expert Advisor)
3. Replace all default code with the code above
4. Press Compile (F7) - ensure zero errors
5. Drag the EA from Navigator onto a chart
6. Adjust parameters in the Inputs tab
7. Enable AutoTrading (green triangle button)

Important Risk Warning



Martingale strategies carry inherent risk of large drawdowns. The risk controls in this EA (MaxPositions, MaxLot, StopLoss) are critical safety features. Always test thoroughly on demo accounts before considering live deployment.

Compilation & Modification Tips



Key modifications to consider:
  • Reduce MartingaleMultiplier to 1.5 for conservative scaling

  • Set MaxPositions to 3 to limit consecutive adds

  • Adjust Stochastic parameters based on timeframe (higher values for higher timeframes)

  • Add ATR filter by incorporating iATR() in IsBuySignal/IsSellSignal


  • Reference



    This EA source code is independently compiled based on Stochastic oscillator and Martingale principles discussed in algorithmic trading literature. The implementation includes essential risk controls for educational deployment.

    *For professionally optimized EAs with advanced filtering, multi-timeframe confirmation, and complete backtest suites, check our premium EA collection. Subscribe for weekly strategy updates and exclusive trading tools.*