量化工具:Python实现库存-基差相关性分析与可视化

好,咱们进入实战环节。

前面几章我们聊了不少理论,库存怎么影响基差,基差怎么反映供需。但说实话,光靠眼睛看K线图,很难捕捉到那种微妙的联动关系。我个人的习惯是——让数据说话。

这一章,我们就用Python把库存和基差的关系量化出来。你想想看,当你能用一行代码算出相关系数,用一张图看清两个变量的走势,那种掌控感是完全不一样的。

准备工作:你需要哪些数据?

做这个分析,核心数据就两样:

  • 库存数据:比如某个品种的社会库存、港口库存或交易所仓单。频率最好是周度或日度。
  • 基差数据:现货价格减去期货主力合约价格。注意,基差有正负,别搞反了。

我在项目中遇到过一个问题:库存数据经常有缺失,尤其是节假日前后。嗯,这里要注意,缺失值处理不好,相关系数会失真。

小技巧:如果库存数据是周度的,而基差是日度的,我建议把基差也降频到周度,取每周五的收盘价计算。这样两个序列的时间轴就对齐了。

第一步:数据导入与清洗

假设我们已经有了一个CSV文件,里面包含日期、库存、基差三列。代码很简单:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# 读取数据
df = pd.read_csv('inventory_basis.csv', parse_dates=['date'])
df.set_index('date', inplace=True)

# 看一眼数据长什么样
print(df.head())

# 处理缺失值——我习惯用前向填充
df.fillna(method='ffill', inplace=True)

# 删除仍然存在的空值
df.dropna(inplace=True)

为什么要用前向填充?因为库存数据通常不会突变,上周的库存对本周有很强的参考意义。我曾经试过用插值法,结果把一些异常波动给平滑掉了,反而掩盖了真实的市场信号。

第二步:计算滚动相关系数

静态的相关系数只能告诉你「整体上」库存和基差有没有关系。但做交易的人都知道,这种关系是动态的。有时候正相关,有时候负相关,有时候根本不相关。

所以我更看重滚动相关系数。说白了,就是用一个固定窗口,比如20个交易日,不断计算这段时间内的相关系数,然后画成一条曲线。

# 计算滚动相关系数,窗口设为20天
window = 20
df['rolling_corr'] = df['inventory'].rolling(window=window).corr(df['basis'])

# 看看结果
print(df[['inventory', 'basis', 'rolling_corr']].tail(10))
关键点:窗口大小的选择很重要。窗口太小,噪声大;窗口太大,反应迟钝。我个人习惯先用20天(约一个自然月)试跑,再根据品种特性调整。比如螺纹钢这种季节性强的品种,我会用30天。

第三步:可视化——让关系一目了然

光看数字不够直观。我一般会画三张图:

  1. 库存与基差的走势对比图(双Y轴)
  2. 滚动相关系数的时序图
  3. 散点图 + 回归线

下面这段代码,我用了很多年,改过好几版,现在拿出来分享给你:

fig, axes = plt.subplots(3, 1, figsize=(12, 10))

# 图1:库存 vs 基差(双Y轴)
ax1 = axes[0]
color1 = 'tab:blue'
color2 = 'tab:red'

ax1.set_ylabel('库存', color=color1)
ax1.plot(df.index, df['inventory'], color=color1, label='库存')
ax1.tick_params(axis='y', labelcolor=color1)

ax2 = ax1.twinx()
ax2.set_ylabel('基差', color=color2)
ax2.plot(df.index, df['basis'], color=color2, label='基差', alpha=0.7)
ax2.tick_params(axis='y', labelcolor=color2)

ax1.set_title('库存与基差走势对比')

# 图2:滚动相关系数
axes[1].plot(df.index, df['rolling_corr'], color='green', linewidth=2)
axes[1].axhline(y=0, color='gray', linestyle='--')
axes[1].axhline(y=0.5, color='orange', linestyle='--', alpha=0.5)
axes[1].axhline(y=-0.5, color='orange', linestyle='--', alpha=0.5)
axes[1].set_title(f'滚动相关系数(窗口={window}天)')
axes[1].set_ylabel('相关系数')
axes[1].set_ylim([-1, 1])

