金融时间序列中的保形预测探索·综合运用
(3/3)·单点预测在厚尾行情里常常失灵,本篇把MAPIE校准、训练与终端导出串成一条可跑的通路
「标签切割后的样本回收」
上面这段 Python 风格切片把特征列和两类标签一并返回,但在 MQL5 里做同类样本整理时,得先确认 meta_labels 字段确实落到了 0 值分组,否则多元素集合会被误并。 打印 multi_element_sets 的数量,是验证标签离散程度最直接的办法;若输出远大于预期,说明元标签没按设想归零,后续训练集分布会偏。 开 MT5 接这套逻辑前,建议先跑一遍计数,把 meta_labels=0 的样本规模记下来,外汇与贵金属数据高频跳变,标签错分会让回测概率明显失真。
print(f"Multi-element sets(meta_labels=class="num">0): {multi_element_sets}") class="kw">return data[feature_columns + [&class="macro">#x27;labels&class="macro">#x27;, &class="macro">#x27;meta_labels&class="macro">#x27;]]
◍ 用保形置信度反洗基础模型标签
初步跑通保形预测流程后会撞到一个硬伤:元模型靠屏蔽高不确定性样本来兜错,但底层分类器仍在含噪原始标签上训练,泛化偏弱,碰到非平稳行情整体就容易垮。 MAPIE 训练完会吐出两样东西——点预测 predicted 和预测集合 y_prediction_sets。原版只拿预测集合去训元模型,基础模型却还吃着原始 labels。可以反过来:只用 predicted 与原始 labels 一致的那部分样本去训基础模型,相当于让保形分类器先给数据做一次置信过滤。 下面这段代码先把 meta_labels 全置 0,再把 predicted 等于原始 labels 的行标成 1;fit_final_models 里主模型 X、y 都只取 meta_labels==1 的干净子集,元模型仍用全量特征配 conformal_labels。两套模型都尽量在保形置信度高的区段上学习,输出质量在非平稳序列上会好一些。 外汇与贵金属波动受突发事件驱动,这类过滤只能降低噪声干扰、无法消除跳空风险,实盘前请在 MT5 用历史数据复跑确认标签一致率。
predicted, y_prediction_sets = mapie_classifier.predict_set(X) <span class="comment"># Initialize meta_labels column with zeros</span> data[<span class="class="type">class="kw">string">&class="macro">#x27;meta_labels&class="macro">#x27;</span>] = <span class="number">class="num">0.0</span> <span class="comment"># Set meta_labels to class="num">1 where predicted labels match original labels</span> <span class="comment"># Compare predicted values with the original labels in the dataset</span> data.loc[predicted == data[<span class="class="type">class="kw">string">&class="macro">#x27;labels&class="macro">#x27;</span>], <span class="class="type">class="kw">string">&class="macro">#x27;meta_labels&class="macro">#x27;</span>] = <span class="number">class="num">1.0</span> <span class="keyword">def</span> fit_final_models(dataset) -> <span class="built_in">list</span>: <span class="comment"># features for model\meta models. We learn main model only on filtered labels </span> X = dataset[dataset[<span class="class="type">class="kw">string">&class="macro">#x27;meta_labels&class="macro">#x27;</span>]==<span class="number">class="num">1</span>] X = X[X.columns[<span class="number">class="num">1</span>:-<span class="number">class="num">3</span>]] X_meta = dataset[dataset.columns[<span class="number">class="num">1</span>:-<span class="number">class="num">3</span>]] <span class="comment"># labels for model\meta models</span> y = dataset[dataset[<span class="class="type">class="kw">string">&class="macro">#x27;meta_labels&class="macro">#x27;</span>]==<span class="number">class="num">1</span>] y = y[y.columns[-<span class="number">class="num">3</span>]] y_meta = dataset[<span class="class="type">class="kw">string">&class="macro">#x27;conformal_labels&class="macro">#x27;</span>]
置信水平如何改写EURUSD的预测噪声比
用 2020 年初到 2025 年初的 EURUSD_H1 数据循环训练 10 个模型,留出之后时段做未来预测。基础分类器是 15 棵最大深度 5 的随机森林,交叉验证 5 折,置信水平先锁在 0.90。 训练报告里最刺眼的是噪声占比:单类别可靠预测仅 6715 条,而多类别不可靠集合高达 22948 条,说明原始样本里大部分时候模型根本给不出 90% 置信的单方向判断。模型只吃可靠预测,主模型验证准确率 0.92、元模型 0.965,平衡曲线 R2 跑到 0.993,拟合几乎贴着资金线。 把置信水平从 0.9 降到 0.6,报告立刻变样——单类别集合从 6715 涨到 28647,多类别只剩 991。预测覆盖变广,但交易获利的置信度同步塌方,净值回撤明显变密。外汇与贵金属属高风险品种,这种回撤放大不是纸面波动,而是实盘可能连续吃止损。 调置信水平本质是在交易频次、收益厚度和黑天鹅概率之间拉闸。想少做但稳,就往 0.9 靠;想铺开信号,就承受 0.6 带来的不确定性。参数文件里 stop_loss=0.00500、take_profit=0.00200 已经写死,改 confidence_level 前先想清楚盈亏比会不会被回撤吃掉。
hyper_params = {
&class="macro">#x27;symbol&class="macro">#x27;: &class="macro">#x27;EURUSD_H1&class="macro">#x27;,
&class="macro">#x27;export_path&class="macro">#x27;: &class="macro">#x27;/Users/dmitrievsky/Library/Program Files/MetaTrader class="num">5/MQL5/Include/Trend following/&class="macro">#x27;,
&class="macro">#x27;model_number&class="macro">#x27;: class="num">0,
&class="macro">#x27;markup&class="macro">#x27;: class="num">0.00010,
&class="macro">#x27;stop_loss&class="macro">#x27;: class="num">0.00500,
&class="macro">#x27;take_profit&class="macro">#x27;: class="num">0.00200,
&class="macro">#x27;periods&class="macro">#x27;: [i for i in range(class="num">5, class="num">300, class="num">30)],
&class="macro">#x27;backward&class="macro">#x27;: class="type">class="kw">datetime(class="num">2020, class="num">1, class="num">1),
&class="macro">#x27;forward&class="macro">#x27;: class="type">class="kw">datetime(class="num">2025, class="num">1, class="num">1),
}
models = []
for i in range(class="num">10):
print(&class="macro">#x27;Learn &class="macro">#x27; + str(i) + &class="macro">#x27; model&class="macro">#x27;)
models.append(fit_final_models(meta_learners_mapie(class="num">15, class="num">5, confidence_level=class="num">0.90, CV_folds=class="num">5)))
Learn class="num">9 model
Empty sets(meta_labels=class="num">0): class="num">0
Single element sets(meta_labels=class="num">1): class="num">6715
Multi-element sets(meta_labels=class="num">0): class="num">22948
Correct predictions(meta_labels=class="num">1): class="num">16638
Incorrect predictions(meta_labels=class="num">0): class="num">13025
R2: class="num">0.9931613891107195
>>> models[-class="num">1][class="num">1].get_best_score()[&class="macro">#x27;validation&class="macro">#x27;]
{&class="macro">#x27;Accuracy&class="macro">#x27;: class="num">0.92, &class="macro">#x27;Logloss&class="macro">#x27;: class="num">0.1990666082064606}
>>> models[-class="num">1][class="num">2].get_best_score()[&class="macro">#x27;validation&class="macro">#x27;]
{&class="macro">#x27;Accuracy&class="macro">#x27;: class="num">0.9647435897435898, &class="macro">#x27;Logloss&class="macro">#x27;: class="num">0.10955056411224594}
Learn class="num">8 model
Empty sets(meta_labels=class="num">0): class="num">0
Single element sets(meta_labels=class="num">1): class="num">28647
Multi-element sets(meta_labels=class="num">0): class="num">991
Correct predictions(meta_labels=class="num">1): class="num">16999
Incorrect predictions(meta_labels=class="num">0): class="num">12639
R2: class="num">0.9850582205528567「把模型丢进 MT5 跑一遍」
导出步骤和之前几篇里写的没差别,老读者直接按熟悉的流程把 ONNX 模型丢进终端就行。重点在测试环节:这套系统在样本外预测区间没飘,2017 到 2020 的四年历史行情里也跑得通,说明泛化不是纸上谈兵。 主阈值和元阈值按置信度卡信号。调高阈值,单笔信号的确定性倾向更强,但触发次数会明显变少——这是频率与胜率的硬权衡,外汇和贵金属杠杆高,信号稀疏时更要防样本偏差。 下面这段是导出调用的核心,逐行看:model 取训练列表里最后一个模型;symbol、periods 从超参字典拿;periods_meta 复用同一周期;model_number 标模型编号;export_path 指定落盘位置。复制去你本地改路径就能用。
export_model_to_ONNX(model = models[-class="num">1], symbol = hyper_params[&class="macro">#x27;symbol&class="macro">#x27;], periods = hyper_params[&class="macro">#x27;periods&class="macro">#x27;], periods_meta = hyper_params[&class="macro">#x27;periods&class="macro">#x27;], model_number = hyper_params[&class="macro">#x27;model_number&class="macro">#x27;], export_path = hyper_params[&class="macro">#x27;export_path&class="macro">#x27;])
◍ 把置信阈值当作风险闸门来调
保形预测不负责找规律,只给已有模型套一层不确定性量化外壳。训练阶段调高置信阈值,被筛掉的边缘样本变多,EA 触发频率下降但单笔错判概率倾向降低;调低则反之,样本通过率高却放大了外汇与贵金属的高风险暴露。 随文给出的 MQL5_files.zip 仅 99.17 KB,内含 mapie_trader.ex5 与源码,直接拖进 MT5 终端就能跑。想验证效果,先用 EURUSD_H1.csv 在 Python 侧按 mapie_causal.py 重训,再把 ONNX 丢进 Include\Trend following 文件夹覆盖,观察 EA 在实盘模拟里的通过样本数变化。 评论区有人反馈删掉 fixing_lib 不影响主逻辑,说明实验模块只是旁路。真要落地,重点还是你自己在 MT5 里把置信阈值那一行参数从默认改到 0.9 以上,看回测权益曲线是否平滑些。