在 GitHub 上查看

ABE BE RSI 策略

概述

ABE BE RSI 策略 移植自 MetaTrader 专家顾问 Expert_ABE_BE_RSI。该系统将经典的吞没形态与相对强弱指数(RSI)动量确认结合起来。必须出现连续两根完全收盘的蜡烛形态,构成看涨或看跌吞没,并且最新收盘的蜡烛 RSI 值需要落在预设阈值范围内。策略还监控 RSI 穿越关口的情况,用于平仓或反手操作,从而最大限度地还原原始 MQL 版本的投票逻辑。

交易逻辑

  1. 吞没形态识别
    策略在每根新蜡烛收盘后评估最近两根完整蜡烛:

    • 看涨信号要求 t-2 蜡烛为阴线,t-1 蜡烛为阳线;
    • t-1 蜡烛实体长度需大于最近 MovingAveragePeriod 根蜡烛实体的平均值;
    • t-1 蜡烛收盘价高于 t-2 开盘价,开盘价低于 t-2 收盘价,实现真实的吞没;
    • t-2 蜡烛的开收价中点位于收盘价移动平均线之下,以确认短期下行趋势。

    看跌信号条件完全对称:较早的蜡烛为阳线,较新的蜡烛为阴线且实体更大,新蜡烛完全覆盖旧蜡烛,同时旧蜡烛的中点高于收盘价移动平均线,以确认上行趋势的衰竭。

  2. RSI 过滤

    • 开多必须满足上一根完整蜡烛的 RSI 低于 BullishEntryLevel(默认 40)。
    • 开空必须满足上一根完整蜡烛的 RSI 高于 BearishEntryLevel(默认 60)。
  3. 离场管理
    RSI 穿越两个关键阈值用于处理已有仓位:

    • 当 RSI 从下方突破 ExitLowerLevel(默认 30)或 ExitUpperLevel(默认 70)时,空头仓位将被平掉。
    • 当 RSI 从上方跌破上述阈值时,多头仓位将被平掉。
  4. 下单方式
    策略仅使用市价单。若出现反向信号,会先平掉当前仓位,再以参数 Volume 指定的手数建立新仓,模拟原始 EA 中固定手数的资金管理。

参数

参数 说明 默认值
Volume 每笔订单的合约数量。 0.1
RsiPeriod RSI 指标的计算周期。 11
MovingAveragePeriod 计算蜡烛实体和收盘价均线的周期。 5
BullishEntryLevel 看涨吞没信号可接受的最高 RSI 值。 40
BearishEntryLevel 看跌吞没信号所需的最低 RSI 值。 60
ExitLowerLevel RSI 上穿后平仓的低位阈值。 30
ExitUpperLevel RSI 下穿后平仓的高位阈值。 70
CandleType 策略处理的蜡烛类型/周期。 1 小时

所有参数均通过 StrategyParam 封装,可在 Designer 或 Runner 中优化。

指标结构

  • RSI:提供动量读数,判断入场和离场条件。
  • 收盘价简单移动平均:判定旧蜡烛所处趋势,以过滤吞没信号。
  • 蜡烛实体简单移动平均:确保新蜡烛的实体足够大,避免弱信号。

使用提示

  • 仅对 CandleStates.Finished 的完整蜡烛进行处理,忽略未完成的实时柱,以防提前发出信号。
  • 内部仅保存最近两根蜡烛及相关指标值,不会遍历长序列,符合仓库要求的高性能实现方式。
  • 调用 StartProtection() 以启用 StockSharp 的风险保护机制,当策略持仓不为零时自动生效。

与原版 EA 的差异

  • MetaTrader 版本使用“投票”机制,本移植版本将其映射为直接的入场/出场动作,但保持相同条件。
  • 资金管理简化为固定手数参数,与原始 EA 的 Money_FixLot_Lots 设置一致。
  • 原 EA 未使用追踪止损,因此本策略也未添加该功能。

建议测试流程

  1. 在 Designer 或 Runner 中,将策略附加到对吞没形态敏感的品种(如主要外汇对)的历史数据上。
  2. 根据目标市场调整 RSI 及移动平均参数,默认值已与原 EA 对齐。
  3. 使用优化器探索不同的阈值组合,以寻找更适合目标市场的配置。
namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// ABE BE RSI strategy: Engulfing pattern with RSI confirmation.
/// Bullish engulfing + oversold RSI for long, bearish engulfing + overbought RSI for short.
/// </summary>
public class AbeBeRsiStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _oversold;
	private readonly StrategyParam<decimal> _overbought;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private readonly List<ICandleMessage> _candles = new();
	private int _candlesSinceTrade;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
	public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public AbeBeRsiStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period", "Indicators");
		_oversold = Param(nameof(Oversold), 40m)
			.SetDisplay("Oversold", "RSI oversold level", "Signals");
		_overbought = Param(nameof(Overbought), 60m)
			.SetDisplay("Overbought", "RSI overbought level", "Signals");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_candles.Clear();
		_candlesSinceTrade = SignalCooldownCandles;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_candles.Clear();
		_candlesSinceTrade = SignalCooldownCandles;
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(rsi, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
	{
		if (candle.State != CandleStates.Finished) return;

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		_candles.Add(candle);
		if (_candles.Count > 5)
			_candles.RemoveAt(0);

		if (_candles.Count >= 2)
		{
			var curr = _candles[^1];
			var prev = _candles[^2];

			var bullishEngulfing = prev.OpenPrice > prev.ClosePrice
				&& curr.ClosePrice > curr.OpenPrice
				&& curr.OpenPrice <= prev.ClosePrice
				&& curr.ClosePrice >= prev.OpenPrice;

			var bearishEngulfing = prev.ClosePrice > prev.OpenPrice
				&& curr.OpenPrice > curr.ClosePrice
				&& curr.OpenPrice >= prev.ClosePrice
				&& curr.ClosePrice <= prev.OpenPrice;

			if (bullishEngulfing && rsiValue < Oversold && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
			else if (bearishEngulfing && rsiValue > Overbought && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
		}
	}
}