第4节 树形计算可转换债券价格

第4节 树形计算可转换债券价格


4.1 简介

       
可转换债券是一种由公司发行的债券。此种债券的持有者在债券有效期内有权利将债券转换成一定数量的该公司的股票,一个单位的债券可转换的股票的数量为转换率。同时这种债券在有效期内一般都是可以被公司以一定价格赎回的,赎回一单位该债券的价格为赎回价。如果公司将要赎回债券,债券的持有者可以选择将债券转换为股票,即债券被转换的优先级高于债券被赎回。
       
计算可转换债券的价格时不能忽略公司有可能违约。一种相对简单的计算可转换债券价格的方法是基于假设公司股票价格的变化服从几何布朗运动,可以使用一个树形描述股价变化过程,并且在每个Δ t \Delta tΔt的时间段内该公司有1 − e − λ Δ t 1-e^{-\lambda \Delta t}1eλΔt的概率会违约。其中λ \lambdaλ为风险中性违约概率密度。如果公司违约,则在该时刻可转换债券的价格变为债券面值乘以回收率。

4.2 计算可转换债券价格算法

  1. 根据给定参数构建一个公司股价变化的二叉树,其中分叉参数为(由于有概率违约,所以和普通二叉树模型不同):
    u = e ( σ 2 − λ ) Δ t , d = 1 u , p u = e r Δ t − d e − λ Δ t u − d , p d = u e − λ Δ t − e r Δ t u − d . u=e^{\sqrt{(\sigma^2-\lambda)\Delta t}}, \;\; d = \frac{1}{u}, \;\; p_u = \frac{e^{r\Delta t}-de^{-\lambda\Delta t}}{u-d}, \;\; p_d = \frac{ue^{-\lambda\Delta t}-e^{r\Delta t}}{u-d}.u=e(σ2λ)Δt,d=u1,pu=uderΔtdeλΔt,pd=udueλΔterΔt.
  2. 计算出叶子层每个节点的股价对应的可转换债券的价格B i , j B_{i,j}Bi,j,具体为B i , j = max ⁡ ( S i , j × 转 换 率 , 债 券 面 值 ) B_{i,j} = \max(S_{i,j}\times转换率,债券面值)Bi,j=max(Si,j×)。其中i ii为层数,j jj为节点位于该层的位置(由低股价到高股价)。
  3. 计算前一层每个节点的股价对应的可转换债券的价格。首先计算不考虑债券被转换或赎回的情况,债券价格的期望
    B i , j ′ ′ = e − r Δ t [ p u B i + 1 , j + 1 + p d B i + 1 , j + p 违 约 × 面 值 × 回 收 率 ] , p 违 约 = 1 − p u − p d . B_{i,j}^{\prime\prime} = e^{-r\Delta t}[p_uB_{i+1, j+1}+p_dB_{i+1, j}+p_{违约}\times 面值\times 回收率],\;\; p_{违约} = 1-p_u-p_d .Bi,j=erΔt[puBi+1,j+1+pdBi+1,j+p××],p=1pupd.
    如果该价格大于公司可以赎回的价格,那么公司将会赎回,所以
    B i , j ′ = min ⁡ ( B i , j ′ ′ , 赎 回 价 ) . B_{i,j}^\prime = \min(B_{i,j}^{\prime\prime}, \;赎回价).Bi,j=min(Bi,j,).
    持有者可以选择在公司赎回前转换债券为股票,所以该节点债券最终价格为
    B i , j = max ⁡ ( B i , j ′ , 转 换 率 × S i , j ) = max ⁡ [ min ⁡ ( B i , j ′ ′ , 赎 回 价 ) , 转 换 率 × S i , j ] . B_{i,j} = \max(B_{i,j}^\prime,\; 转换率\times S_{i,j}) = \max\left[\min(B_{i,j}^{\prime\prime},\; 赎回价),\; 转换率\times S_{i,j}\right].Bi,j=max(Bi,j,×Si,j)=max[min(Bi,j,),×Si,j].
  4. 重复步骤3,直到计算出根节点处的可转换债券价格。

