I am building a financial application in which I have to solve for a rate( bond yield ) that discounts cashflows back to a present value. We are solving for the rate.
What would the best way to do this in Javascript? What I have works, but I feel it can be optimised as it currently might take 60/70ms even with a solid 'best guess' parameter.
Also - is there a algorithmic way of determining the iteratingIncrement and allowable error such that there is never a time when the iteratingIncrement is too large to be able to find the allowableError?
All suggestions very welcome.
Many Thanks
const solveDcfYield = ( workoutCashflows, target, bestGuess=0.00, iteratingIncrement=0.000001, allowableError=0.0001, maxIterations=1000000) => {
/**
* @param {array} workoutCashflows Array of cashflows for specific settle and exchange.
* @param {float} target Value for which to solve toward.
* @param {float} bestGuess Best Guess to optimise algorithm solve time. As percentage e.g. 5.00.
* @param {float} iteratingIncrement Value to increment solver each iteration.
* @param {float} allowableError Minimum distance from target to solve, unless max iterations hit.
* @param {int} maxIterations Maximum number of iterations to un in the solver
*
* @return {float} r Rate/yield that discounts cashflows coto within allowableError of target
*/
let iteratingDifference = allowableError + 10;
let r = bestGuess/100;
let iterations = 0;
let startTime = new Date().getTime();
while ((( iteratingDifference > allowableError) || (iteratingDifference < -allowableError)) && (iterations <= maxIterations)) {
iteratingDifference = target - sumAndDiscountCashFlows(workoutCashflows, r);
r = (iteratingDifference > 0) ? (r - iteratingIncrement) : (r + iteratingIncrement);
iteratingDifference = parseFloat(iteratingDifference.toFixed(8));
iterations = iterations + 1;
if (iterations === (maxIterations - 1)){
console.log("maximum iterations hit...");
}
};
let endTime = new Date().getTime();
let time = endTime - startTime
console.log("Solver Algorithm Execution Time: ", time);
return r;
};
An efficient way to compute the yield to maturity of a bond1 is to use Newton's method, which requires the calculation of derivatives, which I derive below.
You can express your problem as follows.
Let
α = 1/(1+i)
Then
p = αnf + c(1+α-0.5)Σj=1.. nαj
Note that α-0.5 is an adjustment that slightly increases the value of mid-year coupons so that they may be discounted from the end of the same year to provide an equivalent present value. This follows from the following identity.
α(cα-0.5) = cα0.5
As
Σj=1.. nαj
is seen to be the sum of a geometric series we can write:
p = fαn + (1+α-0.5)cα(αn-1)/(1-α)
which is equivalent to the following:
p = (f(1-α) + (1+α-0.5)αc)αn - (1+α-0.5)αc)(1-α)-1
Now define the following functions.
g1(α) = (1+α-0.5)ac
g2(α) = (f(1-α)+g1(α))
g3(α) = αn
g4(α) = (1-α)-1
g5(α) = g2(α)g3(α)-1
h(α) = g1(α)g5(α)g4(α)
We wish to find α+ for which
h(α+) = p
As α+ = 1/(1+i), the yield to maturity therefore equals 1/α+-1.
The derivative of h
relative to α
is as follows:
h'(α) = g1'(α)g5(α)g4(α) + g1(α)g5'(α)g4(α) + g1(α)g5(α)g4'(α)
where
g1'(α) = (-0.5α-1.5)ac + (1+α-0.5)c
g2'(α) = -f+g1'(α)
g3'(α) = nαn-1
g4'(α) = -1/(1-α)2
g5'(α) = g2'(α)g3(α) + g2(α)g3'(α)
We might begin by setting α equal to some average yield α0. Then estimate a new value of α using the straight-line approximation (i.e., Newton's method):
h(α0) + (α0+d)*h'(α0) = p
and solving for d:
d = (p-h(α0))/h'(α0) - α0
The new estimate of α is therefore α + d. Next, calculate
d = (p-h(α))/h'(α) - α
update α and continue in this fashion until the change in α is sufficiently small. This should quickly converge, certainly faster that methods that do not use derivates.
1. A bond's yield to maturity is the annual discount rate that causes the purchase price of the bond to equal the sum of the present values of coupon payments (two per year) plus the present value of the payment of the bond's face value on the maturity date.