Hello everybody.

This week, the second part of a series on the QuantLib tree framework that started in the previous post. As usual, it’s taken from my book.

In case you missed it: we set the dates for my next Introduction to QuantLib Development course, which will be in London from June 29th to July 1st. More info at this link. You can get an early-bird discount until April 30th.

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.

Example: discretized bonds

Vanilla bonds (zero-coupon, fixed-rate and floating-rate) are simple enough to work as first examples, but at the same time provide a range of features large enough for me to make a few points.

Well, maybe not the zero-coupon bond. It’s probably the simplest possible asset (bar one with no payoff) so it’s not that interesting on its own; however, it’s going to be useful as a helper class in the examples that follow, since it provides a way to estimate discount factors on the lattice.

Its implementation is shown in the following listing.

    class DiscretizedDiscountBond : public DiscretizedAsset {
      public:
        DiscretizedDiscountBond() {}
        void reset(Size size) {
            values_ = Array(size, 1.0);
        }
        std::vector<Time> mandatoryTimes() const {
            return std::vector<Time>();
        }
    };

It defines no mandatory times (because it will be happy to be initialized at any time you choose), it performs no adjustments (because nothing ever happens during its life), and its reset method simply fills each value in the array with one unit of currency. Thus, if you initialize an instance of his class at a time \( T \) and roll it back on a lattice until an earlier time \( t \), the array values will then equal the discount factors from \( T \) to \( t \) as seen from the corresponding lattice nodes.

Things start to get more interesting when we turn to fixed-rate bonds. QuantLib doesn’t provide discretized fixed-rate bonds at this time; the listing below shows a simple implementation which works as an example, but should still be improved to enter the library.

    class DiscretizedFixedRateBond : public DiscretizedAsset {
      public:
        DiscretizedFixedRateBond(const vector<Time>& paymentTimes,
                                 const vector<Real>& coupons,
                                 Real redemption)
        : paymentTimes_(paymentTimes), coupons_(coupons),
          redemption_(redemption) {}

        vector<Time> mandatoryTimes() const {
            return paymentTimes_;
        }
        void reset(Size size) {
            values_ = Array(size, redemption_);
            adjustValues();
        }
      private:
        void postAdjustValuesImpl() {
            for (Size i=0; i<paymentTimes_.size(); i++) {
                Time t = paymentTimes_[i];
                if (t >= 0.0 && isOnTime(t)) {
                    addCoupon(i);
                }
            }
        }
        void addCoupon(Size i) {
            values_ += coupons_[i];
        }

        vector<Time> paymentTimes_;
        vector<Real> coupons_;
        Real redemption_;
    };

The constructor takes and stores a vector of times holding the payment schedule, the vector of the coupon amounts, and the amount of the redemption. Note that the type of the arguments is different from what the corresponding instrument class is likely to store (say, a vector of CashFlow instances); this implies that the pricing engine will have to perform some conversion work before instantiating the discretized asset.

The presence of a payment schedule implies that, unlike the zero-coupon bond above, this bond cannot be instantiated at just any time; and in fact, this class implements the mandatoryTimes method by returning the vector of the payment times. Rollback on the lattice will have to stop at each such time, and initialization will have to be performed at the maturity time of the bond, i.e., the latest of the returned times. When one does so, the reset method will fill each value in the array with the redemption amount and then call the adjustValues method, which will take care of the final coupon.

In order to enable adjustValues to do so, this class overrides the virtual postAdjustValuesImpl method. (Why the post- version of the method and not the pre-, you say? Bear with me a bit longer: all will become clear.) The method loops over the payment times and checks, by means of the probably poorly named isOnTime method, whether any of them equals the current asset time. If this is the case, we add the corresponding coupon amount to the asset values. For readability, the actual work is factored out in the addCoupon method. The coupon amounts will be automatically discounted as the asset is rolled back on the lattice.

Finally, let’s turn to the floating-rate bond, shown in the listing that follows; this, too, is a simplified implementation.

    class DiscretizedFloatingRateBond : public DiscretizedAsset {
      public:
        DiscretizedFloatingRateBond(const vector<Time>& paymentTimes,
                                    const vector<Time>& fixingTimes,
                                    Real notional)
        : paymentTimes_(paymentTimes), fixingTimes_(fixingTimes),
          notional_(notional) {}

        vector<Time> mandatoryTimes() const {
            vector<Time> times = paymentTimes_;
            std::copy(fixingTimes_.begin(), fixingTimes_.end(),
                      back_inserter(times));
            return times;
        }
        void reset(Size size) {
            values_ = Array(size, notional_);
            adjustValues();
        }
      private:
        void preAdjustValuesImpl() {
            for (Size i=0; i<fixingTimes_.size(); i++) {
                Time t = fixingTimes_[i];
                if (t >= 0.0 && isOnTime(t)) {
                    addCoupon(i);
                }
            }
        }
        void addCoupon(Size i) {
            DiscretizedDiscountBond bond;
            bond.initialize(method(), paymentTimes_[i]);
            bond.rollback(time_);

            for (Size j=0; j<values_.size(); j++) {
                 values_[j] += notional_ * (1.0 - bond.values()[j]);
            }
        }

        vector<Time> paymentTimes_;
        vector<Time> fixingTimes_;
        Real notional_;
    };

