GitHub で見る

トレンド RDS 戦略

概要

Trend RDS は、もともと MetaTrader 用に作成されたセッションベースの反転戦略です。指定された取引ウィンドウの開始時に 3 本の足のモメンタム形成をスキャンし、反対方向にエントリーすることで動きを弱めます。 StockSharp ポートは、オプションのシグナル反転、固定ストップロスとテイクプロフィットレベル、損益分岐点保護、調整可能なステップサイズを持つトレーリングストップなど、元の資金管理ロジックを保持しています。

取引ロジック

  1. シグナル ウィンドウ – 設定された Start Time で、戦略は最近閉じた最大 100 個のローソク足を検査します。
  2. パターン検出 – 次のいずれかの場合に、連続する 3 つのバーの最初のシーケンスを検索します。
    • 高値は上昇し、安値は上昇します (High[n] < High[n+1] < High[n+2]Low[n] > Low[n+1] > Low[n+2])。
    • 高値は下がり、安値は下がります (High[n] > High[n+1] > High[n+2]Low[n] < Low[n+1] < Low[n+2])。 両方向への対称的な展開は競合として扱われ、無視されます。 Reverse Signals が有効な場合、信号の方向はオプションで反転されます。
  3. エントリ – オープンポジションがない場合、ストラテジーは設定された Trade Volume を使用して成行注文を送信します。反対側のポジションがまだ開いている場合は、最初に閉じられます。
  4. 強制終了ウィンドウClose Time からその後 15 分の間に、残りのポジションは清算されます。
  5. プロテクション – ポジションがオープンになると、戦略が次のように登録されます。
    • 要求されたピップ距離でのストップロスとテイクプロフィット注文。
    • Break-Even (pips) に達した後にストップをエントリー価格に移動する損益分岐点トリガー。
    • 現在の価格から Trailing Stop (pips) の距離を保ち、さらに Trailing Step (pips) が移動した後にのみ前進するトレーリング ストップ。

パラメーター

名前 説明
取引高 ロットまたは契約で表される成行注文のサイズ。
ストップロス (pips) 保護停止までの距離。無効にするには、ゼロに設定します。
利益確定 (pips) 利益目標までの距離。無効にするには、ゼロに設定します。
開始時間 パターン検索を開始する時刻(交換時刻)。
閉店時間 すべての開いている取引が 15 分以内に終了する時刻 (為替時間)。
リバース信号 長いエントリと短いエントリを反転します。
トレーリングストップ (pips) 基本トレーリング距離。ゼロは末尾を無効にします。
トレーリングステップ (pips) トレーリングストップが再び更新される前に追加の動きが必要です。
損益分岐点 (pips) ストップをエントリー価格に移動するための利益のしきい値。ゼロを指定すると、この機能が無効になります。
キャンドルタイプ 分析に使用したキャンドル シリーズ。

実用上の注意

  • この戦略は、商品の価格ステップに基づいてピップ距離を計算します。セキュリティが有効な PriceStep または MinPriceStep 値を公開していることを確認してください。
  • 完成したローソク足のみが処理されるため、シグナルは時間枠ごとに 1 日に最大 1 回表示されます。
  • ストップ注文とテイクプロフィット注文はポジションサイズが変更されるたびに更新され、部分約定でも一貫した保護が確保されます。
  • トレーリングおよび損益分岐点ロジックは、ポジションがオープンで有効なエントリー価格がわかっている場合にのみ有効になります。

ファイル

  • CS/TrendRdsStrategy.cs – StockSharp 戦略の C# 実装。
  • README_zh.md – 中国語のドキュメント。
  • README_ru.md – ロシア語のドキュメント。
using System;
using System.Linq;
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;

/// <summary>
/// Port of the MetaTrader Trend_RDS expert advisor.
/// Detects three-bar momentum patterns and reverses into the move.
/// Includes configurable stop-loss, take-profit, and trailing management.
/// </summary>
public class TrendRdsReversalStrategy : Strategy
{
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<decimal> _stopLossPips;
	private readonly StrategyParam<decimal> _takeProfitPips;
	private readonly StrategyParam<bool> _reverseSignals;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _maxPatternDepth;

	private readonly List<(decimal High, decimal Low)> _recentExtremes = new();

