在 30 天的时间内 PANDAS 滚动产品 window

Rolling Product in PANDAS over 30-day time window

我正在尝试为金融事件分析准备数据,并想计算买入并持有的异常值 return (BHAR)。对于测试数据集,我有三个事件(用 event_id 表示),对于每个事件,我有 272 行,从 t-252 天到 t+20 天(由变量 time 记录)。对于每一天,我还有股票的 return 数据 (ret) 以及预期的 return (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天的买入并持有异常值return,我必须计算(1+ret_t =0)x(1+ret_t=1)...x(1+ret_t=10), 然后对预期的 return, (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 方程的左侧,因为它将添加正确的乘积——尽管它将输入向下两行的值(这可以通过移动来解决)。但是,一个问题是数据框中存在三个不同的 'groups'(3 个事件),如果 window 向前推进超过 30 天,它可能会开始使用下一个事件的产品。我试图用 rolling_apply 实现 groupby 但不断出现错误: TypeError: 'Series'对象是可变的,因此它们不能被散列

df.groupby('event_id').apply(pd.rolling_apply(df['ret'], 10, lambda x : (1+x).prod()))

我确信我在这里遗漏了一些基本的东西,因此我们将不胜感激。我可能只需要从不同的角度来处理它。这是一个想法:最后,我最感兴趣的是从 time=0 开始获取 30 天和 60 天买入并持有异常 returns。所以,也许更容易 select 每个时间为 0 的事件,然后计算未来 30 天的产品?我不确定如何才能最好地解决这个问题。

提前感谢您的任何见解。

编辑后 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() 将循环折叠成一行,但我喜欢这种方式。更短的阅读行 = 更好的可读性。

# 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

这是我所做的:

((df+1.0) \
  .apply(lambda x: np.log(x),axis=1)\
  .rolling(365).sum() \
  .apply(lambda x: np.exp(x),axis=1)-1.0)

结果是滚动产品。