在MQL5中相关性分析的要素:皮尔逊卡方独立性检验和相关比率·进阶篇
📘

在MQL5中相关性分析的要素:皮尔逊卡方独立性检验和相关比率·进阶篇

第 2/2 篇

「用卡方检验拆相邻价格增量的独立性」

皮尔逊卡方独立性检验在 MT5 里由 Chi2Test 指标承载,它针对单一品种每根 K 线的相邻价格增量,检验「前后增量相互独立」这一假设是否成立。指标四个输入分别是:alpha 显著性水平、Data 计算窗口、Tails 标准差裁剪数、Contingency coefficient 相关系数。 连续价格数据不能直接进列联表,得先分组。检验有效性的硬约束是:单元格期望频数最小值不小于 1,且期望频数小于 5 的格子不能多——总格数少于 10 时最多允许 2 个。股票类数据重尾明显,原始分组常踩这条线。 实操里建议先标准化再分组,并用 Tails 把重尾修到 2~4 个标准差内。样本量 200~300 时选 Tails=2,分组区间变成 (-2,-1)、(-1,0)、(0,1)、(1,2),拼出 4x4 列联表,顺手合并期望频过小的相邻区间。外汇与贵金属波动更肥尾,裁剪不当会放大假显著性,属高风险验证动作。 下面这段 Crosstab 是列联表实际频数的核心。它接收两组数据 dataX、dataY 和分箱边界 bins,返回频数矩阵 freq。 //+------------------------------------------------------------------+

//Calculate the table of contingency of two random values (X,Y)

//+------------------------------------------------------------------+ bool Crosstab(const double &dataX[],const double &dataY[],const double &bins[],matrix &freq) // 函数签名:传入 X、Y 数据数组与分箱边界,输出 freq 矩阵;返回是否成功 { int datasizeX=ArraySize(dataX); int datasizeY=ArraySize(dataY); int binssize=ArraySize(bins); // 取三个数组长度

if(datasizeX==0datasizeY==0binssize==0)

return(false); // 任一为空则失败 if(datasizeX != datasizeY) return(false); // X、Y 长度必须一致 for(int i=0; i<datasizeX; i++) {

if(!MathIsValidNumber(dataX[i])!MathIsValidNumber(dataY[i]))

return(false); } // 逐点校验数值有效性,含 NaN 即退出 matrix m_freq=matrix::Zeros(binssize, binssize); // 建 binssize×binssize 零矩阵 for(int x=0; x<binssize; x++) { for(int i=0; i<datasizeX; i++) { for(int y=0; y<binssize; y++) { if(dataX[i]<=bins[x] && dataY[i]<=bins[y]) { m_freq[x,y]=m_freq[x,y]+1; break; } } } } // 三重循环:按 X、Y 各自落入的分箱累加计数 matrix Actual = m_freq; vector p1,p2,diffp; for (int j=1; j<binssize; j++) { p1 = m_freq.Row(j-1); p2 = m_freq.Row(j); diffp = p2-p1; Actual.Row(diffp,j); } // 对相邻行做差分,把累积计数转成区间实际频数 freq = Actual; return(true); } 把这段代码塞进 MT5 脚本,喂入相邻增量标准化后的两组序列,就能在终端直接打印出实际频数矩阵,再套卡方公式看 p 值。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  Calculate the table of contingency of two random values(X,Y)    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool Crosstab(const class="type">class="kw">double &dataX[],const class="type">class="kw">double &dataY[],const class="type">class="kw">double &bins[],matrix &freq)
  {
   class="type">int datasizeX=ArraySize(dataX);
   class="type">int datasizeY=ArraySize(dataY);
   class="type">int binssize=ArraySize(bins);
   if(datasizeX==class="num">0 || datasizeY==class="num">0 || binssize==class="num">0)
     class="kw">return(false);
   if(datasizeX != datasizeY)
     class="kw">return(false);
   for(class="type">int i=class="num">0; i<datasizeX; i++)
     {
      if(!MathIsValidNumber(dataX[i]) || !MathIsValidNumber(dataY[i]))
        class="kw">return(false);
     }
   matrix m_freq=matrix::Zeros(binssize, binssize);
   for(class="type">int x=class="num">0; x<binssize; x++)
     {
      for(class="type">int i=class="num">0; i<datasizeX; i++)
       {
        for(class="type">int y=class="num">0; y<binssize; y++)
          {
           if(dataX[i]<=bins[x] && dataY[i]<=bins[y])
             {
              m_freq[x,y]=m_freq[x,y]+class="num">1;
              break;
             }
          }
       }
     }
   matrix Actual = m_freq;
   vector p1,p2,diffp;
   for (class="type">int j=class="num">1; j<binssize; j++)
     {
      p1 = m_freq.Row(j-class="num">1);
      p2 = m_freq.Row(j);
      diffp = p2-p1;
      Actual.Row(diffp,j);
     }
   freq = Actual;
   class="kw">return(true);
  }

