Estrategia Exp Rj SlidingRangeRj Digit System Tm Plus
Descripción general
Esta estrategia StockSharp es un port del asesor experto MetaTrader Exp_Rj_SlidingRangeRj_Digit_System_Tm_Plus. Recrea la lógica de trading original basada en el indicador de canal personalizado Rj_SlidingRangeRj_Digit y conserva las opciones de gestión de operaciones configurables. La estrategia monitorea velas finalizadas en un marco temporal configurable, detecta rupturas más allá del canal y reacciona a esos eventos con entradas retrasadas, salidas temporizadas opcionales y gestión de stop/objetivo basada en precio.
Lógica del indicador
El indicador Rj_SlidingRangeRj_Digit construye un canal de precio adaptativo usando un proceso de promediado de múltiples pasos:
Para la banda superior, el máximo-máximo dentro de UpCalcPeriodRange barras se calcula para cada una de las últimas UpCalcPeriodRange ventanas deslizantes, desplazadas por UpCalcPeriodShift barras. El promedio de estos máximos se redondea a la precisión especificada por UpDigit.
La banda inferior repite la misma lógica en mínimos usando DnCalcPeriodRange, DnCalcPeriodShift y DnDigit.
Una vela se etiqueta como ruptura cuando su precio de cierre está por encima de la banda superior (colores 2 / 3) o por debajo de la banda inferior (colores 0 / 1). Las velas dentro del canal producen un color neutro (4).
La estrategia transmite velas finalizadas, reconstruye las bandas en cada actualización y almacena los códigos de color más recientes para imitar el comportamiento CopyBuffer/SignalBar de la implementación MQL.
Reglas de trading
Retraso de entrada: Las señales se evalúan en la barra definida por SignalBar (por defecto una barra atrás). La estrategia espera hasta que aparezca un color de ruptura y la barra anterior no tenga el mismo color de ruptura. Esto reproduce el retraso original de una barra antes de tomar una operación.
Entradas largas: Habilitadas por EnableBuyEntries. Una ruptura alcista (color 2 o 3) activa una compra de mercado cuando no hay posición larga abierta (la exposición corta se compensa automáticamente).
Entradas cortas: Habilitadas por EnableSellEntries. Una ruptura bajista (color 0 o 1) activa una venta de mercado cuando no hay posición corta abierta.
Señales de salida:
Los largos se cierran con colores de ruptura bajista si EnableBuyExits es verdadero.
Los cortos se cierran con colores de ruptura alcista si EnableSellExits es verdadero.
La salida opcional basada en tiempo (UseTimeExit) cierra cualquier posición abierta una vez que se ha mantenido por más de ExitMinutes.
Los niveles opcionales de stop-loss y take-profit expresados en puntos (StopLossPoints, TakeProfitPoints) se convierten en desplazamientos de precio usando el PriceStep del instrumento.
Todas las acciones usan BuyMarket / SellMarket para que la estrategia revierta automáticamente las posiciones cuando sea necesario.
Parámetros
Parámetro
Descripción
Valor predeterminado
CandleType
Tipo de vela (marco temporal) usado para la detección de señales.
Velas de 8 horas
EnableBuyEntries / EnableSellEntries
Permitir entradas de ruptura largas/cortas.
true
EnableBuyExits / EnableSellExits
Permitir salidas basadas en indicador para largos/cortos.
true
UseTimeExit
Cerrar operaciones después de un tiempo de mantenimiento fijo.
true
ExitMinutes
Límite de tiempo de mantenimiento en minutos.
1920
UpCalcPeriodRange, UpCalcPeriodShift, UpDigit
Parámetros de la banda del canal superior.
5, 0, 2
DnCalcPeriodRange, DnCalcPeriodShift, DnDigit
Parámetros de la banda del canal inferior.
5, 0, 2
SignalBar
Desplazamiento de barra usado para evaluar señales de ruptura.
1
StopLossPoints, TakeProfitPoints
Stop-loss / take-profit en puntos de precio (convertidos con PriceStep).
1000, 2000
Establezca la propiedad Volume de la estrategia para controlar el tamaño de posición. Los parámetros de stop-loss y take-profit son opcionales; establézcalos en 0 para deshabilitar cualquier nivel de protección.
Notas
La estrategia espera suficiente historial para formar el canal deslizante (aproximadamente max(shift + 2 × range) velas). Gestiona automáticamente los buffers internos e ignora las señales hasta que haya suficientes datos disponibles.
El redondeo de precio se realiza usando dígitos decimales, reflejando el comportamiento de redondeo del indicador MQL.
La implementación en Python se omite intencionalmente según las instrucciones del proyecto; solo se proporciona la versión en C#.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Sliding range breakout strategy using Highest/Lowest channel.
/// Enters on breakout above/below the channel, exits on opposite breakout.
/// </summary>
public class ExpRjSlidingRangeRjDigitSystemTmPlusStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _period;
private decimal? _prevUpper;
private decimal? _prevLower;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int Period
{
get => _period.Value;
set => _period.Value = value;
}
public ExpRjSlidingRangeRjDigitSystemTmPlusStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_period = Param(nameof(Period), 10)
.SetGreaterThanZero()
.SetDisplay("Period", "Channel lookback", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevUpper = null;
_prevLower = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevUpper = null;
_prevLower = null;
var highest = new Highest { Length = Period };
var lowest = new Lowest { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, highest);
DrawIndicator(area, lowest);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal upper, decimal lower)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevUpper = upper;
_prevLower = lower;
return;
}
var close = candle.ClosePrice;
if (_prevUpper == null || _prevLower == null)
{
_prevUpper = upper;
_prevLower = lower;
return;
}
// Breakout above previous upper → buy
if (close > _prevUpper.Value && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Breakdown below previous lower → sell
else if (close < _prevLower.Value && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevUpper = upper;
_prevLower = lower;
}
}
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 Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class exp_rj_sliding_range_rj_digit_system_tm_plus_strategy(Strategy):
def __init__(self):
super(exp_rj_sliding_range_rj_digit_system_tm_plus_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._period = self.Param("Period", 10) \
.SetDisplay("Period", "Channel lookback", "Indicators")
self._prev_upper = None
self._prev_lower = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def Period(self):
return self._period.Value
def OnReseted(self):
super(exp_rj_sliding_range_rj_digit_system_tm_plus_strategy, self).OnReseted()
self._prev_upper = None
self._prev_lower = None
def OnStarted2(self, time):
super(exp_rj_sliding_range_rj_digit_system_tm_plus_strategy, self).OnStarted2(time)
self._prev_upper = None
self._prev_lower = None
highest = Highest()
highest.Length = self.Period
lowest = Lowest()
lowest.Length = self.Period
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(highest, lowest, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, highest)
self.DrawIndicator(area, lowest)
self.DrawOwnTrades(area)
def _on_process(self, candle, upper_value, lower_value):
if candle.State != CandleStates.Finished:
return
uv = float(upper_value)
lv = float(lower_value)
close = float(candle.ClosePrice)
if self._prev_upper is None or self._prev_lower is None:
self._prev_upper = uv
self._prev_lower = lv
return
# Breakout above previous upper
if close > self._prev_upper and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Breakdown below previous lower
elif close < self._prev_lower and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_upper = uv
self._prev_lower = lv
def CreateClone(self):
return exp_rj_sliding_range_rj_digit_system_tm_plus_strategy()