【问题标题】:Historical Volatility from Prices of many different bonds in same column同一列中许多不同债券价格的历史波动率
【发布时间】:2021-08-19 18:44:36
【问题描述】:

我有一个 csv 文件,其中包含过去 1 年的许多债券的买入/卖出价格(使用 ISIN 标识符)。使用这些历史价格,我试图计算每只债券的历史波动率。尽管这通常应该是一件容易的事,但问题在于并非所有债券都具有完全相同的交易价格数据天数,而它们都在同一列中并且没有堆叠。因此,如果我需要计算滚动标准偏差,我不能选择 1 年 252 天的标准滚动窗口。

数据集有这种格式-

BusinessDate ISIN Bid Ask
Date 1 ISIN1 P1 P2
Date 2 ISIN1 P1 P2
Date 252 ISIN1 P1 P2
Date 1 ISIN2 P1 P2
Date 2 ISIN2 P1 P2

......

等等。

我目前的代码如下-

vol_df = pd.read_csv('hist_prices.csv')
vol_df['BusinessDate'] = pd.to_datetime(vol_df['BusinessDate'])
vol_df[Mid Price'] = vol_df[['Bid', 'Ask']].mean(axis = 1)
vol_df['log_return'] = vol_df.groupby('ISIN')['Mid Price'].apply(lambda x: np.log(x) - np.log(x.shift(1)))
vol_df['hist_vol'] = vol_df['log_return'].std() * np.sqrt(252)

最后一行代码似乎给出了列中的所有 NaN 值。这很可能是因为计算标准偏差的操作发生在同一行号上,而不是针对数字列表。我尝试替换最后一行以使用 rolling_std-

vol_df.set_index('BusinessDate').groupby('ISIN').rolling(window = 1, freq = 'A').std()['log_return']

但这也无济于事。它为每个 ISIN 提供 2 个数字。我还尝试使用 pivot() 将 ISIN 放在列中,将 BusinessDate 作为索引,将价格作为“值”。但它给出了一个错误。此外,我有近 9,000 个不同的 ISIN,因此将它们放在列中以计算每列的 std() 可能不是最好的方法。关于如何解决这个问题的任何线索?

【问题讨论】:

    标签: python pandas dataframe volatility stdev


    【解决方案1】:

    我能够以粗略的方式解决这个问题-

    vol_df_2 = vol_df.groupby('ISIN')['logret'].std()
    vol_df_3 = vol_df_2.to_frame()
    vol_df_3.rename(columns = {'logret':'daily_std}, inplace = True)
    

    上面的第一行是返回一个系列和名为“logret”的标准偏差列。因此,第 2 和第 3 行代码将其转换为数据框并重命名每日标准偏差。最后,可以使用 sqrt(252) 计算年度成交量。

    如果有人有更好的方法在同一个数据帧中而不是创建一个系列,那就太好了。

    【讨论】:

      【解决方案2】:

      好的,现在几乎可以使用了。

      每个 ISIN 确实需要一些数学运算来计算滚动期,我在示例中只使用了 3 和 2,您可能需要计算一年中的交易天数或其他什么,并以某种方式将其修复为每个 ISIN .

      然后您需要弄清楚如何将数据合并回来。输出实际上有错误,因为它更新了副本,但这正是我在这里寻找的。我相信知道更多的人可以在这一点上解决它。我无法让它进行合并。

      toy_data={'BusinessDate': ['10/5/2020','10/6/2020','10/7/2020','10/8/2020','10/9/2020',
                                 '10/12/2020','10/13/2020','10/14/2020','10/15/2020','10/16/2020',
                                 '10/5/2020','10/6/2020','10/7/2020','10/8/2020'],
                'ISIN': [1,1,1,1,1, 1,1,1,1,1, 2,2,2,2],
                'Bid': [0.295,0.295,0.295,0.295,0.295,
                        0.296, 0.296,0.297,0.298,0.3,
                        2.5,2.6,2.71,2.8],
                'Ask': [0.301,0.305,0.306,0.307,0.308,
                        0.315,0.326,0.337,0.348,0.37,
                        2.8,2.7,2.77,2.82]}
      #vol_df = pd.read_csv('hist_prices.csv')
      vol_df = pd.DataFrame(toy_data)
      
      vol_df['BusinessDate'] = pd.to_datetime(vol_df['BusinessDate'])
      vol_df['Mid Price'] = vol_df[['Bid', 'Ask']].mean(axis = 1)
      vol_df['log_return'] = vol_df.groupby('ISIN')['Mid Price'].apply(lambda x: np.log(x) - np.log(x.shift(1)))
      vol_df.dropna(subset = ['log_return'], inplace=True)
      # do some math here to calculate how many days you want to roll for an ISIN
      # maybe count how many days over a 1 year period exist???
      # not really sure how you'd miss days unless stuff just doesnt trade
      # (but I don't need to understand it anyway)
      rolling = {1: 3, 2: 2}
      for isin in vol_df['ISIN'].unique():
          roll = rolling[isin]
          print(f'isin={isin}, roll={roll}')
          df_single = vol_df[vol_df['ISIN']==isin]
          df_single['rolling'] = df_single['log_return'].rolling(roll).std()
          # i can't get the right syntax to merge data back, but this shows it
          vol_df[isin, 'rolling'] = df_single['rolling']
          print(df_single)
      print(vol_df)
      

      哪个输出(减去警告错误):

      isin=1, roll=3
        BusinessDate  ISIN    Bid    Ask  Mid Price  log_return   rolling
      1   2020-10-06     1  0.295  0.305     0.3000    0.006689       NaN
      2   2020-10-07     1  0.295  0.306     0.3005    0.001665       NaN
      3   2020-10-08     1  0.295  0.307     0.3010    0.001663  0.002901
      4   2020-10-09     1  0.295  0.308     0.3015    0.001660  0.000003
      5   2020-10-12     1  0.296  0.315     0.3055    0.013180  0.006650
      6   2020-10-13     1  0.296  0.326     0.3110    0.017843  0.008330
      7   2020-10-14     1  0.297  0.337     0.3170    0.019109  0.003123
      8   2020-10-15     1  0.298  0.348     0.3230    0.018751  0.000652
      9   2020-10-16     1  0.300  0.370     0.3350    0.036478  0.010133
      isin=2, roll=2
         BusinessDate  ISIN   Bid  ...    log_return  (1, rolling)   rolling
      11   2020-10-06     2  2.60  ...  2.220446e-16           NaN       NaN
      12   2020-10-07     2  2.71  ...  3.339828e-02           NaN  0.023616
      13   2020-10-08     2  2.80  ...  2.522656e-02           NaN  0.005778
      
      [3 rows x 8 columns]
         BusinessDate  ISIN    Bid  ...    log_return  (1, rolling)  (2, rolling)
      1    2020-10-06     1  0.295  ...  6.688988e-03           NaN           NaN
      2    2020-10-07     1  0.295  ...  1.665279e-03           NaN           NaN
      3    2020-10-08     1  0.295  ...  1.662511e-03      0.002901           NaN
      4    2020-10-09     1  0.295  ...  1.659751e-03      0.000003           NaN
      5    2020-10-12     1  0.296  ...  1.317976e-02      0.006650           NaN
      6    2020-10-13     1  0.296  ...  1.784313e-02      0.008330           NaN
      7    2020-10-14     1  0.297  ...  1.910886e-02      0.003123           NaN
      8    2020-10-15     1  0.298  ...  1.875055e-02      0.000652           NaN
      9    2020-10-16     1  0.300  ...  3.647821e-02      0.010133           NaN
      11   2020-10-06     2  2.600  ...  2.220446e-16           NaN           NaN
      12   2020-10-07     2  2.710  ...  3.339828e-02           NaN      0.023616
      13   2020-10-08     2  2.800  ...  2.522656e-02           NaN      0.005778
      

      【讨论】:

      • 谢谢布赖恩。实际上,我尝试了一个非常小的子集并删除了 NaN 行,然后我没有得到波动率的“NaN”值。但问题也正如您在示例中看到的那样 - 所有 ISIN 的历史波动率都是相同的。不同的 ISIN 应该是不同的(除非不同债券在此期间的回报相同是一个巨大的巧合)
      • 我确实在最终波动率计算中使用了 groupby('ISIN'),这样我就不会得到所有 ISIN 的相同数字,并且它在所有行中都给出了“NaN”
      • 它给出了 nan,因为每个组(第一个条目)都有一个 nan,这破坏了这一切。我将修改答案以显示我的意思。如果您希望我运行它以获得实际输出,请随意添加更好的示例。
      • 不,我已经从日志返回列中删除了 NaN,就像我之前提到的那样。这不是原因。当您执行 df['column name'].std() 时,它会为您提供整列的标准偏差。但是,需要的是标识符列中的每个条目。如果每个标识符的天数相同,那么 rolling_std() 会起作用,您可以提及 window = 'number of days'。但是,就我而言,每个人都有不同的号码。天。我的数据在我公司的笔记本电脑中,我无法将其粘贴到此处。我可以添加一个示例,但它与您已有的没有什么不同
      • 我想我现在理解得更好了,希望这次编辑更符合您的要求。虽然我打赌你可以,但我无法让合并工作。
      猜你喜欢
      • 1970-01-01
      • 2021-02-24
      • 2021-01-01
      • 1970-01-01
      • 2013-02-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多