4. 产业链价格传导机制:价格传导理论、相关性分析、Granger因果检验、时滞效应分析

各位同学好,今天我们聊一个非常实战的话题——价格传导机制。说白了,就是原油涨价了,下游的塑料、橡胶、化纤什么时候跟着涨?涨多少?谁先涨谁后涨?

我在做能化产业链量化分析的头两年,踩过不少坑。最典型的一次,我拿着原油和PTA的日度数据做回归,R²高达0.85,心里美滋滋。结果领导一问「那原油跌了3天,PTA为啥还涨?」我当场就懵了。后来才明白——价格传导不是同步的,有滞后,有方向,甚至有时候是反的。

所以这一章,我们就把价格传导这件事彻底讲透。从理论到代码,一步步来。

4.1 价格传导理论:为什么会有传导?

价格传导,本质上就是成本推动或需求拉动在产业链上下游之间的传递。你想想看,原油是化工品的源头,它一涨价,石脑油跟着涨,然后乙烯、丙烯、PX、PTA……一层层往下传。

但传导不是100%的。这里有三个核心因素:

  • 成本占比:上游原料在成本中的占比越高,传导越强。比如PTA,PX成本占80%以上,传导就很直接。
  • 市场结构:垄断程度高的环节,定价权强,传导快。完全竞争的市场,价格容易被下游压制。
  • 供需弹性:下游需求弹性大,上游涨价可能被「消化」掉,不会完全传导。

核心观点:价格传导不是机械的「上游涨1%,下游涨1%」。它受制于产业链的议价能力、库存周期、甚至政策干预。我们做量化分析,就是要量化这个「传导系数」和「时滞长度」。

我个人习惯把产业链分成三类传导模式:

  • 成本推动型:上游涨价→下游被迫跟涨。典型如原油→燃料油、沥青。
  • 需求拉动型:下游需求旺盛→上游供不应求→价格上行。比如2021年新能源爆发带动锂电材料。
  • 预期驱动型:期货市场交易预期,价格先于现货变动。这个在能化品种上特别明显。

4.2 相关性分析:先看看有没有关系

做传导分析,第一步永远是相关性分析。别一上来就Granger因果检验,先看看两个序列是不是「有关系」。

我常用的方法有三种:

  1. 皮尔逊相关系数:衡量线性相关。注意,它要求数据平稳,否则容易出伪相关。
  2. 斯皮尔曼秩相关系数:不要求正态分布,对异常值不敏感。我一般先用这个做快速筛查。
  3. 滚动相关系数:看相关性随时间的变化。这个在实战中特别有用——你会发现,有些品种平时不相关,一到极端行情就高度相关。

来,看一段代码。我们用Python计算原油和PTA的滚动相关系数:

import pandas as pd
import numpy as np
import yfinance as yf

# 获取数据
oil = yf.download('CL=F', start='2020-01-01', end='2024-01-01')['Close']
pta = yf.download('TA=F', start='2020-01-01', end='2024-01-01')['Close']

# 合并并计算日收益率
df = pd.DataFrame({'oil': oil, 'pta': pta}).dropna()
returns = df.pct_change().dropna()

# 滚动相关系数(60天窗口)
rolling_corr = returns['oil'].rolling(60).corr(returns['pta'])

print(f"全样本相关系数: {returns['oil'].corr(returns['pta']):.3f}")
print(f"滚动相关系数均值: {rolling_corr.mean():.3f}")
print(f"滚动相关系数标准差: {rolling_corr.std():.3f}")

实战技巧:滚动窗口选多少?我个人习惯:日度数据用60天(约一个季度),周度数据用52周(一年)。窗口太短噪声大,太长又反应迟钝。你可以多试几个窗口,看看稳定性。

嗯,这里要注意——相关性高不代表有传导关系。我见过有人拿原油和比特币的相关性做套利策略,结果亏得很惨。所以下一步,我们要做因果检验。

4.3 Granger因果检验:谁是谁的「因」?

Granger因果检验,说白了就是看「X的过去值能不能帮助预测Y的当前值」。如果能,我们就说X是Y的Granger原因。

注意,这不是哲学意义上的因果关系。它只是统计上的「预测能力」。我在项目中遇到过好几次,检验结果显示「PTA是原油的Granger原因」,乍一看不合理,但仔细分析——那是因为PTA期货市场对原油需求的预期提前反应了。

做Granger检验有几个前提:

  • 序列必须平稳(非平稳要差分)
  • 滞后阶数要选对(常用AIC或BIC准则)
  • 两个序列要同频(日度对日度,周度对周度)

代码实现如下:

from statsmodels.tsa.stattools import grangercausalitytests
import warnings
warnings.filterwarnings('ignore')

# 确保数据平稳(这里用收益率,一般已平稳)
data = returns[['oil', 'pta']].dropna()

# Granger因果检验,最大滞后5期
max_lag = 5
test_result = grangercausalitytests(data[['pta', 'oil']], max_lag, verbose=False)

print("Granger因果检验结果(PTA → 原油):")
for lag in range(1, max_lag+1):
    p_value = test_result[lag][0]['ssr_chi2test'][1]
    print(f"  滞后{lag}期: p值 = {p_value:.4f}", end="")
    if p_value < 0.05:
        print(" ✅ 显著")
    else:
        print(" ❌ 不显著")

