Ver en GitHub

Estrategia NRTR Extr

Esta estrategia implementa el algoritmo Nick Rypock Trailing Reverse (NRTR) con flechas de señal adicionales. Es una conversión del ejemplo original de MQL5 "Exp_NRTR_extr" a la API de alto nivel de StockSharp.

Cómo funciona

  • El NrtrExtrIndicator personalizado calcula un rango promedio durante un período configurable y dibuja un nivel de trailing que sigue al precio.
  • Cuando el precio invierte más allá de este nivel, el indicador cambia de dirección y emite una señal de compra o venta.
  • La estrategia abre una posición larga ante una señal de compra y una posición corta ante una señal de venta.
  • Las posiciones existentes se cierran ante la señal opuesta o cuando se alcanzan los niveles definidos de stop loss o take profit.

Parámetros

Nombre Descripción
Period Número de velas usadas para el cálculo del rango promedio.
Digits Shift Ajuste de precisión adicional aplicado al factor de rango.
Stop Loss Stop protector en puntos de precio.
Take Profit Objetivo de ganancia en puntos de precio.
Enable Buy Open / Enable Sell Open Permitir apertura de posiciones largas o cortas.
Enable Buy Close / Enable Sell Close Permitir cierre de posiciones existentes ante señales opuestas.
Candle Type Marco temporal de velas usado para el indicador.

Notas

El indicador se basa en el Average True Range para estimar la volatilidad del mercado. Para visualización, la estrategia dibuja automáticamente velas y operaciones ejecutadas en el área del gráfico.

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;
	}
}