在 GitHub 上查看

RSI 与 CCI 背离策略

概述

RSI 与 CCI 背离策略 是对 MetaTrader 专家顾问 RSI&CCI_DIVERGENCE.mq4(MQL ID 22266)的移植。策略在主图上寻找价格高点与两个震荡指标(CCI 与 RSI)之间的多空背离,通过线性加权移动平均线过滤趋势,再利用三个不同时间框架上的 MACD 排列确认信号,并借助更高周期的动量指标验证行情强度。策略还提供可选的绝对止损和止盈距离,以便在 StockSharp 中控制持仓风险。

本实现完全依赖 StockSharp 的高层 API。所有指标均通过蜡烛订阅绑定获得,逻辑由实时蜡烛数据驱动,无需手动读取或缓存指标值。

交易逻辑

  1. 趋势过滤
    • 主周期上计算快慢两条线性加权移动平均线(LWMA)。
    • 当快线位于慢线上方时视为多头环境,快线位于慢线下方时视为空头环境。
  2. 背离检测
    • 将最新收盘蜡烛与最近 CandlesToRetrace 根历史蜡烛比较。
    • 若 CCI 或 RSI 形成更高的低点,而对应历史蜡烛的高点高于最新高点,则判定为多头背离。
    • 若 CCI 或 RSI 形成更低的高点,而对应历史蜡烛的高点低于最新高点,则判定为空头背离。
  3. MACD 确认
    • 同时在主周期、次级周期及宏观周期上计算 MACD(默认参数 12/26/9)。
    • 多头信号需要三重 MACD 均位于信号线上方,空头信号需要位于信号线下方。
  4. 动量确认
    • 在较高周期(默认 1 小时)上计算动量指标(默认长度 14)。
    • 最近若干动量值与基准 100 的偏离必须超过设定阈值,方可通过多头或空头过滤。
  5. 价格结构约束
    • 为复刻原始 EA 的条件,策略检查最近几根蜡烛的高低点(例如多头要求 Low[2] < High[1],空头要求 Low[1] < High[2])。
  6. 下单方式
    • 当所有条件满足时,使用 BuyMarketSellMarket 下市价单,数量为基础仓位量加上当前绝对持仓量,从而在需要时立即反向。
  7. 风险控制
    • 如果设置了止损或止盈距离,策略会在每根收盘蜡烛上检查是否触发,并以市价单平仓。

参数说明

参数 默认值 说明
FastMaLength 6 快速 LWMA 长度。
SlowMaLength 85 慢速 LWMA 长度。
CciLength 14 CCI 指标周期。
RsiLength 14 RSI 指标周期。
CandlesToRetrace 10 回溯用于检测背离的已收蜡烛数量。
MacdFastPeriod 12 MACD 快速均线长度。
MacdSlowPeriod 26 MACD 慢速均线长度。
MacdSignalPeriod 9 MACD 信号线长度。
MomentumLength 14 动量指标的周期。
MomentumBuyThreshold 0.3 多头动量确认所需相对 100 的最小偏离。
MomentumSellThreshold 0.3 空头动量确认所需相对 100 的最小偏离。
StopLoss 0 绝对止损距离(0 表示禁用)。
TakeProfit 0 绝对止盈距离(0 表示禁用)。
CandleType 15 分钟 主周期蜡烛类型。
MomentumCandleType 1 小时 动量确认使用的蜡烛类型。
HigherMacdCandleType 1 小时 MACD 次级周期蜡烛类型。
MacroMacdCandleType 30 天 MACD 宏观周期蜡烛类型,可根据可用数据调整。

使用建议

  • 请确保数据源提供所需的全部时间框架,否则需要调整蜡烛类型参数。
  • 默认关闭止损与止盈,以贴近原 EA 的权益止损与追踪方式;如需硬性风险控制,可设置大于零的数值。
  • 动量过滤基于 Momentum 指标以 100 为基准的经典定义(100 * Close / Close[N]),若使用其他定义,需要相应调整阈值。
  • 策略仅使用市价单进出场,行为与原版本一致。

移植说明

  • 采用 StockSharp 的绑定机制传递指标值,无需手动调用 GetValue
  • 原 EA 中的权益止损、移动止损及通知功能未移植,重点放在核心信号与基础止损/止盈处理。
  • 为检测背离,策略维护少量最近的价格与指标数据列表,以保证性能与可读性。
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

public class RsiCciDivergenceStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _cciPeriod;
	private decimal? _prevRsi, _prevCci;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }

	public RsiCciDivergenceStrategy()
	{
		_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");
		_cciPeriod = Param(nameof(CciPeriod), 14).SetGreaterThanZero().SetDisplay("CCI Period", "CCI lookback", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevRsi = null;
		_prevCci = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevRsi = null; _prevCci = null;
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var cci = new CommodityChannelIndex { Length = CciPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(rsi, cci, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal rsiVal, decimal cciVal)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!IsFormedAndOnlineAndAllowTrading()) { _prevRsi = rsiVal; _prevCci = cciVal; return; }
		if (_prevRsi == null || _prevCci == null) { _prevRsi = rsiVal; _prevCci = cciVal; return; }
		var buySignal = (_prevRsi.Value < 30m && rsiVal >= 30m) || (_prevCci.Value < -100m && cciVal >= -100m);
		var sellSignal = (_prevRsi.Value > 70m && rsiVal <= 70m) || (_prevCci.Value > 100m && cciVal <= 100m);
		_prevRsi = rsiVal; _prevCci = cciVal;
		if (buySignal && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
		else if (sellSignal && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
	}
}