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
Assinar o CandleType configurado e calcular um WeightedMovingAverage com o período de MaPeriod.
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.
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
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.
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.
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-loss – StopLossPips é 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
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.
Revisar os pesos do perceptron (X**) e PassMode para corresponder à configuração otimizada do MetaTrader.
Ajustar TradeVolume para cumprir o tamanho mínimo e o passo do broker conectado.
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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class gold_dust_strategy(Strategy):
def __init__(self):
super(gold_dust_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(gold_dust_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(gold_dust_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return gold_dust_strategy()