Ver en GitHub

Estrategia de precio único Stop-Loss/Take-Profit

Esta estrategia de utilidad replica el script MetaTrader "One Price SL TP" dentro de StockSharp. En lugar de abrir operaciones, el algoritmo observa la posición actual en el instrumento configurado y se asegura de que ambas órdenes de protección estén alineadas con un único precio objetivo especificado por el usuario.

Siempre que el parámetro ZenPrice esté por encima de cero, la estrategia lo compara con las cotizaciones de oferta/demanda en vivo:

  • Para una posición larga: si ZenPrice es mayor que el precio solicitado, se coloca una orden límite de obtención de ganancias a ese precio; si ZenPrice es inferior a la oferta, en su lugar se registra una orden stop-loss.
  • Para una posición corta: si ZenPrice es menor que la oferta, se convierte en la orden límite de obtención de ganancias; si ZenPrice es mayor que la demanda, se convierte en la orden stop-loss.

Cuando el precio cae entre la oferta y la demanda no se envía nada, por lo que la orden de protección anterior permanece intacta. Tan pronto como se cierra la posición o el parámetro se pone a cero, todas las órdenes de protección se cancelan automáticamente.

como funciona

  1. Suscríbase a los datos de Level1 para recibir cotizaciones de oferta y demanda actualizadas que se requieren para las verificaciones de dirección.
  2. Realiza un seguimiento del volumen y la dirección de la posición de la estrategia actual. Se supone que las posiciones se crean manualmente o mediante otras estrategias.
  3. En cada cotización, posición o actualización comercial personal, recalcula a qué lado del mercado pertenece el ZenPrice y crea el tipo de orden de protección correspondiente.
  4. Normaliza el precio solicitado utilizando el paso del precio del instrumento y redondea el volumen de la orden a los límites del intercambio antes de enviar algo al conector comercial.
  5. Utiliza ReRegisterOrder para modificar órdenes de protección ya activas en lugar de cancelarlas, coincidiendo con el comportamiento de la modificación local de MetaTrader.

Parámetro

  • ZenPrice – precio absoluto que debe usarse como nivel de límite de pérdidas o de obtención de ganancias. Establezca el valor en 0 para deshabilitar la automatización. Predeterminado: 0.

Notas practicas

  • La estrategia nunca envía órdenes de entrada. Es seguro iniciarlo junto con terminales comerciales discrecionales u otras estrategias automatizadas.
  • Las órdenes de protección se emiten solo después de que la primera instantánea de Level1 proporcione cotizaciones tanto de oferta como de demanda. Hasta entonces, el script espera, al igual que la versión original MQL se basaba en las comillas del terminal.
  • Cuando solo un lado del mercado cumple la condición (por ejemplo, ZenPrice está por encima de la oferta pero no por debajo de la oferta), la otra orden de protección se cancela para evitar precios obsoletos.
  • Todos los comentarios dentro del código están en inglés, mientras que esta documentación se proporciona en varios idiomas de acuerdo con las pautas del proyecto.

Diferencias con el script MetaTrader

  • El script original modifica los campos stop-loss y take-profit de un ticket de posición existente. StockSharp expone las órdenes de protección como órdenes de límite y stop explícitas, por lo que la conversión opera en órdenes visibles en el mercado.
  • MetaTrader ajusta automáticamente el precio a la precisión del corredor. En este puerto, el mismo comportamiento se reproduce a través de NormalizePrice, que aprovecha el paso de precio del símbolo y la configuración decimal.
  • El volumen de posición se redondea para intercambiar límites de lotes antes de enviar las órdenes de protección, lo que garantiza la compatibilidad con lugares que requieren pasos de lote específicos.
namespace StockSharp.Samples.Strategies;

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

/// <summary>
/// One Price SL TP strategy: EMA + candle body size filter.
/// Buys when bullish candle with large body closes above EMA, sells on opposite.
/// </summary>
public class OnePriceSlTpStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaPeriod;
	private bool _wasBullishSignal;
	private bool _hasPrevSignal;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }

	public OnePriceSlTpStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_emaPeriod = Param(nameof(EmaPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA trend filter period", "Indicators");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_wasBullishSignal = false;
		_hasPrevSignal = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_wasBullishSignal = false;
		_hasPrevSignal = false;
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ema, ProcessCandle).Start();
	}

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

		var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
		var range = candle.HighPrice - candle.LowPrice;
		if (range <= 0) return;

		// Strong candle: body > 60% of range
		if (body < range * 0.7m) return;

		var bullishSignal = candle.ClosePrice > candle.OpenPrice && candle.ClosePrice > emaValue;
		var bearishSignal = candle.ClosePrice < candle.OpenPrice && candle.ClosePrice < emaValue;
		var crossedUp = bullishSignal && (!_hasPrevSignal || !_wasBullishSignal);
		var crossedDown = bearishSignal && (!_hasPrevSignal || _wasBullishSignal);

		if (crossedUp && Position <= 0)
			BuyMarket();
		else if (crossedDown && Position >= 0)
			SellMarket();

		if (bullishSignal || bearishSignal)
		{
			_wasBullishSignal = bullishSignal;
			_hasPrevSignal = true;
		}
	}
}