Complete MQL4 Expert Advisor Code
This EA performs a two-stage process on every tick:
Initial Protection: Instantly attaches your percentage-based SL and TP to any unprotected order opened manually or by another script.
Dynamic Trailing Stop: Continuously trails the stop loss behind the current price by a defined percentage once the trade moves into profit, stepping up only in discrete increments to respect broker rate limits.
Code: Select all
//+------------------------------------------------------------------+
//| Auto_SL_TP_Trailing_EA.mq4 |
//| Copyright 2026, Scalping EA |
//+------------------------------------------------------------------+
#property copyright "Open Source"
#property link ""
#property version "1.10"
#property strict
//--- Input Parameters
extern string ___Initial_Protection___ = "--- Initial SL / TP Settings ---";
extern double SL_Percent = 0.15; // Initial Stop Loss in % from Open Price
extern double TP_Percent = 0.30; // Initial Take Profit in % from Open Price
extern string ___Trailing_Settings___ = "--- Trailing Stop Settings ---";
extern bool UseTrailingStop = true; // Enable Percentage-Based Trailing Stop
extern double Trail_Distance_Percent = 0.10; // Trailing distance in % behind current price
extern double Trail_Step_Percent = 0.02; // Minimum price change in % before updating SL
extern string ___Filter_Settings___ = "--- Trade Filters ---";
extern bool OnlyCurrentSymbol = true; // Apply only to the active chart symbol
extern int TargetMagicNumber = 0; // 0 = Manage manual trades & all EAs; >0 = Specific EA ID
//+------------------------------------------------------------------+
//| Expert tick function - Fires on every incoming price quote |
//+------------------------------------------------------------------+
void OnTick()
{
int total = OrdersTotal();
// Loop backwards through all open orders
for(int i = total - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
// 1. Filter by Symbol
if(OnlyCurrentSymbol && OrderSymbol() != Symbol())
continue;
// 2. Filter by Magic Number (0 manages manual trades)
if(TargetMagicNumber > 0 && OrderMagicNumber() != TargetMagicNumber)
continue;
// 3. Process only live market orders (Buy and Sell)
if(OrderType() == OP_BUY || OrderType() == OP_SELL)
{
ManageOrder();
}
}
}
}
//+------------------------------------------------------------------+
//| Manage individual open order (Initial SL/TP + Trailing Logic) |
//+------------------------------------------------------------------+
void ManageOrder()
{
double openPrice = OrderOpenPrice();
double currentSL = OrderStopLoss();
double currentTP = OrderTakeProfit();
int digits = (int)MarketInfo(OrderSymbol(), MODE_DIGITS);
double point = MarketInfo(OrderSymbol(), MODE_POINT);
// Broker's minimum stop distance in points (prevents Error 130)
int stopLevel = (int)MarketInfo(OrderSymbol(), MODE_STOPLEVEL);
double minStopDistance = stopLevel * point;
//-----------------------------------------------------------------
// PHASE 1: Apply Initial SL/TP if order is unprotected
//-----------------------------------------------------------------
if(currentSL == 0 || currentTP == 0)
{
double newSL = currentSL;
double newTP = currentTP;
if(OrderType() == OP_BUY)
{
if(currentSL == 0 && SL_Percent > 0)
newSL = NormalizeDouble(openPrice * (1.0 - (SL_Percent / 100.0)), digits);
if(currentTP == 0 && TP_Percent > 0)
newTP = NormalizeDouble(openPrice * (1.0 + (TP_Percent / 100.0)), digits);
// Verify against broker StopLevel constraints
if(newSL > 0 && (Bid - newSL) < minStopDistance) newSL = NormalizeDouble(Bid - minStopDistance, digits);
if(newTP > 0 && (newTP - Bid) < minStopDistance) newTP = NormalizeDouble(Bid + minStopDistance, digits);
}
else if(OrderType() == OP_SELL)
{
if(currentSL == 0 && SL_Percent > 0)
newSL = NormalizeDouble(openPrice * (1.0 + (SL_Percent / 100.0)), digits);
if(currentTP == 0 && TP_Percent > 0)
newTP = NormalizeDouble(openPrice * (1.0 - (TP_Percent / 100.0)), digits);
// Verify against broker StopLevel constraints
if(newSL > 0 && (newSL - Ask) < minStopDistance) newSL = NormalizeDouble(Ask + minStopDistance, digits);
if(newTP > 0 && (Ask - newTP) < minStopDistance) newTP = NormalizeDouble(Ask - minStopDistance, digits);
}
// Execute modification only if new values differ from current ones
if(NormalizeDouble(newSL, digits) != NormalizeDouble(currentSL, digits) ||
NormalizeDouble(newTP, digits) != NormalizeDouble(currentTP, digits))
{
if(OrderModify(OrderTicket(), openPrice, newSL, newTP, 0, clrBlue))
{
Print("Initial SL/TP attached to Order #", OrderTicket());
currentSL = newSL; // Update local variable for immediate trailing check
}
else
{
Print("Error attaching Initial SL/TP to Order #", OrderTicket(), " - Error Code: ", GetLastError());
return; // Abort further processing for this tick if modification failed
}
}
}
//-----------------------------------------------------------------
// PHASE 2: Percentage-Based Trailing Stop Logic
//-----------------------------------------------------------------
if(!UseTrailingStop || Trail_Distance_Percent <= 0) return;
// Calculate the minimum price change required before sending a modify request
double trailStep = NormalizeDouble(openPrice * (Trail_Step_Percent / 100.0), digits);
if(OrderType() == OP_BUY)
{
double targetSL = NormalizeDouble(Bid * (1.0 - (Trail_Distance_Percent / 100.0)), digits);
// Ensure target stop does not violate broker StopLevel
if((Bid - targetSL) < minStopDistance)
targetSL = NormalizeDouble(Bid - minStopDistance, digits);
// Trailing Conditions for BUY:
// 1. targetSL is above the trade OpenPrice (locks in break-even + profit)
// 2. targetSL is higher than currentSL by at least Trail_Step_Percent
if(targetSL > openPrice && (currentSL == 0 || targetSL >= (currentSL + trailStep)))
{
if(OrderModify(OrderTicket(), openPrice, targetSL, currentTP, 0, clrGreen))
{
Print("Trailing Stop advanced for BUY Order #", OrderTicket(), " -> New SL: ", targetSL);
}
else
{
Print("Error trailing BUY Order #", OrderTicket(), " - Error Code: ", GetLastError());
}
}
}
else if(OrderType() == OP_SELL)
{
double targetSL = NormalizeDouble(Ask * (1.0 + (Trail_Distance_Percent / 100.0)), digits);
// Ensure target stop does not violate broker StopLevel
if((targetSL - Ask) < minStopDistance)
targetSL = NormalizeDouble(Ask + minStopDistance, digits);
// Trailing Conditions for SELL:
// 1. targetSL is below the trade OpenPrice (locks in break-even + profit)
// 2. targetSL is lower than currentSL by at least Trail_Step_Percent
if(targetSL < openPrice && (currentSL == 0 || targetSL <= (currentSL - trailStep)))
{
if(OrderModify(OrderTicket(), openPrice, targetSL, currentTP, 0, clrRed))
{
Print("Trailing Stop advanced for SELL Order #", OrderTicket(), " -> New SL: ", targetSL);
}
else
{
Print("Error trailing SELL Order #", OrderTicket(), " - Error Code: ", GetLastError());
}
}
}
}
//+------------------------------------------------------------------+
How the Production Safeguards Work
1. The Trailing Step (Trail_Step_Percent)
In high-volatility scalping, prices fluctuate by micro-points every millisecond. Without a trailing step, an EA will bombard your broker with OrderModify() requests for every 0.00001 price move. By defaulting Trail_Step_Percent to 0.02%, the EA waits until price moves significantly in your favor before moving the stop loss again. This keeps your execution logs clean and prevents server throttling.
2. Break-Even Validation (targetSL > openPrice)
The trailing stop logic is engineered specifically for scalping preservation: it will not begin trailing your stop loss until the calculated target stop has crossed better than break-even. This ensures that once trailing activates, a reversal cannot result in a principal loss (excluding severe slippage).
3. Broker StopLevel Compliance (MODE_STOPLEVEL)
Every MT4 broker dictates a minimum distance (in points) that pending orders or stop losses must maintain away from the current Bid/Ask price. During volatile news spikes, brokers often widen this StopLevel. The EA dynamically reads MODE_STOPLEVEL on every tick and clamps the proposed SL/TP to the minimum legal distance if your percentage calculation falls too close to the current price, completely preventing Error 130 (ERR_INVALID_STOPS).
Installation Steps
In MetaTrader 4, press F4 to launch MetaEditor.
1) In the Navigator window, right-click Experts -> New File -> Expert Advisor (template), and name it Auto_SL_TP_Trailing_EA.
2) Replace the entire generated template code with the MQL4 code above and click Compile.
3) Return to MT4, open your target scalping chart (e.g., EURUSD 1M), and ensure Auto Trading is enabled in the top toolbar (green play icon).
4) Drag the compiled EA from the Navigator panel onto your chart. Ensure "Allow live trading" is checked under the Common tab in the settings pop-up.