Открыть на GitHub

Обзор

NRTR Reversal — это порт стратегии MetaTrader 4 «NRTR_Revers» на платформу StockSharp. Исходный советник строит линию Noise Reduction Trailing Range (NRTR) на основе индикатора ATR и переворачивает позицию, когда цена уверенно пробивает эту адаптивную границу. Реализация для StockSharp сохраняет модель «одна позиция одновременно», повторяет расчёт ATR-смещения и передаёт выходы встроенному модулю защиты.

Торговая логика

  1. Подписка на свечи, указанные параметром CandleType, и обработка только завершённых баров — аналог проверки счётчика Bars в MetaTrader.
  2. Индикатор AverageTrueRange с периодом Period получает каждую свечу. Его последнее значение переводится из ценовых единиц в «пункты» (шаги цены) и умножается на AtrMultiplier / 10, что соответствует формуле MathRound(k * (iATR / Point) / 10) в MQL.
  3. Поддерживается скользящее окно свечей для расчёта опорной точки NRTR. Минимум за Period свечей (для ап-тренда) или максимум (для даун-тренда) выступает базовой точкой.
  4. Опорная точка смещается на величину ATR:
    • Ап-тренд: line = lowestLow - offset.
    • Даун-тренд: line = highestHigh + offset.
  5. Переворот тренда происходит, когда выполняется одно из условий:
    • Пробой по закрытию: цена закрытия последней свечи ушла за линию больше чем на offset пунктов.
    • Расширение диапазона: последние Period / 2 свечей заходят за линию как минимум на ReverseDistancePoints пунктов. Это повторяет дополнительную проверку из MQL, которая анализировала более ранние бары.
  6. При смене направления отправляется рыночная заявка (BuyMarket или SellMarket) объёмом TradeVolume + |Position|, что одновременно закрывает противоположную позицию и открывает новую — так же, как в MetaTrader.
  7. Выход из позиции передан в StartProtection, который автоматически превращает стоп и тейк из пунктов в реальные ценовые величины брокера.

Параметры

Название Тип Значение по умолчанию Описание
CandleType DataType Таймфрейм 15 минут Свечи, используемые в расчётах.
TakeProfitPoints decimal 4000 Дистанция тейк-профита в шагах цены. Ноль отключает тейк.
StopLossPoints decimal 4000 Дистанция стоп-лосса в шагах цены. Ноль отключает стоп.
TrailingStopPoints decimal 0 Резервный параметр для внешнего трейлинга. В стратегии не используется.
TradeVolume decimal 0.1 Базовый объём (лоты), перенесённый из MetaTrader.
Period int 3 Количество свечей для расчёта опорной точки NRTR.
ReverseDistancePoints int 100 Дополнительная дистанция подтверждения пробоя в пунктах.
AtrMultiplier decimal 3.0 Множитель ATR перед построением смещения.

Управление рисками

  • Стратегия вызывает StartProtection с параметрами в UnitTypes.Step, поэтому указанные в пунктах дистанции автоматически переводятся в абсолютные цены на основе Security.PriceStep.
  • Даже если стоп и тейк выключены (равны нулю), StartProtection() запускается для контроля позиций StockSharp — как и в оригинальном советнике.
  • Параметр TrailingStopPoints оставлен для совместимости: в MQL он объявлен, но логика трейлинга отсутствовала.

Особенности реализации

  • Используется только высокоуровневый API (SubscribeCandles().BindEx(...)); вручную индикаторы не пересчитываются, запрещённых GetValue нет.
  • Компактная структура CandleSnapshot хранит только High/Low/Close последних свечей, что повторяет нужные окна NRTR без тяжёлых объектов ICandleMessage.
  • Конвертация ATR в пункты выполняется по оригинальной формуле: значение делится на шаг цены, затем умножается на коэффициент и округляется.
  • История ограничена Period * 3 свечами, чтобы предотвратить рост памяти при длительной работе.

Отличия от версии MetaTrader

  • Закрытие сделок упрощено: вместо перебора ордеров и OrderClose отправляется одна рыночная заявка, которая и закрывает старую позицию, и открывает новую.
  • Magic number, проскальзывание и номера тикетов опущены — в StockSharp эти параметры обрабатываются иначе.
  • Графические элементы необязательны: при наличии области графика отображаются ATR и сделки для удобства анализа.

Практические советы

  • Перед реальной торговлей согласуйте TradeVolume с шагом лота инструмента (Security.VolumeStep).
  • Настраивайте Period, AtrMultiplier и ReverseDistancePoints совместно: чем короче период, тем меньше дистанция разворота во избежание гиперторговли.
  • Подберите величины стопов и тейков под размер тика. Для инструментов с крупным PriceStep уменьшите стандартные 4000 пунктов до реалистичных значений.

Индикаторы

  • AverageTrueRange(Period) на данных High/Low/Close.
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// NRTR reversal strategy using ATR-based trailing stop.
/// Maintains a trailing line based on ATR distance from price extremes.
/// Reverses position when price crosses the trailing line.
/// </summary>
public class NrtrReversalStrategy : Strategy
{
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _atrMultiplier;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _trailLine;
	private decimal _extreme;
	private int _trend; // 1 = up, -1 = down, 0 = init
	private bool _isInitialized;

	public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
	public decimal AtrMultiplier { get => _atrMultiplier.Value; set => _atrMultiplier.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public NrtrReversalStrategy()
	{
		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetDisplay("ATR Period", "ATR period for trailing", "Indicators");

		_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
			.SetDisplay("ATR Multiplier", "ATR multiplier for trailing distance", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromDays(1).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_trailLine = 0m;
		_extreme = 0m;
		_trend = 0;
		_isInitialized = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_isInitialized = false;
		_trend = 0;

		var atr = new AverageTrueRange { Length = AtrPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(atr, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal atrValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var close = candle.ClosePrice;
		var offset = atrValue * AtrMultiplier;

		if (!_isInitialized)
		{
			_extreme = close;
			_trailLine = close - offset;
			_trend = 1;
			_isInitialized = true;
			return;
		}

		if (_trend == 1)
		{
			if (close > _extreme)
				_extreme = close;

			_trailLine = Math.Max(_trailLine, _extreme - offset);

			if (close < _trailLine)
			{
				// Switch to downtrend
				_trend = -1;
				_extreme = close;
				_trailLine = close + offset;

				if (Position > 0)
					SellMarket();
				SellMarket();
			}
			else if (Position <= 0)
			{
				if (Position < 0)
					BuyMarket();
				BuyMarket();
			}
		}
		else
		{
			if (close < _extreme)
				_extreme = close;

			_trailLine = Math.Min(_trailLine, _extreme + offset);

			if (close > _trailLine)
			{
				// Switch to uptrend
				_trend = 1;
				_extreme = close;
				_trailLine = close - offset;

				if (Position < 0)
					BuyMarket();
				BuyMarket();
			}
			else if (Position >= 0)
			{
				if (Position > 0)
					SellMarket();
				SellMarket();
			}
		}
	}
}