数据科学和机器学习(第 29 部分):为 AI 训练目的而选择最佳外汇数据的基本技巧(基础篇)
挑外汇数据喂AI前先过特征选择这一关
在 MT5 上做外汇或贵金属的 AI 训练,直接把几十个指标一股脑丢进模型,往往换来的是过拟合和样本外崩盘。特征选择就是先筛掉冗余和噪声变量,只留对价格运动有解释力的输入。外汇保证金和贵金属杠杆交易高风险,模型再漂亮也得用历史外数据验证。 为什么非做不可?MT5 导出的 tick 或分钟级数据维度极高,EURUSD 一天就能产生上万行样本,若特征间高度共线,梯度下降会来回震荡,训练集精度虚高但实盘概率倾向失效。 常用三路打法:过滤法看相关矩阵和统计检验(卡方、ANOVA),包装法用递归特征消除(RFE)和顺序特征选择(SFS)反复试子集,嵌入式法靠套索回归和决策树自带的重要性权重边训边砍。降维如 PCA 可作为补充,但会损失可解释性,盯盘派一般更爱保留原始价格行为特征。
◍ 信息过剩下的特征筛选
MT5 终端里内置指标超过 36 个,可交易品种对也有 100 多个,再加上新闻流,手工交易或搭建 AI 模型时,手头素材其实严重过剩。 但常识告诉我们,这些里头肯定混着不少噪声:不是每个指标、每类数据都对某个品种或某套逻辑有用,硬塞进模型只会拖慢效率。 特征选择要解决的正是这件事——从一堆可用数据里挑出真正对交易决策和机器学习有用的子集,避免用错信号。外汇与贵金属波动受宏观与流动性影响大,错用特征会放大高风险下的误判概率。
「挑特征就是在给模型减负」
特征选择干的事,是从原始数据里筛出对预测真正有用的那部分变量,把噪音和弱相关项丢出去。它本质是给模型做减法:喂进去的维度越干净,过拟合的概率越低,训练时的计算开销也越小。 在 MT5 用 Python/R 外接做信号模型时,如果你把 50 个指标全塞进随机森林,跑一次调参可能要十几分钟;先做单变量相关性过滤,剩 8~12 个特征,同样的网格搜索能压到 2 分钟内出结果。 这一步不是可选项。特征堆得越多,模型越容易记住样本里的随机扰动,而不是学出价格行为的真实结构,外汇和贵金属这种高噪声市场里尤其明显,杠杆品种波动受消息扰动大,垃圾特征会直接把回测曲线美化到失真。
特征选择为什么是AI模型的命门
在 MT5 里跑 AI 信号,28 个变量全喂进去基本等于自找过拟合。特征选择第一步就是砍掉不相关或冗余的输入,模型维度降下来,CPU 和内存开销直接变小,回测速度也能提一截。 更关键的是预测质量:只留信息量最大的特征,模型对“下一根蜡烛开盘价 TARGET_OPEN”和“收盘价 TARGET_CLOSE”的拟合倾向更干净,准确率和泛化能力可能明显优于全量特征。 特征少了,逻辑也更容易被人看懂。你调参时能说清“为什么这个变量在影响输出”,而不是被黑箱反噬。 顺带一提,外汇和贵金属波动受噪声驱动,太多无关列容易让模型背下噪声,实盘高风险环境下容易翻车。特征选择本质是给模型去噪,降低过拟合概率。 我们用的数据集就是前文那套 28 变量的行情切片,目标是从中筛出跟 TARGET_OPEN、TARGET_CLOSE 最相关的列,丢掉弱相关项。主流方法分三类:筛选、包装、嵌入。 筛选法不依赖具体模型,先独立评估每个特征。常见做法是拉相关矩阵、跑统计检验,看单变量和目标列的相关性强度再决定留谁。
◍ 用相关矩阵给特征降噪
相关矩阵本质是一张记录变量间线性相关程度的表,表里每个格子填的是相关系数,取值范围锁死在 -1 到 1。1 代表两个量同涨同跌且比例固定,0 代表完全各走各的,-1 则是你升我必降的镜像关系。 在 MT5 导出的行情样本里(开高低收、成交量、时间字段等),先算一张全量相关矩阵,代码见下。矩阵太大时别硬看全图,先盯两类关系:特征之间、以及特征与目标之间。 特征彼此高度相关会触发多重共线性,线性类模型的系数估计会飘。实测开高低收三个字段相关性在 99.x%(四舍五入常显示 100%),这种冗余可直接删到只留一个,或用降维方法压缩。 特征与目标变量的强相关才值得留,信息量高、对预测帮助大;但 DAYOFWEEK、MONTH 这类类别字段不能直接进相关矩阵,因为系数只衡量数值变量的线性关系。后续可用统计检验筛出与目标显著相关的数值特征再喂模型。
# Compute the correlation <span class="keyword">matrix</span> corr_matrix = df.corr() # We generate a mask <span class="keyword">for</span> the upper triangle mask = np.triu(np.ones_like(corr_matrix, dtype=<span class="keyword">class="type">bool</span>)) cmap = sns.diverging_palette(<span class="number">class="num">220</span>, <span class="number">class="num">10</span>, as_cmap=True) # Custom colormap plt.figure(figsize=(<span class="number">class="num">28</span>, <span class="number">class="num">28</span>)) # <span class="number">class="num">28</span> columns to fit better # Draw the heatmap with the mask and correct aspect ratio sns.heatmap(corr_matrix, mask=mask, cmap=cmap, vmax=<span class="number">class="num">1.0</span>, center=<span class="number">class="num">0</span>, annot=True, square=True, linewidths=<span class="number">class="num">1</span>, cbar_kws={"shrink": .<span class="number">class="num">75</span>}) plt.title(&class="macro">#x27;Correlation Matrix&class="macro">#x27;) plt.savefig("correlation <span class="keyword">matrix</span>.png") plt.show()