在 GitHub 上查看
Exp KWAN NRP 策略
概述
Exp KWAN NRP 策略通过将随机指标、相对强弱指数和动量指标组合成一个比率来复刻原版 MetaTrader 专家顾问。该比率再通过可配置的移动平均线进行平滑,平滑线的斜率决定何时开仓或平仓。策略适用于任意品种和周期,专注在动量发生拐点时捕捉方向性机会。
交易逻辑
- 计算 KWAN 比率:使用随机指标 %D 线与 RSI 数值相乘,再除以动量指标的读数。
- 通过可选择的移动平均(简单、指数、平滑、加权)对比率进行平滑。
- 在可配置的信号栏位处评估平滑线的斜率。
- 当平滑线向上转折时开多并平空;当平滑线向下转折时开空并平多。
- 可选的止损/止盈保护可以在价格按步长移动到指定距离后自动平仓。
信号说明
- 做多开仓:信号栏位上的平滑 KWAN 值高于前一栏,且允许做多开仓。
- 做多平仓:持有多单时平滑 KWAN 值转为下行,且允许做多平仓。
- 做空开仓:信号栏位上的平滑 KWAN 值低于前一栏,且允许做空开仓。
- 做空平仓:持有空单时平滑 KWAN 值转为上行,且允许做空平仓。
风险管理
- 使用策略的
Volume 属性设定基础下单数量。翻转仓位时会先关闭反向仓位再建立新仓。
- 启用
UseProtection 可根据合约最小报价步长设置止损与止盈,两个保护可单独或同时使用。
- 策略基于
CandleType 指定的K线订阅数据,只在完全收盘的K线结束时触发交易。
参数列表
| 名称 |
说明 |
默认值 |
CandleType |
指标计算与信号评估所使用的K线周期。 |
1 小时K线 |
KPeriod |
随机指标 %K 的周期。 |
5 |
DPeriod |
随机指标 %D 的周期。 |
3 |
SlowingPeriod |
应用于 %K 的附加平滑周期。 |
3 |
RsiPeriod |
RSI 指标周期。 |
14 |
MomentumPeriod |
动量指标周期。 |
14 |
SmoothingMethod |
对 KWAN 比率进行平滑的移动平均类型(Simple、Exponential、Smoothed、Weighted)。 |
Simple |
SmoothingLength |
平滑移动平均的周期长度。 |
3 |
SignalBar |
用于评估斜率的历史栏位偏移(0 表示当前已收盘的K线)。 |
1 |
EnableBuyEntries |
允许在看多信号时开多。 |
true |
EnableSellEntries |
允许在看空信号时开空。 |
true |
EnableBuyExits |
允许在看空信号出现时平多。 |
true |
EnableSellExits |
允许在看多信号出现时平空。 |
true |
UseProtection |
启用止损/止盈保护。 |
true |
StopLossSteps |
以报价步长表示的止损距离。 |
1000 |
TakeProfitSteps |
以报价步长表示的止盈距离。 |
2000 |
使用提示
- 当动量指标为零时 KWAN 比率会变得不稳定,策略会自动跳过这些栏位以避免除零错误。
- 通过调整
SignalBar 可以延迟确认或对齐历史信号。
- 如需在生产环境使用,可结合券商层面的风险控制或额外过滤条件。
namespace StockSharp.Samples.Strategies;
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Strategy replicating the Exp KWAN NRP expert advisor by combining RSI and Momentum signals.
/// Enters long when RSI crosses above 50 and momentum is positive,
/// enters short when RSI crosses below 50 and momentum is negative.
/// </summary>
public class ExpKwanNrpStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _momentumPeriod;
private decimal _prevRsi;
private decimal _prevMom;
private bool _initialized;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
public int MomentumPeriod
{
get => _momentumPeriod.Value;
set => _momentumPeriod.Value = value;
}
public ExpKwanNrpStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for calculations", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI length", "Indicators");
_momentumPeriod = Param(nameof(MomentumPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Momentum Period", "Momentum length", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = 0m;
_prevMom = 0m;
_initialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = 0;
_prevMom = 0;
_initialized = false;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var momentum = new Momentum { Length = MomentumPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, momentum, (ICandleMessage candle, decimal rsiValue, decimal momValue) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!_initialized)
{
_prevRsi = rsiValue;
_prevMom = momValue;
_initialized = true;
return;
}
var rsiUp = rsiValue > _prevRsi;
var rsiDown = rsiValue < _prevRsi;
var momUp = momValue > _prevMom;
var momDown = momValue < _prevMom;
// Buy when both RSI and Momentum are turning up
if (rsiUp && momUp && Position <= 0)
{
BuyMarket();
}
// Sell when both RSI and Momentum are turning down
else if (rsiDown && momDown && Position >= 0)
{
SellMarket();
}
_prevRsi = rsiValue;
_prevMom = momValue;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
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 RelativeStrengthIndex, Momentum
from StockSharp.Algo.Strategies import Strategy
class exp_kwan_nrp_strategy(Strategy):
def __init__(self):
super(exp_kwan_nrp_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for calculations", "General")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI length", "Indicators")
self._momentum_period = self.Param("MomentumPeriod", 14) \
.SetDisplay("Momentum Period", "Momentum length", "Indicators")
self._prev_rsi = 0.0
self._prev_mom = 0.0
self._initialized = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@property
def MomentumPeriod(self):
return self._momentum_period.Value
def OnReseted(self):
super(exp_kwan_nrp_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._prev_mom = 0.0
self._initialized = False
def OnStarted2(self, time):
super(exp_kwan_nrp_strategy, self).OnStarted2(time)
self._prev_rsi = 0.0
self._prev_mom = 0.0
self._initialized = False
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
momentum = Momentum()
momentum.Length = self.MomentumPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(rsi, momentum, self._on_process) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, rsi)
self.DrawOwnTrades(area)
def _on_process(self, candle, rsi_value, mom_value):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_value)
mv = float(mom_value)
if not self._initialized:
self._prev_rsi = rv
self._prev_mom = mv
self._initialized = True
return
rsi_up = rv > self._prev_rsi
rsi_down = rv < self._prev_rsi
mom_up = mv > self._prev_mom
mom_down = mv < self._prev_mom
if rsi_up and mom_up and self.Position <= 0:
self.BuyMarket()
elif rsi_down and mom_down and self.Position >= 0:
self.SellMarket()
self._prev_rsi = rv
self._prev_mom = mv
def CreateClone(self):
return exp_kwan_nrp_strategy()