Ver en GitHub

Estrategia CloseDeleteEA

Descripción general

La estrategia CloseDeleteEA reproduce la utilidad de MetaTrader que cierra posiciones en masa y elimina órdenes pendientes. Escanea periódicamente la cartera seleccionada y envía órdenes de mercado o solicitudes de cancelación según filtros definidos por el usuario. Esto la hace útil para liquidación de emergencia o escenarios de limpieza cuando la gestión manual de órdenes es demasiado lenta.

Funciones clave

  • Cierra exposición larga y/o corta con órdenes de mercado.
  • Cancela órdenes pendientes que coinciden con los filtros configurados.
  • Filtros opcionales de ganancia/pérdida para evitar tocar posiciones específicas.
  • Restringe el escaneo al valor actual o procesa toda la cartera.
  • Filtra posiciones y órdenes por identificador de estrategia.

Parámetros

Nombre Descripción
CloseBuyPositions Cierra exposición larga que coincide con los filtros.
CloseSellPositions Cierra exposición corta que coincide con los filtros.
CloseMarketPositions Habilita el módulo de cierre de posiciones de mercado.
CancelPendingOrders Habilita la cancelación de órdenes pendientes.
CloseOnlyProfitable Cierra posiciones solo cuando el PnL actual es no negativo.
CloseOnlyLosing Cierra posiciones solo cuando el PnL actual es no positivo.
ApplyToCurrentSecurity Cuando es true, solo se escanea el valor de la estrategia. De lo contrario se procesan todos los valores de la cartera.
TargetStrategyId Filtro opcional de identificador de estrategia (valor vacío coincide con todo).
TimerInterval Frecuencia del temporizador usada para el bucle de gestión.

Notas de uso

  1. Adjunte la estrategia a un conector con una cartera asignada.
  2. Configure filtros opcionalmente antes de iniciar la estrategia.
  3. Inicie la estrategia para activar el ciclo close/delete. La estrategia se detiene automáticamente cuando no quedan posiciones u órdenes coincidentes.
  4. Tenga en cuenta que las solicitudes de cancelación solo pueden dirigirse a órdenes visibles para la estrategia a través del conector.

Diferencias frente a la versión MQL

  • StockSharp trabaja con posiciones agregadas, por lo que el control individual a nivel de ticket se sustituye por gestión de exposición neta basada en volumen.
  • El filtrado por id de estrategia imita el concepto original de magic number.
  • Los elementos visuales de limpieza del gráfico de MetaTrader no se reproducen.
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;

public class CloseDeleteEaStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public CloseDeleteEaStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}