Multi-Timeframe EMA + BB + RSI-Strategie
Kombiniert zwei exponentielle gleitende Durchschnitte, Bollinger Bands und RSI, um Bounces zu handeln. Long-Trades entstehen, wenn der Kurs nach dem Berühren des unteren Bandes über der schnellen EMA schließt. Short-Trades werden ausgelöst, wenn der Kurs nach dem Durchstechen des oberen Bandes unter der schnellen EMA schließt und RSI über 50 liegt.
Eine optionale Gewinnmitnahme schließt die Position nach einer benutzerdefinierten Anzahl von Kerzen, wenn sich der Kurs günstig entwickelt. Das System ist flexibel genug für Swing- oder Intraday-Trading und unterstützt das unabhängige Aktivieren oder Deaktivieren der Long- und Short-Seiten.
Details
- Einstiegskriterien:
- Long: Schluss über der schnellen EMA mit einem Tief, das das untere Bollinger Band durchsticht.
- Short: Schluss unter der schnellen EMA mit einem Hoch, das das obere Band durchsticht, und RSI > 50.
- Ausstiegskriterien:
- Long: RSI steigt über den Überverkauft-Level.
- Short: Preis schließt unter dem unteren Band.
- Indikatoren:
- Zwei EMAs (Perioden 10 und 55)
- Bollinger Bands (Länge 20, Multiplikator 2)
- RSI (Länge 14, Überverkauft 71)
- Stops: Optionales Gewinnziel nach X Kerzen; kein fester Stop-Loss.
- Standardwerte:
Ma1Period= 10Ma2Period= 55BBLength= 20BBMultiplier= 2.0RSILength= 14RSIOversold= 71XBars= 12
- Filter:
- Mean Reversion mit Trendfilter
- Zeitrahmen: Konfigurierbar
- Indikatoren: EMA, Bollinger Bands, RSI
- Stops: Optional
- Komplexität: Moderat
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Multi EMA + Bollinger Bands + RSI Strategy.
/// Buys when price is above fast EMA and touches lower BB.
/// Sells when RSI becomes overbought or price touches upper BB.
/// </summary>
public class MemaBbRsiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleTypeParam;
private readonly StrategyParam<int> _ma1Period;
private readonly StrategyParam<int> _ma2Period;
private readonly StrategyParam<int> _bbLength;
private readonly StrategyParam<decimal> _bbMultiplier;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _rsiOverbought;
private readonly StrategyParam<int> _cooldownBars;
private ExponentialMovingAverage _ma1;
private ExponentialMovingAverage _ma2;
private BollingerBands _bollinger;
private RelativeStrengthIndex _rsi;
private int _cooldownRemaining;
public MemaBbRsiStrategy()
{
_candleTypeParam = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General");
_ma1Period = Param(nameof(Ma1Period), 10)
.SetGreaterThanZero()
.SetDisplay("MA1 Period", "Fast EMA period", "Moving Average");
_ma2Period = Param(nameof(Ma2Period), 55)
.SetGreaterThanZero()
.SetDisplay("MA2 Period", "Slow EMA period", "Moving Average");
_bbLength = Param(nameof(BBLength), 20)
.SetGreaterThanZero()
.SetDisplay("BB Length", "Bollinger Bands period", "Bollinger Bands");
_bbMultiplier = Param(nameof(BBMultiplier), 2.0m)
.SetDisplay("BB StdDev", "Standard deviation multiplier", "Bollinger Bands");
_rsiLength = Param(nameof(RSILength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI");
_rsiOverbought = Param(nameof(RsiOverbought), 70)
.SetDisplay("RSI Overbought", "RSI overbought level", "RSI");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
public DataType CandleType
{
get => _candleTypeParam.Value;
set => _candleTypeParam.Value = value;
}
public int Ma1Period
{
get => _ma1Period.Value;
set => _ma1Period.Value = value;
}
public int Ma2Period
{
get => _ma2Period.Value;
set => _ma2Period.Value = value;
}
public int BBLength
{
get => _bbLength.Value;
set => _bbLength.Value = value;
}
public decimal BBMultiplier
{
get => _bbMultiplier.Value;
set => _bbMultiplier.Value = value;
}
public int RSILength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
public int RsiOverbought
{
get => _rsiOverbought.Value;
set => _rsiOverbought.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ma1 = null;
_ma2 = null;
_bollinger = null;
_rsi = null;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ma1 = new ExponentialMovingAverage { Length = Ma1Period };
_ma2 = new ExponentialMovingAverage { Length = Ma2Period };
_bollinger = new BollingerBands
{
Length = BBLength,
Width = BBMultiplier
};
_rsi = new RelativeStrengthIndex { Length = RSILength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_ma1, _ma2, _bollinger, _rsi, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ma1);
DrawIndicator(area, _ma2);
DrawIndicator(area, _bollinger);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, IIndicatorValue ma1Value, IIndicatorValue ma2Value, IIndicatorValue bbValue, IIndicatorValue rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_ma1.IsFormed || !_ma2.IsFormed || !_bollinger.IsFormed || !_rsi.IsFormed)
return;
if (ma1Value.IsEmpty || ma2Value.IsEmpty || bbValue.IsEmpty || rsiValue.IsEmpty)
return;
var ma1Price = ma1Value.ToDecimal();
var rsiVal = rsiValue.ToDecimal();
var bb = (BollingerBandsValue)bbValue;
if (bb.UpBand is not decimal upper || bb.LowBand is not decimal lower)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
return;
}
// Buy: price above fast EMA and low touches lower BB (mean reversion from below)
var entryLong = candle.ClosePrice > ma1Price && candle.LowPrice <= lower;
// Sell: price below fast EMA and high touches upper BB
var entryShort = candle.ClosePrice < ma1Price && candle.HighPrice >= upper;
// Exit long: RSI overbought
var exitLong = rsiVal > RsiOverbought;
// Exit short: price drops below lower BB
var exitShort = candle.ClosePrice < lower;
// Exit positions first
if (exitLong && Position > 0)
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
else if (exitShort && Position < 0)
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
// Enter new positions
else if (entryLong && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
else if (entryShort && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, BollingerBands, RelativeStrengthIndex, IndicatorHelper
from StockSharp.Algo.Strategies import Strategy
class mema_bb_rsi_strategy(Strategy):
"""Multi EMA + Bollinger Bands + RSI Strategy."""
def __init__(self):
super(mema_bb_rsi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General")
self._ma1_period = self.Param("Ma1Period", 10) \
.SetDisplay("MA1 Period", "Fast EMA period", "Moving Average")
self._ma2_period = self.Param("Ma2Period", 55) \
.SetDisplay("MA2 Period", "Slow EMA period", "Moving Average")
self._bb_length = self.Param("BBLength", 20) \
.SetDisplay("BB Length", "Bollinger Bands period", "Bollinger Bands")
self._bb_multiplier = self.Param("BBMultiplier", 2.0) \
.SetDisplay("BB StdDev", "Standard deviation multiplier", "Bollinger Bands")
self._rsi_length = self.Param("RSILength", 14) \
.SetDisplay("RSI Length", "RSI period", "RSI")
self._rsi_overbought = self.Param("RsiOverbought", 70) \
.SetDisplay("RSI Overbought", "RSI overbought level", "RSI")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._ma1 = None
self._ma2 = None
self._bollinger = None
self._rsi = None
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(mema_bb_rsi_strategy, self).OnReseted()
self._ma1 = None
self._ma2 = None
self._bollinger = None
self._rsi = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(mema_bb_rsi_strategy, self).OnStarted2(time)
self._ma1 = ExponentialMovingAverage()
self._ma1.Length = int(self._ma1_period.Value)
self._ma2 = ExponentialMovingAverage()
self._ma2.Length = int(self._ma2_period.Value)
self._bollinger = BollingerBands()
self._bollinger.Length = int(self._bb_length.Value)
self._bollinger.Width = float(self._bb_multiplier.Value)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = int(self._rsi_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(self._ma1, self._ma2, self._bollinger, self._rsi, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._ma1)
self.DrawIndicator(area, self._ma2)
self.DrawIndicator(area, self._bollinger)
self.DrawOwnTrades(area)
def _on_process(self, candle, ma1_value, ma2_value, bb_value, rsi_value):
if candle.State != CandleStates.Finished:
return
if not self._ma1.IsFormed or not self._ma2.IsFormed or not self._bollinger.IsFormed or not self._rsi.IsFormed:
return
if ma1_value.IsEmpty or ma2_value.IsEmpty or bb_value.IsEmpty or rsi_value.IsEmpty:
return
ma1 = float(IndicatorHelper.ToDecimal(ma1_value))
rsi = float(IndicatorHelper.ToDecimal(rsi_value))
if bb_value.UpBand is None or bb_value.LowBand is None:
return
upper = float(bb_value.UpBand)
lower = float(bb_value.LowBand)
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
return
close = float(candle.ClosePrice)
low = float(candle.LowPrice)
high = float(candle.HighPrice)
cooldown = int(self._cooldown_bars.Value)
rsi_ob = int(self._rsi_overbought.Value)
entry_long = close > ma1 and low <= lower
entry_short = close < ma1 and high >= upper
exit_long = rsi > rsi_ob
exit_short = close < lower
if exit_long and self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif exit_short and self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif entry_long and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif entry_short and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
def CreateClone(self):
return mema_bb_rsi_strategy()