pandas 使用每日时间序列数据(标准普尔500(SPX指数)每日价格)计算多个时间框架(年化、季度)的回报率

d4so4syb  于 2023-01-28  发布在  其他
关注(0)|答案(2)|浏览(178)

我有一个CSV文件,其中包含S&P 500(SPX)股票市场指数30年的每日收盘价,我将其读取为Dataframe Series,日期设置为Index。
Dataframe :
| 日期|开放|高|低|关闭|
| - ------|- ------|- ------|- ------|- ------|
| 2023年1月13日|小行星3960.60|小行星4003.95|小行星3947.67|小行星3999.09|
| 2023年1月12日|小行星3977.57|小行星3997.76|小行星3937.56|小行星3983.17|
| 2023年1月11日|小行星3932.35|小行星3970.07|小行星3928.54|小行星3969.61|
| 2023年1月10日|三千八百八十八点五七|小行星3919.83|小行星3877.29|小行星3919.25|
| 2023年1月9日|小行星3910.82|小行星3950.57|小行星3890.42|小行星3892.09|
| 一九九○年一月八日|三百五十三点七九|三百五十四点二四分|三百五十块五四|三百五十三点七九|
| 一九九○年一月五日|三百五十二点二零|三百五十五点六七|三百五十一点三五|三百五十二点二零|
| 一九九○年一月四日|三百五十五点六七|三百五十八点七六|三百五十二点八九|三百五十五点六七|
| 一九九○年一月三日|三百五十八点七六|三百六十点五九|三百五十七点八九|三百五十八点七六|
| 一九九○年一月二日|三百五十九点六九|三百五十九点六九|三百五十一块九八|三百五十九点六九|
它实际上有一个日期(作为指数)列和四列(开盘价、最高价、最低价、收盘价)的每日价格,我使用的是 * 收盘价 *。
我想使用一个灵活的函数来计算从选定的开始日期到结束日期的年回报率,公式如下:

(end_price / beginning_price - 1) * 100

因此,2022年的年回报率为:

(SPX_Close_price_at_31_December_2022 - SPX_Close_price_at_31_December_2021 - 1)*100

如果同一个函数可以处理每月或每季度的数据输入,那就太理想了,然后,我想把这些周期性收益率(%)添加到 Dataframe 中的一个单独的列中,或者添加到一个新的 Dataframe 中,并匹配各行的开始和结束日期,这样我就可以在Matplotlib折线图上生成连续的年收益率,而且我想对整个30年的时间序列都这样做。
这是我希望最终 Dataframe 的外观(以下返回数字仅为示例):
| 日期|年回报率(%)|
| - ------|- ------|
| 月/日/2022|-18岁|
| 2021年月日|二十个|
| 2020年月日|十五|
| 2019年月日|十八|
我是Python的初学者,仍然在努力处理日期和日期时间格式,并将这些日期与跨选定行的列中的数据进行匹配。
下面是我到目前为止所做的,但它不能正常工作。我将尝试 * dateutil * 库,但我认为构建高效函数的概念仍然是我需要努力的事情。这是我关于堆栈溢出的第一个问题,所以感谢我的提问:)

def spx_return(df, sdate, edate):

    delta = dt.timedelta(days=365)
    while (sdate <= edate):

        df2 = df['RoR'] = (df['Close'] / df['Close'].shift(-365) - 1) * 100

        sdate += delta
        #print(sdate, end="\n")

    return df2
p1iqtdky

p1iqtdky1#

为了以通用的方式计算年度和季度比率,我设计了一个函数,该函数将开始日期、结束日期以及区分年和季度的模式作为频率类型作为参数。()以提取目标数据行。对于该提取的结果,我们将在下一行中合并您的公式。此外,在从开始日期确定比率时,我们需要在时间上往回走,所以我们减去“366天”或“90天”作为频率关键字,我还没有验证这个值在所有情况下都能得到正确的结果,这是由于市场假期,如年底和新年假期,设置更大的天数可能会解决这个问题。

import pandas as pd
import yfinance as yf

df = yf.download("^GSPC", start="2016-01-01", end="2022-01-01")
df.index = pd.to_datetime(df.index)
df.index = df.index.tz_localize(None)

def rating(data, startdate, enddate, freq):
    offset = '366 days' if freq == 'Y' else '90 days'
    #dff = df.loc[(df.index >= startdate) & (df.index <= enddate)]
    dff = df.loc[(df.index >= pd.Timestamp(startdate) - pd.Timedelta(offset)) & (df.index <= pd.Timestamp(enddate))]
    dfy = dff.groupby(pd.Grouper(level='Date', freq=freq)).tail(1)
    ratio = (dfy['Close'] / dfy['Close'].shift()-1)*100
    return ratio
    
period_rating = rating(df, '2017-01-01', '2019-12-31', freq='Y')
print(period_rating)

Date
2016-12-30          NaN
2017-12-29    19.419966
2018-12-31    -6.237260
2019-12-31    28.878070
Name: Close, dtype: float64

period_rating = rating(df, '2017-01-01', '2019-12-31', freq='Q')
print(period_rating)

Date
2016-12-30          NaN
2017-03-31     5.533689
2017-06-30     2.568647
2017-09-29     3.959305
2017-12-29     6.122586
2018-03-29    -1.224561
2018-06-29     2.934639
2018-09-28     7.195851
2018-12-31   -13.971609
2019-03-29    13.066190
2019-06-28     3.787754
2019-09-30     1.189083
2019-12-31     8.534170
Name: Close, dtype: float64
5f0d552i

5f0d552i2#

如果df包含DatetimeIndex,则可以使用.loc访问器,并将日期格式化为字符串,以检索所需的值。例如,df.loc['2022-12-31'].Close应返回Close值on 2022-12-31。
就效率而言,虽然可以使用移位操作,但实际上并不需要在 Dataframe 中分配更多内存--可以使用循环:

annual_returns = []
end_dates = []
for year in range(1991,2022):
    end_date = f"{year}-12-31"
    start_date = f"{year-1}-12-31"
    end_dates.append(end_date)

    end_price, start_price = df.loc[end_date].Close, df.loc[start_date].Close
    annual_returns.append((end_price / start_price - 1)*100)

然后您可以从列表中构建最终的 Dataframe :

df_final = pd.DataFrame(
    data=annual_returns,
    index=pd.DatetimeIndex(end_dates, name='Date'),
    columns=['Annual Return (%)']
)

使用yfinance中的一些示例数据,我得到以下结果:

>>> df_final
            Annual Return (%)
Date
2008-12-31         -55.508475
2009-12-31         101.521206
2010-12-31          -4.195294
2013-12-31          58.431109
2014-12-31          -5.965609
2015-12-31          44.559938
2019-12-31          29.104585
2020-12-31          31.028712
2021-12-31          65.170561

相关问题