A estratégia recria o comportamento do assessor especialista MetaTrader 5 Exp_RJTX_Matches_Smoothed_Duplex.mq5. Dois blocos de sinal RJTX independentes analisam preços de abertura e fechamento suavizados em seus respectivos períodos de tempo. Cada bloco classifica cada vela completada como altista ou baixista, dependendo se o fechamento suavizado sobe acima da abertura suavizada de Period barras atrás. "Correspondências" altistas acionam entradas para o módulo comprado, enquanto correspondências baixistas gerenciam o módulo vendido.
Geração de sinais
Suavização – ambos os blocos alimentam aberturas e fechamentos de velas no algoritmo de suavização selecionado. O mesmo método é aplicado aos fluxos de abertura e fechamento, mas instâncias separadas são usadas para manter os buffers internos independentes.
Comparação – assim que houver histórico suficiente disponível, o fechamento suavizado atual é comparado com a abertura suavizada registrada Period barras antes.
Detecção de correspondência – se o fechamento for maior, a vela recebe uma correspondência altista; caso contrário, torna-se baixista. Os sinais são avaliados após o deslocamento de SignalBar velas fechadas, assim como o acesso ao buffer MT5.
Gestão de posições
O bloco comprado abre uma posição comprada (cobrindo qualquer vendido existente se permitido) quando uma correspondência altista alcança a janela de avaliação. Uma correspondência baixista fecha a posição comprada se as saídas compradas estiverem habilitadas.
O bloco vendido espelha essa lógica: uma correspondência baixista abre uma operação vendida (fechando a exposição comprada se permitido) e uma correspondência altista cobre o vendido.
As estratégias do StockSharp são neteadas. Portanto, módulos opostos fecham a posição atual antes de abrir uma nova, em vez de manter duas posições hedgeadas independentes como a versão MT5. Desative o parâmetro Allow ... Close correspondente para proibir a cobertura automática.
Gestão de risco
Stops e metas de lucro são expressos em passos de preço (PriceStep × points). Para cada vela terminada, a estratégia verifica se o intervalo da barra toca o nível ativo de stop-loss ou take-profit e fecha a posição correspondente imediatamente. Isso emula o comportamento das ordens de proteção MT5 sem depender de ordens gerenciadas pelo broker.
Parâmetros
Seção
Parâmetro
Padrão
Descrição
Long
LongCandleType
H4
Período usado pelo bloco RJTX comprado.
Long
LongVolume
0.1
Volume aberto quando um sinal comprado é executado.
Long
LongAllowOpen
true
Habilitar abertura de posições compradas.
Long
LongAllowClose
true
Habilitar fechamento de posições compradas em correspondências baixistas.
Long
LongStopLossPoints
1000
Distância de stop-loss para operações compradas em passos de preço (0 desabilita a verificação).
Long
LongTakeProfitPoints
2000
Distância de take-profit para operações compradas em passos de preço (0 desabilita a verificação).
Número de barras entre o fechamento suavizado atual e a abertura suavizada histórica.
Long
LongMethod
Sma
Algoritmo de suavização para o bloco comprado (Sma, Ema, Smma, Lwma, Jjma, Jurx, Parma, T3, Vidya, Ama).
Long
LongLength
12
Comprimento do filtro de suavização aplicado às séries de abertura/fechamento.
Long
LongPhase
15
Parâmetro de fase para filtros estilo Jurik (mantido por compatibilidade).
Short
ShortCandleType
H4
Período usado pelo bloco RJTX vendido.
Short
ShortVolume
0.1
Volume aberto quando um sinal vendido é executado.
Short
ShortAllowOpen
true
Habilitar abertura de posições vendidas.
Short
ShortAllowClose
true
Habilitar fechamento de posições vendidas em correspondências altistas.
Short
ShortStopLossPoints
1000
Distância de stop-loss para operações vendidas em passos de preço (0 desabilita a verificação).
Short
ShortTakeProfitPoints
2000
Distância de take-profit para operações vendidas em passos de preço (0 desabilita a verificação).
Short
ShortSignalBar
1
Deslocamento aplicado ao ler buffers RJTX para o bloco vendido.
Short
ShortPeriod
10
Número de barras entre o fechamento suavizado atual e a abertura suavizada histórica.
Short
ShortMethod
Sma
Algoritmo de suavização para o bloco vendido.
Short
ShortLength
12
Comprimento do filtro de suavização aplicado aos sinais vendidos.
Short
ShortPhase
15
Parâmetro de fase para filtros estilo Jurik no bloco vendido.
Notas
Jjma corresponde ao Jurik Moving Average. Jurx, Parma e Vidya são aproximados com Zero-Lag EMA, Arnaud Legoux MA e EMA respectivamente, porque o StockSharp não expõe filtros idênticos da biblioteca SmoothAlgorithms.
A lógica de stop-loss / take-profit é avaliada nos extremos da vela. Spikes intrabarra mais curtos que o máximo/mínimo da vela não acionarão saídas.
Os sinais são processados apenas em velas completadas; correspondências intrabarra são ignoradas conforme o comportamento IsNewBar do 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()