在 GitHub 上查看
AML RSI Meeting Lines 策略
概述
AML RSI Meeting Lines Strategy 是将 MetaTrader 5 专家顾问 Expert_AML_RSI.mq5 迁移到 StockSharp 的版本。原始策略结合日本蜡烛形态 “Meeting Lines” 与 RSI 指标来捕捉反转。本次转换完全基于 StockSharp 的高级 API:使用蜡烛订阅、内置指标以及标准化的交易辅助方法。
交易逻辑
- 订阅可配置的蜡烛类型,只处理收盘完成的蜡烛。
- 对蜡烛实体长度求简单移动平均,以识别构成 Meeting Lines 所需的“长实体”蜡烛。
- 保存最近两根蜡烛的 RSI 值,用于确认信号和判断离场。
- 做多条件:前两根蜡烛形成牛市 Meeting Lines,且 RSI 低于多头阈值时买入。
- 做空条件:镜像形态并且 RSI 高于空头阈值时卖出。
- 离场条件:当 RSI 穿越自定义的上下限(30 与 70)时,在相反方向平仓。
- 使用
BuyMarket、SellMarket 与 ClosePosition 管理仓位;在出现反向信号时会自动翻转仓位规模。
参数
| 名称 |
说明 |
默认值 |
CandleType |
用于识别形态的蜡烛类型。 |
1 小时蜡烛 |
RsiPeriod |
RSI 计算周期。 |
11 |
BodyAveragePeriod |
计算实体平均的蜡烛数量。 |
3 |
BullishRsiLevel |
确认牛市形态的 RSI 上限。 |
40 |
BearishRsiLevel |
确认熊市形态的 RSI 下限。 |
60 |
LowerExitLevel |
RSI 向上穿越该值时平空。 |
30 |
UpperExitLevel |
RSI 向下穿越该值时平多。 |
70 |
所有参数都以 StrategyParam<T> 暴露,可在 StockSharp Designer 中进行优化。
风险控制
- 在
OnStarted 中调用 StartProtection(),启用平台的仓位保护。
- 每次下单前都会先检查 RSI 是否触发离场条件,避免同时持有相反仓位。
- 市价单会自动考虑当前仓位的绝对值,从而在一次下单中完成平仓与反向开仓。
转换说明
- 蜡烛实体均值通过
SimpleMovingAverage 实现,输入为 |Open - Close|,与 MQL5 中的 AvgBody 函数一致。
- RSI 确认逻辑使用上一根与上上一根蜡烛的数值,对应原始代码中的
RSI(1) 与 RSI(2) 判断。
- 代码采用文件级命名空间、制表符缩进,并在关键步骤添加了英文注释,符合仓库规范。
使用建议
- 在 StockSharp 中选择标的和蜡烛类型后启动策略。
- 根据交易品种调整 RSI 阈值与平均周期,必要时进行历史回测。
- 推荐先在模拟或回测环境验证形态识别的准确性,再切换到真实交易。
- 通过 Designer 或自定义优化流程,微调参数以适配不同市场。
免责声明
本策略仅用于学习与研究,请务必在历史数据与模拟环境中充分测试后再考虑投入真实资金。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Meeting Lines + RSI strategy.
/// Buys on bullish meeting lines with low RSI, sells on bearish meeting lines with high RSI.
/// </summary>
public class AmlRsiMeetingLinesStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiLow;
private readonly StrategyParam<decimal> _rsiHigh;
private ICandleMessage _prevCandle;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public decimal RsiLow { get => _rsiLow.Value; set => _rsiLow.Value = value; }
public decimal RsiHigh { get => _rsiHigh.Value; set => _rsiHigh.Value = value; }
public AmlRsiMeetingLinesStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
_rsiLow = Param(nameof(RsiLow), 40m)
.SetDisplay("RSI Low", "RSI level for bullish entry", "Signals");
_rsiHigh = Param(nameof(RsiHigh), 60m)
.SetDisplay("RSI High", "RSI level for bearish entry", "Signals");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevCandle = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCandle = null;
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 (_prevCandle != null)
{
var avgBody = (Math.Abs(candle.ClosePrice - candle.OpenPrice) +
Math.Abs(_prevCandle.ClosePrice - _prevCandle.OpenPrice)) / 2m;
if (avgBody > 0)
{
var prevBearish = _prevCandle.OpenPrice > _prevCandle.ClosePrice;
var currBullish = candle.ClosePrice > candle.OpenPrice;
var closesNear = Math.Abs(candle.ClosePrice - _prevCandle.ClosePrice) < avgBody * 0.3m;
if (prevBearish && currBullish && closesNear && rsiValue < RsiLow && Position <= 0)
BuyMarket();
var prevBullish = _prevCandle.ClosePrice > _prevCandle.OpenPrice;
var currBearish = candle.OpenPrice > candle.ClosePrice;
var closesNear2 = Math.Abs(candle.ClosePrice - _prevCandle.ClosePrice) < avgBody * 0.3m;
if (prevBullish && currBearish && closesNear2 && rsiValue > RsiHigh && Position >= 0)
SellMarket();
}
}
_prevCandle = candle;
}
}
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 aml_rsi_meeting_lines_strategy(Strategy):
def __init__(self):
super(aml_rsi_meeting_lines_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._rsi_period = self.Param("RsiPeriod", 14)
self._rsi_low = self.Param("RsiLow", 40.0)
self._rsi_high = self.Param("RsiHigh", 60.0)
self._prev_candle = None
@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 RsiLow(self):
return self._rsi_low.Value
@RsiLow.setter
def RsiLow(self, value):
self._rsi_low.Value = value
@property
def RsiHigh(self):
return self._rsi_high.Value
@RsiHigh.setter
def RsiHigh(self, value):
self._rsi_high.Value = value
def OnReseted(self):
super(aml_rsi_meeting_lines_strategy, self).OnReseted()
self._prev_candle = None
def OnStarted2(self, time):
super(aml_rsi_meeting_lines_strategy, self).OnStarted2(time)
self._prev_candle = None
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
rsi_val = float(rsi_value)
if self._prev_candle is not None:
avg_body = (abs(float(candle.ClosePrice) - float(candle.OpenPrice))
+ abs(float(self._prev_candle.ClosePrice) - float(self._prev_candle.OpenPrice))) / 2.0
if avg_body > 0:
prev_bearish = float(self._prev_candle.OpenPrice) > float(self._prev_candle.ClosePrice)
curr_bullish = float(candle.ClosePrice) > float(candle.OpenPrice)
closes_near = abs(float(candle.ClosePrice) - float(self._prev_candle.ClosePrice)) < avg_body * 0.3
if prev_bearish and curr_bullish and closes_near and rsi_val < self.RsiLow and self.Position <= 0:
self.BuyMarket()
prev_bullish = float(self._prev_candle.ClosePrice) > float(self._prev_candle.OpenPrice)
curr_bearish = float(candle.OpenPrice) > float(candle.ClosePrice)
closes_near2 = abs(float(candle.ClosePrice) - float(self._prev_candle.ClosePrice)) < avg_body * 0.3
if prev_bullish and curr_bearish and closes_near2 and rsi_val > self.RsiHigh and self.Position >= 0:
self.SellMarket()
self._prev_candle = candle
def CreateClone(self):
return aml_rsi_meeting_lines_strategy()