使用计量经济学方法分析图表·综合运用
📊

使用计量经济学方法分析图表·综合运用

(3/3)·把前两篇的时域与非线性模型拼成完整诊断链,避开‘看图形凭感觉’的盲区

含代码示例 第 3/3 篇
很多人把汇率图当纯视觉对象,忽略了波动集群和肥尾这些统计特性。用计量经济模型做诊断,能把‘感觉不对’变成可检验的假设。本篇把前面铺垫的方法收拢成一套可跑的流程。

◍ 用傅里叶共轭算自相关再跑Ljung-Box

这段实现里,先对序列做正向 FFT(F.Forward(nFFT)),随后在循环里把输出搬进 Data21,再与自身的共轭 cData 做乘法。复数乘共轭后虚部抵消,只剩实部,相当于在频域完成了自相关的对称运算,比时域逐 lag 滑动快一个量级。 权重化逆变换 F1.InverseT(nFFT) 之后,用 getComplexArr 把复数结果压成 double 二维数组 rA,再取 rA[i][0] 填进 ACF 并除以 rA[0][0] 做归一化。归一后首项为 1,第 k 项即滞后 k 的自相关系数,可直接拿去画外汇小时线 EURUSD 的 ACF 图验证。 lbqtest 函数封装了 Ljung-Box-Pierce 检验:固定看 lags[3]={10,15,20} 三个滞后,最大 lag 设 20,显著性水平 alpha 全取 0.05。它先调 autocorr 算出 21 长 ACF,再丢掉首项 1 得到 20 长 acf 数组供 Q 统计量使用。 开盘前把这段贴进 MT5 脚本,用最近 500 根 XAUUSD 的 log return 跑一遍,若 H[0]~H[2] 有多个 false,说明在 10/15/20 滞后上序列可能非随机,存在可捕捉的均值回复倾向;贵金属杠杆高,信号失效时回撤可能剧烈,仓位要控。

MQL5 / C++
F.Forward(nFFT);             class=class="str">"cmt">//perform direct Fourier transformation
  for(class="type">int i=class="num">0;i<nFFT;i++)
    {
      Data21[i].opEqual(F.Output[i]);class=class="str">"cmt">//assign the values of the F.Output array to the Data21 array;
      cData[i].conjugate(Data21[i]); class=class="str">"cmt">//perform conjugation for the array Data21
      Data21[i].opMultEq(cData[i]);  class=class="str">"cmt">//multiplication of the complex number by the one adjacent to it
class=class="str">"cmt">//results in a complex number that has only real component not equal to zero
    }
  F1.setCFFT(Data21,Data22);    class=class="str">"cmt">//initialize data-members for the instance F1
F1.InverseT(nFFT);             class=class="str">"cmt">//perform weighter reverse Fourier transformation
getComplexArr(rA,F1.Output); class=class="str">"cmt">//get the result in class="type">class="kw">double format after
class=class="str">"cmt">//weighted reverse Fourier transformation
for(class="type">int i=class="num">0;i<nLags+class="num">1;i++)
    {
      ACF[i]=rA[i][class="num">0];          class=class="str">"cmt">//in the output ACF array save the calculated values
class=class="str">"cmt">//of autocorrelation function
      ACF[i]=ACF[i]/rA[class="num">0][class="num">0];   class=class="str">"cmt">//normalization relatively to the first element
    }
  }
