Monday, February 10, 2014

Chapter 4, part 4 of 5: generating cash-flow sequences

Hello everybody.

This week, the fourth part of the series on chapter 4 of my book that started in this post.

Oh, and you can still register for my Introduction to QuantLib Development course (I won't bore you with the details again; I've done it in previous posts).

Follow me on Twitter if you want to be notified of new posts, or add me to your circles, or subscribe via RSS: the widgets for that are in the sidebar, at the top right of the page. Also, make sure to check my Training page.

Generating cash-flow sequences

Coupons seldom come alone. They come packed in legs—and with them, comes trouble for the library writer. Of course, we wanted to provide functions to build sequences of coupons based on a payment schedule; but doing so presented a few interface problems.

At first, we wrote such facilities in the obvious way; that is, as free-standing functions that took all the needed parameters (quite a few, in all but the simplest cases), built the corresponding leg, and returned it. However, the result was quite uncomfortable to call from client code: in order to build a floating-rate leg, the unsuspecting library user (that's you) had to write code like:
    vector<shared_ptr<CashFlow> > leg =
IborLeg(schedule, index,
std::vector<Real>(1, 100.0),
std::vector<Real>(1, 1.0),
std::vector<Rate>(1, 0.01),
std::vector<Real>(),
index->dayCounter(),
true);


The above has two problems. The first is a common one for functions taking a lot of parameters: unless we look up the function declaration, it's not clear what parameters we are passing (namely, a notional of 100, a unit gearing, a spread of 40 bps, a floor of 1 %, and fixings in arrears—did you guess them?) The problem might be reduced by employing named variables, such as:
    std::vector<Real> notionals(1, 100.0);
std::vector<Real> unit_gearings(1, 1.0);
std::vector<Real> no_caps;
bool inArrears = true;

but this increases the verbosity of the code, as the above definitions must be added before the function call. Moreover, although several parameters have meaningful default values, they must be all specified if one wants to change a parameter that appears later in the list; for instance, specifying in-arrears fixings in the above call forced us to also pass a day counter and a null vector of cap rates.

The second problem is specific to our domain. Beside the coupon dates, other parameters can possibly change between one coupon and the next; e.g, the coupon notionals could decrease according to an amortizing plan, or a cap/floor corridor might follow the behavior of the expected forward rates. Since we need to cover the general case, the coupon builders must take all such parameters as vectors. However, this forces us to verbosely instantiate vectors even when such parameters don't change—as it happens, most of the times. (By using overloading, we could accept both vectors and single numbers; but for N parameters, we'd need 2^N overloads. This becomes unmanageable very quickly.)

Both problems have the same solution. It is an idiom which has been known for quite a while in the C++ circles as the Named Parameter Idiom (it is described as an established idiom in C++ FAQs [1], published in 1998) and was made popular in the dynamic-language camp by Martin Fowler (of Refactoring fame) under the name of Fluent Interface [2]. The idea is simple enough. Instead of a function, we'll use an object to build our coupons. Instead of passing the parameters together in a single call, we'll pass them one at a time to explicitly named methods. This will enable us to rewrite the function call in the previous example less verbosely and more clearly as
    vector<shared_ptr<CashFlow> > leg =
IborLeg(schedule, index)
.withNotionals(100.0)
.withFloors(0.01)
.inArrears();


A partial implementation of the IborLeg class is shown in listing 4.12.

Listing 4.12: Partial implementation of the IborLeg class.
    class IborLeg {
public:
IborLeg(const Schedule& schedule,
const shared_ptr<IborIndex>& index);
IborLeg& withNotionals(Real notional);
IborLeg& withNotionals(const vector<Real>& notionals);
IborLeg& withPaymentDayCounter(const DayCounter&);
IborLeg& withFixingDays(Natural fixingDays);
IborLeg& withFixingDays(const vector<Natural>& fixingDays);
IborLeg& withGearings(Real gearing);
IborLeg& withGearings(const vector<Real>& gearings);
IborLeg& withCaps(Rate cap);
IborLeg& withCaps(const vector<Rate>& caps);
IborLeg& withFloors(Rate floor);
IborLeg& withFloors(const vector<Rate>& floors);
IborLeg& inArrears(bool flag = true);
IborLeg& withZeroPayments(bool flag = true);
operator Leg() const;
private:
Schedule schedule_;
shared_ptr<IborIndex> index_;
vector<Real> notionals_;
DayCounter paymentDayCounter_;
vector<Natural> fixingDays_;
vector<Real> gearings_;
vector<Rate> caps_, floors_;
bool inArrears_, zeroPayments_;
};

