The power of Scala: A bond calculator with just 39 lines of code

Calculator and financial statement

Yield to maturity is a basic measure for investment in bonds. One way to calculate it is by using object oriented and functional programming with Scala.

The problem

Conceptually YTM is a fairly straightforward ballgame. We are looking for the discount rate that makes today’s bond price equal the net present value of all future cash flow. Let’s say we are offered a bond that costs 11.500 euros and will pay annual coupons of 850 euros until the 4th year, when the principal amount of 10.000 euros will also be returned. The yield to maturity the interest rate that satisfy the following equation:

11.500 = 850 / (1 + y) ^ 1 + 850 / (1 + y) ^ 2 + 850 / (1 + y) ^ 3 + (850 + 10.000) / (1 + y) ^ 4

Unfortunately we cannot rearrange this equation to resolve for y. Instead, we have to have to apply some trial and error procedure. That is, calculating the right hand expression with different yields, until we find one that fits.

The programme

I will now walk you through a small and simple, yet efficient app that calculates yield to maturity. Please don’t be scared off by the Scala code, I will try to explain what it does. The most important part will be the conclusions we draw later on in this article.

As for a starter, we import a couple of standard libraries. We will also write a trait, similar to Java’s interfaces, with a couple of standard financial formulas for discounting cash flow. Notably, the cashFlowsPresentValue takes a sequence of cash flows as argument, add period numbers, map each flow to its discounted value and finally sum the values:

import scala.annotation.tailrec
import scala.math.{pow, round}

trait Formulae {
 def discount(r: Double, t: Long): Double = pow((1 + r/100), -t)
 def cashFlowsPresentValue(r: Double, cf: Seq[Double]): Double = {
   val idx = cf.indices.map(_ + 1) // E.g. period numbers
   val values: Seq[Double] = (cf zip idx) map { case (c, t) =>
     c * discount(r, t)
   }
   values.foldLeft(0.0)(_ + _)
 }
}

The next step is to specify class that represents a bond. When an object is created with this template, it’s cashflows are automatically generated. The class also contains a formula for calculating the cash flows’ NPV:

case class Bond(name: String, principal: Double, coupon: Double, periods: Long) extends Formulae {
 val cashflows: Seq[Double] = Seq.fill(periods.toInt - 1)(coupon) ++ Seq(coupon + principal)
 def initialNPV(r: Double): Double = cashFlowsPresentValue(r, cashflows)

Now for the tricky part. To find the YTM we will apply the following procedure:

  1. Start with a yield of 0 % and the precision of 0 decimals, calculating the NPV.
  2.  If NPV happens to equal the price, all is well and the function returns our yield.
  3. If NPV is above the price we increase yield in 1 % steps and repeat the calculus until our NPV becomes less than price.
  4. We then increase the precision, decreasing yield in 0.1% steps and repeat the calculus until NPV overshoots price again.
  5. This procedure is repeated until NPV equals price or precision reaches 5 decimals, whichever comes first.

This can be expressed as a recursion, e.g. a function that repeatedly calls itself until a condition is met. Since we don’t need intermediary results but only the YTM from the final call, we can use a programmer’s trick, tail recursion, to make things faster and safer: 

@tailrec final def yieldToMaturity(price: Double, ytm: Double, precision: Long): Double = {
   println("Trying with ytm = " + ytm)
   val inpv: Double = initialNPV(ytm)
   if (price == inpv || precision > 5) { println("This value seems to be OK"); ytm }
   else {
     val nprec: Long = (price < inpv) == (precision % 2 == 0) match {
        case false => precision + 1
        case (_) => precision
    }
     val nytm: Double = (price < inpv) match {
        case true => round(pow(10, nprec) * (ytm + pow(10, -nprec))) / pow(10, nprec)
        case (_) => round(pow(10, nprec) * (ytm - pow(10, -nprec))) / pow(10, nprec)
     }
     yieldToMaturity(price, nytm, nprec)
   }
 }
} // end of case class Bond

It is worthy to note the logics for obtaining new yield and precision for the next recursive call. They are deduced via the conditions “Price above or below NPV” and “Precision is odd or even”. However, it all boils down to just a few cases to consider in the code.

To round up, I also provide a small sample application for trying the bond calculator:

object BondExample extends App {
 val bond = new Bond("BuBa 4Y 8,5%", 100, 8.5, 4)
 val YTM: Double = round(bond.yieldToMaturity(115, 0.0, 0) * 100.0) / 100.0
 println("\n----------------------------------------------------")
 println("The yield to maturity is approximately " + YTM + " %")
 println("----------------------------------------------------\n")
}

The conclusions

The reader who has beared with the code example will now be rewarded with several hopefully interesting observations. As promised, we have made a bond calculator with just 39 lines of code. Despite this brevity, our app is object oriented. For example, we create bonds that inherit formulas from another object. We also pass bonds and cash flows as objects in function calls.

As seen in the previous section, we have also used tail recursion. Together with Scala’s own syntax it allows us to write very condensed code, doing the same as other languages’ more verbose “for-next” and “while” loops. Scala also includes modern programming language features such as “map”, “zip” and “fold” for efficient data transformation.

Moreover, our bond calculator has been developed with functional programming. That is, each and every one of the programme’s functions just takes input arguments and outputs a result, without side effects. (The only exceptions are the print line calls.). Functional programming is a technique that makes safer and better programmes. It has been around for quite a while, with languages such as Erlang, Haskell and LISP. But some unique features of Scala are that it is also purely object oriented and runs almost everywhere in Java’s virtual machine.

The bottom line is that the power of Scala resides in its portability, speed and above all in its productivity, where it actually competes with top-notch languages like Python and Ruby. Then again, Scala is more flexible in programming paradigms and also benefits vastly from its interoperability with the Java ecosystem. Thus, I definitely hold Scala as programming language with a promising future.

More information

A code example based on this article is available at GitHub

 

Gimlé Digital está actualmente buscando patrocinadores, colaboradores y empresas asociadas. Si nuestras actividades le resultan interesantes, le invitamos a hacérnoslo saber mediante el formulario de contacto de nuestro sitio web.