数据科学与机器学习(第 01 部分):线性回归·综合运用
(3/3)· 散点里找直线看似简单,错把非线性当线性才是实盘建模的隐性坑
「多变量回归在 MT5 类里的硬编码路子」
多元线性回归和单变量最大的区别,是模型里同时塞了多个自变量:Y = M1X1 + M2X2 + M3X3 + … + C。Y 轴截距只有一个,因为整条拟合直线只会穿过 y 轴一次,这点跟简单线性回归一致。 由于要处理的是数组集合,类的私有段先记一个 m_independent_vars 表示自变量个数,公开段则直接重载 MultipleRegressionMain——两个自变量和三个自变量的版本分开写,没有取巧的泛化办法。 推导后的常数公式是 C = Y - M1X1 - M2X2,代码里 y_interceptforMultiple 就是拿各序列均值按这个算的。下面这段是两个自变量版本的类的骨架与核心函数,逐行拆一下能帮助你在 MT5 里直接抄去验证。 class CMultipleLinearRegression: public CSimpleLinearRegression —— 继承简单回归类,复用斜率计算。 private: int m_independent_vars; —— 私有成员,记录自变量数量。 public: 构造析构与多个重载函数声明,包括双变量和三变量两套 MultipleRegressionMain 与 y_interceptforMultiple。 void MultipleRegressionMain(...) 双变量版:先算截距和两个斜率,Print 出形如 Y=0.12A+0.08B+1.35 的模型串,再按数组均长 resize 预测数组,循环填 predicted_y[i] = slope1*A[i]+slope2*B[i]+截距。 DoubleToString(slope1,2) 里的 2 是保留两位小数,跑出来你能在Experts日志里直接看到系数。 double y_interceptforMultiple(...) :return(mean(Y)-coefficient_of_X(A,Y)*mean(A)-coefficient_of_X(B,Y)*mean(B)); 就是 C = Y均值 - M1*A均值 - M2*B均值。 外汇和贵金属行情受新闻冲击大,多变量回归拟合出的系数只在样本区间内有解释力,拿去预测未来价格只是概率倾向,实盘前务必用历史数据回测截距稳定性。
class CMultipleLinearRegression: class="kw">public CSimpleLinearRegression { class="kw">private: class="type">int m_independent_vars; class="kw">public: CMultipleLinearRegression(class="type">void); ~CMultipleLinearRegression(class="type">void); class="type">class="kw">double coefficient_of_X(class="type">class="kw">double& x_arr[],class="type">class="kw">double& y_arr[]); class="type">void MultipleRegressionMain(class="type">class="kw">double& predicted_y[],class="type">class="kw">double& Y[],class="type">class="kw">double& A[],class="type">class="kw">double& B[]); class="type">class="kw">double y_interceptforMultiple(class="type">class="kw">double& Y[],class="type">class="kw">double& A[],class="type">class="kw">double& B[]); class="type">void MultipleRegressionMain(class="type">class="kw">double& predicted_y[],class="type">class="kw">double& Y[],class="type">class="kw">double& A[],class="type">class="kw">double& B[],class="type">class="kw">double& C[],class="type">class="kw">double& D[]); class="type">class="kw">double y_interceptforMultiple(class="type">class="kw">double& Y[],class="type">class="kw">double& A[],class="type">class="kw">double& B[],class="type">class="kw">double& C[],class="type">class="kw">double& D[]); }; class="type">void CMultipleLinearRegression::MultipleRegressionMain(class="type">class="kw">double &predicted_y[],class="type">class="kw">double &Y[],class="type">class="kw">double &A[],class="type">class="kw">double &B[]) { class=class="str">"cmt">// Multiple regression formula = y = M1X1+M2X2+M3X3+...+C class="type">class="kw">double constant_y_intercept=y_interceptforMultiple(Y,A,B); class="type">class="kw">double slope1 = coefficient_of_X(A,Y); class="type">class="kw">double slope2 = coefficient_of_X(B,Y); Print("Multiple Regression Model is ","Y="+DoubleToString(slope1,class="num">2)+"A+"+DoubleToString(slope2,class="num">2)+"B+"+ DoubleToString(constant_y_intercept,class="num">2)); class="type">int ArrSize = (ArraySize(A)+ArraySize(B))/class="num">2; ArrayResize(predicted_y,ArrSize); for (class="type">int i=class="num">0; i<ArrSize; i++) predicted_y[i] = slope1*A[i]+slope2*B[i]+constant_y_intercept; } class="type">class="kw">double CMultipleLinearRegression::y_interceptforMultiple(class="type">class="kw">double &Y[],class="type">class="kw">double &A[],class="type">class="kw">double &B[]) { class=class="str">"cmt">//formula c=Y-M1X1-M2X2; class="kw">return(mean(Y)-coefficient_of_X(A,Y)*mean(A)-coefficient_of_X(B,Y)*mean(B)); } class="type">void CMultipleLinearRegression::MultipleRegressionMain(class="type">class="kw">double &predicted_y[],class="type">class="kw">double &Y[],class="type">class="kw">double &A[],class="type">class="kw">double &B[],class="type">class="kw">double &C[],class="type">class="kw">double &D[]) {
把四因子回归塞进预测数组
多元线性回归算完系数后,关键一步是把斜率和截距落进预测序列。下面这段代码先抓出 Y 对 A/B/C/D 各自的斜率,以及整体截距 constant_y_intercept,随后用 Print 把模型式子打到日志里,方便你直接在 MT5 终端核对数值。 ArrSize 取 A、B 数组长度的平均值,假定四组因子长度一致;ArrayResize 给 predicted_y 定容,循环里按 Y = slope1*A + slope2*B + slope3*C + slope4*D + intercept 逐点算预测值。外汇与贵金属市场的高风险意味着,这种拟合只描述历史样本关系,样本外预测可能明显偏离。 截距函数 y_interceptforMultiple 用均值差法:intercept = mean(Y) - Σ(slope_i * mean(因子_i))。复制进 EA 后,把 A/B/C/D 换成你自己的指标序列(比如波动率、动量、价差、成交量),跑一遍看 Print 出来的系数符号和大小是否符合盘感。
class="type">class="kw">double constant_y_intercept = y_interceptforMultiple(Y,A,B,C,D); class="type">class="kw">double slope1 = coefficient_of_X(A,Y); class="type">class="kw">double slope2 = coefficient_of_X(B,Y); class="type">class="kw">double slope3 = coefficient_of_X(C,Y); class="type">class="kw">double slope4 = coefficient_of_X(D,Y); class=class="str">"cmt">//--- Print("Multiple Regression Model is ","Y="+DoubleToString(slope1,class="num">2),"A+"+DoubleToString(slope2,class="num">2)+"B+"+ DoubleToString(slope3,class="num">2)+"C"+DoubleToString(slope4,class="num">2)+"D"+DoubleToString(constant_y_intercept,class="num">2)); class=class="str">"cmt">//--- class="type">int ArrSize = (ArraySize(A)+ArraySize(B))/class="num">2; ArrayResize(predicted_y,ArrSize); for (class="type">int i=class="num">0; i<ArrSize; i++) predicted_y[i] = slope1*A[i]+slope2*B[i]+slope3*C[i]+slope4*D[i]+constant_y_intercept; } class="type">class="kw">double CMultipleLinearRegression::y_interceptforMultiple(class="type">class="kw">double &Y[],class="type">class="kw">double &A[],class="type">class="kw">double &B[],class="type">class="kw">double &C[],class="type">class="kw">double &D[]) { class="kw">return (mean(Y)-coefficient_of_X(A,Y)*mean(A)-coefficient_of_X(B,Y)*mean(B)-coefficient_of_X(C,Y)*mean(C)-coefficient_of_X(D,Y)*mean(D)); }
◍ 线性模型背后的几个硬前提
在 MT5 里跑线性回归类指标前,得先认清楚它依赖的几条假设,否则拟合出来的线只是看起来顺眼。最基础的一条是线性关系:你假定因变量和自变量之间确实存在直线型关联,若价格结构本质是分段的或带突变的,硬套线性就会系统性偏误。 误差项要满足正态性,并且围绕模型均值随机分布。实操中可把实际值与预测值做散点图,理想状态下点云应在整条回归线上下均匀铺开;若呈现喇叭口或明显弧带,说明异方差或非线性残留,线性模型在该样本上可能失效。 这类模型实现简单、系数易读,但外汇与贵金属属高杠杆高风险品种,假设不满足时模型给出的方向倾向仅作参考,不可直接当作进出场依据。
「线性回归用在行情里的几处硬伤」
把价格序列塞进线性回归,第一个隐含前提是自变量和因变量之间真有一条直线能连起来。实际 K 线里多空切换常是折线甚至突变,硬套直线会系统性低估拐点附近的误差。 极端值对回归系数的破坏力极大。一根秒级插针就能把整条拟合线拽偏,使后续基于斜率的判断全部失真,这种脆弱性在贵金属跳空夜尤为明显。 模型还默认各输入特征彼此独立。但像收盘价和均线这类量本就同源,强行当独立变量处理,会得到统计上虚胖的显著性。 回归刻画的只是均值层面的关系,就像平均数掩盖了分布尾部一样,它给不出极端行情下变量如何脱钩。边界被限定为线性,也意味着非线性套利结构一律看不见。 外汇与贵金属杠杆高、跳空频繁,直接拿裸回归信号下单风险很大,建议先在小布里做残差诊断再决定是否采信。
函数库还差训练与测试这一步
眼下这套线性回归函数库只是搭了骨架,模型的训练与测试模块还没接进去,想直接拿来跑配对相关性策略还差最后一段路。作者把 Python 参考实现丢在 GitHub,目前附件里的 GetDataToFile.mq5(2.85 KB)、LinearRegressionLib.mqh(16.86 KB)、TestScript.mq5(2.32 KB)是能跑通取数和基础拟合的最小集合。 如果你手上有 MT5 环境,先把 TestScript.mq5 挂上实盘模拟账户,看 LinearRegressionLib 吐出的斜率和残差是否符合你盯的贵金属跨期价差;外汇与贵金属杠杆交易风险极高,回测漂亮不等于实盘能活。 下一篇才会补上样本外测试和参数寻优,在那之前别把未训练的函数当信号源。
◍ 把工具请下神坛
回看这一节里读者在评论区挑出的几个刺:有人指出 CSV 必须先转 UTF-8 才能被 Python 读,其实直接在 MQL 里写 UTF-8 就省掉这道工序;还有人纠正图上标错的点——那不是 y-截距,坐标应是 (-5,0) 而非 (0,-5)。这些都不是大道理,只是实盘前顺手该核一下的细节。 另有读者提到 MT4 上做过带综合 R 平方的线性回归指标,也有人说 Pearson 系数分母有缺陷,说明同一套数学工具在不同人手里误差点完全不同。外汇和贵金属杠杆高、滑点凶,模型只是 y=mx+c 这种后缀符号,别把它当圣旨。 开 MT5 把脚本跑一遍,自己导一份 CSV 看编码、对一遍图上的截距点,比盲信任何现成指标都实在。工具用熟了,它只是你验证想法的锤子,不是替你下单的神。