GitHub で見る

2526 TDI-2 ReOpen戦略

概要

この戦略はMetaTrader 5のエキスパートアドバイザー Exp_TDI-2_ReOpen のC#変換版です。Trend Direction Index(TDI-2)インジケーターを使用してトレードし、元のポジション再エントリーロジックを適用します。C#ポートはStockSharpの高レベルAPIを使用し、MQLバージョンのコア動作を維持します:TDIモメンタムラインとTDIインデックスラインのクロスに反応し、設定可能な価格前進後に利益ポジションをスケールアップし、オプションの保護ストップでトレードを管理します。

インジケーター

  • TDI-2インジケーター – このリポジトリに実装されたカスタムのモメンタムベースインジケーター。2本のラインを構築します:
    • 方向ライン期間 × スムーズドモメンタム、モメンタムは適用価格から期間バー前の価格を引いたもの。
    • インデックスライン|方向| − (2 × 期間 × スムーズ(|モメンタム|, 2×期間) − |モメンタム|)
  • インジケーターは以下のスムージングメソッドをサポート:単純、指数、スムーズ(RMA)、線形加重移動平均。
  • サポートされている適用価格オプションは、TrendFollowとDemarkの式を含む元のMQL実装を再現します。

トレードロジック

  1. 完了したローソク足ごとに、戦略は Signal Bar(デフォルト:直前の閉じたローソク足)で指定されたバーとその1本前のTDI-2値を評価します。
  2. 方向ラインがインデックスラインの上にあって、その後下方クロスした場合:
    • Allow Long Entries が有効でアクティブなロングポジションがない場合、戦略は新規ロングエントリーを準備します。
    • ショートポジションが存在して Allow Short Exits が有効な場合、ショートポジションを決済します。
  3. 方向ラインがインデックスラインの下にあって、その後上方クロスした場合:
    • Allow Short Entries が有効でアクティブなショートポジションがない場合、戦略は新規ショートエントリーを準備します。
    • ロングポジションが存在して Allow Long Exits が有効な場合、ロングポジションを決済します。
  4. 再エントリーロジック(スケールイン):
    • ロングポジションを保有中、戦略は最後のロングトレードの約定価格を追跡します。市場が Re-entry Step (points) だけ有利に動き、実行されたロングトレード数がまだ Max Entries を下回っている場合、ベースボリュームで追加のロング注文を開きます。
    • 同じロジックが、最新のショート約定価格を使用してショートポジションに適用されます。
  5. 反対ポジションが存在する状態でポジションを開く場合、戦略は反対のエクスポージャーを閉じて設定されたベースボリュームで新規ポジションを確立するために、サイズを調整した合成成行注文を送信します。
  6. オプションのストップロスとテイクプロフィットレベルは、インストゥルメントのPriceStep乗数を使用してStartProtectionを通じてアクティブになります。

パラメーター

名前 説明 デフォルト値
Money Management ベース注文ボリューム。 0.1
Max Entries 方向ごとの最大エントリー数(初回トレード+再エントリー)。 10
Stop Loss (points) インストゥルメントポイントでのストップロス距離。 1000
Take Profit (points) インストゥルメントポイントでのテイクプロフィット距離。 2000
Slippage (points) 互換性のために保持;StockSharp実装では使用されない。 10
Re-entry Step (points) 既存ポジションにスケールインする前の最小有利な動き。 300
Allow Long Entries / Allow Short Entries ロング/ショートポジションのオープンを有効化。 true
Allow Long Exits / Allow Short Exits ロング/ショートポジションのクローズを有効化。 true
Candle Type 計算に使用するローソク足シリーズ。 H4ローソク足
TDI Smoothing TDI-2インジケーターのスムージングメソッド。 単純MA
TDI Period モメンタムのルックバック期間。 20
TDI Phase MQL入力との互換性のために予約済み(サポートされているスムージングモードでは効果なし)。 15
Applied Price TDI-2が使用する価格ソース。 Close
Signal Bar クロスを評価する際に振り返る閉じたローソク足の数。 1

追加メモ

  • StockSharpインジケーターでサポートされているスムージングメソッド(SMA、EMA、SMMA、LWMA)のみが実装されています。JJMAやT3などの他のMQLモードは利用できません。
  • TDI Phase パラメーターは完全性のために保持されています。サポートされているスムージングメソッドには影響せず、デフォルト値のままにしておけます。
  • Slippage (points) パラメーターは元のエキスパートアドバイザーとの同等性のために提供されていますが、高レベルAPIでは使用されません。
  • スケールインカウンターは、ネットポジションがゼロに戻ると自動的にリセットされます。
using System;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Trend Direction Index re-entry strategy.
/// Trades based on crossings between the TDI momentum line and the TDI index line.
/// </summary>
public class Tdi2ReOpenStrategy : Strategy
{
	private readonly StrategyParam<int> _tdiPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _lastClose;
	private decimal? _directional;
	private decimal? _index;
	private decimal? _prevDirectional;
	private decimal? _prevIndex;

	public int TdiPeriod { get => _tdiPeriod.Value; set => _tdiPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public Tdi2ReOpenStrategy()
	{
		_tdiPeriod = Param(nameof(TdiPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("TDI Period", "Momentum lookback period", "Indicator")
			.SetOptimize(5, 30, 5);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Data series", "General");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_lastClose = null;
		_directional = null;
		_index = null;
		_prevDirectional = null;
		_prevIndex = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_lastClose = null;
		_directional = null;
		_index = null;
		_prevDirectional = null;
		_prevIndex = null;

		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ProcessCandle).Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}

		void ProcessCandle(ICandleMessage candle)
		{
			if (candle.State != CandleStates.Finished)
				return;

			var close = candle.ClosePrice;
			if (_lastClose is not decimal lastClose)
			{
				_lastClose = close;
				return;
			}

			var momentum = close - lastClose;
			_lastClose = close;
			var alpha = 2m / (TdiPeriod + 1m);

			if (_directional is not decimal prevDirectionalLine || _index is not decimal prevIndexLine)
			{
				_directional = momentum;
				_index = momentum;
				return;
			}

			var directional = prevDirectionalLine + alpha * (momentum - prevDirectionalLine);
			var index = prevIndexLine + alpha * (directional - prevIndexLine);

			if (_prevDirectional is not decimal prevDir || _prevIndex is not decimal prevIdx)
			{
				_directional = directional;
				_index = index;
				_prevDirectional = prevDirectionalLine;
				_prevIndex = prevIndexLine;
				return;
			}

			var crossUp = prevDir <= prevIdx && directional > index;
			var crossDown = prevDir >= prevIdx && directional < index;

			if (crossUp && Position <= 0)
				BuyMarket();
			else if (crossDown && Position >= 0)
				SellMarket();

			_directional = directional;
			_index = index;
			_prevDirectional = directional;
			_prevIndex = index;
		}
	}
}