Ver no GitHub

Estratégia de grade de limite pendente (conversão MQL/8147)

Visão geral

A Pending Limit Grid Strategy reproduz o comportamento do especialista MetaTrader armazenado em MQL/8147. A estratégia constrói uma grade simétrica de ordens de limite pendentes em torno dos preços atuais de compra/venda. Mantém a grade ativa enquanto o lucro flutua permanece dentro de uma meta de lucro configurada e limite de rebaixamento. Quando um dos os limites são violados, todas as ordens são canceladas, as posições abertas são achatadas e a grade é reconstruída usando o novo patrimônio líquido como linha de base.

Lógica de negociação

  1. Assine os dados de nível um para rastrear os melhores preços de compra e venda.
  2. Capture o patrimônio da conta na primeira vez que os dados em tempo real forem recebidos e armazene-os como a linha de base da sessão.
  3. Coloque LevelsPerSide limites de venda acima do mercado e o mesmo número de compras limites abaixo do mercado. A distância entre os níveis da grade é controlada por GridStepPoints convertido para a etapa de preço do instrumento.
  4. Manter as ordens pendentes sem reemitir novas quando forem atendidas. O a grade é recriada somente após uma reinicialização completa.
  5. Monitore continuamente o PnL flutuante:
    • Se o lucro atingir ProfitTargetCurrency, feche toda a exposição e reinicie.
    • Se o rebaixamento exceder MaxDrawdownCurrency, nivele o livro e reinicie.
  6. Após cada reinicialização, o patrimônio da linha de base é capturado novamente e a grade é reconstruída usando o instantâneo de oferta/venda mais recente.

Parâmetros

Parâmetro Descrição
ProfitTargetCurrency Lucro líquido (na moeda da conta) que desencadeia uma redefinição completa da grade.
MaxDrawdownCurrency Perda flutuante máxima tolerada antes que toda a exposição seja encerrada.
GridStepPoints Distância entre níveis de grade consecutivos expressos em pontos de corretagem.
LevelsPerSide Número de ordens pendentes criadas acima e abaixo do mercado.
OrderVolume Volume atribuído a cada ordem limite pendente.

Gestão de risco

A estratégia não anexa paradas ou metas por pedido. Em vez disso, supervisiona o lucros e perdas agregados. O auxiliar RequestFlatten cancela pedidos pendentes e usa ordens de mercado (via ClosePosition) para remover qualquer exposição aberta. Depois do nivelamento for concluído, o estado da rede e a equidade da linha de base serão redefinidos antes de colocar novas encomendas.

Notas

  • Os preços são normalizados através de Security.ShrinkPrice para respeitar o câmbio etapa de preço.
  • O valor MetaTrader "Ponto" é emulado pela análise do instrumento PriceStep para corresponder às cotações de quatro e cinco dígitos.
  • A estratégia evita o reenvio de ordens de grade depois de colocadas, imitando o especialista original que dependia de variáveis de sinalização para manter cada nível único até ocorre uma reinicialização manual ou automática.
using System;

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

namespace StockSharp.Samples.Strategies;

public class PendingLimitGridStrategy : Strategy
{
	private readonly StrategyParam<int> _channelPeriod;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cooldownBars;

	private decimal _prevClose;
	private decimal _prevMid;
	private bool _hasPrev;
	private int _barsSinceLastTrade;

	public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }

	public PendingLimitGridStrategy()
	{
		_channelPeriod = Param(nameof(ChannelPeriod), 24).SetDisplay("Channel Period", "Grid channel lookback", "Indicators");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
		_cooldownBars = Param(nameof(CooldownBars), 200).SetDisplay("Cooldown Bars", "Minimum bars between trades", "Risk");
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClose = 0;
		_prevMid = 0;
		_hasPrev = false;
		_barsSinceLastTrade = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_hasPrev = false;
		_barsSinceLastTrade = 0;
		var highest = new Highest { Length = ChannelPeriod };
		var lowest = new Lowest { Length = ChannelPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(highest, lowest, ProcessCandle).Start();
	}

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

		var close = candle.ClosePrice;
		var mid = (highest + lowest) / 2;

		if (!_hasPrev) { _prevClose = close; _prevMid = mid; _hasPrev = true; return; }

		_barsSinceLastTrade++;

		if (_barsSinceLastTrade >= CooldownBars)
		{
			if (_prevClose <= _prevMid && close > mid && Position <= 0)
			{
				if (Position < 0) BuyMarket();
				BuyMarket();
				_barsSinceLastTrade = 0;
			}
			else if (_prevClose >= _prevMid && close < mid && Position >= 0)
			{
				if (Position > 0) SellMarket();
				SellMarket();
				_barsSinceLastTrade = 0;
			}
		}

		_prevClose = close;
		_prevMid = mid;
	}
}