FOClass: 二分逼近法求債券殖利率
一債券殖利率等式( 原理 )如下,試求 r 值:
如果該等式中 (1+r)^t 的期別 t 皆為大於 0 的整數時,我們可以直接使用 numpy.lib.financial.irr([0.875*(1-280/365.), 0.875, 0.875, 0.875, 0.875, 100.875]) 求解債券殖利率 r 。但可惜它不是,所以我們使用二分逼近法來求得近似解。
概念乃是先把等式左邊移位至右邊,變成 -99.2679 - .... + 0.875/(1+r)^(4+280/365) = f(r) ,再將 r0=0, r1=1 代入 f(r),則會得到一正值及一負值,所以我們可相信真實的 r’ 值的確位於 0 ~ 1 之間。
接下來,就進行一系列的迭代。令新 r 值等於 (r0 + r1)/2 ,再代入 f(r) 中,若 f(r) 為正,則表 r’ 必在 (r0+r1)/2 ~ r1 之間; 若 f(r) 為負,則表 r’ 必在 r0 ~ (r0+r1)/2 之間。持續進行此一動作,直到 r 值的差異值小於設定值為止。
以下則是我的 Python 程式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | class AnnumYield: """ 求解年化殖利率。 """ def __init__(self, PV=0, Ci=[], Ni=[], precision=6, start_rate=0, end_rate=1): """ Ci: 第 i 個現金流量 Ni: 第 i 個期別的真實時間 PV: 現值。為債券購入價格,其值應為負值。 precision: 數值分析時的精度, 當殖利率變化值小於 10**(-1*precision) , 則停止求解。 start_rate,end_rate: 起始利率。 self.equation: 現金流量的方程式 self.yieldrate: 債券殖利率 """ self.Ci = Ci self.Ni = Ni != [] and Ni or range(len(Ci)) self.PV = PV < 0 and PV or -1*PV self.precision = precision self.start_rate = start_rate self.end_rate = end_rate equations = [] for i, c in enumerate(Ci): equations.append('%s/(1+r)**(%s)'%(c, Ni[i])) self.equation = ('%s + ' % self.PV + ' + '.join(equations)) self.yieldrate = self.getYield() def getYield(self): """ 利用二分逼近法求 self.equation 的根。 當所求出的 yieldrate 與前一個解的差值小於 10 ** (-1*precision) 即停止求解。 預設代入 start_rate 及 end_rate 去作逼近, 所以真實的 yieldrate 必須滿足 start_rate < yieldrate < end_rate 的條件, 否則無解。 """ r = self.start_rate self.list = [(self.start_rate, eval(self.equation))] r = self.end_rate self.list.append((self.end_rate, eval(self.equation))) i0, (r0, res0) = 0, self.list[0] i1, (r1, res1) = 1, self.list[1] precision = 10 ** (-1*self.precision) while abs(r0 - r1) > precision: r = (r0 + r1)/2. res = eval(self.equation) self.list.insert(i1, (r, res)) if res * res0 < 0: i0, (r0, res0) = i1-1, self.list[i1-1] i1, (r1, res1) = i1 , self.list[i1] elif res * res1 < 0: i0, (r0, res0) = i1 , self.list[i1] i1, (r1, res1) = i1+1, self.list[i1+1] elif res == 0: break else: raise ValueError, \ '無解。 end_rate 設定為 %s ' \ % self.end_rate + \ ',此數值比 yieldrate 解還小' return round((r0 + r1)/2., self.precision) if __name__ == '__main__': t = 280./365 Ci = [-0.875*(1-t), 0.875, 0.875, 0.875, 0.875, 100.875] Ni = [0, 0+t, 1+t, 2+t, 3+t, 4+t] annumyield = AnnumYield(Ci=Ci, Ni=Ni, PV=99.2679) print annumyield.yieldrate |