The Gold Dust strategy reproduces the MetaTrader 5 expert advisor "Gold Dust" inside the StockSharp framework. It evaluates up to two perceptrons built from a linear weighted moving average (LWMA) applied to the weighted candle price. Each perceptron observes how the price deviates from the moving average at four different lookback points spaced by the MA period. When the perceptron output is positive the original expert opens a sell position, and when it is negative it opens a buy position. The StockSharp port keeps the same behaviour while relying on the high-level candle subscription API.
Signal generation
Subscribe to the configured CandleType and compute a WeightedMovingAverage with the period taken from MaPeriod.
On every finished candle store the candle's open and close prices together with the LWMA value. The strategy always keeps three full MA periods of history to mirror the CopyRates/CopyBuffer calls from the MQL version.
Calculate the price/MA offsets:
a1 – current close minus current LWMA
a2 – open price one MA period ago minus LWMA at the same candle
a3 – open price two MA periods ago minus LWMA at the same candle
a4 – open price three MA periods ago minus LWMA at the same candle
Build the perceptron output result = Σ (wi × ai) where each weight is the raw parameter (e.g. X11) minus 100, matching the original w = x - 100 transformation.
Interpret the perceptron outputs depending on PassMode:
1 – use the first perceptron only.
2 – use the second perceptron only.
3 – require both perceptrons to produce the same non-zero sign.
A negative signal opens or maintains a long position, a positive signal opens or maintains a short position, and a zero signal triggers profit-taking on existing positions.
Position management
Entries – the strategy trades with a fixed TradeVolume. Entering long closes any outstanding short exposure and vice versa so that only one directional position remains, matching the behaviour of m_need_open_buy/m_need_open_sell in the original code.
Stop-loss – StopLossPips is converted into absolute price distance using Security.PriceStep. For instruments quoted with three or five decimals the distance is multiplied by ten to mimic the "adjusted point" logic in the MQL version. The stop is evaluated on every completed candle: if the candle's low (for longs) or high (for shorts) crosses the stop level the position is closed with a market order.
Trailing stop – when TrailingStopPips is greater than zero the trailing logic becomes active. After the price moves by TrailingStopPips + TrailingStepPips in the trade's favour the stop is stepped to close ± TrailingStopPips (depending on direction). Trailing is candle-based and creates a stop even if the initial stop-loss was disabled, just like PositionModify in the EA.
Profit management – when no perceptron agrees on a direction (signal == 0) the strategy closes the position only if the floating profit is positive. This reproduces CloseProfitPositions where swaps, commissions, and profit must be greater than zero.
Parameters
Parameter
Default
Description
TradeVolume
1
Base volume for every new entry. Opposite positions are flattened before taking a new side.
StopLossPips
150
Initial stop-loss distance in adjusted pips (takes the 3/5-digit multiplier into account). Set to zero to disable the initial stop.
TrailingStopPips
25
Trailing stop distance in adjusted pips. Set to zero to disable trailing.
TrailingStepPips
5
Additional favourable move (in pips) required before the trailing stop is advanced.
MaPeriod
20
Period length of the weighted moving average that feeds the perceptrons.
CandleType
H1
Candle series used for signal evaluation. Any other timeframe supported by the data provider can be selected.
PassMode
1
Controls which perceptron(s) are evaluated: 1 – first, 2 – second, 3 – consensus of both.
X11, X21, X31, X41
100
Raw weights for perceptron #1. The strategy subtracts 100 from each value before using it in the perceptron.
X12, X22, X32, X42
100
Raw weights for perceptron #2, handled the same way as the first set.
Notes on the conversion
The original EA relied on tick-by-tick updates to manage stops; the StockSharp port evaluates stops and trailing on candle close. This keeps the implementation within the high-level API while remaining faithful to the overall logic.
Money-management via CMoneyFixedMargin was replaced with a fixed TradeVolume parameter. Users can integrate their own position-sizing logic if required.
Perceptron calculations avoid direct indicator buffers (CopyBuffer) by caching the necessary candle and MA values in bounded lists.
All pip distances respect the MetaTrader "adjusted point" convention: if the security trades with 3 or 5 decimals the distance is multiplied by ten before being applied to price levels.
Usage tips
Create or select a symbol, then set CandleType to the timeframe that corresponds to the historical chart used in the MQL version.
Review the perceptron weights (X**) and PassMode to match the optimised configuration from MetaTrader. Each weight can be optimised independently inside StockSharp.
Adjust TradeVolume so that it complies with the connected broker's minimum and step size. The strategy automatically adds the absolute opposite exposure when flipping direction.
Monitor the log: every time the trailing stop is advanced or a stop-loss is triggered a message is recorded, which helps verify that the port behaves like the original EA.
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()