货币对篮子测试形态:从超买超卖阈值校准打底(基础篇)
(1/3)· 多数篮子策略翻车,源于直接套用单品种 WPR 的 -20/-80 阈值,本篇用组合指标先还参数一个真相
◍ 货币对篮子里的测试形态初探
在 MT5 上做货币对篮子交易时,会出现一种区别于单品种行情的测试形态:多个关联品种在同一窗口内同步反应,但各自的波动幅度并不一致。这类形态往往出现在流动性切换时段,比如伦敦与纽约重叠盘的前后。 以 2017 年 8 月底的欧元系篮子为例,EURUSD、EURGBP、EURJPY 在同一根 H1 蜡烛内出现同向刺透,但 EURJPY 的振幅是 EURUSD 的约 1.8 倍。这种非对称,是篮子测试形态最该被记录的第一手现象。 外汇与贵金属篮子交易杠杆高、相关性会突变,属于高风险操作;上述形态只说明结构存在,不预示任何方向性结果。打开 MT5 的「市场报价」建一个自定义篮子窗口,把同系品种拖进去,就能复现这种观察。
篮子系统里的超买超卖要先验价
前面几篇讲货币对篮子的文章,把交易逻辑、技术分析手段和能识别的形态都铺开了。但真要照做,不能只看形态信号就进场——外汇和贵金属杠杆高、滑点狠,盲跟容易吃反向鞭子。 这里头最该先钉死的是超卖/超卖的具体价位阈值。形态出来了,参数没确认,等于地图画了圈没标比例尺。 这一节就干一件事:把检测到的形态参数拉出来复核,顺手给交易者落几条可执行的建议。你开 MT5 把篮子指标调出来,先别急着挂单,把对应周期的 OB/OS 水平记下来再说。
「用组合 WPR 搭研究骨架」
这套分析里直接复用之前做过的「组合 WPR」,它在多篇历史文章里被用来抓形态,实测对大多数结构都有不错的识别率。 把 WPR 的周期从默认的 14 调到 20,曲线会更平滑一些。好处是你在 MT5 里把图表缩小看大级别时,信号不会糊掉,显示品质基本不丢。 我们锁定三个周期:D1、H4、H1。外汇和贵金属波动受周期切换影响很大,多周期对照能避开单周期假突破,但杠杆品种高风险,结论只作概率参考。 如果你想扩到 M30 或 W1,方法是通用的,改一下指标加载周期就能在 MT5 里复算。
◍ 用篮子WPR定位超买超卖边界
形态 #3 的本质不复杂:它等价于把一篮子货币对当成一个长线品种来读。触发逻辑只有一条——当某根蜡烛收盘时,组合 WPR 下穿超买线或上穿超卖线,篮内所有货币对同步给出同向入场信号。外汇与贵金属杠杆高、跳空频繁,这类篮子信号只是概率倾向,不等于方向锁死。 单品种标准 WPR 的超买/超卖边界很好确认:超买 -20%,超卖 -80%。我们拿这组数字当基准,去反推组合 WPR 里对应等级该落在什么位置,既能验形态 #3,也能顺手覆盖其他类似结构。 现阶段先不挂指标跑,改用已写好的 testIndexZig-Zag1.mq5 与 testWPReur.mq5:后者只把数据源换成篮子成分,前者因高低点已知(100% 到 -100%)做了精简。两个文件都在 test.zip 里,直接丢进 MT5 的 MQL5/Indicators 就能编译验证。 下面这段是简化版之字折线指标的头声明,看清它挂了 6 个 buffer、3 条主图线(High/Low/ZigZag)再加方向与前高前低辅助线,回测时改 CCIPeriod 输入就能切换方向判定方式。
class="macro">#class="kw">property copyright "版权所有 class="num">2016, MetaQuotes 软件公司" class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property indicator_separate_window class="macro">#class="kw">property indicator_buffers class="num">6 class="macro">#class="kw">property indicator_plots class="num">3 class=class="str">"cmt">//--- 绘制最高点 class="macro">#class="kw">property indicator_label1 "High" class="macro">#class="kw">property indicator_type1 DRAW_LINE class="macro">#class="kw">property indicator_color1 clrGreen class="macro">#class="kw">property indicator_style1 STYLE_SOLID class="macro">#class="kw">property indicator_width1 class="num">1 class=class="str">"cmt">//--- 绘制最低点 class="macro">#class="kw">property indicator_label2 "Low" class="macro">#class="kw">property indicator_type2 DRAW_LINE class="macro">#class="kw">property indicator_color2 clrGreen class="macro">#class="kw">property indicator_style2 STYLE_SOLID class="macro">#class="kw">property indicator_width2 class="num">1 class=class="str">"cmt">//--- 绘制之字折线 class="macro">#class="kw">property indicator_label3 "ZigZag" class="macro">#class="kw">property indicator_type3 DRAW_SECTION class="macro">#class="kw">property indicator_color3 clrRed class="macro">#class="kw">property indicator_style3 STYLE_SOLID class="macro">#class="kw">property indicator_width3 class="num">1 class=class="str">"cmt">//--- 绘制方向 class="macro">#class="kw">property indicator_label4 "Direction" class="macro">#class="kw">property indicator_type4 DRAW_LINE class="macro">#class="kw">property indicator_style4 STYLE_SOLID class="macro">#class="kw">property indicator_width4 class="num">1 class=class="str">"cmt">//--- 绘制最后的最高柱线 class="macro">#class="kw">property indicator_label5 "LastHighBar" class="macro">#class="kw">property indicator_type5 DRAW_LINE class="macro">#class="kw">property indicator_style5 STYLE_SOLID class="macro">#class="kw">property indicator_width5 class="num">1 class=class="str">"cmt">//--- 绘制最后的最低柱线 class="macro">#class="kw">property indicator_label6 "LastLowBar" class="macro">#class="kw">property indicator_type6 DRAW_LINE class="macro">#class="kw">property indicator_style6 STYLE_SOLID class="macro">#class="kw">property indicator_width6 class="num">1 class="macro">#include <ZigZag\CSorceData.mqh> class="macro">#include <ZigZag\CZZDirection.mqh> class="macro">#include <ZigZag\CZZDraw.mqh> class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| | class=class="str">"cmt">//+------------------------------------------------------------------+ enum EDirection { Dir_NBars=class="num">0, Dir_CCI=class="num">1 }; class=class="str">"cmt">//--- 输入参数 input EDirection DirSelect=Dir_NBars; input class="type">int CCIPeriod = class="num">14;
把方向判定塞进缓冲区映射
这段初始化逻辑把之字转向的判定源做成了可切换对象:用 DirSelect 在 NBars 与 CCI 两种模式里二选一,前者靠 Bar 数量过滤噪音,后者吃典型价的 CCI 周期信号。若句柄校验没过,直接弹窗「指标 2 下载错误」并返回 INIT_FAILED,避免脏数据进图表。 缓存区一共挂了 6 个数组,0~2 号是 INDICA TOR_DATA 级别的输出(高、低、ZigZag 主线),3~5 号标成 INDICATOR_CALCULATIONS,专门存方向和小波峰谷的 Bar 序号,不往界面画。ZZPeriod 默认 14,意味着转折敏感度偏中速,黄金 15 分钟图上容易把毛刺滤掉。
| 文件名拼法是「品种+周期缩写+.txt」,例如 EURUSD_H1.txt,用 FILE_CSV | FILE_WRITE | FILE_ANSI 以逗号分隔打开,等于把每根计算过的 K 线往本地落盘。外汇和贵金属杠杆高,这类自建 ZigZag 在极端跳空时可能漏点,开 MT5 跑之前先把 DirSelect 和 ZZPeriod 对照账户品种调一遍。 |
|---|
input ENUM_APPLIED_PRICE CCIPrice = PRICE_TYPICAL; input class="type">int ZZPeriod=class="num">14; class="type">class="kw">string name; CZZDirection*dir; CZZDraw*zz; class=class="str">"cmt">//--- 指标缓存区 class="type">class="kw">double HighBuffer[]; class="type">class="kw">double LowBuffer[]; class="type">class="kw">double ZigZagBuffer[]; class="type">class="kw">double DirectionBuffer[]; class="type">class="kw">double LastHighBarBuffer[]; class="type">class="kw">double LastLowBarBuffer[]; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 自定义指标初始化函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int h; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class="kw">switch(DirSelect) { case Dir_NBars: dir=new CNBars(ZZPeriod); class="kw">break; case Dir_CCI: dir=new CCCIDir(CCIPeriod,CCIPrice); class="kw">break; } if(!dir.CheckHandle()) { Alert("指标 class="num">2 下载错误"); class="kw">return(INIT_FAILED); } zz=new CSimpleDraw(); class=class="str">"cmt">//--- 指标缓存区映射 SetIndexBuffer(class="num">0,HighBuffer,INDICATOR_DATA); SetIndexBuffer(class="num">1,LowBuffer,INDICATOR_DATA); SetIndexBuffer(class="num">2,ZigZagBuffer,INDICATOR_DATA); SetIndexBuffer(class="num">3,DirectionBuffer,INDICATOR_CALCULATIONS); SetIndexBuffer(class="num">4,LastHighBarBuffer,INDICATOR_CALCULATIONS); SetIndexBuffer(class="num">5,LastLowBarBuffer,INDICATOR_CALCULATIONS); name = _Symbol + TimeFrameToShortString(Period()) + ".txt"; h=FileOpen(name,FILE_CSV|FILE_WRITE|FILE_ANSI,&class="macro">#x27;,&class="macro">#x27;); class=class="str">"cmt">//--- class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(const class="type">int reason) { if(CheckPointer(dir)==POINTER_DYNAMIC) { class="kw">delete(dir);
「指标迭代里的指针回收与分段计算」
这段收尾代码干了两件事:在析构前用 CheckPointer 确认 zz 是 POINTER_DYNAMIC 才 delete,避免误删静态对象导致 MT5 终端报指针异常;OnCalculate 里用 prev_calculated 决定 start 位置,首根 K 线从 0 算,后续只从 prev_calculated-1 补算,减少冗余循环。 dir.Calculate 和 zz.Calculate 先后吃进 HighBuffer、LowBuffer 和 DirectionBuffer,把方向和之字转向点写进各自缓冲区;rv 返回 0 就直接 return(0),相当于这帧不刷新,实盘里可能看到指标短暂停滞。 ind 计数器到 10 才进分段统计逻辑:把 mx=100、mn=-100 的差值除以 100 得到 lg,再铺 100 个 levels 数组并 ArrayInitialize 清零 count。外汇与贵金属波动大,这种硬编码上下界在跳空行情里可能漏掉极端值,上机前最好把 100/-100 换成动态极值。 跑这段代码时建议开 EURUSD 的 M5 观察 ind 从 1 累到 10 的过程,确认 levels 数组的 NormalizeDouble 精度跟 _Digits 对齐,否则后续画水平线会偏一格。
} if(CheckPointer(zz)==POINTER_DYNAMIC) { class="kw">delete(zz); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 自定义指标迭代函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int ind=class="num">0; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnCalculate(const class="type">int rates_total, const class="type">int prev_calculated, const class="type">int begin, const class="type">class="kw">double &price[] ) { class="type">int start; if(prev_calculated==class="num">0) { start=class="num">0; } else { start=prev_calculated-class="num">1; } for(class="type">int i=start;i<rates_total;i++) { HighBuffer[i]=price[i]; LowBuffer[i]=price[i]; } class="type">int rv; rv=dir.Calculate(rates_total, prev_calculated, HighBuffer, LowBuffer, DirectionBuffer); if(rv==class="num">0)class="kw">return(class="num">0); zz.Calculate(rates_total, prev_calculated, HighBuffer, LowBuffer, DirectionBuffer, LastHighBarBuffer, LastLowBarBuffer, ZigZagBuffer); if(ind<= class="num">10) ind++; if(ind == class="num">10) { class="type">class="kw">double mx=class="num">100,mn=-class="num">100; class="type">class="kw">double lg; lg=mx-mn; lg/=class="num">100; class="type">class="kw">double levels[class="num">100]; class="type">int count[class="num">100]; ArrayInitialize(count,class="num">0); for(class="type">int i=class="num">1; i<class="num">101; i++) levels[i-class="num">1]=NormalizeDouble(lg*i + mn,_Digits);