Ver en GitHub

Estrategia de Cobertura de Cualquier Posición

Descripción general

La Estrategia de Cobertura de Cualquier Posición es una conversión directa del expert MQL5 original Hedge any positions (barabashkakvn's edition). La versión de StockSharp mantiene la idea central intacta: monitorea cada pata abierta creada por la estrategia y, una vez que una pata pierde un número definido de pips, inmediatamente abre una posición opuesta con un tamaño de lote amplificado. La implementación se basa en la API de alto nivel de StockSharp, por lo que las órdenes de hedge se colocan mediante órdenes de mercado y el seguimiento de posiciones se maneja internamente sin código de enrutamiento de órdenes personalizado.

La estrategia puede opcionalmente colocar un trade inicial cuando comienza. Después simplemente reacciona a movimientos adversos de precio y construye una escalera de trades de cobertura, marcando cada pata como cubierta para que la misma posición no pueda desencadenar múltiples entradas opuestas.

Flujo de trabajo de cobertura

  1. Feed de velas – un CandleType configurable impulsa la estrategia. Solo se procesan velas terminadas.
  2. Cálculo de pérdidas – en cada cierre de vela la estrategia verifica si el precio de cierre se movió contra cualquier pata abierta al menos LosingPips multiplicado por el tamaño de pip calculado.
  3. Ejecución del hedge – si se encuentra una pata perdedora, se envía una orden de mercado en la dirección opuesta. El volumen de la orden equivale al volumen de la pata original multiplicado por LotCoefficient, redondeado al paso de volumen del instrumento y limitado al volumen mínimo/máximo permitido.
  4. Actualización de estado – una vez que se despacha una orden opuesta, la pata original se marca como cubierta y el trade recién abierto se almacena como una nueva pata que a su vez puede ser cubierta más tarde si el precio se revierte nuevamente.

Parámetros

Parámetro Descripción Predeterminado
CandleType Marco temporal usado para evaluar los movimientos de precio y desencadenar coberturas. Velas de 1 minuto
LosingPips Número de pips que el precio debe moverse contra una pata antes de abrir una cobertura. 5
LotCoefficient Multiplicador aplicado al volumen original al enviar la orden de cobertura. 2.0
AutoPlaceInitialTrade Cuando está habilitado la estrategia envía el primer trade automáticamente al iniciar. Deshabilitado
InitialVolume Tamaño de la orden usado por el trade inicial opcional. Redondeado al paso de volumen del instrumento. 0.10
InitialDirection Lado (compra o venta) usado para el trade inicial opcional. Compra

Nota: Establecer la propiedad Strategy.Volume al tamaño base de orden que se desea que use la estrategia. Los parámetros anteriores solo controlan el comportamiento específico de cobertura.

Pautas de uso

  1. Asignar un Security, Portfolio y Volume base deseado antes de iniciar la estrategia.
  2. Ajustar LosingPips y LotCoefficient para reflejar la volatilidad y la tolerancia al riesgo del instrumento seleccionado.
  3. Habilitar AutoPlaceInitialTrade si se desea que la versión de StockSharp cree la primera posición automáticamente; de lo contrario, abrir manualmente una pata inicial o dejar que otro componente lo haga.
  4. Debido a que la API de alto nivel de StockSharp trabaja con posiciones netas, la lista de patas interna se usa para emular la estructura de hedge. Monitorear la exposición de la cuenta al ejecutar en cuentas de netting.
  5. Revisar los reportes de ejecución: cada cobertura se coloca con una orden de mercado (BuyMarket o SellMarket).

Diferencias del expert original

  • La validación de margen, las comprobaciones de slippage y el registro detallado de resultados fueron eliminados; StockSharp ya reporta los problemas de ejecución a través de los eventos de estrategia.
  • La conversión usa velas terminadas en lugar de datos tick a tick. Elegir un marco temporal suficientemente pequeño si se necesitan tiempos de reacción más rápidos.
  • El redondeo de lotes ahora se basa en Security.VolumeStep, Security.MinVolume y Security.MaxVolume para mantenerse en conformidad con las reglas de trading del instrumento.
  • Las alertas, notificaciones y el trade inicial aleatorio solo para el tester de la versión MQL fueron intencionalmente omitidos. El parámetro de entrada automática opcional reemplaza ese comportamiento.

Mejoras recomendadas

  • Combinar el módulo de cobertura con una estrategia de entrada separada que define cuándo debe crearse la primera posición.
  • Agregar reglas de cierre basadas en capital o límites de profundidad máxima para prevenir cadenas de cobertura sin límite.
  • Integrar monitoreo a nivel de portafolio para asegurar que los requerimientos de margen se mantengan dentro de límites aceptables.
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;

/// <summary>
/// Hedge Any Positions strategy using ATR-based mean reversion.
/// Enters long when price drops below EMA by ATR threshold, enters short on rally above.
/// Uses stop-loss and take-profit for risk management.
/// </summary>
public class HedgeAnyPositionsStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _atrMultiplier;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _ema;
	private AverageTrueRange _atr;

	private decimal _entryPrice;
	private int _cooldown;

	/// <summary>
	/// EMA period.
	/// </summary>
	public int EmaPeriod
	{
		get => _emaPeriod.Value;
		set => _emaPeriod.Value = value;
	}

	/// <summary>
	/// ATR period.
	/// </summary>
	public int AtrPeriod
	{
		get => _atrPeriod.Value;
		set => _atrPeriod.Value = value;
	}

	/// <summary>
	/// Multiplier applied to ATR for entry threshold.
	/// </summary>
	public decimal AtrMultiplier
	{
		get => _atrMultiplier.Value;
		set => _atrMultiplier.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in price steps.
	/// </summary>
	public int StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Take-profit distance in price steps.
	/// </summary>
	public int TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="HedgeAnyPositionsStrategy"/> class.
	/// </summary>
	public HedgeAnyPositionsStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 100)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA period for mean price", "Indicator");

		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("ATR Period", "ATR calculation period", "Indicator");

		_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
			.SetGreaterThanZero()
			.SetDisplay("ATR Multiplier", "Multiplier for entry distance", "Indicator");

		_stopLossPoints = Param(nameof(StopLossPoints), 200)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Stop-loss distance in price steps", "Risk");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 300)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Take-profit distance in price steps", "Risk");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

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

		_ema = null;
		_atr = null;
		_entryPrice = 0;
		_cooldown = 0;
	}

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

		_ema = new ExponentialMovingAverage { Length = EmaPeriod };
		_atr = new AverageTrueRange { Length = AtrPeriod };

		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_ema, _atr, ProcessCandle);
		subscription.Start();
	}

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

		if (!_ema.IsFormed || !_atr.IsFormed)
			return;

		if (_cooldown > 0)
		{
			_cooldown--;
			return;
		}

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;
		var threshold = atrValue * AtrMultiplier;

		// Check SL/TP
		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 80;
				return;
			}

			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 80;
				return;
			}
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 80;
				return;
			}

			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 80;
				return;
			}
		}

		// Mean reversion: buy when price drops below EMA by threshold
		if (close < emaValue - threshold && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

			BuyMarket();
			_entryPrice = close;
			_cooldown = 80;
		}
		// Mean reversion: sell when price rallies above EMA by threshold
		else if (close > emaValue + threshold && Position >= 0)
		{
			if (Position > 0)
				SellMarket();

			SellMarket();
			_entryPrice = close;
			_cooldown = 80;
		}
	}
}