NRTR-Trailing-Stop-Strategie
Diese Strategie folgt Markttrends mithilfe des NRTR (Nick R's Trend Reverse)-Indikators. Der Algorithmus berechnet ein Trailing-Stop-Level, das aus der durchschnittlichen Spanne der letzten Kerzen abgeleitet wird. Wenn der Preis das Trailing-Level bricht, kehrt sich die Position in Richtung des Ausbruchs um. Das System arbeitet auf beiden Long- und Short-Seiten und enthält optionale Stop-Loss- und Take-Profit-Schutzmaßnahmen.
Die NRTR-Länge definiert die Empfindlichkeit des Trailing-Stops: Eine kürzere Periode reagiert schneller, kann aber zu Fehlsignalen führen, während eine längere Periode Rauschen herausfiltert. Ein zusätzlicher Stellenverschiebungsparameter passt den Indikator an Instrumente mit unterschiedlichen Preisskalen an. Die Strategie abonniert Kerzen des gewählten Zeitrahmens und berechnet die NRTR-Werte auf jeder fertigen Bar.
Details
- Einstiegslogik:
- Long: Preis kreuzt nach einem Abwärtstrend über das NRTR-Level.
- Short: Preis kreuzt nach einem Aufwärtstrend unter das NRTR-Level.
- Ausstiegslogik:
- Positionen werden bei einem entgegengesetzten Ausbruch umgekehrt.
- Stops: Optionaler Stop-Loss und Take-Profit über
StartProtection.
- Standardwerte:
Length = 10
DigitsShift = 0
TakeProfit = 2000 Punkte
StopLoss = 1000 Punkte
CandleType = 1-Stunden-Kerzen
- Filter:
- Kategorie: Trendfolge
- Richtung: Beide
- Indikatoren: NRTR, ATR
- Stops: Ja
- Komplexität: Moderat
- Zeitrahmen: Flexibel
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
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()