MQL5交易策略自动化(第二十部分):基于CCI和AO指标的多品种策略·综合运用
「多指标共振下的开仓闸门」
这段逻辑把 CCI 与 AO 两条趋势线叠在一起做过滤,只有当 CCI 当前值卡在买入阈值与卖出阈值之间、且上一根 K 线的 AO 值相对当前发生方向性偏转时,才把对应方向的 trend 标志置真。注意 m_candle_shift+1 与 m_candle_shift 的错位比较——它实际是在拿前一根确认的 AO 斜率,去佐证当前 CCI 区间内的趋势倾向,属于典型的「慢线定方向、快线定区间」写法。 真正下单前还有三道硬性闸门:点差必须低于 m_params.max_spread、同品种当前挂单与成交单总数 total_orders 必须等于 0、且要么允许任意时间交易要么卡在新 K 线诞生瞬间。外汇与贵金属杠杆高,点差跳变常在数据行情出现,total_orders==0 这条能避免同源信号在同一波行情里叠仓。 SL/TP 的计算很直白:stop 或 take 参数为 0 就传 0 给 broker 代表不挂,否则用 ask/bid 加减点数乘 point。下面这段是趋势判定与执行核心,可直接丢进 MT5 的 EA 主循环里对照验证。
m_symbols[i].cci_trend_data[m_candle_shift] > CCI_TREND_BUY_VALUE && class=class="str">"cmt">//--- Confirm CCI trend buy threshold m_symbols[i].cci_trend_data[m_candle_shift] < CCI_TREND_SELL_VALUE && class=class="str">"cmt">//--- Confirm CCI trend sell threshold (!m_params.use_ao_for_trend || class=class="str">"cmt">//--- Check AO trend condition (m_symbols[i].ao_trend_data[m_candle_shift+class="num">1] < m_symbols[i].ao_trend_data[m_candle_shift])); class=class="str">"cmt">//--- Confirm AO trend buy sell_trend = m_symbols[i].cci_trend_data[m_candle_shift+class="num">1] > m_symbols[i].cci_signal_data[m_candle_shift] && class=class="str">"cmt">//--- Check CCI trend sell condition m_symbols[i].cci_trend_data[m_candle_shift] > CCI_TREND_BUY_VALUE && class=class="str">"cmt">//--- Confirm CCI trend buy threshold m_symbols[i].cci_trend_data[m_candle_shift] < CCI_TREND_SELL_VALUE && class=class="str">"cmt">//--- Confirm CCI trend sell threshold (!m_params.use_ao_for_trend || class=class="str">"cmt">//--- Check AO trend condition (m_symbols[i].ao_trend_data[m_candle_shift+class="num">1] > m_symbols[i].ao_trend_data[m_candle_shift])); class=class="str">"cmt">//--- Confirm AO trend sell class=class="str">"cmt">// Execute trades if(spread < m_params.max_spread && total_orders == class="num">0 && class=class="str">"cmt">//--- Check spread and open orders (m_params.trade_anytime || is_new_bar)) { class=class="str">"cmt">//--- Check trade timing if(buy_signal && buy_trend) { class=class="str">"cmt">//--- Check buy conditions class="type">class="kw">double sl = m_params.stop == class="num">0 ? class="num">0 : ask - m_params.stop * point; class=class="str">"cmt">//--- Calculate stop-loss class="type">class="kw">double tp = m_params.take == class="num">0 ? class="num">0 : ask + m_params.take * point; class=class="str">"cmt">//--- Calculate take-profit OpenOrder(m_symbols[i].name, ORDER_TYPE_BUY, ask, sl, tp, m_params.lots, class=class="str">"cmt">//--- Open buy order m_params.magic, "Open BUY " + m_params.comment); } if(sell_signal && sell_trend) { class=class="str">"cmt">//--- Check sell conditions class="type">class="kw">double sl = m_params.stop == class="num">0 ? class="num">0 : bid + m_params.stop * point; class=class="str">"cmt">//--- Calculate stop-loss class="type">class="kw">double tp = m_params.take == class="num">0 ? class="num">0 : bid - m_params.take * point; class=class="str">"cmt">//--- Calculate take-profit OpenOrder(m_symbols[i].name, ORDER_TYPE_SELL, bid, sl, tp, m_params.lots, class=class="str">"cmt">//--- Open sell order m_params.magic, "Open SELL " + m_params.comment); } } class=class="str">"cmt">// Debug output if((m_params.trade_anytime || is_new_bar) && (buy_signal || sell_signal)) { class=class="str">"cmt">//--- Check debug conditions PrintDebug(StringFormat("Debug - IsNewBar: %b - candle_shift: %d - buy_signal: %b - " class=class="str">"cmt">//--- Log debug information
一键清仓的底层实现
EA 在切换信号或退出前,往往需要把当前策略开出的所有持仓和挂单清掉。下面这段 CloseAllTrades 就是干这件事的,逻辑上分两步:先扫持仓(PositionsTotal),再扫挂单(OrdersTotal),两者都倒序遍历以避免删除元素后索引错位。 外汇与贵金属杠杆高,一键平仓若 magic 号匹配范围过宽,可能误杀手动单,实盘前务必在策略测试器用历史数据跑一遍确认只清自己的单。 代码里用 PositionGetInteger(POSITION_MAGIC) == m_params.magic 做过滤,说明只有本 EA 的 magic 单才会被 PositionClose;挂单侧则用 OrderGetInteger(ORDER_MAGIC) 比对,且类型限定在 BUY_STOP 到 SELL_LIMIT 区间内才 OrderDelete。PrintMessage 把 ticket 号打出来,方便你回看日志核对哪笔被平了。
class="type">bool CloseAllTrades() { class=class="str">"cmt">//--- Define method to close all trades for(class="type">int i = PositionsTotal() - class="num">1; i >= class="num">0; i--) { class=class="str">"cmt">//--- Iterate through positions class="type">class="kw">ulong ticket = PositionGetTicket(i); class=class="str">"cmt">//--- Get position ticket if(PositionSelectByTicket(ticket) && class=class="str">"cmt">//--- Select position PositionGetInteger(POSITION_MAGIC) == m_params.magic) { class=class="str">"cmt">//--- Check magic number class="type">ENUM_POSITION_TYPE type = (class="type">ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); class=class="str">"cmt">//--- Get position type if(type == POSITION_TYPE_BUY || type == POSITION_TYPE_SELL) { class=class="str">"cmt">//--- Check position type m_trade.PositionClose(ticket); class=class="str">"cmt">//--- Close position PrintMessage("Position close " + IntegerToString(ticket)); class=class="str">"cmt">//--- Log closure } } } for(class="type">int i = OrdersTotal() - class="num">1; i >= class="num">0; i--) { class=class="str">"cmt">//--- Iterate through orders class="type">class="kw">ulong ticket = OrderGetTicket(i); class=class="str">"cmt">//--- Get order ticket if(OrderSelect(ticket) && OrderGetInteger(ORDER_MAGIC) == m_params.magic) { class=class="str">"cmt">//--- Select order and check magic ENUM_ORDER_TYPE type = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE); class=class="str">"cmt">//--- Get order type if(type >= ORDER_TYPE_BUY_STOP && type <= ORDER_TYPE_SELL_LIMIT) { class=class="str">"cmt">//--- Check order type m_trade.OrderDelete(ticket); class=class="str">"cmt">//--- Delete order PrintMessage("Order class="kw">delete " + IntegerToString(ticket)); class=class="str">"cmt">//--- Log deletion } } } class="kw">return true; class=class="str">"cmt">//--- Return success }
◍ EA初始化函数的两种写法对照
在 MQL5 的 EA 骨架里,OnInit 是整个策略生命周期的入口。下面这段把初始化逻辑完全委托给 g_strategy.Init(),靠返回值决定 INIT_SUCCEEDED 还是 INIT_FAILED,写法干净,适合把策略对象化管理的老手。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert Advisor Functions | //--- Define EA core functions class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- Define initialization function class="kw">return g_strategy.Init() ? INIT_SUCCEEDED : INIT_FAILED; class=class="str">"cmt">//--- Initialize strategy and class="kw">return status } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert Advisor Functions | //--- Define EA core functions class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- Define initialization function class="kw">return g_strategy.Init() ? INIT_SUCCEEDED : INIT_FAILED; class=class="str">"cmt">//--- Initialize strategy and class="kw">return status } class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert Advisor Functions | //--- Define EA core functions class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- Define initialization function class="kw">return g_strategy.Init() ? INIT_SUCCEEDED : INIT_FAILED; class=class="str">"cmt">//--- Initialize strategy and class="kw">return status } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert Advisor Functions | //--- Define EA core functions class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- Define initialization function class="kw">return g_strategy.Init() ? INIT_SUCCEEDED : INIT_FAILED; class=class="str">"cmt">//--- Initialize strategy and class="kw">return status } class=class="str">"cmt">//+------------------------------------------------------------------+
「拿这套架构继续往下改」
这套多品种 CCI 与 AO 系统把信号生成和订单管理拆成了两层:CTradingStrategy 类只管出信号,CTrade 库专门接执行,点差校验和止损挂在模块边界上,所以你在 MT5 里换货币对或者加过滤条件,不用动核心循环。 想验证就直接把附带的 MultiSymbolCCIAO_EA.mq5(22.21 KB)拖进 MetaEditor 编译,默认同时跑多个符号,先在策略测试器用 2020—2023 年 H1 数据回测,看 CCI 阈值和 AO 零轴穿越的组合在哪些品种上夏普掉得厉害。 外汇和贵金属杠杆高、跳空频繁,实测里点差超过预设值会自动跳过入场,但这不杜绝滑点吞噬止损,真上实盘前先把最大持仓数和单笔风险百分比写死。 顺着这个骨架,你把 CCI 周期从 14 调到 20 或者接一个时段过滤器,就能衍生出完全不同的多品种策略,架构本身不用重写。