Ver en GitHub

Estrategia de Three Breaky

La Estrategia de Three Breaky es una conversión completa del experto asesor MetaTrader 4 ThreeBreaky_v1.mq4. La versión de StockSharp conserva el trío original de subsistemas de ruptura, traduce su lógica basada en velas a la API de alto nivel y añade una gestión clara de posiciones para cada módulo. La estrategia trabaja en un único marco temporal configurable y puede habilitar o deshabilitar cualquier subsistema sin afectar a los otros.

Módulos de trading

  1. Sistema 1 – Ruptura de expansión ATR

    • Usa solo la vela anterior.
    • Va largo cuando la vela anterior es alcista y su rango alto-bajo supera cuatro veces el rango verdadero promedio de 72 períodos.
    • Va corto cuando la vela anterior es bajista y se cumple la misma condición de rango.
  2. Sistema 2 – Cambio de nube Ichimoku

    • Observa los límites de la nube (Senkou Span A y Senkou Span B) con períodos predeterminados 9/26/52.
    • Una señal larga se activa cuando hace dos velas el cierre fue por debajo de ambos spans y el último cerró por encima de ambos (un cambio alcista a través de la nube).
    • Una señal corta se activa cuando hace dos velas el cierre fue por encima de ambos spans y el último cerró por debajo de ambos.
  3. Sistema 3 – Ruptura de cuerpo excepcional

    • Registra el tamaño del cuerpo de las últimas 20 velas completadas.
    • Una configuración larga requiere que la vela anterior sea alcista y su cuerpo sea más de tres veces el cuerpo máximo observado en esa historia de 20 velas.
    • Una configuración corta refleja la condición para cuerpos bajistas.

Cada subsistema opera una posición virtual dedicada. Las marcas de tiempo de las órdenes se almacenan para garantizar que un módulo pueda abrir como máximo una operación por vela, igual que la lógica original buyTag y sellTag.

Lógica de salida

  • Reversión de SAR Parabólico: Todas las posiciones abiertas comparten una salida de SAR Parabólico (0.005/0.2). Cuando el precio cruza el SAR entre las dos últimas velas, la posición afectada se cierra.
  • Gestión de riesgo: Las distancias opcionales de stop-loss y take-profit (en pips) se evalúan en cada vela completada. Si se superan los umbrales configurados, la posición relevante se cierra inmediatamente.

Indicadores utilizados

  • Rango verdadero promedio (período 72) para la línea base de volatilidad promedio.
  • Ichimoku Kinko Hyo (9, 26, 52) para el filtro de cambio de nube.
  • SAR Parabólico (aceleración 0.005, máximo 0.2) para salidas y lógica de trailing.
  • Buffer de tamaño de cuerpo rodante (20 velas) para reproducir la comparación de cuerpo máximo MQL.

Parámetros

Parámetro Descripción
UseSystem1 Habilita el módulo de ruptura de expansión ATR.
UseSystem2 Habilita el módulo de cambio de nube Ichimoku.
UseSystem3 Habilita el módulo de ruptura de cuerpo grande.
OrderVolume Volumen usado para cada orden de mercado generada por cualquier módulo.
StopLossPips Distancia protectora del stop en pips. Establecer en cero para deshabilitar.
TakeProfitPips Distancia del take-profit en pips. Establecer en cero para deshabilitar.
CandleType Marco temporal para las velas de trabajo (predeterminado 1 hora).

Resumen del flujo de trabajo

  1. Suscribirse a la serie de velas configurada y procesar solo las velas finalizadas.
  2. Actualizar los indicadores ATR, Ichimoku y SAR Parabólico junto con el historial de cuerpos rodante.
  3. Cerrar posiciones que alcancen stops, objetivos o reversiones de SAR Parabólico.
  4. Si el trading está permitido, evaluar cada subsistema de forma independiente y emitir órdenes de mercado cuando se cumplan todas las condiciones respectivas.
  5. Almacenar las últimas salidas de los indicadores para que la siguiente vela pueda acceder a los mismos valores históricos que en la implementación MQL original.

Notas

  • La estrategia asume un valor de pip basado en el paso de precio del instrumento; las cotizaciones FX de cinco y tres dígitos se normalizan a tamaños de pip de cuatro y dos decimales respectivamente.
  • Los subsistemas pueden ejecutarse simultáneamente. Cada uno mantiene su propio precio de entrada, dirección de posición y últimas marcas de tiempo de señal para reflejar la separación MagicNumber+N del EA fuente.
  • La implementación de StockSharp retiene el patrón de ejecución "una vez por barra" usando los tiempos de apertura de velas para bloquear órdenes duplicadas dentro de una sola barra.
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 ThreeBreakyStrategy : 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 ThreeBreakyStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 15).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 60).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;
	}
}