A bit of trivia: one week ago, on April 20th, the QuantLib repository was forked for the 400th time (and they keep coming: we’re up to 407 already as I write this post). Kudos to GitHub user donglijiujiu—which doesn’t win any prize, apart from this shout-out.
This week: the final part of the series on tree that started a few weeks ago.
As you might have guessed, a tree-based pricing engine will perform few actual computations; its main job will rather be to instantiate and drive the needed discretized asset and lattice. (If you’re pattern-minded, you can have your pick here. This implementation has suggestions of the Adapter, Mediator, or Facade pattern, even though it doesn’t match any of them exactly.)
Example: callable fixed-rate bonds
As an example, I’ll sketch the implementation of a tree-based pricing
engine for callable fixed-rate bonds. For the sake of brevity, I’ll
skip the description of the
CallableBond class. (To be specific, the
class name should be
CallableFixedRateBond; but that would get old
very quickly here, so please allow me to use the shorter name.)
Instead, I’ll just show its inner
which act as its interface with the pricing engine and which you can
see in the listing below together with the corresponding
class. If you’re interested in a complete implementation, you can look
for it in QuantLib’s experimental folder.
Now, let’s move into engine territory. In order to implement the
behavior of the instrument, we’ll need a discretized asset; namely,
DiscretizedCallableBond class, shown in the next listing.
To prevent much aggravation, its constructor takes and stores an
instance of the
arguments class. This avoids having to spell out the
list of needed data in at least three places: the declaration of the
data members, the constructor, and the client code that instantiates
the discretized asset. Besides the
arguments instance, the
constructor is also passed a reference date and a day counter that are
used in its body to convert the several bond dates into corresponding
times. (The conversion is somewhat verbose, which suggests that we
might be missing an abstraction here. However, “time converter” sounds
a bit too vague. If you find it, please do let me know. The thing has
been bugging me for a while.)
Next comes the required
DiscretizedAsset interface. The
mandatoryTimes method collects the redemption time, the coupon
times, and the callability times filtering out the negative ones; and
reset method resizes the array of the values, sets each one to
the redemption amount, and proceeds to perform the needed
adjustments—that is, the more interesting part of the class.
Being rather specialized, it is pretty unlikely that this class will
be composed with others; therefore, it doesn’t really matter in this
case whether the adjustments go into
postAdjustValuesImpl. However, for sake of illustration, I’ll
separate the callability from the coupon payments and manage them as
pre- and post-adjustment, respectively.
preAdjustValuesImpl loops over the callability times, checks
whether any of them equals the current time, and calls the
applyCallability} method if this is the case. The
postAdjustValuesImpl does the same, but checking the coupon times
and calling the
addCoupon method instead.
applyCallability method is passed the index of the callability
being exercised; it checks its type (both callable and puttable bonds
are supported) and sets the value at each node to the value after
exercise. The logic is simple enough: at each node, given the
estimated value of the rest of the bond (that is, the current asset
value) and the exercise premium, the issuer will choose the lesser of
the two values while the holder will choose the greater. The
addCoupon method is much simpler, and just adds the coupon amount to
each of the values.
As you might have noticed, this class assumes that the exercise dates coincide with the coupon dates; it won’t work if an exercise date is a few days before a coupon payment (the coupon amount would be added to the asset values before the exercise condition is checked). Of course, this is often the case, and it should be accounted for. Currently, the library implementation sidesteps the problem by adjusting each exercise date so that it equals the nearest coupon date. A better choice would be to detect which coupons are affected; each of them would be put into a new asset, rolled back until the relevant exercise time, and added after the callability adjustment.
Finally, the listing below shows the
Its constructor takes and stores a handle to a short-rate model that will provide the lattice, the total number of time steps we want the lattice to have, and an optional reference date and day counter; the body just registers to the handle.
calculate method is where everything happens. By the time it is
called, the engine arguments have been filled by the instrument, so
that base is covered; the other data we need are a date and a day
counter for time conversion. Not all short-rate models can provide
them, so, in a boring few lines of code not shown here, the engine
tries to downcast the model to some specific class that does; if it
fails, it falls back to using the ones optionally passed to the
At that point, the actual calculations can begin. The engine instantiates the discretized bond, asks it for its mandatory times, and uses them to build a time grid; then, the grid is passed to the model which returns a corresponding lattice based on the short-rate dynamics. All that remains is to initialize the bond at its redemption time (which in the current code is recalculated explicitly, but could be retrieved as the largest of the mandatory times), roll it back to the present time, and read its value.