Ver en GitHub

Estrategia de canal equidistante

Descripción general

La estrategia de canal equidistante adapta el asesor experto MQL4 original "Equidistant Channel" a la API de alto nivel de StockSharp. La estrategia analiza cruces de la línea MACD y gestiona posiciones existentes mediante toques de Bollinger Bands, lógica de breakeven y objetivos trailing basados en dinero.

Cuando la línea MACD cruza por encima de su señal, la estrategia abre posiciones largas; cuando cruza por debajo de la señal, abre posiciones cortas. Mientras una operación está activa, la estrategia vigila salidas cuando el precio alcanza Bollinger Bands, cuando la ganancia flotante llega a objetivos monetarios o porcentuales configurables, o cuando se infringe un umbral de drawdown trailing. Un modo breakeven replica la implementación de MetaTrader moviendo el stop de protección cuando la ganancia supera un número configurable de pasos de precio.

Indicadores

  • MACD (12, 26, 9) - genera señales de entrada en cruces entre la línea MACD y su línea de señal.
  • Bollinger Bands (20, 2) - proporcionan niveles de salida cuando el cierre de la vela toca la banda superior o inferior.

Gestión de posiciones

  • Distancias opcionales de stop loss, take profit y trailing stop expresadas en puntos de precio mediante StartProtection.
  • Lógica de take profit y trailing basada en dinero que rastrea la ganancia flotante usando metadatos de precio/tamaño de paso del instrumento.
  • Take profit porcentual calculado desde el valor inicial de la cartera.
  • Modo breakeven que empuja el stop a la entrada más un desplazamiento cuando la ganancia alcanza un disparador definido.

Parámetros

Grupo Nombre Predeterminado Descripción
Trading Volumen 1 Volumen de orden para nuevas entradas.
General Tipo de vela 5 minutos Serie de velas usada para los cálculos.
Indicadores MACD rápido 12 Longitud de EMA rápida para MACD.
Indicadores MACD lento 26 Longitud de EMA lenta para MACD.
Indicadores Señal MACD 9 Longitud de la línea de señal para MACD.
Indicadores Período BB 20 Período retrospectivo de Bollinger Bands.
Indicadores Desviación BB 2 Anchura de Bollinger Bands en desviaciones estándar.
Riesgo Stop Loss 20 Distancia del stop loss en puntos de precio.
Riesgo Take Profit 50 Distancia del take profit en puntos de precio.
Riesgo Trailing Stop 40 Distancia del trailing stop en puntos de precio.
Riesgo Usar TP (dinero) false Cierra cuando la ganancia flotante alcanza un objetivo monetario absoluto.
Riesgo Dinero TP 10 Valor absoluto de take profit en la divisa de la cuenta.
Riesgo Usar TP (%) false Cierra cuando la ganancia flotante alcanza un porcentaje del capital inicial.
Riesgo Porcentaje TP 10 Porcentaje del capital inicial para el take profit porcentual.
Riesgo Habilitar trailing true Habilita la lógica trailing sobre la ganancia flotante.
Riesgo Activar trailing 40 Nivel de ganancia (divisa) que arma la lógica trailing.
Riesgo Paso trailing 10 Drawdown máximo permitido desde el pico de ganancia (divisa).
Riesgo Usar stop BB true Habilita salidas cuando el precio toca Bollinger Bands.
Riesgo Usar breakeven true Habilita el comportamiento breakeven.
Riesgo Disparador breakeven 10 Ganancia (pasos de precio) necesaria para armar el stop breakeven.
Riesgo Desplazamiento breakeven 5 Desplazamiento (pasos de precio) aplicado al nivel breakeven.

Notas

  • La estrategia funciona con un solo instrumento que proporcione metadatos válidos de PriceStep y StepPrice para que los cálculos monetarios sean precisos.
  • El módulo de trailing de ganancia sigue el comportamiento de MetaTrader: cuando la ganancia flotante supera el umbral de activación, la estrategia registra el máximo en curso y cierra la operación cuando el drawdown supera el paso trailing configurado.
  • La lógica breakeven replica el EA original usando disparadores y desplazamientos basados en pasos de precio.
  • Todos los comentarios dentro del código de la estrategia están escritos en inglés, como exigen las directrices del proyecto.
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

public class EquidistantChannelStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public EquidistantChannelStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}