基于MQL5的自动化交易策略(第一部分):Profitunity系统(比尔·威廉姆斯的《交易混沌》)(基础篇)
◍ 用 MQL5 把混沌交易搬上 MT5
比尔·威廉姆斯在《交易混沌》里提出的 Profitunity 系统,核心是用分形、鳄鱼线和动量震荡来识别趋势起点。把它写成 MQL5 脚本,就能在 MT5 上自动标信号,省掉肉眼刷图的功夫。 这套系统对外汇和贵金属都适用,但两类品种波动属性差异大,杠杆下的回撤风险极高,任何信号都只是概率倾向,不是确定性入口。 原文示例发布于 2025 年 7 月 8 日,在 MQL5 社区拿到 1103 次查看、8 条讨论,说明手动混沌交易仍有不少人在找自动化解法。开 MT5 新建 EA,把分形判断逻辑先跑通,比直接上全系统更稳。
「这套混沌交易系统要怎么用 MQL5 落地」
Bill Williams 的 Profitunity 系统试图从市场“混沌”里抓可交易结构,核心靠一组自研指标而非传统均线堆叠。本文系列第 1 篇先把策略骨架和原则讲清,再逐步在 MQL5 里把指标和信号写成可跑的 EA。 落地路线分四块:系统概览、MQL5 实现、测试与优化、收尾讨论。实现部分会重点拆编码关键指标与自动进出场信号,测试段则覆盖多市场条件下的表现验证。 外汇与贵金属属高风险品种,任何自动化策略都只提高概率,不消除爆仓可能。读完本篇你应能自己开 MT5 按文内结构搭出基础 EA 框架,再往优化走。
比尔·威廉姆斯混沌组件的盘前识别逻辑
Profitunity 这套打法把市场当混沌系统处理,核心是靠四个指标叠加过滤:分形标局部拐点,鳄鱼线判趋势状态,AO 量动能方向,AC 测动能加速度。四者不同步时不出手,只在信号顺向共振时进场,以此压住假突破。 分形默认周期参数是 2(即左右各 2 根 K 线包围中间极值根),连续 5 根 K 线中间最高或最低才成形态,用来提前圈出可能的反转区。鳄鱼三线参数为颚 13 周期平滑 8、牙齿 8 周期平滑 5、嘴唇 5 周期平滑 3;三线张口代表趋势展开,缠绕代表横盘。 AO 取中位价上 34 与 5 周期 SMA 之差画直方图,零上偏多零下偏空。AC 在 AO 基础上再算一层加速度,围绕零线绿红切换。实战中买点要求:向下分形后价格上破鳄鱼颚线,AC 绿区,且 AO 直方从下穿上零才触发;卖点反之。出场用 AO 穿回反向区作离场信号,嘴唇反转或 AC 变色的优先级靠后。 外汇与贵金属杠杆高,上述共振只是概率倾向,MT5 里加载 Alligator+AO+AC 对照分形把历史波段翻一遍,能直接看出这套过滤在震荡市里会连续打损,趋势市才显优势。
◍ 把 Profitunity 四指标接进 EA 的初始化骨架
在 MT5 里落地比尔·威廉姆斯的 Profitunity 思路,第一步不是写交易判断,而是把分形、鳄鱼、AO、AC 四个指标稳稳挂到 EA 里。按 F4 进 MetaEditor,CTRL+N 新建 Expert Advisor (template),文件名前带反斜杠可指定子目录,默认落在 Experts\ 下。 代码开头用 #include <Trade/Trade.mqh> 引入 CTrade 类,再声明 obj_Trade 对象,后续下单全靠它。四个指标句柄先定义成整型并赋 INVALID_HANDLE:handle_Fractals、handle_Alligator、handle_AO、handle_AC,未初始化前显式无效,能挡掉一半句柄误用导致的报错。 分形和普通指标不同,它只在摆动点形成且离当前至少 3 根 K 线,不能随手取缓冲值。所以除了 fractals_up / fractals_down 两个数组,还要用枚举 fractal_direction 记录 lastFractal_direction(初始 FRACTAL_NEUTRAL)和 lastFractal_value,用来追最近一个分形的方向和价格。 OnInit 里分别用 iFractals(_Symbol,_Period)、iAlligator(_Symbol,_Period,13,8,5,8,5,3,MODE_SMMA,PRICE_MEDIAN) 等拿到句柄,每个都判 INVALID_HANDLE,失败就 Print 错误并 return INIT_FAILED。实测一次成功初始化后,日志里句柄 ID 从 10 排到 13,这四个整数就是 EA 生命周期内调 CopyBuffer 取数的“指针”。 指标初始化完用 ChartIndicatorAdd 贴图:分形、鳄鱼进主窗(窗口 0),AO 进子窗 1,AC 进子窗 2,任一个返回 false 同样 INIT_FAILED 退出。最后把所有数组 ArraySetAsSeries(...,true),最新值锁在索引 0,实时逻辑才好写。
class="macro">#include <Trade/Trade.mqh> CTrade obj_Trade; class="type">int handle_Fractals = INVALID_HANDLE; class="type">int handle_Alligator = INVALID_HANDLE; class="type">int handle_AO = INVALID_HANDLE; class="type">int handle_AC = INVALID_HANDLE; class="type">class="kw">double fractals_up[]; class="type">class="kw">double fractals_down[]; class="type">class="kw">double alligator_jaws[]; class="type">class="kw">double alligator_teeth[]; class="type">class="kw">double alligator_lips[]; class="type">class="kw">double ao_values[]; class="type">class="kw">double ac_color[]; class="macro">#define AC_COLOR_UP class="num">0 class="macro">#define AC_COLOR_DOWN class="num">1 class="type">class="kw">double lastFractal_value = class="num">0.0; enum fractal_direction { FRACTAL_UP, FRACTAL_DOWN, FRACTAL_NEUTRAL }; fractal_direction lastFractal_direction = FRACTAL_NEUTRAL; class="type">int OnInit() { handle_Fractals = iFractals(_Symbol, _Period); if(handle_Fractals == INVALID_HANDLE) { Print("ERROR: UNABLE TO INITIALIZE THE FRACTALS INDICATOR. REVERTING NOW!"); class="kw">return INIT_FAILED; } handle_Alligator = iAlligator(_Symbol, _Period, class="num">13, class="num">8, class="num">5, class="num">8, class="num">5, class="num">3, MODE_SMMA, PRICE_MEDIAN); if(handle_Alligator == INVALID_HANDLE) { Print("ERROR: UNABLE TO INITIALIZE THE ALLIGATOR INDICATOR. REVERTING NOW!"); class="kw">return INIT_FAILED; } handle_AO = iAO(_Symbol, _Period); if(handle_AO == INVALID_HANDLE) { Print("ERROR: UNABLE TO INITIALIZE THE AO INDICATOR. REVERTING NOW!"); class="kw">return INIT_FAILED; } handle_AC = iAC(_Symbol, _Period); if(handle_AC == INVALID_HANDLE) { Print("ERROR: UNABLE TO INITIALIZE THE AC INDICATOR. REVERTING NOW!"); class="kw">return INIT_FAILED; } if(!ChartIndicatorAdd(class="num">0, class="num">0, handle_Fractals)) { Print("ERROR: UNABLE TO ADD THE FRACTALS INDICATOR TO CHART. REVERTING NOW!"); class="kw">return INIT_FAILED; } if(!ChartIndicatorAdd(class="num">0, class="num">0, handle_Alligator)) { Print("ERROR: UNABLE TO ADD THE ALLIGATOR INDICATOR TO CHART. REVERTING NOW!"); class="kw">return INIT_FAILED; } if(!ChartIndicatorAdd(class="num">0, class="num">1, handle_AO)) { Print("ERROR: UNABLE TO ADD THE AO INDICATOR TO CHART. REVERTING NOW!"); class="kw">return INIT_FAILED; } if(!ChartIndicatorAdd(class="num">0, class="num">2, handle_AC)) { Print("ERROR: UNABLE TO ADD THE AC INDICATOR TO CHART. REVERTING NOW!"); class="kw">return INIT_FAILED; } ArraySetAsSeries(fractals_up, true); ArraySetAsSeries(fractals_down, true); ArraySetAsSeries(alligator_jaws, true); ArraySetAsSeries(alligator_teeth, true); ArraySetAsSeries(alligator_lips, true); ArraySetAsSeries(ao_values, true); ArraySetAsSeries(ac_color, true); class="kw">return INIT_SUCCEEDED; }
「从分形到鳄鱼线:把指标缓冲拼成开平仓逻辑」
先把 Fractals 指标句柄里的上下分形搬进数组。用 CopyBuffer 从索引 2 往前取 3 根 K 线,分别填 fractals_up 和 fractals_down;只要返回值小于 3,就打印错误并退出,避免拿残缺数据跑后面的反转判定。 指标缓冲区的编号得盯紧:分形向上是 0、向下是 1;鳄鱼三线依次是 0、1、2;AO 只有 0,AC 除了数据缓冲 0 还有颜色缓冲 1,颜色索引在指标属性窗口的参数页能直接看到。搞错编号,后面全乱。 新 K 线检测交给 isNewBar:静态变量 prevBars 记住上次柱数,iBars(_Symbol,_Period) 取当前柱数,不等就说明出新柱,更新并返回 true,相等返回 false。这样每根 K 线只跑一次检查,省资源。 分形落地后,index_fractal=0 读最新一根:up 不等于 EMPTY_VALUE 就记 lastFractal_value 且方向 FRACTAL_UP,down 同理记 FRACTAL_DOWN;getLastFractalDirection 靠 lastFractal_direction 回 UP / DOWN / NEUTRAL。 开仓条件组合拳:买入要最后分形向下 + 鳄鱼颚线跌破前收但高于前前收(isBreakdown_jaws_buy)+ AC 颜色向上 + AO[1]>0 且 AO[2]<0;卖出镜像反转,手数写死 0.01。 平仓只看 AO 反转:ao_values[1]<0 且 [2]>0 平所有买仓,反之平所有卖仓,用 PositionsTotal 遍历 ticket 调 PositionClose。外汇与贵金属杠杆高,这套逻辑回测前先在 MT5 策略测试器跑一遍,参数可能要随市况重调。