【手把手教你】使用DoWhy做因果推断分析
引言
因果推断是一种统计和哲学框架,旨在从数据中识别变量之间的因果关系,而不仅仅是相关性或关联。换句话说,它试图解答“如果我做X,会发生什么?”这类问题,而非仅仅描述“X与Y有关联”这种观察性的信息。
在金融量化中,因果推断的应用相当重要。金融市场是一个高度复杂、多变量互动的系统。在这样的环境中,仅依赖相关性往往是不足够的,因为相关性并不意味着因果性。例如,股票价格上升可能与多个因素相关,如交易量增加、宏观经济指标改善或公司业绩增长。然而,并不是所有这些因素都是导致股票价格上升的真正“原因”。
通过因果推断,量化分析师、投资者和决策者可以更精准地识别哪些因素会影响股票价格、市场走势或其他金融指标。这不仅有助于更有效地管理风险,还可以用于创建更为强健的交易策略。
【号外】qstock已更新到1.3.6版本!修正了问财不能调用的问题,请使用“pip install qstock ”进行安装,或通过’pip install –upgrade qstock’进行更新。
qstock系列专题:
DoWhy库简介
DoWhy是一个用于因果推断(Causal Inference)的Python库。该库由微软研究院(Microsoft Research)开发,旨在简化从数据中识别因果关系的过程DoWhy提供了一整套统一和一致的API,支持从建模到识别,再到因果效应估计和验证的整个工作流程。
主要特点
模块化和可扩展性: DoWhy将因果推断流程分解为四个主要步骤:模型建立、识别、估计和验证。这使得用户可以轻松更换不同的估计或验证方法。
基于图模型: DoWhy使用因果图(Causal Graph)作为基础模型,帮助用户更直观地理解变量之间的关系,并且可以明确地指定共同因素(Common Causes)和介入变量(Interventions)。
多种因果估计方法: DoWhy支持多种因果估计算法,包括反事实模型(Counterfactual Models)、工具变量法(Instrumental Variables)、传播分数匹配(Propensity Score Matching)等。
诊断和验证: 除了因果效应的点估计外,DoWhy还提供了用于检验模型健壮性和有效性的诊断工具。
使用DoWhy通常涉及以下几个步骤:
数据准备: 获取和预处理你需要分析的数据。
建立因果模型: 利用因果图明确指定处理(Treatment)变量、结果(Outcome)变量和可能的共同因素(Common Causes)。
识别因果效应: 基于你的因果模型,DoWhy将尝试识别一个合适的估计方法。
估计因果效应: 使用识别的方法来估计处理变量对结果变量的因果效应。
验证和诊断: 评估估计的因果效应的稳健性和可靠性。
应用示例
下面以跳空高开(Gap Up)是否对一个股票指数(如沪深300指数)的日收益率有因果影响,使用qstock库获取数据和DoWhy库进行因果推断。为了避免反向因果的影响,共同因素变量均滞后一期。
import qstock as qs
import pandas as pd
import numpy as np
import pandas_ta as ta
import matplotlib.pyplot as plt
import dowhy
from dowhy import CausalModel
#获取沪深300指数数据
df=qs.get_data('hs300',start='20130901',end='20230901')
# 计算日收益率和跳空高开(Gap Up)
df['daily_return'] = df['close'].pct_change() * 100 # 日收益率
df['gap_up'] = (df['open'] - df['close'].shift(1)) > 0 # 跳空高开
# 特征提取
df['mean_lagged_return'] = df['daily_return'].rolling(5).mean().shift(1)
df['var_lagged_return'] = df['daily_return'].rolling(5).var().shift(1)
df['volume%'] = df['volume'].pct_change()
df['ma_20']=df.close.rolling(20).mean()
df['ma_close']=df['ma_20'].shift(1)/df.close.shift(1)-1
df['macd'] = df.ta.macd().iloc[:,0].shift(1)
df['rsi'] = df.ta.rsi().shift(1)
# 删除NaN
df.dropna(inplace=True)
画图
qs.line(df['daily_return'])
qs.box(x='gap_up', y='daily_return', data=df)
箱线图显示,gap_up = 1的组的中位数明显高于gap_up = 0的组,且几乎没有重叠,这意味着“跳空高开”对daily_return有显著正面影响。此外,两组数据均有较多异常值点,可能是由于股票收益率本身可能具有很高的波动性,导致大量异常值;也可能暗示着股票收益率受到多个因素的影响,而这些因素在模型中没有被完全考虑。
# 定义共同因素和因果模型
covariates = ['mean_lagged_return', 'var_lagged_return','volume%','ma_close', 'macd', 'rsi']
model = CausalModel(
data=df,
treatment='gap_up',
outcome='daily_return',
common_causes=covariates
)
# 识别和估计因果效应
identified_estimand = model.identify_effect()
estimate = model.estimate_effect(
identified_estimand,
method_name="backdoor.linear_regression",
test_significance=True
)
print(estimate)
*** Causal Estimate ***
## Identified estimand
Estimand type: EstimandType.NONPARAMETRIC_ATE
### Estimand : 1
Estimand name: backdoor
Estimand expression:
d
────────(E[daily_return|mean_lagged_return,macd,var_lagged_return,volume%,ma_c
d[gapᵤₚ]
lose,rsi])
Estimand assumption 1, Unconfoundedness: If U→{gap_up} and U→daily_return then P(daily_return|gap_up,mean_lagged_return,macd,var_lagged_return,volume%,ma_close,rsi,U) = P(daily_return|gap_up,mean_lagged_return,macd,var_lagged_return,volume%,ma_close,rsi)
## Realized estimand
b: daily_return~gap_up+mean_lagged_return+macd+var_lagged_return+volume%+ma_close+rsi
Target units: ate
## Estimate
Mean value: 0.48177222629125094
p-value: [5.90117295e-24]
这个结果提供了一个因果推断的全面分析,在本例中是关于一个金融变量(“日收益率”)如何受到其他变量(“跳空高开”和一系列技术指标)的影响。下面逐一解读结果的各个部分:
Identified estimand结果:
Estimand type: EstimandType.NONPARAMETRIC_ATE:这表示所使用的是非参数平均处理效应(ATE)的估计。非参数意味着模型没有对数据分布做特定假设。
Estimand : 1 (Backdoor):使用了后门(Backdoor)准则来识别因果效应。
Estimand Expression: 这是一个数学表达式,描述了感兴趣的因果问题。在这个情况下,如果想要知道gap_up(跳空高开)对daily_return(日收益率)的影响,同时控制多个共同因素。
Estimand Assumption 1, Unconfoundedness: 这是一个关键假设,即所有的共同因素(即混杂变量)都已经考虑在内。简单地说,这意味着在控制了所有列出的共同因素后,gap_up和daily_return之间的关系应该是无偏的。
Realized Estimand结果:
给出了具体用于估计因果效应的模型表达式,它包括了所有的共同因素。
Estimate结果:
Mean value: 0.48177222629125094:这是估计的平均处理效应(ATE)。这个数值表示当gap_up从0变为1时,daily_return平均会增加大约0.482。这是一个相当大的效应,特别是在金融市场这样的高度噪音环境中。
p-value: 5.90117295e-24: p值接近于零意味着结果是统计上高度显著的,即表明gap_up统计上显著影响daily_return。
综合上述分析,这个因果效应估计表明gap_up(跳空高开)对daily_return(日收益率)有正向的影响,且这个影响在统计上是非常显著的。然而,这些结果应当在经过更多的稳健性检验和模型验证之后,才能得出最终的结论。这包括使用不同的数据集或时间段进行验证,以及考虑可能的模型偏见和潜在的未观察到的混杂变量。下面再做一个Placebo测试(安慰剂检验)
# Placebo测试
placebo_test = model.refute_estimate(identified_estimand, estimate, method_name="placebo_treatment_refuter", placebo_type="permute")
print(placebo_test)
Refute: Use a Placebo Treatment
Estimated effect:0.48177222629125094
New effect:-0.002086351760118241
p value:0.98
Placebo测试是一种稳健性检验,用于验证模型的可靠性。在这个测试中,处理(treatment)变量被随机排列(permute)或用无关的数据替换,然后再次进行因果效应的估计。理论上,如果你的模型是可靠的,那么用一个无关的“安慰剂(placebo)”作为处理变量不应该产生显著的因果效应。
从估计结果来看,Estimated effect(0.48177)是原始数据上计算出的估计因果效应,而New effect(-0.00208)是在随机排列处理变量后得到的新因果效应。这个值接近于0,表明当处理变量被随机化时,预期的因果效应几乎消失。p-value为0.98,意味着新效应是不显著的,表明原始的因果效应估计是可靠的。
这个例子用跳空高开作为处理(treatment)变量,并用股票的日收益率作为结果(outcome)变量。请注意,这只是一个非常简单的例子,只是考虑了可能影响股票收益率的技术面因素(如交易量、RSI和MACD技术指标)。在实际应用中,建议进行更为全面和细致的分析。这种类型的因果推断通常需要更多的数据和更复杂的模型,以及可能还需要领域专家的意见。
在金融领域内,因果推断是一个非常活跃的研究方向,涉及到的模型和方法也相当复杂和多样。所以,这个简单的示例仅供入门和学习使用。
结语
DoWhy是一个强大的Python库,专为因果推断设计。它提供了一种系统化的方法来进行因果分析,从提出因果问题、识别因果估计量、估计因果效应,到检验因果效应。DoWhy的优点在于其强调因果关系的可解释性和健壮性,而不仅仅是预测。通过一系列内置的因果推断方法和多种推论测试(如Placebo测试、Bootstrap测试等),DoWhy使得用户能够更有信心地解读和验证因果效应。
然而,正如任何因果推断方法一样,DoWhy也有其局限性。比如,如果共因变量没有正确地被观察或者测量,那么得出的结论可能是有偏的。此外,虽然库提供了多种工具来测试模型稳健性,但最终结果的可信度还取决于因果模型的合理性和数据质量。
关于Python金融量化
专注于分享Python在金融量化领域的应用。加入知识星球,可以免费获取qstock源代码、30多g的量化投资视频资料、量化金融相关PDF资料、公众号文章Python完整源码、与博主直接交流、答疑解惑等。添加个人微信sky2blue2可获取八五折优惠。