The constructor takes and stores the vector of payment times, the vector of fixing times, and the notional of the bond; there are no coupon amounts, since they will be estimated during the calculation. For simplicity of implementation, we’ll assume that the accrual time for the i-th coupon equals the time between its fixing time and its payment time.

The mandatoryTimes method returns the union of payment times and fixing times, since we’ll need to work on both during the calculations. The reset method is similar to the one for fixed-rate bonds, and fills the array with the redemption value (which equals the notional of the bond) before calling adjustValues.

Adjustment is performed in the overridden preAdjustValuesImpl method. (Yes, the pre- version. Patience.) It loops over the fixing times, checks whether any of them equals the current time, and if so calls addCoupon method.

Now, like the late Etta James in one of her hits, you’d be justified in shouting “Stop the wedding”. Of course the coupon should be added at the payment date, right? Well, yes; but the problem is that we’re going backwards in time. In general, at the payment date we don’t have enough information to add the coupon; it can only be estimated based on the value of the rate at an earlier time that we haven’t yet reached. Therefore, we have to keep rolling back on the lattice until we get to the fixing date, at which point we can calculate the coupon amount and add it to the bond. In this case, we’ll have to take care ourselves of discounting from the payment date, since we passed that point already.

That’s exactly what the addCoupon method does. First of all, it instantiates a discount bond at the payment time \( T \) and rolls it back to the current time, i.e., the fixing date \( t \), so that its value equal at the \( j \)-th node the discount factors \( D_j \) between \( t \) and \( T \). From those, we could estimate the floating rates \( r_j \) (since it must hold that \( 1 + r_j(T-t) = 1/D_j \)) and then the coupon amounts; but with a bit of algebra, we can find a simpler calculation. The coupon amount \( C_j \) is given by \( Nr_j(T-t) \), with \( N \) being the notional; and since the relation above tells us that \( r_j(T-t) = 1/D_j - 1 \), we can substitute that to find that \( C = N(1/D_j - 1) \). Now, remember that we already rolled back to the fixing date, so if we add the amount here we also have to discount it because it won’t be rolled back from the payment date. This means that we’ll have to multiply it by \( D_j \), and thus the amount to be added to the \( j \)-th value in the array is simply \( C_j = N(1/D_j - 1)D_j = N(1-D_j) \). The final expression is the one that can be seen in the implementation of addCoupon.

As you probably noted, the above hinges on the assumption that the accrual time equals the time between payment and fixing time. If this were not the case, the calculation would no longer simplify and we’d have to change the implementation; for instance, we might instantiate a first discount bond at the accrual end date to estimate the floating rate and the coupon amount, and a second one at the payment date to calculate the discount factors to be used when adding the coupon amount to the bond value. Of course, the increased accuracy would cause the performance to degrade since addCoupon would roll back two bonds, instead of one. You can choose either implementation based on your requirements.

Example: discretized option

Sorry to have kept you waiting, folks. Here is where I finally explain the pre- vs post-adjustment choice, after the previous example helped me put my ducks in a row. I’ll do so by showing an example of an asset class (the DiscretizedOption class, shown in the listing below) that can be used to wrap an underlying asset and obtain an option to enter the same: for instance, it could take a swap and yield a swaption. The implementation shown here is a slightly simplified version of the one provided by QuantLib, since it assumes a Bermudan exercise (or European, if one passes a single exercise time). Like the implementation in the library, it also assumes that there’s no premium to pay in order to enter the underlying deal.

    class DiscretizedOption : public DiscretizedAsset {
      public:
        DiscretizedOption(
                  const shared_ptr<DiscretizedAsset>& underlying,
                  const vector<Time>& exerciseTimes)
        : underlying_(underlying), exerciseTimes_(exerciseTimes) {}

        vector<Time> mandatoryTimes() const {
            vector<Time> times = underlying_->mandatoryTimes();
            for (Size i=0; i<exerciseTimes_.size(); ++i)
                if (exerciseTimes_[i] >= 0.0)
                    times.push_back(exerciseTimes_[i]);
            return times;
        }
        void reset(Size size) {
            QL_REQUIRE(method() == underlying_->method(),
                       "option and underlying were initialized on "
                       "different lattices");
            values_ = Array(size, 0.0);
            adjustValues();
        }
      private:
        void postAdjustValuesImpl() {
            underlying_->partialRollback(time());
            underlying_->preAdjustValues();
            for (Size i=0; i<exerciseTimes_.size(); i++) {
                Time t = exerciseTimes_[i];
                if (t >= 0.0 && isOnTime(t))
                    applyExerciseCondition();
            }
            underlying_->postAdjustValues();
        }
        void DiscretizedOption::applyExerciseCondition() {
            for (Size i=0; i<values_.size(); i++)
                values_[i] = std::max(underlying_->values()[i],
                                      values_[i]);
        }

        shared_ptr<DiscretizedAsset> underlying_;
        vector<Time> exerciseTimes_;
    };

