Ver no GitHub

Estratégia de probabilidade do padrão Gselector

Visão geral

A estratégia Gselector Pattern Probability é uma versão StockSharp do especialista MetaTrader 4 "Gselector". Ele estuda mudanças de direção de séries de preços sintéticos construídas a partir de vários tamanhos de passos, mantém estatísticas de probabilidade para cada padrão observado e negocia quando a probabilidade de um movimento de continuação é alta o suficiente. As distâncias de stop-loss e take-profit são simuladas em software para refletir o comportamento original do especialista.

Processo de aprendizagem

  1. Escadas sintéticas – Para cada delta múltiplo configurado, a estratégia constrói uma série baseada em etapas, registrando o último preço de fechamento sempre que o mercado se move na distância necessária.
  2. Codificação de padrão – Uma máscara de bits é criada comparando cada par de valores vizinhos dentro da escada. As etapas ascendentes recebem o bit 0, as etapas descendentes recebem o bit 1, que reproduz a codificação Ncomb da implementação MQL.
  3. Rastreamento de eventos – Quando um novo padrão aparece, a estratégia inicia observadores para cada nível de stop configurado. Um observador armazena o preço de origem e espera até que o preço suba ou desça no limite.
  4. Atualização de probabilidade – Depois que um observador é concluído, os movimentos ascendentes aumentam a estatística de “crescimento”, os movimentos descendentes aumentam a estatística de “declínio”. Um fator de esquecimento emula a lógica de decaimento (forg) do especialista original.
  5. Persistência na memória – Todas as estatísticas são mantidas na memória e redefinidas no início da estratégia, correspondendo ao comportamento da versão MQL quando ReadHistory está desabilitado.

Lógica de negociação

  1. As probabilidades de continuação são calculadas para o padrão atual em cada escada delta.
  2. Um sinal de compra requer:
    • Probabilidade ≥ ProbabilityThreshold.
    • Observações ≥ MinSamples.
    • O tempo de espera decorreu desde a compra anterior.
    • Se existir uma posição curta, a nova probabilidade deve exceder a probabilidade de venda armazenada mais o ProbabilityBuffer.
  3. Um sinal de venda reflete as regras de compra com as funções de crescimento/declínio trocadas.
  4. As entradas usam BuyMarket / SellMarket para emular OrderSend. Quando a posição oposta está aberta a estratégia a fecha primeiro, reproduzindo o comportamento de reversão do consultor especialista.
  5. As saídas de proteção são tratadas internamente: as paradas e as tomadas são expressas em unidades de preço derivadas do valor do ponto e do nível de parada.

Parâmetros

Nome Descrição Padrão
CandleType Tipo de dados Candle usado para backtest/sessão ao vivo. Período de 1 minuto
ProbabilityThreshold Probabilidade mínima de continuação necessária para abrir uma negociação. 0,8
BaseDeltaPoints Distância do ponto base que define a primeira escada sintética. 1
DeltaSteps Número de escadas delta a serem avaliadas. 20
PatternLength Número de elementos no histórico da escada. 10
StopLevels Contagem de níveis de stop/take. 1
StopDistancePoints Parada base/medida de distância em pontos. 25
ForgetFactor Decaimento aplicado aos contadores de crescimento/declínio após cada observação. 1.05
MinSamples Número mínimo de observações concluídas. 10
ProbabilityBuffer Probabilidade extra necessária para fechar a posição oposta. 0,05
FixedVolume Volume base de comércio. 1 lote
UseReinvest Permite ajuste de volume proporcional ao equilíbrio. verdade
VolumeMode 0 – fixo, 1 – por cento por 10k, 2 – escada, 3 – linear. 1
PercentPer10k Percentagem por 10 000 unidades no modo 1. 3
BaseDeposit Depósito base para modos 2 e 3. 500
DepositStep Incremento de depósito para os modos 2 e 3. 500
MaxVolume Limite máximo de volume. 10.000
CooldownFactor Número de intervalos de velas usados como resfriamento de reativação. 2

Diferenças do especialista MQL

  • A persistência baseada em arquivo foi removida; as estatísticas são reconstruídas do zero sempre que a estratégia é iniciada.
  • Os pedidos são simulados por meio de BuyMarket/SellMarket e gerenciamento de parada de software em vez de pedidos pendentes MT4.
  • Os auxiliares de dimensionamento de posição foram adaptados aos dados do portfólio StockSharp. Se os valores patrimoniais não estiverem disponíveis, a estratégia volta ao volume fixo.
  • As entradas de trailing stop do código original são ignoradas porque a versão MT4 nunca as aplicou.

Notas de uso

  • Anexe a estratégia a um título com um PriceStep válido. Se o passo for desconhecido, a estratégia volta para 0,0001.
  • O processo de aprendizagem necessita de um número mínimo de ativações na escada; espere uma fase de aquecimento antes do início das negociações.
  • Aumentar DeltaSteps ou PatternLength aumenta o uso de memória exponencialmente porque o dicionário de padrões cresce rapidamente.
  • O limite de probabilidade padrão (0,8) é muito rigoroso. Reduza o valor para negociações mais frequentes.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Gselector Pattern Probability strategy - SMA crossover with RSI filter.
/// Buys when fast SMA crosses above slow SMA and RSI is below overbought.
/// Sells when fast SMA crosses below slow SMA and RSI is above oversold.
/// </summary>
public class GselectorPatternProbabilityStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _overbought;
	private readonly StrategyParam<decimal> _oversold;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
	public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public GselectorPatternProbabilityStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetDisplay("Fast SMA", "Fast SMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetDisplay("Slow SMA", "Slow SMA period", "Indicators");

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetDisplay("RSI Period", "RSI lookback", "Indicators");

		_overbought = Param(nameof(Overbought), 75m)
			.SetDisplay("Overbought", "RSI overbought level", "Levels");

		_oversold = Param(nameof(Oversold), 25m)
			.SetDisplay("Oversold", "RSI oversold level", "Levels");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevFast = 0m; _prevSlow = 0m; _hasPrev = false; }

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_hasPrev = false;

		var fast = new SimpleMovingAverage { Length = FastPeriod };
		var slow = new SimpleMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, ProcessCandle)
			.Start();
	}

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

		if (!_hasPrev)
		{
			_prevFast = fast;
			_prevSlow = slow;
			_hasPrev = true;
			return;
		}

		// Fast crosses above slow = buy
		if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Fast crosses below slow = sell
		else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevFast = fast;
		_prevSlow = slow;
	}
}