Ver en GitHub

Una estrategia de campeonato del sistema

Descripción general

  • Puerto del MetaTrader 4 asesor experto "Un sistema: edición final de la estrategia del campeonato" (archivo ACB6.MQ4).
  • Detecta rupturas alcistas o bajistas en un período de tiempo primario configurable y confirma el impulso con precios de oferta y demanda en vivo.
  • Utiliza un marco de tiempo secundario para dimensionar la distancia del trailing stop, reproduciendo la lógica multiproceso del EA original a través de dos flujos de velas.
  • Implementa los bloques de parada de acciones globales, pausa comercial y tamaño de riesgo adaptable que estaban codificados en el robot de origen.

Suscripciones de datos

  • Se suscribe a dos series de velas (PrimaryTimeFrame, SecondaryTimeFrame) para reconstruir los rangos de precios utilizados para objetivos y topes dinámicos.
  • Suscríbase a cotizaciones de nivel 1 para leer la mejor oferta/demanda que activan entradas, comprobaciones de stop-loss, toma de ganancias y salida de retroceso.

Condiciones de entrada

  1. Espere a que termine la vela principal y calcule su rango multiplicado por TakeFactor.
  2. Vaya largo cuando:
    • La vela cierra por encima de su punto medio.
    • El precio de venta actual rompe el máximo de la vela.
    • La distancia entre la oferta y el mínimo de la vela supera MinStopDistance.
  3. Vaya en corto cuando las condiciones reflejadas sean ciertas para la ruptura a la baja.
  4. Omita las entradas si la distancia de obtención de beneficios calculada es menor que el espacio mínimo entre paradas.

Gestión de salidas

  • Niveles de protección iniciales: el stop está anclado al mínimo/máximo de la vela anterior, mientras que la toma de ganancias es igual al precio de entrada más/menos el rango multiplicado por TakeFactor.
  • Salida de retroceso (FallLimit/FallFactor):
    • Realice un seguimiento de la excursión máxima favorable para la posición activa.
    • Si el movimiento actual cae por debajo de FallLimit * maxMove y el movimiento ya avanzó más allá de FallFactor * target, cierre la operación en el mercado.
  • Parada móvil (TrailFactor):
    • La distancia final es igual al rango del período de tiempo secundario multiplicado por TrailFactor.
    • El stop solo se mueve en la dirección comercial y nunca cruza la toma de ganancias o el espacio mínimo entre stop.
  • Paradas duras: el precio que toca los niveles de parada o toma mantenidos da como resultado una liquidación inmediata utilizando órdenes de mercado.

Gestión de riesgos

  • Tamaño de posición dinámico: combina RiskPerTrade con el valor de pip derivado de Security.StepSize y Security.StepPrice. El volumen resultante se redondea según las restricciones de intercambio y nunca baja de BaseVolume.
  • Ajuste estadístico: el ratio LossesExpected/TradesExpected del EA original modula el riesgo por operación comparándolo con el ratio de pérdida realizada.
  • Parada de equidad (SystemStop): rastrea el pico de equidad y deshabilita nuevas operaciones si el valor actual cae por debajo de SystemStop * peak. Los registros informativos marcan la parada de activación y recuperación.
  • Pausa comercial (TradePause): aplica una ventana de enfriamiento después de cada orden de mercado, al igual que la implementación de MT4.

Parámetros

Nombre Predeterminado Descripción
PrimaryTimeFrame 1 dia Se utiliza un plazo más alto para la detección de fugas.
SecondaryTimeFrame 4 horas Marco temporal que proporciona el rango del trailing stop.
TakeFactor 0,8 Multiplicador aplicado al rango de velas principal al crear la toma de ganancias.
TrailFactor 10 Multiplicador aplicado al rango de velas secundarias al actualizar el trailing stop.
FallLimit 0,5 Relación del beneficio máximo que permite la salida del retroceso.
FallFactor 0,4 Participación mínima del objetivo total que debe alcanzarse antes de que se permita una salida de retroceso.
RiskPerTrade 0,02 Fracción del capital asignado a cada operación antes de ajustes.
BaseVolume 1 El tamaño de la orden alternativa se utiliza cuando el tamaño del riesgo produce un volumen menor.
MinStopDistance 0 Restricción de distancia de parada de cambio expresada en unidades de precio.
TradePause 5 minutos Período de espera después de cualquier orden ejecutada.
SystemStop 0,8 Factor de reducción para el stop de acciones de la cartera (por ejemplo, 0,8 = reducción permitida del 20 %).
LossesExpected 20 Número esperado de operaciones perdedoras para el ajuste de riesgo.
TradesExpected 85 Número esperado de operaciones totales para el ajuste de riesgo.

