Estrategia Multi-timeframe EMA + BB + RSI
Combina dos medias móviles exponenciales, Bollinger Bands y RSI para operar rebotes. Las operaciones largas ocurren cuando el precio cierra por encima de la EMA rápida después de tocar la banda inferior. Las operaciones cortas se activan cuando el precio cierra por debajo de la EMA rápida después de perforar la banda superior y RSI está por encima de 50.
La toma de ganancias opcional cierra la posición después de un número de barras definido por el usuario si el precio se mueve favorablemente. El sistema es lo suficientemente flexible para el trading swing o intradía y admite habilitar o deshabilitar los lados largo y corto de forma independiente.
Detalles
- Criterios de entrada:
- Largo: Cierre por encima de la EMA rápida con un mínimo perforando la banda inferior de Bollinger Bands.
- Corto: Cierre por debajo de la EMA rápida con un máximo perforando la banda superior y RSI > 50.
- Criterios de salida:
- Largo: RSI sube por encima del nivel de sobreventa.
- Corto: El precio cierra por debajo de la banda inferior.
- Indicadores:
- Dos EMAs (períodos 10 y 55)
- Bollinger Bands (longitud 20, multiplicador 2)
- RSI (longitud 14, sobreventa 71)
- Stops: Objetivo de ganancia opcional después de X barras; sin stop-loss fijo.
- Valores predeterminados:
Ma1Period= 10Ma2Period= 55BBLength= 20BBMultiplier= 2.0RSILength= 14RSIOversold= 71XBars= 12
- Filtros:
- Reversión a la media con filtro de tendencia
- Marco temporal: Configurable
- Indicadores: EMA, Bollinger Bands, RSI
- Stops: Opcional
- Complejidad: Moderado
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>
/// Multi EMA + Bollinger Bands + RSI strategy.
/// Goes long when the close is above the fast EMA on a candle whose low pierced the lower band, and short when the
/// close is below the fast EMA on a candle whose high pierced the upper band while RSI is above 50. A long closes when
/// RSI rises above RSIOversold and a short when the close drops below the lower band. After XBars bars a position that
/// is in profit is closed as well. Each side can be switched off. The slow EMA is plotted as a trend reference.
/// </summary>
public class MemaBbRsiStrategy : Strategy
{
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<decimal> _rsiOversold;
private readonly StrategyParam<int> _xBars;
private readonly StrategyParam<bool> _enableLong;
private readonly StrategyParam<bool> _enableShort;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private int _barsInPosition;
/// <summary>
/// Fast EMA period.
/// </summary>
public int Ma1Period
{
get => _ma1Period.Value;
set => _ma1Period.Value = value;
}
/// <summary>
/// Slow EMA period.
/// </summary>
public int Ma2Period
{
get => _ma2Period.Value;
set => _ma2Period.Value = value;
}
/// <summary>
/// Bollinger Bands period.
/// </summary>
public int BBLength
{
get => _bbLength.Value;
set => _bbLength.Value = value;
}
/// <summary>
/// Bollinger Bands standard deviation multiplier.
/// </summary>
public decimal BBMultiplier
{
get => _bbMultiplier.Value;
set => _bbMultiplier.Value = value;
}
/// <summary>
/// RSI period.
/// </summary>
public int RSILength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// RSI level above which a long is closed.
/// </summary>
public decimal RSIOversold
{
get => _rsiOversold.Value;
set => _rsiOversold.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>
/// Allow long trades.
/// </summary>
public bool EnableLong
{
get => _enableLong.Value;
set => _enableLong.Value = value;
}
/// <summary>
/// Allow short trades.
/// </summary>
public bool EnableShort
{
get => _enableShort.Value;
set => _enableShort.Value = value;
}
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public MemaBbRsiStrategy()
{
_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)
.SetGreaterThanZero()
.SetDisplay("BB Multiplier", "Bollinger Bands standard deviation multiplier", "Bollinger Bands");
_rsiLength = Param(nameof(RSILength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI");
_rsiOversold = Param(nameof(RSIOversold), 71m)
.SetDisplay("RSI Exit Level", "RSI level above which a long is closed", "RSI");
_xBars = Param(nameof(XBars), 12)
.SetNotNegative()
.SetDisplay("X Bars", "Bars after which a profitable position is closed, 0 disables", "Exit");
_enableLong = Param(nameof(EnableLong), true)
.SetDisplay("Enable Long", "Allow long trades", "Trading");
_enableShort = Param(nameof(EnableShort), true)
.SetDisplay("Enable Short", "Allow short trades", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).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();
_entryPrice = 0m;
_barsInPosition = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0m;
_barsInPosition = 0;
var ma1 = new ExponentialMovingAverage { Length = Ma1Period };
var ma2 = new ExponentialMovingAverage { Length = Ma2Period };
var bollinger = new BollingerBands { Length = BBLength, Width = BBMultiplier };
var rsi = new RelativeStrengthIndex { Length = RSILength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(ma1, ma2, bollinger, rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ma1);
DrawIndicator(area, ma2);
DrawIndicator(area, bollinger);
DrawOwnTrades(area);
var rsiArea = CreateChartArea();
if (rsiArea != null)
DrawIndicator(rsiArea, rsi);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue ma1Value, IIndicatorValue ma2Value, IIndicatorValue bollingerValue, IIndicatorValue rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (Position != 0)
_barsInPosition++;
if (!ma1Value.IsFormed || !bollingerValue.IsFormed || !rsiValue.IsFormed)
return;
var bb = (BollingerBandsValue)bollingerValue;
if (bb.UpBand is not decimal upper || bb.LowBand is not decimal lower)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var ma1 = ma1Value.ToDecimal();
var rsi = rsiValue.ToDecimal();
var close = candle.ClosePrice;
var longSignal = EnableLong && close > ma1 && candle.LowPrice < lower;
var shortSignal = EnableShort && close < ma1 && candle.HighPrice > upper && rsi > 50m;
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 > RSIOversold || (timeUp && close > _entryPrice)))
{
SellMarket(Position);
}
else if (Position < 0 && (close < lower || (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, BollingerBands, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class mema_bb_rsi_strategy(Strategy):
"""
Multi EMA + Bollinger Bands + RSI strategy.
Goes long when the close is above the fast EMA on a candle whose low pierced the lower band, and short when the
close is below the fast EMA on a candle whose high pierced the upper band while RSI is above 50. A long closes when
RSI rises above RSIOversold and a short when the close drops below the lower band. After XBars bars a position that
is in profit is closed as well. Each side can be switched off. The slow EMA is plotted as a trend reference.
"""
def __init__(self):
super(mema_bb_rsi_strategy, self).__init__()
self._ma1_period = self.Param("Ma1Period", 10) \
.SetGreaterThanZero() \
.SetDisplay("MA1 Period", "Fast EMA period", "Moving Average")
self._ma2_period = self.Param("Ma2Period", 55) \
.SetGreaterThanZero() \
.SetDisplay("MA2 Period", "Slow EMA period", "Moving Average")
self._bb_length = self.Param("BBLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("BB Length", "Bollinger Bands period", "Bollinger Bands")
self._bb_multiplier = self.Param("BBMultiplier", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("BB Multiplier", "Bollinger Bands standard deviation multiplier", "Bollinger Bands")
self._rsi_length = self.Param("RSILength", 14) \
.SetGreaterThanZero() \
.SetDisplay("RSI Length", "RSI period", "RSI")
self._rsi_oversold = self.Param("RSIOversold", 71.0) \
.SetDisplay("RSI Exit Level", "RSI level above which a long is closed", "RSI")
self._x_bars = self.Param("XBars", 12) \
.SetNotNegative() \
.SetDisplay("X Bars", "Bars after which a profitable position is closed, 0 disables", "Exit")
self._enable_long = self.Param("EnableLong", True) \
.SetDisplay("Enable Long", "Allow long trades", "Trading")
self._enable_short = self.Param("EnableShort", True) \
.SetDisplay("Enable Short", "Allow short trades", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle type", "Candle type for strategy calculation", "General")
self._entry_price = 0.0
self._bars_in_position = 0
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(mema_bb_rsi_strategy, self).OnReseted()
self._entry_price = 0.0
self._bars_in_position = 0
def OnStarted2(self, time):
super(mema_bb_rsi_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._bars_in_position = 0
ma1 = ExponentialMovingAverage()
ma1.Length = self._ma1_period.Value
ma2 = ExponentialMovingAverage()
ma2.Length = self._ma2_period.Value
bollinger = BollingerBands()
bollinger.Length = self._bb_length.Value
bollinger.Width = Decimal(self._bb_multiplier.Value)
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(ma1, ma2, bollinger, rsi, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ma1)
self.DrawIndicator(area, ma2)
self.DrawIndicator(area, bollinger)
self.DrawOwnTrades(area)
rsi_area = self.CreateChartArea()
if rsi_area is not None:
self.DrawIndicator(rsi_area, rsi)
def _process_candle(self, candle, ma1_value, ma2_value, bollinger_value, rsi_value):
if candle.State != CandleStates.Finished:
return
if self.Position != 0:
self._bars_in_position += 1
if not ma1_value.IsFormed or not bollinger_value.IsFormed or not rsi_value.IsFormed:
return
if bollinger_value.UpBand is None or bollinger_value.LowBand is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
upper = float(bollinger_value.UpBand)
lower = float(bollinger_value.LowBand)
ma1 = float(ma1_value.GetValue[Decimal](None))
rsi = float(rsi_value.GetValue[Decimal](None))
close = float(candle.ClosePrice)
long_signal = self._enable_long.Value and close > ma1 and float(candle.LowPrice) < lower
short_signal = self._enable_short.Value and close < ma1 and float(candle.HighPrice) > upper and rsi > 50.0
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 > float(self._rsi_oversold.Value) or (time_up and close > self._entry_price)):
self.SellMarket(self.Position)
elif self.Position < 0 and (close < lower or (time_up and close < self._entry_price)):
self.BuyMarket(-self.Position)
def CreateClone(self):
return mema_bb_rsi_strategy()