2. 金融时间序列基础

在把 TCN 用于金融长序列预测之前,必须先回答一个更基础的问题:我们手里的“序列”到底是什么。金融数据不是实验室里等间隔采样的理想信号,它同时受到交易制度、市场微观结构、信息披露节奏和参与者行为的影响。理解数据类型、频率、长序列特性、非平稳性以及噪声与缺失值,是后续特征提取和模型设计能否成立的前提。

一、金融数据类型与频率:先分清对象,再谈建模

金融数据按形态大致可分为四类:行情类(价格、成交量、买卖盘)、基本面类(财报、估值、宏观指标)、文本类(公告、新闻、研报、社交媒体)和衍生类(技术指标、波动率、资金流)。它们的采样机制完全不同:行情是事件驱动的高频流,基本面是低频离散快照,文本是不规则到达的异步事件。

频率决定了序列的“信息密度”和“噪声结构”。常见频率包括:

  • Tick 级:逐笔成交与委托,毫秒至秒级,含大量微观结构噪声;
  • 分钟级:1min、5min、15min,适合日内策略,但存在日内周期;
  • 日频:最常用的建模频率,样本量适中,噪声相对可控;
  • 周频/月频:宏观与基本面常用,样本少、长序列依赖强;
  • 混合频率:如日频价格配月频宏观,需要对齐或分层建模。

频率选择不是越高越好。高频数据信噪比低、存储与计算成本高,且非同步交易会引入伪相关。对 TCN 而言,日频与分钟级重采样通常是长序列建模的平衡点。

二、长序列特性:TCN 的用武之地

金融序列的“长”体现在两个层面:一是样本长度长(数年日频可达数千点),二是依赖跨度长(宏观周期、政策滞后、情绪传导可能跨越数百个时间步)。传统 RNN 在长依赖上易梯度消失,而 TCN 通过膨胀因果卷积以指数级扩大感受野,天然适合捕捉长程模式。

但长序列也带来三个现实问题:

  1. 分布漂移:市场制度变化使早期样本与近期样本分布不一致;
  2. 记忆衰减:并非所有历史都同等重要,需要注意力或门控机制筛选;
  3. 计算与显存:长输入会显著增加卷积层显存占用,需要分块或降采样。
实践建议:对日频数据,输入窗口可从 60~250 步起步;对分钟级,可先聚合为 5min 或 15min 再送入 TCN,以降低噪声并控制序列长度。

三、非平稳性:金融序列的“默认状态”

平稳性要求均值、方差和自协方差不随时间变化。金融价格几乎从不满足这一条件:存在趋势、波动率聚集、结构突变。直接对原始价格建模,模型容易学到“时间位置”而非“可泛化模式”。

处理非平稳性的常用手段包括:

  • 差分:一阶差分消除趋势,得到收益率序列;
  • 对数变换:稳定方差,使乘法关系线性化;
  • 滚动标准化:用滚动窗口的均值方差做 z-score,避免未来信息泄露;
  • 波动率建模:GARCH 类模型或滚动波动率作为特征;
  • 结构突变检测:识别制度切换点,分段建模或加入虚拟变量。

需要强调:差分和标准化必须只用历史信息。若用全样本均值方差做标准化,会造成严重的数据穿越,离线指标虚高、实盘失效。

四、噪声与缺失值:清洗决定上限

金融噪声来源多样:买卖价差跳动、非同步交易、错误报价、停牌、涨跌停、节假日。缺失值则常见于:非交易日、停牌、数据源断档、字段未披露。处理方式需区分“真缺失”与“假缺失”。

问题类型 典型场景 推荐处理 风险提示
随机缺失 数据源偶发断点 线性插值 / 前向填充 连续长段缺失不宜插值
结构性缺失 停牌、非交易日 按交易日历对齐,保留掩码 直接填充会制造虚假连续
异常值 错误报价、闪崩 MAD / 分位数截断 / 中位数替换 勿用全局均值,易受极值污染
高频噪声 Tick 级跳动 重采样、移动平均、小波去噪 过度平滑会抹掉有效信号

一个稳健的清洗流程可以概括为:对齐交易日历 → 标记缺失与异常 → 分类型填充 → 生成缺失掩码 → 滚动标准化。掩码本身可作为特征输入 TCN,让模型知道“这里原本没有数据”。

# 示例:滚动标准化 + 缺失掩码(避免未来信息泄露)
import numpy as np
import pandas as pd

def rolling_zscore(series, window=60):
    mean = series.rolling(window, min_periods=1).mean()
    std = series.rolling(window, min_periods=1).std()
    return (series - mean) / (std + 1e-8)

df = pd.read_csv("prices.csv", parse_dates=["date"]).set_index("date")
df["ret"] = np.log(df["close"]).diff()
df["ret_z"] = rolling_zscore(df["ret"], window=60)
df["mask"] = df["ret"].notna().astype(int)
df["ret_filled"] = df["ret"].fillna(0.0)
print(df[["ret", "ret_z", "mask", "ret_filled"]].tail())
常见误区:用未来数据做标准化、用全局统计量填充缺失、把停牌日当作正常交易日、对价格直接建模而不做平稳化。这些都会让离线回测“看起来很美”,实盘却迅速失效。

五、案例复盘:从原始行情到可建模序列

假设我们拿到某股票 2018—2024 年的日频行情,目标是预测未来 5 日收益率。第一步,按交易日历对齐,剔除停牌日并记录掩码;第二步,用对数差分得到收益率,用 MAD 截断极端值;第三步,用 60 日滚动窗口做 z-score;第四步,构造 TCN 输入张量,形状为 [batch, channels, seq_len],其中 channels 包含收益率、成交量变化、波动率和掩码。

复盘发现:若跳过掩码,模型在停牌前后会出现异常预测;若用全样本标准化,验证集指标明显优于实盘。可见,数据基础工作直接决定 TCN 的上限,模型结构只是在这个上限内做优化。

金融时间序列从原始数据到 TCN 输入的处理链路 原始行情 价格/成交量/Tick 频率对齐 交易日历/重采样 平稳化 对数差分/滚动标准化 噪声与缺失 MAD/掩码/填充 TCN 输入 关键约束 • 所有统计量(均值/方差/分位数)只能来自当前及历史窗口,严禁未来信息泄露 • 缺失掩码与填充值同时输入,让模型区分“真实为零”与“原本缺失” 非平稳性 趋势 / 波动率聚集 / 结构突变 → 差分 + 滚动标准化 长序列依赖 宏观周期 / 政策滞后 / 情绪传导 → TCN 膨胀因果卷积 噪声与缺失 错误报价 / 停牌 / 断档 → 掩码 + 分类型填充

把上述基础工作做扎实,TCN 才能专注于学习真正可泛化的时序模式。数据类型决定输入通道,频率决定序列长度与噪声水平,非平稳性决定预处理方式,噪声与缺失决定清洗策略——四者共同构成金融长序列预测的地基。