EMA-Kreuzungssignal-Strategie
Diese Strategie handelt auf die Kreuzung zweier exponentieller gleitender Durchschnitte (EMA). Ein schnellerer EMA und ein langsamerer EMA werden aus der gewählten Kerzenserie berechnet. Wenn der schnelle EMA den langsamen EMA von unten nach oben kreuzt, kann die Strategie eine bestehende Short-Position schließen und optional eine Long-Position eröffnen. Wenn der schnelle EMA den langsamen EMA von oben nach unten kreuzt, kann sie eine Long-Position schließen und optional eine Short-Position eröffnen.
Zum Risikomanagement erlaubt die Strategie das Setzen von Take-Profit- und Stop-Loss-Orders nach dem Eröffnen einer neuen Position. Beide Abstände werden in Ticks angegeben. Diese Schutzorders werden bei jedem neuen Einstieg storniert und neu erstellt.
Die Strategie bietet separate Schalter zum Aktivieren oder Deaktivieren von Long- und Short-Einstiegen sowie zum unabhängigen Schließen von Long- und Short-Positionen beim entgegengesetzten Signal. Alle Berechnungen verwenden nur abgeschlossene Kerzen.
Parameter
- Fast Period – Länge des schnellen EMA.
- Slow Period – Länge des langsamen EMA.
- Candle Type – Zeitrahmen der für Berechnungen verwendeten Kerzen.
- Allow Buy Open – Long öffnen, wenn der schnelle EMA den langsamen EMA von unten nach oben kreuzt.
- Allow Sell Open – Short öffnen, wenn der schnelle EMA den langsamen EMA von oben nach unten kreuzt.
- Allow Buy Close – Long schließen, wenn der schnelle EMA den langsamen EMA von oben nach unten kreuzt.
- Allow Sell Close – Short schließen, wenn der schnelle EMA den langsamen EMA von unten nach oben kreuzt.
- Take Profit Ticks – Take-Profit-Abstand in Ticks vom Einstiegspreis.
- Stop Loss Ticks – Stop-Loss-Abstand in Ticks vom Einstiegspreis.
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;
/// <summary>
/// Trades EMA crossovers with optional separate entry and exit permissions for long and short positions.
/// </summary>
public class EmaCrossoverSignalStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _allowBuyOpen, _allowSellOpen, _allowBuyClose, _allowSellClose;
private readonly StrategyParam<decimal> _stopLossTicks, _takeProfitTicks;
private Order _pending;
private bool _isInitialized;
private bool _wasFastAboveSlow;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public bool AllowBuyOpen { get => _allowBuyOpen.Value; set => _allowBuyOpen.Value = value; }
public bool AllowSellOpen { get => _allowSellOpen.Value; set => _allowSellOpen.Value = value; }
public bool AllowBuyClose { get => _allowBuyClose.Value; set => _allowBuyClose.Value = value; }
public bool AllowSellClose { get => _allowSellClose.Value; set => _allowSellClose.Value = value; }
public decimal StopLossTicks { get => _stopLossTicks.Value; set => _stopLossTicks.Value = value; }
public decimal TakeProfitTicks { get => _takeProfitTicks.Value; set => _takeProfitTicks.Value = value; }
public EmaCrossoverSignalStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Length of the fast EMA", "EMA");
_slowPeriod = Param(nameof(SlowPeriod), 13)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Length of the slow EMA", "EMA");
_allowBuyOpen = Param(nameof(AllowBuyOpen), true);
_allowSellOpen = Param(nameof(AllowSellOpen), true);
_allowBuyClose = Param(nameof(AllowBuyClose), true);
_allowSellClose = Param(nameof(AllowSellClose), true);
_stopLossTicks = Param(nameof(StopLossTicks), 1000m).SetNotNegative();
_takeProfitTicks = Param(nameof(TakeProfitTicks), 2000m).SetNotNegative();
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for calculations", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_isInitialized = default;
_wasFastAboveSlow = default;
_pending = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_pending = null;
var step = Security?.PriceStep ?? 1m;
StartProtection(takeProfit: new Unit(TakeProfitTicks * step, UnitTypes.Absolute), stopLoss: new Unit(StopLossTicks * step, UnitTypes.Absolute), useMarketOrders: true);
var fastEma = new ExponentialMovingAverage { Length = FastPeriod };
var slowEma = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, Process)
.Start();
}
private void Process(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_isInitialized)
{
_wasFastAboveSlow = fastValue > slowValue;
_isInitialized = true;
return;
}
var isFastAboveSlow = fastValue > slowValue;
var crossing = _wasFastAboveSlow != isFastAboveSlow;
_wasFastAboveSlow = isFastAboveSlow;
if (!crossing || !IsFormedAndOnlineAndAllowTrading() || (_pending is not null && _pending.State is not (OrderStates.Done or OrderStates.Failed))) return;
if (isFastAboveSlow)
{
if (Position < 0m && AllowSellClose) _pending = BuyMarket(-Position + (AllowBuyOpen ? Volume : 0m));
else if (Position == 0m && AllowBuyOpen) _pending = BuyMarket(Volume);
}
else
{
if (Position > 0m && AllowBuyClose) _pending = SellMarket(Position + (AllowSellOpen ? Volume : 0m));
else if (Position == 0m && AllowSellOpen) _pending = SellMarket(Volume);
}
}
}
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, Unit, UnitTypes, OrderStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ema_crossover_signal_strategy(Strategy):
def __init__(self):
super(ema_crossover_signal_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 5) \
.SetDisplay("Fast Period", "Length of the fast EMA", "EMA")
self._slow_period = self.Param("SlowPeriod", 13) \
.SetDisplay("Slow Period", "Length of the slow EMA", "EMA")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for calculations", "General")
self._allow_buy_open = self.Param("AllowBuyOpen", True)
self._allow_sell_open = self.Param("AllowSellOpen", True)
self._allow_buy_close = self.Param("AllowBuyClose", True)
self._allow_sell_close = self.Param("AllowSellClose", True)
self._stop_loss_ticks = self.Param("StopLossTicks", 1000.0)
self._take_profit_ticks = self.Param("TakeProfitTicks", 2000.0)
self._pending = None
self._is_initialized = False
self._was_fast_above_slow = False
@property
def FastPeriod(self):
return self._fast_period.Value
@FastPeriod.setter
def FastPeriod(self, value):
self._fast_period.Value = value
@property
def SlowPeriod(self):
return self._slow_period.Value
@SlowPeriod.setter
def SlowPeriod(self, value):
self._slow_period.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(ema_crossover_signal_strategy, self).OnStarted2(time)
self._pending = None
step = self.Security.PriceStep if self.Security.PriceStep and self.Security.PriceStep > 0 else 1
self.StartProtection(takeProfit=Unit(self._take_profit_ticks.Value * step, UnitTypes.Absolute), stopLoss=Unit(self._stop_loss_ticks.Value * step, UnitTypes.Absolute), useMarketOrders=True)
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.FastPeriod
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.SlowPeriod
self.SubscribeCandles(self.CandleType) \
.Bind(fast_ema, slow_ema, self.ProcessCandle) \
.Start()
def ProcessCandle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast = float(fast_value)
slow = float(slow_value)
if not self._is_initialized:
self._was_fast_above_slow = fast > slow
self._is_initialized = True
return
is_fast_above_slow = fast > slow
crossing = self._was_fast_above_slow != is_fast_above_slow
self._was_fast_above_slow = is_fast_above_slow
if not crossing or not self.IsFormedAndOnlineAndAllowTrading() or (self._pending is not None and self._pending.State not in (OrderStates.Done, OrderStates.Failed)):
return
if is_fast_above_slow:
if self.Position < 0 and self._allow_sell_close.Value:
self._pending = self.BuyMarket(-self.Position + (self.Volume if self._allow_buy_open.Value else 0))
elif self.Position == 0 and self._allow_buy_open.Value:
self._pending = self.BuyMarket(self.Volume)
else:
if self.Position > 0 and self._allow_buy_close.Value:
self._pending = self.SellMarket(self.Position + (self.Volume if self._allow_sell_open.Value else 0))
elif self.Position == 0 and self._allow_sell_open.Value:
self._pending = self.SellMarket(self.Volume)
def OnReseted(self):
super(ema_crossover_signal_strategy, self).OnReseted()
self._is_initialized = False
self._was_fast_above_slow = False
def CreateClone(self):
return ema_crossover_signal_strategy()