Estrategia de Trailing Stop NRTR
Esta estrategia sigue las tendencias del mercado usando el indicador NRTR (Nick R's Trend Reverse). El algoritmo calcula un nivel de trailing stop derivado del rango promedio de velas recientes. Cuando el precio rompe el nivel de trailing, la posición se invierte en la dirección del rompimiento. El sistema funciona tanto en el lado largo como corto e incluye protecciones opcionales de stop-loss y take-profit.
La longitud del NRTR define la sensibilidad del trailing stop: un período más corto reacciona más rápido pero puede dar falsas señales, mientras que un período más largo filtra el ruido. Un parámetro adicional de desplazamiento de dígitos ajusta el indicador a instrumentos con diferentes escalas de precio. La estrategia se suscribe a velas del marco temporal elegido y calcula los valores NRTR en cada barra finalizada.
Detalles
- Lógica de entrada:
- Largo: El precio cruza por encima del nivel NRTR después de una tendencia bajista.
- Corto: El precio cruza por debajo del nivel NRTR después de una tendencia alcista.
- Lógica de salida:
- Las posiciones se invierten cuando ocurre un rompimiento opuesto.
- Stops: Stop-loss y take-profit opcionales mediante
StartProtection.
- Valores predeterminados:
Length = 10
DigitsShift = 0
TakeProfit = 2000 puntos
StopLoss = 1000 puntos
CandleType = velas de 1 hora
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Ambos
- Indicadores: NRTR, ATR
- Stops: Sí
- Complejidad: Moderado
- Marco temporal: Configurable
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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()