Problem Description
Solution
Here is a robust and reusable Trailing Stop Loss function written in MQL4.
This code handles 4 and 5-digit brokers automatically, includes a "Step" parameter to prevent spamming the broker server with modification requests, and filters by Magic Number and Symbol.
MQL4 Trailing Stop Function
//+------------------------------------------------------------------+
//| Inputs |
//+------------------------------------------------------------------+
input int TrailingStopPips = 20; // Distance in Pips to trail price
input int TrailingStepPips = 5; // Minimum Pips change before modifying
input int MagicNumber = 123456; // Magic Number of your EA
//+------------------------------------------------------------------+
//| Function: ApplyTrailingStop |
//| Description: Adjusts SL based on current price movement |
//+------------------------------------------------------------------+
void ApplyTrailingStop()
{
// 1. Adjust for 4 or 5 digit brokers
double myPoint = Point;
if(Digits == 3 || Digits == 5) myPoint = Point * 10;
double trailingStopPoints = TrailingStopPips * myPoint;
double trailingStepPoints = TrailingStepPips * myPoint;
// 2. Loop through all open orders
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
// 3. Filter by Symbol and Magic Number
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
// --- BUY ORDERS ---
if(OrderType() == OP_BUY)
{
// Calculate the new proposed Stop Loss level
double newSL = NormalizeDouble(Bid - trailingStopPoints, Digits);
// Check if price is far enough above the open price to start trailing
// and if the new SL is higher than the current SL (plus the step)
if(Bid - OrderOpenPrice() > trailingStopPoints)
{
if(OrderStopLoss() < newSL - trailingStepPoints || OrderStopLoss() == 0)
{
// Modify the order
bool res = OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrGreen);
if(!res) Print("Error modifying Buy Trailing Stop: ", GetLastError());
}
}
}
// --- SELL ORDERS ---
else if(OrderType() == OP_SELL)
{
// Calculate the new proposed Stop Loss level
double newSL = NormalizeDouble(Ask + trailingStopPoints, Digits);
// Check if price is far enough below the open price
// and if the new SL is lower than the current SL (minus the step)
if(OrderOpenPrice() - Ask > trailingStopPoints)
{
if(OrderStopLoss() > newSL + trailingStepPoints || OrderStopLoss() == 0)
{
// Modify the order
bool res = OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrRed);
if(!res) Print("Error modifying Sell Trailing Stop: ", GetLastError());
}
}
}
}
}
}
}
How to Use This Code
- Copy the Inputs section to the top of your EA (Global scope).
- Copy the
ApplyTrailingStopfunction to the bottom of your EA file. - Call the function inside
OnTick().
Example Implementation:
void OnTick()
{
// ... your entry logic ...
// Call the trailing stop function on every tick
ApplyTrailingStop();
}
Key Features of This Implementation
- Point Adjustment: It automatically detects if the broker uses 3/5 digits (Jpy/Standard) or 2/4 digits and adjusts the
Pointvalue accordingly. This ensures that "20 Pips" acts as 20 Pips, not 2 Pips or 200 Pips. - Trailing Step: The
TrailingStepPipsparameter ensures the EA does not try to modify the order on every single tick (which can get your account banned by brokers for server spamming). It waits until the price has moved by the "Step" amount before updating. - Normalization: It uses
NormalizeDouble(..., Digits)on the calculated Stop Loss price. This is crucial in MQL4 to prevent "Invalid Price" (Error 129) errors. - Logic Check:
- For Buys: It only moves the Stop Loss Up.
- For Sells: It only moves the Stop Loss Down.
- It handles the case where
OrderStopLoss() == 0(no initial Stop Loss set).
Q&A: Quantitative Trading & MQL4
Q: Why is NormalizeDouble necessary in MQL4 order modification?
A: Financial markets use floating-point numbers. A calculated price might result in 1.12345000001. If you send this to the broker server when the symbol only supports 5 decimal places, the server will reject it with an "Invalid Price" error. NormalizeDouble rounds the value to the specific precision supported by the symbol.
Q: What is the difference between a Trailing Stop and a Break Even function?
A: A Break Even function moves the Stop Loss to the entry price (plus spread/commissions) once a specific profit target is reached and stops there. A Trailing Stop continues to move the Stop Loss behind the price as the trade moves further into profit, attempting to capture the maximum trend run.
Q: Can this function handle multiple currency pairs on one chart?
A: No. This specific implementation uses OrderSymbol() == Symbol(), meaning it only manages trades for the chart the EA is currently attached to. To manage all pairs from a single chart, you would remove the OrderSymbol() == Symbol() check, but you must then ensure Point and Digits are retrieved dynamically for each order's specific symbol using MarketInfo().
Q: Why do we iterate through orders in reverse (i--)?
A: While strictly necessary only when deleting orders (to avoid index shifting issues), it is a best practice in MQL4 programming to iterate backwards through the order pool. It ensures stability if you decide to add closing logic (like a basket close) within the same loop later.