# 反向检验:原油 → PTA
test_result_rev = grangercausalitytests(data[['oil', 'pta']], max_lag, verbose=False)
print("\nGranger因果检验结果(原油 → PTA):")
for lag in range(1, max_lag+1):
    p_value = test_result_rev[lag][0]['ssr_chi2test'][1]
    print(f"  滞后{lag}期: p值 = {p_value:.4f}", end="")
    if p_value < 0.05:
        print(" ✅ 显著")
    else:
        print(" ❌ 不显著")

避坑指南:我曾经犯过一个错误——用日度数据做Granger检验,结果发现几乎所有品种都互相因果。后来才意识到,日度数据噪声太大,而且存在微观结构效应。建议至少用周度数据,或者对日度数据做移动平均平滑处理。

4.4 时滞效应分析:传导到底要多久?

这是最实战的部分。知道了谁传导谁,还得知道传导需要多长时间。时滞分析,我常用两种方法:

4.4.1 互相关函数(CCF)

互相关函数可以直观地看到两个序列在不同滞后下的相关性。峰值对应的滞后,就是最可能的传导时滞。

import matplotlib.pyplot as plt
from statsmodels.tsa.stattools import ccf

# 计算互相关
ccf_values = ccf(returns['oil'], returns['pta'], adjusted=False)

# 只看前20期滞后
lags = range(0, 21)
plt.figure(figsize=(10, 5))
plt.bar(lags, ccf_values[:21], width=0.6)
plt.axhline(y=0, color='gray', linestyle='--')
plt.axhline(y=1.96/np.sqrt(len(returns)), color='red', linestyle='--', label='95%置信区间')
plt.axhline(y=-1.96/np.sqrt(len(returns)), color='red', linestyle='--')
plt.xlabel('滞后阶数(天)')
plt.ylabel('互相关值')
plt.title('原油与PTA收益率的互相关函数')
plt.legend()
plt.show()

# 找出最大相关对应的滞后
max_lag_ccf = np.argmax(np.abs(ccf_values[:21]))
print(f"最大互相关出现在滞后 {max_lag_ccf} 期,值为 {ccf_values[max_lag_ccf]:.3f}")

4.4.2 分布滞后模型(DLM)

这个方法更精确。我们把上游价格的多个滞后项作为自变量,回归下游价格,看哪些滞后项显著。

import statsmodels.api as sm

# 构造滞后变量
def create_lagged_data(y, x, max_lag):
    df = pd.DataFrame({'y': y, 'x': x})
    for lag in range(1, max_lag+1):
        df[f'x_lag{lag}'] = df['x'].shift(lag)
    return df.dropna()

# 最大滞后10天
lag_data = create_lagged_data(returns['pta'], returns['oil'], max_lag=10)

# OLS回归
X = lag_data.drop('y', axis=1)
X = sm.add_constant(X)
y = lag_data['y']
model = sm.OLS(y, X).fit()

print(model.summary())

# 找出显著的滞后项
significant_lags = []
for lag in range(1, 11):
    coef = model.params[f'x_lag{lag}']
    pval = model.pvalues[f'x_lag{lag}']
    if pval < 0.05:
        significant_lags.append((lag, coef, pval))
        print(f"滞后{lag}期: 系数={coef:.4f}, p值={pval:.4f} ✅")
    else:
        print(f"滞后{lag}期: 系数={coef:.4f}, p值={pval:.4f} ❌")

实战经验:我在做甲醇-聚丙烯传导分析时,发现互相关显示滞后3天最强,但分布滞后模型显示滞后1天和5天都显著。后来跟产业客户聊才知道——现货市场是3天定价周期,但期货市场提前1天交易预期,而5天是物流运输的时间。你看,量化结果和产业逻辑对上了,这才叫靠谱。

4.5 知识体系总览

下面这张图,是我自己梳理的价格传导分析框架。每次做新项目,我都会先对照这张图,看看自己走到了哪一步。

产业链价格传导机制分析框架 第一步:数据准备 价格序列采集、频率对齐 第二步:相关性分析 皮尔逊/斯皮尔曼/滚动相关 第三步:Granger因果检验 确定传导方向 第四步:时滞效应分析 互相关函数 / 分布滞后模型 常用方法工具箱 • 皮尔逊相关系数(线性相关) • 斯皮尔曼秩相关(稳健) • 滚动相关系数(时变相关) • ADF平稳性检验(前置条件) 实战避坑指南 • 伪相关:非平稳数据要差分 • 滞后阶数:AIC/BIC准则选择 • 频率匹配:日度vs周度差异大 • 极端行情:相关性会突变 最终输出:传导系数 + 时滞长度 + 置信区间 用于套利策略、库存管理、风险对冲

这张图把整个分析流程串起来了。从数据准备开始,到相关性分析做初步筛查,再到Granger因果检验确定方向,最后用互相关和分布滞后模型量化时滞。每一步都有对应的统计方法和Python实现。

我个人建议,在实际项目中不要跳过任何一步。哪怕你觉得「这俩品种肯定有关系」,也请用数据说话。我见过太多拍脑袋的结论,最后被Granger检验打脸。

好了,这一章的内容就到这里。价格传导是产业链量化分析的基石,掌握了它,你就能理解为什么原油涨了3天,塑料才跟着动;为什么有时候上游涨价,下游反而跌——那是因为需求太差,传导链条断了。

把这些方法用到你的数据上,你会发现很多有意思的规律。嗯,祝你好运。


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