Ver no GitHub

Estratégia Basket Close Utility

Visão geral

A estratégia Basket Close Utility reflete o comportamento do especialista MetaTrader "Basket Close 2". Ele monitora continuamente os lucros e perdas flutuantes de cada posição aberta na carteira conectada. When either a configurable profit objective or a loss limit is reached, the strategy sends market orders to flatten all exposures in every instrument involved. Opcionalmente, ele pode abrir automaticamente uma pequena posição de teste sempre que o livro estiver plano, o que é útil em backtests para validar se a lógica de proteção funciona conforme o esperado.

Parâmetros

Nome Descrição
LossMode Chooses whether the loss guard compares percentages or currency values.
LossPercentage Negative percentage drawdown (expressed in absolute value) that triggers the loss exit when LossMode is Percentage.
LossCurrency Floating loss in account currency that triggers the exit when LossMode is Currency.
ProfitMode Escolhe se o objetivo de lucro compara porcentagens ou valores monetários.
ProfitPercentage Percentage gain that closes all positions when ProfitMode is Percentage.
ProfitCurrency Lucro flutuante na moeda da conta que fecha todas as posições quando ProfitMode é Currency.
CandleType Prazo usado para acionar verificações periódicas dos lucros e perdas flutuantes.
EnableTestOrders Quando habilitada, a estratégia envia uma única ordem de compra de mercado sempre que não houver posições abertas.
TestOrderVolume Tamanho da negociação usado quando a ordem de teste opcional está ativa.

Lógica de negociação

  1. Subscribe to the configured candle series and run the evaluation only when a candle is fully finished, matching the behaviour of the original EA that works on closed bars.
  2. Agregue os lucros e perdas flutuantes de cada posição aberta. If the portfolio object exposes a combined floating profit it is used; otherwise the strategy sums the PnL of each position.
  3. Compute the percentage change relative to the current account balance captured at start-up.
  4. Acione a rotina de perda quando o PnL flutuante ultrapassar o limite configurado. Trigger the profit routine when the floating PnL or the percentage gain reaches the profit target.
  5. Uma vez acionado, continue enviando ordens de mercado até que todas as posições abertas em todo o portfólio sejam fechadas. Isso inclui o título principal, bem como as posições abertas por estratégias infantis.
  6. Opcionalmente, envie uma ordem de mercado para reabrir a exposição (para teste) depois que o livro ficar estável.

Notas

  • O especialista MetaTrader exibiu informações textuais no gráfico. Em StockSharp, os números importantes são registrados por meio de LogInfo.
  • Os ajustes de swap e comissão do script original são implicitamente incluídos no PnL flutuante reportado pela carteira ou posições individuais.
  • Os limites percentuais utilizam o saldo da conta capturado quando a estratégia é iniciada. Ajuste os limites ao realizar sessões longas se a base de capital mudar substancialmente.
  • Quando a ordem de teste opcional está habilitada, a ordem auxiliar é reemitida sempre que a exposição anterior tiver sido encerrada pela guarda de lucros ou perdas.
namespace StockSharp.Samples.Strategies;

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

/// <summary>
/// Basket Close strategy: EMA trend following with profit/loss close thresholds.
/// Enters on EMA direction, closes when accumulated P&L hits target or stop.
/// </summary>
public class BasketCloseStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaPeriod;

	private decimal _entryPrice;
	private bool _wasBullish;
	private bool _hasPrevSignal;

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_entryPrice = 0m;
		_wasBullish = false;
		_hasPrevSignal = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_entryPrice = 0;
		_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 close = candle.ClosePrice;
		var isBullish = close > emaValue;

		if (_hasPrevSignal && isBullish != _wasBullish)
		{
			if (isBullish && Position <= 0)
			{
				BuyMarket();
				_entryPrice = close;
			}
			else if (!isBullish && Position >= 0)
			{
				SellMarket();
				_entryPrice = close;
			}
		}

		_wasBullish = isBullish;
		_hasPrevSignal = true;
	}
}