Ver en GitHub

Estrategia ACB1

Descripción general

La Estrategia ACB1 es el puerto StockSharp del asesor experto MetaTrader distribuido como MQL/8586/ACB1.MQ4. El sistema original opera con el par EURUSD y espera fuertes rupturas diarias antes de ingresar al mercado. Esta conversión reproduce el mismo proceso de decisión con StockSharp primitivas de alto nivel:

  • Las velas diarias (SignalCandleType) definen la dirección de ruptura y proporcionan los anclajes de parada y toma de ganancias.
  • Las velas H4 (TrailCandleType) determinan la distancia de seguimiento que se multiplica por TrailFactor.
  • Las órdenes se ejecutan en el mercado una vez que se cumplen las condiciones de ruptura y la estrategia mantiene solo una posición neta, reflejando las comprobaciones OrdersTotal() en el código MQL.
  • El stop-loss y el take-profit se gestionan internamente: la estrategia vigila los mejores precios de oferta y demanda y cierra la posición con órdenes de mercado cuando se superan los niveles de protección virtual.

Reglas comerciales

  1. Configuración larga

    • Utilice la vela diaria terminada anteriormente.
    • Si Close > (High + Low) / 2 y el precio de venta actual está por encima del máximo anterior, abra una posición larga en el mercado.
    • El stop-loss se coloca en el mínimo anterior (redondeado al nivel del precio del instrumento).
    • La toma de ganancias es igual al precio de entrada más (High − Low) × TakeFactor.
  2. Configuración corta

    • Si Close < (High + Low) / 2 y el precio de oferta actual está por debajo del mínimo anterior, abra una posición corta en el mercado.
    • El stop-loss se fija en el máximo anterior; la toma de ganancias resta (High − Low) × TakeFactor del precio de entrada.
  3. Parada de seguimiento

    • Las TrailCandleType velas terminadas más recientes suministran (High − Low) × TrailFactor.
    • Para posiciones largas, el stop sigue a Bid − TrailDistance mientras el precio permanece por debajo del nivel de obtención de beneficios menos el stop del corredor.
    • Para posiciones cortas, el stop sigue Ask + TrailDistance mientras que el precio se mantiene por encima del nivel de toma de ganancias más el stop del corredor.
  4. Guardia de riesgos

    • La estrategia rastrea el capital máximo observado de la cartera. La negociación se detiene cada vez que el capital actual cae por debajo del 50% de ese pico, exactamente como en el asesor original.
    • Un tiempo de reutilización de cinco segundos (CooldownSeconds) evita nuevos pedidos o detiene las actualizaciones con demasiada frecuencia, reproduciendo el acelerador TimeLocal() de MQL.

Dimensionamiento de posiciones y control de riesgos

  • El volumen por operación se deriva de Portfolio.CurrentValue × RiskFraction.
  • El riesgo monetario por contrato se calcula a partir de la distancia de parada y los metadatos de seguridad (PriceStep y StepPrice).
  • El tamaño resultante se alinea con Security.VolumeStep y se fija en [Security.MinVolume, Security.MaxVolume], luego se limita por el parámetro MaxVolume (5 lotes predeterminados).
  • Las órdenes se omiten cuando el volumen normalizado es cero o cuando la distancia de parada viola MinStopDistancePoints, lo que emula la verificación MetaTrader MODE_STOPLEVEL.

Parámetros

Parámetro Predeterminado Descripción
SignalCandleType Diariamente Tipo de vela utilizado para la detección de fugas.
TrailCandleType 4 horas Tipo de vela que suministra la distancia del trailing stop.
TakeFactor 0,8 Multiplicador aplicado al rango diario para calcular la obtención de beneficios.
TrailFactor 10 Multiplicador aplicado al rango final al actualizar la parada.
RiskFraction 0,05 Fracción del capital de la cartera arriesgada en cada operación (5%).
MaxVolume 5 Límite estricto para el volumen final del pedido.
MinStopDistancePoints 0 Distancia mínima de parada/toma expresada en puntos de precio; configúrelo en el corredor MODE_STOPLEVEL.
CooldownSeconds 5 Retraso mínimo entre acciones comerciales consecutivas.

