EMA/SMA + RSI Kreuzungs-Strategie
Diese Strategie verfolgt drei exponentielle gleitende Durchschnitte (schnell, mittel und langsam) zusammen mit einem RSI-Filter, um an entstehenden Trends teilzuhaben. Ein Trade wird ausgelöst, wenn der schnelle Durchschnitt den mittleren in Richtung des vorherrschenden langsamen Durchschnitts kreuzt, was darauf hindeutet, dass das Momentum zunimmt. Nur Kerzen, die in Richtung der Kreuzung schließen, werden berücksichtigt, um Whipsaws zu vermeiden.
Ein schützender Ausstieg kann Positionen optional nach einer benutzerdefinerten Anzahl von Bars schließen, wenn sie profitabel bleiben. Der RSI dient als Überkauft-/Überverkauft- Schutz, um auszusteigen, wenn das Momentum zu weit gedehnt wird.
Backtests zeigen, dass die Technik am besten an liquiden Krypto-Paaren während Trendphasen funktioniert, bei denen gleitende Durchschnitte eine klare Trennung bieten.
Details
- Einstiegskriterien:
- Long:
EMA_fast > EMA_mediumundEMA_fast(t-1) <= EMA_medium(t-1)undClose > EMA_slowundClose > Open - Short:
EMA_fast < EMA_mediumundEMA_fast(t-1) >= EMA_medium(t-1)undClose < EMA_slowundClose < Open
- Long:
- Long/Short: Beide Seiten.
- Ausstiegskriterien:
- Long:
RSI > 70oderX Bars im Gewinn und Close > entry - Short:
RSI < 30oderX Bars im Gewinn und Close < entry
- Long:
- Stops: Keine.
- Standardwerte:
EMA_fast= 10EMA_medium= 20EMA_slow= 100RSI_length= 14X bars= 24
- Filter:
- Kategorie: Trendfolge
- Richtung: Beide
- Indikatoren: EMA, RSI
- Stops: Optional zeitbasiert
- Komplexität: Mittel
- Zeitrahmen: Kurzfristig
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
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>
/// EMA/SMA + RSI crossover strategy.
/// Goes long when the fast EMA crosses above the medium EMA on a bullish candle closing above the slow EMA, and short
/// on the mirrored setup. A long closes when RSI rises above 70 or when it has been held XBars bars and is in profit;
/// a short closes when RSI falls below 30 or after XBars bars in profit.
/// </summary>
public class EmaSmaRsiStrategy : Strategy
{
private const decimal _rsiOverbought = 70m;
private const decimal _rsiOversold = 30m;
private readonly StrategyParam<int> _emaFast;
private readonly StrategyParam<int> _emaMedium;
private readonly StrategyParam<int> _emaSlow;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _xBars;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevFast;
private decimal? _prevMedium;
private decimal _entryPrice;
private int _barsInPosition;
/// <summary>
/// Fast EMA period.
/// </summary>
public int EMA_fast
{
get => _emaFast.Value;
set => _emaFast.Value = value;
}
/// <summary>
/// Medium EMA period.
/// </summary>
public int EMA_medium
{
get => _emaMedium.Value;
set => _emaMedium.Value = value;
}
/// <summary>
/// Slow EMA period.
/// </summary>
public int EMA_slow
{
get => _emaSlow.Value;
set => _emaSlow.Value = value;
}
/// <summary>
/// RSI period.
/// </summary>
public int RSI_length
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// Bars after which a profitable position is closed. 0 disables the time exit.
/// </summary>
public int XBars
{
get => _xBars.Value;
set => _xBars.Value = value;
}
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public EmaSmaRsiStrategy()
{
_emaFast = Param(nameof(EMA_fast), 10)
.SetGreaterThanZero()
.SetDisplay("EMA Fast", "Fast EMA period", "Moving Averages");
_emaMedium = Param(nameof(EMA_medium), 20)
.SetGreaterThanZero()
.SetDisplay("EMA Medium", "Medium EMA period", "Moving Averages");
_emaSlow = Param(nameof(EMA_slow), 100)
.SetGreaterThanZero()
.SetDisplay("EMA Slow", "Slow EMA period", "Moving Averages");
_rsiLength = Param(nameof(RSI_length), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI");
_xBars = Param(nameof(XBars), 24)
.SetNotNegative()
.SetDisplay("X Bars", "Bars after which a profitable position is closed", "Exit");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevMedium = null;
_entryPrice = 0m;
_barsInPosition = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null;
_prevMedium = null;
_entryPrice = 0m;
_barsInPosition = 0;
var fast = new ExponentialMovingAverage { Length = EMA_fast };
var medium = new ExponentialMovingAverage { Length = EMA_medium };
var slow = new ExponentialMovingAverage { Length = EMA_slow };
var rsi = new RelativeStrengthIndex { Length = RSI_length };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(fast, medium, slow, rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, medium);
DrawIndicator(area, slow);
DrawOwnTrades(area);
var rsiArea = CreateChartArea();
if (rsiArea != null)
DrawIndicator(rsiArea, rsi);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue fastValue, IIndicatorValue mediumValue, IIndicatorValue slowValue, IIndicatorValue rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!fastValue.IsFormed || !mediumValue.IsFormed || !slowValue.IsFormed || !rsiValue.IsFormed)
return;
var fast = fastValue.ToDecimal();
var medium = mediumValue.ToDecimal();
var slow = slowValue.ToDecimal();
var rsi = rsiValue.ToDecimal();
var prevFast = _prevFast;
var prevMedium = _prevMedium;
_prevFast = fast;
_prevMedium = medium;
if (Position != 0)
_barsInPosition++;
if (prevFast is not decimal lastFast || prevMedium is not decimal lastMedium)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var longSignal = fast > medium && lastFast <= lastMedium && close > slow && close > candle.OpenPrice;
var shortSignal = fast < medium && lastFast >= lastMedium && close < slow && close < candle.OpenPrice;
var timeUp = XBars > 0 && _barsInPosition >= XBars;
if (longSignal && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_entryPrice = close;
_barsInPosition = 0;
}
else if (shortSignal && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_entryPrice = close;
_barsInPosition = 0;
}
else if (Position > 0 && (rsi > _rsiOverbought || (timeUp && close > _entryPrice)))
{
SellMarket(Position);
}
else if (Position < 0 && (rsi < _rsiOversold || (timeUp && close < _entryPrice)))
{
BuyMarket(-Position);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
RSI_OVERBOUGHT = 70.0
RSI_OVERSOLD = 30.0
class ema_sma_rsi_strategy(Strategy):
"""
EMA/SMA + RSI crossover strategy.
Goes long when the fast EMA crosses above the medium EMA on a bullish candle closing above the slow EMA, and short
on the mirrored setup. A long closes when RSI rises above 70 or when it has been held XBars bars and is in profit;
a short closes when RSI falls below 30 or after XBars bars in profit.
"""
def __init__(self):
super(ema_sma_rsi_strategy, self).__init__()
self._ema_fast = self.Param("EMA_fast", 10) \
.SetGreaterThanZero() \
.SetDisplay("EMA Fast", "Fast EMA period", "Moving Averages")
self._ema_medium = self.Param("EMA_medium", 20) \
.SetGreaterThanZero() \
.SetDisplay("EMA Medium", "Medium EMA period", "Moving Averages")
self._ema_slow = self.Param("EMA_slow", 100) \
.SetGreaterThanZero() \
.SetDisplay("EMA Slow", "Slow EMA period", "Moving Averages")
self._rsi_length = self.Param("RSI_length", 14) \
.SetGreaterThanZero() \
.SetDisplay("RSI Length", "RSI period", "RSI")
self._x_bars = self.Param("XBars", 24) \
.SetNotNegative() \
.SetDisplay("X Bars", "Bars after which a profitable position is closed", "Exit")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle type", "Candle type for strategy calculation", "General")
self._reset_state()
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def _reset_state(self):
self._prev_fast = None
self._prev_medium = None
self._entry_price = 0.0
self._bars_in_position = 0
def OnReseted(self):
super(ema_sma_rsi_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(ema_sma_rsi_strategy, self).OnStarted2(time)
self._reset_state()
fast = ExponentialMovingAverage()
fast.Length = self._ema_fast.Value
medium = ExponentialMovingAverage()
medium.Length = self._ema_medium.Value
slow = ExponentialMovingAverage()
slow.Length = self._ema_slow.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(fast, medium, slow, rsi, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, medium)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
rsi_area = self.CreateChartArea()
if rsi_area is not None:
self.DrawIndicator(rsi_area, rsi)
def _process_candle(self, candle, fast_value, medium_value, slow_value, rsi_value):
if candle.State != CandleStates.Finished:
return
if not fast_value.IsFormed or not medium_value.IsFormed or not slow_value.IsFormed or not rsi_value.IsFormed:
return
fast = float(fast_value.GetValue[Decimal](None))
medium = float(medium_value.GetValue[Decimal](None))
slow = float(slow_value.GetValue[Decimal](None))
rsi = float(rsi_value.GetValue[Decimal](None))
prev_fast = self._prev_fast
prev_medium = self._prev_medium
self._prev_fast = fast
self._prev_medium = medium
if self.Position != 0:
self._bars_in_position += 1
if prev_fast is None or prev_medium is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
long_signal = fast > medium and prev_fast <= prev_medium and close > slow and close > open_price
short_signal = fast < medium and prev_fast >= prev_medium and close < slow and close < open_price
x_bars = int(self._x_bars.Value)
time_up = x_bars > 0 and self._bars_in_position >= x_bars
if long_signal and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._entry_price = close
self._bars_in_position = 0
elif short_signal and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._entry_price = close
self._bars_in_position = 0
elif self.Position > 0 and (rsi > RSI_OVERBOUGHT or (time_up and close > self._entry_price)):
self.SellMarket(self.Position)
elif self.Position < 0 and (rsi < RSI_OVERSOLD or (time_up and close < self._entry_price)):
self.BuyMarket(-self.Position)
def CreateClone(self):
return ema_sma_rsi_strategy()