Overview
This example utilizes the short-rate library to calculate the value of a bond with an embedded option.
Calibrating the Tree
To calibrate the tree, we need to provide a set of discount rates for each level of the tree. A short rate volatility needs to also be provided. We assume the following:
- Short rate volatility of 0.1
- At the risk free rate, $100 invested today would yield $103.5, $108, and $110 at each level of the tree.
let sr = await import('/lib/finance/tree-models/binomial/v1.0.0/short-rate.mjs');
let vol = 0.1;
//the discounts array is the price of zero coupon bonds at each level of the tree.
let zeros = [103.5, 108, 110];
let discounts = zeros.map(p=>100/(p));
//tree is a
let tree = sr.calibrate(vol, discounts);
The tree that is constructed is an array of arrays. Each array within the tree represents one layer of the binomial tree, and each value in the layer represents the short rate at that point. The tree is constructed such that the risk neutral probability of going from any node of the tree to either of the two successful nodes is one half.
Bond Details
We assume that the bond principal is $100. At each period, the bond pays $3.5. Also, there is an embedded call option in the bond such that the bond can be called at $101.8. The bond has a maturity of 3 years (periods).
Iteratively Discounting Risk Neutral Values
To value the bond, we begin at the bond maturity. The bond maturity occurs at the 4th layer of the tree. At this point, the bond returns $100 principal plus $3.5 of interest. Therefore, the value is the same, $103.5 at each node of the fourth layer of the tree.
Then, those values are discounted back to the prior layer of the tree. The interest payments (3.5) are added to the value of the bond. If the discounted value of the bond at this layer exceeds the strike price, it is replaced by the strike price. This process proceeds backwards to the first layer of the tree.
let values = [103.5,103.5, 103.5, 103.5];
let values3 = sr.discount(tree, values);
let values2 = sr.discount(tree, values3.map(p=>p+3.5).map(p=>{
if(p>101.8) return 101.8;
return p;
}));
let values1 = sr.discount(tree, values2.map(p=>p+3.5).map(p=>{
if(p>101.8) return 101.8;
return p;
}));
values1 will hold an array with the value of the bond at the root of the tree. That is, values1 has length 1, with the risk neutral price of the bond with the embedded option.
Full Code
The following is a full listing of the above code.
let sr = await import('/lib/finance/tree-models/binomial/v1.0.0/short-rate.mjs');
let vol = 0.1;
//the discounts array is the price of zero coupon bonds at each level of the tree.
let zeros = [103.5, 108, 110];
let discounts = zeros.map(p=>100/(p));
let tree = sr.calibrate(vol, discounts);
let values = [103.5,103.5, 103.5, 103.5];
let values3 = sr.discount(tree, values);
let values2 = sr.discount(tree, values3.map(p=>p+3.5).map(p=>{
if(p>101.8) return 101.8;
return p;
}));
let values1 = sr.discount(tree, values2.map(p=>p+3.5).map(p=>{
if(p>101.8) return 101.8;
return p;
}));
Try it!
Using PV
The "pv" function in the short-rate library runs the above iteration. It requires a map function that is applied at each level of the tree.
Note, the map function can take the array representing the layer of the tree, from which it can determine how to map the items. Also, the map function is applied to each level of the tree, including the layer at maturity.
let sr = await import('/lib/finance/tree-models/binomial/v1.0.0/short-rate.mjs');
let vol = 0.1;
//the discounts array is the price of zero coupon bonds at each level of the tree.
let zeros = [103.5, 108, 110];
let discounts = zeros.map(p=>100/(p));
let tree = sr.calibrate(vol, discounts);
let values = [100,100, 100, 100];
let value = sr.pv(tree, values,(p,i,data)=>{
let val = p + 3.5;
if(val>101.8 && data.length<4) return 101.8;
return val;
});
Try it!