Blog

Trailing Stop EA for MT5: Logic, Code and Pitfalls

TradeSmithy Team4 min read

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

SettingExampleWhat it does
Activation300 pointsTrailing starts only once the trade is this far in profit
Distance200 pointsThe stop is placed this far behind the current price
Step50 pointsThe 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

← More from the TradeSmithy blog