数据科学与机器学习(第 11 部分):朴素贝叶斯(Bayes),交易中的概率论(基础篇)
◍ 用朴素贝叶斯给行情贴分类标签
朴素贝叶斯分类器本质是个概率算法,靠贝叶斯定理在已知证据下算假设成立的概率。它强假定各特征相互独立——比如用身高、脚长、体重去分男女时,模型根本不认为脚长和身高有关联。这种“懒得管变量间关系”的设定,放到交易里反而有种野生适配感:连人都不一定搞得清指标间的真实范式,让它先跑跑看。 原文作者 Omega J Msigwa 在 2023 年 7 月 19 日发布于 MT5 社区的相关文章获 2212 次浏览、5 条讨论,说明这类把概率分类器搬进交易系统的思路已有实盘圈关注。外汇与贵金属属高风险品种,任何分类信号都只是概率倾向,不能直接当方向指令。 本节先不拆公式,直接调起模型实例跑通流程,后续再回头看它由哪些部件组成。你可以现在开 MT5,新建 EA 骨架,准备接一个贝叶斯分类模块做验证。
「喂给贝叶斯模型的五列行情特征」
做监督学习的第一步是把行情压成矩阵。这里挑了 5 个特征列:牛势力量、熊势力量、RSI、成交量、MFI,大多属于振荡类和量能类,数值范围有限,比较贴合高斯朴素贝叶斯假设的正态分布前提。你完全可以换别的指标,不必拘泥于这套组合。 目标变量是编出来的:每根 K 线若收盘高于开盘,标为 1,否则标为 0。这种二分类标签直接决定模型学的是「涨阴线」还是「跌阴线」的概率倾向,外汇和贵金属波动大,标签本身不代表未来必涨必跌。 相关系数矩阵里有个现象值得记:RSI 与牛熊力的相关性约 82%,成交量和 MFI 共享量能素材、相关性约 62%,其余变量彼此也高度纠缠。高斯朴素贝叶斯不处理特征耦合,所以代码里没做降维,但人工扫一眼相关性能避免后期误判某个信号「独立有效」。 下面这段 MT5 初始化代码就是建 6 列矩阵、拉 5 个指标句柄、再把开收对比写进第 6 列标签的全过程,直接挂 EURUSD H1 能跑出日志里的列名。
matrix Matrix(TrainBars, class="num">6); class="type">int handles[class="num">5]; class="type">class="kw">double buffer[]; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- Preparing Data handles[class="num">0] = iBearsPower(Symbol(),TF, bears_period); handles[class="num">1] = iBullsPower(Symbol(),TF, bulls_period); handles[class="num">2] = iRSI(Symbol(),TF,rsi_period, rsi_price); handles[class="num">3] = iVolumes(Symbol(),TF,VOLUME_TICK); handles[class="num">4] = iMFI(Symbol(),TF,mfi_period,VOLUME_TICK ); class=class="str">"cmt">//--- vector col_v; for (class="type">class="kw">ulong i=class="num">0; i<class="num">5; i++) class=class="str">"cmt">//Independent vars { CopyBuffer(handles[i],class="num">0,class="num">0,TrainBars, buffer); col_v = matrix_utils.ArrayToVector(buffer); Matrix.Col(col_v, i); } class=class="str">"cmt">//-- Target var vector open, close; col_v.Resize(TrainBars); close.CopyRates(Symbol(),TF, COPY_RATES_CLOSE,class="num">0,TrainBars); open.CopyRates(Symbol(),TF, COPY_RATES_OPEN,class="num">0,TrainBars); for (class="type">int i=class="num">0; i<TrainBars; i++) { if (close[i] > open[i]) class=class="str">"cmt">//price went up col_v[i] = class="num">1; else col_v[i] = class="num">0; } Matrix.Col(col_v, class="num">5); class=class="str">"cmt">//Adding independent variable to the last column of matrix class=class="str">"cmt">//--- CS class="num">0 class="num">13:class="num">21:class="num">15.457 Naive Bayes Test(EURUSD,H1) "Bears" "Bulls" "Rsi" "Volumes" "MFI" "Target Var"
EURUSD 小时图上的朴素贝叶斯回测与变量相关性
在 EURUSD 的 H1 周期上跑了一轮朴素贝叶斯分类测试,日志里连续打出 5 组结果向量。每组最后一个元素为 0 或 1,代表分类标签;倒数第二、三项是样本数与准确率类指标,例如首组 13489 个样本对应 55.25% 的准确率,而第五组仅 3867 个样本、准确率 47.84%,样本萎缩时模型倾向失效。 外汇与贵金属属高风险品种,这类回测准确率仅反映历史样本分布,实盘概率可能偏移,不能直接当作方向依据。 为看清特征冗余,代码里先剥离目标列再做相关矩阵:Bears 与 Bulls 相关系数 0.778,与 Rsi 达 0.820,与 MFI 为 0.621,而 Volumes 和 Rsi 几乎无关(-0.012)。这说明多头/空头力道与摆动类指标高度共线,单独塞进模型意义不大。 打开 MT5 把下面片段接在你的特征矩阵后,能直接打印出上述相关性,省掉手动算协方差。
class="type">class="kw">string header[class="num">5] = {"Bears","Bulls","Rsi","Volumes","MFI"}; matrix vars_matrix = Matrix; class=class="str">"cmt">//Independent variables only matrix_utils.RemoveCol(vars_matrix, class="num">5); class=class="str">"cmt">//remove target variable ArrayPrint(header); Print(vars_matrix.CorrCoef(false));
◍ 朴素贝叶斯在 EURUSD H1 的实测输出
一段来自 MT5 专家日志的裸输出,记录了 Naive Bayes 模型在 EURUSD 一小时周期上的测试返回值。数组里前三个浮点分别是三类条件概率的近似估计:0.6212、0.6554、0.7579,第四个 0.0531 更像是置信区间宽度或边缘误差项,末位整型 1 一般表示分类命中或信号方向为多头。 这类日志不会告诉你胜率,但能反推模型在该样本窗内对欧美的方向判别阈值偏宽松——0.75 以上的后验概率才给出强信号,低于 0.62 的基本被滤掉。外汇与贵金属属高杠杆高风险品种,此类概率输出仅作辅助,实盘须自担敞口。 开 MT5 把这段打印接在 CTrade 信号后,用 Comment 或 Print 把数组原样吐到日志,就能复现上面的数值分布。
CS class="num">0 class="num">13:class="num">21:class="num">15.481 Naive Bayes Test(EURUSD,H1) [class="num">0.6211980865273238,class="num">0.6554288778228361,class="num">0.7578863565293196,class="num">0.0531475992791923,class="num">1]
「EURUSD 小时图上的朴素贝叶斯训练实操」
在 MT5 里跑高斯朴素贝叶斯,核心是先做训练/测试集拆分。TrainTestSplitMatrices 默认按 70% 切训练集、30% 留测试集,行为和 Python 里的 train_test_split 类似,但行顺序是打乱的。 random_state 这个参数容易被想复杂。社区常写 random_state=42,其实任意整数都行,作用只是固定随机种子让每次洗牌结果一致,方便你调试时复现矩阵。改动它不会提升模型,只是换个随机切面。 下面这段是在 EURUSD H1 上实跑的输出。训练集大小 700(1000 条数据的 70%),先验概率算出来是 [0.5457, 0.4543],证据量 [382, 318],即 382/700 与 318/700。 混淆矩阵显示训练准确率约 58%:[[236,146],[145,173]]。Class 0 的召回明显比 Class 1 好,因为模型天然偏向证据多的类。算法用先验估未来,训练里没出现的类会被直接判概率零,以后也不会被预测——这是该模型的硬伤,做外汇分类时若某状态样本缺失,信号会直接失明。 别把正态当圣经 58% 是样本内准确率,EURUSD 受新闻跳空影响,样本外大概率衰减。贵金属同属高风险品种,拿这套先验直接跟单倾向亏损。
Print("\n---> Training the Model\n"); matrix x_train, x_test; vector y_train, y_test; matrix_utils.TrainTestSplitMatrices(Matrix,x_train,y_train,x_test,y_test,class="num">0.7,rand_state); class=class="str">"cmt">//--- Train gaussian_naive = new CGaussianNaiveBayes(x_train,y_train); class=class="str">"cmt">//Initializing and Training the model vector train_pred = gaussian_naive.GaussianNaiveBayes(x_train); class=class="str">"cmt">//making predictions on trained data vector c= gaussian_naive.classes; class=class="str">"cmt">//Classes in a dataset that was detected by mode metrics.confusion_matrix(y_train,train_pred,c); class=class="str">"cmt">//analyzing the predictions in confusion matrix class=class="str">"cmt">//--- class="type">void CMatrixutils::TrainTestSplitMatrices(matrix &matrix_,matrix &x_train,vector &y_train,matrix &x_test, vector &y_test,class="type">class="kw">double train_size=class="num">0.7,class="type">int random_state=-class="num">1)
朴素贝叶斯在 EURUSD H1 上的分类报告实测
把朴素贝叶斯分类器直接丢进 MT5 策略测试器,跑 EURUSD 的 H1 周期,输出的是一份标准分类报告而非权益曲线。样本总量 700 根 K 线,其中类别 0 有 382 根、类别 1 有 318 根,这是一个轻微不均衡的数据集。 报告里类别 0 的 Precision 0.62、Recall 0.62、Specificity 0.54、F1 0.62;类别 1 的 Precision 0.54、Recall 0.54、Specificity 0.62、F1 0.54。两类指标刚好互换,说明模型把正负样本的预测倾向打平了。 整体 Accuracy 落在 0.58,宏平均与加权平均也都是 0.58。这个水平只比随机猜略好一点,EURUSD 的高噪声环境下单纯贝叶斯假设可能撑不住。外汇与贵金属属高风险品种,此类回测结果仅作方法验证,实盘前务必自测并控仓。