在MQL5中相关性分析的要素:皮尔逊卡方独立性检验和相关比率·进阶篇
「用卡方检验拆相邻价格增量的独立性」
皮尔逊卡方独立性检验在 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==0 | datasizeY==0 | binssize==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 值。
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(X | Y)] / Sx),Nxy = sqrt(D[E(Y | X)] / 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 看相关比率是不是线性——两条路对不上,就别信单一相关系数。