Ver no GitHub

Estratégia Gold Dust

Visão Geral

A Estratégia Gold Dust reproduz o consultor especialista MetaTrader 5 "Gold Dust" dentro do framework StockSharp. Ela avalia até dois perceptrons construídos a partir de uma média móvel ponderada linear (LWMA) aplicada ao preço ponderado da vela. Cada perceptron observa como o preço desvia da média móvel em quatro pontos de retrocesso diferentes espaçados pelo período da MA. Quando a saída do perceptron é positiva, o especialista original abre uma posição de venda, e quando é negativa abre uma compra. O port StockSharp mantém o mesmo comportamento usando a API de assinatura de velas de alto nível.

Geração de Sinais

  1. Assinar o CandleType configurado e calcular um WeightedMovingAverage com o período de MaPeriod.
  2. Em cada vela terminada, armazenar os preços de abertura e fechamento da vela junto com o valor da LWMA. A estratégia sempre mantém três períodos completos de MA de histórico para replicar as chamadas CopyRates/CopyBuffer da versão MQL.
  3. Calcular os desvios preço/MA:
    • a1 – fechamento atual menos LWMA atual
    • a2 – preço de abertura um período de MA atrás menos LWMA na mesma vela
    • a3 – preço de abertura dois períodos de MA atrás menos LWMA na mesma vela
    • a4 – preço de abertura três períodos de MA atrás menos LWMA na mesma vela
  4. Construir a saída do perceptron result = Σ (wi × ai) onde cada peso é o parâmetro bruto (por exemplo X11) menos 100, replicando a transformação original w = x - 100.
  5. Interpretar as saídas do perceptron dependendo de PassMode:
    • 1 – usar apenas o primeiro perceptron.
    • 2 – usar apenas o segundo perceptron.
    • 3 – exigir que ambos os perceptrons produzam o mesmo sinal diferente de zero.
  6. Um sinal negativo abre ou mantém uma posição longa, um sinal positivo abre ou mantém uma posição curta, e um sinal zero aciona a realização de lucros em posições existentes.

Gestão de Posições

  • Entradas – a estratégia opera com um TradeVolume fixo. Entrar comprado fecha qualquer exposição vendida pendente e vice-versa, mantendo apenas uma posição direcional.
  • Stop-lossStopLossPips é convertido em distância de preço absoluta usando Security.PriceStep. Para instrumentos cotados com três ou cinco decimais, a distância é multiplicada por dez para imitar a lógica do "ponto ajustado" na versão MQL. O stop é avaliado em cada vela concluída.
  • Trailing stop – quando TrailingStopPips é maior que zero, a lógica de trailing é ativada. Após o preço se mover TrailingStopPips + TrailingStepPips a favor da operação, o stop é posicionado em fechamento ± TrailingStopPips (dependendo da direção).
  • Gestão de lucros – quando nenhum perceptron concorda sobre uma direção (signal == 0), a estratégia fecha a posição apenas se o lucro flutuante for positivo.

Parâmetros

Parâmetro Padrão Descrição
TradeVolume 1 Volume base para cada nova entrada. Posições opostas são achatadas antes de tomar um novo lado.
StopLossPips 150 Distância inicial de stop-loss em pips ajustados (considera o multiplicador de 3/5 dígitos). Definir como zero para desabilitar o stop inicial.
TrailingStopPips 25 Distância de trailing stop em pips ajustados. Definir como zero para desabilitar o trailing.
TrailingStepPips 5 Movimento favorável adicional (em pips) necessário antes que o trailing stop avance.
MaPeriod 20 Comprimento do período da média móvel ponderada que alimenta os perceptrons.
CandleType H1 Série de velas usada para avaliação de sinais. Qualquer outro período suportado pelo provedor de dados pode ser selecionado.
PassMode 1 Controla qual(is) perceptron(s) são avaliados: 1 – primeiro, 2 – segundo, 3 – consenso de ambos.
X11, X21, X31, X41 100 Pesos brutos para o perceptron #1. A estratégia subtrai 100 de cada valor antes de usar.
X12, X22, X32, X42 100 Pesos brutos para o perceptron #2, tratados da mesma forma que o primeiro conjunto.

Notas sobre a Conversão

  • O EA original dependia de atualizações tick a tick para gerenciar stops; o port StockSharp avalia stops e trailing no fechamento da vela.
  • Gestão monetária via CMoneyFixedMargin foi substituída por um parâmetro fixo TradeVolume.
  • Cálculos do perceptron evitam buffers diretos de indicadores (CopyBuffer) armazenando em cache os valores necessários de velas e MA em listas limitadas.
  • Todas as distâncias de pips respeitam a convenção do "ponto ajustado" do MetaTrader: se o instrumento opera com 3 ou 5 decimais, a distância é multiplicada por dez.

Dicas de Uso

  1. Criar ou selecionar um símbolo, depois definir CandleType para o período que corresponde ao gráfico histórico usado na versão MQL.
  2. Revisar os pesos do perceptron (X**) e PassMode para corresponder à configuração otimizada do MetaTrader.
  3. Ajustar TradeVolume para cumprir o tamanho mínimo e o passo do broker conectado.
  4. Monitorar o log: cada vez que o trailing stop avança ou um stop-loss é acionado, uma mensagem é registrada.
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;

public class GoldDustStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public GoldDustStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

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

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}