Ver en GitHub

Estrategia Eugene de Rompimiento Interno

La estrategia Eugene de Rompimiento Interno es un puerto directo del asesor experto original de MetaTrader por barabashkakvn. Se centra en la acción del precio pura: una secuencia de velas internas seguida de un rompimiento de rango. Los niveles de confirmación derivados del cuerpo de la vela anterior aseguran que el rompimiento desarrolle momentum antes de que la estrategia tome una posición.

Descripción general

La estrategia vigila un nuevo máximo o mínimo en relación con la vela anterior. Las configuraciones largas requieren que la vela anterior tenga un mínimo por debajo del máximo de la vela anterior a ella, destacando la compresión antes del rompimiento. Las configuraciones cortas se niegan a operar si la vela anterior es una barra interna, reflejando las salvaguardas en la lógica MQL fuente. Las órdenes siempre se ejecutan al mercado con un volumen fijo.

Lógica de mercado

  • Enfatiza los rompimientos del máximo/mínimo más reciente para captar movimientos direccionales temprano.
  • Usa el cuerpo de la vela anterior para calcular dos niveles de retroceso de un tercio (zigLevelBuy y zigLevelSell). El precio debe tocar estos niveles, o la sesión debe estar pasada la hora de activación configurada, antes de que se permita una entrada.
  • Previene nuevas posiciones cuando un rompimiento coincide con una vela interna contra la dirección de la operación.
  • Cierra posiciones abiertas siempre que se confirme la señal de rompimiento opuesta, asegurando que la estrategia siempre esté plana o alineada con la última señal.

Reglas de entrada

Largo

  1. El máximo de la vela actual es mayor que el máximo de la vela anterior.
  2. La confirmación se recibe cuando el mínimo actual perfora el retroceso de un tercio del cuerpo de la vela anterior, o la hora actual está más allá del parámetro de hora de activación.
  3. El mínimo actual debe mantenerse por encima del mínimo anterior mientras el mínimo anterior se sitúa por debajo del máximo de hace dos velas.
  4. No hay ninguna posición abierta.

Corto

  1. El mínimo de la vela actual es menor que el mínimo de la vela anterior.
  2. La confirmación se recibe cuando el máximo actual prueba el retroceso superior de un tercio del cuerpo de la vela anterior, o la hora actual está más allá del parámetro de hora de activación.
  3. La vela anterior no debe ser una barra interna.
  4. El máximo actual debe estar por debajo del máximo anterior.
  5. No hay ninguna posición abierta.

Reglas de salida

  • Cerrar posiciones largas cuando se forma un rompimiento corto validado (condiciones 1–3 de la lógica de entrada corta).
  • Cerrar posiciones cortas cuando se forma un rompimiento largo validado (condiciones 1–3 de la lógica de entrada larga).

Parámetros

Nombre Descripción Predeterminado
CandleType Marco temporal de las velas procesadas por la estrategia. Velas de 1 hora
Volume Tamaño de la orden enviada con cada orden de mercado. 0.1
ActivationHour Hora del día después de la cual las confirmaciones se aceptan automáticamente, replicando el filtro TimeCurrent() del código MQL. 8

Notas

  • Las verificaciones de confirmación etiquetadas "white bird" y "black bird" en el script original siempre se evalúan como falsas debido a las condiciones fuente; se conservan para paridad pero no afectan las decisiones de trading.
  • No se utilizan indicadores adicionales ni trailing stops—el enfoque es puramente basado en precios y voltea posiciones en cada rompimiento opuesto.
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>
/// Breakout strategy derived from the Eugene expert advisor.
/// </summary>
public class EugeneInsideBreakoutStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _activationHour;

	private decimal _prevOpen1;
	private decimal _prevClose1;
	private decimal _prevHigh1;
	private decimal _prevLow1;

	private decimal _prevOpen2;
	private decimal _prevClose2;
	private decimal _prevHigh2;
	private decimal _prevLow2;

	private bool _hasPrev1;
	private bool _hasPrev2;

	/// <summary>
	/// Candle type to process.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}


	/// <summary>
	/// Hour of day after which confirmations are automatically valid.
	/// </summary>
	public int ActivationHour
	{
		get => _activationHour.Value;
		set => _activationHour.Value = value;
	}

	/// <summary>
	/// Initializes <see cref="EugeneInsideBreakoutStrategy"/>.
	/// </summary>
	public EugeneInsideBreakoutStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to process", "General");


		_activationHour = Param(nameof(ActivationHour), 8)
			.SetRange(0, 23)
			.SetDisplay("Activation Hour", "Hour when confirmations become unconditional", "Filters");

		ResetHistory();
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		ResetHistory();
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_hasPrev2)
		{
			UpdateHistory(candle);
			return;
		}

		var open1 = _prevOpen1;
		var close1 = _prevClose1;
		var open2 = _prevOpen2;
		var close2 = _prevClose2;
		var high0 = candle.HighPrice;
		var high1 = _prevHigh1;
		var high2 = _prevHigh2;
		var low0 = candle.LowPrice;
		var low1 = _prevLow1;
		var low2 = _prevLow2;

		// Replicate the original expert advisor checks for inside bars.
		var blackInsider = high1 <= high2 && low1 >= low2 && close1 <= open1;
		var whiteInsider = high1 <= high2 && low1 >= low2 && close1 > open1;
		var whiteBird = whiteInsider && close2 > open2;
		var blackBird = blackInsider && close2 < open2;

		// ZigZag style confirmation levels based on the previous candle body.
		var zigLevelBuy = close1 < open1
			? open1 - (close1 - open1) / 3m
			: open1 - (open1 - low1) / 3m;

		var zigLevelSell = close1 > open1
			? open1 + (close1 - open1) / 3m
			: open1 + (high1 - open1) / 3m;

		var confirmBuy = (low0 <= zigLevelBuy || candle.CloseTime.Hour >= ActivationHour) && !blackBird && !whiteInsider;
		var confirmSell = (high0 >= zigLevelSell || candle.CloseTime.Hour >= ActivationHour) && !whiteBird && !blackInsider;

		var buySignal = high0 > high1;
		var sellSignal = low0 < low1;

		if (Position == 0)
		{
			if (buySignal && confirmBuy && low0 > low1 && low1 < high2)
			{
				BuyMarket();
			}
			else if (sellSignal && confirmSell && high0 < high1)
			{
				SellMarket();
			}
		}
		else if (Position > 0)
		{
			if (sellSignal && confirmSell && high0 < high1)
				SellMarket();
		}
		else if (Position < 0)
		{
			if (buySignal && confirmBuy && low0 > low1 && low1 < high2)
				BuyMarket();
		}

		UpdateHistory(candle);
	}

	private void UpdateHistory(ICandleMessage candle)
	{
		// Keep the two most recent completed candles for decision making.
		_prevOpen2 = _prevOpen1;
		_prevClose2 = _prevClose1;
		_prevHigh2 = _prevHigh1;
		_prevLow2 = _prevLow1;
		_hasPrev2 = _hasPrev1;

		_prevOpen1 = candle.OpenPrice;
		_prevClose1 = candle.ClosePrice;
		_prevHigh1 = candle.HighPrice;
		_prevLow1 = candle.LowPrice;
		_hasPrev1 = true;
	}

	private void ResetHistory()
	{
		_prevOpen1 = default;
		_prevClose1 = default;
		_prevHigh1 = default;
		_prevLow1 = default;
		_prevOpen2 = default;
		_prevClose2 = default;
		_prevHigh2 = default;
		_prevLow2 = default;
		_hasPrev1 = false;
		_hasPrev2 = false;
	}
}