GitHub で見る

TDS Global Pending戦略 (C#)

概要

この戦略は、MetaTrader 5のエキスパートアドバイザー TDSGlobalMQL/23255/TDSGlobal.mq5)をStockSharpの高レベルAPIにポートしたものです。MACD線、MACDヒストグラム(OsMA)、フォースインデックスを通じて4時間ローソク足のMomentumを評価します。インジケーターの組み合わせが潜在的な反転を示すと、前のローソク足の極値付近にペンディング指値注文を発注し、オプションのストップロス、テイクプロフィット、トレーリングストップロジックで結果のポジションを管理します。

実装は元のワークフローを再現しながら、StrategyParam<T>SubscribeCandlesによるローソク足サブスクリプション、戦略ライフサイクルイベントを通じた非同期注文処理などのStockSharp固有の構造に適応しています。

トレードロジック

  1. インジケーター計算
    • MACD(12, 26, 9) はMACD線とヒストグラム(OsMA)の両方を提供します。
    • ForceIndex(24) は最後に完成したローソク足の力を測定します。
    • 各インジケーターは選択したローソク足タイプのクローズ時に更新されます(デフォルト:4時間)。
  2. シグナル検出
    • アルゴリズムはMACDとOsMAの2つの履歴値が利用可能になるまで待機して傾きを判定します。
    • 売りセットアップはOsMAが増加し(osma[1] > osma[2])、前のローソク足のフォースインデックスが負であることを必要とします。
    • 買いセットアップはOsMAが減少し(osma[1] < osma[2])、前のフォースインデックスが正であることを必要とします。
  3. 注文配置
    • 売り指値注文は前のローソク足の高値のわずか上に配置され、買い指値注文は前のローソク足の安値のわずか下に配置されます。
    • 価格が現在のbid/askから十分に離れていない場合、注文価格は設定されたオフセットバッファ(EntryOffsetPips、デフォルト16 pips)に引き寄せられます。
    • 戦略は注文価格と現在のbid/askの間の距離がブローカーの安全レベル近似(MinDistancePipsまたはスプレッドベースの動的値)を超えることを確認します。
  4. リスク管理
    • オプションのストップロスとテイクプロフィットレベルは注文価格から計算されます。
    • ポジションがアクティブな場合、価格が初期トレーリング距離を超えると、設定されたステップだけトレーリングストップが前進できます。
    • ローソク足内で価格が保護レベルに達した場合、MetaTraderの動作を模倣するためにポジションは成行注文で決済されます。
  5. 注文メンテナンス
    • OsMAの傾きが元のセットアップに反転したとき、ソースEAのクリーンアップルーティンに合わせてペンディング注文がキャンセルされます。
    • 片側の約定は競合するエクスポージャーを避けるために、反対側のペンディング注文を自動的にキャンセルします。

資金管理

2つのポジションサイジングアプローチが利用可能です:

  • 固定ボリューム(デフォルト OrderVolume = 1)— 調整なしでベースの Strategy.Volume を使用します。
  • リスクベースのサイジングUseRiskSizing が有効な場合、戦略はポートフォリオの資産を推定し、設定されたリスク割合を通貨リスクに変換し、ストップロス距離で割って注文ボリュームを導出します。ボリュームは無効な注文サイズを避けるためにインストゥルメントのボリュームステップに揃えられます。

パラメーター

名前 説明 デフォルト
OrderVolume リスクサイジングが無効な場合の固定注文サイズ。 1
UseRiskSizing RiskPercent に基づく資金管理を有効化。 true
RiskPercent 取引ごとにリスクにさらすポートフォリオ資産の割合。 3
MacdFastPeriod MACD線の速いEMA長。 12
MacdSlowPeriod MACD線の遅いEMA長。 26
MacdSignalPeriod MACDヒストグラムのシグナルEMA長。 9
ForceLength フォースインデックスのEMA平滑化長。 24
StopLossPips pips単位のストップロス距離(0で無効)。 50
TakeProfitPips pips単位のテイクプロフィット距離(0で無効)。 50
TrailingStopPips pips単位のトレーリングストップ距離(0で無効)。 5
TrailingStepPips トレーリング更新の最小ステップ。 5
EntryOffsetPips ペンディング注文の前の高値/安値周辺に追加されるバッファ。 16
MinDistancePips 価格と保護レベルの間の最小許容距離。 3
PipSize pip-価格変換に使用されるpipサイズ。 0.0001
CandleType 戦略が処理するローソク足タイプ。 4時間ローソク足

使用上の注意

  1. CS/TdsGlobalPendingStrategy.cs ファイルをStockSharpプロジェクトに追加するか、Backtester環境を通じて動的に読み込みます。
  2. 戦略を開始する前に目的のインストゥルメントとポートフォリオを割り当てます。UseRiskSizing が有効な場合、ポートフォリオが現在の資産値を提供することを確認します。
  3. 戦略はMACD/OsMAの傾きを初期化するために少なくとも2つの完成したローソク足を必要とします。短いウォームアップフェーズが予想されます。
  4. 詳細な注文とポジションイベントのログを監視します。実装は元のEAの動作に対する検証を支援するために、主要なアクション(注文送信、キャンセル、トレーリング更新)を記録します。

MQLバージョンとの違い

  • 高レベルAPIは非同期注文イベントを管理するため、指値注文の約定は同期的な OrderSend の結果ではなく OnOwnTradeReceived を通じて処理されます。
  • ブローカーの「フリーズ」と「ストップ」レベルは、StockSharpがMetaTrader固有のトレーディング制限を公開しないため、設定された最小距離とスプレッドベースのヒューリスティックを使用して近似されます。
  • ローソク足が突破を示したとき、保護的な出口は成行注文を通じて実行されます。これはMT5トレードサーバーの制約に依存せずにEAの手動ストップ変更ロジックを再現します。

これらの調整により、戦略がStockSharpフレームワークとスムーズに統合されることを確保しながら、トレードロジックを忠実に維持します。

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 TdsGlobalPendingStrategy : 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 TdsGlobalPendingStrategy()
	{
		_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;
	}
}