数据科学和机器学习(第 29 部分):为 AI 训练目的而选择最佳外汇数据的基本技巧·综合运用
📘

数据科学和机器学习(第 29 部分):为 AI 训练目的而选择最佳外汇数据的基本技巧·综合运用

第 3/3 篇

用 L1 惩罚把无关变量直接归零

线性回归靠最小化预测值和真实值的平方差来定系数,但特征一多就容易过拟合、方差炸开。Lasso 引入 L1 正则化,把系数按一个常量做绝对值惩罚,逼着不重要的系数精确落到 0——这不是简单缩小,而是顺手完成了特征选择,能直接把对应列从数据里删掉。 Ridge 用的是 L2(罚平方),只会把系数压小但不会归零;Elastic Net 则是两者混搭。Lasso 的惩罚强度由 alpha 控制,得靠超参搜索找合适值,不是拍脑袋定的。 跑通下面这段 Python 就能看见实效:在测试集上只保留 4 个非零系数特征(HIGH、LOW、CLOSE、30DAY_SMA),R² 达到 0.989,说明砍掉冗余列后模型依旧贴合。外汇与贵金属行情高度随机,此类特征筛选仅降低过拟合概率,不预示任何方向。 别把正态当圣经:当特征间高度相关时,Lasso 给哪列留系数倾向随机,估算可能不稳;若特征数多于样本数,它也可能跑歪。 代码逐行拆解: from sklearn.model_selection import train_test_split —— 导入切分训练/测试集工具 from sklearn.linear_model import Lasso —— 导入 Lasso 回归类 from sklearn.metrics import r2_score —— 导入 R² 评分函数 from sklearn.preprocessing import MinMaxScaler —— 导入归一化缩放器 y = df["TARGET_CLOSE"] —— 取目标收盘价列作标签 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) —— 按 8:2 切数据,固定随机种子保证可复现 scaler = MinMaxScaler() —— 建归一化器,把特征压到 0~1 区间 lasso = Lasso(alpha=0.001) # You need tune for the best penalty value —— 初始化 Lasso,alpha=0.001 是起点,实际要调 X_train = scaler.fit_transform(X_train) —— 用训练集拟合并转换 lasso.fit(X_train, y_train) —— 训练 Lasso print(f'Coefficients: {lasso.coef_}') —— 打印各特征系数 X_test = scaler.transform(X_test) —— 测试集用同一 scaler 转换 y_pred = lasso.predict(X_test) —— 预测测试集 mse = r2_score(y_test, y_pred) —— 算 R² print(f'Lasso regression test accuracy = {mse}') —— 输出准确率 selected_features = X.columns[lasso.coef_ != 0] —— 挑出系数非 0 的特征名 print(f'Selected Features: {selected_features}') —— 打印入选特征

