Ver en GitHub

Estrategia de Tendencia KWAN RDP

Esta estrategia es una conversión StockSharp del experto MetaTrader Exp_KWAN_RDP. La lógica calcula el oscilador KWAN RDP combinando tres indicadores estándar y suavizando su producto:

  1. DeMarker — mide la relación entre máximos y mínimos recientes para evaluar el agotamiento del momentum.
  2. Money Flow Index — evalúa precio y volumen para detectar condiciones de sobrecompra o sobreventa.
  3. Momentum — captura la velocidad de los cambios de precio usando el período seleccionado.
  4. El valor bruto 100 * DeMarker * MFI / Momentum se suaviza con una media móvil configurable (SMA, EMA, SMMA, WMA o Jurik).

La pendiente del oscilador suavizado produce señales de trading:

  • Giro alcista (pendiente ascendente): cerrar posiciones cortas y opcionalmente abrir una posición larga.
  • Giro bajista (pendiente descendente): cerrar posiciones largas y opcionalmente abrir una posición corta.
  • Las barras neutrales (pendiente plana) no desencadenan acciones.

Parámetros

  • CandleType — serie de velas para los cálculos de indicadores (predeterminado: marco temporal H1).
  • DeMarkerPeriod — período del indicador DeMarker.
  • MfiPeriod — período del Money Flow Index.
  • MomentumPeriod — período del indicador Momentum.
  • SmoothingLength — longitud de la media móvil de suavizado.
  • Smoothing — método de suavizado (Simple, Exponential, Smoothed, Weighted, Jurik).
  • EnableLongEntries / EnableShortEntries — permite abrir posiciones largas o cortas.
  • CloseLongsOnReverse / CloseShortsOnReverse — cerrar posiciones opuestas cuando aparece una señal de reversión.
  • TakeProfitPercent / StopLossPercent — protección opcional basada en porcentaje aplicada a través de StartProtection.

Reglas de trading

  1. Suscribirse a la serie de velas configurada y calcular DeMarker, MFI, Momentum y el valor KWAN suavizado en cada vela terminada.
  2. Detectar la dirección de la pendiente del último valor del oscilador frente al anterior.
  3. Cuando la pendiente sube, cerrar cortos (si está habilitado) y abrir un largo si el trading largo está permitido y no hay posición larga activa.
  4. Cuando la pendiente baja, cerrar largos (si está habilitado) y abrir un corto si el trading corto está permitido y no hay posición corta activa.
  5. Usar los porcentajes opcionales de stop-loss y take-profit para proteger las posiciones con protección de la plataforma.

Notas

  • Las señales solo se procesan en velas completadas para evitar el ruido intrabarra.
  • El cálculo de DeMarker utiliza suavizado interno para coincidir con la implementación de MetaTrader.
  • Todos los comentarios en el código C# están escritos en inglés según las directrices del proyecto.
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);
		}
	}
}