收益率曲线(Yield Curve)是显示一组货币和信贷风险均相同,但期限不同的债券或其他金融工具收益率的图表。纵轴代表收益率,横轴则是距离到期的时间。在此用python建模分析零息票收益率曲线,输出图表并制图。

首先要理解收益率的计算方法,然后计算出连续复利和复利。再根据计算步骤在python中编写代码建模

此为连续复利的计算

python中建模分析零息票收益率曲线--复利和连续复利-LMLPHP

 # 没有年息票的一年期以内的零息票年收益率YTM=(log(面值/价格))/期限
 r1 = np.log(100/97.5)/0.25
 r2 = np.log(100/94.9)/0.5
 r3 = np.log(100/90)/1
 print('第0.25年年息票收益率:',round(r1,5))
 print('第0.5年年息票收益率:',round(r2,5))
 print('第1年年息票收益率:',round(r3,5))
 #每半年付息一次的有年息票的零息票年收益率YTM:零息票年收益率=[log((年息票/2+面值)/(债券价格-年息票/2*(前期价格/面值)))]/期限
 #价格=(年息票/2)*e^(-r2*0.5)+(年息票/2)*e^(-r3*1)+(年息票/2+面值)*e^(-r4*1.5)
 # r4=[log(104/(94-(94.9+90)/100)]/1.5
 #超过一年期的零息票年收益率YTM=(面值/价格)开期限n的次方根减1
 rate2 = math.pow(100 / 84.99, float(1) / float(3))-1
 print(rate2)
 # 96=4*(1+0.5*r2)^-1+4*(1+r3)^-1+104*(1+r4)^-1.5
 r4 = np.log(104/88.604)/1.5
 print('第1.5年年息票收益率:',round(r4,5))

 r5 = np.log(106/(101.6-(1.849*6+88.604/104*6)))/2#np.log(106/85.394)/2
 print('第2年年息票收益率:',round(r5,5))

python中建模分析零息票收益率曲线--复利和连续复利-LMLPHP

 #线性插值
 #第0.75年年息票收益率:
 r6 = (r2+r3)/2
 print('第0.75年年息票收益率:',round(r6,5))
 #第1.25年年息票收益率:
 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))

python中建模分析零息票收益率曲线--复利和连续复利-LMLPHP

1.我们需要在python中加载相应的模块来进行开发调式

 import pandas as pd
 import numpy as np
 import matplotlib.pyplot as plt
 import math
 import os
 import operator
 import sys#实现从程序外部向程序传递参数。
 import xlrd

2.确定输入及输出的文件路径,读取文件

 InputPath=sys.argv[1]#输入路径
 OutputPath=sys.argv[2]#输出路径
 filePath = InputPath#处理的文件为在输入路径中读取的文件

3.明确表中的字段,定义两个空字典输出计算的数值

python中建模分析零息票收益率曲线--复利和连续复利-LMLPHP

上表为输入表,根据表中的字段来计算收益率,Period为付息频率,我们需要取CouponFrequency中的最大值来进行计算

 df = pd.read_excel(filePath)
 bondsCount = df.shape[0]

 dicE4Calc = {}#定义一个空的价格比计算表。价格/面值
 dicResult = {}#定义一个空的结果表

 Period = 1 / df['CouponFrequency'].max()#步长为0.5

4.定义一个价格合计,根据这个合计来进行迭代计算

 def getPreSum(pCoupon, targetTerm, startTerm):#前期价格合计
     sum = 0
     p = startTerm
     while (p < targetTerm):#要小于目标的期限
         sum += dicE4Calc[str(p)] * pCoupon
         p += Period#期限以0.5递增

     return sum#返回的是新计算出来的价格

5.定义线性插值法计算,利用前后两期数据可以求出中间的值

 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

6.读取表格

df['Coupon']=df['Coupon'].fillna(0)#若Coupon为空值则填充为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

7.计算有年息和无年息的收益率

  if Coupon == 0:

         e4Calc = Price / FaceValue

         YTM = math.log(FaceValue / Price) / Term

     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 = math.log(1 / e4Calc) / Term

     dicE4Calc[str(Term)] = e4Calc
     dicResult[str(Term)] = round(YTM, 9)

8.把计算结果写到输出表中

 sorted_dicResult = sorted(dicResult.items(),key =operator.itemgetter(0))#把求出的收益率按期限排序,把字典转为列表
 # print(dicResult)
 print(sorted_dicResult)
 Term = [i[0] for i in sorted_dicResult ]#遍历列表中的期限
 Yield = [i[1] for i in sorted_dicResult ]#遍历列表中的
 data={"Term":Term,"Yield":Yield}
 columns=['Term','Yield']
 df=pd.DataFrame(data=data,columns=columns)
 df['TermBase']='Y'
 df = df.set_index("TermBase")
 df.to_excel(OutputPath,sheet_name='OutPut')

 print(df)

9.绘制出收益率曲线图

 x = Term
 y = Yield
 plt.plot(x,y)
 plt.xlabel('CouponFrequency')#期限
 plt.ylabel('YTM')#收益率
 plt.title('Zero coupon yield curve')#命名
 plt.show()

最终结果如下:

python中建模分析零息票收益率曲线--复利和连续复利-LMLPHP

复利的计算也类似,完整代码如下:

python中建模分析零息票收益率曲线--复利和连续复利-LMLPHP

 #复利计算
 import pandas as pd
 import numpy as np
 import matplotlib.pyplot as plt
 import math
 import os
 import operator
 import sys
 import xlrd
 from openpyxl import Workbook
 from openpyxl import load_workbook
 from openpyxl.utils import get_column_letter
 from openpyxl.compat import range

 InputPath=sys.argv[1]
 OutputPath=sys.argv[2]
 print(InputPath)
 print(OutputPath)

 filePath = InputPath
 df = pd.read_excel(filePath)
 bondsCount = df.shape[0]
 dicE4Calc = {}
 dicResult = {}
 Period = 0.5

 def getPreSum(pCoupon, targetTerm, startTerm):
     sum = 0
     p = startTerm
     while (p < targetTerm):
         sum += dicE4Calc[str(p)] * pCoupon
         p += Period

     return sum

 def LinearInterpolation(pCoupon, targetTerm, interval):
     sum = 0
     p = interval
     while p < targetTerm:
         if str(p) not in dicResult:
             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)
                 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)

         p += interval
 Period = 1 / df['CouponFrequency'].max()
 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

     else:
         PeriodCoupon = Coupon * Period

         if Term % Period == 0:
             LinearInterpolation(PeriodCoupon, Term, Period)

             e4Calc = (Price - getPreSum(PeriodCoupon, Term, Period)) / (FaceValue + PeriodCoupon)

         else:

             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, 9)

 # print(dicE4Calc)
 # print(dicResult)

 sorted_dicResult = sorted(dicResult.items(),key =operator.itemgetter(0))
 # print(dicResult)
 print(sorted_dicResult)
 Term = [i[0] for i in sorted_dicResult ]
 Yield = [i[1] for i in sorted_dicResult ]

 data={"Term":Term,"Yield":Yield}
 columns=['Term','Yield']
 df=pd.DataFrame(data=data,columns=columns)
 df['TermBase']='Y'
 df = df.set_index("TermBase")
 df.to_excel(OutputPath,sheet_name='OutPut')
 print(df)

 x = Term
 y = Yield
 plt.plot(x,y)
 plt.xlabel('CouponFrequency')
 plt.ylabel('YTM')
 plt.title('Zero coupon yield curve')
 plt.show()
05-08 14:53