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