月收益率的计算
基金净值时间序列数据与benchmark数据概览:
fund.head()
Out[1]:
fund 100 200 500 1000 全指
date
2016-03-17 1.002 2955.7422 3855.9222 5745.9750 7779.8753 4392.5355
2016-03-18 1.002 2980.0660 3963.8730 5949.2630 8097.2040 4514.1640
2016-03-21 1.002 3047.7244 4072.7380 6099.4805 8325.9770 4631.2287
2016-03-22 1.002 3022.5462 4050.1460 6075.4922 8311.2112 4610.1958
2016-03-23 1.002 3027.8622 4073.2864 6118.9664 8408.6385 4643.0294
月百分比收益率=该月最后一个交易日的基金净值/上个月最后一个交易日的基金净值-1。先将日期index中的年和月分离出来,找出每一个"年月"下对应的索引并生成array,然后我们只需要用到前后两个array元素的最后一位来计算,可以使用字典逐个添加到dataframe中。为了第一个月的数据不浪费,第一个月的收益率为该月最后一个交易日的基金净值-1。
whatmonth=[]
for n in fund.index:
whatmonth.append(n.strftime("%Y%m"))
m=pd.Series(whatmonth)
m_r=pd.DataFrame(columns=('日期','月收益率'))
m_index=[];a=-1
for i in m.unique():
a+=1
m_index.append(np.where(m==i)[0])
if i==m.unique()[0]:
rmonth=fund['fund'].iloc[m_index[0][-1]]-1
else:
rmonth=fund['fund'].iloc[m_index[a][-1]]/fund['fund'].iloc[m_index[a-1][-1]]-1
#将计算结果逐行插入,注意变量要用[]括起来,同时ignore_index=True,否则会报错
m_r=m_r.append(pd.DataFrame({'日期':[i],'月收益率':[rmonth]}),ignore_index=True)
m_r.set_index('日期',inplace=True)
m_r
Out[11]:
月收益率
日期
201603 0.002000
201604 -0.003992
201605 0.017034
201606 0.032512
201607 -0.017176
...... ......
201910 0.020524
201911 0.003467
201912 0.060815
202001 0.111401
202002 0.009965
月对数收益率=ln(该月最后一个交易日的基金净值/该月第一个交易日的基金净值)。下面用另一种思路计算:对数收益率具有可加性,每月的所有日对数收益率之和就是本月的月对数收益率。分离出“年月”后,可以用groupby函数来分组求和,同时修改第一个月的收益率即可。
month=[]
lograte=np.log(fund/fund.shift(1)).dropna()['fund']
index=lograte.index
for i in range(0,len(lograte)):
month.append(''.join([index[i].strftime("%Y"),index[i].strftime("%m")]))
y=pd.DataFrame(lograte.values,index=month,columns=['月收益率'])
ret_monthly=y.groupby(y.index).sum()
ret_monthly.iloc[0]=np.log(fund['fund'].iloc[m_index[0][-1]])
ret_monthly
Out[35]:
月收益率
201603 0.001998
201604 -0.004000
201605 0.016891
201606 0.031995
201607 -0.017325
...... ......
201910 0.020316
201911 0.003461
201912 0.059038
202001 0.105621
202002 0.009916
周收益率的计算
周百分比收益率=该周最后一个交易日的基金净值/上周最后一个交易日的基金净值-1。为了第一周的数据不浪费,第一周的收益率为该周最后一个交易日的基金净值-1。由于会受到节假日的影响,每周交易日可能不足5天,甚至有可能两周的交易日加起来都不足5天,这为我们的计算增加了难度,可以分类计算:
whatday=[]
w_r=[]
suoyin=[]#存放一周有5天的交易日索引号
for i in fund.index:
whatday.append(i.strftime("%w"))
def delta_days(x,y):
d1=fund.index[x]
d2=fund.index[y]
return (d2-d1).days
for i in range(len(fund.index)-1):
if whatday[i:i+5]==['1','2','3','4','5'] and delta_days(i,i+4)==4:
suoyin.append([i,i+1,i+2,i+3,i+4])
m=[]#存放一周没有5天的交易日索引号
for i in range(len(suoyin)-1):
if suoyin[i][4]-suoyin[i+1][4]!=-5:
m.append([j for j in range(suoyin[i][4]+1,suoyin[i+1][0])])
#两周小于5个交易日的会合成为一周,分成两周(可能发生于春节和国庆假期)
for i in m:
for j in range(len(i)-1):
if delta_days([i[j]],[i[j+1]])!=1:
m.append([q for q in range(i[0],i[j]+1)])
m.append([q for q in range(i[j+1],i[-1]+1)])
m.remove([q for q in range(i[0],i[-1]+1)])
#将交易日不足5日的索引号插入对应位置
for i in m:
for j in suoyin:
if i[0]==j[-1]+1:
suoyin.insert(suoyin.index(j)+1,i)
if suoyin[0][0]!=5:#检查第一周是否为5个交易日
suoyin.insert(0,[j for j in range(0,suoyin[0][0])])
if suoyin[-1][-1]!=len(fund)-1:#检查最后一周是否为5个交易日
suoyin.insert(len(suoyin),[j for j in range(suoyin[-1][-1]+1,len(fund))])
for i in range(1,len(suoyin)):
w_r.append(fund['fund'].iloc[suoyin[i][-1]]/fund['fund'].iloc[suoyin[i-1][-1]]-1)
w_r.insert(0,fund['fund'].iloc[suoyin[1][-1]]/1-1)
weekr=pd.Series(w_r,index=[i for i in range(1,len(w_r)+1)])
weekr.index.name='周数'
weekr
Out[13]:
周数
1 0.002000
2 0.000000
3 0.000000
4 0.004990
5 0.003972
197 0.020946
198 0.002931
199 0.024547
200 0.069595
201 -0.081067
Length: 201, dtype: float64
连续最大跌幅的计算
在连续几日收益率幅为负的区间内,从收益率为负的第一天算起,一直累加到收益率为正的前一天为止,得到该区间的连续跌幅,求所有符合条件的区间连续跌幅的最小值。
maxdown=[]
c=0
from datetime import date
#rfund为fund的日百分比收益率数据,截止到2020年2月28日
rfund.loc[date(2020,2,29)]=5*[0]#解决最后一个收益率是负数求不了的问题
for i in range(len(rfund)-1):
if rfund['fund'][i]<0 and rfund['fund'][i+1]<0:
c+=rfund['fund'][i]
elif rfund['fund'][i]<0 and rfund['fund'][i+1]>=0:
if rfund['fund'][i-1]<0:
c+=rfund['fund'][i]
maxdown.append(c)
c=0
else:
maxdown.append(rfund['fund'][i])
else:
pass
min(maxdown)
Out[20]: -0.10155444714139317