基于MQL5的自动化交易策略(第一部分):Profitunity系统(比尔·威廉姆斯的《交易混沌》)·进阶篇
(2/3)· 接上篇概念铺垫,本篇把比尔·威廉姆斯的混沌指标拆成可运行代码与回测框架
◍ 混沌交易系统的指标句柄预声明
Bill Williams 的 Trading Chaos 体系在 MT5 里落地,第一道坎不是信号逻辑,而是指标句柄的初始化顺序。Fractals、Alligator、AO、AC 四个核心指标必须各自拿到有效 handle,否则后续取值全是 INVALID_HANDLE 导致的空数组。 下面这段代码把四个句柄先统一赋成 INVALID_HANDLE,再用 iFractals / iAlligator 等函数向当前图表(_Symbol、_Period)申请真实句柄。Alligator 这里用的是 13/8/8/5/5/3 的 SMMA 中价参数组合,和原著默认设置一致。 初始化函数里只要任一句柄申请失败就 Print 报错并返回 INIT_FAILED,EA 不会偷偷带着残句柄跑。外汇与贵金属杠杆高,句柄失效若未被拦截,可能让策略在错误数据上出信号,实盘前务必在 MT5 策略测试器里确认四个指标均正常加载。 别把句柄当全局常量 句柄是运行时向图表申请的引用,切换品种或周期后旧 handle 会失效。把初始化写死在 OnInit 一次就不管,切换周期容易读不到数据,正确做法是周期变动时重新申请或做有效性检查。
class="macro">#class="kw">property link "https:class=class="str">"cmt">//forexalgo-trader.com" class="macro">#class="kw">property description "class="num">1. PROFITUNITY(TRADING CHAOS BY BILL WILLIAMS)" class="macro">#class="kw">property version "class="num">1.00" class="macro">#include <Trade/Trade.mqh> CTrade obj_Trade; class="type">int handle_Fractals = INVALID_HANDLE; class=class="str">"cmt">//--- Initialize fractals indicator handle with an invalid handle value class="type">int handle_Alligator = INVALID_HANDLE; class=class="str">"cmt">//--- Initialize alligator indicator handle with an invalid handle value class="type">int handle_AO = INVALID_HANDLE; class=class="str">"cmt">//--- Initialize Awesome Oscillator(AO) handle with an invalid handle value class="type">int handle_AC = INVALID_HANDLE; class=class="str">"cmt">//--- Initialize Accelerator Oscillator(AC) handle with an invalid handle value class="type">class="kw">double fractals_up[]; class=class="str">"cmt">//--- Array to store values for upward fractals class="type">class="kw">double fractals_down[]; class=class="str">"cmt">//--- Array to store values for downward fractals class="type">class="kw">double alligator_jaws[]; class=class="str">"cmt">//--- Array to store values for Alligator&class="macro">#x27;s Jaw line class="type">class="kw">double alligator_teeth[]; class=class="str">"cmt">//--- Array to store values for Alligator&class="macro">#x27;s Teeth line class="type">class="kw">double alligator_lips[]; class=class="str">"cmt">//--- Array to store values for Alligator&class="macro">#x27;s Lips line class="type">class="kw">double ao_values[]; class=class="str">"cmt">//--- Array to store values of the Awesome Oscillator(AO) class="type">class="kw">double ac_color[]; class=class="str">"cmt">//--- Array to store class="type">class="kw">color status of the Accelerator Oscillator(AC) class="macro">#define AC_COLOR_UP class="num">0 class=class="str">"cmt">//--- Define constant for upward AC class="type">class="kw">color state class="macro">#define AC_COLOR_DOWN class="num">1 class=class="str">"cmt">//--- Define constant for downward AC class="type">class="kw">color state class="type">class="kw">double lastFractal_value = class="num">0.0; class=class="str">"cmt">//--- Variable to store the value of the last detected fractal enum fractal_direction {FRACTAL_UP, FRACTAL_DOWN, FRACTAL_NEUTRAL}; class=class="str">"cmt">//--- Enum for fractal direction states fractal_direction lastFractal_direction = FRACTAL_NEUTRAL; class=class="str">"cmt">//--- Variable to store the direction of the last fractal class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit(){ class=class="str">"cmt">//--- class=class="str">"cmt">//--- class="kw">return(INIT_SUCCEEDED); class=class="str">"cmt">//--- Return successful initialization status } handle_Fractals = iFractals(_Symbol,_Period); class=class="str">"cmt">//--- Initialize the fractals indicator handle if (handle_Fractals == INVALID_HANDLE){ class=class="str">"cmt">//--- Check if the fractals indicator failed to initialize Print("ERROR: UNABLE TO INITIALIZE THE FRACTALS INDICATOR. REVERTING NOW!"); class=class="str">"cmt">//--- Print error if fractals initialization failed class="kw">return (INIT_FAILED); class=class="str">"cmt">//--- Exit initialization with failed status } handle_Alligator = iAlligator(_Symbol,_Period,class="num">13,class="num">8,class="num">8,class="num">5,class="num">5,class="num">3,MODE_SMMA,PRICE_MEDIAN); class=class="str">"cmt">//--- Initialize the alligator indicator with specific settings if (handle_Alligator == INVALID_HANDLE){ class=class="str">"cmt">//--- Check if the alligator indicator failed to initialize Print("ERROR: UNABLE TO INITIALIZE THE ALLIGATOR INDICATOR. REVERTING NOW!"); class=class="str">"cmt">//--- Print error if alligator initialization failed class="kw">return (INIT_FAILED); class=class="str">"cmt">//--- Exit initialization with failed status }
把四大指标句柄挂上图表窗口
在 MT5 的 EA 初始化阶段,先拿指标句柄再往图表上挂,是避免运行时报空引用的第一步。下面这段直接在 OnInit 里把 AO、AC 以及主图的 Fractals、Alligator 全部接好,并逐个判断 INVALID_HANDLE,任一失败就 return(INIT_FAILED) 让 EA 停止加载。 注意 ChartIndicatorAdd 的第二个参数:主图窗口编号是 0,AO 被塞进子窗口 1,AC 进子窗口 2。若你手动拖过指标导致子窗口序号变动,这段代码可能挂错位置,需要在 MT5 里先清空图表再测。 最后用 ArraySetAsSeries 把 fractals_up、fractals_down、alligator_jaws 等数组设为时间序列(true),保证 buffer[0] 对应最新 K 线。外汇与贵金属杠杆高,指标初始化失败若没拦住,后续取 buffer 会直接崩策略,务必在回测前确认四个句柄 ID 都正常打印。
handle_AO = iAO(_Symbol,_Period); class=class="str">"cmt">//--- Initialize the Awesome Oscillator(AO) indicator handle if (handle_AO == INVALID_HANDLE){ class=class="str">"cmt">//--- Check if AO indicator failed to initialize Print("ERROR: UNABLE TO INITIALIZE THE AO INDICATOR. REVERTING NOW!"); class=class="str">"cmt">//--- Print error if AO initialization failed class="kw">return (INIT_FAILED); class=class="str">"cmt">//--- Exit initialization with failed status } handle_AC = iAC(_Symbol,_Period); class=class="str">"cmt">//--- Initialize the Accelerator Oscillator(AC) indicator handle if (handle_AC == INVALID_HANDLE){ class=class="str">"cmt">//--- Check if AC indicator failed to initialize Print("ERROR: UNABLE TO INITIALIZE THE AC INDICATOR. REVERTING NOW!"); class=class="str">"cmt">//--- Print error if AC initialization failed class="kw">return (INIT_FAILED); class=class="str">"cmt">//--- Exit initialization with failed status } if (!ChartIndicatorAdd(class="num">0,class="num">0,handle_Fractals)){ class=class="str">"cmt">//--- Add the fractals indicator to the main chart window and check for success Print("ERROR: UNABLE TO ADD THE FRACTALS INDICATOR TO CHART. REVERTING NOW!"); class=class="str">"cmt">//--- Print error if fractals addition failed class="kw">return (INIT_FAILED); class=class="str">"cmt">//--- Exit initialization with failed status } if (!ChartIndicatorAdd(class="num">0,class="num">0,handle_Alligator)){ class=class="str">"cmt">//--- Add the alligator indicator to the main chart window and check for success Print("ERROR: UNABLE TO ADD THE ALLIGATOR INDICATOR TO CHART. REVERTING NOW!"); class=class="str">"cmt">//--- Print error if alligator addition failed class="kw">return (INIT_FAILED); class=class="str">"cmt">//--- Exit initialization with failed status } if (!ChartIndicatorAdd(class="num">0,class="num">1,handle_AO)){ class=class="str">"cmt">//--- Add the AO indicator to a separate subwindow and check for success Print("ERROR: UNABLE TO ADD THE AO INDICATOR TO CHART. REVERTING NOW!"); class=class="str">"cmt">//--- Print error if AO addition failed class="kw">return (INIT_FAILED); class=class="str">"cmt">//--- Exit initialization with failed status } if (!ChartIndicatorAdd(class="num">0,class="num">2,handle_AC)){ class=class="str">"cmt">//--- Add the AC indicator to a separate subwindow and check for success Print("ERROR: UNABLE TO ADD THE AC INDICATOR TO CHART. REVERTING NOW!"); class=class="str">"cmt">//--- Print error if AC addition failed class="kw">return (INIT_FAILED); class=class="str">"cmt">//--- Exit initialization with failed status } Print("HANDLE ID FRACTALS = ",handle_Fractals); class=class="str">"cmt">//--- Print the handle ID for fractals Print("HANDLE ID ALLIGATOR = ",handle_Alligator); class=class="str">"cmt">//--- Print the handle ID for alligator Print("HANDLE ID AO = ",handle_AO); class=class="str">"cmt">//--- Print the handle ID for AO Print("HANDLE ID AC = ",handle_AC); class=class="str">"cmt">//--- Print the handle ID for AC ArraySetAsSeries(fractals_up,true); class=class="str">"cmt">//--- Set the fractals_up array as a time series ArraySetAsSeries(fractals_down,true); class=class="str">"cmt">//--- Set the fractals_down array as a time series ArraySetAsSeries(alligator_jaws,true); class=class="str">"cmt">//--- Set the alligator_jaws array as a time series
「把鳄鱼与震荡指标塞进时间序列」
在 MT5 的 EA 初始化阶段,先把几个数组按时间序列排好,是后续取「最新值」不出错的前提。下面四行把 alligator_teeth、alligator_lips、ao_values、ac_color 全部设为 true,意味着索引 0 永远对应当前 K 线,回看历史只要加下标即可。 ArraySetAsSeries(alligator_teeth,true); // 将牙齿线数组设为时间序列 ArraySetAsSeries(alligator_lips,true); // 将嘴唇线数组设为时间序列 ArraySetAsSeries(ao_values,true); // 将 AO 数值数组设为时间序列 ArraySetAsSeries(ac_color,true); // 将 AC 颜色数组设为时间序列 OnInit 里依次用 iFractals、iAlligator、iAO、iAC 拿句柄,参数上鳄鱼用的是 (13,8,8,5,5,3) 这组经典设置,平滑方式 MODE_SMMA、价格 PRICE_MEDIAN。任何一个句柄返回 INVALID_HANDLE 就 Print 报错并 return INIT_FAILED,EA 不会带着残废指标硬跑。 外汇与贵金属杠杆高,指标初始化失败若被忽略,可能让策略在错误数据上开仓,实盘前务必在策略测试器里确认四个句柄都正常返回。最后用 ChartIndicatorAdd(0,0,handle_Fractals) 把分形画到主图,返回 false 同样走 INIT_FAILED,避免主图缺了分形导致形态识别逻辑失效。
ArraySetAsSeries(alligator_teeth,true); class=class="str">"cmt">//--- Set the alligator_teeth array as a time series ArraySetAsSeries(alligator_lips,true); class=class="str">"cmt">//--- Set the alligator_lips array as a time series ArraySetAsSeries(ao_values,true); class=class="str">"cmt">//--- Set the ao_values array as a time series ArraySetAsSeries(ac_color,true); class=class="str">"cmt">//--- Set the ac_color array as a time series class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit(){ class=class="str">"cmt">//--- handle_Fractals = iFractals(_Symbol,_Period); class=class="str">"cmt">//--- Initialize the fractals indicator handle if (handle_Fractals == INVALID_HANDLE){ class=class="str">"cmt">//--- Check if the fractals indicator failed to initialize Print("ERROR: UNABLE TO INITIALIZE THE FRACTALS INDICATOR. REVERTING NOW!"); class=class="str">"cmt">//--- Print error if fractals initialization failed class="kw">return (INIT_FAILED); class=class="str">"cmt">//--- Exit initialization with failed status } handle_Alligator = iAlligator(_Symbol,_Period,class="num">13,class="num">8,class="num">8,class="num">5,class="num">5,class="num">3,MODE_SMMA,PRICE_MEDIAN); class=class="str">"cmt">//--- Initialize the alligator indicator with specific settings if (handle_Alligator == INVALID_HANDLE){ class=class="str">"cmt">//--- Check if the alligator indicator failed to initialize Print("ERROR: UNABLE TO INITIALIZE THE ALLIGATOR INDICATOR. REVERTING NOW!"); class=class="str">"cmt">//--- Print error if alligator initialization failed class="kw">return (INIT_FAILED); class=class="str">"cmt">//--- Exit initialization with failed status } handle_AO = iAO(_Symbol,_Period); class=class="str">"cmt">//--- Initialize the Awesome Oscillator(AO) indicator handle if (handle_AO == INVALID_HANDLE){ class=class="str">"cmt">//--- Check if AO indicator failed to initialize Print("ERROR: UNABLE TO INITIALIZE THE AO INDICATOR. REVERTING NOW!"); class=class="str">"cmt">//--- Print error if AO initialization failed class="kw">return (INIT_FAILED); class=class="str">"cmt">//--- Exit initialization with failed status } handle_AC = iAC(_Symbol,_Period); class=class="str">"cmt">//--- Initialize the Accelerator Oscillator(AC) indicator handle if (handle_AC == INVALID_HANDLE){ class=class="str">"cmt">//--- Check if AC indicator failed to initialize Print("ERROR: UNABLE TO INITIALIZE THE AC INDICATOR. REVERTING NOW!"); class=class="str">"cmt">//--- Print error if AC initialization failed class="kw">return (INIT_FAILED); class=class="str">"cmt">//--- Exit initialization with failed status } if (!ChartIndicatorAdd(class="num">0,class="num">0,handle_Fractals)){ class=class="str">"cmt">//--- Add the fractals indicator to the main chart window and check for success Print("ERROR: UNABLE TO ADD THE FRACTALS INDICATOR TO CHART. REVERTING NOW!"); class=class="str">"cmt">//--- Print error if fractals addition failed class="kw">return (INIT_FAILED); class=class="str">"cmt">//--- Exit initialization with failed status }
◍ 把鳄鱼AOAC挂上图表并初始化序列
EA 初始化阶段要把比尔·威廉姆斯的几个指标句柄绑到图表上,主窗口放鳄鱼(子窗口 0),AO 放子窗口 1,AC 放子窗口 2。任何一次 ChartIndicatorAdd 返回 false 都应立刻 Print 错误并 return INIT_FAILED,否则后续 CopyBuffer 会拿到空句柄。 下面这段是实际的挂载与兜底写法,三个指标分开判断、互不牵连: if (!ChartIndicatorAdd(0,0,handle_Alligator)){ Print("ERROR: UNABLE TO ADD THE ALLIGATOR INDICATOR TO CHART. REVERTING NOW!"); return (INIT_FAILED); } if (!ChartIndicatorAdd(0,1,handle_AO)){ Print("ERROR: UNABLE TO ADD THE AO INDICATOR TO CHART. REVERTING NOW!"); return (INIT_FAILED); } if (!ChartIndicatorAdd(0,2,handle_AC)){ Print("ERROR: UNABLE TO ADD THE AC INDICATOR TO CHART. REVERTING NOW!"); return (INIT_FAILED); } 挂载成功后把四个句柄 ID 用 Print 打出来,方便在 MT5 Experts 日志里核对 handle_Fractals / handle_Alligator / handle_AO / handle_AC 是否非零。外汇与贵金属波动剧烈,指标句柄失效可能导致信号误判,实盘前务必在策略测试器跑一遍初始化日志。 所有用于计算的数组都要用 ArraySetAsSeries(...,true) 设成时间序列,否则 fractals_up、alligator_jaws、ao_values 这类数组下标 0 会指向最旧一根 K 线,CopyBuffer 取到的就不是当前柱。初始化末尾 return INIT_SUCCEEDED 即代表挂载与序列设置完成。 OnTick 里先把分形上沿数据拉进来,从偏移 2 开始取 3 根(跳过当前未闭合的两根): if (CopyBuffer(handle_Fractals,0,2,3,fractals_up) < 3){ Print("ERROR: UNABLE TO COPY THE FRACTALS UP DATA. REVERTING!"); return; } 少于 3 个值说明复制失败,直接 return 不往下走,避免空数组参与条件判断。
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); } Print("HANDLE ID FRACTALS = ",handle_Fractals); Print("HANDLE ID ALLIGATOR = ",handle_Alligator); Print("HANDLE ID AO = ",handle_AO); Print("HANDLE ID AC = ",handle_AC); 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); } class="type">void OnTick(){ } if (CopyBuffer(handle_Fractals,class="num">0,class="num">2,class="num">3,fractals_up) < class="num">3){ Print("ERROR: UNABLE TO COPY THE FRACTALS UP DATA. REVERTING!"); class="kw">return; }
把鳄鱼与分形数据搬进缓冲区再判新K
在 MT5 自定义指标里,先把分形、鳄鱼三线和 AO、AC 的缓冲区一次性拉满,是后续信号判断的前提。下面这段逻辑对每个 CopyBuffer 都做了小于 3 的返回值校验——也就是说,若任意一项连最近 3 根 K 的数据都拷不下来,直接打印错误并 return,避免拿空数组去算信号。 向下分形取的是缓冲区索引 1、起始位置 2、要 3 个值;鳄鱼颚线(0)、齿线(1)、唇线(2)都从当前柱(0)开始取 3 个;AO 取 0 号缓冲 3 个;AC 取 1 号缓冲(颜色数据)3 个。实际跑的时候,若你切换到了 tick 数据少的品种,这类拷贝失败会在日志里直接看到 REVERTING 提示。 新 K 线判定用了一个静态变量 prevBars 存上次柱数,iBars(_Symbol,_Period) 拿到当前柱数,不等就说明出了新 bar。这个写法比用时间比对省事,但注意它只认「柱数变化」,若历史回拔导致柱数回退不会触发。 最后用 index_fractal=0 去读最新一根分形:向上分形不为 EMPTY_VALUE 就记 lastFractal_value 并标 FRACTAL_UP,向下同理标 FRACTAL_DOWN。外汇与贵金属杠杆高,分形假突破频繁,这个方向标记只作概率参考,实盘须结合鳄鱼开口状态过滤。
if (CopyBuffer(handle_Fractals,class="num">1,class="num">2,class="num">3,fractals_down) < class="num">3){ class=class="str">"cmt">//--- Copy downward fractals data; check if copying is successful Print("ERROR: UNABLE TO COPY THE FRACTALS DOWN DATA. REVERTING!"); class=class="str">"cmt">//--- Print error message if failed class="kw">return; } if (CopyBuffer(handle_Alligator,class="num">0,class="num">0,class="num">3,alligator_jaws) < class="num">3){ class=class="str">"cmt">//--- Copy Alligator&class="macro">#x27;s Jaw data Print("ERROR: UNABLE TO COPY THE ALLIGATOR JAWS DATA. REVERTING!"); class="kw">return; } if (CopyBuffer(handle_Alligator,class="num">1,class="num">0,class="num">3,alligator_teeth) < class="num">3){ class=class="str">"cmt">//--- Copy Alligator&class="macro">#x27;s Teeth data Print("ERROR: UNABLE TO COPY THE ALLIGATOR TEETH DATA. REVERTING!"); class="kw">return; } if (CopyBuffer(handle_Alligator,class="num">2,class="num">0,class="num">3,alligator_lips) < class="num">3){ class=class="str">"cmt">//--- Copy Alligator&class="macro">#x27;s Lips data Print("ERROR: UNABLE TO COPY THE ALLIGATOR LIPS DATA. REVERTING!"); class="kw">return; } if (CopyBuffer(handle_AO,class="num">0,class="num">0,class="num">3,ao_values) < class="num">3){ class=class="str">"cmt">//--- Copy AO data Print("ERROR: UNABLE TO COPY THE AO DATA. REVERTING!"); class="kw">return; } if (CopyBuffer(handle_AC,class="num">1,class="num">0,class="num">3,ac_color) < class="num">3){ class=class="str">"cmt">//--- Copy AC class="type">class="kw">color data Print("ERROR: UNABLE TO COPY THE AC COLOR DATA. REVERTING!"); class="kw">return; } if (isNewBar()){ class=class="str">"cmt">//--- Check if a new bar has formed class=class="str">"cmt">//--- } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| IS NEW BAR FUNCTION | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool isNewBar(){ class="kw">static class="type">int prevBars = class="num">0; class=class="str">"cmt">//--- Store previous bar count class="type">int currBars = iBars(_Symbol,_Period); class=class="str">"cmt">//--- Get current bar count for the symbol and period if (prevBars == currBars) class="kw">return (false); class=class="str">"cmt">//--- If bars haven&class="macro">#x27;t changed, class="kw">return false prevBars = currBars; class=class="str">"cmt">//--- Update previous bar count class="kw">return (true); class=class="str">"cmt">//--- Return true if new bar is detected } const class="type">int index_fractal = class="num">0; if (fractals_up[index_fractal] != EMPTY_VALUE){ class=class="str">"cmt">//--- Detect upward fractal presence lastFractal_value = fractals_up[index_fractal]; class=class="str">"cmt">//--- Store fractal value lastFractal_direction = FRACTAL_UP; class=class="str">"cmt">//--- Set last fractal direction as up } if (fractals_down[index_fractal] != EMPTY_VALUE){ class=class="str">"cmt">//--- Detect downward fractal presence lastFractal_value = fractals_down[index_fractal]; lastFractal_direction = FRACTAL_DOWN; }