4.3 Python代码实现计算

import math

E = math.e

class Tree_convertible_bond:
    def __init__(self, r, sigma, S_0, T, lbd, conversion_ratio, callback_price, par_value, recycle_ratio, steps):
        self.r = r
        self.sigma = sigma
        self.S_0 = S_0
        self.T = T
        self.lbd = lbd
        self.conversion_ratio = conversion_ratio
        self.callback_price = callback_price
        self.par_value = par_value
        self.recycle_ratio = recycle_ratio
        self.steps = steps
        
        self.dt = self.T/self.steps
        self.u = E**(((self.sigma*self.sigma-self.lbd)*self.dt)**0.5)
        self.d = 1/self.u
        self.p_u = (E**(self.r*self.dt)-self.d*E**(-self.lbd*self.dt))/(self.u-self.d)
        self.p_d = (self.u*E**(-self.lbd*self.dt)-E**(self.r*self.dt))/(self.u-self.d)
        self.p_default = 1-self.p_u-self.p_d
        
        self.bond_price = None
        
        self.tree = None
        
        self.build_tree()
    
    def build_tree(self):
        self.tree = list()
        for lvl in range(self.steps+1):
            row = list()
            for j in range(lvl+1):
                node = dict()
                node["S"] = self.S_0*(self.u**j)*(self.d**(lvl-j))
                node["B"] = None
                row.append(node)
            self.tree.append(row)
        return
    
    def calculate_bond_price(self):
        tree = self.tree
        r, steps = self.r, self.steps
        conversion_ratio, callback_price = self.conversion_ratio, self.callback_price
        recycle_ratio, par_value = self.recycle_ratio, self.par_value
        
        dt, u, d = self.dt, self.u, self.d 
        p_u, p_d, p_default = self.p_u, self.p_d, self.p_default
        
        # Discount factor.
        a = E**(-r*dt)
        
        # Boundary condition.
        for node in tree[-1]:
            node["B"] = max(node["S"]*conversion_ratio, par_value)
        
        # Iteratively calculate back to root node.
        for lvl in range(steps-1, -1, -1):
            for j in range(lvl+1):
                tree[lvl][j]["B"] = a*p_u*tree[lvl+1][j+1]["B"]+a*p_d*tree[lvl+1][j]["B"]
                tree[lvl][j]["B"] += a*p_default*par_value*recycle_ratio
                tree[lvl][j]["B"] = max(min(tree[lvl][j]["B"], callback_price), tree[lvl][j]["S"]*conversion_ratio)
        
        self.bond_price = tree[0][0]["B"]
        
        return
                

4.4 计算示例

       
当无风险利率为0.05,公司股价波动率为0.3,股价初始值为50,考虑一到期日为9个月后的可转换债券。其转换率为2(1债券可以被持有人转换为2支股票),公司可以用113的价格提前赎回债券,债券的面值为100。此外假设该公司每年有0.01的概率会违约,如果违约债券的回收率为0.4,即价值变为40 。
       
对于这种情况,我们用一个10步的树形计算该可转换债券价格,具体结果如下:

tree_obj = Tree_convertible_bond(0.05, 0.3, 50, 0.75, 0.01, 2, 113, 100, 0.4, 10)
tree_obj.calculate_bond_price()
bond_price = tree_obj.bond_price

print("r: 0.05,  sigma: 0.3,  S_0: 50,  T: 0.75,  lambda: 0.01,  conversion_ratio: 2,\n")
print("callback_price: 113,  par_value: 100,  recycle_ratio: 0.4,  steps: 10 . \n\n")
print("可转换债券价格为:", bond_price)

r: 0.05, sigma: 0.3, S_0: 50, T: 0.75, lambda: 0.01, conversion_ratio: 2,
callback_price: 113, par_value: 100, recycle_ratio: 0.4, steps: 10 .
可转换债券价格为: 106.61156436861458




参考资料:

  1. 《期权、期货及其他衍生产品》,John C. Hull 著,王勇、索吾林译 。

版权声明:本文为weixin_49544040原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。