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.

    Real CashFlows::npv(const Leg& leg,
                        const YieldTermStructure& discountCurve,
                        Date settlementDate) {
        ...checks...

        Real totalNPV = 0.0;
        for (Size i=0; i<leg.size(); ++i) {
            if (!leg[i]->hasOccurred(settlementDate))
                totalNPV += leg[i]->amount() *
                            discountCurve.discount(leg[i]->date());
        }
        return totalNPV;
    }

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

    for (Size i=0; i<leg.size(); ++i) {
       if (shared_ptr<Coupon> c =
                        dynamic_pointer_cast<Coupon>(leg[i]) {
            // do something with c
        }
    }

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.

    namespace {

        const Spread basisPoint_ = 1.0e-4;

        class BPSCalculator : public AcyclicVisitor,
                              public Visitor<CashFlow>,
                              public Visitor<Coupon> {
          public:
            BPSCalculator(const YieldTermStructure& discountCurve)
            : discountCurve_(discountCurve), result_(0.0) {}
            void visit(Coupon& c) {
                result_ += c.nominal() *
                           c.accrualPeriod() *
                           basisPoint_ *
                           discountCurve_.discount(c.date());
            }
            void visit(CashFlow&) {}
            Real result() const {
                return result_;
            }
          private:
            const YieldTermStructure& discountCurve_;
            Real result_;
        };

    }

    Real CashFlows::bps(const Leg& leg,
                        const YieldTermStructure& discountCurve,
                        Date settlementDate) {
        ...checks...

        BPSCalculator calc(discountCurve);
        for (Size i=0; i<leg.size(); ++i) {
            if (!leg[i]->hasOccurred(settlementDate))
                leg[i]->accept(calc);
        }
        return calc.result();
    } 

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.

    class Bond : public Instrument {
      public:
        class arguments;
        class results;
        class engine;

        Bond(...some data...);

        bool isExpired() const;
        bool isTradable(Date d = Date()) const;
        Date settlementDate(Date d = Date()) const;
        Real notional(Date d = Date()) const;

        const Leg& cashflows() const;
        ...other inspector...

        Real cleanPrice() const;
        Real dirtyPrice() const;
        Real accruedAmount(Date d = Date()) const;
        Real settlementValue() const;

        Rate yield(const DayCounter& dc,
                   Compounding comp,
                   Frequency freq,
                   Real accuracy = 1.0e-8,
                   Size maxEvaluations = 100) const;

        ...other methods...

        Real cleanPrice(Rate yield,
                        const DayCounter& dc,
                        Compounding comp,
                        Frequency freq,
                        Date settlementDate = Date()) const;

        Rate yield(Real cleanPrice,
                   const DayCounter& dc,
                   Compounding comp,
                   Frequency freq,
                   Date settlementDate = Date(),
                   Real accuracy = 1.0e-8,
                   Size maxEvaluations = 100) const;
      protected:
        void setupExpired() const;
        void setupArguments(PricingEngine::arguments*) const;
        void fetchResults(const PricingEngine::results*) const;

        Leg cashflows_;
        ...other data members...

        mutable Real settlementValue_;

    }; 

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.

    Real Bond::settlementValue() const {
        calculate();
        QL_REQUIRE(settlementValue_ != Null<Real>(),
                   "settlement value not provided");
        return settlementValue_;
    }

    Date Bond::settlementDate(Date d) const {
        if (d == Date())
            d = Settings::instance().evaluationDate();
        Date settlement =
            calendar_.advance(d, settlementDays_, Days);
        return std::max(settlement, issueDate_);
    }

    Real Bond::dirtyPrice() const {
        Real currentNotional = notional(settlementDate());
        if (currentNotional == 0.0)
            return 0.0;
        else
            return settlementValue()*100.0/currentNotional;
    }

    Real Bond::cleanPrice() const {
        return dirtyPrice() - accruedAmount(settlementDate());
    }

    Rate Bond::yield(const DayCounter& dc,
                     Compounding comp,
                     Frequency freq,
                     Real accuracy,
                     Size maxEvaluations) const {
        Real currentNotional = notional(settlementDate());
        if (currentNotional == 0.0)
            return 0.0;

        return CashFlows::yield(cashflows(), dirtyPrice(),
                                dc, comp, freq, false,
                                settlementDate(), settlementDate(),
                                accuracy, maxIterations);
    } 

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.

    class DiscountingBondEngine : public Bond::engine {
      public:
        DiscountingBondEngine(
              const Handle<YieldTermStructure>& discountCurve);
        void calculate() const {
            QL_REQUIRE(!discountCurve_.empty(),
                       "discounting term structure handle is empty");

            results_.settlementValue =
                CashFlows::npv(arguments_.cashflows,
                               **discountCurve_,
                               false,
                               arguments_.settlementDate);

            // same for results_.value, but discounting the cash flows
            // to the reference date of the discount curve.
        }
      private:
        Handle<YieldTermStructure> discountCurve_;
    };

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.

    class FixedRateBond : public Bond {
      public:
        FixedRateBond(Natural settlementDays,
                      const Calendar& calendar,
                      Real faceAmount,
                      const Date& startDate,
                      const Date& maturityDate,
                      const Period& tenor,
                      const std::vector<Rate>& coupons,
                      const DayCounter& accrualDayCounter,
                      BusinessDayConvention accrualConvention =
                                                           Following,
                      BusinessDayConvention paymentConvention =
                                                           Following,
                      Real redemption = 100.0,
                      const Date& issueDate = Date(),
                      const Date& firstDate = Date(),
                      const Date& nextToLastDate = Date(),
                      DateGeneration::Rule rule =
                                            DateGeneration::Backward)
        : Bond(...needed arguments...) {

            Schedule schedule = MakeSchedule()
                                .from(startDate).to(maturityDate)
                                .withTenor(tenor)
                                .withCalendar(calendar)
                                .withConvention(accrualConvention)
                                .withRule(rule)
                                .withFirstDate(firstDate)
                                .withNextToLastDate(nextToLastDate);

            cashflows_ = FixedRateLeg(schedule)
                         .withNotionals(faceAmount)
                         .withCouponRates(coupons, accrualDayCounter)
                         .withPaymentAdjustment(paymentConvention);

            shared_ptr<CashFlow> redemption(
                new SimpleCashFlow(faceAmount*redemption,
                                   maturityDate));
            cashflows.push_back(redemption);
        }
    };

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:

    void Bond::performCalculations() const {
        Instrument::performCalculation();
        Real currentNotional = ...;
        dirtyPrice_ = settlementValue_*100.0/currentNotional;
        cleanPrice_ = ...;
        yield_ = ...;
        // whatever else needs to be calculated.
    }

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.