Estrategia DEMA RSI
Esta estrategia opera los cruces entre el RSI de una media móvil exponencial doble y su valor suavizado. Se abre una posición larga cuando el RSI cruza al alza la línea suavizada y una posición corta en el cruce opuesto. Las posiciones pueden protegerse con take profit, stop trailing y un filtro de sesión de trading opcional.
Parámetros
- Tipo de vela
- Longitud de MA
- Longitud de RSI
- Longitud de suavizado de RSI
- Puntos de take profit
- Puntos de stop trailing
- Usar sesión
- Inicio de sesión
- Fin de sesión
Condiciones reales RSI(DEMA)
Predeterminados: CandleType=1m, MaLength=20, RsiLength=14, RsiSmoothingLength=5, TakeProfitPoints=0, TrailStopPoints=0, UseSession=false, SessionStart=09:00, SessionEnd=17:00. En vela terminada: EMA1=EMA(Close,20), EMA2=EMA(EMA1,20), DEMA=2×EMA1−EMA2, R=RSI(DEMA,14), S=SMA(R,5). Comprar si antes R <= S y ahora R > S; vender si antes R >= S y ahora R < S, desde plano o invirtiendo la posición opuesta con Volume + abs(Position). UseSession=true limita nuevas entradas a [SessionStart,SessionEnd), incluso cruzando medianoche; las salidas por protección son independientes. Los puntos de TakeProfitPoints y TrailStopPoints son ticks mínimos del instrumento: valores no nulos usan protección nativa a distancia absoluta, cero desactiva cada componente.
El filtro de sesión usa la hora UTC actual de la decisión: una vela abierta dentro de la sesión no permite entrar después del cierre. Los valores RSI vacíos durante el calentamiento no alimentan la media de suavizado.
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>
/// Trade strict crossings of RSI computed from a double exponential moving
/// average (DEMA) and its own SMA, with optional session and point protections.
/// </summary>
public class DemaRsiStrategy : Strategy
{
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _rsiSmoothingLength;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<decimal> _trailStopPoints;
private readonly StrategyParam<bool> _useSession;
private readonly StrategyParam<TimeSpan> _sessionStart;
private readonly StrategyParam<TimeSpan> _sessionEnd;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _ema1;
private ExponentialMovingAverage _ema2;
private RelativeStrengthIndex _rsi;
private SimpleMovingAverage _smoothRsi;
private decimal? _previousRsi;
private decimal? _previousSmooth;
public int MaLength { get => _maLength.Value; set => _maLength.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public int RsiSmoothingLength { get => _rsiSmoothingLength.Value; set => _rsiSmoothingLength.Value = value; }
public decimal TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public decimal TrailStopPoints { get => _trailStopPoints.Value; set => _trailStopPoints.Value = value; }
public bool UseSession { get => _useSession.Value; set => _useSession.Value = value; }
public TimeSpan SessionStart { get => _sessionStart.Value; set => _sessionStart.Value = value; }
public TimeSpan SessionEnd { get => _sessionEnd.Value; set => _sessionEnd.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public DemaRsiStrategy()
{
_maLength = Param(nameof(MaLength), 20)
.SetGreaterThanZero().SetDisplay("MA Length", "DEMA first/second EMA length", "Indicators");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero().SetDisplay("RSI Length", "RSI of DEMA", "Indicators");
_rsiSmoothingLength = Param(nameof(RsiSmoothingLength), 5)
.SetGreaterThanZero().SetDisplay("RSI Smoothing Length", "SMA of RSI(DEMA)", "Indicators");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 0m)
.SetRange(0m, 100000m).SetDisplay("Take Profit Points", "Take profit in security price ticks; zero disables", "Risk");
_trailStopPoints = Param(nameof(TrailStopPoints), 0m)
.SetRange(0m, 100000m).SetDisplay("Trail Stop Points", "Trailing stop in security price ticks; zero disables", "Risk");
_useSession = Param(nameof(UseSession), false)
.SetDisplay("Use Session", "Restrict signal entries to a time window", "Calendar");
_sessionStart = Param(nameof(SessionStart), TimeSpan.FromHours(9))
.SetDisplay("Session Start", "Inclusive start of entry session", "Calendar");
_sessionEnd = Param(nameof(SessionEnd), TimeSpan.FromHours(17))
.SetDisplay("Session End", "Exclusive end of entry session", "Calendar");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Completed candle interval", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnReseted()
{
base.OnReseted();
_ema1 = null;
_ema2 = null;
_rsi = null;
_smoothRsi = null;
_previousRsi = null;
_previousSmooth = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
if (SessionStart < TimeSpan.Zero || SessionStart >= TimeSpan.FromDays(1) ||
SessionEnd < TimeSpan.Zero || SessionEnd >= TimeSpan.FromDays(1))
throw new ArgumentException("Session times must be within one day.");
_ema1 = new ExponentialMovingAverage { Length = MaLength };
_ema2 = new ExponentialMovingAverage { Length = MaLength };
_rsi = new RelativeStrengthIndex { Length = RsiLength };
_smoothRsi = new SimpleMovingAverage { Length = RsiSmoothingLength };
_previousRsi = null;
_previousSmooth = null;
var subscription = SubscribeCandles(CandleType);
subscription.Bind(_ema1, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ema1);
DrawOwnTrades(area);
}
// Protection uses ABSOLUTE price distance in minimum price ticks, not percent.
var tick = Security?.PriceStep ?? 0m;
if (tick > 0m && (TakeProfitPoints > 0m || TrailStopPoints > 0m))
{
StartProtection(
new Unit(TakeProfitPoints * tick, UnitTypes.Absolute),
new Unit(TrailStopPoints * tick, UnitTypes.Absolute),
TrailStopPoints > 0m);
}
}
private bool IsInSession(TimeSpan time)
{
if (!UseSession || SessionStart == SessionEnd)
return true;
return SessionStart < SessionEnd
? time >= SessionStart && time < SessionEnd
: time >= SessionStart || time < SessionEnd;
}
private void ProcessCandle(ICandleMessage candle, decimal firstEma)
{
if (candle.State != CandleStates.Finished)
return;
var secondEma = _ema2.Process(firstEma, candle.OpenTime, true).ToDecimal();
var dema = 2m * firstEma - secondEma;
var rsiValue = _rsi.Process(dema, candle.OpenTime, true);
if (rsiValue.IsEmpty)
return;
var rsi = rsiValue.ToDecimal();
var smoothed = _smoothRsi.Process(rsi, candle.OpenTime, true).ToDecimal();
var crossUp = _previousRsi is decimal pr && _previousSmooth is decimal ps &&
pr <= ps && rsi > smoothed;
var crossDown = _previousRsi is decimal qr && _previousSmooth is decimal qs &&
qr >= qs && rsi < smoothed;
_previousRsi = rsi;
_previousSmooth = smoothed;
if (!_ema1.IsFormed || !_ema2.IsFormed || !_rsi.IsFormed || !_smoothRsi.IsFormed ||
!IsFormedAndOnlineAndAllowTrading() || !IsInSession(CurrentTime.TimeOfDay))
return;
if (crossUp && Position <= 0m)
BuyMarket(Volume + Math.Abs(Position));
else if (crossDown && Position >= 0m)
SellMarket(Volume + Math.Abs(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, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage, RelativeStrengthIndex, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import process_float
class dema_rsi_strategy(Strategy):
"""RSI(DEMA) versus its own smoothing, with optional point protection and session."""
def __init__(self):
super(dema_rsi_strategy, self).__init__()
self._ma_length = self.Param("MaLength", 20) \
.SetGreaterThanZero().SetDisplay("MA Length", "DEMA EMA lengths", "Indicators")
self._rsi_length = self.Param("RsiLength", 14) \
.SetGreaterThanZero().SetDisplay("RSI Length", "RSI(DEMA) period", "Indicators")
self._rsi_smoothing_length = self.Param("RsiSmoothingLength", 5) \
.SetGreaterThanZero().SetDisplay("RSI Smoothing Length", "SMA of RSI", "Indicators")
self._take_profit_points = self.Param("TakeProfitPoints", 0.0) \
.SetRange(0.0, 100000.0).SetDisplay("Take Profit Points", "Price ticks, zero disables", "Risk")
self._trail_stop_points = self.Param("TrailStopPoints", 0.0) \
.SetRange(0.0, 100000.0).SetDisplay("Trail Stop Points", "Price ticks, zero disables", "Risk")
self._use_session = self.Param("UseSession", False) \
.SetDisplay("Use Session", "Limit entries to session", "Calendar")
self._session_start = self.Param("SessionStart", TimeSpan.FromHours(9)) \
.SetDisplay("Session Start", "Inclusive entry start", "Calendar")
self._session_end = self.Param("SessionEnd", TimeSpan.FromHours(17)) \
.SetDisplay("Session End", "Exclusive entry end", "Calendar")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))) \
.SetDisplay("Candle Type", "Completed candle interval", "General")
self._ema1 = None
self._ema2 = None
self._rsi = None
self._smooth_rsi = None
self._previous_rsi = None
self._previous_smooth = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(dema_rsi_strategy, self).OnReseted()
self._ema1 = None
self._ema2 = None
self._rsi = None
self._smooth_rsi = None
self._previous_rsi = None
self._previous_smooth = None
def OnStarted2(self, time):
super(dema_rsi_strategy, self).OnStarted2(time)
start = self._session_start.Value
end = self._session_end.Value
if start < TimeSpan.Zero or start >= TimeSpan.FromDays(1) or end < TimeSpan.Zero or end >= TimeSpan.FromDays(1):
raise ValueError("Session times must be within one day")
length = int(self._ma_length.Value)
self._ema1 = ExponentialMovingAverage()
self._ema1.Length = length
self._ema2 = ExponentialMovingAverage()
self._ema2.Length = length
self._rsi = RelativeStrengthIndex()
self._rsi.Length = int(self._rsi_length.Value)
self._smooth_rsi = SimpleMovingAverage()
self._smooth_rsi.Length = int(self._rsi_smoothing_length.Value)
self._previous_rsi = None
self._previous_smooth = None
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._ema1, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._ema1)
self.DrawOwnTrades(area)
tick = float(self.Security.PriceStep) if self.Security is not None else 0.0
tp_points = float(self._take_profit_points.Value)
trail_points = float(self._trail_stop_points.Value)
if tick > 0 and (tp_points > 0 or trail_points > 0):
self.StartProtection(
Unit(tp_points * tick, UnitTypes.Absolute),
Unit(trail_points * tick, UnitTypes.Absolute),
trail_points > 0
)
def _is_in_session(self, time):
if not bool(self._use_session.Value):
return True
start = self._session_start.Value
end = self._session_end.Value
if start == end:
return True
if start < end:
return time >= start and time < end
return time >= start or time < end
def _process_candle(self, candle, first_ema):
if candle.State != CandleStates.Finished:
return
ema1 = float(first_ema)
ema2 = float(process_float(self._ema2, Decimal(ema1), candle.OpenTime, True))
dema = 2.0 * ema1 - ema2
rsi_value = process_float(self._rsi, Decimal(dema), candle.OpenTime, True)
if rsi_value.IsEmpty:
return
rsi = float(rsi_value)
smoothed = float(process_float(self._smooth_rsi, Decimal(rsi), candle.OpenTime, True))
cross_up = (
self._previous_rsi is not None and self._previous_smooth is not None and
self._previous_rsi <= self._previous_smooth and rsi > smoothed
)
cross_down = (
self._previous_rsi is not None and self._previous_smooth is not None and
self._previous_rsi >= self._previous_smooth and rsi < smoothed
)
self._previous_rsi = rsi
self._previous_smooth = smoothed
if not self._ema1.IsFormed or not self._ema2.IsFormed or not self._rsi.IsFormed or not self._smooth_rsi.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading() or not self._is_in_session(self.CurrentTime.TimeOfDay):
return
if cross_up and self.Position <= 0:
self.BuyMarket(self.Volume + Math.Abs(self.Position))
elif cross_down and self.Position >= 0:
self.SellMarket(self.Volume + Math.Abs(self.Position))
def CreateClone(self):
return dema_rsi_strategy()