Onwards. The constructor takes and, as usual, stores the underlying asset and the exercise times; nothing to write much about.

The mandatoryTimes method takes the vector of times required by the underlying and adds the option’s exercise times. Of course, this is done so that both the underlying and the option can be priced on the same lattice; the sequence of operations to get the option price will be something like:

    underlying = shared_ptr<DiscretizedAsset>(...);
    option = shared_ptr<DiscretizedAsset>(
                new DiscretizedOption(underlying, exerciseTimes));
    grid = TimeGrid(..., option->mandatoryTimes());
    lattice = shared_ptr<Lattice>(new SomeLattice(..., grid, ...));
    underlying->initialize(lattice, T1);
    option->initialize(lattice, T2);
    option->rollback(t0);
    NPV = option->presentValue();

in which, first, we instantiate both underlying and option and retrieve the mandatory times from the latter; then, we create the lattice and initialize both assets (usually at different times, e.g., the maturity date for a swap and the latest exercise date for the corresponding swaption); and finally, we roll back the option and get its price. As we’ll see in a minute, the option also takes care of rolling the underlying back as needed.

Back to the class implementation. The reset method performs the sanity check that underlying and option were initialized on the same lattice, fills the values with zeroes (what you end up with if you don’t exercise), and then calls adjustValues to take care of a possible exercise.

Which brings us to the crux of the matter, i.e., the postAdjustValuesImpl method. The idea is simple enough: if we’re on an exercise time, we check whether keeping the option is worth more than entering the underlying asset. To do so, we roll the underlying asset back to the current time, compare values at each node, and set the option value to the maximum of the two; this latest part is abstracted out in the applyExerciseCondition method.

The tricky part of the problem is that the underlying might need to perform an adjustment of its own when rolled back to the current time. Should this be done before or after the option looks at the underlying values?

It depends on the particular adjustment. Let’s look at the bonds in the previous example. If the underlying is a discretized fixed-rate bond, and if the current time is one of its payment times, it needs to adjust its values by adding a coupon. This coupon, though, is being paid now and thus is no longer part of the asset if we exercise and enter it. Therefore, the decision to exercise must be based on the bond value without the coupon; i.e., we must call the applyExerciseCondition method before adjusting the underlying.

The discretized floating-rate bond is another story. It adjusts the values if the current time is one of its fixing times; but in this case the corresponding coupon is just starting and will be paid at the end of the period, and so must be added to the bond value before we decide about exercise. Thus, the conclusion is the opposite: we must call applyExerciseCondition after adjusting the underlying.

What should the option do? It can’t distinguish between the two cases, since it doesn’t know the specific behavior of the asset it was passed; therefore, it lets the underlying itself sort it out. First, it rolls the underlying back to the current time, but without performing the final adjustment (that’s what the partialRollback method does); instead, it calls the underlying’s preAdjustValues method. Then, if we’re on an exercise time, it performs its own adjustment; and finally, it calls the underlying’s postAdjustValues method.

This is the reason the DiscretizedAsset class has both a preAdjustValues and a postAdjustValues method; they’re there so that, in case of asset composition, the underlying can choose on which side of the fence to be when some other adjustment (such as an exercise) is performed at the same time. In the case of our previous example, the fixed-rate bond will add its coupon in postAdjustValues and have it excluded from the future bond value, while the floating-rate bond will add its coupon in preAdjustValues and have it included.

Unfortunately, this solution is not very robust. For instance, if the exercise dates were a week or two before the coupon dates (as is often the case) the option would break for fixed-rate coupons, since it would have no way to stop them from being added before the adjustment. The problem can be solved: in the library, this is done for discretized interest-rate swaps by adding fixed-rate coupons on their start dates, much in the same way as floating-rate coupons. Another way to fix the issue would be to roll the underlying back only to the date when it’s actually entered, then to make a copy of it and roll the copy back to the exercise date without performing any adjustment. Both solutions are somewhat clumsy at this time; it would be better if QuantLib provided some means to do it more naturally.