Ver no GitHub

Estratégia de Cobertura de Qualquer Posição

Visão geral

A Estratégia de Cobertura de Qualquer Posição é uma conversão direta do expert MQL5 original Hedge any positions (barabashkakvn's edition). A versão do StockSharp mantém a ideia central intacta: ela monitora cada perna aberta criada pela estratégia e, uma vez que uma perna perde um número definido de pips, imediatamente abre uma posição oposta com um tamanho de lote amplificado. A implementação se baseia na API de alto nível do StockSharp, portanto as ordens de hedge são colocadas por meio de ordens a mercado e o rastreamento de posições é gerenciado internamente sem código de roteamento de ordens personalizado.

A estratégia pode opcionalmente colocar um trade inicial quando começa. Depois ela simplesmente reage a movimentos adversos de preço e constrói uma escada de trades de cobertura, marcando cada perna como coberta para que a mesma posição não possa desencadear múltiplas entradas opostas.

Fluxo de trabalho de cobertura

  1. Feed de velas – um CandleType configurável impulsiona a estratégia. Apenas velas terminadas são processadas.
  2. Cálculo de perdas – a cada fechamento de vela a estratégia verifica se o preço de fechamento se moveu contra qualquer perna aberta pelo menos LosingPips multiplicado pelo tamanho de pip calculado.
  3. Execução do hedge – se uma perna perdedora for encontrada, uma ordem a mercado na direção oposta é enviada. O volume da ordem equivale ao volume da perna original multiplicado por LotCoefficient, arredondado para o passo de volume do instrumento e limitado ao volume mínimo/máximo permitido.
  4. Atualização de estado – uma vez que uma ordem oposta é despachada, a perna original é marcada como coberta e o trade recém-aberto é armazenado como uma nova perna que pode ser coberta mais tarde se o preço reverter novamente.

Parâmetros

Parâmetro Descrição Padrão
CandleType Período usado para avaliar os movimentos de preço e acionar coberturas. Velas de 1 minuto
LosingPips Número de pips que o preço deve se mover contra uma perna antes de abrir uma cobertura. 5
LotCoefficient Multiplicador aplicado ao volume original ao enviar a ordem de cobertura. 2.0
AutoPlaceInitialTrade Quando habilitado a estratégia envia o primeiro trade automaticamente ao iniciar. Desabilitado
InitialVolume Tamanho da ordem usado pelo trade inicial opcional. Arredondado para o passo de volume do instrumento. 0.10
InitialDirection Lado (compra ou venda) usado para o trade inicial opcional. Compra

Nota: Definir a propriedade Strategy.Volume para o tamanho base de ordem que se deseja que a estratégia use. Os parâmetros acima controlam apenas o comportamento específico de cobertura.

Diretrizes de uso

  1. Atribuir um Security, Portfolio e Volume base desejado antes de iniciar a estratégia.
  2. Ajustar LosingPips e LotCoefficient para refletir a volatilidade e a tolerância ao risco do instrumento selecionado.
  3. Habilitar AutoPlaceInitialTrade se quiser que a versão do StockSharp crie a primeira posição automaticamente; caso contrário, abrir manualmente uma perna inicial ou deixar outro componente fazê-lo.
  4. Como a API de alto nível do StockSharp trabalha com posições líquidas, a lista de pernas interna é usada para emular a estrutura de hedge. Monitorar a exposição da conta ao executar em contas de netting.
  5. Revisar os relatórios de execução: cada cobertura é colocada com uma ordem a mercado (BuyMarket ou SellMarket).

Diferenças do expert original

  • A validação de margem, as verificações de slippage e o registro detalhado de resultados foram removidos; o StockSharp já reporta os problemas de execução por meio dos eventos de estratégia.
  • A conversão usa velas terminadas em vez de dados tick a tick. Escolher um período suficientemente pequeno se forem necessários tempos de reação mais rápidos.
  • O arredondamento de lotes agora se baseia em Security.VolumeStep, Security.MinVolume e Security.MaxVolume para manter a conformidade com as regras de trading do instrumento.
  • Alertas, notificações e o trade inicial aleatório apenas para o testador da versão MQL foram intencionalmente omitidos. O parâmetro de entrada automática opcional substitui esse comportamento.

Aprimoramentos recomendados

  • Combinar o módulo de cobertura com uma estratégia de entrada separada que define quando a primeira posição deve ser criada.
  • Adicionar regras de encerramento baseadas em capital ou limites de profundidade máxima para evitar cadeias de cobertura ilimitadas.
  • Integrar monitoramento no nível do portfólio para garantir que os requisitos de margem permaneçam dentro de limites aceitáveis.
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;
		}
	}
}