在 GitHub 上查看
ABE BE RSI 策略
概述
ABE BE RSI 策略 移植自 MetaTrader 专家顾问 Expert_ABE_BE_RSI。该系统将经典的吞没形态与相对强弱指数(RSI)动量确认结合起来。必须出现连续两根完全收盘的蜡烛形态,构成看涨或看跌吞没,并且最新收盘的蜡烛 RSI 值需要落在预设阈值范围内。策略还监控 RSI 穿越关口的情况,用于平仓或反手操作,从而最大限度地还原原始 MQL 版本的投票逻辑。
交易逻辑
吞没形态识别
策略在每根新蜡烛收盘后评估最近两根完整蜡烛:
- 看涨信号要求 t-2 蜡烛为阴线,t-1 蜡烛为阳线;
- t-1 蜡烛实体长度需大于最近
MovingAveragePeriod 根蜡烛实体的平均值;
- t-1 蜡烛收盘价高于 t-2 开盘价,开盘价低于 t-2 收盘价,实现真实的吞没;
- t-2 蜡烛的开收价中点位于收盘价移动平均线之下,以确认短期下行趋势。
看跌信号条件完全对称:较早的蜡烛为阳线,较新的蜡烛为阴线且实体更大,新蜡烛完全覆盖旧蜡烛,同时旧蜡烛的中点高于收盘价移动平均线,以确认上行趋势的衰竭。
RSI 过滤
- 开多必须满足上一根完整蜡烛的 RSI 低于
BullishEntryLevel(默认 40)。
- 开空必须满足上一根完整蜡烛的 RSI 高于
BearishEntryLevel(默认 60)。
离场管理
RSI 穿越两个关键阈值用于处理已有仓位:
- 当 RSI 从下方突破
ExitLowerLevel(默认 30)或 ExitUpperLevel(默认 70)时,空头仓位将被平掉。
- 当 RSI 从上方跌破上述阈值时,多头仓位将被平掉。
下单方式
策略仅使用市价单。若出现反向信号,会先平掉当前仓位,再以参数 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 未使用追踪止损,因此本策略也未添加该功能。
建议测试流程
- 在 Designer 或 Runner 中,将策略附加到对吞没形态敏感的品种(如主要外汇对)的历史数据上。
- 根据目标市场调整 RSI 及移动平均参数,默认值已与原 EA 对齐。
- 使用优化器探索不同的阈值组合,以寻找更适合目标市场的配置。
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;
}
}
}
}
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 abe_be_rsi_strategy(Strategy):
def __init__(self):
super(abe_be_rsi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30)))
self._rsi_period = self.Param("RsiPeriod", 14)
self._oversold = self.Param("Oversold", 40.0)
self._overbought = self.Param("Overbought", 60.0)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 6)
self._candles = []
self._candles_since_trade = 6
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def Oversold(self):
return self._oversold.Value
@Oversold.setter
def Oversold(self, value):
self._oversold.Value = value
@property
def Overbought(self):
return self._overbought.Value
@Overbought.setter
def Overbought(self, value):
self._overbought.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(abe_be_rsi_strategy, self).OnReseted()
self._candles.clear()
self._candles_since_trade = self.SignalCooldownCandles
def OnStarted2(self, time):
super(abe_be_rsi_strategy, self).OnStarted2(time)
self._candles.clear()
self._candles_since_trade = self.SignalCooldownCandles
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self._process_candle).Start()
def _process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
rsi_val = float(rsi_value)
self._candles.append(candle)
if len(self._candles) > 5:
self._candles.pop(0)
if len(self._candles) >= 2:
curr = self._candles[-1]
prev = self._candles[-2]
bullish_engulfing = (float(prev.OpenPrice) > float(prev.ClosePrice)
and float(curr.ClosePrice) > float(curr.OpenPrice)
and float(curr.OpenPrice) <= float(prev.ClosePrice)
and float(curr.ClosePrice) >= float(prev.OpenPrice))
bearish_engulfing = (float(prev.ClosePrice) > float(prev.OpenPrice)
and float(curr.OpenPrice) > float(curr.ClosePrice)
and float(curr.OpenPrice) >= float(prev.ClosePrice)
and float(curr.ClosePrice) <= float(prev.OpenPrice))
if bullish_engulfing and rsi_val < self.Oversold and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
elif bearish_engulfing and rsi_val > self.Overbought and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
def CreateClone(self):
return abe_be_rsi_strategy()