View on GitHub

Expert ADC PL Stoch Strategy

Overview

The Expert ADC PL Stoch Strategy is a candlestick pattern strategy converted from the original MQL5 expert advisor Expert_ADC_PL_Stoch. It looks for bullish Piercing Line and bearish Dark Cloud Cover formations on finished candles and confirms the signals with the %D line of a Stochastic Oscillator. The method is trend-following when the market retraces into an established move and requires the oscillator to be in extreme zones before opening positions. Position exits are based on Stochastic crossovers out of extreme areas, mirroring the vote-based exit logic of the source system.

Trading Logic

  1. Subscribe to a configurable candle type (default: 1-hour time frame).
  2. For each finished candle, maintain the last candles needed for candlestick pattern evaluation and the recent Stochastic %D values.
  3. Long Entry
    • The previous candle pair must form a Piercing Line pattern:
      • Candle at bar t-1 is bullish with a body greater than the average body size.
      • Candle at bar t-2 is bearish with a body greater than the average.
      • The bullish candle gaps below the bearish low and closes back inside the bearish body while the overall trend is downward according to the close average.
    • The Stochastic %D value on bar t-1 must be below the long entry threshold (default 30).
  4. Short Entry
    • The previous candle pair must form a Dark Cloud Cover pattern:
      • Candle at bar t-2 is bullish with a large body.
      • Candle at bar t-1 opens above the previous high and closes back within the bullish body.
      • The mid-price of the bearish candle is above the moving average of closes, signalling an uptrend prior to the reversal.
    • The Stochastic %D on bar t-1 must be above the short entry threshold (default 70).
  5. Exit Conditions
    • Long positions are closed when the Stochastic %D on bar t-1 crosses below either the upper (80) or lower (20) thresholds compared with bar t-2.
    • Short positions are closed when the Stochastic %D on bar t-1 crosses above either the lower (20) or upper (80) thresholds compared with bar t-2.
  6. All calculations are performed on finished candles; no intrabar processing is used.

Parameters

Name Description Default
CandleType Time frame of candles used for pattern detection. 1 hour
StochasticLength Base length for the Stochastic oscillator. 47
StochasticKPeriod Smoothing length for the %K line. 9
StochasticDPeriod Smoothing length for the %D line. 13
StochasticSlow Additional slowing factor applied to the oscillator. 3
AverageBodyPeriod Number of candles used to measure the reference body size and close average. 5
LongEntryThreshold Maximum %D value allowed before entering long trades. 30
ShortEntryThreshold Minimum %D value required before entering short trades. 70
ExitLowerThreshold Lower boundary used for exit crossovers. 20
ExitUpperThreshold Upper boundary used for exit crossovers. 80

Risk Management

  • The strategy sends market orders using the base strategy volume (default 1 contract/lot).
  • No automatic protective orders are configured; external risk management or StartProtection can be added if needed.
  • Only one position is managed at a time; opposite signals close the active position before opening a new one.

Notes

  • Average candle bodies and close averages are computed from historical candles to replicate the MQL5 vote logic closely.
  • Stochastic values are stored per finished bar to evaluate the same offsets used in the original expert advisor.
  • Trades are opened and closed only when the strategy is fully formed and trading is allowed by the base class checks.
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;
	}
}