Die Candle Trailing Stop-Strategie ist eine StockSharp-Portierung des gleichnamigen MetaTrader-Expert-Advisors. Der ursprüngliche Roboter kombinierte Multi-Timeframe-Trendfilter, Momentum-Bestätigung und eine aggressive Trailing-Stop-Engine, die den Tiefs und Hochs der letzten Kerzen folgte. Die C#-Version behält denselben Workflow bei, stützt sich aber auf High-Level-StockSharp-Komponenten und macht alle wichtigen Einstellungen als Strategieparameter verfügbar.
Kernlogik
Datenabonnements
Der Handelszeitrahmen treibt Einstiege und Trailing-Stop-Updates an.
Ein höherer Zeitrahmen liefert Bestätigung mithilfe von linear gewichteten gleitenden Durchschnitten (LWMA) und einem Momentum-Indikator.
Ein drittes Abonnement berechnet eine MACD-Linie auf einem langsamen Zeitrahmen (standardmäßig monatlich) zum Filtern von Trades.
Trendausrichtung
Trades sind nur erlaubt, wenn die Sequenzen des schnellen, mittleren und langsamen LWMA auf beiden Zeitrahmen ausgerichtet sind (bullische Sequenz für Longs, bearische für Shorts).
Momentum-Gate
Der Momentum-Indikator muss bei mindestens einem der letzten drei Höherzeitrahmen-Balken in der Nähe des neutralen Werts 100 liegen.
MACD-Bestätigung
Longs erfordern, dass die MACD-Linie über der Signallinie liegt; Shorts erfordern die umgekehrte Beziehung.
Einstiegstrigger
Ein Ausbruch durch den schnellen LWMA im aktuellen Zeitrahmen (Kerze schließt über/unter dem Durchschnitt nach Berühren im vorherigen Balken) initiiert neue Trades unter Einhaltung eines konfigurierbaren Positionslimits.
Risiko- und Exit-Management
Anfängliche Stop-Loss- und Take-Profit-Abstände werden in Pips definiert und automatisch in Preisschritte umgerechnet.
Stops können auf Break-Even migrieren, hinter dem Extrem der letzten Kerzen nachziehen oder auf ein klassisches Trailing mit fester Distanz zurückfallen.
Optionale kapitalbasierte Features spiegeln den ursprünglichen EA wider: monetäres Take-Profit, prozentuales Take-Profit, Eigenkapital-Trailing und Drawdown-Schutz.
Parameter
Gruppe
Name
Beschreibung
Standard
Handel
Volume
Ordergröße in Lots/Kontrakten.
1
MaxTrades
Maximales aggregiertes Engagement ausgedrückt als Volume * MaxTrades.
10
Indikatoren
FastCurrentLength
Schneller LWMA im Handelszeitrahmen.
9
MiddleCurrentLength
Mittlerer LWMA im Handelszeitrahmen.
20
SlowCurrentLength
Langsamer LWMA im Handelszeitrahmen.
52
FastHigherLength
Schneller LWMA im höheren Zeitrahmen.
9
MiddleHigherLength
Mittlerer LWMA im höheren Zeitrahmen.
20
SlowHigherLength
Langsamer LWMA im höheren Zeitrahmen.
52
MomentumPeriod
Momentum-Periode im höheren Zeitrahmen.
14
MomentumBuyThreshold
Maximale Abweichung von 100 für Long-Trades.
0.3
MomentumSellThreshold
Maximale Abweichung von 100 für Short-Trades.
0.3
MacdFastLength
Schnelle EMA-Länge für MACD-Bestätigung.
12
MacdSlowLength
Langsame EMA-Länge für MACD-Bestätigung.
26
MacdSignalLength
Signal-EMA-Länge für MACD-Bestätigung.
9
Risiko
StopLossPips
Stop-Loss-Distanz in Pips.
20
TakeProfitPips
Take-Profit-Distanz in Pips.
50
UseMoveToBreakEven
Aktiviert die Break-Even-Logik.
true
BreakEvenTriggerPips
Gewinn in Pips erforderlich, bevor der Stop verschoben wird.
30
BreakEvenOffsetPips
Offset beim Verschieben des Stops auf Break-Even.
30
UseCandleTrail
Wählen zwischen kerzenbasiertem Trailing (true) oder klassischem Trailing (false).
true
CandleTrailLength
Anzahl der geschlossenen Kerzen zur Berechnung der Trailing-Extreme.
3
PadAmountPips
Zusätzlicher Puffer unterhalb/oberhalb des Trailing-Extrems.
10
TrailTriggerPips
Gewinn erforderlich, bevor das klassische Trailing aktiviert.
40
TrailAmountPips
Vom klassischen Trailing gehaltene Distanz.
40
Eigenkapitalregeln
UseMoneyTakeProfit
Alle Positionen schließen, wenn der schwebende Gewinn das monetäre Ziel überschreitet.
false
MoneyTakeProfit
Monetäres Gewinnziel.
40
UsePercentTakeProfit
Alle Positionen schließen, wenn der schwebende Gewinn das Prozentziel überschreitet.
false
PercentTakeProfit
Prozentsatz des Anfangskapitals als Gewinnziel.
10
EnableMoneyTrailing
Aktiviert Trailing des schwebenden Gewinns nach einem Schwellenwert.
true
MoneyTrailTarget
Gewinnniveau, das die monetäre Trailing-Logik einschaltet.
40
MoneyTrailStop
Maximaler erlaubter Rückgang nach Erreichen des Ziels.
10
UseEquityStop
Aktiviert Eigenkapital-Drawdown-Schutz.
true
EquityRiskPercent
Maximaler Drawdown vom Eigenkapitalhöchststand vor erzwungener Flat-Position.
1
Daten
CurrentCandleType
Handelszeitrahmen.
5m
HigherCandleType
Höherer Zeitrahmen für Filter.
30m
MacdCandleType
Zeitrahmen für MACD-Bestätigung (standardmäßig monatlich).
30d
Hinweise und Annahmen
Pips werden mit der Instrument-Tick-Größe in Preisschritte umgerechnet. Bei Symbolen, bei denen ein Pip von einem Tick abweicht, müssen möglicherweise die Standard-Pip-Abstände angepasst werden.
Monetäre Features basieren auf dem nicht realisierten Gewinn, angenähert als (Schlusskurs - Durchschnittspreis) * Position. Swap- und Provisionsanpassungen werden nicht simuliert.
Die Strategie verwendet Market-Orders für Einstiege und Ausstiege. Anfängliche Take-Profit-Orders werden registriert, sobald ein Trade geöffnet wird, während das Stop-Loss-Management intern gehandhabt wird und durch Market-Orders schließt, wenn das berechnete Niveau überschritten wird.
Alle Code-Kommentare sind gemäß den Projektrichtlinien auf Englisch verfasst.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class CandleTrailingStopStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public CandleTrailingStopStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class candle_trailing_stop_strategy(Strategy):
def __init__(self):
super(candle_trailing_stop_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(candle_trailing_stop_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(candle_trailing_stop_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return candle_trailing_stop_strategy()