Chapter 4, part 5 of 5: cash-flow analysis
Welcome back.
This week, the final post in a series of 5 covering chapter 4 of my book; the series starts here, and all the posts can be reached from the Archive page.
A bit of QuantLib news: I’ve created a branch for the 1.4 release in our GitHub repository. I’m not famous for estimating release dates correctly, but I’m aiming at the end of February.
And of course, you can still register for my Introduction to QuantLib Development course; it’s three days of presentations and exercises in London, and previous editions have been a nice experience for both me and the attendees.
Follow me on Twitter if you want to be notified of new posts, or add me to your circles, or subscribe via RSS: the buttons for that are in the footer. Also, make sure to check my Training page.
Cash-flow analysis
After having generated one or more legs, we naturally want to analyze
them. The library provides a number of commonly used functions such as
NPV, basis-point sensitivity, yield, duration, and others. The
functions are defined as static methods of a CashFlows
class so that
they do not take up generic names such as npv
in the main QuantLib
namespace; the same could have been done by defining them as free
functions into an inner CashFlows
namespace.
The functions themselves, being the implementation of well-known
formulas, are not particularly interesting from a coding point of
view; most of them are simple loops over the cash-flow sequence, like
the npv
function sketched in listing 4.13. What’s interesting is
that, unlike npv
, they might need to access information that is not
provided by the generic CashFlow
interface; or they might need to
discriminate between different kinds of cash flows.
Listing 4.13: Sketch of the CashFlows::npv
method.
One such function calculates the basis-point sensitivity of a leg. It
needs to discriminate between coupons accruing a rate and other cash
flows, since only the former contribute to the BPS. Then, it has to
call the Coupon
interface to obtain the data needed for the
calculation.
Of course, this could be done by trying a dynamic_pointer_cast
on
each cash flow and adding terms to the result when the cast
succeeds. However, casting is somewhat frowned upon in the high
society of programming—especially so if we were to chain if
clauses to discriminate between several cash-flow types.
Instead of explicit casts, we implemented the bps
method by means of
the Acyclic Visitor pattern [1]. Mind you, this was not an obvious
choice. On the one hand, explicit casts can litter the code and make
it less clear. On the other hand, the Visitor pattern is one of the
most debated, and it’s definitely not a simple one. Depending on the
function, either casts or a visitor might be the most convenient
choice. My advice (which I’m going to disregard it in the very next
paragraph) is to have a strong bias towards simplicity.
In the case of bps
, casts might in fact have sufficed; the resulting
code would have been something like
which is not that bad a violation of object-oriented
principles—and, as you’ll see in a minute, is simpler than the
visitor-based implementation. One reason why we chose to use a visitor
was to use bps
as an example for implementing more complex
functions, for which casts would add too much scaffolding to the
code. (Other reasons might have included glamour or
overthinking—I know I’ve been guilty of them before. Nowadays,
I’d probably use a simple cast. But I think that having an example is
still reason enough to keep the current implementation.)
The implementation of the bps
method is sketched in listing
4.14. For the time being, I’ll gloss over the mechanics of the Acyclic
Visitor pattern (the details will be in a future post) and just
describe the steps we’ve taken to use it.
Listing 4.14: Sketch of the CashFlows::bps
method.
Our visitor—the BPSCalculator
class—inherits from a few
classes. The first is the AcyclicVisitor
class, needed so that our
class matches the interface of the CashFlow::accept
method. The
others are instantiation of the Visitor
class template specifying
what different kind of cash flows should be processed. Then, the class
implements different overloads of a visit
method. The overload
taking a Coupon
will be used for all cash-flows inheriting from such
class, and will access its interface to calculate the term to add to
the result. The overload taking a CashFlow
does nothing and will be
used as a fallback for all other cash flows.
At this point, the class can work with the pattern implementation
coded in the cash-flow classes. The bps
method is written as a loop
over the cash flows, similar to the one in the npv
method. An
instance of our visitor is created before starting the loop. Then, for
each coupon, the accept
method is called and passed the visitor as
an argument; each time, this causes the appropriate overload of
visit
to be called, adding terms to the result for each coupon and
doing nothing for any other cash flow. At the end, the result is
fetched from the visitor and returned.
As I said, it’s not simple; the same code using a cast would probably be half as long. However, the scaffolding needed for implementing a visitor remains pretty much constant; functions that need to discriminate between several kinds of cash flow can follow this example and end up with a higher signal-to-noise ratio.
Example: fixed-rate bonds
In this example, I’ll try and bolt the cash-flow machinery on the
pricing-engine framework. The instrument I target is the fixed-rate
bond; but hindsight being 20/20, I’ll put most code in a base Bond
class (as it turns out, most calculations are generic enough that they
work for any bond). Derived classes such as FixedRateBond
will
usually just contain code for building their specific cash flows.
Naturally, a user will expect to retrieve a great many values from a
bond instance: clean and dirty prices, accrued amount, yield, whatever
strikes a trader’s fancy. The Bond
class will need methods to return
such values. Part of the resulting interface is shown in listing 4.15;
you can look at the class declaration in the library for a full list
of the supported methods.
Listing 4.15: Partial interface of the Bond
class.
Now, if all the corresponding calculations were to be delegated to a
pricing engine, this would make for a long but fairly uninteresting
example. However, this is not the case. A very few calculations, such
as the one returning the settlement value of the bond, are depending
on the chosen model and will be performed by the pricing engine. Other
calculations are independent of the model (in a sense—I’ll
explain presently) and can be performed by the Bond
class itself,
avoiding duplication in the possible several engines.
As a consequence, the set of data in the Bond::results
class (and
the corresponding mutable data members in the Bond
class itself) is
pretty slim; in fact, it consists only of the settlement value, i.e.,
the sum of future cash flows discounted to the settlement date of the
bond. (The NPV is distinct from the settlement value in that the
former discounts the cash flows to the reference date of the discount
curve.) The settlementValue
method, shown in listing 4.16 with the
others I’ll be discussing here, is like most methods that rely on a
pricing engine: it triggers calculation if needed, checks that the
engine assigned a value to the data member, and returns it.
Listing 4.16: Partial implementation of the Bond
class.
Other calculations are of two or three kinds, all represented in the
listing. Methods of the first kind return static information; they’re
not necessarily simple inspectors, but in any case the returned
information is determined and doesn’t depend on a given model. One
such method, shown in the listing, is the settlementDate
method
which returns the settlement date corresponding to a given evaluation
date. First, if no date is given, the current evaluation date is
assumed; then, the evaluation date is advanced by the stored number of
settlement days; and finally, we check that the result is not earlier
than the issue date (before which the bond cannot be traded). Another
such method, not shown, is the notional
method which returns the
notional of a bond (possibly amortizing) at a given date.
The second kind of methods return information such as the clean and
dirty price of the bond. Of course such values depend on the model
used, so that in principle they should be calculated by the engine;
but in practice, they can be calculated from the settlement value
independently of how it was obtained. One such method, shown in the
listing, is the dirtyPrice
method; it calculates the dirty price of
the bond by taking the settlement value and normalizing it to 100
through a division by the current notional. If the current notional is
null (and thus cannot divide the settlement value) then the bond is
expired and the dirty price is also null. The cleanPrice
method,
also shown, takes the dirty price and subtracts the accrued amount to
obtain the clean price. Finally, the yield
method takes the dirty
price, the cash flows, the settlement date, and the passed parameters
and calls the appropriate method of the CashFlows
class to return
the bond yield.
A third kind of methods is similar to the second; they return results
based on other values. The difference is simply that the input values
are passed by the caller, rather than taken from engines: examples of
such methods are the overloads of the cleanPrice
and yield
shown
at the end of listing 4.15. The first one takes a yield and returns
the corresponding clean price; the second one does the opposite. They,
too, forward the calculation to a method of the CashFlows
class.
The final step to obtain a working Bond
class is to provide a
pricing engine. A simple one is sketched in listing 4.17. Its
constructor takes a discount curve and stores it; its calculate
method passes the stored curve and the cash flows of the bond to the
CashFlows::npv
method. By passing as discount date the settlement
date of the bond, it obtains its settlement value; by passing the
reference date of the discount curve, its NPV.
Listing 4.17: Sketch of the DiscountingBondEngine
class.
All that remains is to provide the means to instantiate bonds—for the purpose of our example, fixed-rate bonds. The needed class is shown in listing 4.18.
Listing 4.18: Implementation of the FixedRateBond
class.
It doesn’t have much to do; its constructor takes the needed
parameters and uses them to instantiate the cashflows. Apart for maybe
a few inspectors, no other methods are needed as the Bond
machinery
can take over the calculations.
Now, to quote Craig Ferguson: what did we learn on the show tonight?
The example showed how to code common calculations in an instrument
class (here, the Bond
class) independently of the pricing
engine. The idea seems sound—the calculations should be
replicated in each engine otherwise—but I should note that it
comes with a disadvantage. In the design shown here (which is also the
one currently implemented in the library) the calculations outside the
pricing engine cannot use the caching mechanism described in this
post and
this other one. Thus, for
instance, the current notional is calculated and the settlement value
is normalized again and again every time the dirtyPrice
method is
called.
There would be a couple of ways out of this, neither exactly
satisfactory. The first one would be to group the calculations
together in the performCalculation
method, after the engine worked
its magic. Something like:
However, this has the problem that all calculations are performed, including slower ones such as the yield calculation (which needs iterative root solving). This would not be convenient if one, for instance, just wanted bond prices. (Calculating all results might also be a problem for pricing engines. If performance is paramount—and, of course, depending on one’s application—one might consider adding “light” versions of the existing engines that only calculate some results.)
A second alternative would be to add some kind of caching mechanism to each method. This would avoid unneeded calculations, but could get messy very quickly. It would require to store a separate observer for each method, register all of them with the appropriate observables, and check in each method whether the corresponding observer was notified. If this kind of performance was required, I’d probably do this on a per-method basis, and only after profiling the application.
All in all, though, I’d say that the current compromise seems acceptable. The repeated calculations are balanced by simpler (and thus more maintainable) code. If one were to need more caching, the exiting code can be patched to obtain it.
Bibliography
[1] R.C. Martin, Acyclic Visitor. In Pattern Languages of Program Design 3. Addison-Wesley, 1997.