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:
DeMarker — mide la relación entre máximos y mínimos recientes para evaluar el agotamiento del momentum.
Money Flow Index — evalúa precio y volumen para detectar condiciones de sobrecompra o sobreventa.
Momentum — captura la velocidad de los cambios de precio usando el período seleccionado.
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
Suscribirse a la serie de velas configurada y calcular DeMarker, MFI, Momentum y el valor KWAN suavizado en cada vela terminada.
Detectar la dirección de la pendiente del último valor del oscilador frente al anterior.
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.
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.
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);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import DeMarker, Momentum
from StockSharp.Algo.Strategies import Strategy
class kwan_rdp_strategy(Strategy):
def __init__(self):
super(kwan_rdp_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Primary candle series", "General")
self._demarker_period = self.Param("DeMarkerPeriod", 14) \
.SetDisplay("DeMarker Period", "DeMarker indicator length", "Indicators")
self._momentum_period = self.Param("MomentumPeriod", 14) \
.SetDisplay("Momentum Period", "Momentum indicator length", "Indicators")
self._prev_dem = 0.0
self._prev_mom = 0.0
self._initialized = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def DeMarkerPeriod(self):
return self._demarker_period.Value
@property
def MomentumPeriod(self):
return self._momentum_period.Value
def OnReseted(self):
super(kwan_rdp_strategy, self).OnReseted()
self._prev_dem = 0.0
self._prev_mom = 0.0
self._initialized = False
def OnStarted2(self, time):
super(kwan_rdp_strategy, self).OnStarted2(time)
self._prev_dem = 0.0
self._prev_mom = 0.0
self._initialized = False
demarker = DeMarker()
demarker.Length = self.DeMarkerPeriod
momentum = Momentum()
momentum.Length = self.MomentumPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(demarker, momentum, self._on_process) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, demarker)
self.DrawOwnTrades(area)
def _on_process(self, candle, dem_value, mom_value):
if candle.State != CandleStates.Finished:
return
dv = float(dem_value)
mv = float(mom_value)
if not self._initialized:
self._prev_dem = dv
self._prev_mom = mv
self._initialized = True
return
dem_up = dv > self._prev_dem
dem_down = dv < self._prev_dem
mom_up = mv > self._prev_mom
mom_down = mv < self._prev_mom
if dem_up and mom_up and self.Position <= 0:
self.BuyMarket()
elif dem_down and mom_down and self.Position >= 0:
self.SellMarket()
self._prev_dem = dv
self._prev_mom = mv
def CreateClone(self):
return kwan_rdp_strategy()