Notas de implementación

  • La estrategia requiere metadatos de instrumentos adecuados: Security.PriceStep, Security.StepPrice, Security.VolumeStep, Security.MinVolume y (si está disponible) Security.MaxVolume.
  • Los niveles de protección son virtuales. StockSharp cierra posiciones mediante órdenes de mercado cuando la oferta/demanda toca el límite de pérdidas o la toma de ganancias calculados.
  • El seguimiento de acciones utiliza Portfolio.CurrentValue. Si el conector no proporciona este campo, la protección contra riesgos mantendrá las operaciones desactivadas hasta que esté disponible.
  • Sólo se mantiene una única posición neta. Las señales opuestas mientras una operación está activa se ignoran hasta que la posición se cierra por completo.
  • No se incluye ningún puerto Python; este directorio solo contiene la implementación y documentación de C#.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Breakout strategy converted from the "ACB1" MetaTrader expert advisor.
/// Enters on breakouts above previous candle high / below previous candle low,
/// with trailing stop based on ATR.
/// </summary>
public class Acb1Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _takeFactor;
	private readonly StrategyParam<decimal> _trailFactor;

	private decimal _prevHigh;
	private decimal _prevLow;
	private decimal _prevClose;
	private decimal _prevMid;
	private decimal _entryPrice;
	private decimal _stopPrice;
	private bool _hasPrev;

	public Acb1Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for breakout detection.", "General");

		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetDisplay("ATR Period", "Period for ATR indicator used in trailing.", "Indicators");

		_takeFactor = Param(nameof(TakeFactor), 2m)
			.SetDisplay("Take Factor", "ATR multiplier for take profit distance.", "Execution");

		_trailFactor = Param(nameof(TrailFactor), 1.5m)
			.SetDisplay("Trail Factor", "ATR multiplier for trailing stop distance.", "Execution");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int AtrPeriod
	{
		get => _atrPeriod.Value;
		set => _atrPeriod.Value = value;
	}

	public decimal TakeFactor
	{
		get => _takeFactor.Value;
		set => _takeFactor.Value = value;
	}

	public decimal TrailFactor
	{
		get => _trailFactor.Value;
		set => _trailFactor.Value = value;
	}

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

		_prevHigh = 0;
		_prevLow = 0;
		_prevClose = 0;
		_prevMid = 0;
		_entryPrice = 0;
		_stopPrice = 0;
		_hasPrev = false;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevHigh = 0;
		_prevLow = 0;
		_prevClose = 0;
		_prevMid = 0;
		_entryPrice = 0;
		_stopPrice = 0;
		_hasPrev = false;

		var atr = new AverageTrueRange { Length = AtrPeriod };

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

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

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

		if (atrValue <= 0)
			return;

		// Manage open position
		if (Position != 0)
		{
			if (Position > 0)
			{
				// Trailing stop for long
				var newStop = candle.ClosePrice - atrValue * TrailFactor;
				if (newStop > _stopPrice)
					_stopPrice = newStop;

				// Check stop hit
				if (candle.LowPrice <= _stopPrice)
				{
					SellMarket();
					_entryPrice = 0;
					_stopPrice = 0;
				}
				// Check take profit
				else if (_entryPrice > 0 && candle.HighPrice >= _entryPrice + atrValue * TakeFactor)
				{
					SellMarket();
					_entryPrice = 0;
					_stopPrice = 0;
				}
			}
			else
			{
				// Trailing stop for short
				var newStop = candle.ClosePrice + atrValue * TrailFactor;
				if (_stopPrice == 0 || newStop < _stopPrice)
					_stopPrice = newStop;

				// Check stop hit
				if (candle.HighPrice >= _stopPrice)
				{
					BuyMarket();
					_entryPrice = 0;
					_stopPrice = 0;
				}
				// Check take profit
				else if (_entryPrice > 0 && candle.LowPrice <= _entryPrice - atrValue * TakeFactor)
				{
					BuyMarket();
					_entryPrice = 0;
					_stopPrice = 0;
				}
			}
		}

		// Entry logic after managing position
		if (_hasPrev && Position == 0)
		{
			if (_prevClose > _prevMid && candle.ClosePrice > _prevHigh)
			{
				// Bullish breakout
				BuyMarket();
				_entryPrice = candle.ClosePrice;
				_stopPrice = _prevLow;
			}
			else if (_prevClose < _prevMid && candle.ClosePrice < _prevLow)
			{
				// Bearish breakout
				SellMarket();
				_entryPrice = candle.ClosePrice;
				_stopPrice = _prevHigh;
			}
		}

		// Store for next candle
		_prevHigh = candle.HighPrice;
		_prevLow = candle.LowPrice;
		_prevClose = candle.ClosePrice;
		_prevMid = (candle.HighPrice + candle.LowPrice) / 2m;
		_hasPrev = true;
	}
}