This strategy implements the Nick Rypock Trailing Reverse (NRTR) algorithm with additional signal arrows. It is a conversion of the original MQL5 example "Exp_NRTR_extr" to the StockSharp high level API.
How It Works
The custom NrtrExtrIndicator computes an average range over a configurable period and draws a trailing level that follows price.
When the price reverses beyond this level the indicator flips direction and emits a buy or sell signal.
The strategy opens a long position on a buy signal and a short position on a sell signal.
Existing positions are closed on the opposite signal or when the defined stop loss or take profit levels are hit.
Parameters
Name
Description
Period
Number of candles used for average range calculation.
Digits Shift
Additional precision adjustment applied to the range factor.
Stop Loss
Protective stop in price points.
Take Profit
Profit target in price points.
Enable Buy Open / Enable Sell Open
Allow opening long or short positions.
Enable Buy Close / Enable Sell Close
Allow closing existing positions on opposite signals.
Candle Type
Timeframe of candles used for the indicator.
Notes
The indicator is based on the Average True Range to estimate market volatility. For visualization the strategy automatically draws candles and executed trades on the chart area.
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 Extr strategy - trend following based on ATR-based trailing levels.
/// Opens long when trend turns up, short when trend turns down.
/// </summary>
public class NrtrExtrStrategy : Strategy
{
private readonly StrategyParam<int> _period;
private readonly StrategyParam<DataType> _candleType;
private decimal _price;
private decimal _value;
private int _trend;
private int _trendPrev;
private bool _initialized;
public int Period { get => _period.Value; set => _period.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public NrtrExtrStrategy()
{
_period = Param(nameof(Period), 10)
.SetGreaterThanZero()
.SetDisplay("Period", "ATR period for NRTR", "Indicator")
.SetOptimize(5, 20, 5);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Time frame", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_price = 0;
_value = 0;
_trend = 0;
_trendPrev = 0;
_initialized = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var atr = new AverageTrueRange { Length = Period };
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>();
if (atr <= 0)
return;
if (!_initialized)
{
_price = candle.ClosePrice;
_value = candle.ClosePrice;
_trend = 1;
_trendPrev = 1;
_initialized = true;
return;
}
var dK = atr / Period;
if (_trend >= 0)
{
_price = Math.Max(_price, candle.HighPrice);
_value = Math.Max(_value, _price * (1m - dK));
if (candle.ClosePrice < _value)
{
_price = candle.LowPrice;
_value = _price * (1m + dK);
_trend = -1;
}
}
else
{
_price = Math.Min(_price, candle.LowPrice);
_value = Math.Min(_value, _price * (1m + dK));
if (candle.ClosePrice > _value)
{
_price = candle.HighPrice;
_value = _price * (1m - dK);
_trend = 1;
}
}
var buySignal = _trendPrev <= 0 && _trend > 0;
var sellSignal = _trendPrev >= 0 && _trend < 0;
if (IsFormedAndOnlineAndAllowTrading())
{
if (buySignal && Position <= 0)
BuyMarket();
else if (sellSignal && Position >= 0)
SellMarket();
}
_trendPrev = _trend;
}
}
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 AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class nrtr_extr_strategy(Strategy):
def __init__(self):
super(nrtr_extr_strategy, self).__init__()
self._period = self.Param("Period", 10) \
.SetDisplay("Period", "ATR period for NRTR", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Time frame", "General")
self._price = 0.0
self._value = 0.0
self._trend = 0
self._trend_prev = 0
self._initialized = False
@property
def period(self):
return self._period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(nrtr_extr_strategy, self).OnReseted()
self._price = 0.0
self._value = 0.0
self._trend = 0
self._trend_prev = 0
self._initialized = False
def OnStarted2(self, time):
super(nrtr_extr_strategy, self).OnStarted2(time)
atr = AverageTrueRange()
atr.Length = self.period
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)
if atr <= 0:
return
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
if not self._initialized:
self._price = close
self._value = close
self._trend = 1
self._trend_prev = 1
self._initialized = True
return
dk = atr / self.period
if self._trend >= 0:
self._price = max(self._price, high)
self._value = max(self._value, self._price * (1.0 - dk))
if close < self._value:
self._price = low
self._value = self._price * (1.0 + dk)
self._trend = -1
else:
self._price = min(self._price, low)
self._value = min(self._value, self._price * (1.0 + dk))
if close > self._value:
self._price = high
self._value = self._price * (1.0 - dk)
self._trend = 1
buy_signal = self._trend_prev <= 0 and self._trend > 0
sell_signal = self._trend_prev >= 0 and self._trend < 0
if buy_signal and self.Position <= 0:
self.BuyMarket()
elif sell_signal and self.Position >= 0:
self.SellMarket()
self._trend_prev = self._trend
def CreateClone(self):
return nrtr_extr_strategy()