Universelle Trailing-Stop-Strategie
Diese Strategie repliziert die Kernidee des originalen MQL4-Skripts cm_universal_trailing_stop.mq4. Sie generiert keine Einstiegssignale, sondern verwaltet eine bestehende Position, indem sie den Stop-Loss in Richtung des Gewinns verschiebt.
Der Algorithmus hält einen Abstand vom aktuellen Preis und verschiebt den Stop jedes Mal, wenn sich der Markt um einen konfigurierbaren Schritt bewegt. Sobald die minimale Gewinnschwelle erreicht ist, wird der Trailing-Stop aktiv und folgt dem Preis automatisch für Long- und Short-Positionen.
Details
- Einstiegskriterien: keine. Die Position sollte manuell oder durch eine andere Strategie eröffnet werden.
- Long/Short: beide.
- Ausstiegskriterien: Stop-Order ausgelöst, wenn der Preis um den konfigurierten Abstand zurückläuft.
- Stops: Trailing-Stop basierend auf Punkten.
- Parameter:
Delta– Abstand vom Preis zum Stop in Punkten.Step– minimale Preisbewegung in Punkten zum Verschieben des Stops.StartProfit– Gewinn in Punkten, der zum Aktivieren des Trailings erforderlich ist.CandleType– Zeitrahmen für die Trailing-Berechnungen.
- Filter:
- Kategorie: Risikomanagement
- Richtung: Beide
- Indikatoren: Keine
- Stops: Trailing
- Komplexität: Einfach
- Zeitrahmen: Beliebig
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that enters on EMA crossover and manages position with a trailing stop.
/// Uses two EMAs for entries and a price-based trailing stop for exits.
/// </summary>
public class UniversalTrailingStopStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<decimal> _trailPercent;
private decimal _prevFast;
private decimal _prevSlow;
private bool _isInitialized;
private decimal _entryPrice;
private decimal _bestPrice;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public decimal TrailPercent
{
get => _trailPercent.Value;
set => _trailPercent.Value = value;
}
public UniversalTrailingStopStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 10)
.SetDisplay("Fast Period", "Fast EMA period", "Entry");
_slowPeriod = Param(nameof(SlowPeriod), 30)
.SetDisplay("Slow Period", "Slow EMA period", "Entry");
_trailPercent = Param(nameof(TrailPercent), 1.5m)
.SetDisplay("Trail %", "Trailing stop distance in percent", "Trailing");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0;
_prevSlow = 0;
_isInitialized = false;
_entryPrice = 0;
_bestPrice = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0;
_prevSlow = 0;
_isInitialized = false;
_entryPrice = 0;
_bestPrice = 0;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_isInitialized)
{
_prevFast = fastValue;
_prevSlow = slowValue;
_isInitialized = true;
return;
}
var price = candle.ClosePrice;
// Trailing stop check
if (Position > 0)
{
if (price > _bestPrice)
_bestPrice = price;
var stopLevel = _bestPrice * (1 - TrailPercent / 100m);
if (price <= stopLevel)
{
SellMarket();
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0)
{
if (price < _bestPrice)
_bestPrice = price;
var stopLevel = _bestPrice * (1 + TrailPercent / 100m);
if (price >= stopLevel)
{
BuyMarket();
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
// Entry signals: EMA crossover
var prevCrossUp = _prevFast <= _prevSlow;
var currCrossUp = fastValue > slowValue;
var prevCrossDown = _prevFast >= _prevSlow;
var currCrossDown = fastValue < slowValue;
if (prevCrossUp && currCrossUp && !(_prevFast > _prevSlow))
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
{
BuyMarket();
_entryPrice = price;
_bestPrice = price;
}
}
else if (prevCrossDown && currCrossDown && !(_prevFast < _prevSlow))
{
if (Position > 0)
SellMarket();
if (Position >= 0)
{
SellMarket();
_entryPrice = price;
_bestPrice = price;
}
}
_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 universal_trailing_stop_strategy(Strategy):
def __init__(self):
super(universal_trailing_stop_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._fast_period = self.Param("FastPeriod", 10) \
.SetDisplay("Fast Period", "Fast EMA period", "Entry")
self._slow_period = self.Param("SlowPeriod", 30) \
.SetDisplay("Slow Period", "Slow EMA period", "Entry")
self._trail_percent = self.Param("TrailPercent", 1.5) \
.SetDisplay("Trail %", "Trailing stop distance in percent", "Trailing")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._is_initialized = False
self._entry_price = 0.0
self._best_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def trail_percent(self):
return self._trail_percent.Value
def OnReseted(self):
super(universal_trailing_stop_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._is_initialized = False
self._entry_price = 0.0
self._best_price = 0.0
def OnStarted2(self, time):
super(universal_trailing_stop_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._is_initialized = False
self._entry_price = 0.0
self._best_price = 0.0
fast = ExponentialMovingAverage()
fast.Length = self.fast_period
slow = ExponentialMovingAverage()
slow.Length = self.slow_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_value = float(fast_value)
slow_value = float(slow_value)
if not self._is_initialized:
self._prev_fast = fast_value
self._prev_slow = slow_value
self._is_initialized = True
return
price = float(candle.ClosePrice)
trail_pct = float(self.trail_percent)
# Trailing stop check
if self.Position > 0:
if price > self._best_price:
self._best_price = price
stop_level = self._best_price * (1.0 - trail_pct / 100.0)
if price <= stop_level:
self.SellMarket()
self._prev_fast = fast_value
self._prev_slow = slow_value
return
elif self.Position < 0:
if price < self._best_price:
self._best_price = price
stop_level = self._best_price * (1.0 + trail_pct / 100.0)
if price >= stop_level:
self.BuyMarket()
self._prev_fast = fast_value
self._prev_slow = slow_value
return
# Entry signals: EMA crossover
prev_cross_up = self._prev_fast <= self._prev_slow
curr_cross_up = fast_value > slow_value
prev_cross_down = self._prev_fast >= self._prev_slow
curr_cross_down = fast_value < slow_value
if prev_cross_up and curr_cross_up and not (self._prev_fast > self._prev_slow):
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
self._entry_price = price
self._best_price = price
elif prev_cross_down and curr_cross_down and not (self._prev_fast < self._prev_slow):
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._entry_price = price
self._best_price = price
self._prev_fast = fast_value
self._prev_slow = slow_value
def CreateClone(self):
return universal_trailing_stop_strategy()