4、库存因子构建:库存变化率、库存消费比、库销比偏离度、库存季节性调整、库存趋势分解

好,咱们进入第四章。这一章我打算把库存因子的构建方法掰开揉碎了讲清楚。你想想看,库存数据本身是死的,但因子是活的。怎么把一堆原始库存数据变成能用的交易信号?这就是本章要解决的核心问题。

我个人习惯把库存因子分成五类:变化率类、比值类、偏离度类、季节性调整类、趋势分解类。每一类都有它独特的应用场景。咱们一个一个来过。

4.1 库存变化率:最基础的动量信号

库存变化率,说白了就是看库存是涨了还是跌了。公式很简单:

库存变化率 = (当期库存 - 上期库存) / 上期库存

但这里有个坑。我在项目中遇到过,直接用月度库存算变化率,结果噪音特别大。为什么?因为库存数据本身就有很强的季节性。比如农产品,收获季库存暴增,这能说明需求变差了吗?不一定。

所以我建议用同比变化率:

库存同比变化率 = (当期库存 - 去年同期库存) / 去年同期库存

这样能过滤掉季节性因素。当然,如果你做的是高频交易,用环比也行,但一定要配合后面的季节性调整一起用。

核心要点:库存变化率反映的是库存的边际变化。正数代表累库,负数代表去库。累库速度加快,通常利空价格;去库速度加快,通常利多价格。

4.2 库存消费比:供需平衡的标尺

库存消费比,英文叫 Inventory-to-Consumption Ratio。这个指标在商品圈里用得特别多。公式:

库存消费比 = 期末库存 / 当期消费量

它衡量的是:以当前的消费速度,现有库存能撑多久。比如库存消费比是3个月,那就意味着即使不生产,现有库存还能卖3个月。

嗯,这里要注意。消费量数据往往比库存数据更难获取。我一般用两种方式处理:

  • 直接法:如果有官方消费数据,直接用
  • 间接法:用产量 + 进出口 - 库存变化 倒推消费量

举个例子,假设某商品月度数据如下:

月份 期初库存 产量 进口 出口 期末库存 推算消费 库存消费比
1月 100 50 10 5 110 45 2.44
2月 110 45 8 6 105 52 2.02

你看,1月库存消费比是2.44,2月降到2.02。说明供需在收紧,价格有上涨动力。

4.3 库销比偏离度:发现异常信号

库销比偏离度,是我个人比较喜欢的一个因子。它不直接看库销比的绝对值,而是看当前库销比偏离历史均值的程度。

库销比偏离度 = (当前库销比 - 历史均值) / 历史标准差

为什么要这么做?因为不同商品的库销比正常范围不一样。比如铜的库销比通常在1-2个月之间,而玉米的库销比可能高达6-8个月。直接用绝对值没法横向比较。

标准化之后,偏离度超过2或者低于-2,就属于极端情况。我曾经用这个因子做回测,发现当偏离度超过2.5时,价格反转的概率高达70%以上。

实战技巧:计算历史均值时,建议用滚动窗口,比如过去3年或5年。不要用全历史,因为市场结构会变。我一般用60个月的滚动窗口。

4.4 库存季节性调整:剥离周期干扰

库存数据有很强的季节性规律。比如:

  • 农产品:收获季库存高,种植季库存低
  • 能源品:冬季取暖需求高,库存消耗快
  • 工业品:春节前后工厂停工,库存累积

如果不做季节性调整,你看到的库存变化可能只是季节性波动,而不是真正的供需变化。

常用的季节性调整方法有两种:

  1. X-13ARIMA-SEATS:美国人口普查局开发的经典方法,适合月度数据
  2. 移动平均比率法:简单粗暴,适合快速计算

我给你们看一段Python代码,用移动平均比率法做季节性调整:

import pandas as pd
import numpy as np

def seasonal_adjust(data, window=12):
    """
    移动平均比率法季节性调整
    data: 月度库存数据,pandas Series
    window: 移动平均窗口,默认12个月
    """
    # 计算中心化移动平均
    ma = data.rolling(window=window, center=True).mean()
    
    # 计算季节性因子
    seasonal_ratio = data / ma
    
    # 计算同月平均季节性因子
    seasonal_factor = seasonal_ratio.groupby(seasonal_ratio.index.month).mean()
    
    # 调整后的数据
    adjusted = data / seasonal_factor[data.index.month].values
    
    return adjusted

# 使用示例
inventory = pd.Series(...)  # 你的库存数据
adjusted_inventory = seasonal_adjust(inventory)

嗯,这里要注意。季节性调整后的数据,反映的是「剔除季节性后的库存变化」。如果调整后的数据还在上升,说明累库是结构性的,不是季节性的。

4.5 库存趋势分解:看清长期方向

最后一个是趋势分解。我个人习惯用HP滤波或者STL分解,把库存数据拆成三部分:

  • 趋势项:长期方向,比如产能扩张带来的持续累库
  • 季节项:周期性波动,一年以内的规律
  • 残差项:随机扰动,突发事件的影响

为什么要做趋势分解?因为不同的交易策略关注不同的成分:

  • 趋势交易者:关注趋势项,做长线
  • 套利交易者:关注季节项,做跨期
  • 事件驱动:关注残差项,抓异常

下面是用STL分解的代码示例:

from statsmodels.tsa.seasonal import STL

def inventory_decomposition(data, period=12):
    """
    STL分解库存数据
    data: 月度库存数据
    period: 周期长度,月度数据设为12
    """
    stl = STL(data, period=period)
    result = stl.fit()
    
    trend = result.trend
    seasonal = result.seasonal
    residual = result.resid
    
    return trend, seasonal, residual

# 使用示例
trend, seasonal, residual = inventory_decomposition(inventory)

避坑指南:我曾经在回测中直接用原始库存数据做因子,结果发现信号滞后严重。后来改成用趋势项,信号提前了2-3个月。原因很简单:趋势项过滤掉了短期噪音,反映了真正的供需变化。

4.6 本章知识体系

为了让你更直观地理解这五个因子的关系,我画了一张图:

库存因子构建体系 原始库存数据 库存变化率 环比/同比变化 库存消费比 供需平衡标尺 库销比偏离度 异常信号发现 季节性调整 剥离周期干扰 趋势分解 长期方向判断 因子应用场景 变化率 → 短期动量 | 消费比 → 供需判断 偏离度 → 极端反转 | 季节性 → 跨期套利 | 趋势 → 长线配置

这五个因子不是孤立的。在实际项目中,我通常会把它们组合使用。比如:

  • 先用季节性调整处理原始数据
  • 再计算调整后的库存变化率和库存消费比
  • 然后算库销比偏离度,看是否处于极端位置
  • 最后用趋势分解确认长期方向

这样一套组合拳下来,你对库存的理解就立体多了。嗯,这一章的内容就到这里。记住,因子构建不是目的,目的是找到能赚钱的信号。下一章咱们会讲怎么把这些因子用到实际策略中。


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