因果推断中的时间序列聚类(基础篇)
📘

因果推断中的时间序列聚类(基础篇)

第 1/2 篇

「用聚类把行情切成可比较的因果样本」

在 MT5 上做因果推断,最容易被忽略的一步是:历史价格不是独立同分布,而是存在明显的波动聚类和机制切换。直接拿全样本回测,会把不同市况下的因果效应混在一起,得出偏误很大的结论。 聚类在这里的作用,是把时间序列先按状态分组——比如低波动震荡、趋势加速、消息冲击后的高波动。同一簇内的样本在统计属性上更接近,用来做匹配或效应估计,才谈得上「可比」。外汇与贵金属杠杆高,簇间切换往往伴随滑点和点差放大,分组本身也要计入交易成本。 MQL5 里可以用基础的距离度量对 K 线特征向量做简单聚类,下面这段是原文给出的波动性聚类雏形,逐行看逻辑: [CODE] double VolatilityCluster(double &returns[], int period) { double var=0; for(int i=0;i<period;i++) var+=returns[i]*returns[i]; var/=period; if(var<0.0001) return 0; // low vol if(var<0.0004) return 1; // mid vol return 2; // high vol } [/CODE] 第一行定义函数,传入收益率数组和回看周期;第二至四行累加平方收益算方差;第五行按阈值切三档:低于 0.0001 判低波动返回 0,低于 0.0004 判中波动返回 1,其余高波动返回 2。 原文在 2024-10-15 发布的这组示例,基础样本下把 EURUSD 的 M1 收益按 20 根 K 线滚动,约 13% 的时段落在高波动簇。你开 MT5 把 period 调到 50,高波动占比通常会掉到 5% 上下,说明短窗更容易误判簇边界。

MQL5 / C++
[CODE]
class="type">class="kw">double VolatilityCluster(class="type">class="kw">double &returns[], class="type">int period)
  {
   class="type">class="kw">double var=class="num">0;
   for(class="type">int i=class="num">0;i<period;i++) var+=returns[i]*returns[i];
   var/=period;
   if(var<class="num">0.0001) class="kw">return class="num">0; class=class="str">"cmt">// low vol
   if(var<class="num">0.0004) class="kw">return class="num">1; class=class="str">"cmt">// mid vol
   class="kw">return class="num">2; class=class="str">"cmt">// high vol
  }
[/CODE]

◍ 用聚类给市场波动率分状态

聚类本质是无监督划分:把数据集拆成若干组,组内对象相似、组间相异。放到交易里,它能把一段资产的价格波动序列按特征切分,露出平时肉眼难辨的隐藏结构。 对贵金属和外汇这类高杠杆品种,波动率本身就是风险温度计。基于波动率做聚类,可自动标出「低波横盘」「高波突破」「无序震荡」等市场状态——这类状态识别能帮交易者避开方向不明期,但外汇/贵金属波动受消息驱动,状态切换可能瞬间发生,高风险始终存在。 具体落地分三步:先由价格标准差算出波动率序列;再用 K-Means 或 DBSCAN 对序列聚类;最后把聚类映射成可交易的市场状态。低波聚类倾向对应整理,高波聚类可能预示趋势启动或尖峰。 这法子省人工、能自动化,但也挑参数:聚类数、距离度量选错,分出来的状态就失真。部分算法在大样本下效率低,且忽略时间依赖,直接套用可能漏掉序列前后的因果联系。

几类聚类算法的实战取舍

在 MT5 以外的研究环境里,Python 现成库已经把主流聚类算法都打包好了。直接调用库做实验,比自己从零写实现更快,能省掉大量配置和调试时间,适合先快速验证思路。 K-Means 简单高效,但依赖初始中心且必须事先给定聚类数 K,用在行情分段时容易因 K 值拍脑袋而切歪。Affinity Propagation 不用预设簇数、能抓各种形状,但样本量大时计算复杂度偏高。 Mean Shift 可探任意形状且免设簇数,大样本计算贵;Spectral Clustering 适合非线性结构但调参难、也费算。层次聚类能出树状结构、适合未知簇数;GMM 给概率化软分配,可模拟异形密度。 HDBSCAN 与 BIRCH 都能自动定簇且吃得住大数据,但对参数敏感、内部复杂度不低。外汇与贵金属波动具有高杠杆高风险,拿这些算法做形态归类前,建议先用小样本回测确认稳定性。

「用波动率聚类切分 EURUSD 市场状态」

把金融序列按波动率做聚类,可以同时解决两件事:识别当前市场状态,以及给不同状态匹配差异化的模型。原文在 EURUSD 上跑了一整套流程——先由 meta_learner 用波动率元特征纠错并打簇,再对每个簇单独训一个最终模型。 实测里四种能出结果的算法速度分化明显:近邻传播、谱聚类、凝聚聚类、均值漂移在标准设置下慢到拿不到结果,直接垫底;HDBSCAN 因无需预设簇数且分离干净排第一,K-means(设 10 簇)次之,BIRCH 再次且略慢,高斯混合(也设 10 簇)利润图最噪、表现最差。 整个循环用 meta_learner(models_number=5, iterations=25, depth=2, bad_samples_fraction=0.9, n_clusters=N_CLUSTERS, algorithm=6) 跑元学习,algorithm=6 即 HDBSCAN;随后对数据里每个唯一簇过滤成二分类问题调 fit_final_models。主模型用 CatBoost 迭代 200、早停 25 轮、评估 Accuracy,元模型迭代 100、早停 15 轮、评估 F1。 聚类质量高度依赖输入:货币对、周期、起止时间、主/元特征数量、n_clusters、get_labels 的 min/max。因为搜索本身有随机性,原文建议把训练循环多跑几次(演示代码只 range(1) 跑一次)观察稳定性。外汇与贵金属杠杆高,聚类只是状态划分辅助,不代表任何方向确定性。

MQL5 / C++
def meta_learner(models_number: class="type">int,
                 iterations: class="type">int,
                 depth: class="type">int,
                 bad_samples_fraction: class="type">float,
                 n_clusters: class="type">int,
                 algorithm: class="type">int) -> pd.DataFrame:
    
    dataset = get_labels(get_prices())
    data = dataset[(dataset.index < FORWARD) & (dataset.index > BACKWARD)].copy()
    X = data[data.columns[class="num">1:-class="num">2]]
    X = X.loc[:, ~X.columns.str.contains(&class="macro">#x27;std&class="macro">#x27;)]
    meta_X = data.loc[:, data.columns.str.contains(&class="macro">#x27;std&class="macro">#x27;)]
    y = data[&class="macro">#x27;labels&class="macro">#x27;]
    B_S_B = pd.DatetimeIndex([])
    for i in range(models_number):
        X_train, X_val, y_train, y_val = train_test_split(
            X, y, train_size = class="num">0.5, test_size = class="num">0.5, shuffle = True)
        
        # learn debias model with train and validation subsets
        meta_m = CatBoostClassifier(iterations = iterations,
                                    depth = depth,
                                    custom_loss = [&class="macro">#x27;Accuracy&class="macro">#x27;],

常见问题

可用波动率聚类把历史行情切成高/低波动样本,当前K线落入哪类状态就按哪类做。优先看最近20~50根K线的波动分组结果。
不同波动状态下价格生成机制不同,混在一起会掩盖因果效应。先聚类切分样本,再在同类状态内做因果比较才靠谱。
可以。小布能按聚类结果标注当前品种所处的波动状态,并提示你该状态的历史胜率和样本量,省去手动切分。
KMeans计算快、适合日频状态切分;层次聚类能看出状态嵌套关系但慢。实盘多数用KMeans先定大状态。
样本少于30个的聚类簇统计意义弱,建议合并相邻状态或拉长观察周期。别拿小样本簇直接下重注,外汇高风险。