我正在尝试为财务事件分析准备数据,并希望计算买入并持有异常收益(BHAR)。对于测试数据集,我有三个事件(由 event_id 表示),对于每个事件,我有272行,从t-252天到t + 20天(由变量表示)时间)。对于每一天,我还有股票的回报数据( ret )以及预期回报( Exp_Ret ),这是使用市场模型计算的。这是一个数据样本:
index event_id time ret vwretd Exp_Ret
0 0 -252 0.02905 0.02498 nan
1 0 -251 0.01146 -0.00191 nan
2 0 -250 0.01553 0.00562 nan
...
250 0 -2 -0.00378 0.00028 -0.00027
251 0 -1 0.01329 0.00426 0.00479
252 0 0 -0.01723 -0.00875 -0.01173
271 0 19 0.01335 0.01150 0.01398
272 0 20 0.00722 -0.00579 -0.00797
273 1 -252 0.01687 0.00928 nan
274 1 -251 -0.00615 -0.01103 nan
这就是问题所在。我想计算每天的以下 BHAR 公式:
所以,使用上面的公式作为例子,如果我想计算10天的买入和持有异常收益,我将不得不计算(1 + ret_t = 0)x(1 + ret_t = 1 )... x(1 + ret_t = 10),然后对预期的返回进行相同的操作,(1 + Exp_Ret_t = 0)x(1 + Exp_Ret_t = 1)... x(1 + Exp_Ret_t = 10),然后从后者中减去后者。
我使用 rolling_apply 取得了一些进展,但它并没有解决我的所有问题:
df['part1'] = pd.rolling_apply(df['ret'], 10, lambda x : (1+x).prod())
这似乎正确地实现了BHAR方程的左侧,因为它将添加正确的乘积 - 尽管它将输入两行向下的值(可以通过移位来解决)。但是,一个问题是,有三个不同的小组'在数据框中(3个事件),如果窗口超过30天,它可能会开始使用下一个事件的产品。我尝试使用 rolling_apply 实现 groupby ,但一直收到错误: TypeError:' Series'对象是可变的,因此它们不能被散列
df.groupby('event_id').apply(pd.rolling_apply(df['ret'], 10, lambda x : (1+x).prod()))
我确信我遗漏了一些基本的东西,所以任何帮助都会受到赞赏。我可能只需要从不同的角度来看待它。这里有一个想法:最后,我最感兴趣的是从时间= 0开始获得30天和60天的买入和持有异常回报。那么,也许更容易在时间= 0选择每个事件,然后计算未来30天的产品?我不确定我怎么能最好地接近它。
提前感谢任何见解。
答案 0 :(得分:1)
编辑以便BHAR的最终值包含在主DataFrame中。
BHAR = pd.Series()
def bhar(arr):
return np.cumprod(arr+1)[-1]
grouped = df.groupby('event_id')
for name, group in grouped:
BHAR = BHAR.append(pd.rolling_apply(group['ret'],10,bhar) -
pd.rolling_apply(group['Exp_Ret'],10,bhar))
df['BHAR'] = BHAR
然后,您可以使用df[df['time']>=0]
对DataFrame进行切片,这样您就只能获得所需的部分。
你可以使用组中的.apply()
显然在一行中折叠循环,但我喜欢这样。读取的行更短=可读性更好。
答案 1 :(得分:1)
# Create sample data.
np.random.seed(0)
VOL = .3
df = pd.DataFrame({'event_id': [0] * 273 + [1] * 273 + [2] * 273,
'time': range(-252, 21) * 3,
'ret': np.random.randn(273 * 3) * VOL / 252 ** .5,
'Exp_Ret': np.random.randn(273 * 3) * VOL / 252 ** .5})
# Pivot on time and event_id.
df = df.set_index(['time', 'event_id']).unstack('event_id')
# Calculated return difference from t=0.
df_diff = df.ix[df.index >= 0, 'ret'] - df.loc[df.index >= 0, 'Exp_Ret']
# Calculate cumulative abnormal returns.
cum_returns = (1 + df_diff).cumprod() - 1
# Get 10 day abnormal returns.
>>> cum_returns.loc[10]
event_id
0 -0.014167
1 -0.172599
2 -0.032647
Name: 10, dtype: float64
答案 2 :(得分:0)
这就是我所做的:
((df+1.0) \
.apply(lambda x: np.log(x),axis=1)\
.rolling(365).sum() \
.apply(lambda x: np.exp(x),axis=1)-1.0)
结果是滚动产品。