Ver en GitHub

Estrategia doble de stoploss

Esta estrategia replica el comportamiento del experto MetaTrader Dual StopLoss.mq4. Actúa como una capa de gestión de riesgos: monitorea las órdenes protectoras de stop-loss adjuntas a las posiciones abiertas y cierra esas posiciones unos pocos puntos antes de que se active el stop. La salida anticipada está diseñada para evitar un deslizamiento negativo en movimientos altamente volátiles y al mismo tiempo respetar la colocación del stop inicial del operador.

como funciona

  1. La estrategia se suscribe a los datos de Level1 para realizar un seguimiento de la mejor oferta/demanda actual y la distancia StopLevel (o equivalente) publicada por el corredor.
  2. Cada vez que llegan nuevos precios o cambian órdenes/negocios, busca la orden de parada activa más cercana que pertenece al valor administrado.
  3. La distancia entre el precio de mercado y ese stop de protección se compara con un umbral configurable:
    • Umbral = WhenToClosePoints × pointValue + stopLevelDistance.
    • pointValue coincide con el Point de MetaTrader (0,0001 para la mayoría de los pares de divisas, detectado automáticamente desde la configuración de seguridad).
    • stopLevelDistance proviene de los campos de Nivel 1 (StopLevel, MinStopPrice, StopPrice o StopDistance) cuando estén disponibles; de lo contrario, cero.
  4. Cuando la distancia restante es menor o igual que el umbral, la posición se cierra inmediatamente mediante una orden de mercado.

La lógica cubre tanto posiciones largas como cortas. Para posiciones largas, la mejor oferta se compara con el precio de venta; para posiciones cortas, la mejor demanda se compara con el precio stop de compra. Sólo se consideran las órdenes stop y stop-limit en estado activo.

Parámetros

Parámetro Descripción
CuandoCerrarPuntos Distancia (en MetaTrader puntos) desde el nivel de parada que debería activar la salida anticipada. Valor predeterminado: 10. Establezca en cero para confiar únicamente en la distancia mínima del nivel de parada del corredor.

Notas y limitaciones

  • La estrategia no abre posiciones por sí sola; solo gestiona posiciones que ya existen y tienen órdenes de parada de protección.
  • Asegúrese de que el conector/corredor subyacente proporcione valores de nivel de parada a través de datos de Nivel1 si desea tener en cuenta las distancias mínimas impuestas por el corredor. Si falta esa información, la estrategia aún funciona utilizando solo la distancia del punto configurada.
  • La llamada StartProtection() habilita las guardas de seguridad integradas en StockSharp para que las salidas de emergencia permanezcan activas una vez iniciada la estrategia.
  • Las paradas se detectan a partir de la colección Orders de la estrategia. Asegúrese de que las paradas de protección estén registradas a través de la misma instancia de estrategia para que aparezcan en esta lista.
  • Cuando existen varias órdenes stop para la misma dirección, se utiliza la más cercana al mercado.

Consejos de uso

  1. Adjunte la estrategia a una cartera/valor donde las posiciones se abren manualmente o mediante otro sistema, pero las paradas de protección se colocan bajo el mismo contexto de estrategia.
  2. Configure WhenToClosePoints para que coincida con la cantidad de protección que necesita antes de la parada. Este valor se interpreta exactamente igual que en MetaTrader (puntos, no unidades de precio).
  3. Inicie la estrategia y supervise el registro. Cuando el precio de mercado se acerca al tope, la estrategia emitirá una orden de mercado para cerrar la posición de forma proactiva.
  4. Combine este módulo con otras estrategias de entrada o de tamaño de posición para crear un flujo de trabajo comercial 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;
	}
}