Ver en GitHub

3100 Cerrar Todas las Posiciones

Descripción general

  • Convierte la utilidad MQL5 Close all positions en una estrategia de alto nivel de StockSharp.
  • Observa velas terminadas del marco temporal configurado y acumula el beneficio flotante de cada posición abierta en el portafolio asignado.
  • Cuando el beneficio flotante es igual o supera el umbral, se envían órdenes de mercado para aplanar todos los valores manejados por la estrategia (incluyendo estrategias hijas) hasta que el libro esté completamente cerrado.
  • El indicador _closeAllRequested refleja la variable MQL m_close_all para que las órdenes de salida continúen emitiéndose hasta que no queden posiciones.

Parámetros

Nombre Tipo Predeterminado Descripción
ProfitThreshold decimal 10 Beneficio flotante (en moneda de cuenta) requerido antes de que la estrategia aplana cada posición abierta. Refleja InpProfit del EA.
CandleType DataType Marco temporal 1m Serie de velas que define los momentos de "nueva barra". La comprobación de beneficio se ejecuta solo cuando termina una vela, emulando la lógica PrevBars original.

Lógica de trading

  1. La estrategia se suscribe a velas de CandleType y procesa solo barras terminadas, igual que el EA evaluaba el beneficio solo en una nueva barra.
  2. En cada barra terminada el helper CalculateTotalProfit recupera Portfolio.CurrentProfit (PnL flotante incluyendo comisión y swap). Si el adaptador no puede proporcionar este valor recurre a sumar los valores individuales de PnL de posición.
  3. Si el beneficio flotante calculado está por debajo de ProfitThreshold, no ocurre nada.
  4. Tan pronto como el beneficio alcanza el umbral, _closeAllRequested se establece en true y CloseAllPositions() se ejecuta inmediatamente.
  5. CloseAllPositions() recopila cada valor que tiene una exposición en el portafolio o en estrategias anidadas y envía órdenes de mercado en la dirección opuesta al volumen actual (largo → venta, corto → compra).
  6. El indicador _closeAllRequested permanece establecido hasta que HasAnyOpenPosition() detecta que el portafolio está plano, coincidiendo con el comportamiento MQL donde m_close_all permanecía verdadero hasta que todos los tickets estaban cerrados.

Notas adicionales

  • Solo se proporciona la implementación en C#; la carpeta Python se deja intencionalmente vacía según los requisitos de la tarea.
  • La estrategia no cancela órdenes pendientes porque el script original solo cerraba posiciones de mercado.
  • Usar SetOptimize en ProfitThreshold para explorar objetivos de beneficio alternativos a través del optimizador de Designer si es necesario.

Archivos

  • CS/CloseAllPositionsStrategy.cs
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;

/// <summary>
/// Opens positions based on SMA trend and closes when floating PnL reaches a profit threshold.
/// Simplified from the "Close all positions" utility expert.
/// </summary>
public class CloseAllPositionsStrategy : Strategy
{
	private readonly StrategyParam<int> _smaPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private SimpleMovingAverage _sma;

	private decimal _entryPrice;
	private decimal _prevSma;
	private int _cooldown;

	/// <summary>
	/// SMA period for entry signals.
	/// </summary>
	public int SmaPeriod
	{
		get => _smaPeriod.Value;
		set => _smaPeriod.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in price steps.
	/// </summary>
	public int StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Take-profit distance in price steps.
	/// </summary>
	public int TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Initializes strategy parameters.
	/// </summary>
	public CloseAllPositionsStrategy()
	{
		_smaPeriod = Param(nameof(SmaPeriod), 100)
			.SetGreaterThanZero()
			.SetDisplay("SMA Period", "Moving average period for entry signals", "Indicators");

		_stopLossPoints = Param(nameof(StopLossPoints), 200)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Stop-loss distance in price steps", "Risk");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 300)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Take-profit distance in price steps", "Risk");
	}

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

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

		_sma = null;
		_entryPrice = 0;
		_prevSma = 0;
		_cooldown = 0;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_sma = new SimpleMovingAverage { Length = SmaPeriod };

		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_sma, ProcessCandle);
		subscription.Start();
	}

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

		if (!_sma.IsFormed)
		{
			_prevSma = smaValue;
			return;
		}

		if (_cooldown > 0)
		{
			_cooldown--;
			_prevSma = smaValue;
			return;
		}

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

		// Check SL/TP
		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevSma = smaValue;
				return;
			}

			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevSma = smaValue;
				return;
			}
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevSma = smaValue;
				return;
			}

			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevSma = smaValue;
				return;
			}
		}

		// Crossover entry: price crosses above SMA -> buy
		if (close > smaValue && candle.OpenPrice <= _prevSma && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

			BuyMarket();
			_entryPrice = close;
			_cooldown = 60;
		}
		// Price crosses below SMA -> sell
		else if (close < smaValue && candle.OpenPrice >= _prevSma && Position >= 0)
		{
			if (Position > 0)
				SellMarket();

			SellMarket();
			_entryPrice = close;
			_cooldown = 60;
		}

		_prevSma = smaValue;
	}
}