Ver en GitHub

Estrategia del canal comercial ATR

La estrategia Trade Channel replica el asesor experto MetaTrader original que negociaba canales de precios con paradas basadas en ATR. Espera a que los límites del canal permanezcan sin cambios y a que la última vela toque o rechace esos niveles. Cuando aparece la configuración, la estrategia abre una posición en la dirección opuesta al toque y aplica un trailing stop adaptativo medido en puntos.

El enfoque busca explotar la reversión a la media en torno a un canal de precios estable. Filtra las señales para que el canal deba estar plano (sin nuevos máximos ni mínimos) antes de ingresar. Las paradas de protección se colocan más allá del canal utilizando el rango verdadero promedio, y una parada dinámica opcional bloquea las ganancias una vez que se desarrolla el movimiento.

Detalles

  • Criterios de entrada:
    • Corto: el máximo del canal es igual al máximo del canal anterior y la última vela rompe ese máximo o cierra entre el máximo y el pivote (high + low + close) / 3.
    • Largo: el mínimo del canal es igual al mínimo del canal anterior y la última vela rompe ese mínimo o cierra entre el mínimo y el pivote.
  • Largo/Corto: Ambas direcciones, pero solo una posición a la vez.
  • Criterios de salida:
    • Largo: el precio toca el máximo del canal mientras el máximo se mantiene sin cambios.
    • Corto: el precio toca el mínimo del canal mientras el mínimo se mantiene sin cambios.
    • El trailing stop opcional se ajusta detrás del mercado una vez que las ganancias superan los TrailingDistance puntos.
  • Paradas: Stop Loss inicial en channel boundary ± ATR. El trailing stop lo reemplaza cuando se activa.
  • Valores predeterminados:
    • Volume = 0,1 m
    • ChannelPeriod = 20
    • AtrPeriod = 4
    • TrailingDistance = 30
    • CandleType = velas de 30 minutos
  • Filtros:
    • Categoría: Reversión a la media
    • Dirección: Ambos
    • Indicadores: rango verdadero más alto, más bajo y promedio
    • Paradas: ATR parada, arrastrando
    • Complejidad: Intermedia
    • Plazo: Intradiario (30 minutos)
    • Estacionalidad: No
    • Redes neuronales: No
    • Divergencia: No
    • Nivel de riesgo: medio

Notas

  • Volume controla el tamaño del pedido; sólo puede existir una posición a la vez.
  • TrailingDistance se especifica en puntos (escalones de precio). Establezca en cero para desactivar el trailing stop.
  • La estrategia requiere velas históricas para calentar los indicadores más alto/más bajo y ATR antes de operar.
  • Las órdenes stop se cancelan automáticamente cuando se cierra la posición o se reinicia la estrategia.
using System;

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

namespace StockSharp.Samples.Strategies;

public class TradeChannelAtrStrategy : Strategy
{
	private readonly StrategyParam<int> _channelPeriod;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<int> _cooldownCandles;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevClose;
	private decimal _prevMid;
	private bool _hasPrev;
	private int _cooldownRemaining;

	public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
	public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
	public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public TradeChannelAtrStrategy()
	{
		_channelPeriod = Param(nameof(ChannelPeriod), 48).SetDisplay("Channel Period", "Channel lookback", "Indicators");
		_atrPeriod = Param(nameof(AtrPeriod), 14).SetDisplay("ATR Period", "ATR lookback", "Indicators");
		_cooldownCandles = Param(nameof(CooldownCandles), 150).SetDisplay("Cooldown", "Candles between signals", "General");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClose = default;
		_prevMid = default;
		_hasPrev = default;
		_cooldownRemaining = default;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevClose = 0;
		_prevMid = 0;
		_hasPrev = false;
		_cooldownRemaining = 0;

		var highest = new Highest { Length = ChannelPeriod };
		var lowest = new Lowest { Length = ChannelPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(highest, lowest, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal highest, decimal lowest)
	{
		if (candle.State != CandleStates.Finished) return;
		var close = candle.ClosePrice;
		var mid = (highest + lowest) / 2;
		if (!_hasPrev) { _prevClose = close; _prevMid = mid; _hasPrev = true; return; }

		if (_cooldownRemaining > 0)
		{
			_cooldownRemaining--;
			_prevClose = close;
			_prevMid = mid;
			return;
		}

		if (_prevClose <= _prevMid && close > mid && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
			_cooldownRemaining = CooldownCandles;
		}
		else if (_prevClose >= _prevMid && close < mid && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
			_cooldownRemaining = CooldownCandles;
		}
		_prevClose = close;
		_prevMid = mid;
	}
}