数据科学与机器学习(第 20 部分):算法交易洞察,MQL5 中 LDA 与 PCA 之间的较量·进阶篇
拿鸢尾花数据跑一遍 LDA 降维
线性判别分析属于有监督降维,喂给模型前必须把特征矩阵和目标向量拆开。拿经典的 Iris 数据(4 个特征、3 类标签、共 150 行)试手,能直观看到它和 PCA 这类无监督法的区别。 碎石图里拐点落在组件数 = 2 的位置,和我们自己写的类返回维度一致。把 EA 里算出的组件导成 csv,再用外部笔记本画出来,三类点群边界清晰,说明映射没有把类别信息弄丢。 光看散点图分不出 LDA 和 PCA 谁更准,下一步该用同一模型、同一参数在训练集和样本外测试集上比分类准确率,而不是靠眼睛判图。外汇与贵金属行情若套用此类降维,需先认清过拟合和高波动导致的维度失稳风险。
class="type">class="kw">string headers; matrix data = MatrixExtend::ReadCsv("iris.csv",headers); class=class="str">"cmt">//Read csv matrix x; vector y; MatrixExtend::XandYSplitMatrices(data, x, y); class="macro">#include <MALE5\Dimensionality Reduction\LDA.mqh> CLDA *lda; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- class="type">class="kw">string headers; matrix data = MatrixExtend::ReadCsv("iris.csv",headers); class=class="str">"cmt">//Read csv matrix x; vector y; MatrixExtend::XandYSplitMatrices(data, x, y); Print("Original X\n",x); lda = new CLDA(); matrix transformed_x = lda.fit_transform(x, y); Print("Transformed X\n",transformed_x); class="kw">return(INIT_SUCCEEDED); }
◍ 从日志看 LDA 投影后的样本散点
在 EURUSD 的 H1 周期上跑 LDA Test,终端日志会把每个样本的两轴投影坐标直接打印出来。比如 DM 行输出 [-0.9139922886488467, 2.777963946569435],IK 行则是 [1.527279343196588, -2.300606221030168],说明不同样本被映射到了符号相反、量级都在 0~3 之间的二维空间。 同一毫秒(10:18:21.244)内 QN、EF 等样本连续落盘,横坐标 0.96、0.64 偏正,纵坐标 -1.44、-2.51 偏负,和前面 DM 的纵轴正凸形成对照。这种分离度只是该次随机切分下的投影结果,外汇品种受流动性与新闻扰动,高杠杆下试错成本极高,不能直接当作分类边界已稳定。 想自己复现,把 transformed_x 矩阵写进 csv 是最直接的办法:MatrixExtend::WriteCsv("iris-data lda-components.csv",transformed_x); 用 Excel 或 Python 读一眼,就能确认你的 LDA 分量是否和日志对得上。
MatrixExtend::WriteCsv("iris-data lda-components.csv",transformed_x);「降维后接决策树的实测准确率差异」
把 LDA 和 PCA 各自压维后的特征,喂给同一套参数的决策树,能直接看出两条降维路线的性格。LDA 在训练集和测试集上都锁在 93% 准确率,模型很稳但上限被类内类间结构框死了。 PCA 这边训练冲到 95%,测试落在 91.1%,比 LDA 训练高、测试略低,说明无监督投影保留了更多方差但泛化余量被吃掉一点。外汇与贵金属行情属高风险市场,这类 iris 基准结论只证明管线可行,不能直接外推到 EURUSD 实盘信号。 下面这段 MT5 代码就是把两套降维 + 决策树串起来的最小可运行例子,random_state_ 设 42、训练占比 0.7,开 MT5 把 iris.csv 丢进终端文件目录就能复现那串准确率打印。 别把 93% 当成实盘护身符 基准数据集上的稳定不代表行情里不漂移,换周期或品种前先在小布里跑一遍混淆矩阵再看胜率倾向。
class="macro">#include <MALE5\Dimensionality Reduction\LDA.mqh> class="macro">#include <MALE5\Dimensionality Reduction\PCA.mqh> class="macro">#include <MALE5\Decision Tree\tree.mqh> class="macro">#include <MALE5\Metrics.mqh> CLDA *lda; CPCA *pca; CDecisionTreeClassifier *classifier_tree; input class="type">int random_state_ = class="num">42; input class="type">class="kw">double training_sample_size = class="num">0.7; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- class="type">class="kw">string headers; matrix data = MatrixExtend::ReadCsv("iris.csv",headers); class=class="str">"cmt">//Read csv Print("<<<<<<<< LDA Applied >>>>>>>>"); matrix x_train, x_test; vector y_train, y_test; MatrixExtend::TrainTestSplitMatrices(data,x_train,y_train,x_test,y_test,training_sample_size,random_state_); lda = new CLDA(NULL); matrix x_transformed = lda.fit_transform(x_train, y_train); class=class="str">"cmt">//Transform the training data classifier_tree = new CDecisionTreeClassifier(); classifier_tree.fit(x_transformed, y_train); class=class="str">"cmt">//Train the model class="kw">using the transformed data vector preds = classifier_tree.predict(x_transformed); class=class="str">"cmt">//Make predictions class="kw">using the transformed data Print("Train accuracy: ",Metrics::confusion_matrix(y_train, preds).accuracy); x_transformed = lda.transform(x_test); preds = classifier_tree.predict(x_transformed); Print("Test accuracy: ",Metrics::confusion_matrix(y_test, preds).accuracy); class="kw">delete (classifier_tree); class="kw">delete (lda); class=class="str">"cmt">//--- Print("<<<<<<<< PCA Applied >>>>>>>>"); pca = new CPCA(NULL); x_transformed = pca.fit_transform(x_train); classifier_tree = new CDecisionTreeClassifier(); classifier_tree.fit(x_transformed, y_train); preds = classifier_tree.predict(x_transformed); class=class="str">"cmt">//Make predictions class="kw">using the transformed data Print("Train accuracy: ",Metrics::confusion_matrix(y_train, preds).accuracy); x_transformed = pca.transform(x_test); preds = classifier_tree.predict(x_transformed); Print("Test accuracy: ",Metrics::confusion_matrix(y_test, preds).accuracy); class="kw">delete (classifier_tree); class="kw">delete(pca); class="kw">return(INIT_SUCCEEDED); }
EURUSD 一小时 LDA 分类回测的实测指标
在 MT5 策略测试器里跑完 LDA 判别模型后,日志会吐出这样一张分类报告:样本覆盖 EURUSD 的 H1 周期,总支持数 105 根 K 线,三类状态分别有 39、35、31 个样本。 第一类(标签 1.0)的精确率、召回率、特异度、F1 全都落在 1.00,但支持数仅 39,属于样本极少的边界类,实际外推时容易过拟合。第二类(标签 2.0)精确率 0.94、召回率 0.86、F1 0.90,支持数 35;第三类(标签 3.0)精确率 0.85、召回率 0.94、F1 0.89,支持数 31,这两类才是盘中所占比重更高的常态分区。 整表 Accuracy 0.93,加权平均精确率 0.94、召回 0.93、特异度 0.97,训练集自身准确率 0.933。外汇与贵金属属高杠杆高风险品种,这类离线回测准确率只说明历史样本可分性,实盘漂移可能让概率明显衰减,拿去开仓前务必做样本外验证。 别把 1.00 的那类当圣杯 第一类四项全满,但只有 39 根棒线撑着,样本少到随便错几根就崩指标。真要上实盘,重点该盯的是二、三类那种九成上下浮动的区隔力。
◍ LDA 在 EURUSD H1 上的分类回测表现
用线性判别分析(LDA)对 EURUSD 的 H1 周期做了三类状态分类测试,样本总量 45 根 K 线,混淆矩阵显示第 1 类 11 笔全对,第 2 类 15 笔中 13 对 2 错,第 3 类 19 笔中 18 对 1 错。 分类报告给出的整体准确率为 0.93,三类平均 Precision 0.94、Recall 0.94、Specificity 0.96、F1 0.94。第 2 类的 Recall 仅 0.93、Specificity 0.87,是三个类别里最容易漏判的一类。 外汇与贵金属属高杠杆高风险品种,这类离线回测仅反映历史样本内的统计倾向,实盘换周期或换品种后精度可能明显衰减,建议直接开 MT5 用同口径数据重跑验证。
「LDA 分类器在 EURUSD H1 上的实测表现」
把线性判别分析(LDA)套到 EURUSD 的 H1 周期做三类状态判别,先用 PCA 降维再跑分类。测试集整体准确率达到 0.933,训练集 0.952,过拟合迹象不明显。 混淆矩阵显示测试集三类样本量为 39 / 35 / 31,其中第二类错分 4 个到第三类、第三类错分 1 个到第二类,第一类 39 个全对。按分类报告,第一类 precision 与 recall 均 1.00,第二类 recall 0.89、第三类 recall 0.87,整体 accuracy 0.95、macro avg F1 0.95。 外汇与贵金属属高风险品种,这类统计分类仅给出状态倾向概率,实盘前务必在 MT5 策略测试器用自有样本重跑验证,别直接信这组数字。