IborLeg::IborLeg(const Schedule& schedule,
const shared_ptr<IborIndex>& index)
: schedule_(schedule), index_(index),
paymentDayCounter_(index->dayCounter()),
inArrears_(false), zeroPayments_(false) {}

IborLeg& IborLeg::withNotionals(Real notional) {
notionals_ = vector<Real>(1,notional);
return *this;
}

IborLeg& IborLeg::withNotionals(const vector<Real>& notionals) {
notionals_ = notionals;
return *this;
}

IborLeg& IborLeg::withPaymentDayCounter(
const DayCounter& dayCounter) {
paymentDayCounter_ = dayCounter;
return *this;
}

IborLeg::operator Leg() const {

Leg cashflows = ...

if (caps_.empty() && floors_.empty() && !inArrears_) {
shared_ptr<FloatingRateCouponPricer> pricer(
new BlackIborCouponPricer);
setCouponPricer(cashflows, pricer);
}

return cashflows;
}


The constructor takes just a couple of arguments, namely, those for which there's no sensible default and that can't be confused with other arguments of the same type. This restricts the set to the coupon schedule and the LIBOR index. The other data members are set to their default values, which can be either fixed ones or can depend on the passed parameters (e.g., the day counter for the coupon defaults to that of the index).

Any other leg parameters are passed through a setter. Each setter is unambiguously named, can be overloaded to take either a single value or a vector (for N parameters we'll need 2×N overloads, which is much more manageable than the alternative) and return a reference to the IborLeg instance so that they can be chained. After all parameters are set, assigning to a Leg triggers the relevant conversion operator, which contains the actual coupon generation (in itself, not so interesting; it instantiates a sequence of coupons, each with the dates and parameters picked from the right place in the stored vectors).

Finally, let me add a final note; not on this implementation, but to mention that the Boost folks provided another way to solve the same problem. Managing once again to do what seemed impossible, their Boost Parameter Library gives one the possibility to use named parameters in C++ and write function calls such as
    vector<shared_ptr<CashFlow> > leg =
IborLeg(schedule, index,
_notional = 100.0,
_floor = 0.01,
_inArrears = true);

I've been tempted to use the above in QuantLib, but the existing implementation based on the Named Parameter Idiom worked and needed less work from the developers. Just the same, I wanted to mention the Boost solution here for its sheer awesomeness.

Other coupons and further developments

Of course, other types of coupons can be implemented besides those shown here. On the one hand, the ones I described just begin to explore interest-rate coupons. The library provides a few others in the same style, such as those paying a constant-maturity swap rate; their pricer merely implements a different formula. Others, not yet implemented, would need additional design; for example, an interesting exercise you can try might be to generate a sequence of several cash flows obtaining their amounts from a single Monte Carlo simulation. (Hint: the pricers for the coupons would share a pointer to a single Monte Carlo model, as well as some kind of index—an integer would suffice—identifying the coupon and allowing to retrieve the correct payoff among the results.)

On the other hand, the same design can be used for other kinds of coupons besides interest-rate ones; this was done, for instance, for inflation-rate coupons. Of course, this begs the question of whether or not the design can be generalized to allow for different kind of indexes. On the whole, I'm inclined to wait for a little more evidence (say, a third and so-far unknown kind of coupon) before trying to refactor the classes.

Next time: cash-flow analysis and a final example.

Bibliography

[1] M.P. Cline, G. Lomow and M. Girou, C++ FAQs, 2nd edition. Addison-Wesley, 1998.
[2] M. Fowler, Fluent Interface. 2005.
[3] M. Fowler, K. Beck, J. Brant, W. Opdyke and D. Roberts, Refactoring: Improving the Design of Existing Code. Addison-Wesley, 1999.