A estratégia de reversão NRTR é uma versão StockSharp do MetaTrader 4 especialista "NRTR_Revers". O sistema original traça uma linha Noise Reduction Trailing Range (NRTR) derivada do Average True Range (ATR) e inverte posições sempre que o preço quebra de forma convincente essa barreira adaptativa. A versão StockSharp mantém o comportamento de posição única do consultor especialista, espelha o cálculo de deslocamento baseado em ATR e gerencia saídas por meio do módulo de proteção integrado.
Lógica de negociação
Assine a série de velas principal configurada por CandleType e processe apenas velas concluídas, replicando a verificação do contador Bars de MetaTrader.
Alimente um indicador AverageTrueRange com período Period. O valor ATR mais recente é traduzido de unidades de preço em "pontos" (etapas de preço) antes de ser multiplicado por AtrMultiplier / 10, assim como a expressão MQL MathRound(k * (iATR / Point) / 10).
Mantenha um cache contínuo de velas recentes para reconstruir o pivô NRTR. A mínima mais baixa (para uma tendência de alta) ou a máxima mais alta (para uma tendência de baixa) nas últimas Period velas se torna o pivô base.
Mude o pivô pelo deslocamento baseado em ATR para formar a linha final:
Tendência de alta: line = lowestLow - offset.
Tendência de baixa: line = highestHigh + offset.
Detecte uma reversão sempre que uma das condições for atendida:
Rompimento de fechamento: o último fechamento da vela cruza a linha em mais de offset pontos.
Expansão de intervalo: as velas Period / 2 mais recentes se estendem além da linha em pelo menos ReverseDistancePoints pontos. Isso reproduz o teste de reversão secundária do código MQL que analisou mais atrás na história.
Quando a direção mudar, envie uma ordem de mercado (BuyMarket ou SellMarket) com volume TradeVolume + |Position|. Isso fecha a exposição oposta e abre a nova posição, correspondendo ao comportamento MetaTrader de fechar e reverter imediatamente.
As saídas são delegadas ao gerenciador de risco iniciado por StartProtection, que converte as distâncias configuradas de stop-loss e take-profit dos pontos em unidades de preço específicas da corretora.
Parâmetros
Nome
Tipo
Padrão
Descrição
CandleType
DataType
Período de 15 minutos
Série de velas usadas para cálculos.
TakeProfitPoints
decimal
4000
Distância de take-profit expressa em etapas de preço do instrumento. Defina como zero para desativar.
StopLossPoints
decimal
4000
Distância de stop-loss em etapas de preço. Defina como zero para desativar.
TrailingStopPoints
decimal
0
Parâmetro reservado para módulos finais externos. Não usado dentro da estratégia.
TradeVolume
decimal
0.1
Volume base (lotes) espelhado da configuração MetaTrader.
Period
int
3
Número de velas usadas para calcular o pivô NRTR.
ReverseDistancePoints
int
100
Distância de fuga adicional em pontos necessários para confirmação.
AtrMultiplier
decimal
3.0
Multiplicador aplicado a ATR antes de construir o deslocamento.
Gestão de risco
A estratégia chama StartProtection com UnitTypes.Step, então as distâncias dos pontos configurados são automaticamente convertidas em compensações de preço absoluto com base em Security.PriceStep.
Se o stop-loss e o take-profit forem zero, StartProtection() ainda será chamado para ativar o monitoramento da posição de StockSharp, replicando as verificações de segurança usadas pelo EA.
TrailingStopPoints é exposto para fins de integridade, mas deixado para extensões futuras, porque o especialista original não implementou uma função final, apesar de declarar o parâmetro.
Detalhes de implementação
A estratégia depende exclusivamente do API (SubscribeCandles().BindEx(...)) de alto nível com ligações de indicadores; nenhum loop de indicador manual ou chamadas GetValue proibidas são usadas.
Uma estrutura CandleSnapshot compacta mantém apenas valores altos/baixos/fechados de velas recentes, evitando armazenamento pesado de ICandleMessage enquanto ainda reproduz as janelas de lookback NRTR.
A conversão de ATR em pontos respeita a fórmula MetaTrader dividindo ATR pela etapa do instrumento antes de aplicar o multiplicador e o arredondamento.
O corte do histórico mantém o cache em Period * 3 velas para corresponder às necessidades de lookback originais sem crescimento descontrolado.
Diferenças do especialista MetaTrader
O fechamento da ordem é simplificado: em vez de iterar cada negociação e chamar OrderClose, a porta StockSharp envia uma única ordem de mercado que lisonjeia a posição existente e estabelece a nova direção.
Números mágicos, slippage e parâmetros específicos do ticket são omitidos porque StockSharp gerencia pedidos de maneira diferente.
As anotações do gráfico são opcionais; quando uma área do gráfico está disponível, a série ATR e as próprias negociações são plotadas para fins de depuração.
Dicas de uso
Alinhe TradeVolume com a etapa do lote de bolsa (Security.VolumeStep) antes de ativar a negociação ao vivo.
Sintonize Period, AtrMultiplier e ReverseDistancePoints juntos. Períodos mais curtos exigem distâncias reversas menores para evitar negociações excessivas.
Defina as distâncias de parada/alvo de acordo com o tamanho do tick do instrumento. Em instrumentos com PriceStep grande, reduza os deslocamentos padrão de 4.000 pontos para níveis realistas.
Indicadores
AverageTrueRange(Period) calculado com base nos preços máximo/mínimo/fechamento.
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>
/// NRTR reversal strategy using ATR-based trailing stop.
/// Maintains a trailing line based on ATR distance from price extremes.
/// Reverses position when price crosses the trailing line.
/// </summary>
public class NrtrReversalStrategy : Strategy
{
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<DataType> _candleType;
private decimal _trailLine;
private decimal _extreme;
private int _trend; // 1 = up, -1 = down, 0 = init
private bool _isInitialized;
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal AtrMultiplier { get => _atrMultiplier.Value; set => _atrMultiplier.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public NrtrReversalStrategy()
{
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetDisplay("ATR Period", "ATR period for trailing", "Indicators");
_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
.SetDisplay("ATR Multiplier", "ATR multiplier for trailing distance", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromDays(1).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_trailLine = 0m;
_extreme = 0m;
_trend = 0;
_isInitialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_isInitialized = false;
_trend = 0;
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
var offset = atrValue * AtrMultiplier;
if (!_isInitialized)
{
_extreme = close;
_trailLine = close - offset;
_trend = 1;
_isInitialized = true;
return;
}
if (_trend == 1)
{
if (close > _extreme)
_extreme = close;
_trailLine = Math.Max(_trailLine, _extreme - offset);
if (close < _trailLine)
{
// Switch to downtrend
_trend = -1;
_extreme = close;
_trailLine = close + offset;
if (Position > 0)
SellMarket();
SellMarket();
}
else if (Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
}
else
{
if (close < _extreme)
_extreme = close;
_trailLine = Math.Min(_trailLine, _extreme + offset);
if (close > _trailLine)
{
// Switch to uptrend
_trend = 1;
_extreme = close;
_trailLine = close - offset;
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class nrtr_reversal_strategy(Strategy):
"""NRTR reversal strategy using ATR-based trailing stop.
Maintains a trailing line based on ATR distance from price extremes.
Reverses position when price crosses the trailing line."""
def __init__(self):
super(nrtr_reversal_strategy, self).__init__()
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR period for trailing", "Indicators")
self._atr_multiplier = self.Param("AtrMultiplier", 2.0) \
.SetDisplay("ATR Multiplier", "ATR multiplier for trailing distance", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromDays(1))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._trail_line = 0.0
self._extreme = 0.0
self._trend = 0
self._is_initialized = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def AtrPeriod(self):
return self._atr_period.Value
@property
def AtrMultiplier(self):
return self._atr_multiplier.Value
def OnReseted(self):
super(nrtr_reversal_strategy, self).OnReseted()
self._trail_line = 0.0
self._extreme = 0.0
self._trend = 0
self._is_initialized = False
def OnStarted2(self, time):
super(nrtr_reversal_strategy, self).OnStarted2(time)
self._is_initialized = False
self._trend = 0
atr = AverageTrueRange()
atr.Length = self.AtrPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(atr, self._process_candle).Start()
def _process_candle(self, candle, atr_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
atr_val = float(atr_value)
offset = atr_val * float(self.AtrMultiplier)
if not self._is_initialized:
self._extreme = close
self._trail_line = close - offset
self._trend = 1
self._is_initialized = True
return
if self._trend == 1:
if close > self._extreme:
self._extreme = close
candidate = self._extreme - offset
if candidate > self._trail_line:
self._trail_line = candidate
if close < self._trail_line:
# Switch to downtrend
self._trend = -1
self._extreme = close
self._trail_line = close + offset
if self.Position > 0:
self.SellMarket()
self.SellMarket()
elif self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
else:
if close < self._extreme:
self._extreme = close
candidate = self._extreme + offset
if candidate < self._trail_line:
self._trail_line = candidate
if close > self._trail_line:
# Switch to uptrend
self._trend = 1
self._extreme = close
self._trail_line = close - offset
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return nrtr_reversal_strategy()