Chapter 3, part 7 of 7; interest-rate volatilities
Welcome back, and happy new year.
I hope you all had a nice holiday. I had a nice Christmas, thanks for asking; and I finally managed to find a couple of free afternoons to finish chapter 3. This post is the last in a series of seven that covered this chapter and that started with this one.
Important news: thanks to MoneyScience, and in particular to Jacob Bettany, I’ll be in London from March 24th to 26th to teach another edition of my Introduction to QuantLib Development course. It is the course that I teach based on the contents of the Implementing QuantLib book, and you can find more information, a brochure and a booking form by clicking on this link. Yes, go ahead, click: I’ll wait right here.
Done? Thanks. I look forward to see you there: I’m told London is quite nice in spring. Ok, maybe. You can probably strike that last bit.
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, even if you can’t attend the course this time.
Interest-rate volatility structures
Last in our tour are interest-rate volatilities. There are three different hierarchies of them, each with its quirks.
The first hierarchy models cap and floor term volatilities; the base
class is the CapFloorTermVolatilityStructure
, shown in listing 3.19.
Listing 3.19: Interface of the CapFloorTermVolatilityStructure
class.
class CapFloorTermVolatilityStructure
: public VolatilityTermStructure {
public:
... // constructors, not shown
Volatility volatility(const Period& length, Rate strike,
bool extrapolate = false) const;
Volatility volatility(const Date& end, Rate strike,
bool extrapolate = false) const;
Volatility volatility(Time t, Rate strike,
bool extrapolate = false) const;
protected:
virtual Volatility volatilityImpl(Time length,
Rate strike) const = 0;
};
It is a straightforward application of the patterns seen so far, with one main difference; the volatility is not dependent on the exercise time, which is fixed to today’s time, but on the maturity of the strip of caplets or floorlets of which the instrument is composed.
The difference is semantic: volatility(t,strike)
has for this class
a different meaning, even though the interface and the implementation
are the same as for the other volatility classes. In turn, this has a
couple of small consequences on the interface: on the one hand,
there’s an additional overload of the volatility
method that takes
the length of the cap as a Period
instance, as seemed natural; and
on the other hand, the volatility doesn’t really measure the width of
the distribution of any variable at time t
, so the variance
method
was omitted.
The second hierarchy models the volatilities of single caplets and
floorlets. (There are ways to convert from cap to caplet volatilities,
of course, but I won’t cover them here. Look into QuantLib for the
OptionletStripper
class and its derived classes, if you’re
interested in their implementation.) Its base class is the
OptionletVolatilityStructure
class, shown in listing 3.20.
Listing 3.20: Interface of the OptionletVolatilityStructure
class.
class OptionletVolatilityStructure
: public VolatilityTermStructure {
public:
... // constructors, not shown
Volatility volatility(const Period& optionTenor,
Rate strike,
bool extrapolate = false) const;
Volatility volatility(const Date& optionDate,
Rate strike,
bool extrapolate = false) const;
Volatility volatility(Time optionTime,
Rate strike,
bool extrapolate = false) const;
Real blackVariance(const Period& optionTenor,
Rate strike,
bool extrapolate = false) const;
// same overloads as for volatility
shared_ptr<SmileSection> smileSection(
const Period& optionTenor,
bool extrapolate = false) const;
shared_ptr<SmileSection> smileSection(
const Date& optionDate,
bool extrapolate = false) const;
shared_ptr<SmileSection> smileSection(
Time optionTime,
bool extrapolate = false) const;
protected:
virtual shared_ptr<SmileSection> smileSectionImpl(
const Date& optionDate) const;
virtual shared_ptr<SmileSection> smileSectionImpl(
Time optionTime) const = 0;
virtual Volatility volatilityImpl(const Date& d,
Rate strike) const {
return volatilityImpl(timeFromReference(d), strike);
}
virtual Volatility volatilityImpl(Time optionTime,
Rate strike) const = 0;
};
The semantics are back to normal, with the volatility being dependent
on the exercise time. The structure of the class is as usual, too, but
with a notable addition: besides the usual volatility
method, the
class declares a smileSection
method that takes an exercise date (or
the corresponding time or period) and returns an object that models
the whole smile at that date and inherits from the base class
SmileSection
, shown in listing 3.21. The interface is modeled after
that of the volatility class and shouldn’t need explanation, apart
from noting that the time is fixed and doesn’t need to be passed as an
argument to the various methods.
Listing 3.21: Partial interface of the SmileSection
class.
class SmileSection : public virtual Observable,
public virtual Observer {
public:
SmileSection(Time exerciseTime,
const DayCounter& dc = DayCounter());
virtual ~SmileSection() {}
virtual Real minStrike() const = 0;
virtual Real maxStrike() const = 0;
Real variance(Rate strike) const;
Volatility volatility(Rate strike) const;
virtual Real atmLevel() const = 0;
protected:
virtual Real varianceImpl(Rate strike) const;
virtual Volatility volatilityImpl(Rate strike) const = 0;
};
Simple as it seems, the addition of smile sections yields a new design that sees volatilities not as surfaces, but as collections of smiles at different times. This opens up the possibility to model the smile directly and to reuse the corresponding classes across different types of volatilities (say, for both cap/floors and swaptions).
However, we weren’t bold enough to switch completely to the new
interface. The two representations (the surface and the series of
smiles) still coexist in the base caplet-volatility class, leading to
some disadvantages. Any derived class can implement volatilityImpl
in terms of smileSectionImpl
as
Volatility volatilityImpl(Time t, Real strike) const {
return smileSectionImpl(t).volatility(strike);
}
but this is only convenient if we’re modeling the smile to begin
with. If we’re modeling the volatility surface, instead, this design
makes it a lot more cumbersome to implement a new class. There’s no
easy way to implement smileSectionImpl
in terms of volatilityImpl
:
we should return an object that’s able to call volatilityImpl
from
its own volatility
method, but that would link the lifetimes of the
smile section and the volatility surface, and in turn raise all kind
of problems. We would probably end up writing a smile-section class
that contains the same code as the volatility surface; therefore, we
might as well drop volatilityImpl
altogether.
Unfortunately, naive implementations of the smileSectionImpl
method
cause new objects to be allocated at each call, which is obviously not
good for performance. Smarter implementations would need to cache
objects, and with this comes more complexity. Thus, the smile section
is an interesting concept, but maybe more trouble than it’s worth. It
might be reduced in scope, and used as an implementation detail for
classes that model the smile directly.
A final note on the OptionletVolatilityStructure
class: unlike the
classes we’ve seen so far, it also declares an overload of
volatilityImpl
that takes a date. It has a default implementation
that converts the date to a time and calls the other overload, so
writers of derived classes don’t need to override it; but it can
increase accuracy when the derived class takes and stores dates as
input.
Finally, the third hierarchy models swaption volatilities; its base
class is the SwaptionVolatilityStructure
, shown in listing 3.22.
Listing 3.22: Interface of the SwaptionVolatilityStructure
class.
class SwaptionVolatilityStructure
: public VolatilityTermStructure {
public:
... // constructors, not shown
Volatility volatility(const Period& optionTenor,
const Period& swapTenor,
Rate strike,
bool extrapolate = false) const;
// various overloads, also for blackVariance and smileSection
virtual const Period& maxSwapTenor() const = 0;
Time maxSwapLength() const;
protected:
virtual Volatility volatilityImpl(Time optionTime,
Time swapLength,
Rate strike) const = 0;
void checkSwapTenor(Time swapLength,
bool extrapolate) const;
};
It has the same new features I just described for caplet volatilities
(the use of smile sections and the overload of volatilityImpl
) and a
new shtick: the additional dimension given by the length of the
underlying swap, which in turn brings a few more methods for
specifying and checking the range of the corresponding argument.
That’s all for term structures. You’ll find plenty of examples in the library; I hope this chapter will help you make sense of them.