Estratégia DEMA RSI
Esta estratégia opera os cruzamentos entre o RSI de uma média móvel exponencial dupla e seu valor suavizado. Uma posição comprada é aberta quando o RSI cruza acima da linha suavizada e uma posição vendida no cruzamento oposto. As posições podem ser protegidas com take profit, trailing stop e um filtro de sessão de trading opcional.
Parâmetros
- Tipo de vela
- Comprimento de MA
- Comprimento de RSI
- Comprimento de suavização de RSI
- Pontos de take profit
- Pontos de trailing stop
- Usar sessão
- Início da sessão
- Fim da sessão
Regras reais de RSI(DEMA)
Padrões: CandleType=1m, MaLength=20, RsiLength=14, RsiSmoothingLength=5, TakeProfitPoints=0, TrailStopPoints=0, UseSession=false, SessionStart=09:00, SessionEnd=17:00. Em candle concluído: EMA1=EMA(Close,20), EMA2=EMA(EMA1,20), DEMA=2×EMA1−EMA2, R=RSI(DEMA,14), S=SMA(R,5). Compra quando antes R <= S e agora R > S; vende quando antes R >= S e agora R < S, podendo inverter posição com Volume + abs(Position). Com UseSession=true, apenas entradas dentro de [SessionStart,SessionEnd), inclusive sessões noturnas; proteções encerram independentemente do horário. TakeProfitPoints e TrailStopPoints são ticks mínimos de preço: valores positivos ativam TP e trailing nativos em distância absoluta, zero desativa.
O filtro de sessão usa o horário UTC atual da decisão: uma vela aberta na sessão não permite entrar após o encerramento. Valores RSI vazios no aquecimento não alimentam a média de suavização.
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()