交易货币篮子形态·基础篇:用 WPR 重构看清篮子强弱信号(基础篇)
🧺

交易货币篮子形态·基础篇:用 WPR 重构看清篮子强弱信号(基础篇)

(1/3)·多数人盯单一货币对却亏在篮子联动,本文先铺术语与指标改造底层的逻辑

含代码示例偏理论 第 1/3 篇
把 WPR 原生的 0~-100% 直接套到货币篮子上,超买超卖线会失真。先改指标值域再到 100~-100%,才看得清篮子整体拐弯的位置。

◍ 货币篮子里的形态识别起点

在 MT5 中做货币篮子交易,形态识别不能只盯单一图表。把若干相关性弱的货币对放进一个观察组,同一类价格行为会在不同品种上同步或错位出现,这比单品种信号更有参考价值。 原文作者 Andrei Novichkov 在 2017 年 2 月 2 日发布该主题,截至统计显示阅读量 6 658、收藏 9,说明篮子交易形态在早期就有人气积累。 实操上,先开 MT5 的「市场观察」右键添加一组主要货币对,切到相同周期(如 H1),用肉眼或脚本扫「双顶 / 双底 / 旗形」是否跨品种共现。若出现 3 个以上品种同形态,该篮子方向倾向有更高确认度,但外汇与贵金属属高风险,跨品种共振也可能被突发事件打断。

「把 WPR 改造成篮子专用尺」

做货币篮子不能只靠之前讲的原则和术语,入场离场信号才是能不能跑通的关键。本文目标很直接:把处理篮子时已形成的、能明显抬升盈利概率的形态拆清楚,术语沿用之前那套篮子与技术工具定义。 分析篮子状态用威廉姆斯百分比范围(WPR)。原版 WPR 数值落在 0% 到 -100%,对篮子形态识别不够用——正确显示全部形态要求数值区间扩到 100% 到 -100%,这个改动在指标代码里改几行就能完成。 原版超卖/超买阈值在合并后的区间里依然有效,我们会直接拿来用。你在 MT5 里打开 WPR 源码,把 UpperBuffer 和 LowerBuffer 的映射改掉,就能验证区间扩展后形态是否更完整。外汇与贵金属篮子组合波动叠加,属高风险玩法,改参数前先开模拟盘跑一遍。

动态入场的两种篮子形态陷阱

动态入场指的是指标信号触达特定阈值就下单,不等当前 K 线收线。以货币篮子 WPR 为例,当组合读数逼近区间上沿或下沿(比如 ±90% 以外),往往对应某币种因事件驱动被集中买压或卖压。此时若按动态逻辑入场,等于赌反转或延续会在信号点立刻发生。 形态 #1 是教科书里最常见的:指标几乎触碰边界后反向突破超买/超卖线,被当作篮子内全部货币的趋势跟随信号。权威资料几乎一致反对动态执行这类入场,实盘里追边界反转的滑点和假突破概率都不友好,不建议采用。 形态 #2 是它的逆势改版:指标贴近边界却随原趋势再反转,试图抓趋势起点。WPR 本就不是趋势指标,能在边界外徘徊很久只微偏再贴近。2015 年 1 月瑞郎黑天鹅前后,H1 读数在 12:51:32 达到 99.16、H4 99.17、D1 99.17,两秒后降到 98.85 附近,反复贴近边界——任何这时动态逆势接刀的人基本秒触止损。 有些交易者只在读数高于 90% 或低于 -90% 才用形态 #2,并辅以价格行为确认,且避开刚出的基本面消息、只在已成趋势里找末端迹象。但失败率奇高,外汇与贵金属杠杆环境下这种逆势动态入场风险极大,我也不推荐。

◍ 收盘后再确认信号,篮子交易别踩限价坑

