12、库存数据的平滑处理:移动平均、HP滤波与指数平滑

做基差交易的朋友都知道,库存数据这玩意儿,看着挺规整,实际上坑不少。我刚开始做量化那会儿,拿到的库存数据经常是「一惊一乍」的——今天突然跳升,明天又暴跌,搞得策略信号乱跳。后来才明白,不是市场疯了,是数据本身需要「洗一洗」。

说白了,原始库存数据里混杂着三种东西:趋势、周期和噪声。我们做平滑处理,就是要尽量把噪声滤掉,把真正的趋势和周期信号留下来。今天我就把三种最常用的方法——移动平均、HP滤波和指数平滑——掰开揉碎讲清楚。

核心观点:没有完美的平滑方法,只有最适合你交易频率的方法。日内交易和月频调仓,用的平滑参数完全不同。

12.1 移动平均:最朴素但最实用

移动平均,说白了就是「取最近N期的平均值」。我最早接触库存分析时,用的就是这招。简单,但有效。

举个例子。你手里有每周的铜库存数据,原始数据波动很大。你取一个4周移动平均,相当于把最近一个月的库存水平「抹平」了。这样做的好处是:季节性波动被削弱了,趋势更清晰了。

但这里有个坑——移动平均会引入滞后。你想想看,如果库存突然开始累库,移动平均线要过好几期才能跟上。我曾在一次锌的交易中吃过这个亏:移动平均显示库存还在去化,实际上已经连续三周累库了。等我反应过来,价差已经走了一大段。

我的经验:做日内或周频交易,用3-5期移动平均就够了。做月频调仓,可以考虑12期甚至更长。但记住,滞后是你的敌人。

代码实现很简单,但要注意边界处理:

# Python 实现简单移动平均
import pandas as pd
import numpy as np

def sma(series, window=4):
    """
    简单移动平均
    series: 库存时间序列
    window: 窗口期数
    """
    return series.rolling(window=window, min_periods=1).mean()

# 示例:对铜库存做4周平滑
copper_inventory = pd.Series([120, 125, 118, 130, 128, 135, 132, 140])
smoothed = sma(copper_inventory, window=4)
print(smoothed)

输出结果你会看到,前3期因为窗口不够,用的是部分数据。嗯,这里要注意——前几期的平滑值参考价值不大,我一般会直接丢弃。

12.2 HP滤波:把趋势和周期拆开

移动平均虽然好用,但它有个硬伤:你没法区分「长期趋势」和「周期性波动」。HP滤波就是来解决这个问题的。

HP滤波的原理,我尽量说得简单点:它把时间序列拆成两部分——趋势项和周期项。趋势项是缓慢变化的,周期项是围绕趋势上下波动的。中间有个参数λ(拉姆达),控制着趋势的平滑程度。

我在项目中遇到过这样的情况:用移动平均看铜库存,总觉得趋势在变,但说不清是季节性还是真正的方向性变化。用HP滤波一拆,趋势项和周期项分得清清楚楚。当时我就觉得,这工具真香。

但HP滤波也有坑。λ参数怎么选? 这是个经典问题。对于周度数据,我一般用λ=1600;月度数据用λ=14400;年度数据用λ=100。这些是Hodrick和Prescott当年做宏观经济研究时用的值,做商品库存分析也基本适用。

避坑指南:我曾经在铝库存数据上试过λ=100,结果趋势项几乎就是原始数据的复制品,周期项全是噪声。后来改成λ=1600,效果才正常。记住:λ越大,趋势越平滑;λ越小,趋势越贴近原始数据。

代码实现:

# HP滤波实现
from scipy import signal
import numpy as np

def hp_filter(series, lamb=1600):
    """
    HP滤波
    series: 输入序列
    lamb: 平滑参数
    返回: (趋势项, 周期项)
    """
    n = len(series)
    I = np.eye(n)
    D = np.diff(I, 2)  # 二阶差分矩阵
    trend = np.linalg.solve(I + lamb * D.T @ D, series)
    cycle = series - trend
    return trend, cycle

# 示例
inventory = np.array([120, 125, 118, 130, 128, 135, 132, 140])
trend, cycle = hp_filter(inventory, lamb=1600)
print("趋势项:", trend)
print("周期项:", cycle)

12.3 指数平滑:给近期数据更高权重

移动平均有个问题:它给窗口内的每个数据点相同的权重。但做交易的人都知道,最近的数据比三个月前的数据重要得多。指数平滑就是来解决这个问题的。

指数平滑的核心思想很简单:越近的数据权重越大,越远的数据权重呈指数衰减。你只需要一个参数α(alpha),控制衰减速度。α越大,近期数据权重越高,平滑效果越弱;α越小,历史数据影响越大,平滑效果越强。

我个人习惯用α=0.3做周度库存平滑。为什么是0.3?因为在这个参数下,最近4周的数据占了大约75%的权重,既保留了近期变化,又不会太敏感。当然,这个值不是固定的——如果你做日内交易,α可能要调到0.5甚至更高。

一个小技巧:如果你不确定α取多少,可以用网格搜索。把历史数据分成训练集和验证集,选那个让预测误差最小的α。我在做铜库存策略时就是这么干的,效果比拍脑袋好得多。

代码实现:

# 指数平滑实现
def exponential_smoothing(series, alpha=0.3):
    """
    指数平滑
    series: 输入序列
    alpha: 平滑系数 (0 < alpha < 1)
    """
    result = [series[0]]  # 初始值用第一个数据点
    for t in range(1, len(series)):
        smoothed = alpha * series[t] + (1 - alpha) * result[-1]
        result.append(smoothed)
    return np.array(result)

# 示例
inventory = [120, 125, 118, 130, 128, 135, 132, 140]
smoothed = exponential_smoothing(inventory, alpha=0.3)
print("平滑后:", smoothed)

12.4 三种方法的对比与选择

说了这么多,到底该用哪种?我整理了一个对比表,方便你快速决策:

方法 优点 缺点 适用场景
移动平均 简单直观,计算快 滞后明显,权重均等 快速查看趋势,周频交易
HP滤波 分离趋势和周期,参数可调 端点效应,参数敏感 研究库存周期,月频调仓
指数平滑 近期权重高,滞后小 参数选择依赖经验 日内交易,高频策略

你想想看,如果你做的是铜的跨期套利,价差对库存变化非常敏感。这时候用指数平滑可能更好,因为它能更快反映库存的边际变化。但如果你做的是铝的长期趋势交易,HP滤波可能更合适,因为它能帮你看到真正的周期拐点。

最后说一句:别迷信任何一种方法。我见过有人把HP滤波的参数调得天花乱坠,结果过拟合得一塌糊涂。也见过有人只用简单移动平均,照样赚钱。工具是死的,人是活的。关键是你得理解你的数据,理解你的交易逻辑。

我的建议:先用移动平均快速上手,等你对数据有了感觉,再尝试HP滤波和指数平滑。别一上来就搞复杂模型,容易迷失在参数调优里。

好了,库存平滑处理就讲到这里。记住,平滑只是手段,不是目的。我们的最终目标是找到库存与价差之间的稳定关系,而不是把数据抹得越平越好。


公众号:蓝海资料掘金营,微信deep3321