Ver no GitHub

Fechar com lucro ou perda na moeda da conta

Esta estratégia reproduz o consultor especialista MetaTrader Close_on_PROFIT_or_LOSS_inAccont_Currency. Ele monitora continuamente o patrimônio do portfólio ao qual a estratégia está vinculada e, uma vez atingida uma meta de lucro configurada ou piso de rebaixamento, liquida todas as posições abertas e cancela todas as ordens pendentes gerenciadas pela estratégia. A classe depende do alto nível de StockSharp API: uma assinatura de vela fornece o batimento cardíaco, CancelActiveOrders() remove ordens de trabalho e ClosePosition() nivela a exposição por meio de ordens de mercado.

Como funciona

  1. A estratégia continua pesquisando o patrimônio atual (Portfolio.CurrentValue) sempre que uma vela de pulsação fecha.
  2. Se o patrimônio for maior ou igual a Fecho Positivo, a estratégia envia uma solicitação de fechamento completo.
  3. Se o patrimônio for menor ou igual a Fecho Negativo, a mesma rotina de liquidação é executada para limitar as perdas.
  4. Durante a liquidação, a estratégia cancela todas as ordens pendentes, envia ordens de mercado para fechar todas as posições ativas e, finalmente, para (espelhando a chamada ExpertRemove() do EA original).

Importante: defina os limites na moeda da conta. Para emular o comportamento original, escolha um valor de Fecho Positivo acima do patrimônio atual e um valor de Fecho Negativo abaixo dele; caso contrário, a saída será acionada imediatamente na partida.

Parâmetros

Nome Descrição Padrão
PositiveClosureInAccountCurrency Nível de patrimônio que desencadeia uma liquidação total quando excedido. 0
NegativeClosureInAccountCurrency Piso patrimonial que força a liquidação quando atingido. 0
CandleType Período usado para as velas de pulsação que orientam as verificações de patrimônio. Reduza-o para reações mais rápidas. 1 minute

Notas

  • StartProtection() é ativado na inicialização para copiar o comportamento de segurança original.
  • A estratégia interage apenas com posições e ordens que administra; anexe-o à carteira que contém as negociações que você deseja proteger.
  • Não há entrada separada de spread/slippage porque StockSharp ordens de mercado já contabilizam custos de execução específicos do conector.
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

using StockSharp.Algo.Candles;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Closes all positions when portfolio equity reaches configured profit or loss thresholds.
/// Pending orders are cancelled before liquidating the exposure.
/// </summary>
public class CloseOnProfitOrLossInAccountCurrencyStrategy : Strategy
{
	private readonly StrategyParam<decimal> _positiveClosure;
	private readonly StrategyParam<decimal> _negativeClosure;
	private readonly StrategyParam<DataType> _candleType;

	private bool _closeRequested;
	private SimpleMovingAverage _smaFast;
	private SimpleMovingAverage _smaSlow;

	/// <summary>
	/// Equity level in account currency that triggers closing all positions when exceeded.
	/// </summary>
	public decimal PositiveClosureInAccountCurrency
	{
		get => _positiveClosure.Value;
		set => _positiveClosure.Value = value;
	}

	/// <summary>
	/// Equity level in account currency that triggers closing all positions when reached on drawdown.
	/// </summary>
	public decimal NegativeClosureInAccountCurrency
	{
		get => _negativeClosure.Value;
		set => _negativeClosure.Value = value;
	}

	/// <summary>
	/// Candle type used as a heartbeat to evaluate portfolio equity.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="CloseOnProfitOrLossInAccountCurrencyStrategy"/> class.
	/// </summary>
	public CloseOnProfitOrLossInAccountCurrencyStrategy()
	{
		_positiveClosure = Param(nameof(PositiveClosureInAccountCurrency), 0m)
			.SetDisplay("Positive Closure", "Equity level that triggers full liquidation", "Risk");

		_negativeClosure = Param(nameof(NegativeClosureInAccountCurrency), 0m)
			.SetDisplay("Negative Closure", "Equity floor that forces liquidation", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Heartbeat Candle", "Candle type that triggers equity checks", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		if (Security == null)
			return Array.Empty<(Security, DataType)>();

		return new (Security, DataType)[] { (Security, CandleType) };
	}

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

		if (Portfolio == null)
			throw new InvalidOperationException("Portfolio cannot be null.");

		if (Security == null)
			throw new InvalidOperationException("Security must be set to subscribe for candles.");

		_smaFast = new SimpleMovingAverage { Length = 10 };
		_smaSlow = new SimpleMovingAverage { Length = 30 };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_smaFast, _smaSlow, ProcessCandleWithIndicators)
			.Start();
	}

	private void ProcessCandleWithIndicators(ICandleMessage candle, decimal fast, decimal slow)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (fast > slow && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (fast < slow && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}
	}

	private void RequestCloseAll(string reason)
	{
		if (_closeRequested)
			return;

		_closeRequested = true;

		LogInfo(reason);

		// Cancel any pending orders to avoid unexpected executions during liquidation.
		CancelActiveOrders();

		foreach (var position in Positions.ToArray())
		{
			var value = GetPositionValue(position.Security, Portfolio) ?? 0m;

			if (value == 0m)
				continue;

			// Submit a market order opposite to the current exposure.
			ClosePosition(position.Security);
		}

		// Stop the strategy after sending exit orders, mirroring ExpertRemove behavior.
		Stop();
	}
}