组合 WPR 在母指标基础上做了归一:区间仍是 -100% 到 100%,但超买线划在 60%、超卖线在 -60%,而非单货币 WPR 常见的 -20% / -80%。只有当当前时间帧蜡烛完全收盘,组合线向下穿越 60% 或向上穿越 -60%,才把篮子内全部货币对视为同向下单信号——这根收盘棒是形态 #3 不可省略的过滤器。 顺带注意,边界突破往往伴生指标在极值附近的反转,意味着一段虽短但真实的价格摆动,所以信号本质是顺这一摆动入场。下单可用市价,也可挂限价;但篮子单若用限价,窗口内未必每对都触价。 实战里容易吃暗亏:比如 EUR 篮子组合线从 -60% 下方反转向上,你买多欧元篮子,可 EURJPY 因紧接着出日元利好,日元涨得比欧元快,这对反而走平甚至拐跌。若限价单只触发了 EURJPY 这一对,整体就是亏的,其他无新消息的欧系叉盘盈利也没接上。外汇与贵金属属高风险品种,篮子信号不代表每对必同向。 下面这段 MT5 指标把 7 个欧系对 WPR(14) 等权平均再映射到 ±100 区间,可直接拖到 H1 验证上面说的级别位置。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                      testWPR.mq5 |
class=class="str">"cmt">//|                                        MetaQuotes 软件公司|
class=class="str">"cmt">//|                                               http://fxstill.com |
class=class="str">"cmt">//+------------------------------------------------------------------+
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_minimum - class="num">100
class="macro">#class="kw">property indicator_maximum class="num">100
class="macro">#class="kw">property indicator_buffers class="num">1
class="macro">#class="kw">property indicator_plots    class="num">1
input class="type">int      WPR= class="num">14 ;                     class=class="str">"cmt">// WRP 周期
input class="type">color    clr= clrBlue ;
class="macro">#define LG class="num">7
class="type">class="kw">string pair[]={ "EURUSD" , "EURJPY" , "EURCHF" , "EURGBP" , "EURNZD" , "EURCAD" , "EURAUD" };
class="type">int h[LG];
class="type">class="kw">double ind[];
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 自定义指标初始化函数                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//--- 指标缓存区映射
   for ( class="type">int i= class="num">0 ; i<LG; i++)
     {
      h[i]= iWPR(pair[i], class="num">0 ,WPR);
     }
   ArraySetAsSeries(ind, true );
   SetIndexBuffer( class="num">0 ,ind);
   IndicatorSetString( INDICATOR_SHORTNAME , "testWPR" );
   IndicatorSetInteger( INDICATOR_DIGITS , class="num">2 );
   IndicatorSetInteger( INDICATOR_LEVELS , class="num">2 );
   IndicatorSetInteger( INDICATOR_LEVELSTYLE , class="num">0 , STYLE_SOLID );
   IndicatorSetInteger( INDICATOR_LEVELSTYLE , class="num">1 , STYLE_SOLID );
   IndicatorSetInteger( INDICATOR_LEVELCOLOR , class="num">0 , clrRed );
   IndicatorSetInteger( INDICATOR_LEVELCOLOR , class="num">1 , clrRed );
   IndicatorSetInteger( INDICATOR_LEVELWIDTH , class="num">0 , class="num">1 );
   IndicatorSetInteger( INDICATOR_LEVELWIDTH , class="num">1 , class="num">1 );
   IndicatorSetDouble( INDICATOR_LEVELVALUE , class="num">0 ,- class="num">60 );
   IndicatorSetDouble( INDICATOR_LEVELVALUE , class="num">1 , class="num">60 );
   PlotIndexSetInteger( class="num">0 , PLOT_DRAW_TYPE , DRAW_LINE );
   PlotIndexSetInteger( class="num">0 , PLOT_LINE_STYLE , STYLE_SOLID );
   PlotIndexSetInteger( class="num">0 , PLOT_LINE_WIDTH , class="num">2 );
   PlotIndexSetInteger( class="num">0 , PLOT_LINE_COLOR ,clr);
   PlotIndexSetString( class="num">0 , PLOT_LABEL , "_tstWPR_" );