Notas de implementación

  • Los hilos de bloqueo/cobertura de la versión MQL se omiten porque las estrategias StockSharp operan en una posición neta. El control de riesgos y la lógica de seguimiento proporcionan un mecanismo de protección del capital equivalente.
  • Los niveles de detener y tomar se rastrean dentro de la estrategia en lugar de utilizar órdenes nativas separadas para mantenerse alineados con el motor de backtesting.
  • Asegúrese de que la seguridad conectada exponga StepSize, StepPrice, MinVolume y VolumeStep; de lo contrario, el tamaño vuelve a ser BaseVolume.
  • La estrategia debe ejecutarse con cotizaciones en tiempo real habilitadas; de lo contrario, solo se ejecutarán las actualizaciones impulsadas por velas y la lógica de parada reaccionará con la latencia de la vela.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Multi-timeframe breakout strategy converted from the original MetaTrader "A System" expert advisor.
/// Enters on momentum breakouts when close is above/below the midpoint of the previous candle range.
/// Uses ATR-based trailing stop for position management.
/// </summary>
public class ASystemChampionshipStrategy : 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 bool _hasPrev;
	private decimal _entryPrice;
	private decimal _stopPrice;

	public ASystemChampionshipStrategy()
	{
		_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 used in trailing stop.", "Indicators");

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

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

	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;
		_hasPrev = false;
		_entryPrice = 0;
		_stopPrice = 0;
	}

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

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

		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)
		{
			SaveCandle(candle);
			return;
		}

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

			if (candle.LowPrice <= _stopPrice)
			{
				SellMarket();
				ResetPosition();
			}
			else if (_entryPrice > 0 && candle.HighPrice >= _entryPrice + atrValue * TakeFactor)
			{
				SellMarket();
				ResetPosition();
			}
		}
		else if (Position < 0)
		{
			// Trail stop down
			var newStop = candle.ClosePrice + atrValue * TrailFactor;
			if (_stopPrice == 0 || newStop < _stopPrice)
				_stopPrice = newStop;

			if (candle.HighPrice >= _stopPrice)
			{
				BuyMarket();
				ResetPosition();
			}
			else if (_entryPrice > 0 && candle.LowPrice <= _entryPrice - atrValue * TakeFactor)
			{
				BuyMarket();
				ResetPosition();
			}
		}

		// Entry logic
		if (_hasPrev && Position == 0)
		{
			var mid = (_prevHigh + _prevLow) / 2m;

			if (_prevClose > mid && candle.ClosePrice > _prevHigh)
			{
				// Bullish breakout
				BuyMarket();
				_entryPrice = candle.ClosePrice;
				_stopPrice = candle.ClosePrice - atrValue * TrailFactor;
			}
			else if (_prevClose < mid && candle.ClosePrice < _prevLow)
			{
				// Bearish breakout
				SellMarket();
				_entryPrice = candle.ClosePrice;
				_stopPrice = candle.ClosePrice + atrValue * TrailFactor;
			}
		}

		SaveCandle(candle);
	}

	private void SaveCandle(ICandleMessage candle)
	{
		_prevHigh = candle.HighPrice;
		_prevLow = candle.LowPrice;
		_prevClose = candle.ClosePrice;
		_hasPrev = true;
	}

	private void ResetPosition()
	{
		_entryPrice = 0;
		_stopPrice = 0;
	}
}