Die Trend-Reversal-Strategie ist ein richtungsorientiertes System, das versucht, Ausbrüche nach einem kurzfristigen Pullback innerhalb eines bestehenden Trends zu erfassen. Sie wurde aus dem MetaTrader Expert Advisor "Trend Reversal" portiert und für die High-Level-API von StockSharp neu geschrieben. Die Umwandlung bewahrt den Kern der Bestätigungskette (gleitende Durchschnitte, Momentum und MACD), ersetzt jedoch die ursprünglichen grafischen Linienfilter durch Preisüberlappungsprüfungen, die sich programmatisch einfacher reproduzieren lassen.
Indikatorstapel
Linear gewichtete gleitende Durchschnitte (LWMA) auf typischem Preis mit anpassbaren schnellen und langsamen Längen. Die schnelle Linie verfolgt den neuesten Swing, während die langsame Linie den dominanten Trend identifiziert.
Momentum-Oszillator, berechnet auf demselben Zeitrahmen. Die Strategie zeichnet die absolute Distanz vom neutralen Niveau 100 für die letzten drei geschlossenen Kerzen auf, um die MetaTrader-Logik nachzubilden.
MACD-Signallinienpaar, konfiguriert mit unabhängigen schnellen, langsamen und Signal-Längen. Die Histogrammrichtung wird als Bestätigung auf höherer Ebene für Long- und Short-Trades verwendet.
Handelslogik
Auf eine abgeschlossene Kerze im konfigurierten Zeitrahmen warten. Teilweise gebildete Bars werden ignoriert.
Sicherstellen, dass beide LWMAs und der Momentum-Indikator vollständig ausgebildet sind. Ohne genügend Historie bleibt das System flat.
Eine rollierende Queue der drei jüngsten Momentum-Abweichungen von 100 führen. Ein Setup ist nur gültig, wenn mindestens einer dieser Werte den jeweiligen Kauf- oder Verkaufsschwellenwert überschreitet.
Verlangen, dass die Kerze vor zwei Bars ein tieferes Tief als das Hoch der vorherigen Kerze hat. Dadurch wird die "überlappende" Struktur des ursprünglichen EA nachgebildet, die eine enge Konsolidierung vor dem Ausbruch erkennt.
Richtungsfilter bewerten:
Long: schnelle LWMA über langsamer LWMA und MACD-Hauptwert über der Signallinie.
Short: schnelle LWMA unter langsamer LWMA und MACD-Hauptwert unter der Signallinie.
Das Nettopositionslimit beachten. Die Strategie steigt nur ein oder baut eine Position aus, wenn die absolute Exposure (aktuelle Position geteilt durch Handelsvolumen) unter dem konfigurierten Wert MaxPositions liegt.
Orders werden mit BuyMarket() oder SellMarket() gesendet, wodurch je nach aktueller Exposure teilweise oder vollständige Umkehrungen möglich sind.
Risikomanagement
Optionale Take-Profit- und Stop-Loss-Distanzen (in Preiseinheiten) können über den integrierten Schutzblock von StockSharp angehängt werden. Beide Niveaus sind deaktiviert, wenn ein Parameter auf null gesetzt ist.
Diese Portierung enthält keinen automatischen Trailing Stop und keine Break-even-Anpassung. Diese Funktionen können bei Bedarf mit zusätzlichen Event-Handlern implementiert werden.
Parameter
Name
Beschreibung
Standard
CandleType
Primärer Zeitrahmen zum Erstellen der Kerzen.
15-Minuten-Zeitrahmen
FastLength
Periode der schnellen LWMA.
6
SlowLength
Periode der langsamen LWMA.
85
MomentumLength
Periode des Momentum-Oszillators.
14
MomentumBuyThreshold
Minimale absolute Momentum-Abweichung (von 100), die ein Long-Setup validiert.
0.3
MomentumSellThreshold
Minimale absolute Momentum-Abweichung (von 100), die ein Short-Setup validiert.
0.3
MacdFastLength
Schnelle EMA-Periode im MACD-Filter.
12
MacdSlowLength
Langsame EMA-Periode im MACD-Filter.
26
MacdSignalLength
Signal-EMA-Periode im MACD-Filter.
9
TakeProfit
Take-Profit-Distanz in Preiseinheiten. Auf 0 setzen, um zu deaktivieren.
50
StopLoss
Stop-Loss-Distanz in Preiseinheiten. Auf 0 setzen, um zu deaktivieren.
20
TradeVolume
Ordervolumen in Lots.
1
MaxPositions
Maximale Anzahl von Handelsvolumen-Einheiten in der Nettoposition.
1
Nutzungshinweise
Binden Sie die Strategie an ein Wertpapier mit gültigen Schritt- und Preisinformationen, damit Schutzorders korrekt funktionieren.
Für multidirektionales Trading (Pyramiding oder Skalieren) erhöhen Sie MaxPositions. Die Strategie fügt weiter Positionen hinzu, solange die Filter gültig bleiben und die Exposure innerhalb dieses Limits bleibt.
Backtests sollten mit demselben Kerzenzeitrahmen durchgeführt werden, den der Parameter CandleType angibt. StockSharp fordert beim Start der Strategie automatisch die passenden Daten an.
Da die MetaTrader-Version von handgezeichneten Trendlinien abhing, ersetzt diese Neufassung diese Prüfungen durch eine deterministische Kerzenüberlappung. Dadurch bleibt das Verhalten zwischen Backtests und Live-Ausführung konsistent.
Unterschiede zum ursprünglichen EA
Trailing Stop, Break-even-Bewegungen und equitybasierte Notausstiege sind nicht implementiert, damit das Beispiel auf die zentrale Signalgenerierung fokussiert bleibt.
Money-Management-Funktionen wie Lot-Multiplikation und Magic-Number-Filterung sind in StockSharp nicht erforderlich und wurden daher entfernt.
Die MACD-Bestätigung verwendet denselben Zeitrahmen wie die Handelskerzen statt der ursprünglichen monatlichen Aggregation. Sie können das Multi-Timeframe-Setup nachbilden, indem Sie einen langsameren Kerzentyp abonnieren und den MACD-Filter an diese Subscription binden.
Optimierungstipps
Optimieren Sie zuerst die Längen der gleitenden Durchschnitte passend zum dominanten Marktzyklus und verfeinern Sie danach die Momentum-Schwellenwerte.
Experimentieren Sie bei volatilen Instrumenten mit breiteren Stop-Loss- und Take-Profit-Distanzen. Da die Logik trendfolgend ist, verbessern größere Ausstiegspuffer häufig die Profitabilität.
Überwachen Sie Drawdown-Statistiken während der Optimierungsläufe. Ein höheres MaxPositions kann die Reaktionsfähigkeit verbessern, vergrößert aber auch das Risiko.
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 TrendReversalStrategy : 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 TrendReversalStrategy()
{
_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 trend_reversal_strategy(Strategy):
def __init__(self):
super(trend_reversal_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(trend_reversal_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(trend_reversal_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 trend_reversal_strategy()