货币对篮子测试形态:从超买超卖阈值校准打底(基础篇)
📊

货币对篮子测试形态:从超买超卖阈值校准打底(基础篇)

(1/3)· 多数篮子策略翻车,源于直接套用单品种 WPR 的 -20/-80 阈值,本篇用组合指标先还参数一个真相

含代码示例偏理论 第 1/3 篇
把单货币对的超买 -20%、超卖 -80% 直接搬进篮子组合 WPR,是新手最容易踩的坑。组合指标波动范围虽同为 -100%~+100%,但成分对冲让极值分布完全两样。不先校准阈值就跟着信号入场,外汇贵金属这种高杠杆品种可能反复吃假突破。

◍ 货币对篮子里的测试形态初探

在 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 输入就能切换方向判定方式。

MQL5 / C++
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_CSVFILE_WRITEFILE_ANSI 以逗号分隔打开,等于把每根计算过的 K 线往本地落盘。外汇和贵金属杠杆高,这类自建 ZigZag 在极端跳空时可能漏点,开 MT5 跑之前先把 DirSelect 和 ZZPeriod 对照账户品种调一遍。
MQL5 / C++
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 对齐,否则后续画水平线会偏一格。

MQL5 / C++
  }
  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);
交给小布盯盘看组合极值
这些篮子 WPR 的跨周期极值分布,小布盯盘的 AIGC 已内置,打开对应品种页即可看到 D1/H4/H1 三线越界统计,你只需判断形态是否成立。

常见问题

单品种 WPR 极值由自身波动决定,篮子因多成分对冲,同等阈值下触达频率更低,需按组合历史重新定位等级。
拉长周期可轻微平滑指标线,相当于缩小图表噪点而不丢结构,便于跨 D1/H4/H1 统一观察形态。
可以,小布内置了组合 WPR 的跨周期越界计数,省去手动挂 testIndexZig-Zag1 这类脚本的重复劳动。
逻辑通用,只要指标输入支持多符号合成,把逐笔报价换成篮子加权即可,后续篇会拆实战改写。
蜡烛收盘时组合 WPR 下穿或上穿设定等级,即向篮内全部货币对发同向入场信号,具体等级见本篇校准结果。