Ver en GitHub

Enviar estrategia de cierre de orden

Send Close Order es una adaptación del asesor experto MetaTrader 4 de 2009 "SendCloseOrder" de Vladimir Hlystov. El script original dibuja cuatro líneas de tendencia manuales basadas en fractales de Bill Williams y abre o cierra órdenes de mercado cada vez que el precio toca uno de esos niveles proyectados. La versión StockSharp replica la lógica de decisión con una gestión de línea totalmente automatizada y funciona con cualquier serie de velas proporcionada por la plataforma.

Lógica comercial

  1. Detección de fractales: cada vela terminada actualiza una ventana deslizante de cinco barras. Una vez que la ventana está llena, la vela en el medio se compara con las condiciones fractales de Bill Williams. Los máximos y mínimos confirmados se almacenan cronológicamente.
  2. Reconstrucción de la línea de tendencia
    • Línea de venta conecta los dos últimos fractales ascendentes que están separados por un fractal descendente, formando una pendiente de resistencia.
    • El cierre #1 es la línea de venta desplazada hacia arriba en 15 pasos de precio (15 × Security.PriceStep) y actúa como el carril de salida largo.
    • Línea de compra conecta los dos últimos fractales descendentes que están separados por un fractal ascendente, formando una pendiente de soporte.
    • Cierre #2 es la línea de compra desplazada hacia abajo en 15 pasos de precio y actúa como carril de salida corto.
  3. Evaluación de señal: las cuatro líneas se extrapolan a la marca de tiempo de la vela terminada. Si el precio proyectado se encuentra dentro del rango máximo/bajo de la vela (con una pequeña tolerancia de dos pasos de precio), se activa la acción correspondiente.
  4. Gestión de pedidos
    • Al tocar Cerrar #1 o Cerrar #2 se cierra inmediatamente toda la posición a través de ClosePosition().
    • Al tocar la línea Vender o Comprar se abre una orden de mercado con volumen TradeVolume, siempre que la posición absoluta resultante no supere MaxOrders × TradeVolume. Cuando existe una posición opuesta, la orden la compensa primero y luego acumula una nueva entrada, reflejando el comportamiento de las cuentas de cobertura.

Parámetros

Nombre Predeterminado Descripción
EnableSellLine true Permita operaciones cuando se alcance la línea de resistencia proyectada.
EnableBuyLine true Permitir operaciones cuando se alcance la línea de soporte proyectada.
EnableCloseLongLine true Permitir cerrar posiciones largas en la línea de resistencia desplazada (Cierre n.º 1).
EnableCloseShortLine true Permitir cerrar posiciones cortas en la línea de soporte desplazada (Cierre #2).
MaxOrders 1 Número máximo de entradas apiladas en la dirección actual.
TradeVolume 0.1 Volumen de cada orden de mercado individual.
CandleType 1h período de tiempo Serie de velas utilizadas para cálculos fractales.

Diferencias versus la versión MetaTrader

  • El puerto StockSharp recalcula las cuatro líneas cada vez que aparece un nuevo fractal. En MetaTrader el usuario tuvo que eliminar y volver a dibujar líneas de tendencia manualmente.
  • La ejecución se basa en posiciones netas agregadas; El modelo de cartera predeterminado de StockSharp no admite cestas largas y cortas simultáneas.
  • La detección táctil utiliza el máximo/mínimo de la vela terminada con una tolerancia de paso de precio en lugar de las cotizaciones instantáneas de oferta y demanda de los ticks.
  • Los objetos del gráfico (líneas de tendencia y etiquetas) no se crean; la atención se centra en las señales comerciales.

Notas de uso

  • La estrategia puede ejecutarse en cualquier instrumento que proporcione velas y un PriceStep válido. Cuando Security.PriceStep es cero, el código vuelve a ser 0.0001.
  • Aumente MaxOrders para emular el comportamiento de apilamiento del EA original. Mantenga TradeVolume alineado con el tamaño de lote del instrumento para evitar el redondeo.
  • El desplazamiento de línea se fija en el valor histórico de 15 puntos. Ajuste el código fuente si se modifica la entrada MetaTrader.

Solo se proporciona la implementación de C#. Se agregará una traducción de Python por separado si es necesario.

using System;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// SendCloseOrder: Fractal high/low breakout with EMA filter and ATR stops.
/// </summary>
public class SendCloseOrderStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _entryPrice;
	private decimal _fractalHigh;
	private decimal _fractalLow;
	private decimal _prev2High;
	private decimal _prev1High;
	private decimal _prev2Low;
	private decimal _prev1Low;
	private int _barCount;

	public SendCloseOrderStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_emaLength = Param(nameof(EmaLength), 50)
			.SetDisplay("EMA Length", "Trend filter.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

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

	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

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

		_entryPrice = 0;
		_fractalHigh = 0;
		_fractalLow = 0;
		_prev2High = 0;
		_prev1High = 0;
		_prev2Low = 0;
		_prev1Low = 0;
		_barCount = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_entryPrice = 0;
		_fractalHigh = 0;
		_fractalLow = 0;
		_prev2High = 0;
		_prev1High = 0;
		_prev2Low = 0;
		_prev1Low = 0;
		_barCount = 0;

		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, atr, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, ema);
			DrawOwnTrades(area);
		}
	}

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

		_barCount++;

		var high = candle.HighPrice;
		var low = candle.LowPrice;
		var close = candle.ClosePrice;

		// Detect fractal high: prev1High > prev2High and prev1High > current high
		if (_barCount > 3 && _prev1High > _prev2High && _prev1High > high)
			_fractalHigh = _prev1High;

		// Detect fractal low: prev1Low < prev2Low and prev1Low < current low
		if (_barCount > 3 && _prev1Low < _prev2Low && _prev1Low < low)
			_fractalLow = _prev1Low;

		_prev2High = _prev1High;
		_prev1High = high;
		_prev2Low = _prev1Low;
		_prev1Low = low;

		if (_fractalHigh == 0 || _fractalLow == 0 || atrVal <= 0)
			return;

		if (Position > 0)
		{
			if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 1.5m)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 1.5m)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		if (Position == 0)
		{
			if (close > _fractalHigh && close > emaVal)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (close < _fractalLow && close < emaVal)
			{
				_entryPrice = close;
				SellMarket();
			}
		}
	}
}