Esta estrategia replica el asesor experto Exp_XRSIDeMarker_Histogram. Opera reversiones detectadas por un oscilador personalizado que combina un Índice de Fuerza Relativa (RSI) con el indicador DeMarker y luego suaviza el resultado. El sistema puede abrir o cerrar operaciones largas y cortas de forma independiente, y se admiten stops protectores opcionales expresados en pasos de precio.
Construcción del indicador
Precio aplicado – el RSI se calcula sobre la entrada seleccionada (precio de cierre, apertura, máximo, mínimo, mediano, típico o ponderado) usando el período configurado.
Componente DeMarker – para cada vela terminada, la estrategia mide la presión alcista (deMax) y bajista (deMin):
deMax = max(High_t - High_{t-1}, 0)
deMin = max(Low_{t-1} - Low_t, 0)
Ambas series se suavizan con una media móvil simple cuya longitud coincide con el período del RSI.
DeMarker = deMaxAvg / (deMaxAvg + deMinAvg) (escalado al rango 0–100).
Oscilador compuesto – el valor final es (RSI + 100 * DeMarker) / 2.
Suavizado – el oscilador compuesto pasa por una de las medias móviles soportadas (SMA, EMA, SMMA, LWMA o Jurik). Si se selecciona un modo no soportado de la versión MQL original, el indicador recurre a una EMA con la longitud solicitada. La opción Jurik también respeta el parámetro de fase.
Historial de señales – la estrategia almacena valores históricos y evalúa señales en la barra definida por SignalBar, imitando el EA original que esperaba la siguiente vela antes de operar.
Lógica de trading
Reversión alcista
Condición: el valor en SignalBar+1 es menor que en SignalBar+2 (pendiente descendente) y el valor en SignalBar vuelve a subir (>=).
Acciones:
Cerrar operaciones cortas existentes cuando CloseShortOnLongSignal es verdadero.
Abrir una nueva operación larga con TradeVolume (más la cantidad necesaria para invertir desde un corto) cuando AllowBuyEntries está habilitado.
Reversión bajista
Condición: el valor en SignalBar+1 es mayor que en SignalBar+2 (pendiente ascendente) y el valor en SignalBar baja (<=).
Acciones:
Cerrar operaciones largas existentes cuando CloseLongOnShortSignal es verdadero.
Abrir una nueva operación corta cuando AllowSellEntries está habilitado.
Las señales se ignoran hasta que el indicador y los componentes DeMarker estén completamente formados, y las órdenes se colocan solo cuando la estrategia está en línea y se permite el trading.
Gestión de riesgos
StopLossTicks y TakeProfitTicks representan distancias en pasos de precio. La estrategia multiplica estos valores por Security.PriceStep (usando 1 si el paso del instrumento es desconocido) y cierra la posición cuando se alcanza la distancia dentro del rango de la vela.
Pasar 0 desactiva la protección respectiva.
El parámetro TradeVolume se usa como tamaño de orden predeterminado y también para calcular reversiones (la posición opuesta se cierra antes de abrir una nueva).
Parámetros
Parámetro
Descripción
Predeterminado
TradeVolume
Volumen al abrir nuevas posiciones.
0.1
StopLossTicks
Stop protector en pasos de precio.
1000
TakeProfitTicks
Objetivo de beneficio en pasos de precio.
2000
AllowBuyEntries
Habilitar/deshabilitar entradas largas.
true
AllowSellEntries
Habilitar/deshabilitar entradas cortas.
true
CloseLongOnShortSignal
Cerrar largos cuando aparece una señal corta.
true
CloseShortOnLongSignal
Cerrar cortos cuando aparece una señal larga.
true
CandleType
Marco temporal usado para el análisis (velas de 4 horas por defecto).
H4
IndicatorPeriod
Retrospectiva para los componentes RSI y DeMarker.
14
AppliedPriceSelection
Precio aplicado usado por el cálculo del RSI.
Close
SmoothingMethodSelection
Media móvil usada para el suavizado (SMA/EMA/SMMA/LWMA/Jurik/Adaptive).
Sma
SmoothingLength
Período de la media de suavizado.
5
SmoothingPhase
Argumento de fase pasado al suavizado Jurik.
15
SignalBar
Número de barras cerradas atrás usadas para la evaluación de señales.
1
Notas vs. EA original
Los modos de gestión monetaria de la versión MQL (basado en balance, margen libre, etc.) se reemplazan con un parámetro directo TradeVolume.
El deslizamiento de órdenes (Deviation) no es necesario porque StockSharp usa órdenes de mercado.
Los algoritmos de suavizado avanzados (MA Parabólica, T3, VIDYA, AMA) no están disponibles en StockSharp y se mapean a la EMA mediante la opción Adaptive.
Todos los comentarios en el código fuente C# están escritos en inglés, y la lógica solo se ejecuta en velas terminadas, igual que la implementación original.
namespace StockSharp.Samples.Strategies;
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// XRSI DeMarker Histogram strategy (simplified).
/// Uses RSI combined with momentum to detect reversals.
/// Buys on RSI reversal from oversold, sells on reversal from overbought.
/// </summary>
public class XrsidDeMarkerHistogramStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _smaPeriod;
private decimal _prevRsi;
private decimal _prevPrevRsi;
private bool _initialized;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
public int SmaPeriod
{
get => _smaPeriod.Value;
set => _smaPeriod.Value = value;
}
public XrsidDeMarkerHistogramStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Source candles", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI indicator period", "Indicators");
_smaPeriod = Param(nameof(SmaPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("SMA Period", "Smoothing period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = 0m;
_prevPrevRsi = 0m;
_initialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = 0;
_prevPrevRsi = 0;
_initialized = false;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var sma = new SimpleMovingAverage { Length = SmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, sma, (ICandleMessage candle, decimal rsiValue, decimal smaValue) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!_initialized)
{
_prevPrevRsi = rsiValue;
_prevRsi = rsiValue;
_initialized = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevPrevRsi = _prevRsi;
_prevRsi = rsiValue;
return;
}
// Buy on RSI reversal from oversold (V-bottom)
var buySignal = _prevPrevRsi > _prevRsi && rsiValue >= _prevRsi && _prevRsi < 35m;
// Sell on RSI reversal from overbought (inverse V)
var sellSignal = _prevPrevRsi < _prevRsi && rsiValue <= _prevRsi && _prevRsi > 65m;
if (buySignal && Position <= 0)
{
BuyMarket();
}
else if (sellSignal && Position >= 0)
{
SellMarket();
}
_prevPrevRsi = _prevRsi;
_prevRsi = rsiValue;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class xrsid_de_marker_histogram_strategy(Strategy):
def __init__(self):
super(xrsid_de_marker_histogram_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Source candles", "General")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI indicator period", "Indicators")
self._sma_period = self.Param("SmaPeriod", 5) \
.SetDisplay("SMA Period", "Smoothing period", "Indicators")
self._prev_rsi = 0.0
self._prev_prev_rsi = 0.0
self._initialized = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@property
def SmaPeriod(self):
return self._sma_period.Value
def OnReseted(self):
super(xrsid_de_marker_histogram_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._prev_prev_rsi = 0.0
self._initialized = False
def OnStarted2(self, time):
super(xrsid_de_marker_histogram_strategy, self).OnStarted2(time)
self._prev_rsi = 0.0
self._prev_prev_rsi = 0.0
self._initialized = False
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
sma = SimpleMovingAverage()
sma.Length = self.SmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(rsi, sma, self._on_process) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, rsi)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _on_process(self, candle, rsi_value, sma_value):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_value)
if not self._initialized:
self._prev_prev_rsi = rv
self._prev_rsi = rv
self._initialized = True
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_prev_rsi = self._prev_rsi
self._prev_rsi = rv
return
buy_signal = self._prev_prev_rsi > self._prev_rsi and rv >= self._prev_rsi and self._prev_rsi < 35.0
sell_signal = self._prev_prev_rsi < self._prev_rsi and rv <= self._prev_rsi and self._prev_rsi > 65.0
if buy_signal and self.Position <= 0:
self.BuyMarket()
elif sell_signal and self.Position >= 0:
self.SellMarket()
self._prev_prev_rsi = self._prev_rsi
self._prev_rsi = rv
def CreateClone(self):
return xrsid_de_marker_histogram_strategy()