Ver en GitHub

Gold Dust Strategy

Overview

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

  1. Subscribe to the configured CandleType and compute a WeightedMovingAverage with the period taken from MaPeriod.
  2. 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.
  3. 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
  4. 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.
  5. 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.
  6. 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-lossStopLossPips 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

  1. Create or select a symbol, then set CandleType to the timeframe that corresponds to the historical chart used in the MQL version.
  2. Review the perceptron weights (X**) and PassMode to match the optimised configuration from MetaTrader. Each weight can be optimised independently inside StockSharp.
  3. 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.
  4. 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;
	}
}