Ver no GitHub

Estratégia de Stoploss Duplo

Esta estratégia replica o comportamento do especialista MetaTrader Dual StopLoss.mq4. Atua como uma camada de gestão de risco: monitoriza as ordens protetoras de stop-loss associadas às posições abertas e fecha essas posições alguns pontos antes de o stop ser acionado. A saída antecipada foi projetada para evitar derrapagens negativas em movimentos altamente voláteis, ao mesmo tempo que respeita a colocação inicial do stop do trader.

Como funciona

  1. A estratégia assina os dados do Nível 1 para rastrear o melhor lance/venda atual e a distância StopLevel (ou equivalente) publicada pela corretora.
  2. Cada vez que novos preços chegam ou ordens/negociações mudam, ele procura a ordem stop ativa mais próxima que pertence ao título gerenciado.
  3. A distância entre o preço de mercado e esse stop de proteção é comparada com um limite configurável:
    • Limite = WhenToClosePoints × pointValue + stopLevelDistance.
    • pointValue corresponde ao MetaTrader de Point (0,0001 para a maioria dos pares FX, detectado automaticamente nas configurações de segurança).
    • stopLevelDistance vem de campos de Nível 1 (StopLevel, MinStopPrice, StopPrice ou StopDistance) quando disponíveis, caso contrário, zero.
  4. Quando a distância restante é menor ou igual ao limite, a posição é fechada imediatamente através de uma ordem de mercado.

A lógica cobre posições longas e curtas. Para posições longas, a melhor oferta é comparada com o preço stop de venda; para posições curtas, o melhor preço de venda é comparado com o preço stop de compra. Apenas são consideradas ordens stop e stop-limit no estado ativo.

Parâmetros

Parâmetro Descrição
QuandoToClosePoints Distância (em MetaTrader pontos) do nível de stop que deverá desencadear a saída antecipada. Padrão: 10. Defina como zero para contar apenas com a distância mínima do nível de stop da corretora.

Notas e limitações

  • A estratégia não abre posições por si só; gerencia apenas posições que já existem e possuem ordens de stop de proteção.
  • Certifique-se de que o conector/corretor subjacente forneça valores de nível de parada por meio de dados de Nível 1 se você quiser levar em conta as distâncias mínimas impostas pelo corretor. Se essa informação estiver faltando, a estratégia ainda funciona usando apenas a distância do ponto configurada.
  • A chamada StartProtection() ativa os guardas de segurança integrados de StockSharp para que as saídas de emergência permaneçam ativas depois que a estratégia for iniciada.
  • As paradas são detectadas na coleção Orders da estratégia. Certifique-se de que as paradas de proteção sejam registradas através da mesma instância de estratégia para que apareçam nesta lista.
  • Quando existem múltiplas ordens de stop para a mesma direção, é utilizada aquela mais próxima do mercado.

Dicas de uso

  1. Anexe a estratégia a uma carteira/título onde as posições são abertas manualmente ou por outro sistema, mas as paradas de proteção são colocadas no mesmo contexto estratégico.
  2. Configure WhenToClosePoints para corresponder à quantidade de amortecimento necessária antes da parada. Este valor é interpretado exatamente como em MetaTrader (pontos, não unidades de preço).
  3. Inicie a estratégia e monitore o log. Quando o preço de mercado se aproximar do stop, a estratégia emitirá uma ordem de mercado para fechar a posição de forma proativa.
  4. Combine este módulo com outras estratégias de entrada ou dimensionamento de posição para criar um fluxo de trabalho de negociação completo.
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Dual Stoploss strategy: dual SMA crossover with confirmation.
/// Buys when fast SMA crosses above mid SMA and mid is above slow, sells on opposite.
/// </summary>
public class DualStoplossStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _midPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	private decimal _prevFast;
	private decimal _prevMid;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int MidPeriod { get => _midPeriod.Value; set => _midPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }

	public DualStoplossStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_fastPeriod = Param(nameof(FastPeriod), 3)
			.SetGreaterThanZero()
			.SetDisplay("Fast SMA", "Fast SMA period", "Indicators");
		_midPeriod = Param(nameof(MidPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Mid SMA", "Mid SMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow SMA", "Slow SMA period", "Indicators");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0;
		_prevMid = 0;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_hasPrev = false;
		var fast = new SimpleMovingAverage { Length = FastPeriod };
		var mid = new SimpleMovingAverage { Length = MidPeriod };
		var slow = new SimpleMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, mid, slow, ProcessCandle).Start();
	}

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

		if (_hasPrev)
		{
			if (_prevFast <= _prevMid && fastValue > midValue && midValue > slowValue && Position <= 0)
				BuyMarket();
			else if (_prevFast >= _prevMid && fastValue < midValue && midValue < slowValue && Position >= 0)
				SellMarket();
		}
		else
		{
			if (fastValue > midValue && midValue > slowValue && Position <= 0)
				BuyMarket();
			else if (fastValue < midValue && midValue < slowValue && Position >= 0)
				SellMarket();
		}

		_prevFast = fastValue;
		_prevMid = midValue;
		_hasPrev = true;
	}
}