class="type">void lbqtest(class="type">bool &H[],class="type">class="kw">double &rets[])
class=class="str">"cmt">/* Function that implements the Q test of Ljung-Box-Pierce
   H - output array of logic values, that confirm or disprove
    the zero hypothesis on the specified lag;
   rets - array of observations of the stochastic time series;
*/
  {
   class="type">class="kw">double lags[class="num">3]={class="num">10.0,class="num">15.0,class="num">20.0};       class=class="str">"cmt">//specified lags
   class="type">int maxLags=class="num">20;                         class=class="str">"cmt">//maximum number of lags
   class="type">class="kw">double ACF[]; ArrayResize(ACF,class="num">21);      class=class="str">"cmt">//epmty ACF array
   class="type">class="kw">double acf[]; ArrayResize(acf,class="num">20);      class=class="str">"cmt">//alternate ACF array
   autocorr(ACF,rets,maxLags);             class=class="str">"cmt">//calculated ACF array
   for(class="type">int i=class="num">0;i<class="num">20;i++) acf[i]=ACF[i+class="num">1]; class=class="str">"cmt">//remove the first element - one, fill
                                        class=class="str">"cmt">//alternate array
   class="type">class="kw">double alpha[class="num">3]={class="num">0.05,class="num">0.05,class="num">0.05};       class=class="str">"cmt">//array of levels of significance of the test

「Ljung-Box 统计量的数组落地」

这段实现把 Ljung-Box 的 Q 公式拆成了纯数组运算:Q = T(T+2)·Σ(ρ(k)²/(T-k)),其中 T 是样本长度、k 为滞后阶、ρ(k) 是第 k 阶自相关系数。MT5 里没有现成封装,只能自己用 ArrayResize 铺辅助数组。 先开六个 double/int 数组:idx 存索引偏移、arrLags 存滞后阶、stat 存 Q 值、sum 与 iACF 做累加中介、还有给指定滞后用的 stat1。maxLags 决定循环上界,len 取 rets 实际长度,二者差 1 就可能影响 p 值精度。 第一层 for 把 arrLags[i] 填成 i+1,idx[i] 算成 len-arrLags[i],iACF[i] 直接算 ρ²/idx。随后 cumsum 做累进求和,第二层 for 乘回 len*(len+2) 得到各阶 stat[i]。 最后按传入的 lags 挑出 stat1,用 1-gammp(lags/2, stat1/2) 算 p 值;当 alpha[i] >= pValue[i] 时 H[i] 置真,意味着该阶自相关在给定显著性下显著异于零。外汇与贵金属波动聚类明显,p 值小于 0.05 的滞后阶往往集中在前 10~20 阶,可据此判断回看窗口该取多长。

MQL5 / C++
class="type">class="kw">double idx[]; ArrayResize(idx,maxLags);      class=class="str">"cmt">//auxiliary array of indexes
   class="type">int len=ArraySize(rets);                       class=class="str">"cmt">//length of the array of observations
   class="type">int arrLags[];ArrayResize(arrLags,maxLags); class=class="str">"cmt">//auxiliary array of lags
   class="type">class="kw">double stat[]; ArrayResize(stat,maxLags);    class=class="str">"cmt">//array of Q statistics
   class="type">class="kw">double sum[]; ArrayResize(sum,maxLags);      class=class="str">"cmt">//auxiliary array po sums
   class="type">class="kw">double iACF[];ArrayResize(iACF,maxLags);     class=class="str">"cmt">//auxiliary ACF array
   for(class="type">int i=class="num">0;i<maxLags;i++)
     {                                           class=class="str">"cmt">//fill:
      arrLags[i]=i+class="num">1;                            class=class="str">"cmt">//auxiliary array of lags   
      idx[i]=len-arrLags[i];                     class=class="str">"cmt">//auxiliary array of indexes  
      iACF[i]=pow(acf[i],class="num">2)/idx[i];              class=class="str">"cmt">//auxiliary ACF array  
     }
   cumsum(sum,iACF);                             class=class="str">"cmt">//sum the auxiliary ACF array 
                                              class=class="str">"cmt">//by progressive total         
   for(class="type">int i=class="num">0;i<maxLags;i++)
      stat[i]=sum[i]*len*(len+class="num">2);                class=class="str">"cmt">//fill the array Q statistics
   class="type">class="kw">double stat1[];                               class=class="str">"cmt">//alternate of the array of Q statistics
   ArrayResize(stat1,ArraySize(lags));
   for(class="type">int i=class="num">0;i<ArraySize(lags);i++)
      stat1[i]=stat[lags[i]-class="num">1];                  class=class="str">"cmt">//fill the alternate array of specified lags
   class="type">class="kw">double pValue[ArraySize(lags)];               class=class="str">"cmt">//array of &class="macro">#x27;p&class="macro">#x27; values
   for(class="type">int i=class="num">0;i<ArraySize(lags);i++)
     {
      pValue[i]=class="num">1-gammp(lags[i]/class="num">2,stat1[i]/class="num">2); class=class="str">"cmt">//calculation of &class="macro">#x27;p&class="macro">#x27; values
      H[i]=alpha[i]>=pValue[i];                 class=class="str">"cmt">//estimation of zero hypothesis
     }
}

别急着下结论

非线性 GARCH 模型的价值不在于替你喊方向,而是从数学上把序列的波动边界正式刻画出来,并为指定步数给出预测区间。作者在原讨论里也点明:它确定的是未来收益的界限,以及在验证范围内模拟价格行为,而不是预测涨跌符号。 把这套东西接进 MT5 后,你能直接用预测方差去压力测试现成 EA——比如把 GarchTest 脚本跑出来的边界喂给历史回放,看策略在极端波动里会不会爆仓。外汇和贵金属杠杆高,波动边界只是概率参考,实盘仍可能瞬间击穿。 文件落点记一下:ReturnsIndicator.mq5 放 Indicators,Complex_class.mqh 与 FFT_class.mqh 进 Include,GarchTest.mq5 丢进 Scripts,编译不过就查 154 行附近的整数表达式报错。跑通之后,你才算真正摸到了用严方法代替肉眼目测的门槛。

把重复诊断交给小布
这些序列诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到自相关和条件方差的实时估算,你只管判断模型是否失效。

常见问题

看峰度是否低于正态基准,粉红分布那种更宽的概率密度尾意味着极端收益出现概率偏高,外汇贵金属高风险下尤需注意。
倾向比线性模型更贴合,因为能刻画高波动后跟高波动的惯性,但参数估计可能受样本区间影响。
可以,品种页的 AIGC 模块覆盖了自相关与条件方差分析,省去自己写 MQL5 代码跑序列的麻烦。
概率上影响较弱,该效应多见于股市下跌放大波动,外汇市场可暂不计入以简化估计。