4. 周期识别方法:HP滤波、BP滤波、季节调整(X-13ARIMA-SEATS)

做景气度分析,说白了就是跟周期打交道。但经济数据这玩意儿,它不像正弦波那么干净。你拿到的原始序列里,什么成分都有——长期趋势、周期波动、季节效应、还有乱七八糟的噪声。怎么把这些东西拆开?这就是周期识别的核心问题。

我个人习惯,拿到数据第一件事不是跑模型,而是先问自己:我要提取的是哪种周期?是3-5年的基钦周期?还是10年左右的朱格拉周期?不同的目标,对应不同的工具。今天咱们就聊三个最常用的:HP滤波、BP滤波、还有季节调整的工业级工具X-13ARIMA-SEATS。

核心逻辑:任何时间序列都可以分解为:
Y_t = Trend_t + Cycle_t + Seasonal_t + Irregular_t
周期识别的任务,就是把Cycle_t从这锅粥里捞出来。

周期识别方法知识体系 原始时间序列 Y_t HP滤波 BP滤波 X-13ARIMA-SEATS 特点 • 提取长期趋势 + 周期 • 需要指定λ参数 • 端点效应明显 • 适合基钦周期 特点 • 指定频带提取周期 • 需要设定周期范围 • 无相位偏移 • 适合朱格拉周期 特点 • 去除季节效应 • 自动识别异常值 • 支持交易日效应 • 工业标准工具

4.1 HP滤波:最常用的周期分离器

HP滤波,全称Hodrick-Prescott滤波。这玩意儿在宏观研究里有多普及?这么说吧,你随便翻开一篇关于产出缺口的论文,十有八九都用过它。它的思路其实很朴素:把时间序列拆成趋势项周期项,然后通过一个惩罚参数λ来控制趋势的平滑程度。

数学上,它求解的是这样一个优化问题:

min Σ(y_t - τ_t)² + λ Σ[(τ_{t+1} - τ_t) - (τ_t - τ_{t-1})]²

前半部分让趋势尽量贴近原始数据,后半部分惩罚趋势的加速度。λ越大,趋势越平滑,周期成分就越多。

λ怎么选? 这是HP滤波最坑的地方。我见过不少新手直接用默认值,结果周期被扭曲得一塌糊涂。经验法则:

  • 年度数据:λ = 100
  • 季度数据:λ = 1600(最常用)
  • 月度数据:λ = 14400

这个1600是怎么来的?当年Hodrick和Prescott做季度GDP分析时定的,后来就成了行业惯例。但说实话,如果你做的是高频数据或者特定行业,最好自己调一调。

Python里实现HP滤波很简单,statsmodels直接封装好了:

import statsmodels.api as sm

# 假设data是pandas Series,频率为季度
cycle, trend = sm.tsa.filters.hpfilter(data, lamb=1600)

# cycle就是提取出来的周期成分
# trend是长期趋势

避坑指南:我曾经用HP滤波处理一段只有10年长度的月度数据,结果周期项在两端出现了明显的"翘尾巴"现象。这就是所谓的端点效应——滤波在数据起始和结束位置会失真。怎么解决?我一般会在两端各延长3-5期数据做预测,滤波完再截掉。或者用BK滤波做替代。

⚠️ 重要提醒:HP滤波不是万能的。它假设周期是平稳的,但现实中很多经济周期是非平稳的。另外,λ的选择会直接影响周期形态——λ太小,趋势会跟着周期跑;λ太大,周期里会混入噪声。我建议至少试3个不同的λ值,看看周期形态是否稳健。

4.2 BP滤波:精准的频带提取器

BP滤波,全称Band-Pass滤波。它的思路跟HP滤波完全不同——HP是在时域里做优化,BP是在频域里做切割。说白了,就是指定一个周期范围,比如18-36个月,然后只保留这个频带内的波动。

为什么需要BP滤波?你想想看,HP滤波提取的周期其实是个"剩余项"——原始数据减去趋势。但这里面可能同时包含了3年的基钦周期和10年的朱格拉周期。如果你只想研究库存周期,那HP滤波就不够用了。

BP滤波最常用的实现是Christiano-Fitzgerald滤波(简称CF滤波)。它不需要对称窗口,能处理非平稳数据,而且没有相位偏移——这一点很重要,意味着提取出来的周期跟原始数据在时间上是严格对齐的。

import statsmodels.api as sm

# 提取周期范围:18-36个月(月度数据)
cycle = sm.tsa.filters.cffilter(data, low=18, high=36)

