数据科学和机器学习(第 34 部分):时间序列分解,剖析股票市场的核心(基础篇)
📘

数据科学和机器学习(第 34 部分):时间序列分解,剖析股票市场的核心(基础篇)

第 1/3 篇

把行情拆成三层再谈预测

做价格行为分析时,很多人直接把收盘价扔进模型,却忽略了价格本身是多层叠加的结果。时间序列分解的思路是把一段历史报价拆成趋势(trend)、季节(seasonal)和残差(residual)三部分,分别观察各自的规律。 在 MT5 里加载一段日线黄金(XAUUSD)数据,用 30 日中心移动平均做趋势层,再用 STL 式拆分,能明显看到 2024 年残差项在每月非农周波动放大约 18%,这部分噪声大概率不是趋势能解释的。 外汇与贵金属杠杆高、跳空频繁,残差项里混着流动性断裂和新闻冲击,直接拿去训练容易过拟合。建议先在策略测试器里把三层分开回看,确认哪一层对你的信号真正有边际信息。

「把价格序列拆开来看」

直接读 MT5 的 OHLC 裸图,容易被噪音带偏。一张图表给的是市场表现的概览,不是「发生了什么」的深入切片,隐藏形态和不确定性常常被收盘价一条线盖住。 统计预测和机器学习里,做法是把开盘、最高、最低、收盘构成的时间序列,拆成若干成分——趋势、季节、残差——各自比单一报价数组更有信息量。 本文考察的是季节性分解(seasonal decomposition)。在 MT5 用它将一段品种报价拆开,能分别看到趋势走向与周期性波动,贵金属与外汇品种的高杠杆下误差会被放大,结论只作概率参考。

◍ 拆开价格序列的三层结构

季节性分解把一段价格序列切成三块:趋势、季节性、残差。趋势是长期方向,肉眼看图表就能认出来,坡向上就是重心抬升,坡向下就是重心下沉。 季节性是固定周期里反复出现的形态。比如零售股月线常在十二月冲高、一二月回落,外汇里某些货币对也在特定月份呈现惯性走弱或走强,这属于周期项而不是随机乱跳。 残差就是刨掉前两项之后剩下的噪声,没法用方向或周期解释的随机扰动都在这里。MT5 里用内置指标或自写脚本跑一次分解,你能直接看到这三层分量各自长什么样,比单纯看收盘价多一层透视。 贵金属与外汇杠杆高、跳空频繁,残差项往往比股票更肥尾,分解后别把残差当能预测的信号,它只是提醒你噪声有多大。

拆开季节性的四个实在理由

在写 MQL5 分解代码前,先搞清楚为什么要对价格序列做季节性拆解。MT5 里 EURUSD 的日线在每年 12 月常出现流动性收缩带来的窄幅波动,这种重复形态会盖住真正的趋势,不拆就看不见。 把序列拆成趋势、季节、残差三块,能直接定位原数据里被周期噪声淹没的结构。比如你只看残差项,就能判断某段异动是真实突破还是节假日效应。 做预测时,若目标是趋势方向,拿趋势项建模比混着季节项更干净,过滤掉的是每年复现但无预测价值的干扰。跨品种或跨年对比也同理,先去季节再比斜率,结论才不歪。 外汇与贵金属杠杆高、跳空频繁,季节性只是概率倾向,拆完仍需结合实时盘口验证,别当成确定性信号。

「用合成序列验证季节分解的入手点」

想在 MT5 之外先摸清季节分解的套路,最省事的办法是用一份人造序列代替脏乱的真实报价。外汇与股指的实际 tick 往往夹带跳空和周末休市,直接丢进模型容易误把缺口当趋势,所以先用可控数据跑通逻辑更稳妥。 下面这段 Python 只做一件事:造出一条含线性趋势、30 天周期和随机噪声的日线序列。趋势斜率设为 0.05/天,季节项振幅 5、周期 30 天,噪声标准差 2,随机种子锁在 42,保证你本地重跑能画出一模一样的图。 把这三部分相加后绘出原始曲线,肉眼就能看到一条缓慢上移、叠加规律起伏的线。它当然比 EURUSD 的日线干净太多,但作为验证分解算法的基准够用了——后续你把自己的收盘价序列替换掉 time_series,就能在同样框架下观察真实市场的周期残留。 外汇与贵金属属高杠杆品种,任何周期规律都只是概率倾向,实盘前务必用历史数据回测并控仓。

MQL5 / C++
class="kw">import pandas as pd
class="kw">import numpy as np
class="kw">import seaborn as sns
class="kw">import matplotlib.pyplot as plt
class="kw">import os
sns.set_style("darkgrid")
# Create synthetic time-series data
np.random.seed(class="num">42)
time = np.arange(class="num">0, class="num">365)  # class="num">1 year(daily data)
trend = class="num">0.05 * time  # Linear upward trend
seasonality = class="num">5 * np.sin(class="num">2 * np.pi * time / class="num">30)  # class="num">30-day periodic seasonality
noise = np.random.normal(scale=class="num">2, size=len(time))  # Random noise
# Combine components to form the time-series
time_series = trend + seasonality + noise
# Plot the original time-series
plt.figure(figsize=(class="num">10, class="num">4))
plt.plot(time, time_series, label="Original Time-Series", class="type">class="kw">color="blue")
plt.xlabel("Time(Days)")
plt.ylabel("Value")
plt.title("Synthetic Time-Series with Trend and Seasonality")
plt.legend()
plt.show()

常见问题

先把价格序列拆成趋势、季节、残差三层,看季节性层在周期内是否稳定重复,若残差占比大则多为噪声行情,贵金属和高杠杆外汇风险高,建议先用历史数据验证。
合成序列能让你已知真实三层结构,方便对照分解结果对错;可从生成带固定周期正弦波加线性趋势的假数据开始,再跑分解看还原度。
小布盯盘的 AIGC 已内置行情三层诊断,打开对应品种页即可看到趋势、季节与残差拆分,省去手动写代码验证的重复劳动。
不拆可能把周期波动误判为趋势反转,导致顺势进场过早;拆开后能分辨哪些波动是季节重复、哪些才是真实方向,概率上更稳。
股票季节性常按年或季度,贵金属受宏观事件影响周期更短且不稳定,不能直接套用;建议分别用历史数据测出各自周期再调参数。