La estrategia recrea el comportamiento del asesor experto de MetaTrader 5 Exp_RJTX_Matches_Smoothed_Duplex.mq5. Dos bloques de señal RJTX independientes analizan precios de apertura y cierre suavizados en sus respectivos marcos temporales. Cada bloque clasifica cada vela completada como alcista o bajista dependiendo de si el cierre suavizado sube por encima de la apertura suavizada de Period barras atrás. Las "coincidencias" alcistas activan entradas para el módulo largo, mientras que las coincidencias bajistas gestionan el módulo corto.
Generación de señales
Suavizado – ambos bloques alimentan las aperturas y cierres de las velas en el algoritmo de suavizado seleccionado. Se aplica el mismo método a los flujos de apertura y cierre, pero se usan instancias separadas para mantener los búferes internos independientes.
Comparación – una vez que hay suficiente historia disponible, el cierre suavizado actual se compara con la apertura suavizada registrada Period barras antes.
Detección de coincidencia – si el cierre es mayor, la vela recibe una coincidencia alcista; de lo contrario se vuelve bajista. Las señales se evalúan después de desplazarse por SignalBar velas cerradas, igual que el acceso al búfer MT5.
Gestión de posiciones
El bloque largo abre una posición larga (cubriendo cualquier corto existente si está permitido) cuando una coincidencia alcista alcanza la ventana de evaluación. Una coincidencia bajista cierra la posición larga si las salidas largas están habilitadas.
El bloque corto espeja esta lógica: una coincidencia bajista abre una operación corta (cerrando la exposición larga si está permitido) y una coincidencia alcista cubre el corto.
Las estrategias de StockSharp son neteadas. Por lo tanto, los módulos opuestos cierran la posición actual antes de abrir una nueva, en lugar de mantener dos posiciones hedgeadas independientes como la versión MT5. Desactiva el parámetro Allow ... Close correspondiente para prohibir la cobertura automática.
Gestión del riesgo
Los stops y objetivos de ganancia se expresan en pasos de precio (PriceStep × points). Para cada vela terminada, la estrategia verifica si el rango de la barra toca el nivel de stop-loss o take-profit activo y cierra la posición correspondiente inmediatamente. Esto emula el comportamiento de las órdenes de protección MT5 sin depender de órdenes gestionadas por el broker.
Parámetros
Sección
Parámetro
Predeterminado
Descripción
Long
LongCandleType
H4
Marco temporal usado por el bloque RJTX largo.
Long
LongVolume
0.1
Volumen abierto cuando se ejecuta una señal larga.
Long
LongAllowOpen
true
Habilitar apertura de posiciones largas.
Long
LongAllowClose
true
Habilitar cierre de posiciones largas en coincidencias bajistas.
Long
LongStopLossPoints
1000
Distancia de stop-loss para operaciones largas en pasos de precio (0 deshabilita la comprobación).
Long
LongTakeProfitPoints
2000
Distancia de take-profit para operaciones largas en pasos de precio (0 deshabilita la comprobación).
Número de barras entre el cierre suavizado actual y la apertura suavizada histórica.
Long
LongMethod
Sma
Algoritmo de suavizado para el bloque largo (Sma, Ema, Smma, Lwma, Jjma, Jurx, Parma, T3, Vidya, Ama).
Long
LongLength
12
Longitud del filtro de suavizado aplicado a las series de apertura/cierre.
Long
LongPhase
15
Parámetro de fase para filtros de estilo Jurik (mantenido por compatibilidad).
Short
ShortCandleType
H4
Marco temporal usado por el bloque RJTX corto.
Short
ShortVolume
0.1
Volumen abierto cuando se ejecuta una señal corta.
Short
ShortAllowOpen
true
Habilitar apertura de posiciones cortas.
Short
ShortAllowClose
true
Habilitar cierre de posiciones cortas en coincidencias alcistas.
Short
ShortStopLossPoints
1000
Distancia de stop-loss para operaciones cortas en pasos de precio (0 deshabilita la comprobación).
Short
ShortTakeProfitPoints
2000
Distancia de take-profit para operaciones cortas en pasos de precio (0 deshabilita la comprobación).
Short
ShortSignalBar
1
Desplazamiento aplicado al leer búferes RJTX para el bloque corto.
Short
ShortPeriod
10
Número de barras entre el cierre suavizado actual y la apertura suavizada histórica.
Short
ShortMethod
Sma
Algoritmo de suavizado para el bloque corto.
Short
ShortLength
12
Longitud del filtro de suavizado aplicado a las señales cortas.
Short
ShortPhase
15
Parámetro de fase para filtros de estilo Jurik en el bloque corto.
Notas
Jjma se mapea a Jurik Moving Average. Jurx, Parma y Vidya se aproximan con Zero-Lag EMA, Arnaud Legoux MA y EMA respectivamente, porque StockSharp no expone filtros idénticos de la biblioteca SmoothAlgorithms.
La lógica de stop-loss / take-profit se evalúa en los extremos de la vela. Los picos intrabarra más cortos que el máximo/mínimo de la vela no activarán salidas.
Las señales se procesan en velas completadas únicamente; las coincidencias intrabarra se ignoran conforme al comportamiento IsNewBar de MT5.
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>
/// Exp RJTX Matches Smoothed Duplex strategy using SmoothedMA crossover.
/// Buys when fast SmoothedMA crosses above slow SmoothedMA.
/// Sells on reverse crossover.
/// </summary>
public class ExpRjtxMatchesSmoothedDuplexStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private SmoothedMovingAverage _fast;
private SmoothedMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// Fast smoothed MA period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow smoothed MA period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="ExpRjtxMatchesSmoothedDuplexStrategy"/> class.
/// </summary>
public ExpRjtxMatchesSmoothedDuplexStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast smoothed MA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow smoothed MA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400)
.SetNotNegative()
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fast = null;
_slow = null;
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new SmoothedMovingAverage { Length = FastPeriod };
_slow = new SmoothedMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_fast.IsFormed || !_slow.IsFormed)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
// SmoothedMA crossover
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 80;
}
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 80;
}
_prevFast = fastValue;
_prevSlow = slowValue;
}
}
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 SmoothedMovingAverage
from StockSharp.Algo.Strategies import Strategy
class exp_rjtx_matches_smoothed_duplex_strategy(Strategy):
"""
SmoothedMA crossover strategy with SL/TP and cooldown.
Buys when fast SmoothedMA crosses above slow, sells on reverse.
"""
def __init__(self):
super(exp_rjtx_matches_smoothed_duplex_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 12) \
.SetDisplay("Fast Period", "Fast smoothed MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow smoothed MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(exp_rjtx_matches_smoothed_duplex_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(exp_rjtx_matches_smoothed_duplex_strategy, self).OnStarted2(time)
fast = SmoothedMovingAverage()
fast.Length = self._fast_period.Value
slow = SmoothedMovingAverage()
slow.Length = self._slow_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self._process_candle).Start()
def _process_candle(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_fast = float(fast_val)
self._prev_slow = float(slow_val)
return
fast_val = float(fast_val)
slow_val = float(slow_val)
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 1.0
sl_pts = self._stop_loss_points.Value
tp_pts = self._take_profit_points.Value
if self.Position > 0 and self._entry_price > 0:
if sl_pts > 0 and close <= self._entry_price - sl_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if tp_pts > 0 and close >= self._entry_price + tp_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if sl_pts > 0 and close >= self._entry_price + sl_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if tp_pts > 0 and close <= self._entry_price - tp_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 80
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return exp_rjtx_matches_smoothed_duplex_strategy()