# 图3:散点图 + 回归线
sns.regplot(x='inventory', y='basis', data=df, ax=axes[2],
            scatter_kws={'alpha':0.5}, line_kws={'color':'red'})
axes[2].set_title('库存 vs 基差 散点图')

plt.tight_layout()
plt.show()

这三张图放在一起,你能看到什么?

  • 第一张图告诉你:两个变量是不是同涨同跌?还是反向运动?
  • 第二张图告诉你:这种关系什么时候强,什么时候弱。
  • 第三张图告诉你:整体趋势是正相关还是负相关,有没有明显的异常点。
避坑指南:我曾经在分析铜的基差时,发现滚动相关系数突然从-0.8跳到了+0.6。一开始以为是数据错了,后来一查,原来是那个月发生了逼仓事件。所以,看到相关系数剧烈变化时,别急着下结论,先看看市场发生了什么。

第四步:输出分析报告

做量化分析,不能只给自己看。我习惯把关键指标整理成表格,方便团队讨论:

# 计算整体相关系数
overall_corr = df['inventory'].corr(df['basis'])

# 统计滚动相关系数的分布
corr_stats = df['rolling_corr'].describe()

# 找出相关系数最强的时段
strong_positive = df[df['rolling_corr'] > 0.7]
strong_negative = df[df['rolling_corr'] < -0.7]

print(f"整体相关系数: {overall_corr:.3f}")
print("\n滚动相关系数统计描述:")
print(corr_stats)
print(f"\n强正相关时段(r > 0.7): {len(strong_positive)} 个交易日")
print(f"强负相关时段(r < -0.7): {len(strong_negative)} 个交易日")

输出结果可以整理成这样的表格:

指标 数值
整体相关系数 -0.42
滚动相关系数均值 -0.38
滚动相关系数标准差 0.31
强正相关天数占比 8.2%
强负相关天数占比 22.5%

看到这个表格,你可能会问:整体相关系数只有-0.42,是不是说明库存和基差关系不大?

别急。你看滚动相关系数的标准差是0.31,说明这个关系波动很大。有时候强负相关(-0.7以下),有时候甚至变成正相关。这才是关键——关系不稳定,恰恰意味着有交易机会。当相关系数偏离均值太远时,往往预示着回归。

知识体系:库存-基差分析的核心逻辑

说了这么多,我画张图帮你理一理思路:

库存-基差相关性分析核心逻辑 数据输入 库存数据 + 基差数据 数据处理 缺失值处理 · 频率对齐 相关性分析 静态 + 滚动相关系数 可视化输出 走势图 · 相关系数图 · 散点图 交易决策 基差回归 · 库存拐点 · 套利机会

这张图把整个分析流程串起来了。从数据输入到最终决策,每一步都有它的意义。我个人觉得,最容易被忽视的是「数据处理」这一步。很多新手拿到数据就直接算相关系数,结果被缺失值和异常点带偏了方向。

实战中的几点体会

  • 不要迷信相关系数:0.8的相关系数看起来很漂亮,但如果样本量只有10个,那可能就是运气。我一般要求至少30个样本点才敢说「有关系」。
  • 注意滞后效应:库存变化往往领先于基差变化。你可以试试把库存数据向前平移几天,再算相关系数,有时候效果更好。
  • 分阶段分析:把数据按牛熊市分段,分别算相关系数。你会发现,牛市里库存和基差的关系,跟熊市里完全不一样。
进阶玩法:如果你熟悉机器学习,可以试试用LSTM预测基差,把库存作为特征之一。我去年在铜品种上试过,预测准确率比传统回归模型高了15%左右。不过那是另一个话题了,咱们以后有机会再聊。

好了,这一章的内容就到这里。代码你拿去跑一跑,换成你自己的数据,看看能发现什么规律。记住,工具是死的,市场是活的。再好的量化模型,也要结合你对基本面的理解来用。


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