4. 产业链价格传导机制:价格传导理论、相关性分析、Granger因果检验、时滞效应分析
各位同学好,今天我们聊一个非常实战的话题——价格传导机制。说白了,就是原油涨价了,下游的塑料、橡胶、化纤什么时候跟着涨?涨多少?谁先涨谁后涨?
我在做能化产业链量化分析的头两年,踩过不少坑。最典型的一次,我拿着原油和PTA的日度数据做回归,R²高达0.85,心里美滋滋。结果领导一问「那原油跌了3天,PTA为啥还涨?」我当场就懵了。后来才明白——价格传导不是同步的,有滞后,有方向,甚至有时候是反的。
所以这一章,我们就把价格传导这件事彻底讲透。从理论到代码,一步步来。
4.1 价格传导理论:为什么会有传导?
价格传导,本质上就是成本推动或需求拉动在产业链上下游之间的传递。你想想看,原油是化工品的源头,它一涨价,石脑油跟着涨,然后乙烯、丙烯、PX、PTA……一层层往下传。
但传导不是100%的。这里有三个核心因素:
- 成本占比:上游原料在成本中的占比越高,传导越强。比如PTA,PX成本占80%以上,传导就很直接。
- 市场结构:垄断程度高的环节,定价权强,传导快。完全竞争的市场,价格容易被下游压制。
- 供需弹性:下游需求弹性大,上游涨价可能被「消化」掉,不会完全传导。
核心观点:价格传导不是机械的「上游涨1%,下游涨1%」。它受制于产业链的议价能力、库存周期、甚至政策干预。我们做量化分析,就是要量化这个「传导系数」和「时滞长度」。
我个人习惯把产业链分成三类传导模式:
- 成本推动型:上游涨价→下游被迫跟涨。典型如原油→燃料油、沥青。
- 需求拉动型:下游需求旺盛→上游供不应求→价格上行。比如2021年新能源爆发带动锂电材料。
- 预期驱动型:期货市场交易预期,价格先于现货变动。这个在能化品种上特别明显。
4.2 相关性分析:先看看有没有关系
做传导分析,第一步永远是相关性分析。别一上来就Granger因果检验,先看看两个序列是不是「有关系」。
我常用的方法有三种:
- 皮尔逊相关系数:衡量线性相关。注意,它要求数据平稳,否则容易出伪相关。
- 斯皮尔曼秩相关系数:不要求正态分布,对异常值不敏感。我一般先用这个做快速筛查。
- 滚动相关系数:看相关性随时间的变化。这个在实战中特别有用——你会发现,有些品种平时不相关,一到极端行情就高度相关。
来,看一段代码。我们用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因果检验确定方向,最后用互相关和分布滞后模型量化时滞。每一步都有对应的统计方法和Python实现。
我个人建议,在实际项目中不要跳过任何一步。哪怕你觉得「这俩品种肯定有关系」,也请用数据说话。我见过太多拍脑袋的结论,最后被Granger检验打脸。
好了,这一章的内容就到这里。价格传导是产业链量化分析的基石,掌握了它,你就能理解为什么原油涨了3天,塑料才跟着动;为什么有时候上游涨价,下游反而跌——那是因为需求太差,传导链条断了。
把这些方法用到你的数据上,你会发现很多有意思的规律。嗯,祝你好运。
公众号:蓝海资料掘金营,微信deep3321