	/// <summary>
	/// Trading volume for market entries.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set => _tradeVolume.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in absolute price units.
	/// </summary>
	public decimal StopLossPips
	{
		get => _stopLossPips.Value;
		set => _stopLossPips.Value = value;
	}

	/// <summary>
	/// Take-profit distance in absolute price units.
	/// </summary>
	public decimal TakeProfitPips
	{
		get => _takeProfitPips.Value;
		set => _takeProfitPips.Value = value;
	}

	/// <summary>
	/// Inverts the buy and sell conditions when enabled.
	/// </summary>
	public bool ReverseSignals
	{
		get => _reverseSignals.Value;
		set => _reverseSignals.Value = value;
	}

	/// <summary>
	/// Candle type processed by the strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Maximum number of swings tracked when validating the pattern.
	/// </summary>
	public int MaxPatternDepth
	{
		get => _maxPatternDepth.Value;
		set => _maxPatternDepth.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="TrendRdsReversalStrategy"/> class.
	/// </summary>
	public TrendRdsReversalStrategy()
	{
		_tradeVolume = Param(nameof(TradeVolume), 1m)
			.SetGreaterThanZero()
			.SetDisplay("Trade Volume", "Market order volume", "General");

		_stopLossPips = Param(nameof(StopLossPips), 500m)
			.SetDisplay("Stop Loss", "Stop-loss distance", "Risk");

		_takeProfitPips = Param(nameof(TakeProfitPips), 500m)
			.SetDisplay("Take Profit", "Take-profit distance", "Risk");

		_reverseSignals = Param(nameof(ReverseSignals), false)
			.SetDisplay("Reverse Signals", "Invert buy and sell signals", "Filters");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
			.SetDisplay("Candle Type", "Working timeframe", "General");

		_maxPatternDepth = Param(nameof(MaxPatternDepth), 10)
			.SetGreaterThanZero()
			.SetDisplay("Max Pattern Depth", "Maximum candles tracked for pattern detection", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return new[] { (Security, CandleType) };
	}

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

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		// Use a dummy EMA to ensure candle callbacks fire in the backtester
		var ema = new EMA { Length = 5 };

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

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

		// Use StartProtection for SL/TP
		var tp = TakeProfitPips > 0 ? new Unit(TakeProfitPips, UnitTypes.Absolute) : null;
		var sl = StopLossPips > 0 ? new Unit(StopLossPips, UnitTypes.Absolute) : null;
		StartProtection(tp, sl);

		base.OnStarted2(time);
	}

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

		// Track recent highs and lows
		_recentExtremes.Insert(0, (candle.HighPrice, candle.LowPrice));
		if (_recentExtremes.Count > MaxPatternDepth + 2)
			_recentExtremes.RemoveAt(_recentExtremes.Count - 1);

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		// Need at least 3 bars for the pattern
		if (_recentExtremes.Count < 3)
			return;

		var (buySignal, sellSignal) = DetectSignals();

		if (buySignal)
		{
			if (Position < 0)
				BuyMarket(Math.Abs(Position));
			if (Position <= 0)
				BuyMarket(Volume);
		}
		else if (sellSignal)
		{
			if (Position > 0)
				SellMarket(Position);
			if (Position >= 0)
				SellMarket(Volume);
		}
	}

	private (bool Buy, bool Sell) DetectSignals()
	{
		var depth = Math.Min(_recentExtremes.Count - 2, MaxPatternDepth);
		if (depth <= 0)
			return (false, false);

		for (var index = 0; index < depth; index++)
		{
			if (index + 2 >= _recentExtremes.Count)
				break;

			var first = _recentExtremes[index];
			var second = _recentExtremes[index + 1];
			var third = _recentExtremes[index + 2];

			// Conflict: both highs and lows rising simultaneously
			var conflict = first.High < second.High && second.High < third.High &&
				first.Low > second.Low && second.Low > third.Low;

			// Rising lows pattern -> buy
			if (!conflict && first.Low > second.Low && second.Low > third.Low)
			{
				return ReverseSignals ? (false, true) : (true, false);
			}

			// Rising highs pattern -> sell
			if (!conflict && first.High < second.High && second.High < third.High)
			{
				return ReverseSignals ? (true, false) : (false, true);
			}
		}

		return (false, false);
	}
}