Ver en GitHub

Estrategia de ruptura de consolidación

Esta estrategia reproduce el comportamiento central del asesor experto original Consolidation Breakout para MetaTrader. Busca consolidaciones estrechas confirmadas por filtros de momentum y MACD, y luego abre una posición en la dirección de la ruptura. El riesgo se gestiona mediante distancias fijas de take-profit y stop-loss medidas en pasos de precio (pips).

Funcionamiento

  1. El marco temporal principal se define mediante el parámetro CandleType. Todas las comprobaciones de tendencia y consolidación se evalúan en estas velas.
  2. Dos medias móviles ponderadas lineales (LWMAs), calculadas sobre el precio típico, proporcionan el filtro direccional. Las configuraciones largas requieren que la LWMA rápida permanezca por encima de la LWMA lenta, mientras que las cortas necesitan la alineación opuesta.
  3. Se detecta una consolidación cuando el mínimo de la vela de hace dos barras permanece por debajo del máximo de la vela anterior (caso largo), o cuando el mínimo anterior queda por debajo del máximo de hace dos barras (caso corto). Esto refleja la lógica de barras solapadas de la versión MQL.
  4. El momentum debe confirmar el movimiento. El valor absoluto de momentum (relativo a cero) debe superar el umbral de compra o venta correspondiente. Esto aproxima el filtro de momentum del experto original alrededor del nivel 100.
  5. Un MACD separado calculado en el marco temporal MacdCandleType debe coincidir con la dirección de la operación. La estrategia comprueba si la línea MACD lidera a la línea de señal tanto en el lado positivo como en el negativo del eje, reproduciendo la confirmación multitemporal del código fuente.
  6. Cuando todos los filtros se alinean y la cuenta está plana o posicionada en la dirección opuesta, la estrategia envía una orden de mercado dimensionada por TradeVolume. Los niveles de protección se recalculan inmediatamente en pasos de precio para que los extremos intrabar puedan activar salidas.
  7. Cada vela finalizada también supervisa posiciones activas. Si el rango de la vela toca el nivel de stop-loss o take-profit, la estrategia cierra la posición a mercado y reinicia los objetivos de protección.

Indicadores

  • Media móvil ponderada lineal (rápida y lenta, precio típico)
  • Momentum
  • MACD (con períodos 12/26/9 en un marco temporal superior)

Parámetros

  • CandleType - marco temporal principal usado para detectar rupturas.
  • MacdCandleType - marco temporal usado para el filtro MACD de confirmación.
  • FastMaPeriod - longitud de la LWMA rápida.
  • SlowMaPeriod - longitud de la LWMA lenta.
  • MomentumLength - período retrospectivo del filtro de momentum.
  • MomentumBuyThreshold - momentum positivo mínimo requerido para operaciones largas.
  • MomentumSellThreshold - momentum negativo mínimo requerido para operaciones cortas (expresado como valor absoluto).
  • StopLossPips - distancia del stop de protección en pasos de precio.
  • TakeProfitPips - distancia del objetivo de ganancia en pasos de precio.
  • TradeVolume - volumen enviado con cada orden de mercado.

Los valores predeterminados reflejan el asesor experto publicado: períodos LWMA de 6 y 85, momentum length 14, umbrales de compra/venta de 0.3, stop-loss de 20 pips y take-profit de 50 pips. Ajuste las distancias basadas en pips al operar instrumentos con pasos de precio diferentes.

Notas

  • Los trailing stops, movimientos a break-even y módulos de gestión monetaria del script MQL se omiten intencionalmente para mantener la adaptación StockSharp centrada en la lógica principal de ruptura.
  • Asegúrese siempre de que los marcos temporales seleccionados sean compatibles con su proveedor de datos. Si el marco temporal superior produce datos escasos, considere cambiar a un MacdCandleType menor para mantener el filtro MACD reactivo.
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 ConsolidationBreakoutStrategy : 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 ConsolidationBreakoutStrategy()
	{
		_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;
	}
}