Открыть на GitHub

Стратегия Expert ADC PL Stoch

Описание

Expert ADC PL Stoch — это конвертация экспертной системы Expert_ADC_PL_Stoch с платформы MQL5. Стратегия анализирует свечные комбинации Piercing Line и Dark Cloud Cover, а также использует линию %D индикатора Stochastic для подтверждения входов и выхода из позиций. Таким образом воспроизводится исходная «голосующая» логика оригинального эксперта.

Алгоритм работы

  1. Стратегия подписывается на свечи указанного таймфрейма (по умолчанию 1 час).
  2. Для каждой закрытой свечи сохраняются последние значения свечей и %D Stochastic.
  3. Открытие длинной позиции
    • Свечи t-1 и t-2 формируют модель Piercing Line:
      • Свеча t-1 — длинная бычья, её тело больше среднего тела.
      • Свеча t-2 — длинная медвежья, её тело также больше среднего.
      • Бычья свеча открывается с гэпом вниз и закрывается внутри тела предыдущей свечи, а скользящее среднее по закрытиям указывает на нисходящий тренд.
    • Значение %D на свече t-1 ниже порога для лонга (30).
  4. Открытие короткой позиции
    • Свечи t-2 и t-1 формируют модель Dark Cloud Cover:
      • Свеча t-2 — длинная бычья.
      • Свеча t-1 открывается выше максимума предыдущей свечи и закрывается внутри её тела.
      • Среднее значение цены свечи t-1 выше среднего закрытия, что подтверждает предыдущий восходящий тренд.
    • Значение %D на свече t-1 выше порога для шорта (70).
  5. Закрытие позиций
    • Лонг закрывается, если %D на свече t-1 пересекает сверху вниз уровни 80 или 20 относительно свечи t-2.
    • Шорт закрывается, если %D на свече t-1 пересекает снизу вверх уровни 20 или 80 относительно свечи t-2.
  6. Все расчёты выполняются только по завершённым свечам, без внутрисессионных сигналов.

Параметры

Параметр Описание Значение по умолчанию
CandleType Таймфрейм свечей для анализа. 1 час
StochasticLength Базовый период индикатора Stochastic. 47
StochasticKPeriod Период сглаживания линии %K. 9
StochasticDPeriod Период сглаживания линии %D. 13
StochasticSlow Дополнительное сглаживание Stochastic. 3
AverageBodyPeriod Количество свечей для расчёта среднего тела и среднего закрытия. 5
LongEntryThreshold Максимальное значение %D перед входом в лонг. 30
ShortEntryThreshold Минимальное значение %D перед входом в шорт. 70
ExitLowerThreshold Нижний уровень для выхода. 20
ExitUpperThreshold Верхний уровень для выхода. 80

Управление рисками

  • Сделки исполняются рыночными ордерами с базовым объёмом (по умолчанию 1 лот).
  • Защитные ордера не выставляются автоматически; при необходимости можно добавить StartProtection.
  • Одновременно удерживается только одна позиция; при смене сигнала текущая позиция закрывается и открывается новая в противоположную сторону.

Дополнительные замечания

  • Средние значения по телам свечей и закрытиям рассчитываются по истории, чтобы повторить логику MQL5-версии.
  • Значения %D хранятся для каждой закрытой свечи и используются с теми же смещениями, что и в оригинальном коде.
  • Торговля разрешена только после полной инициализации стратегии и прохождения стандартных проверок IsFormedAndOnlineAndAllowTrading().
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>
/// Expert ADC PL Stoch strategy: Dark Cloud Cover and Piercing Line patterns
/// with Stochastic oscillator confirmation for entries and exits.
/// </summary>
public class ExpertAdcPlStochStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _stochPeriod;
	private readonly StrategyParam<decimal> _longThreshold;
	private readonly StrategyParam<decimal> _shortThreshold;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private readonly List<ICandleMessage> _candles = new();
	private decimal _prevSignal;
	private int _candlesSinceTrade;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int StochPeriod { get => _stochPeriod.Value; set => _stochPeriod.Value = value; }
	public decimal LongThreshold { get => _longThreshold.Value; set => _longThreshold.Value = value; }
	public decimal ShortThreshold { get => _shortThreshold.Value; set => _shortThreshold.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public ExpertAdcPlStochStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_stochPeriod = Param(nameof(StochPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Stoch Period", "Stochastic period", "Indicators");
		_longThreshold = Param(nameof(LongThreshold), 30m)
			.SetDisplay("Long Threshold", "Stochastic below this for long", "Signals");
		_shortThreshold = Param(nameof(ShortThreshold), 70m)
			.SetDisplay("Short Threshold", "Stochastic above this for short", "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();
		_prevSignal = 0m;
		_candlesSinceTrade = SignalCooldownCandles;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_candles.Clear();
		_candlesSinceTrade = SignalCooldownCandles;
		var stoch = new StochasticOscillator { K = { Length = StochPeriod }, D = { Length = 3 } };
		var subscription = SubscribeCandles(CandleType);
		subscription.BindEx(stoch, ProcessCandle).Start();

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

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

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		var stochTyped = stochValue as StochasticOscillatorValue;
		if (stochTyped?.K is not decimal kValue) return;

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

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

			// Piercing Line: bearish prev + bullish curr that closes above prev midpoint
			var isPiercing = prev.OpenPrice > prev.ClosePrice
				&& curr.ClosePrice > curr.OpenPrice
				&& curr.OpenPrice < prev.LowPrice
				&& curr.ClosePrice > (prev.OpenPrice + prev.ClosePrice) / 2m;

			// Dark Cloud Cover: bullish prev + bearish curr that closes below prev midpoint
			var isDarkCloud = prev.ClosePrice > prev.OpenPrice
				&& curr.OpenPrice > curr.ClosePrice
				&& curr.OpenPrice > prev.HighPrice
				&& curr.ClosePrice < (prev.OpenPrice + prev.ClosePrice) / 2m;

			if (isPiercing && kValue < LongThreshold && Position == 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
			else if (isDarkCloud && kValue > ShortThreshold && Position == 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
		}

		_prevSignal = kValue;
	}
}