用克莱默系数给依赖性定个量

卡方统计量只能判断“是否相关”,给不出依赖强度,所以得靠克莱默相关系数补位。它取值锁在 0 到 1 之间:0 代表两组数据毫无依赖,1 代表存在函数级映射关系,中间值越大依赖性越强。 计算式是 sqrt(CHI2 / (N*(Bins_count-1))),这里的 Bins_count 取特征交叉表的行数或列数(两者在方形表中相等)。本质上它是把卡方和样本量、分箱数揉在一起,变成可横向量化的指标。 要测两个品种之间的独立性,直接跑 Crosstab 脚本并打开 X vs Y(真实)参数;不打开就只算当前图表品种自身。样本从图表倒数第二根 K 线往前取,避开最右侧未成型的那根。 脚本会吐出实际/预期频数矩阵、分组数据算出的 r、非分组皮尔逊系数(用来比对分组精度损失)、Nxy/Nyx、两个 F 检验(独立假设 & 线性/非线性假设)、CHI2 和克莱默系数,屏上还会画相关域散点图做肉眼复核。 一个坑:像逻辑映射这种一维非线性系统,单看线性相关系数可能误判成白噪声;卡方检验加克莱默系数能揪出非线性依赖——外汇与贵金属这类高噪数据上误用线性指标同样容易翻车,实盘验证前先在小样本回测。

◍ 用条件方差拆开非线性相关

评估两个序列是不是真线性相关,不能只看皮尔逊 r。非线性相关程度由相关系数 Nyx / Nxy 刻画:取条件期望的方差与无条件方差之比再开根,即 Nyx = sqrt(D[E(XY)] / Sx),Nxy = sqrt(D[E(YX)] / Sy)。它只能基于分组数据(相关表)算,不是逐点公式。
这个指标在 X、Y 上不对称,Nxy 不等于 Nyx;取值范围落在 [0,1],且对任意样本恒有r≤ Nxy 与r≤ Nyx。若两变量独立则 Nxy=Nyx=0,但反过来不成立——不相关不代表独立,这是很多回测里踩过的坑。
判线性程度看差值:当r= Nyx = Nxy < 1,关系就是纯线性,回归里找不出比直线更优的曲线;一旦非线性,必有r< min(Nxy, Nyx),且 N 与 r 越接近,关系越贴近直线。

要检验「是否存在相关依赖」(H0: Nyx=0),用 F = Nyx²*(N-Bins_count)/((1-Nyx²)*(Bins_count-1)),自由度为 v1=Bins_count-1、v2=N-Bins_count 的 Fisher 分布。计算值超临界值,则在 α 水平下拒原假设。 确认有依赖后,再判线性与否:检 Y 对 X 线性(H0: Nyx²=r²)用 F = (Nyx²-r²)*(N-Bins_count)/((1-Nyx²)*(Bins_count-2)),v1=Bins_count-2、v2=N-Bins_count。超临界值就拒绝线性假设,接受 Nyx²≠r²。外汇与贵金属价差序列常呈时变非线性,用这套检之前先点明高风险,勿直接当线性套利依据。

「把统计检验接进你的MT5验证流」

CHI2Test 指标跑的是皮尔逊卡方独立性检验,用来判断多品种数据之间到底是不是真独立;显著性水平 α 由你手动选,α 调得越低,误判有关系的概率越小,但样本不够时也容易什么都检不出来。 配套 Crosstab.mq5 与 Crosstab_Models.mq5 两个脚本算相关比率,顺带在屏幕上画相关场图,做成对依赖的肉眼复核。三份源码体积分别是 18.47 KB、32.33 KB、36.33 KB(原文记作 36.53 KB,以你下载包内为准),直接拖进 MT5 脚本目录就能跑。 外汇和贵金属品种相关性随流动性切换会突变,这类统计结论只代表历史样本区间,实盘前务必用你自己品种、你自己周期重跑一遍。 下回测两段 EURUSD 与 XAUUSD 的 M15 收盘序列时,先丢进 CHI2Test 看 p 值,再开 Crosstab 看相关比率是不是线性——两条路对不上,就别信单一相关系数。

常见问题

可用卡方独立性检验把相邻增量分箱后做列联表,若p值小于0.05则倾向不独立,开对应品种历史数据跑一遍即可。
克莱默V在0~1之间,高于0.3通常视为中等以上依赖,但外汇贵金属波动大需结合样本量看,别单凭一个数下结论。
小布盯盘的AIGC已内置卡方与克莱默系数诊断,打开对应品种页即可看到相邻增量的依赖程度,不用自己写脚本。
按条件分组算各组方差,若组间方差差异大说明有非线性结构;皮尔逊只抓线性,漏掉这种折叠式依赖。
只需每周对主做品种跑一次增量独立性检验,异常时再深查,高频盯盘没必要每次都算,贵金属外汇高风险需防过拟合。