class=class="str">"cmt">//---
   class="kw">return ( INIT_SUCCEEDED );
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double GetValue( class="type">int shift)
  {
   class="type">class="kw">double dBuf[ class="num">1 ];
   class="type">class="kw">double res= class="num">0.0 ;
   for ( class="type">int i= class="num">0 ; i<LG; i++)
     {
       CopyBuffer(h[i], class="num">0 ,shift, class="num">1 ,dBuf);
      res+=dBuf[ class="num">0 ];
     }
class=class="str">"cmt">//结束 for (class="type">int i = class="num">0; i < iCount; i++)
   res=res/LG;
   class="kw">return ( NormalizeDouble((res + class="num">50 ) * class="num">2 , _Digits ) );
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 自定义指标迭代函数                                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnCalculate( class="kw">const class="type">int rates_total,
                 class="kw">const class="type">int prev_calculated,
                 class="kw">const class="type">class="kw">datetime &time[],
                 class="kw">const class="type">class="kw">double &open[],
                 class="kw">const class="type">class="kw">double &high[],
                 class="kw">const class="type">class="kw">double &low[],
                 class="kw">const class="type">class="kw">double &close[],
                 class="kw">const class="type">long &tick_volume[],
                 class="kw">const class="type">long &volume[],
                 class="kw">const class="type">int &spread[])
  {
class=class="str">"cmt">//---
   if (prev_calculated== class="num">0 || rates_total>prev_calculated+ class="num">1 )
     {
       class="type">int rt=rates_total-WPR;
       for ( class="type">int i= class="num">1 ; i<rt; i++)
        {
         ind[i]= GetValue(i);
        }
     }
   else
     {
     }
class=class="str">"cmt">//--- 返回 prev_calculated 值用于下次调用
   class="kw">return (rates_total);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit( class="kw">const class="type">int reason)
  {
   for ( class="type">int i= class="num">0 ; i<LG; i++)
     {
       if (h[i]!= INVALID_HANDLE ) IndicatorRelease(h[i]);
     }
   class="type">class="kw">string text;
   class="kw">switch (reason)
     {
       case REASON_PROGRAM :
         text= "指标通过调用 ExpertRemove() 函数终端其操作" ; class="kw">break ;
       case REASON_INITFAILED :
         text= "此值意味着 OnInit() 处理器 " + __FILE__ + " 已返回一个非零值" ; class="kw">break ;
       case REASON_CLOSE :
         text= "终端已关闭" ; class="kw">break ;
       case REASON_ACCOUNT :
         text= "账户已改变" ; class="kw">break ;
       case REASON_CHARTCHANGE :
         text= "品种或时间帧已改变" ; class="kw">break ;
       case REASON_CHARTCLOSE :
         text= "图表已关闭" ; class="kw">break ;
       case REASON_PARAMETERS :
         text= "输入参数已变更" ; class="kw">break ;
       case REASON_RECOMPILE :
         text= "程序 " + __FILE__ + " 已编译" ; class="kw">break ;
       case REASON_REMOVE :
         text= "程序 " + __FILE__ + " 已从图表上移除" ; class="kw">break ;
       case REASON_TEMPLATE :
         text= "新模板已应用于图表" ; class="kw">break ;
       class="kw">default :text= "其它原因" ;
     }
   PrintFormat( "%s" ,text);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
逐行拆解关键处:#define LG 7 与 pair[] 数组写死 7 个 EUR 叉盘;OnInit 里循环 iWPR(pair[i],0,WPR) 拿到每对句柄存 h[]。GetValue 用 CopyBuffer 取各对当前 shift 的 WPR 值加总除以 7,再 (res+50)*2 把 -50~50 母值拉伸到 -100~100。OnInit 中 IndicatorSetDouble 把两条水平线钉在 -60 与 60,正是前面说的组合超卖/超买边界。OnCalculate 仅在首次或漏算时回填 ind 缓冲,平时直接吐 rates_total。 把指标挂上 EURJPY 的 H1,文章撰写时样本里 2016.10.17 附近三小时棒收盘后出现过上穿 -60 的交叉点,可对照 K 线看信号与随后几根的实际分歧。贵金属与外汇篮子均受消息错位影响,验证时建议先开模拟盘跑一周。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                      testWPR.mq5 |
class=class="str">"cmt">//|                                        MetaQuotes 软件公司|
class=class="str">"cmt">//|                                               http://fxstill.com |
class=class="str">"cmt">//+------------------------------------------------------------------+
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_minimum - class="num">100
class="macro">#class="kw">property indicator_maximum class="num">100
class="macro">#class="kw">property indicator_buffers class="num">1
class="macro">#class="kw">property indicator_plots    class="num">1
input class="type">int      WPR= class="num">14 ;                     class=class="str">"cmt">// WRP 周期
input class="type">color    clr= clrBlue ;
class="macro">#define LG class="num">7
class="type">class="kw">string pair[]={ "EURUSD" , "EURJPY" , "EURCHF" , "EURGBP" , "EURNZD" , "EURCAD" , "EURAUD" };
class="type">int h[LG];
class="type">class="kw">double ind[];
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 自定义指标初始化函数                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//--- 指标缓存区映射
   for ( class="type">int i= class="num">0 ; i<LG; i++)
     {
      h[i]= iWPR(pair[i], class="num">0 ,WPR);
     }
   ArraySetAsSeries(ind, true );
   SetIndexBuffer( class="num">0 ,ind);
   IndicatorSetString( INDICATOR_SHORTNAME , "testWPR" );
   IndicatorSetInteger( INDICATOR_DIGITS , class="num">2 );
   IndicatorSetInteger( INDICATOR_LEVELS , class="num">2 );
   IndicatorSetInteger( INDICATOR_LEVELSTYLE , class="num">0 , STYLE_SOLID );
   IndicatorSetInteger( INDICATOR_LEVELSTYLE , class="num">1 , STYLE_SOLID );
   IndicatorSetInteger( INDICATOR_LEVELCOLOR , class="num">0 , clrRed );
   IndicatorSetInteger( INDICATOR_LEVELCOLOR , class="num">1 , clrRed );
   IndicatorSetInteger( INDICATOR_LEVELWIDTH , class="num">0 , class="num">1 );
   IndicatorSetInteger( INDICATOR_LEVELWIDTH , class="num">1 , class="num">1 );
   IndicatorSetDouble( INDICATOR_LEVELVALUE , class="num">0 ,- class="num">60 );
   IndicatorSetDouble( INDICATOR_LEVELVALUE , class="num">1 , class="num">60 );
   PlotIndexSetInteger( class="num">0 , PLOT_DRAW_TYPE , DRAW_LINE );
   PlotIndexSetInteger( class="num">0 , PLOT_LINE_STYLE , STYLE_SOLID );
   PlotIndexSetInteger( class="num">0 , PLOT_LINE_WIDTH , class="num">2 );
   PlotIndexSetInteger( class="num">0 , PLOT_LINE_COLOR ,clr);
   PlotIndexSetString( class="num">0 , PLOT_LABEL , "_tstWPR_" );
class=class="str">"cmt">//---
   class="kw">return ( INIT_SUCCEEDED );
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double GetValue( class="type">int shift)
  {
   class="type">class="kw">double dBuf[ class="num">1 ];
   class="type">class="kw">double res= class="num">0.0 ;
   for ( class="type">int i= class="num">0 ; i<LG; i++)
     {
       CopyBuffer(h[i], class="num">0 ,shift, class="num">1 ,dBuf);
      res+=dBuf[ class="num">0 ];
     }
class=class="str">"cmt">//结束 for (class="type">int i = class="num">0; i < iCount; i++)
   res=res/LG;
   class="kw">return ( NormalizeDouble((res + class="num">50 ) * class="num">2 , _Digits ) );
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 自定义指标迭代函数                                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnCalculate( class="kw">const class="type">int rates_total,
                 class="kw">const class="type">int prev_calculated,
                 class="kw">const class="type">class="kw">datetime &time[],
                 class="kw">const class="type">class="kw">double &open[],
                 class="kw">const class="type">class="kw">double &high[],
                 class="kw">const class="type">class="kw">double &low[],
                 class="kw">const class="type">class="kw">double &close[],
                 class="kw">const class="type">long &tick_volume[],
                 class="kw">const class="type">long &volume[],
                 class="kw">const class="type">int &spread[])
  {
class=class="str">"cmt">//---
   if (prev_calculated== class="num">0 || rates_total>prev_calculated+ class="num">1 )
     {
       class="type">int rt=rates_total-WPR;
       for ( class="type">int i= class="num">1 ; i<rt; i++)
        {
         ind[i]= GetValue(i);
        }
     }
   else
     {
     }
class=class="str">"cmt">//--- 返回 prev_calculated 值用于下次调用
   class="kw">return (rates_total);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit( class="kw">const class="type">int reason)
  {
   for ( class="type">int i= class="num">0 ; i<LG; i++)
     {
       if (h[i]!= INVALID_HANDLE ) IndicatorRelease(h[i]);
     }
   class="type">class="kw">string text;
   class="kw">switch (reason)
     {
       case REASON_PROGRAM :
         text= "指标通过调用 ExpertRemove() 函数终端其操作" ; class="kw">break ;
       case REASON_INITFAILED :
         text= "此值意味着 OnInit() 处理器 " + __FILE__ + " 已返回一个非零值" ; class="kw">break ;
       case REASON_CLOSE :
         text= "终端已关闭" ; class="kw">break ;
       case REASON_ACCOUNT :
         text= "账户已改变" ; class="kw">break ;
       case REASON_CHARTCHANGE :
         text= "品种或时间帧已改变" ; class="kw">break ;
       case REASON_CHARTCLOSE :
         text= "图表已关闭" ; class="kw">break ;
       case REASON_PARAMETERS :
         text= "输入参数已变更" ; class="kw">break ;
       case REASON_RECOMPILE :
         text= "程序 " + __FILE__ + " 已编译" ; class="kw">break ;
       case REASON_REMOVE :
         text= "程序 " + __FILE__ + " 已从图表上移除" ; class="kw">break ;
       case REASON_TEMPLATE :
         text= "新模板已应用于图表" ; class="kw">break ;
       class="kw">default :text= "其它原因" ;
     }
   PrintFormat( "%s" ,text);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
让小布替你重算篮子 WPR
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到改造后的篮子强弱带,不必自己改指标代码。

常见问题

篮子由多货币合成,合力常冲出原值域边界,转换到 100%~-100% 才能完整显示形态并沿用超买超卖级别。
那是趋势跟随信号且权威来源反对动态执行,追边界反转易在篮子未稳时踩错节奏,概率上不划算。
振荡器不适合趋势市,价格可能延续使亏损扩大,回撤何时发生无保证,财会结果可能偏毁灭性。
可以,对应品种页已内置 AIGC 诊断,改造值域与超买超卖带自动算好,你只管读信号。
前者在指标触值即进不等收线,后者等蜡烛定型再确认,系列后篇会展开形态 #3 的收盘逻辑。