Trailing Stop EA for MT5: Logic, Code and Pitfalls
Short answer: a trailing stop EA moves a position's stop loss behind price as the trade moves in your favour — and never back. It needs three settings: an activation distance (how much profit before trailing starts), a trail distance (how far behind price the stop sits) and a step (the minimum move before the stop is modified again). The step is what stops the EA sending a modification on every tick.
Terminal trailing stop vs EA trailing stop
MetaTrader has a built-in trailing stop: right-click a position → Trailing Stop. It is simple and has two limits: it only works while that terminal is running, and it only offers a distance — no activation threshold, no step, no logic such as "trail by ATR".
An EA trailing stop also needs the terminal running (on a VPS if you trade around the clock), but can trail by any rule you can express: fixed points, ATR, the previous candle's low, a moving average.
The three settings
| Setting | Example | What it does |
|---|---|---|
| Activation | 300 points | Trailing starts only once the trade is this far in profit |
| Distance | 200 points | The stop is placed this far behind the current price |
| Step | 50 points | The stop moves only if the new level is at least this much better |
Without an activation, a trailing stop can tighten a fresh trade's stop before it has room to breathe. Without a step, the EA modifies the stop on every tick, floods the server with requests and hits trade-frequency limits.
MQL5 code for a trailing stop
This version trails only this EA's positions on the chart's symbol, and only ever moves the stop in the trade's favour.
#include <Trade\Trade.mqh>
CTrade trade;
input int TrailStart = 300; // points of profit before trailing starts
input int TrailDistance = 200; // points between price and the stop
input int TrailStep = 50; // minimum improvement before modifying
input long MagicNumber = 12345;
void TrailPositions()
{
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i); // also selects the position
if(ticket == 0) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
double open = PositionGetDouble(POSITION_PRICE_OPEN);
double sl = PositionGetDouble(POSITION_SL);
double tp = PositionGetDouble(POSITION_TP);
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(bid - open < TrailStart * point) continue;
double newSl = NormalizeDouble(bid - TrailDistance * point, digits);
if(sl == 0.0 || newSl >= sl + TrailStep * point)
trade.PositionModify(ticket, newSl, tp);
}
else
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(open - ask < TrailStart * point) continue;
double newSl = NormalizeDouble(ask + TrailDistance * point, digits);
if(sl == 0.0 || newSl <= sl - TrailStep * point)
trade.PositionModify(ticket, newSl, tp);
}
}
}
Call TrailPositions() from OnTick(), and call trade.SetExpertMagicNumber(MagicNumber)
in OnInit() so the EA's own trades carry the number it filters on.
Pitfalls that cost trailing stops
- The stops level. The trail distance must be larger than the symbol's minimum stop distance, or every modification fails with invalid stops (retcode 10016).
- The freeze level. Close to the current stop, some brokers refuse any modification at all.
- Buys trail from Bid, sells from Ask. Using the wrong side makes sells trail a spread's width too tight.
- No magic filter. The loop above would otherwise move stops on your manual trades and every other EA's.
- Retcode 10025, "no changes". Sending the same stop again is an error, not a no-op — another reason for the step.
Trailing stops in TradeSmithy
In a TradeSmithy graph, trade management runs off the Positions & Orders
node, which reads this EA's open position — its ticket, direction, open price and
current stop — and a Modify Order node, which changes a position's stop loss.
The quickest route is to describe it to the AI assistant: "add a trailing stop
that starts at 300 points of profit and trails 200 points behind price". Whatever
builds your EA, read the exported PositionModify call and check it against the
logic above before trusting it with money.
Start free to build the strategy and export it; the AI assistant and server backtests are on Pro. Pair a trailing stop with position sizing from balance, and see prop firm EA rules if you trade a challenge.
Frequently asked questions
Does a trailing stop work when MetaTrader is closed?
No. Both the terminal's trailing stop and an EA's run in the terminal. The last stop they set stays on the server, but stops moving.
What is a good trailing stop distance?
One the market's normal noise does not hit. Many traders scale it to volatility with ATR rather than a fixed number of points.
Why does my trailing stop EA give "invalid stops"?
The new stop is closer to price than the symbol's minimum stop distance. Increase the trail distance.
Should I use a trailing stop or a fixed take profit?
A trailing stop lets winners run and gives some back on the reversal; a fixed target banks a set amount. Test both on the same strategy.
Keep reading
Building an EA for Gold (XAUUSD): What Is Different
Gold is not a currency pair with a bigger price. Contract size, point value, spread and volatility change how a gold EA must size and place its stops.
4 min readProp Firm EA Rules: Build an EA That Survives a Challenge
Most prop firms allow Expert Advisors, with limits that end accounts fast. The rules EAs break most, and how to build daily loss and drawdown guards in.
5 min readGrid vs Martingale EA: The Math Behind the Blow-Up
Grid and martingale EAs post smooth equity curves until they don't. The arithmetic of lot growth and losing streaks, and what to do if you still want one.
4 min read