MQL5 / C++
from sklearn.model_selection class="kw">import train_test_split
from sklearn.linear_model class="kw">import Lasso
from sklearn.metrics class="kw">import r2_score
from sklearn.preprocessing class="kw">import MinMaxScaler
y = df["TARGET_CLOSE"]
# Split the data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=class="num">0.2, random_state=class="num">42)
# A scaling technique
scaler = MinMaxScaler()
# Initialize and fit the lasso model
lasso = Lasso(alpha=class="num">0.001)  # You need tune for the best penalty value
# Train the scaler and transfrom data
X_train = scaler.fit_transform(X_train)
lasso.fit(X_train, y_train)
print(f&class="macro">#x27;Coefficients: {lasso.coef_}&class="macro">#x27;) class="macro">#print coefficients
# Predict on the test set
X_test = scaler.transform(X_test)
y_pred = lasso.predict(X_test)
# Calculate mean squared error
mse = r2_score(y_test, y_pred)
print(f&class="macro">#x27;Lasso regression test accuracy = {mse}&class="macro">#x27;)
# select all features with coefficents not equal to zero
selected_features = X.columns[lasso.coef_ != class="num">0]
print(f&class="macro">#x27;Selected Features: {selected_features}&class="macro">#x27;)

「用随机森林筛出真正驱动价格的字段」

决策树靠递归切分数据做预测,每个节点挑一个特征和一个阈值来拆,目标是把混杂度压到最低。某个特征的重要性,看它在整棵树里累计减少了多少杂质——在多个节点都拿来切,就把各处的减少量加总。随机森林是并行长出几十上百棵树,最终预测取均值或投票,特征重要性则是所有树的平均。 梯度提升机(如 XGBoost)按顺序建树,后一棵树专门补前一棵的错,重要性是所有树之和。不管哪种,跑完都能拿到一份特征重要性数值,用来挑出模型最倚重的输入。 别急着把选出的特征直接上实盘。先用测试集量一下随机森林分类器的准确率,确认这些字段在没见过的数据上也站得住脚。下面这段 Python 可直接在 MT5 导出的 CSV 上跑,阈值设 0.04 时筛出了 HIGH、CLOSE、OPEN_LAG3、30DAY_SMA 等 10 个字段,测试集准确率需你本地 print 出来看。 随机森林做特征选择,优势是树群融汇降低了单棵树的过拟合,重要性分数更稳;能扛高维数据不怎么掉性能,还能抓特征间的非线性互动。缺点是大数据集训练贵,相关特征会分到相似分数难区分,且可能偏爱连续或多级别特征,给类别少的字段打分偏低。外汇与贵金属波动剧烈、杠杆高风险极大,任何筛选结果都只是概率倾向,须经历史回测验证。

MQL5 / C++
from sklearn.ensemble class="kw">import RandomForestClassifier
y = []
# Loop through each row in the DataFrame to create the target variable
for i in range(len(df)):
    if df.loc[i, &class="macro">#x27;TARGET_CLOSE&class="macro">#x27;] > df.loc[i, &class="macro">#x27;TARGET_OPEN&class="macro">#x27;]:
        y.append(class="num">1)
    else:
        y.append(class="num">0)
# Split the data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=class="num">0.2, random_state=class="num">42)
model = RandomForestClassifier(n_estimators=class="num">50, min_samples_split=class="num">10, max_depth=class="num">5, min_samples_leaf=class="num">5)
model.fit(X_train, y_train)
importances = model.feature_importances_
print(importances)
selected_features = importances > class="num">0.04
selected_feature_names = X.columns[selected_features]
print("selected features\n",selected_feature_names)
[class="num">0.02691807 class="num">0.05334113 class="num">0.03780997 class="num">0.0563491  class="num">0.03162462 class="num">0.03486413
 class="num">0.02652285 class="num">0.0237652  class="num">0.03398946 class="num">0.02822157 class="num">0.01794172 class="num">0.02818283
 class="num">0.04052433 class="num">0.02821834 class="num">0.0386661  class="num">0.03921218 class="num">0.04406372 class="num">0.06162133
 class="num">0.03103843 class="num">0.02206782 class="num">0.05104613 class="num">0.01700301 class="num">0.05191551 class="num">0.07251801
 选择的特征
 Index([&class="macro">#x27;HIGH&class="macro">#x27;, &class="macro">#x27;CLOSE&class="macro">#x27;, &class="macro">#x27;OPEN_LAG3&class="macro">#x27;, &class="macro">#x27;30DAY_SMA&class="macro">#x27;, &class="macro">#x27;7DAY_STDDEV&class="macro">#x27;, &class="macro">#x27;DAYOFYEAR&class="macro">#x27;,
       &class="macro">#x27;DIFF_LAG1_OPEN&class="macro">#x27;, &class="macro">#x27;DIFF_LAG1_HIGH&class="macro">#x27;, &class="macro">#x27;DIFF_LAG1_LOW&class="macro">#x27;, &class="macro">#x27;DIFF_LAG1_CLOSE&class="macro">#x27;],
      dtype=&class="macro">#x27;object&class="macro">#x27;)
from sklearn.metrics class="kw">import accuracy_score
test_pred = model.predict(X_test)
print(f"Random forest test accuracy = ",accuracy_score(y_test, test_pred))

◍ 把 OHLC 四个价压成一个主成分

开盘、最高、最低、收盘这四列在相关矩阵里高度共线,硬塞进模型只是堆噪声。用 PCA 压到 1 维,新变量叫 OHLC,信息损耗比直觉里小得多。 拿线性回归跑对照:原始 4 特征数据的 R² 是 0.9938,降维后单特征 R² 是 0.9921,差 0.0017。对外汇或贵金属这类高噪品种,这种精度损失通常可忽略,但模型维度从 4 降到 1。 降维的好处是直接砍掉冗余、缓解过拟合,特征空间小了模型更好解释。代价也实在:PCA 要自己定保留几个分量,选错就丢信号;压出来的分量物理含义模糊,回测里容易错失价间微妙关系。 下面这段 Python 可直接复现上面的对照,把 df 换成你的 MT5 导出的 OHLCV 数据就能跑。

MQL5 / C++
from sklearn.decomposition class="kw">import PCA
from sklearn.linear_model class="kw">import LinearRegression
pca = PCA(n_components=class="num">1)
ohlc = pd.DataFrame({
    "OPEN": df["OPEN"],
    "HIGH": df["HIGH"],
    "LOW": df["LOW"],
    "CLOSE": df["CLOSE"]
})
y = df["TARGET_CLOSE"]
# let us use the linear regression model 
model = LinearRegression()
# for OHLC original data 
model.fit(ohlc, y)
preds = model.predict(ohlc)
print("ohlc_original data LR accuracy = ",r2_score(y, preds))
# For data reduced in dimension
ohlc_reduced = pca.fit_transform(ohlc)
print(ohlc_reduced[:class="num">10]) # print class="num">10 rows of the reduced data 
model.fit(ohlc_reduced, y)
preds = model.predict(ohlc_reduced)
print("ohlc_reduced data LR accuracy = ",r2_score(y, preds))
ohlc_original data LR accuracy =  class="num">0.9937597843724363
[[-class="num">0.14447016]
 [-class="num">0.14997874]
 [-class="num">0.14129409]
 [-class="num">0.1293209 ]
 [-class="num">0.12659902]
 [-class="num">0.12895961]
 [-class="num">0.13831287]
 [-class="num">0.14061213]
 [-class="num">0.14719862]
 [-class="num">0.15752861]]
ohlc_reduced data LR accuracy =  class="num">0.9921387699876517
new_df = df.drop(columns=["OPEN", "HIGH", "LOW", "CLOSE"]) #
new_df["OHLC"] = ohlc_reduced
# Reorder the columns to make "ohlc" the first column
cols = ["OHLC"] + [col for col in new_df.columns if col != "OHLC"]
new_df = new_df[cols]
new_df.head(class="num">10)

收束

把特征选择真正跑通,关键不在算法本身,而在你敢不敢动那几个 99% 相关的 OHLC 字段。前文给出的 feature_selection.ipynb 里,Open、High、Low 在样本集上相关系数超过 0.99,直接删变量能省训练时间,但也会把影线和突破强度这类非线性信息一起丢掉。 更稳的做法是把高/低转成相对价差再喂给模型,维度没少多少,几何结构却保留完整。MT5 接 Python 做策略测试时,这种预处理能让回测更贴近真实烛形逻辑。 外汇与贵金属杠杆高、价格跳跃频繁,任何特征工程都只是降低过拟合概率,不保证实盘优势。开 MT5 把 Timeseries_OHLC.csv 拖进笔记本跑一遍,比看结论更有用。

常见问题

用L1惩罚(Lasso)回归把系数压成0,被归零的字段就是无关变量,直接从特征里剔除即可。
看各字段的feature_importance,数值明显低于阈值的品种价量字段大概率不驱动行情,可优先舍弃。
小布可对接你的品种页数据,自动跑随机森林与L1筛选并标出驱动字段,你只需确认结果就行。
用PCA压缩到第一主成分通常保留七成以上方差,回测显示对方向分类影响很小,可放心用。
在归零无关变量和主成分压缩后,用交叉验证看样本外误差,误差跳增就停止继续收束。