Trailing Stop¶
A stop that follows price as it moves in your favor, locking in profits from large moves.
Complete Example¶
from replaybt import BacktestEngine, CSVProvider, Strategy, MarketOrder, Side
class TrailingStopStrategy(Strategy):
def configure(self, config):
self._prev_fast = self._prev_slow = None
def on_bar(self, bar, indicators, positions):
fast = indicators.get("ema_fast")
slow = indicators.get("ema_slow")
if fast is None or slow is None or self._prev_fast is None:
self._prev_fast, self._prev_slow = fast, slow
return None
crossed_up = fast > slow and self._prev_fast <= self._prev_slow
self._prev_fast, self._prev_slow = fast, slow
if not positions and crossed_up:
return MarketOrder(
side=Side.LONG,
stop_loss_pct=0.05, # safety SL at 5%
trailing_stop_pct=0.02, # trail 2% below peak
trailing_stop_activation_pct=0.03, # activate after +3%
)
return None
engine = BacktestEngine(
strategy=TrailingStopStrategy(),
data=CSVProvider("ETH_1m.csv", symbol_name="ETH"),
config={
"initial_equity": 10_000,
"indicators": {
"ema_fast": {"type": "ema", "period": 15, "source": "close"},
"ema_slow": {"type": "ema", "period": 35, "source": "close"},
},
},
)
results = engine.run()
print(results.summary())
How It Works¶
Entry at $2,000 (LONG)
Safety SL = $1,900 (5%)
Trail activates at $2,060 (+3%)
Trail distance = 2%
Price path:
$2,000 → $2,060: trail activates, trail stop = $2,018.80
$2,060 → $2,100: trail stop moves up to $2,058.00
$2,100 → $2,150: trail stop moves up to $2,107.00
$2,150 → $2,110: trail stop at $2,107.00 — EXIT at $2,107
Profit: +5.35% (vs 8% if you'd used a fixed TP at $2,160)
But captures most of a +7.5% move without predicting the top
Parameters¶
| Parameter | Description | Typical Value |
|---|---|---|
trailing_stop_pct |
Distance from peak (LONG) or trough (SHORT) | 0.02 (2%) |
trailing_stop_activation_pct |
Minimum profit to activate trailing | 0.03 (3%) |
With Fixed TP¶
You can combine a trailing stop with a fixed take profit:
MarketOrder(
side=Side.LONG,
take_profit_pct=0.10, # cap at +10%
stop_loss_pct=0.05, # safety SL
trailing_stop_pct=0.02, # trail 2%
trailing_stop_activation_pct=0.04, # activate at +4%
)
Whichever triggers first (fixed TP or trailing stop) closes the position.
Without Activation Threshold¶
Set trailing_stop_activation_pct=0 to start trailing immediately from entry:
MarketOrder(
side=Side.LONG,
trailing_stop_pct=0.03,
trailing_stop_activation_pct=0.0, # active from bar 1
)
Warning
Immediate trailing can trigger premature exits in choppy markets. An activation threshold gives the trade room to develop before the trail starts.