在 GitHub 上查看
AML CCI Meeting Lines 策略
该策略把 MetaTrader 5 智能交易系统 "Expert_AML_CCI" 移植到 StockSharp 的高级 API。当年的机器人将日本蜡烛形态
“Meeting Lines” 与商品通道指数(CCI)过滤器结合,并通过 Expert Advisor 引擎给多空信号赋予权重。移植版本保持相同
的确认逻辑,用纯粹的蜡烛运算重写形态识别,同时把所有阈值暴露为可优化的参数。
工作原理
- 数据源 – 策略通过
SubscribeCandles 订阅可配置的时间框架蜡烛(默认 30 分钟)。每根收盘蜡烛都会通过高级
Bind 管道与同步的 CCI 数值一起传入,因此无需手动维护指标。
- 核心指标 – 单个
CommodityChannelIndex 指标(周期为 CciPeriod)完整复刻 MetaTrader 的振荡器。内部缓存存储
最新两次收盘读数,用来重现 MQL 中的 CCI(1) 与 CCI(2) 访问方式。
- 蜡烛形态逻辑 – 辅助方法重新实现了 “Bullish Meeting Lines” 与 “Bearish Meeting Lines” 的检测。它会按
AverageBodyPeriod
根蜡烛(默认 3)计算实体平均值,并检查长实体与相同收盘价的约束,与原始 CML_CCI 过滤器保持一致。因为
StockSharp 只处理收盘蜡烛,形态会在第二根蜡烛收盘时立即评估,这与 MQL 智能交易系统给出 80 分投票的时刻相同。
- 入场规则 –
- 多头:必须出现看涨 Meeting Lines 形态,并且最近一次 CCI 收盘值小于等于
LongEntryCciLevel(默认 −50)。若持有空头,
下单数量会自动加上当前仓位的绝对值,从而实现翻仓,与原策略一致。
- 空头:逻辑对称,需要看跌 Meeting Lines 形态以及最近 CCI 值大于等于
ShortEntryCciLevel(默认 +50)。
- 出场规则 – 移植版用显式平仓订单取代 Expert Advisor 的投票权重。当 CCI 穿越
ExtremeCciLevel(默认 80)定义的
极值带时会退出:
- 空头在 CCI 上穿 −Extreme 或重新跌破 +Extreme 时离场。
- 多头在 CCI 跌破 +Extreme 或继续下穿 −Extreme 时离场。
这些条件正是 MQL 信号类
LongCondition 和 ShortCondition 中权重为 40 的分支。
- 风险控制 – 策略本身不附带止损止盈,用户可以根据需要调用 StockSharp 的
StartProtection 工具来补充。
参数
| 参数 |
说明 |
默认值 |
CandleType |
蜡烛时间框架。 |
30 分钟 |
CciPeriod |
CCI 指标周期。 |
18 |
AverageBodyPeriod |
计算实体平均长度所需的蜡烛数量。 |
3 |
LongEntryCciLevel |
用于确认看涨形态的超卖阈值。 |
−50 |
ShortEntryCciLevel |
用于确认看跌形态的超买阈值。 |
+50 |
ExtremeCciLevel |
CCI 穿越以触发退出的极值带。 |
80 |
所有数值型参数都带有与原 EA 相同的优化范围,方便在 StockSharp 的优化器中调整灵敏度或重新匹配资金管理方案。
使用提示
- 启动前将策略绑定到目标证券,并设置合适的
Volume。
- 如需复刻原始资金管理或调整信号灵敏度,可修改各类阈值参数。
- 图表模块会绘制蜡烛、CCI 曲线以及成交点,便于快速确认形态识别是否与预期一致。
通过保持同样的蜡烛形态与 CCI 组合,这个 StockSharp 版本忠实地重现了 Expert_AML_CCI,同时完全遵循高层 API 的
推荐用法。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Meeting Lines + CCI strategy.
/// Buys on bullish meeting lines with low CCI, sells on bearish meeting lines with high CCI.
/// </summary>
public class AmlCciMeetingLinesStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<decimal> _cciLow;
private readonly StrategyParam<decimal> _cciHigh;
private ICandleMessage _prevCandle;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
public decimal CciLow { get => _cciLow.Value; set => _cciLow.Value = value; }
public decimal CciHigh { get => _cciHigh.Value; set => _cciHigh.Value = value; }
public AmlCciMeetingLinesStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_cciPeriod = Param(nameof(CciPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "CCI period", "Indicators");
_cciLow = Param(nameof(CciLow), -50m)
.SetDisplay("CCI Low", "CCI level for bullish entry", "Signals");
_cciHigh = Param(nameof(CciHigh), 50m)
.SetDisplay("CCI High", "CCI 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 cci = new CommodityChannelIndex { Length = CciPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(cci, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal cciValue)
{
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;
// Bullish meeting lines
if (prevBearish && currBullish && closesNear && cciValue < CciLow && 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;
// Bearish meeting lines
if (prevBullish && currBearish && closesNear2 && cciValue > CciHigh && 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, Math
from StockSharp.Messages import CandleStates
from StockSharp.Algo.Indicators import CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class aml_cci_meeting_lines_strategy(Strategy):
"""
Meeting Lines + CCI strategy.
Buys on bullish meeting lines with low CCI, sells on bearish meeting lines with high CCI.
"""
def __init__(self):
super(aml_cci_meeting_lines_strategy, self).__init__()
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._cci_period = self.Param("CciPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("CCI Period", "CCI period", "Indicators")
self._cci_low = self.Param("CciLow", -50.0) \
.SetDisplay("CCI Low", "CCI level for bullish entry", "Signals")
self._cci_high = self.Param("CciHigh", 50.0) \
.SetDisplay("CCI High", "CCI level for bearish entry", "Signals")
self._prev_open = None
self._prev_close = None
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, v): self._candle_type.Value = v
@property
def CciPeriod(self): return self._cci_period.Value
@CciPeriod.setter
def CciPeriod(self, v): self._cci_period.Value = v
@property
def CciLow(self): return self._cci_low.Value
@CciLow.setter
def CciLow(self, v): self._cci_low.Value = v
@property
def CciHigh(self): return self._cci_high.Value
@CciHigh.setter
def CciHigh(self, v): self._cci_high.Value = v
def OnReseted(self):
super(aml_cci_meeting_lines_strategy, self).OnReseted()
self._prev_open = None
self._prev_close = None
def OnStarted2(self, time):
super(aml_cci_meeting_lines_strategy, self).OnStarted2(time)
self._prev_open = None
self._prev_close = None
cci = CommodityChannelIndex()
cci.Length = self.CciPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(cci, self.ProcessCandle).Start()
def ProcessCandle(self, candle, cci_value):
if candle.State != CandleStates.Finished:
return
c_open = float(candle.OpenPrice)
c_close = float(candle.ClosePrice)
if self._prev_open is not None and self._prev_close is not None:
avg_body = (abs(c_close - c_open) + abs(self._prev_close - self._prev_open)) / 2.0
if avg_body > 0:
prev_bearish = self._prev_open > self._prev_close
curr_bullish = c_close > c_open
closes_near = abs(c_close - self._prev_close) < avg_body * 0.3
if prev_bearish and curr_bullish and closes_near and cci_value < self.CciLow and self.Position <= 0:
self.BuyMarket()
prev_bullish = self._prev_close > self._prev_open
curr_bearish = c_open > c_close
closes_near2 = abs(c_close - self._prev_close) < avg_body * 0.3
if prev_bullish and curr_bearish and closes_near2 and cci_value > self.CciHigh and self.Position >= 0:
self.SellMarket()
self._prev_open = c_open
self._prev_close = c_close
def CreateClone(self):
"""!! REQUIRED!! Creates a new instance of the strategy."""
return aml_cci_meeting_lines_strategy()