GitHub で見る

KWAN RDP Trend Strategy

This strategy is a StockSharp conversion of the MetaTrader expert Exp_KWAN_RDP. The logic calculates the KWAN RDP oscillator by combining three standard indicators and smoothing their product:

  1. DeMarker – measures the relationship between recent highs and lows to gauge momentum exhaustion.
  2. Money Flow Index – evaluates price and volume to detect overbought or oversold conditions.
  3. Momentum – captures the speed of price changes using the selected period.
  4. The raw value 100 * DeMarker * MFI / Momentum is smoothed with a configurable moving average (SMA, EMA, SMMA, WMA, or Jurik).

The slope of the smoothed oscillator produces trade signals:

  • Bullish turn (rising slope): close short positions and optionally open a long position.
  • Bearish turn (falling slope): close long positions and optionally open a short position.
  • Neutral bars (flat slope) do not trigger actions.

Parameters

  • CandleType – candle series for indicator calculations (default: H1 time frame).
  • DeMarkerPeriod – period of the DeMarker indicator.
  • MfiPeriod – period of the Money Flow Index.
  • MomentumPeriod – period of the Momentum indicator.
  • SmoothingLength – length of the smoothing moving average.
  • Smoothing – smoothing method (Simple, Exponential, Smoothed, Weighted, Jurik).
  • EnableLongEntries / EnableShortEntries – allow opening long or short positions.
  • CloseLongsOnReverse / CloseShortsOnReverse – close opposing positions when a reversal signal appears.
  • TakeProfitPercent / StopLossPercent – optional percentage-based protection applied through StartProtection.

Trading Rules

  1. Subscribe to the configured candle series and calculate DeMarker, MFI, Momentum, and the smoothed KWAN value on each finished candle.
  2. Detect the slope direction of the latest oscillator value versus the previous one.
  3. When the slope turns up, close shorts (if enabled) and open a long if long trading is allowed and no long position is active.
  4. When the slope turns down, close longs (if enabled) and open a short if short trading is allowed and no short position is active.
  5. Use the optional stop-loss and take-profit percentages to guard positions with platform protection.

Notes

  • Signals are only processed on completed candles to avoid intra-bar noise.
  • The DeMarker calculation uses internal smoothing to match the MetaTrader implementation.
  • All comments in the C# code are written in English as required by the project guidelines.
namespace StockSharp.Samples.Strategies;

using System;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// KWAN RDP trend strategy converted from MetaTrader version.
/// Combines DeMarker and Momentum indicators to detect trend reversals.
/// Opens long when DeMarker rises and momentum is positive, short when DeMarker falls and momentum is negative.
/// </summary>
public class KwanRdpStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _deMarkerPeriod;
	private readonly StrategyParam<int> _momentumPeriod;

	private decimal _prevDem;
	private decimal _prevMom;
	private bool _initialized;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int DeMarkerPeriod
	{
		get => _deMarkerPeriod.Value;
		set => _deMarkerPeriod.Value = value;
	}

	public int MomentumPeriod
	{
		get => _momentumPeriod.Value;
		set => _momentumPeriod.Value = value;
	}

	public KwanRdpStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Primary candle series", "General");

		_deMarkerPeriod = Param(nameof(DeMarkerPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("DeMarker Period", "DeMarker indicator length", "Indicators");

		_momentumPeriod = Param(nameof(MomentumPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Momentum Period", "Momentum indicator length", "Indicators");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevDem = 0m;
		_prevMom = 0m;
		_initialized = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevDem = 0;
		_prevMom = 0;
		_initialized = false;

		var deMarker = new DeMarker { Length = DeMarkerPeriod };
		var momentum = new Momentum { Length = MomentumPeriod };

		var subscription = SubscribeCandles(CandleType);

		subscription
			.Bind(deMarker, momentum, (ICandleMessage candle, decimal demValue, decimal momValue) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				if (!_initialized)
				{
					_prevDem = demValue;
					_prevMom = momValue;
					_initialized = true;
					return;
				}

				var demUp = demValue > _prevDem;
				var demDown = demValue < _prevDem;
				var momUp = momValue > _prevMom;
				var momDown = momValue < _prevMom;

				// Long when DeMarker and Momentum both turn up
				if (demUp && momUp && Position <= 0)
				{
					BuyMarket();
				}
				// Short when DeMarker and Momentum both turn down
				else if (demDown && momDown && Position >= 0)
				{
					SellMarket();
				}

				_prevDem = demValue;
				_prevMom = momValue;
			})
			.Start();

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