Summary: Professional MT4 EA implementing Stochastic oscillator crossover strategy. Enters when %K crosses %D in overbought/oversold zones. Features trailing stop, breakeven, spread control, and complete position management. Full source code included.




# Stochastic Oscillator Crossover EA - Complete MQL4 Source Code

This article provides a fully functional Expert Advisor based on the Stochastic oscillator crossover strategy. The Stochastic oscillator, developed by George C. Lane in the 1950s, measures the closing price relative to the price range over a specified period . This EA opens trades when the %K line crosses the %D line in the overbought or oversold zones, a classic signal generation method for this momentum indicator .

Strategy Logic



The EA uses the standard Stochastic oscillator with configurable %K, %D, and slowing periods. When the Stochastic falls below the oversold level (default 20) and %K crosses above %D, a BUY position is opened. When the Stochastic rises above the overbought level (default 80) and %K crosses below %D, a SELL position is opened. Additional features include trailing stop, breakeven management, time filters, and spread control .

Complete MQL4 Code



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

//--- Input Parameters
input double LotSize = 0.1; // Fixed lot size
input int KPeriod = 5; // %K period
input int DPeriod = 3; // %D period
input int Slowing = 3; // Slowing period
input int Overbought = 80; // Overbought level
input int Oversold = 20; // Oversold level
input int StopLoss = 60; // Stop loss in pips
input int TakeProfit = 120; // Take profit in pips
input bool UseTrailingStop = true; // Enable trailing stop
input int TrailingStart = 25; // Pips to start trailing
input int TrailingStep = 15; // Trailing step in pips
input int BreakEvenPips = 30; // Pips to breakeven (0=disable)
input bool UseTimeFilter = false; // Enable time filter
input int StartHour = 8; // Trading start hour
input int EndHour = 20; // Trading end hour
input int MaxSpread = 35; // Maximum allowed spread
input int Slippage = 10; // Maximum slippage
input int MagicNumber = 202413; // EA magic number
input bool CloseOpposite = true; // Close opposite on new signal

//--- Global variables
double stoMainCurr = 0, stoMainPrev = 0;
double stoSignalCurr = 0, stoSignalPrev = 0;
int pointMultiplier = 10;
datetime lastBarTime = 0;

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

// Validate parameters
if(Overbought <= Oversold)
{
Print("Error: Overbought must be greater than Oversold");
return(INIT_PARAMETERS_INCORRECT);
}
if(KPeriod < 1 || DPeriod < 1 || Slowing < 1)
{
Print("Error: Invalid Stochastic periods");
return(INIT_PARAMETERS_INCORRECT);
}
return(INIT_SUCCEEDED);
}

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

//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Check for new bar (trades once per bar)
if(!IsNewBar())
return;

// Time filter check
if(!IsTradingTime())
return;

// Spread check
if(!IsSpreadOK())
return;

// Calculate Stochastic values
CalculateStochastic();

// Check data validity
if(stoMainCurr == EMPTY_VALUE || stoMainPrev == EMPTY_VALUE)
return;

// Manage existing positions
ManagePositions();

// Signal detection
if(IsBuySignal())
{
if(CloseOpposite) CloseSellPositions();
if(CountBuyPositions() == 0)
OpenOrder(OP_BUY);
}
else if(IsSellSignal())
{
if(CloseOpposite) CloseBuyPositions();
if(CountSellPositions() == 0)
OpenOrder(OP_SELL);
}
}

//+------------------------------------------------------------------+
//| Check if new bar has formed |
//+------------------------------------------------------------------+
bool IsNewBar()
{
if(lastBarTime == Time[0])
return false;
lastBarTime = Time[0];
return true;
}

//+------------------------------------------------------------------+
//| Calculate Stochastic oscillator values |
//+------------------------------------------------------------------+
void CalculateStochastic()
{
// Main %K line - MODE_MAIN = 0, Signal %D line - MODE_SIGNAL = 1
stoMainCurr = iStochastic(Symbol(), 0, KPeriod, DPeriod, Slowing, MODE_SMA, 0, MODE_MAIN, 0);
stoMainPrev = iStochastic(Symbol(), 0, KPeriod, DPeriod, Slowing, MODE_SMA, 0, MODE_MAIN, 1);
stoSignalCurr = iStochastic(Symbol(), 0, KPeriod, DPeriod, Slowing, MODE_SMA, 0, MODE_SIGNAL, 0);
stoSignalPrev = iStochastic(Symbol(), 0, KPeriod, DPeriod, Slowing, MODE_SMA, 0, MODE_SIGNAL, 1);
}

//+------------------------------------------------------------------+
//| Check buy signal: oversold area + %K crosses above %D |
//+------------------------------------------------------------------+
bool IsBuySignal()
{
// Both current values in oversold zone
if(stoMainCurr >= Oversold)
return false;

// %K crosses above %D (previous %K <= previous %D, current %K > current %D)
bool crossUp = (stoMainPrev <= stoSignalPrev && stoMainCurr > stoSignalCurr);

return crossUp;
}

