Odds and ends: exercises and payoffs
Welcome back.
In this post, some more content from
Implementing QuantLib:
a short section from the appendix, looking at the Exercise
and
Payoff
classes. At last, it seems that I’m getting close
to completing the book, or at least its first version. (And in case
you’re interested, I’m available for trainings based on its contents.)
Subscribe to my Substack to receive my posts in your inbox, or follow me on Twitter or LinkedIn if you want to be notified of new posts, or subscribe via RSS if you’re the tech type: the buttons for all that are in the footer. Also, I’m available for training, both online and (when possible) on-site: visit my Training page for more information.
Exercises and payoffs
The subjects of this post are a couple of domain-related classes used in the definitions of a few instruments.
First, the Exercise
class, shown in the listing below.
As you would expect, the base class declares methods to retrieve
information on the date, or dates, of the exercise. Quite a few of
them, actually. There’s a dates
method that returns the set of
exercise dates, a date
method that returns the one at a particular
index, and a convenience method lastDate
that, as you might have
guessed, returns the last one; so there’s some redundancy
there. (Lovers of encapsulation will probably prefer the version
taking an index to the one returning a vector, since the latter
reveals more than necessary about the internal storage of the class.)
Also, there’s a type
method that is leaving me scratching my head as
I look at the code again.
The type
method returns the kind of exercise, picking its value from
a set (European, Bermudan, or American) declared in an inner
enumeration Exercise::Type
. This is not puzzling per se, but it
goes somewhat against what we did next, which is to use inheritance to
declare AmericanExercise
, BermudanExercise
, and EuropeanExercise
classes. On the one hand, the use of a virtual destructor in the base
Exercise
class seems to suggest that inheritance is the way to go if
one wants to define new kind of exercises. On the other hand,
enumerating the kind of exercises in the base class seems to go
against this kind of extension, since inheriting a new exercise class
would also require one to add a new case to the enumeration. For
inheritance, one can also argue that the established idiom around the
library is to pass around smart pointers to the Exercise
class; and
against inheritance, that the class doesn’t define any virtual method
except the destructor, and the behavior of an instance of any derived
class is only given by the value of the data members stored in the
base class. In short: it seems that, when we wrote this, we were even
more confused than I am now.
Were I to write it now, I’d probably keep the enumeration and make it
a concrete class: the derived classes might either create objects that
can be safely sliced to become Exercise
instances when passed
around, or they could be turned into functions returning
Exercise
instances directly. As much as this might irk
object-oriented purists, there are a number of places in the code
where the type of the exercise need to be checked, and having an
enumeration is probably the pragmatic choice when compared to using
casts or some kind of visitor pattern. The absence of specific
behavior in derived classes seems another hint to me.
As I wrote this, it occurred to me that an exercise might also be an
Event
, as described in a previous post. However,
this doesn’t always match what the Exercise
class models. In
the case of a European exercise, we could also model it as an
Event
instance; in the case of a Bermudan exercise, the
Exercise
instance would probably correspond to a set of
Event
instances; and in the case of an American exercise, what
we’re really modeling here is an exercise range—and as a matter of
fact, the meaning of the interface also changes in this case, since
the dates
method no longer returns the set of all possible
exercise dates, but just the first and last date in the range. As
often happens, the small things that seem obvious turn out to be
difficult to model soundly when looked up close.
Onwards to the Payoff
class, shown in the next listing together with
a few of its derived classes.
Its interface includes an operator()
, returning the value of the
payoff given the value of the underlying, an accept
method to
support the Visitor pattern, and a couple of inspectors (name
and
description
) which can be used for reporting—and are probably one
too many.
In hindsight, we tried to model the class before having a grasp of
enough use cases. Unfortunately, the resulting interface stuck. The
biggest problem is the dependency of operator()
on a single
underlying, which excludes payoffs based on multiple underlying
values. (This also constrains how we model payoffs based on multiple
fixings of an underlying; for instance, Asian options are passed the
payoff for a plain option and the average of the fixings is done
externally, before passing it to the payoff. One might want the whole
process to be described as “the payoff”.) Another one is the
over-reliance on inheritance. For instance, we have a TypePayoff
class that adds a type (Call
or Put
, which again might be
restrictive) and the corresponding inspector; a StrikedTypePayoff
which adds a strike; and, finally, PlainVanillaPayoff
, which models
a simple call or put payoff and ends up removed from the Payoff
class by three levels of inheritance: probably too many, considering
that this is used to implement a textbook option and will be looked up
by people just starting with the library.
Another misstep we might have made is to add a pointer to a
Payoff
instance to the Option
class as a data member,
with the intent that it should contain the information about the
payoff. This led us to classes such as FloatingTypePayoff
,
also shown in the listing. It’s used in the implementation of
floating lookback options, and stores the information about the type
(as in, call or put); but since the strike is fixed at the maturity of
the option, it can’t specify it and can’t implement the payoff with
the interface we specified. Its operator()
throws an exception
if invoked. In this case, we might as well do without the payoff and
just pass the type to the lookback option; that is, if its base
Option
class didn’t expect a payoff. Yet another thing to
remember when we decide to clean up the code.