Estratégia de Trailing Stop NRTR
Esta estratégia segue as tendências do mercado usando o indicador NRTR (Nick R's Trend Reverse). O algoritmo calcula um nível de trailing stop derivado da faixa média das velas recentes. Quando o preço rompe o nível de trailing, a posição se inverte na direção do rompimento. O sistema funciona tanto no lado comprado quanto vendido e inclui proteções opcionais de stop-loss e take-profit.
O comprimento do NRTR define a sensibilidade do trailing stop: um período mais curto reage mais rápido, mas pode gerar falsas oscilações, enquanto um período mais longo filtra o ruído. Um parâmetro adicional de deslocamento de dígitos ajusta o indicador a instrumentos com diferentes escalas de preço. A estratégia se inscreve em velas do período escolhido e calcula os valores NRTR a cada barra finalizada.
Detalhes
- Lógica de entrada:
- Comprado: O preço cruza acima do nível NRTR após uma tendência de baixa.
- Vendido: O preço cruza abaixo do nível NRTR após uma tendência de alta.
- Lógica de saída:
- As posições são invertidas quando ocorre um rompimento oposto.
- Stops: Stop-loss e take-profit opcionais via
StartProtection.
- Valores padrão:
Length = 10
DigitsShift = 0
TakeProfit = 2000 pontos
StopLoss = 1000 pontos
CandleType = velas de 1 hora
- Filtros:
- Categoria: Seguidor de tendência
- Direção: Ambos
- Indicadores: NRTR, ATR
- Stops: Sim
- Complexidade: Moderado
- Período: Flexível
- Sazonalidade: Não
- Redes neurais: Não
- Divergência: Não
- Nível de risco: Médio
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// NRTR trailing stop strategy based on the NRTR indicator.
/// Opens long when trend turns up and short when trend turns down.
/// Includes optional stop-loss and take-profit.
/// </summary>
public class NrtrTrailingStopStrategy : Strategy
{
private readonly StrategyParam<int> _length;
private readonly StrategyParam<int> _digitsShift;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<DataType> _candleType;
private decimal _price;
private decimal _value;
private int _trend;
private bool _isInitialized;
/// <summary>
/// Number of bars for average range calculation.
/// </summary>
public int Length
{
get => _length.Value;
set => _length.Value = value;
}
/// <summary>
/// Digits adjustment for indicator sensitivity.
/// </summary>
public int DigitsShift
{
get => _digitsShift.Value;
set => _digitsShift.Value = value;
}
/// <summary>
/// Take-profit in price points.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Stop-loss in price points.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Type of candles used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="NrtrTrailingStopStrategy"/>.
/// </summary>
public NrtrTrailingStopStrategy()
{
_length = Param(nameof(Length), 20)
.SetGreaterThanZero()
.SetDisplay("NRTR Length", "Number of bars for average range", "Indicator")
.SetOptimize(5, 20, 5);
_digitsShift = Param(nameof(DigitsShift), 0)
.SetDisplay("Digits Shift", "Adjustment for price digits", "Indicator");
_takeProfit = Param(nameof(TakeProfit), 2000m)
.SetNotNegative()
.SetDisplay("Take Profit (pts)", "Take profit level in points", "Risk")
.SetOptimize(500m, 3000m, 500m);
_stopLoss = Param(nameof(StopLoss), 1000m)
.SetNotNegative()
.SetDisplay("Stop Loss (pts)", "Stop loss level in points", "Risk")
.SetOptimize(500m, 3000m, 500m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for processing", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_price = 0m;
_value = 0m;
_trend = 0;
_isInitialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var atr = new AverageTrueRange { Length = Length };
var subscription = SubscribeCandles(CandleType);
subscription.BindEx(atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!atrValue.IsFormed)
return;
var atr = atrValue.GetValue<decimal>();
var dK = atr / Length * (decimal)Math.Pow(10, -DigitsShift);
if (!_isInitialized)
{
_price = candle.ClosePrice;
_value = _price;
_trend = 0;
_isInitialized = true;
return;
}
var isOnline = IsFormedAndOnlineAndAllowTrading();
if (_trend >= 0)
{
_price = Math.Max(_price, candle.ClosePrice);
_value = Math.Max(_value, _price * (1m - dK));
if (candle.ClosePrice < _value)
{
_price = candle.ClosePrice;
_value = _price * (1m + dK);
_trend = -1;
if (isOnline && Position >= 0)
SellMarket();
}
}
else
{
_price = Math.Min(_price, candle.ClosePrice);
_value = Math.Min(_value, _price * (1m + dK));
if (candle.ClosePrice > _value)
{
_price = candle.ClosePrice;
_value = _price * (1m - dK);
_trend = 1;
if (isOnline && Position <= 0)
BuyMarket();
}
}
}
}
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_trailing_stop_strategy(Strategy):
def __init__(self):
super(nrtr_trailing_stop_strategy, self).__init__()
self._length = self.Param("Length", 20) \
.SetDisplay("NRTR Length", "Number of bars for average range", "Indicator")
self._digits_shift = self.Param("DigitsShift", 0) \
.SetDisplay("Digits Shift", "Adjustment for price digits", "Indicator")
self._take_profit = self.Param("TakeProfit", 2000.0) \
.SetDisplay("Take Profit (pts)", "Take profit level in points", "Risk")
self._stop_loss = self.Param("StopLoss", 1000.0) \
.SetDisplay("Stop Loss (pts)", "Stop loss level in points", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles for processing", "General")
self._price = 0.0
self._value = 0.0
self._trend = 0
self._is_initialized = False
@property
def length(self):
return self._length.Value
@property
def digits_shift(self):
return self._digits_shift.Value
@property
def take_profit(self):
return self._take_profit.Value
@property
def stop_loss(self):
return self._stop_loss.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(nrtr_trailing_stop_strategy, self).OnReseted()
self._price = 0.0
self._value = 0.0
self._trend = 0
self._is_initialized = False
def OnStarted2(self, time):
super(nrtr_trailing_stop_strategy, self).OnStarted2(time)
atr = AverageTrueRange()
atr.Length = self.length
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(atr, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle, atr_value):
if candle.State != CandleStates.Finished:
return
if not atr_value.IsFormed:
return
atr = float(atr_value)
dk = atr / self.length * (10.0 ** (-self.digits_shift))
close = float(candle.ClosePrice)
if not self._is_initialized:
self._price = close
self._value = close
self._trend = 0
self._is_initialized = True
return
if self._trend >= 0:
self._price = max(self._price, close)
self._value = max(self._value, self._price * (1.0 - dk))
if close < self._value:
self._price = close
self._value = self._price * (1.0 + dk)
self._trend = -1
if self.Position >= 0:
self.SellMarket()
else:
self._price = min(self._price, close)
self._value = min(self._value, self._price * (1.0 + dk))
if close > self._value:
self._price = close
self._value = self._price * (1.0 - dk)
self._trend = 1
if self.Position <= 0:
self.BuyMarket()
def CreateClone(self):
return nrtr_trailing_stop_strategy()