Открыть на GitHub

Стратегия CDC PL RSI

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

CDC PL RSI Strategy переносит эксперта MetaTrader Expert_ADC_PL_RSI на платформу StockSharp. Алгоритм анализирует завершённые свечи, выявляет свечные разворотные модели и подтверждает сигналы индикатором RSI. Длинные позиции открываются после модели «Пробой облака тьмы» (Piercing Line) в сочетании с перепроданностью RSI, короткие — после «Покрывающей тёмное облако» (Dark Cloud Cover) в зоне перекупленности. Управление капиталом оставлено базовому механизму StockSharp: объём задаётся свойством Volume стратегии.

Логика индикаторов и фильтров

  • Свечные модели. Поведение полностью повторяет исходный MQL‑код: используются две последние завершённые свечи, проверяются разрывы цен, длина тел относительно скользящего среднего и направление тренда.
  • RSI. Осциллятор с периодом 20 (значение можно оптимизировать) служит для подтверждения импульса. Для лонга требуется RSI < 40, для шорта — RSI > 60. История значений дополнительно используется для фиксации позиции при обратных пересечениях уровней 30 и 70.
  • Среднее тело и тренд. Два индикатора SMA дублируют функции AvgBody и CloseAvg из MQL: первый усредняет длину тел, второй оценивает тренд закрытий. Это позволяет фильтровать шумовые свечи и гарантировать появление моделей после направленного движения.

Торговые правила

Вход в лонг

  1. Найти модель Piercing Line на двух последних свечах.
  2. Убедиться, что RSI предыдущей свечи ниже 40.
  3. Открыть покупку по рынку. При наличии короткой позиции стратегия переворачивается, покупая сумму текущего объёма и абсолютной величины позиции.

Вход в шорт

  1. Найти модель Dark Cloud Cover на двух последних свечах.
  2. Проверить, что RSI предыдущей свечи выше 60.
  3. Открыть продажу по рынку. При наличии длинной позиции выполняется разворот с тем же объёмным правилом.

Выход

  • Закрывать лонг, когда RSI опускается ниже 70 или поднимается выше 30, сигнализируя об окончании импульса.
  • Закрывать шорт, когда RSI поднимается выше 30 или опускается ниже 70, что соответствует логике оригинального эксперта.

Параметры

Имя Значение по умолчанию Описание
RsiPeriod 20 Период RSI. Допускается оптимизация в диапазоне 10–40 с шагом 5.
BodyAveragePeriod 14 Период сглаживания длины тел и трендового фильтра по закрытиям. Оптимизируется в диапазоне 10–30 с шагом 5.
CandleType Таймфрейм 1 час Тип свечей для расчёта. Можно выбрать любой поддерживаемый формат StockSharp.
Volume (устанавливается пользователем) Базовый объём заявки, задаётся перед запуском стратегии.

Использование

  1. Привяжите стратегию к нужным портфелю и инструменту в Designer, Shell или Runner.
  2. Настройте таймфрейм свечей и объём сделок под специфику инструмента.
  3. При необходимости измените периоды RSI и скользящей средней или запустите оптимизацию.
  4. Запустите стратегию и контролируйте сигналы через наложенные на график свечи, линию RSI и линию среднего закрытия.

Дополнительно

  • Метод StartProtection() активирован, поэтому можно задействовать стандартные защитные механизмы (стоп-лосс, тейк-профит, трейлинг и т.п.).
  • Обрабатываются только полностью сформированные свечи — логика совпадает с реализацией в MetaTrader.
  • Хранение больших коллекций не требуется: все расчёты выполняются индикаторами, что соответствует требованиям конвертации.
namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// CDC PL RSI strategy: Dark Cloud Cover and Piercing Line candlestick patterns
/// confirmed by RSI levels.
/// </summary>
public class CdcPlRsiStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _oversoldLevel;
	private readonly StrategyParam<decimal> _overboughtLevel;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private readonly List<ICandleMessage> _candles = new();
	private decimal _prevRsi;
	private bool _hasPrevRsi;
	private int _candlesSinceTrade;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public decimal OversoldLevel { get => _oversoldLevel.Value; set => _oversoldLevel.Value = value; }
	public decimal OverboughtLevel { get => _overboughtLevel.Value; set => _overboughtLevel.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public CdcPlRsiStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period", "Indicators");
		_oversoldLevel = Param(nameof(OversoldLevel), 40m)
			.SetDisplay("Oversold Level", "RSI below this for long entry", "Signals");
		_overboughtLevel = Param(nameof(OverboughtLevel), 60m)
			.SetDisplay("Overbought Level", "RSI above this for short entry", "Signals");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_candles.Clear();
		_prevRsi = 0m;
		_hasPrevRsi = false;
		_candlesSinceTrade = SignalCooldownCandles;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_candles.Clear();
		_hasPrevRsi = false;
		_candlesSinceTrade = SignalCooldownCandles;
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(rsi, ProcessCandle).Start();

		StartProtection(
			takeProfit: new Unit(2, UnitTypes.Percent),
			stopLoss: new Unit(1, UnitTypes.Percent)
		);
	}

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

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		_candles.Add(candle);
		if (_candles.Count > 5)
			_candles.RemoveAt(0);

		if (_candles.Count >= 2 && _hasPrevRsi)
		{
			var curr = _candles[^1];
			var prev = _candles[^2];

			// Piercing Line
			var isPiercing = prev.OpenPrice > prev.ClosePrice
				&& curr.ClosePrice > curr.OpenPrice
				&& curr.OpenPrice < prev.LowPrice
				&& curr.ClosePrice > (prev.OpenPrice + prev.ClosePrice) / 2m;

			// Dark Cloud Cover
			var isDarkCloud = prev.ClosePrice > prev.OpenPrice
				&& curr.OpenPrice > curr.ClosePrice
				&& curr.OpenPrice > prev.HighPrice
				&& curr.ClosePrice < (prev.OpenPrice + prev.ClosePrice) / 2m;

			if (isPiercing && rsiValue < OversoldLevel && Position == 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
			else if (isDarkCloud && rsiValue > OverboughtLevel && Position == 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
		}

		_prevRsi = rsiValue;
		_hasPrevRsi = true;
	}
}