零息票债券是指低于债券面值价格发行,债券发行人不需要给债券持有人支付利息,只用在债券到期日支付给债券持有人债券面值金额的一类债券。本模型研究如何根据选择的数据生成息票收益率曲线。
数据如图:
一.收益率计算规则
1.无年息票的计算:零息票年收益率YTM=YTM=(面值/价格)^1/期限-1
r1 = pow(100/97.5,1/0.25)-1
r2 = pow(100/94.5,1/0.5)-1
r3 = pow(100/90,1/1)-1
print('第0.25年年息票收益率:',round(r1,5))
print('第0.5年年息票收益率:',round(r2,5))
print('第1年年息票收益率:',round(r3,5))
2.每半年付息一次的有年息票的收益率YTM:
超过一年期的零息票年收益率YTM=(面值/价格)开期限n的次方根减1
零息票年收益率=[(年息票/2+面值)/(债券价格-年息票/2*(前期价格/面值))^1/期限]-1
# 有年息票的计算:
# 1.5年:96=4*e^(-r0.5*0.5)+4*e^(-r1*1)+104*e^(-r1.5*1.5)
# r1.5=log(104/(96-4[e^(-r0.5*0.5)+e^(-r1*1)]))/1.5=0.1068
# 简化[e^(-r0.5*0.5)+e^(-r1*1)]得到:
# r1.5=[log(104/(96-4*(94.5/100+90/100))]/1.5 连续复利的算法
# pow(104/((96-(90+94.5)/100*4)),2/3)-1 #复利的算法
rate2 = math.pow(100 / 84.99, float(1) / float(3))-1
print(rate2)
r4 = pow(104/((96-(90+94.5)/100*4)),2/3)-1#pow((104/88.604),2/3)
print('第1.5年年息票收益率:',round(r4,5))
r5 = pow(106/(101.6-(1.849*6+88.604/104*6)),1/2)-1#np.log(106/85.394)/2
print('第2年年息票收益率:',round(r5,5))
3.表中有数据就需要根据给定的数据来计算,表中没有数据就需要用到线性插值
#线性插值
#第0.75年年息票收益率在0.5年和1年之间插值:
r6 = (r2+r3)/2
print('第0.75年年息票收益率:',round(r6,5))
#第1.25年年息票收益率在1年和1.5年之间插值:
r7 = (r3+r4)/2
print('第1.25年年息票收益率:',round(r7,5))
#第1.75年年息票收益率:
r8 = (r4+r5)/2
print('第1.75年年息票收益率:',round(r8,5))
#第2.25年年息票收益率:
r9 = 2(r5)/3 +(r'第2.75年年息票收益率')/3
print('第2.25年年息票收益率:',round(r9,5))
二.息票收益率曲线计算的完整代码
import pandas as pd
import numpy as np
df = pd.read_csv("input.csv")
bondsCount = df.shape[0]
dicE4Calc = {} #定义一个空的价格比计算表。价格/面值
dicResult = {} #定义一个空的结果表
#定义一个价格合计,根据这个合计来进行迭代计算
def getPreSum(pCoupon, targetTerm, startTerm):
#前期价格合计
sum = 0
p = startTerm
while (p < targetTerm): #要小于目标的期限
sum += dicE4Calc[str(p)] * pCoupon
p += Period #期限以0.5递增
return sum
#定义线性插值法计算,利用前后两期数据可以求出中间的值
def LinearInterpolation(pCoupon, targetTerm, interval):
#线性插值法利用中位数求利率
sum = 0
p = interval
while p < targetTerm:
if str(p) not in dicResult: #结果表中没有的数据,left为前面一期,right为后面一期
r_Left = str(p - interval)
r_Right = str(p + interval)
if r_Left in dicResult and r_Right in dicResult: #结果表中有前后的数据就用插值法计算
r = (dicResult[r_Left] + dicResult[r_Right]) / 2
elif r_Left in dicResult and r_Right not in dicResult:
r_Left2 = str(p - interval - interval) #left为前2期
r = dicResult[r_Left2] + (dicResult[r_Left] - dicResult[r_Left2]) / (interval) * (p - float(r_Left2))
dicResult[str(p)] = r
dicE4Calc[str(p)] = pow(math.e, -r * p) #e的(-r*p)次方
p += interval
Period = 1 / df['CouponFrequency'].max() #付息频率步长为0.5
df['Coupon']=df['Coupon'].fillna(0)
for i in range(bondsCount):
FaceValue = df.loc[i, 'FaceValue']
Price = df.loc[i, 'Price']
Term = df.loc[i, 'Term_Y']
Coupon = df.loc[i, 'Coupon']
CouponFrequency = df.loc[i, 'CouponFrequency']
YTM = 0
e4Calc = 0
#计算有年息和无年息的收益率
if Coupon == 0:
e4Calc = Price / FaceValue #价格/面值
YTM = pow(FaceValue / Price,1/ Term) -1 #YTM=(面值/价格)^1/期限-1
else:
PeriodCoupon = Coupon * Period ##年息票的0.5
if Term % Period == 0: #从0.5年开始
LinearInterpolation(PeriodCoupon, Term, Period)
e4Calc = (Price - getPreSum(PeriodCoupon, Term, Period)) / (FaceValue + PeriodCoupon)
else: #不是从0.5开始,需要在起始日期以0.5年递增
LinearInterpolation(PeriodCoupon, Term, Term % Period)
e4Calc = (Price - getPreSum(PeriodCoupon, Term, Term % Period)) / (FaceValue + PeriodCoupon)
YTM = pow(1 / e4Calc,1/ Term) - 1
dicE4Calc[str(Term)] = e4Calc
dicResult[str(Term)] = round(YTM,6)
sorted_dicResult = sorted(dicResult.items(),key =operator.itemgetter(0))
print(sorted_dicResult)
Term = [i[0] for i in sorted_dicResult ]
Yield = [i[1] for i in sorted_dicResult ]
#数据输出为DataFrame
data={"Term":Term,"Yield":Yield}
columns=['Term','Yield']
df=pd.DataFrame(data=data,columns=columns)
df['TermBase']='Y'
df = df.set_index("TermBase")
print(df)
#可视化展示
x = Term
y = Yield
plt.plot(x,y)
plt.xlabel('CouponFrequency')
plt.ylabel('YTM')
plt.title('Zero coupon yield curve')
plt.show()