//+------------------------------------------------------------------+
//| Check sell signal: overbought area + %K crosses below %D |
//+------------------------------------------------------------------+
bool IsSellSignal()
{
// Both current values in overbought zone
if(stoMainCurr <= Overbought)
return false;

// %K crosses below %D (previous %K >= previous %D, current %K < current %D)
bool crossDown = (stoMainPrev >= stoSignalPrev && stoMainCurr < stoSignalCurr);

return crossDown;
}

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

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

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

int ticket = OrderSend(Symbol(), cmd, LotSize, price, Slippage, sl, tp, "Stoch Crossover", MagicNumber, 0, clrNONE);

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

//+------------------------------------------------------------------+
//| Manage existing positions - trailing stop and breakeven |
//+------------------------------------------------------------------+
void ManagePositions()
{
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
// Breakeven management
if(BreakEvenPips > 0)
{
double bePoints = BreakEvenPips * Point * pointMultiplier;

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

// Trailing stop management
if(UseTrailingStop && TrailingStart > 0 && TrailingStep > 0)
{
double trailStartPoints = TrailingStart * Point * pointMultiplier;
double trailStepPoints = TrailingStep * Point * pointMultiplier;

if(OrderType() == OP_BUY)
{
double profitPips = (Bid - OrderOpenPrice()) / Point / pointMultiplier;
if(profitPips >= TrailingStart)
{
double newSL = Bid - trailStepPoints;
if(newSL > OrderStopLoss())
{
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
Print("Trailing stop updated for BUY #", OrderTicket());
}
}
}
else if(OrderType() == OP_SELL)
{
double profitPips = (OrderOpenPrice() - Ask) / Point / pointMultiplier;
if(profitPips >= TrailingStart)
{
double newSL = Ask + trailStepPoints;
if(newSL < OrderStopLoss() || OrderStopLoss() == 0)
{
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
Print("Trailing stop updated for SELL #", OrderTicket());
}
}
}
}
}
}
}
}

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

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

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

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

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

//+------------------------------------------------------------------+
//| Count buy positions |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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 Values |
|-----------|-------------|--------------------|
| LotSize | Fixed trading volume | 0.01 to 1.0 |
| KPeriod | %K line period (Stochastic calculation period) | 5, 9, 14 |
| DPeriod | %D line smoothing period | 3, 5 |
| Slowing | Slowing factor for %K | 3, 5 |
| Overbought | Overbought level (typically 80) | 75-85 |
| Oversold | Oversold level (typically 20) | 15-25 |
| StopLoss | Stop loss distance in pips | 40-80 |
| TakeProfit | Take profit distance in pips | 80-160 |
| UseTrailingStop | Enable trailing stop | true/false |
| TrailingStart | Pips to start trailing | 20-30 |
| TrailingStep | Trailing distance from current price | 10-20 |
| BreakEvenPips | Pips to move stop to breakeven (0=disable) | 25-40 |
| UseTimeFilter | Enable hour filter | true/false |
| StartHour | Begin trading hour (server time) | 0-23 |
| EndHour | Stop trading hour | 0-23 |
| MaxSpread | Maximum allowed spread | 20-40 |
| Slippage | Maximum slippage tolerance | 10 |
| MagicNumber | Unique EA identifier | any unique number |
| CloseOpposite | Close opposite on new signal | true/false |

Installation Instructions



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

Strategy Explanation



The Stochastic oscillator consists of two lines: %K (main line) and %D (signal line). The %K line measures the closing price relative to the high-low range over a specified period, calculated as: %K = (Current Close - Lowest Low) / (Highest High - Lowest Low) × 100 . The %D line is a moving average of %K .

Trading Logic:
  • BUY when Stochastic falls below 20 (oversold) and %K crosses above %D

  • SELL when Stochastic rises above 80 (overbought) and %K crosses below %D


  • Compilation and Modification Tips



    Common compilation issues:
  • Ensure `#property strict` is at the top of the code

  • For 4-digit brokers, verify pointMultiplier setting

  • Check that MagicNumber doesn't conflict with other EAs


  • How to customize:
  • Adjust KPeriod, DPeriod, and Slowing for different sensitivity levels

  • Modify Overbought/Oversold levels for different currency pairs (some pairs work better with 75/25)

  • Enable UseTrailingStop and adjust TrailingStart for profit protection

  • Add additional confirmation indicators like moving averages in OnTick()


  • Reference



    This EA source code is independently compiled and tested on MT4 build 1420+. Strategy concept based on classic Stochastic oscillator crossover methodology documented in MQL4 documentation . The oscillator was originally developed by George C. Lane in the 1950s .

    *For advanced EA strategies with multi-timeframe analysis, machine learning optimization, and comprehensive backtest reports, check our premium EA collection. Subscribe for weekly updates and professional trading tools.*