Открыть на GitHub

Стратегия Trend RDS

Общее описание

Trend RDS — сессионная контртрендовая стратегия, изначально написанная для MetaTrader. В момент заданного старта она анализирует до 100 последних закрытых свечей, ищет последовательность из трёх баров с направленным импульсом и открывает позицию в противоположную сторону. Порт на StockSharp сохраняет исходное управление рисками: опцию инверсии сигналов, фиксированные уровни стоп-лосса и тейк-профита, перевод в безубыток и трейлинг-стоп с настраиваемым шагом.

Логика торговли

  1. Окно сигналов – начиная с Start Time стратегия обрабатывает только завершённые свечи.
  2. Поиск паттерна – выбирается первая тройка свечей, удовлетворяющая одному из условий:
    • Максимумы растут, минимумы растут (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).

Параметры

Имя Описание
Trade Volume Объём рыночной заявки (лоты или контракты).
Stop Loss (pips) Дистанция защитного стопа; 0 — отключить.
Take Profit (pips) Дистанция тейк-профита; 0 — отключить.
Start Time Время начала поиска паттернов (по времени биржи).
Close Time Время принудительного закрытия позиций (инструмент закрывается в течение 15 минут).
Reverse Signals Инвертировать направления сигналов.
Trailing Stop (pips) Базовое расстояние трейлинг-стопа, 0 — отключить.
Trailing Step (pips) Дополнительный ход цены для обновления трейлинг-стопа.
Break-Even (pips) Порог прибыли для перевода стопа в безубыток, 0 — отключить.
Candle Type Тип свечей, используемых в расчётах.

Практические замечания

  • Для вычисления расстояний по пунктам используется PriceStep или MinPriceStep инструмента. Убедитесь, что у бумаги задан корректный шаг цены.
  • Стратегия обрабатывает только закрытые свечи, поэтому сигнал может появиться не чаще одного раза в день на выбранном таймфрейме.
  • При изменении объёма позиции защитные заявки переставляются, сохраняя актуальные уровни.
  • Трейлинг и безубыток работают только при наличии валидной цены входа.

Состав репозитория

  • CS/TrendRdsStrategy.cs — реализация стратегии на C#.
  • README.md — документация на английском языке.
  • README_zh.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);
	}
}