PANDAS的滚动产品超过30天的时间窗口

时间:2015-10-31 18:35:25

标签: python pandas finance

我正在尝试为财务事件分析准备数据,并希望计算买入并持有异常收益(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 公式:

enter image description here

所以,使用上面的公式作为例子,如果我想计算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天的产品?我不确定我怎么能最好地接近它。

提前感谢任何见解。

3 个答案:

答案 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)

结果是滚动产品。