ホーム
/
戦略のサンプル
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:
DeMarker – measures the relationship between recent highs and lows to gauge momentum exhaustion.
Money Flow Index – evaluates price and volume to detect overbought or oversold conditions.
Momentum – captures the speed of price changes using the selected period.
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
Subscribe to the configured candle series and calculate DeMarker, MFI, Momentum, and the smoothed KWAN value on each finished candle.
Detect the slope direction of the latest oscillator value versus the previous one.
When the slope turns up, close shorts (if enabled) and open a long if long trading is allowed and no long position is active.
When the slope turns down, close longs (if enabled) and open a short if short trading is allowed and no short position is active.
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);
}
}
}
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()