Стратегия реализует алгоритм Nick Rypock Trailing Reverse (NRTR) с дополнительными стрелками сигналов. Это конвертация оригинального примера MQL5 "Exp_NRTR_extr" на высокоуровневый API StockSharp.
Как работает
Пользовательский индикатор NrtrExtrIndicator рассчитывает средний диапазон за заданный период и строит следящий уровень, который повторяет цену.
При пробое этого уровня индикатор меняет направление и генерирует сигнал на покупку или продажу.
Стратегия открывает длинную позицию при сигнале покупки и короткую при сигнале продажи.
Текущие позиции закрываются при противоположном сигнале либо при достижении уровней стоп-лосса или тейк-профита.
Параметры
Имя
Описание
Period
Количество свечей для расчёта среднего диапазона.
Digits Shift
Дополнительная поправка точности для коэффициента диапазона.
Stop Loss
Защитный стоп в пунктах цены.
Take Profit
Целевая прибыль в пунктах цены.
Enable Buy Open / Enable Sell Open
Разрешить открытие длинных или коротких позиций.
Enable Buy Close / Enable Sell Close
Разрешить закрытие позиций по противоположным сигналам.
Candle Type
Таймфрейм свечей для расчёта индикатора.
Примечания
Индикатор использует Average True Range для оценки волатильности рынка. Для наглядности стратегия автоматически выводит свечи и совершённые сделки на график.
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()