GitHub で見る

Gold Dust戦略

概要

Gold Dust戦略はStockSharpフレームワーク内でMetaTrader 5エキスパートアドバイザー「Gold Dust」を再現します。加重ローソク価格に適用された線形加重移動平均(LWMA)から構築された最大2つのパーセプトロンを評価します。各パーセプトロンはMA期間で区切られた4つの異なるルックバックポイントで価格が移動平均からどのように逸脱するかを観察します。パーセプトロンの出力が正の場合、オリジナルのエキスパートは売りポジションを開き、負の場合は買いポジションを開きます。StockSharpポートは高レベルのローソク購読APIを利用しながら同じ動作を維持します。

シグナル生成

  1. 設定された CandleType を購読し、MaPeriod から取得した期間で WeightedMovingAverage を計算します。
  2. 各完了ローソクでローソクの始値と終値をLWMA値とともに保存します。戦略はMQLバージョンの CopyRates/CopyBuffer 呼び出しを反映するために常に3つの完全なMAピリオドの履歴を保持します。
  3. 価格/MAオフセットを計算:
    • a1 – 現在の終値から現在のLWMAを引いた値
    • a2 – 1MAピリオド前の始値からその時点のLWMAを引いた値
    • a3 – 2MAピリオド前の始値からその時点のLWMAを引いた値
    • a4 – 3MAピリオド前の始値からその時点のLWMAを引いた値
  4. パーセプトロン出力を計算 result = Σ (wi × ai)。各重みは生のパラメーター(例:X11)から100を引いたもので、オリジナルの w = x - 100 変換と一致します。
  5. PassMode に応じてパーセプトロン出力を解釈:
    • 1 – 最初のパーセプトロンのみ使用。
    • 2 – 2番目のパーセプトロンのみ使用。
    • 3 – 両方のパーセプトロンが同じゼロ以外の符号を生成することが必要。
  6. 負のシグナルはロングポジションを開くか維持し、正のシグナルはショートポジションを開くか維持し、ゼロシグナルは既存ポジションの利益確定を引き起こします。

ポジション管理

  • エントリー – 戦略は固定の TradeVolume で取引します。ロングに参入すると保留中のショート露出が決済され、その逆も同様で、方向性のあるポジションが1つだけ残ります。
  • ストップロスStopLossPipsSecurity.PriceStep を使用して絶対価格距離に変換されます。3桁または5桁で取引される銘柄の場合、距離はMQLバージョンの「調整ポイント」ロジックを模倣するために10倍されます。ストップは各完了ローソクで評価されます。
  • トレーリングストップTrailingStopPips がゼロより大きい場合、トレーリングロジックがアクティブになります。価格がトレードに有利な方向に TrailingStopPips + TrailingStepPips 移動した後、ストップは 終値 ± TrailingStopPips(方向に応じて)に設定されます。
  • 利益管理 – どのパーセプトロンも方向で合意しない場合(signal == 0)、戦略は浮動利益が正の場合にのみポジションを決済します。

パラメーター

パラメーター デフォルト 説明
TradeVolume 1 各新規エントリーのベースボリューム。反対ポジションは新しいサイドを取る前にフラットにされます。
StopLossPips 150 調整ピップでの初期ストップロス距離(3/5桁乗数を考慮)。初期ストップを無効にするには0に設定。
TrailingStopPips 25 調整ピップでのトレーリングストップ距離。トレーリングを無効にするには0に設定。
TrailingStepPips 5 トレーリングストップが進む前に必要な追加の有利な移動(ピップ単位)。
MaPeriod 20 パーセプトロンを供給する加重移動平均の期間長。
CandleType H1 シグナル評価に使用するローソクシリーズ。データプロバイダーがサポートする任意の時間軸を選択可能。
PassMode 1 評価するパーセプトロンを制御:1 – 最初、2 – 2番目、3 – 両方のコンセンサス。
X11, X21, X31, X41 100 パーセプトロン#1の生の重み。戦略はパーセプトロンで使用する前に各値から100を引きます。
X12, X22, X32, X42 100 パーセプトロン#2の生の重み、最初のセットと同様に処理されます。

変換に関する注記

  • オリジナルEAはティックごとの更新を利用してストップを管理していました;StockSharpポートはローソクの終値でストップとトレーリングを評価します。
  • CMoneyFixedMargin によるマネーマネジメントは固定の TradeVolume パラメーターに置き換えられました。
  • パーセプトロンの計算は、必要なローソクとMA値を有界なリストにキャッシュすることで直接のインジケーターバッファ(CopyBuffer)を避けます。
  • すべてのピップ距離はMetaTraderの「調整ポイント」規則を尊重します:銘柄が3桁または5桁で取引される場合、距離はを価格レベルに適用する前に10倍されます。

使用上のヒント

  1. シンボルを作成または選択し、MQLバージョンで使用された履歴チャートに対応する時間軸に CandleType を設定します。
  2. MetaTraderの最適化設定と一致させるためにパーセプトロンの重み(X**)と PassMode を確認します。
  3. 接続されたブローカーの最小ロットサイズとステップサイズに準拠するよう TradeVolume を調整します。
  4. ログを監視:トレーリングストップが進んだりストップロスが発動されるたびにメッセージが記録され、ポートがオリジナル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;
	}
}