在 GitHub 上查看
XRSI Histogram Vol Direct 策略
概览
- 原始脚本:
Exp_XRSI_Histogram_Vol_Direct.mq5
- 移植平台:StockSharp C# 高级策略 API
- 核心思想:当体积加权的 RSI 直方图斜率翻转时执行反向交易
- 数据来源:单标的、单时间框架(默认 H4)
该策略复制了原始 MT5 专家顾问的颜色缓冲区逻辑。它计算 RSI 与成交量(或点数)的乘积,并对其进行平滑处理。当平滑后的数值由升转降或由降转升时,策略会相应平仓并根据配置打开新的反向仓位。
指标与计算流程
- RSI 指标:使用
RsiPeriod 参数,在所选 K 线级别上计算 RSI,并减去 50,使其围绕零轴波动。
- 成交量选择:
Use Tick Volume 为 true 时使用成交笔数,否则使用成交量。
- 体积加权震荡器:将居中的 RSI 乘以对应的体积,突出高活跃度时段的信号。
- 平滑处理:对震荡器以及原始体积同时应用长度为
SmoothLength 的移动平均(支持 SMA、EMA、SMMA、WMA)。只有当两个平滑值都准备好时才会继续执行。
- 斜率颜色:比较当前平滑值与上一根的数值,大于为颜色
0(上升),小于为 1(下降),相等时继承上一根的颜色。
参数
| 名称 |
默认值 |
说明 |
| Candle Type |
H4 |
订阅的 K 线类型。 |
| RSI Period |
14 |
RSI 的回溯周期。 |
| Smoothing Length |
12 |
平滑 RSI×Volume 的移动平均长度。 |
| Smoothing Method |
SMA |
移动平均类型(SMA、EMA、SMMA、WMA)。 |
| Use Tick Volume |
true |
是否使用成交笔数。 |
| Allow Buy Open |
true |
允许开多。 |
| Allow Sell Open |
true |
允许开空。 |
| Allow Buy Close |
true |
允许在反向信号时平多。 |
| Allow Sell Close |
true |
允许在反向信号时平空。 |
提示:原脚本中的 JJMA、VIDYA 等平滑方式在 StockSharp 中不存在,代码提供了最接近的替代方案。
交易逻辑
- 等待两个平滑指标都形成有效数值。
- 读取最近两根已完成 K 线的颜色(斜率方向)。
- 较旧颜色为
0(上升):
- 若允许,则平掉当前空头仓位。
- 若允许开多且最新颜色为
1(下降),则在当前 K 线收盘时开多。
- 较旧颜色为
1(下降):
- 若允许,则平掉当前多头仓位。
- 若允许开空且最新颜色为
0(上升),则在当前 K 线收盘时开空。
整个流程完全复现了 MT5 专家顾问中“颜色翻转即交易”的思想。
使用建议
- 根据品种和波动率测试不同的时间框架,以匹配原系统的节奏。
- 仅依靠斜率方向时,建议结合
StartProtection 设置止损/止盈,增强风险控制。
- 在调试阶段,可在图表上绘制 RSI 曲线并对比 MT5 指标输出,确认移植正确性。
与 MQL 版本的差异
- 未移植
TradeAlgorithms.mqh 中的资金管理函数,策略依赖基类的下单量设置。
- 仅保留 StockSharp 支持的移动平均类型,其他模式退化为 SMA 行为。
- 不再使用
SignalBar 与 TimeShiftSec,策略直接在已完成的 K 线执行指令。
- 止损止盈逻辑需用户自行通过
StartProtection 添加。
限制
- 需要行情源提供成交笔数或成交量,以正确还原震荡器的幅度。
- 策略本身不绘制原指标的直方图,仅实现交易逻辑和可选的 RSI 显示。
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// XRSI Histogram Vol Direct strategy. Uses RSI 50-line crossover.
/// </summary>
public class XrsiHistogramVolDirectStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private decimal? _prevRsi;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public XrsiHistogramVolDirectStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14).SetGreaterThanZero().SetDisplay("RSI Period", "RSI lookback", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = null;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal rsiVal)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) { _prevRsi = rsiVal; return; }
if (_prevRsi == null) { _prevRsi = rsiVal; return; }
if (_prevRsi.Value < 45m && rsiVal >= 55m && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
else if (_prevRsi.Value > 55m && rsiVal <= 45m && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
_prevRsi = rsiVal;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class xrsi_histogram_vol_direct_strategy(Strategy):
def __init__(self):
super(xrsi_histogram_vol_direct_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI lookback", "Indicators")
self._prev_rsi = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiPeriod(self):
return self._rsi_period.Value
def OnReseted(self):
super(xrsi_histogram_vol_direct_strategy, self).OnReseted()
self._prev_rsi = None
def OnStarted2(self, time):
super(xrsi_histogram_vol_direct_strategy, self).OnStarted2(time)
self._prev_rsi = None
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_value)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_rsi = rv
return
if self._prev_rsi is None:
self._prev_rsi = rv
return
if self._prev_rsi < 45.0 and rv >= 55.0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev_rsi > 55.0 and rv <= 45.0 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_rsi = rv
def CreateClone(self):
return xrsi_histogram_vol_direct_strategy()