数据科学与机器学习(第 09 部分):以 MQL5 平铺直叙 K-均值聚类(基础篇)
先把数据当废品看
做聚类之前,先接受一个有点刺耳的判断:原始行情数据在没有明确用途前,和废品没两样。Mark Twain 那句“收集前先想清楚用来干嘛”放到 MQL5 特征工程里同样成立——你若没想好拿 K-均值去切哪类状态,撸再多 Tick 也只是占硬盘。 在 MT5 里跑无监督学习,第一道坎不是算法而是意图。比如你打算用 K-均值把 EURUSD 的波动率聚成几类,就得先决定喂进去的是收盘价差值、ATR 还是归一化收益率。外汇与贵金属杠杆高、跳空频繁,特征没选对,聚类结果在样本外大概率失效,这是高风险前置条件。 本系列后续会直接贴 MQL5 平铺实现,不绕 Python 桥接。你能立刻做的一件事:打开 MT5 策略测试器,随便拉一段 H1 历史,先手算三组收盘差分,别急着写聚类。
「未标记数据如何逼出算法自寻规律」
监督学习依赖带标签的样本,回归、SVM、决策树、神经网络都在此列,模型训练时已知每个输入对应的输出。但无监督学习拿到的数据没有标签,算法得自己从分布里挖关系,没人告诉它什么是对的。 外汇与贵金属行情序列若直接喂入这类方法,往往先要脱去时间标签做特征化,否则模型容易把时序噪声当结构。这类品种杠杆高、跳空频繁,纯无监督得出的簇边界只反映历史样本形态,实盘切换周期后可能迅速失效。 典型任务落在三类:聚类(把相似状态归堆)、降维(压缩维度保留主变异)、密度估算(算某区域样本浓淡)。在 MT5 里用自编指标跑 K-means 前,建议先用密度估算看样本是否真有天然分界,避免硬切造成的伪信号。
◍ K-均值之前的聚类逻辑
聚类分析的本质,是把一组对象按属性相似度切进不同群组:相似度高的进同一簇,差异大的分属不同簇。商场里同类商品摆在一起,就是一次人工聚类;放到未标注的数据集上,算法替你完成这件事。 它不是一个具体算法,而是一类任务框架。不同算法对“什么是簇”的理解差异很大,所以不能拿一种结果套所有场景。 业界常提三种聚类:独占聚类(如 K-均值,一个点只属一簇)、重叠聚类(如模糊 C-均值,一个点可跨多簇)、层次聚类(自底向上或自顶向下建树)。本文后续只落地独占聚类里的 K-均值,外汇与贵金属行情序列做形态归并时,这类硬切分最直观,但需注意高频噪声可能扭曲簇心,属高风险建模前提。
聚类在跨行业推荐里的落点
电商与流媒体早已把聚类当基础工具:亚马逊把历史浏览与购买行为叠成向量,将相似商品聚成一堆做关联推荐;Netflix 按观影兴趣把用户和影片双向聚类,推的可能是一组协同观看概率更高的片子。 这类做法的本质,是从营销、生物医学、地理空间等多变量数据集里,用距离度量切出「相似对象群」。对做价格行为的人而言,思路可直接平移——把 K 线片段、波动率区间、成交量轮廓当成多维样本,聚出的簇就是市场状态的分群,而非拍脑袋划分的震荡或趋势。 外汇与贵金属属高风险品种,这类聚类只是状态识别辅助,不能单独作为开仓依据,须配合结构位与风控。
「手搓 K-均值聚类的 MT5 起步」
K-均值和 k-最邻近是两回事,前者是向量量化方法,把 n 个观测硬拆成 k 个簇(k < n),每个点归到离最近质心那一组。它是实战里最常被直接搬用的聚类手段,数学不绕,核心就是「选初始质心 → 算距离分组 → 迭代更新质心」。 先在 MetaEditor 里把质心存进 CKMeans 函数库的私有矩阵,避免和外部逻辑打架。启动阶段要手动塞入随机质心到 InitialCentroid 矩阵,这一步看着糙,但决定了后面收敛路径。下边代码里我们用了 8 行 2 列的样本集(如 {2,10}、{7,5} 等),并抽第 0 行和第 2 行当初始质心,日志打出的是 [[2,10] [5,8]]。 距离度量建议用欧氏距离,即直线距离,对二维特征(比如两段均线偏离值)足够直观。跑完初始分组后质心先不动,等首轮归属落定再更新——很多人卡在「为啥第一步不算新质心」,答案就是:第一次只有初始质心,新质心是末道工序。 外汇和贵金属行情用这招做状态划分时,样本窗口一换结果可能漂移,属正常概率现象,别当成确定性信号。
class CKMeans { class="kw">private: class="type">class="kw">ulong n; class="type">uint m_clusters; class="type">class="kw">ulong m_cols; matrix InitialCentroids; class=class="str">"cmt">//Intitial Centroids matrix vector cluster_assign; } matrix DMatrix = { {class="num">2,class="num">10}, {class="num">2,class="num">5}, {class="num">8,class="num">4}, {class="num">5,class="num">8}, {class="num">7,class="num">5}, {class="num">6,class="num">4}, {class="num">1,class="num">2}, {class="num">4,class="num">9} }; m_cols = Matrix.Cols(); n = Matrix.Rows(); class=class="str">"cmt">//number of elements | Matrix Rows InitialCentroids.Resize(m_clusters,m_cols); vector cluster_comb_v = {}; matrix cluster_comb_m = {}; vector rand_v = {}; for (class="type">class="kw">ulong i=class="num">0; i<m_clusters; i++) { rand_v = Matrix.Row(i * m_clusters); InitialCentroids.Row(rand_v,i); } Print("Initial Centroids matrix\n",InitialCentroids); CS class="num">0 class="num">06:class="num">44:class="num">02.152 K-means test(EURUSD,M1) Initial Centroids matrix CS class="num">0 class="num">06:class="num">44:class="num">02.152 K-means test(EURUSD,M1) [[class="num">2,class="num">10] CS class="num">0 class="num">06:class="num">44:class="num">02.152 K-means test(EURUSD,M1) [class="num">5,class="num">8]
◍ 用欧氏距离给 M1 行情做 K-means 聚类
上面这段 MT5 专家日志来自 EURUSD 的 M1 图表:CS 0 在 06:44:02.152 触发了 K-means test,输出聚类标签 [1,2],说明这一时刻的样本被划进了两个簇。外汇与贵金属 M1 级别噪声极大,这种聚类结果只能当作状态划分的参考,实战中误分概率不低。 K-means 里衡量样本归属的核心是两点间距离,实现上直接借用了毕达哥拉斯定理(勾股定理):在 n 维特征空间里,点 A 与点 B 的距离等于各维度差值平方和的算术平方根。对价格序列做特征工程时,若取收益率和波动幅度两维,这条距离公式就能直接算出每根 K 线到簇心的远近。 开 MT5 把这段日志对应的 EA 跑一遍,盯住 Experts 标签里 CS 0 的时间戳和 [1,2] 输出,就能验证你的特征维度是不是把行情切得太碎——M1 上簇数超过 3 往往只是过拟合噪声。
CS class="num">0 class="num">06:class="num">44:class="num">02.152 K-means test(EURUSD,M1) [class="num">1,class="num">2]