# low和high的单位是数据频率的周期数
# 如果是季度数据,想提取4-8个季度,就设low=4, high=8

我个人习惯,做库存周期分析时用BP滤波提取18-36个月的成分,做产能周期分析时提取72-120个月的成分。这样每个周期成分都"各归各位",不会互相污染。

HP vs BP 怎么选?

  • HP滤波:适合"我不知道周期多长,先看看有什么"的探索性分析。速度快,参数少。
  • BP滤波:适合"我知道周期范围,要精确提取"的确认性分析。需要先验知识,但结果更干净。

我的工作流通常是:先用HP滤波看个大概,再用BP滤波做精确提取。

注意:BP滤波对数据长度有要求。你想提取18-36个月的周期,数据至少要有5-8年的长度,否则频域分辨率不够。我记得有一次帮一个私募做行业轮动策略,对方只给了3年的数据,我硬着头皮用BP滤波,结果提取出来的周期跟随机噪声差不多。后来我建议他们至少积累5年数据再跑。

4.3 季节调整:X-13ARIMA-SEATS

说到季节调整,很多人的第一反应是"不就是把季节性去掉吗?" 嗯,道理是这个道理,但实际操作远没那么简单。季节效应背后可能隐藏着:

  • 日历效应:春节、国庆、圣诞节这些移动节日
  • 交易日效应:每个月的工作日天数不同
  • 闰年效应:2月多一天
  • 异常值:疫情、罢工、自然灾害

X-13ARIMA-SEATS是美国人口普查局开发的工业级季节调整工具。它结合了两种方法:X-11(基于移动平均)和SEATS(基于ARIMA模型)。我个人的经验是,SEATS方法对复杂季节模式的适应性更好,但X-11更稳健。

Python里调用X-13需要安装额外的库,不过statsmodels已经集成了简化版:

import statsmodels.api as sm

# 季节调整
decomposition = sm.tsa.seasonal_decompose(data, model='additive', period=12)
seasonal = decomposition.seasonal
trend = decomposition.trend
residual = decomposition.resid

# 季节调整后的序列 = 原始数据 - 季节成分
adjusted = data - seasonal

但说实话,statsmodels内置的季节分解功能比较基础。真正做研究时,我建议用Python的x13as库或者直接调用R的seasonal包。R的seasonal包是X-13ARIMA-SEATS的官方封装,功能最全。

实战经验:做季节调整时,一定要先检查数据中是否有异常值。我曾经处理过一组零售数据,2020年2月因为疫情出现了一个巨大的负值。如果不做处理,X-13会把那个异常值"解释"为季节效应的一部分,导致整个季节因子都被扭曲。正确的做法是:先识别异常值,用回归或插值修正,再做季节调整。

季节调整的"三看"原则:

  1. 看原始序列:有没有明显的季节性波动?振幅是否随时间变化?
  2. 看季节因子:每个月的季节因子是否稳定?如果某个月突然变化很大,要警惕数据质量问题。
  3. 看调整后序列:是否还有残留的季节性?可以用谱分析检查。

⚠️ 常见误区:很多人做完季节调整就直接用调整后的数据做周期分析。但别忘了,季节调整本身也会影响周期成分。X-13的移动平均过程会抹掉一些高频波动。我建议:先做季节调整,再做周期滤波,顺序不要搞反。而且做完周期滤波后,最好再跟原始数据做个对比,确保没有过度平滑。

4.4 三种方法的配合使用

说了这么多,你可能会问:这三种方法到底怎么配合?我分享一个自己常用的工作流:

步骤 方法 目的 输出
1 X-13ARIMA-SEATS 去除季节效应和日历效应 季节调整后序列
2 HP滤波(λ=1600) 初步分离趋势和周期 趋势项 + 周期项
3 BP滤波(18-36个月) 精确提取目标周期 基钦周期成分
4 BP滤波(72-120个月) 提取长周期成分 朱格拉周期成分

这个流程的好处是:先用X-13把"脏东西"(季节、异常值)清理掉,再用HP滤波做个粗筛,最后用BP滤波精准定位。每一步的输出都可以作为下一阶段的输入,逻辑非常清晰。

嗯,最后说一句:工具只是工具,关键是你对周期的理解。HP滤波、BP滤波、X-13都是好工具,但如果你不知道自己在找什么周期,再好的工具也帮不了你。我见过太多人把数据扔进滤波器,出来一堆曲线,然后对着曲线编故事——那不是研究,那是算命。

做周期分析,先想清楚你的经济逻辑,再动手跑代码。这个顺序,千万别搞反了。


专注资料整理