Table of Contents
The author has used good faith effort in preparation of this book, but makes no expressed or implied warranty of any kind and disclaims without limitation all responsibility for errors or omissions. No liability is assumed for incidental or consequential damages in connection with or arising out of the use of the information or programs contained herein. Use of the information and instructions in this book is at your own risk.
The cover image is in the public domain and available from the New York Public Library. The cover font is Open Sans Condensed, released by Steve Matteson under the Apache License version 2.0.
1. Introduction
With the enthusiasm of youth, the QuantLib web site used to state that QuantLib aimed at becoming “the standard free/open-source financial library.” By interpreting such statement a bit loosely, one might say that it has somewhat succeeded—albeit by employing the rather devious trick of being the first, and thus for some time the only open-source financial library1.
Standard or not, the project is thriving; at the time of this writing, each new release is downloaded a few thousand times, there is a steady stream of contributions from users, and the library seems to be used in the real world—as far as I can guess through the usual shroud of secrecy used in the financial world. All in all, as a project administrator, I can declare myself a happy camper.
But all the more for that, the lack of proper documentation shows. Although a detailed class reference is available (that was an easy task, since it can be generated automatically) it doesn’t let one see the forest for the trees; so that a new user might get the impression that the QuantLib developers share the views of the Bellman from Lewis Carroll’s Hunting of the Snark:
“What use are Mercator’s North Poles and Equators,
Tropics, Zones and Meridian Lines?”
So the Bellman would cry: and the crew would reply,
“They are merely conventional signs!”
The purpose of this book is to fill a part of the existing void. It is a report on the design and implementation of QuantLib, alike in spirit—but, hopefully, with less frightening results—to the How I did it book2 prominently featured in Mel Brooks’ Young Frankenstein. If you are—or want to be—a QuantLib user, you will find here useful information on the design of the library that might not be readily apparent when reading the code. If you’re working in quantitative finance, even if not using QuantLib, you can still read it as a field report on the design of a financial library. You will find that it covers issues that you might also face, as well as some possible solutions and their rationale. Based on your constraints, it is possible—even likely—that you will choose other solutions; but you might profit from this discussion just the same.
In my descriptions, I’ll also point out shortcomings in the current implementation; not to disparage the library (I’m pretty much involved in it, after all) but for more useful purposes. On the one hand, describing the existing pitfalls will help developers avoid them; on the other hand, it might show how to improve the library. Indeed, it already happened that reviewing the code for this book caused me to go back and modify it for the better.
For reasons of both space and time, I won’t be able to cover every aspect of the library. In the first half of the book, I’ll describe a few of the most important classes, such as those modeling financial instruments and term structures; this will give you a view of the larger architecture of the library. In the second half, I’ll describe a few specialized frameworks, such as those used for creating Monte Carlo or finite-differences models. Some of those are more polished than others; I hope that their current shortcomings will be as interesting as their strong points.
The book is primarily aimed at users wanting to extend the library with their own instruments or models; if you desire to do so, the description of the available class hierarchies and frameworks will provide you with information about the hooks you need to integrate your code with QuantLib and take advantage of its facilities. If you’re not this kind of user, don’t close the book yet; you can find useful information too. However, you might want to look at the QuantLib Python Cookbook instead. It can be useful to C++ users, too.
And now, as is tradition, a few notes on the style and requirements of this book.
Knowledge of both C++ and quantitative finance is assumed. I’ve no pretense of being able to teach you either one, and this book is thick enough already. Here, I just describe the implementation and design of QuantLib; I’ll leave it to other and better authors to describe the problem domain on one hand, and the language syntax and tricks on the other.
As you already noticed, I’ll write in the first person singular. True, it might look rather self-centered—as a matter of fact, I hope you still haven’t put down the book in annoyance—but we would feel rather pompous if we were to use the first person plural. The author of this book feels the same about using the third person. After a bit of thinking, I opted for a less formal but more comfortable style (which, as you noted, also includes a liberal use of contractions). For the same reason, I’ll be addressing you instead of the proverbial acute reader. The use of the singular will also help to avoid confusion; when I use the plural, I describe work done by the QuantLib developers as a group.
I will describe the evolution of a design when it is interesting on its own, or relevant for the final result. For the sake of clarity, in most cases I’ll skip over the blind alleys and wrong turns taken; put together design decisions which were made at different times; and only show the final design, sometimes simplified. This will still leave me with plenty to say: in the words of the Alabama Shakes, “why” is an awful lot of question.
I will point out the use of design patterns in the code I describe. Mind you, I’m not advocating cramming your code with them; they should be applied when they’re useful, not for their own sake.3 However, QuantLib is now the result of several years of coding and refactoring, both based on user feedback and new requirements being added over time. It is only natural that the design evolved toward patterns.
I will apply to the code listings in the book the same conventions used in the library and outlined in appendix B. I will depart from them in one respect: due to the limitations on line length, I might drop the std and boost namespaces from type names. When the listings need to be complemented by diagrams, I will be using UML; for those not familiar with this language, a concise guide can be found in Fowler, 2003.
And now, let’s dive.
2. Financial instruments and pricing engines
The statement that a financial library must provide the means to price financial instruments would certainly have appealed to Monsieur de La Palisse. However, that is only a part of the whole problem; a financial library must also provide developers with the means to extend it by adding new pricing functionality.
Foreseeable extensions are of two kinds, and the library must allow either one. On the one hand, it must be possible to add new financial instruments; on the other hand, it must be feasible to add new means of pricing an existing instrument. Both kinds have a number of requirements, or in pattern jargon, forces that the solution must reconcile. This chapter details such requirements and describes the design that allows QuantLib to satisfy them.
2.1 The Instrument
class
In our domain, a financial instrument is a concept in its own right. For this reason alone, any self-respecting object-oriented programmer will code it as a base class from which specific instruments will be derived.
The idea, of course, is to be able to write code such as
for
(
i
=
portfolio
.
begin
();
i
!=
portfolio
.
end
();
++
i
)
totalNPV
+=
i
->
NPV
();
where we don’t have to care about the specific type of each
instrument. However, this also prevents us from knowing what
arguments to pass to the NPV
method, or even what methods to
call. Therefore, even the two seemingly harmless lines above tell us
that we have to step back and think a bit about the interface.
2.1.1 Interface and requirements
The broad variety of traded assets—which range from the simplest to
the most exotic—implies that any method specific to a given class of
instruments (say, equity options) is bound not to make sense for some
other kind (say, interest-rate swaps). Thus, very few methods were
singled out as generic enough to belong to the Instrument
interface. We limited ourselves to those returning its present value
(possibly with an associated error estimate) and indicating whether or
not the instrument has expired; since we can’t specify what arguments
are needed,4 the methods take none; any needed input will
have to be stored by the instrument. The resulting interface is shown
in the listing below.
Instrument
class.
class
Instrument
{
public
:
virtual
~
Instrument
();
virtual
Real
NPV
()
const
=
0
;
virtual
Real
errorEstimate
()
const
=
0
;
virtual
bool
isExpired
()
const
=
0
;
};
As is good practice, the methods were first declared as pure virtual ones; but—as Sportin’ Life points out in Gershwin’s Porgy and Bess—it ain’t necessarily so. There might be some behavior that can be coded in the base class. In order to find out whether this was the case, we had to analyze what to expect from a generic financial instrument and check whether it could be implemented in a generic way. Two such requirements were found at different times, and their implementation changed during the development of the library; I present them here in their current form.
One is that a given financial instrument might be priced in different ways (e.g., with one or more analytic formulas or numerical methods) without having to resort to inheritance. At this point, you might be thinking “Strategy pattern”. It is indeed so; I devote section 2.2 to its implementation.
The second requirement came from the observation that the value of a financial instrument depends on market data. Such data are by their nature variable in time, so that the value of the instrument varies in turn; another cause of variability is that any single market datum can be provided by different sources. We wanted financial instruments to maintain links to these sources so that, upon different calls, their methods would access the latest values and recalculate the results accordingly; also, we wanted to be able to transparently switch between sources and have the instrument treat this as just another change of the data values.
We were also concerned with a potential loss of efficiency. For instance, we could monitor the value of a portfolio in time by storing its instruments in a container, periodically poll their values, and add the results. In a simple implementation, this would trigger recalculation even for those instruments whose inputs did not change. Therefore, we decided to add to the instrument methods a caching mechanism: one that would cause previous results to be stored and only recalculated when any of the inputs change.
2.1.2 Implementation
The code managing the caching and recalculation of the instrument value was written for a generic financial instrument by means of two design patterns.
When any of the inputs change, the instrument is notified by means of the Observer pattern (Gamma et al, 1995). The pattern itself is briefly described5 in appendix A; I describe here the participants.
Obviously enough, the instrument plays the role of the observer while
the input data play that of the observables. In order to have access
to the new values after a change is notified, the observer needs to
maintain a reference to the object representing the input. This might
suggest some kind of smart pointer; however, the behavior of a pointer
is not sufficient to fully describe our problem. As I already
mentioned, a change might come not only from the fact that values from
a data feed vary in time; we might also want to switch to a different
data feed. Storing a (smart) pointer would give us access to the
current value of the object pointed; but our copy of the pointer,
being private to the observer, could not be made to point to a
different object. Therefore, what we need is the smart equivalent of a
pointer to pointer. This feature was implemented in QuantLib as a
class template and given the name of Handle
. Again, details are
given in appendix A; relevant to this discussion is the
fact that copies of a given Handle
share a link to an
object. When the link is made to point to another object, all copies
are notified and allow their holders to access the new
pointee. Furthermore, Handle
s forward any notifications from
the pointed object to their observers.
Finally, classes were implemented which act as observable data and can
be stored into Handle
s. The most basic is the Quote
class, representing a single varying market value. Other inputs for
financial instrument valuation can include more complex objects such
as yield or volatility term structures.6
Another problem was to abstract out the code for storing and
recalculating cached results, while still leaving it to derived
classes to implement any specific calculations.
In earlier versions of QuantLib, the functionality was included in the
Instrument
class itself; later, it was extracted and coded into
another class—somewhat unsurprisingly called LazyObject
—which is
now reused in other parts of the library. An outline of the class is
shown in the following listing.
LazyObject
class.
class
LazyObject
:
public
virtual
Observer
,
public
virtual
Observable
{
protected
:
mutable
bool
calculated_
;
virtual
void
performCalculations
()
const
=
0
;
public
:
void
update
()
{
calculated_
=
false
;
}
virtual
void
calculate
()
const
{
if
(
!
calculated_
)
{
calculated_
=
true
;
try
{
performCalculations
();
}
catch
(...)
{
calculated_
=
false
;
throw
;
}
}
}
};
The code is not overly complex. A boolean data member calculated_
is
defined which keeps track of whether results were calculated and still
valid. The update
method, which implements the Observer
interface and is called upon notification from observables, sets such
boolean to false
and thus invalidates previous results.
The calculate
method is implemented by means of the Template Method
pattern (Gamma et al, 1995), sometimes also called
non-virtual interface. As explained in the Gang of Four book, the
constant part of the algorithm (in this case, the management of the cached
results) is implemented in the base class; the varying parts (here,
the actual calculations) are delegated to a virtual method, namely,
performCalculations
, which is called in the body of the
base-class method. Therefore, derived classes will only implement
their specific calculations without having to care about caching: the
relevant code will be injected by the base class.
The logic of the caching is simple. If the current results are no longer valid, we let the derived class perform the needed calculations and flag the new results as up to date. If the current results are valid, we do nothing.
However, the implementation is not as simple. You might
wonder why we had to insert a try
block setting
calculated_
beforehand and a handler rolling back the change
before throwing the exception again. After all, we could have written
the body of the algorithm more simply—for instance, as in the
following, seemingly equivalent code, that doesn’t catch and rethrow
exceptions:
if
(
!
calculated_
)
{
performCalculations
();
calculated_
=
true
;
}
The reason is that there are cases (e.g., when the lazy object is a
yield term structure which is bootstrapped lazily) in which
performCalculations
happens to recursively call
calculate
. If calculated_
were not set to true
,
the if
condition would still hold and performCalculations
would be called again, leading to infinite recursion. Setting such
flag to true
prevents this from happening; however, care must
now be taken to restore it to false
if an exception is
thrown. The exception is then rethrown so that it can be caught by the
installed error handlers.
A few more methods are provided in LazyObject
which enable
users to prevent or force a recalculation of the results. They are not
discussed here. If you’re interested, you can heed the advice often
given by master Obi-Wan Kenobi: “Read the source, Luke.”
The Instrument
class inherits from LazyObject
. In order
to implement the interface outlined earlier,
it decorates the calculate
method with code specific to
financial instruments. The resulting method is shown in
the listing below, together with other bits of supporting
code.
Instrument
class.
class
Instrument
:
public
LazyObject
{
protected
:
mutable
Real
NPV_
;
public
:
Real
NPV
()
const
{
calculate
();
return
NPV_
;
}
void
calculate
()
const
{
if
(
isExpired
())
{
setupExpired
();
calculated_
=
true
;
}
else
{
LazyObject
::
calculate
();
}
}
virtual
void
setupExpired
()
const
{
NPV_
=
0.0
;
}
};
Once again, the added code follows the Template Method pattern to
delegate instrument-specific calculations to derived classes. The
class defines an NPV_
data member to store the result of the
calculation; derived classes can declare other data members to store
specific results.7 The body of the calculate
method calls
the virtual isExpired
method to check whether the instrument is an
expired one. If this is the case, it calls another virtual method,
namely, setupExpired
, which has the responsibility of giving
meaningful values to the results; its default implementation sets
NPV_
to 0 and can be called by derived classes. The calculated_
flag is then set to true
. If the instrument is not expired, the
calculate
method of LazyObject
is called instead, which in turn
will call performCalculations
as needed. This imposes a contract on
the latter method, namely, its implementations in derived classes are
required to set NPV_
(as well as any other instrument-specific data
member) to the result of the calculations. Finally, the NPV
method
ensures that calculate
is called before returning the answer.
2.1.3 Example: interest-rate swap
I end this section by showing how a specific financial instrument can be implemented based on the described facilities.
The chosen instrument is the interest-rate swap. As you surely know, it is a contract which consists in exchanging periodic cash flows. The net present value of the instrument is calculated by adding or subtracting the discounted cash-flow amounts depending on whether the cash flows are paid or received.
Not surprisingly, the swap is implemented8 as a new class
deriving from Instrument
. Its outline is shown in
the following listing.
Swap
class.
class
Swap
:
public
Instrument
{
public
:
Swap
(
const
vector
<
shared_ptr
<
CashFlow
>
>&
firstLeg
,
const
vector
<
shared_ptr
<
CashFlow
>
>&
secondLeg
,
const
Handle
<
YieldTermStructure
>&
termStructure
);
bool
isExpired
()
const
;
Real
firstLegBPS
()
const
;
Real
secondLegBPS
()
const
;
protected
:
// methods
void
setupExpired
()
const
;
void
performCalculations
()
const
;
// data members
vector
<
shared_ptr
<
CashFlow
>
>
firstLeg_
,
secondLeg_
;
Handle
<
YieldTermStructure
>
termStructure_
;
mutable
Real
firstLegBPS_
,
secondLegBPS_
;
};
It contains as data members the objects
needed for the calculations—namely, the cash flows on the first and
second leg and the yield term structure used to discount their
amounts—and two variables used to store additional
results. Furthermore, it declares methods implementing the
Instrument
interface and others returning the swap-specific
results. The class diagram of Swap
and the related classes is shown
in the figure below.

Swap
class.The fitting of the class to the Instrument
framework is done in
three steps, the third being optional depending on the derived class;
the relevant methods are shown in the next listing.
Swap
class.
Swap
::
Swap
(
const
vector
<
shared_ptr
<
CashFlow
>
>&
firstLeg
,
const
vector
<
shared_ptr
<
CashFlow
>
>&
secondLeg
,
const
Handle
<
YieldTermStructure
>&
termStructure
)
:
firstLeg_
(
firstLeg
),
secondLeg_
(
secondLeg
),
termStructure_
(
termStructure
)
{
registerWith
(
termStructure_
);
vector
<
shared_ptr
<
CashFlow
>
>::
iterator
i
;
for
(
i
=
firstLeg_
.
begin
();
i
!=
firstLeg_
.
end
();
++
i
)
registerWith
(
*
i
);
for
(
i
=
secondLeg_
.
begin
();
i
!=
secondLeg_
.
end
();
++
i
)
registerWith
(
*
i
);
}
bool
Swap
::
isExpired
()
const
{
Date
settlement
=
termStructure_
->
referenceDate
();
vector
<
shared_ptr
<
CashFlow
>
>::
const_iterator
i
;
for
(
i
=
firstLeg_
.
begin
();
i
!=
firstLeg_
.
end
();
++
i
)
if
(
!
(
*
i
)
->
hasOccurred
(
settlement
))
return
false
;
for
(
i
=
secondLeg_
.
begin
();
i
!=
secondLeg_
.
end
();
++
i
)
if
(
!
(
*
i
)
->
hasOccurred
(
settlement
))
return
false
;
return
true
;
}
void
Swap
::
setupExpired
()
const
{
Instrument
::
setupExpired
();
firstLegBPS_
=
secondLegBPS_
=
0.0
;
}
void
Swap
::
performCalculations
()
const
{
NPV_
=
-
Cashflows
::
npv
(
firstLeg_
,
**
termStructure_
)
+
Cashflows
::
npv
(
secondLeg_
,
**
termStructure_
);
errorEstimate_
=
Null
<
Real
>
();
firstLegBPS_
=
-
Cashflows
::
bps
(
firstLeg_
,
**
termStructure_
);
secondLegBPS_
=
Cashflows
::
bps
(
secondLeg_
,
**
termStructure_
);
}
Real
Swap
::
firstLegBPS
()
const
{
calculate
();
return
firstLegBPS_
;
}
Real
Swap
::
secondLegBPS
()
const
{
calculate
();
return
secondLegBPS_
;
}
The first step is performed in the class constructor, which takes as arguments (and copies into the corresponding data members) the two sequences of cash flows to be exchanged and the yield term structure to be used for discounting their amounts. The step itself consists in registering the swap as an observer of both the cash flows and the term structure. As previously explained, this enables them to notify the swap and trigger its recalculation each time a change occurs.
The second step is the implementation of the required interface. The
logic of the isExpired
method is simple enough; its body loops
over the stored cash flows checking their payment dates. As soon as it
finds a payment which still has not occurred, it reports the swap as
not expired. If none is found, the instrument has expired. In this
case, the setupExpired
method will be called. Its
implementation calls the base-class one, thus taking care of the data
members inherited from Instrument
; it then sets to 0 the
swap-specific results.
The last required method is performCalculations
. The calculation is
performed by calling two external functions from the Cashflows
class.9 The first one, namely, npv
, is a straightforward
translation of the algorithm outlined above: it cycles on a sequence
of cash flows adding the discounted amount of its future cash flows.
We set the NPV_
variable to the difference of the results from the
two legs. The second one, bps
, calculates the basis-point
sensitivity (BPS) of a sequence of cash flows. We call it once per leg
and store the results in the corresponding data members. Since the
result carries no numerical error, the errorEstimate_
variable is
set to Null<Real>()
—a specific floating-point value which is used
as a sentinel value indicating an invalid number.10
The third and final step only needs to be performed if—as in this
case—the class defines additional results. It consists in writing
corresponding methods (here, firstLegBPS
and
secondLegBPS
) which ensure that the calculations are (lazily)
performed before returning the stored results.
The implementation is now complete. Having been written on top of the
Instrument
class, the Swap
class will benefit from its
code. Thus, it will automatically cache and recalculate results
according to notifications from its inputs—even though no related
code was written in Swap
except for the registration calls.
2.1.4 Further developments
You might have noticed a shortcoming in my treatment of the previous
example and of the Instrument
class in general. Albeit
generic, the Swap
class we implemented cannot manage
interest-rate swaps in which the two legs are paid in different
currencies. A similar problem would arise if you wanted to add the
values of two instruments whose values are not in the same currency;
you would have to convert manually one of the values to the
currency of the other before adding them together.
Such problems stem from a single weakness of the implementation: we
used the Real
type (i.e., a simple floating-point number) to
represent the value of an instrument or a cash flow. Therefore, such
results miss the currency information which is attached to them in the
real world.
The weakness might be removed if we were to express such results by
means of the Money
class. Instances of such class contain
currency information; moreover, depending on user settings, they are
able to automatically perform conversion to a common currency upon
addition or subtraction.
However, this would be a major change, affecting a large part of the code base in a number of ways. Therefore, it will need some serious thinking before we tackle it (if we do tackle it at all).
Another (and more subtle) shortcoming is that the Swap
class
fails to distinguish explicitly between two components of the
abstraction it represents. Namely, there is no clear separation
between the data specifying the contract (the cash-flow specification)
and the market data used to price the instrument (the current discount
curve).
The solution is to store in the instrument only the first group of data (i.e., those that would be in its term sheet) and keep the market data elsewhere.11 The means to do this are the subject of the next section.
2.2 Pricing engines
We now turn to the second of the requirements I stated in the previous section. For any given instrument, it is not always the case that a unique pricing method exists; moreover, one might want to use multiple methods for different reasons. Let’s take the classic textbook example—the European equity option. One might want to price it by means of the analytic Black-Scholes formula in order to retrieve implied volatilities from market prices; by means of a stochastic volatility model in order to calibrate the latter and use it for more exotic options; by means of a finite-difference scheme in order to compare the results with the analytic ones and validate one’s finite-difference implementation; or by means of a Monte Carlo model in order to use the European option as a control variate for a more exotic one.
Therefore, we want it to be possible for a single instrument to be
priced in different ways. Of course, it is not desirable to give
different implementations of the performCalculations
method,
as this would force one to use different classes for a single
instrument type. In our example, we would end up with a base
EuropeanOption
class from which AnalyticEuropeanOption
,
McEuropeanOption
and others would be derived. This is wrong in
at least two ways. On a conceptual level, it would introduce different
entities when a single one is needed: a European option is a European
option is a European option, as Gertrude Stein said. On a usability
level, it would make it impossible to switch pricing methods at
run-time.
The solution is to use the Strategy pattern, i.e., to let the
instrument take an object encapsulating the computation to be
performed. We called such an object a pricing engine. A given
instrument would be able to take any one of a number of available
engines (of course corresponding to the instrument type), pass the
chosen engine the needed arguments, have it calculate the value of the
instrument and any other desired quantities, and fetch the results.
Therefore, the performCalculations
method would be
implemented roughly as follows:
void
SomeInstrument
::
performCalculations
()
const
{
NPV_
=
engine_
->
calculate
(
arg1
,
arg2
,
...
,
argN
);
}
where we assumed that a virtual calculate
method is defined in
the engine interface and implemented in the concrete engines.
Unfortunately, the above approach won’t work as such. The problem is,
we want to implement the dispatching code just once, namely, in the
Instrument
class. However, that class doesn’t know the number
and type of arguments; different derived classes are likely to have
data members differing wildly in both number and type. The same goes
for the returned results; for instance, an interest-rate swap might
return fair values for its fixed rate and floating spread, while the
ubiquitous European option might return any number of Greeks.
An interface passing explicit arguments to the engine through a method, as the one outlined above, would thus lead to undesirable consequences. Pricing engines for different instruments would have different interfaces, which would prevent us from defining a single base class; therefore, the code for calling the engine would have to be replicated in each instrument class. This way madness lies.
The solution we chose was that arguments and results be passed and
received from the engines by means of opaque structures aptly called
arguments
and results
. Two structures derived from those
and augmenting them with instrument-specific data will be stored in
any pricing engine; an instrument will write and read such data in
order to exchange information with the engine.
The listing below shows the interface of the resulting
PricingEngine
class, as well as its inner argument
and
results
classes and a helper GenericEngine
class
template. The latter implements most of the PricingEngine
interface, leaving only the implementation of the calculate
method to developers of specific engines. The arguments
and
results
classes were given methods which ease their use as drop
boxes for data: arguments::validate
is to be called after input
data are written to ensure that their values lie in valid ranges,
while results::reset
is to be called before the engine starts
calculating in order to clean previous results.
PricingEngine
and of related classes.
class
PricingEngine
:
public
Observable
{
public
:
class
arguments
;
class
results
;
virtual
~
PricingEngine
()
{}
virtual
arguments
*
getArguments
()
const
=
0
;
virtual
const
results
*
getResults
()
const
=
0
;
virtual
void
reset
()
const
=
0
;
virtual
void
calculate
()
const
=
0
;
};
class
PricingEngine
::
arguments
{
public
:
virtual
~
arguments
()
{}
virtual
void
validate
()
const
=
0
;
};
class
PricingEngine
::
results
{
public
:
virtual
~
results
()
{}
virtual
void
reset
()
=
0
;
};
// ArgumentsType must inherit from arguments;
// ResultType from results.
template
<
class
ArgumentsType
,
class
ResultsType
>
class
GenericEngine
:
public
PricingEngine
{
public
:
PricingEngine
::
arguments
*
getArguments
()
const
{
return
&
arguments_
;
}
const
PricingEngine
::
results
*
getResults
()
const
{
return
&
results_
;
}
void
reset
()
const
{
results_
.
reset
();
}
protected
:
mutable
ArgumentsType
arguments_
;
mutable
ResultsType
results_
;
};
Armed with our new classes, we can now write a generic
performCalculation
method. Besides the already mentioned
Strategy pattern, we will use the Template Method pattern to allow any
given instrument to fill the missing bits. The resulting
implementation is shown in the next listing. Note that an
inner class Instrument::result
was defined; it inherits
from PricingEngine::results
and contains the results that have
to be provided for any instrument12
Instrument
class.
class
Instrument
:
public
LazyObject
{
public
:
class
results
;
virtual
void
performCalculations
()
const
{
QL_REQUIRE
(
engine_
,
"null pricing engine"
);
engine_
->
reset
();
setupArguments
(
engine_
->
getArguments
());
engine_
->
getArguments
()
->
validate
();
engine_
->
calculate
();
fetchResults
(
engine_
->
getResults
());
}
virtual
void
setupArguments
(
PricingEngine
::
arguments
*
)
const
{
QL_FAIL
(
"setupArguments() not implemented"
);
}
virtual
void
fetchResults
(
const
PricingEngine
::
results
*
r
)
const
{
const
Instrument
::
results
*
results
=
dynamic_cast
<
const
Value
*>
(
r
);
QL_ENSURE
(
results
!=
0
,
"no results returned"
);
NPV_
=
results
->
value
;
errorEstimate_
=
results
->
errorEstimate
;
}
template
<
class
T
>
T
result
(
const
string
&
tag
)
const
;
protected
:
shared_ptr
<
PricingEngine
>
engine_
;
};
class
Instrument
::
results
:
public
virtual
PricingEngine
::
results
{
public
:
Value
()
{
reset
();
}
void
reset
()
{
value
=
errorEstimate
=
Null
<
Real
>
();
}
Real
value
;
Real
errorEstimate
;
};
As for performCalculation
, the actual work is split between a
number of collaborating classes—the instrument, the pricing engine,
and the arguments and results classes. The dynamics of such a
collaboration (described in the following paragraphs) might be best
understood with the help of the UML sequence diagram shown in
the first of the next two figures; the static relationship between the
classes (and a possible concrete instrument) is shown in
the second.


Instrument
, PricingEngine
, and related classes
including a derived instrument class.A call to the NPV
method of the instrument eventually triggers
(if the instrument is not expired and the relevant quantities need to
be calculated) a call to its performCalculations
method. Here
is where the interplay between instrument and pricing engine
begins. First of all, the instrument verifies that an engine is
available, aborting the calculation if this is not the case. If one is
found, the instrument prompts it to reset itself. The message is
forwarded to the instrument-specific result structure by means of its
reset
method; after it executes, the structure is a clean slate
ready for writing the new results.
At this point, the Template Method pattern enters the scene. The
instrument asks the pricing engine for its argument structure, which
is returned as a pointer to arguments
. The pointer is then
passed to the instrument’s setupArguments
method, which acts as
the variable part in the pattern. Depending on the specific
instrument, such method verifies that the passed argument is of the
correct type and proceeds to fill its data members with the correct
values. Finally, the arguments are asked to perform any needed checks
on the newly-written values by calling the validate
method.
The stage is now ready for the Strategy pattern. Its arguments set,
the chosen engine is asked to perform its specific calculations,
implemented in its calculate
method. During the processing,
the engine will read the inputs it needs from its argument structure
and write the corresponding outputs into its results structure.
After the engine completes its work, the control returns to the
Instrument
instance and the Template Method pattern continues
unfolding. The called method, fetchResults
, must now ask the
engine for the results, downcast them to gain access to the contained
data, and copy such values into its own data members. The
Instrument
class defines a default implementation which fetches
the results common to all instruments; derived classes might extend it
to read specific results.
2.2.1 Example: plain-vanilla option
At this point, an example is necessary. A word of
warning, though: although a class exists in QuantLib which implements
plain-vanilla options—i.e., simple call and put equity options with
either European, American or Bermudan exercise—such class is
actually the lowermost leaf of a deep class hierarchy. Having the
Instrument
class at its root, such hierarchy specializes it
first with an Option
class, then again with a
OneAssetOption
class generalizing options on a single
underlying, passing through another class or two until it finally
defines the VanillaOption
class we are interested in.
There are good reasons for this; for instance, the code
in the OneAssetOption
class can naturally be reused for, say,
Asian options, while that in the Option
class lends itself for
reuse when implementing all kinds of basket options. Unfortunately,
this causes the code for pricing a plain option to be spread among all
the members of the described inheritance chain, which would not make
for an extremely clear example. Therefore, I will describe a simplified
VanillaOption
class with the same implementation as the one in
the library, but inheriting directly from the Instrument
class;
all code implemented in the intermediate classes will be
shown as if it were implemented in the example
class rather than inherited.
The listing below shows the interface of our
vanilla-option class. It declares the required methods from the
Instrument
interface, as well as accessors for additional
results, namely, the greeks of the options; as pointed out in the
previous section, the corresponding data members are declared as
mutable
so that their values can be set in the logically constant
calculate
method.
VanillaOption
class.
class
VanillaOption
:
public
Instrument
{
public
:
// accessory classes
class
arguments
;
class
results
;
class
engine
;
// constructor
VanillaOption
(
const
shared_ptr
<
Payoff
>&
,
const
shared_ptr
<
Exercise
>&
);
// implementation of instrument method
bool
isExpired
()
const
;
void
setupArguments
(
Arguments
*
)
const
;
void
fetchResults
(
const
Results
*
)
const
;
// accessors for option-specific results
Real
delta
()
const
;
Real
gamma
()
const
;
Real
theta
()
const
;
// ...more greeks
protected
:
void
setupExpired
()
const
;
// option data
shared_ptr
<
Payoff
>
payoff_
;
shared_ptr
<
Exercise
>
exercise_
;
// specific results
mutable
Real
delta_
;
mutable
Real
gamma_
;
mutable
Real
theta_
;
// ...more
};
Besides its own data and methods, VanillaOption
declares a
number of accessory classes: that is, the specific argument and result
structures and a base pricing engine. They are defined as
inner classes to highlight the relationship between them and the
option class; their interface is shown in the next listing.
VanillaOption
inner classes.
class
VanillaOption
::
arguments
:
public
PricingEngine
::
arguments
{
public
:
// constructor
arguments
();
void
validate
()
const
;
shared_ptr
<
Payoff
>
payoff
;
shared_ptr
<
Exercise
>
exercise
;
};
class
Greeks
:
public
virtual
PricingEngine
::
results
{
public
:
Greeks
();
Real
delta
,
gamma
;
Real
theta
;
Real
vega
;
Real
rho
,
dividendRho
;
};
class
VanillaOption
::
results
:
public
Instrument
::
results
,
public
Greeks
{
public
:
void
reset
();
};
class
VanillaOption
::
engine
:
public
GenericEngine
<
VanillaOption
::
arguments
,
VanillaOption
::
results
>
{};
Two comments can be made on such accessory classes. The first is that,
making an exception to what I said in my introduction to the example,
I didn’t declare all data members into the results
class. This
was done in order to point out an implementation detail. One might
want to define structures holding a few related and commonly used
results; such structures can then be reused by means of inheritance,
as exemplified by the Greeks
structure that is here composed
with Instrument::results
to obtain the final structure. In
this case, virtual inheritance from PricingEngine::results
must
be used to avoid the infamous inheritance diamond (see, for instance,
Stroustrup, 2013; the name is in the index).
The second comment is that, as shown, it is sufficient to inherit from
the class template GenericEngine
(instantiated with the right
argument and result types) to provide a base class for
instrument-specific pricing engines. We will see that derived classes
only need to implement their calculate
method.
We now turn to the implementation of the VanillaOption
class,
shown in the following listing.
VanillaOption
class.
VanillaOption
::
VanillaOption
(
const
shared_ptr
<
StrikedTypePayoff
>&
payoff
,
const
shared_ptr
<
Exercise
>&
exercise
)
:
payoff_
(
payoff
),
exercise_
(
exercise
)
{}
bool
VanillaOption
::
isExpired
()
const
{
Date
today
=
Settings
::
instance
().
evaluationDate
();
return
exercise_
->
lastDate
()
<
today
;
}
void
VanillaOption
::
setupExpired
()
const
{
Instrument
::
setupExpired
();
delta_
=
gamma_
=
theta_
=
...
=
0.0
;
}
void
VanillaOption
::
setupArguments
(
PricingEngine
::
arguments
*
args
)
const
{
VanillaOption
::
arguments
*
arguments
=
dynamic_cast
<
VanillaOption
::
arguments
*>
(
args
);
QL_REQUIRE
(
arguments
!=
0
,
"wrong argument type"
);
arguments
->
exercise
=
exercise_
;
arguments
->
payoff
=
payoff_
;
}
void
VanillaOption
::
fetchResults
(
const
PricingEngine
::
results
*
r
)
const
{
Instrument
::
fetchResults
(
r
);
const
VanillaOption
::
results
*
results
=
dynamic_cast
<
const
VanillaOption
::
results
*>
(
r
);
QL_ENSURE
(
results
!=
0
,
"wrong result type"
);
delta_
=
results
->
delta
;
...
// other Greeks
}
Real
VanillaOption
::
delta
()
const
{
calculate
();
QL_ENSURE
(
delta_
!=
Null
<
Real
>
(),
"delta not given"
);
return
delta_
;
}
Its constructor takes a few objects defining the instrument. Most of them will be described in later chapters or in appendix A. For the time being, suffice to say that the payoff contains the strike and type (i.e., call or put) of the option, and the exercise contains information on the exercise dates and variety (i.e., European, American, or Bermudan). The passed arguments are stored in the corresponding data members. Also, note that they do not include market data; those will be passed elsewhere.
The methods related to expiration are straightforward; isExpired
checks whether the latest exercise date is passed, while
setupExpired
calls the base-class implementation and sets the
instrument-specific data to 0.
The setupArguments
and fetchResults
methods are a bit
more interesting. The former starts by downcasting the generic
argument
pointer to the actual type required, raising an
exception if another type was passed; it then turns to the actual
work. In some cases, the data members are just copied verbatim into
the corresponding argument slots. However, it might be the case that
the same calculations (say, converting dates into times) will be
needed by a number of engines; setupArguments
provides a place
to write them just once.
The fetchResults
method is the dual of
setupArguments
. It also starts by downcasting the passed
results
pointer; after verifying its actual type, it
copies the results into his own data members.
Any method that returns additional results, such as delta
, will do
as NPV
does: it will call calculate
, check that the corresponding
result was cached (because any given engine might or might not be able
to calculate it) and return it.
The above implementation is everything we needed to have a working instrument—working, that is, once it is set an engine which will perform the required calculations. Such an engine is sketched in the listing below and implements the analytic Black-Scholes-Merton formula for European options.
VanillaOption
class.
class
AnalyticEuropeanEngine
:
public
VanillaOption
::
engine
{
public
:
AnalyticEuropeanEngine
(
const
shared_ptr
<
GeneralizedBlackScholesProcess
>&
process
)
:
process_
(
process
)
{
registerWith
(
process
);
}
void
calculate
()
const
{
QL_REQUIRE
(
arguments_
.
exercise
->
type
()
==
Exercise
::
European
,
"not an European option"
);
shared_ptr
<
PlainVanillaPayoff
>
payoff
=
dynamic_pointer_cast
<
PlainVanillaPayoff
>
(
arguments_
.
payoff
);
QL_REQUIRE
(
process
,
"Black-Scholes process needed"
);
...
// other requirements
Real
spot
=
process_
->
stateVariable
()
->
value
();
...
// other needed quantities
BlackCalculator
black
(
payoff
,
forwardPrice
,
stdDev
,
discount
);
results_
.
value
=
black
.
value
();
results_
.
delta
=
black
.
delta
(
spot
);
...
// other greeks
}
private
:
shared_ptr
<
GeneralizedBlackScholesProcess
>
process_
;
};
Its constructor takes (and registers itself with) a Black-Scholes
stochastic process that contains market-data information about the
underlying including present value, risk-free rate, dividend yield,
and volatility. Once again, the actual calculations are hidden behind
the interface of another class, namely, the BlackCalculator
class. However, the code has enough detail to show a few relevant
features.
The method starts by verifying a few preconditions. This might come as
a surprise, since the arguments of the calculations were already
validated by the time the calculate
method is called. However,
any given engine can have further requirements to be fulfilled before
its calculations can be performed. In the case of our engine, one such
requirement is that the option is European and that the payoff is a
plain call/put one, which also means that the payoff will be cast down
to the needed class.13
In the middle section of the method, the engine extracts from the passed arguments any information not already presented in digested form. Shown here is the retrieval of the spot price of the underlying; other quantities needed by the engine, e.g., the forward price of the underlying and the risk-free discount factor at maturity, are also extracted.14
Finally, the calculation is performed and the results are stored in
the corresponding slots of the results structure. This concludes both
the calculate
method and the example.
A. Odds and ends
A number of basic issues with the usage of QuantLib have been glossed over in the previous chapters, in order not to undermine their readability (if any) with an accumulation of technical details; as pointed out by Douglas Adams in the fourth book of its Hitchhiker trilogy,15
[An excessive amount of detail] is guff. It doesn’t advance the action. It makes for nice fat books such as the American market thrives on, but it doesn’t actually get you anywhere.
This appendix provides a quick reference to some such issues. It is not meant to be exhaustive nor systematic;16 if you need that kind of documentation, check the QuantLib reference manual, available at the QuantLib web site.
Basic types
The library interfaces don’t use built-in types; instead, a number of
typedefs are provided such as Time
, Rate
,
Integer
, or Size
. They are all mapped to basic types
(we talked about using full-featured types, possibly with range
checking, but we dumped the idea). Furthermore, all floating-point
types are defined as Real
, which in turn is defined as
double
. This makes it possible to change all of them
consistently by just changing Real
.
In principle, this would allow one to choose the desired level of
accuracy; but to this, the test-suite answers “Fiddlesticks!” since
it shows a few failures when Real
is defined as float
or
long double
. The value of the typedefs is really in making the
code more clear—and in allowing dimensional analysis for those who,
like me, were used to it in a previous life as a physicist; for
instance, expressions such as exp(r)
or r+s*t
can be
immediately flagged as fishy if they are preceded by Rate r
,
Spread s
, and Time t
.
Of course, all those fancy types are only aliases to double
and
the compiler doesn’t really distinguish between them. It would nice
if they had stronger typing; so that, for instance, one could overload
a method based on whether it is passed a price or a volatility.
One possibility would be the BOOST_STRONG_TYPEDEF
macro, which is one
of the bazillion utilities provided by Boost. It is used as, say,
BOOST_STRONG_TYPEDEF
(
double
,
Time
)
BOOST_STRONG_TYPEDEF
(
double
,
Rate
)
and creates a corresponding proper class with appropriate conversions to and from the underlying type. This would allow overloading methods, but has the drawbacks that not all conversions are explicit. This would break backward compatibility and make things generally awkward.17
Also, the classes defined by the macro overload all operators: you can happily add a time to a rate, even though it doesn’t make sense (yes, dimensional analysis again). It would be nice if the type system prevented this from compiling, while still allowing, for instance, to add a spread to a rate yielding another rate or to multiply a rate by a time yielding a pure number.
How to do this in a generic way, and ideally with no run-time costs, was shown first by Barton and Nackman, 1995; a variation of their idea is implemented in the Boost.Units library, and a simpler one was implemented once by yours truly while still working in Physics.18 However, that might be overkill here; we don’t have to deal with all possible combinations of length, mass, time and so on.
The ideal compromise for a future library might be to implement wrapper classes (à la Boost strong typedef) and to define explicitly which operators are allowed for which types. As usual, we’re not the first ones to have this problem: the idea has been floating around for a while, and at some point a proposal was put forward (Brown, 2013) to add to C++ a new feature, called opaque typedefs, which would have made it easier to define this kind of types.
A final note: among these types, there is at least one which is not
determined on its own (like Rate
or Time
) but depends on
other types. The volatility of a price and the volatility of a rate
have different dimensions, and thus should have different types.
In short, Volatility
should be a template type.
Date calculations
Date calculations are among the basic tools of quantitative finance. As can be expected, QuantLib provides a number of facilities for this task; I briefly describe some of them in the following subsections.
Dates and periods
An instance of the Date
class represents a specific day such as
November 15th, 2014. This class provides a number of methods for
retrieving basic information such as the weekday, the day of the
month, or the year; static information such as the minimum and maximum
date allowed (at this time, January 1st, 1901 and December 31st, 2199,
respectively) or whether or not a given year is a leap year; or other
information such as a date’s Excel-compatible serial number or whether
or not a given date is the last date of the month. The complete list
of available methods and their interface is documented in the
reference manual. No time information is included (unless you enable
an experimental compilation switch).
Capitalizing on C++ features, the Date
class also overloads a
number of operators so that date algebra can be written in a natural
way; for example, one can write expressions such as ++d
, which
advances the date d
by one day; d + 2
, which yields the
date two days after the given date; d2 - d1
, which yields the
number of days between the two dates; d - 3*Weeks
, which yields
the date three weeks before the given date (and incidentally, features
a member of the available TimeUnit
enumeration, the other
members being Days
, Months
, and Years
); or
d1 < d2
, which yields true
if the first date is earlier
than the second one. The algebra implemented in the Date
class
works on calendar days; neither bank holidays nor business-day
conventions are taken into account.
The Period
class models lengths of time such as two days, three
weeks, or five years by storing a TimeUnit
and an integer. It
provides a limited algebra and a partial ordering. For the non
mathematically inclined, this means that two Period
instances
might or might not be compared to see which is the shorter; while it
is clear that, say, 11 months are less than one year, it is not
possible to determine whether 60 days are more or less than two months
without knowing which two months. When the comparison cannot
be decided, an exception is thrown.
And of course, even when the comparison seems obvious, we managed to sneak in a few surprises. For instance, the comparison
Period
(
7
,
Days
)
==
Period
(
1
,
Weeks
)
returns true
. It seems correct, right? Hold that thought.
Calendars
Holidays and business days are the domain of the Calendar
class. Several derived classes exist which define holidays for a
number of markets; the base class defines simple methods for
determining whether or not a date corresponds to a holiday or a
business day, as well as more complex ones for performing tasks such
as adjusting a holiday to the nearest business day (where “nearest”
can be defined according to a number of business-day conventions,
listed in the BusinessDayConvention
enumeration) or advancing a
date by a given period or number of business days.
It might be interesting to see how the behavior of a calendar changes
depending on the market it describes. One way would have been to
store in the Calendar
instance the list of holidays for the
corresponding market; however, for maintainability we wanted to code
the actual calendar rules (such as “the fourth Thursday in November”
or “December 25th of every year”) rather than enumerating the
resulting dates for a couple of centuries. Another obvious way would
have been to use polymorphism and the Template Method pattern; derived
calendars would override the isBusinessDay
method, from which
all others could be implemented. This is fine, but it has the
shortcoming that calendars would need to be passed and stored in
shared_ptr
s. The class is conceptually simple, though, and is
used frequently enough that we wanted users to instantiate it and pass
it around more easily—that is, without the added verbosity of
dynamic allocation.
The final solution was the one shown in the listing below. It is a variation of the pimpl idiom, also reminiscent of the Strategy or Bridge patterns; these days, the cool kids might call it type erasure, too (Becker,2007).
Calendar
class.
class
Calendar
{
protected
:
class
Impl
{
public
:
virtual
~
Impl
()
{}
virtual
bool
isBusinessDay
(
const
Date
&
)
const
=
0
;
};
shared_ptr
<
Impl
>
impl_
;
public
:
bool
isBusinessDay
(
const
Date
&
d
)
const
{
return
impl_
->
isBusinessDay
(
d
);
}
bool
isHoliday
(
const
Date
&
d
)
const
{
return
!
isBusinessDay
(
d
);
}
Date
adjust
(
const
Date
&
d
,
BusinessDayConvention
c
=
Following
)
const
{
// uses isBusinessDay() plus some logic
}
Date
advance
(
const
Date
&
d
,
const
Period
&
period
,
BusinessDayConvention
c
=
Following
,
bool
endOfMonth
=
false
)
const
{
// uses isBusinessDay() and possibly adjust()
}
// more methods
};
Long story short: Calendar
declares a polymorphic inner class Impl
to which the implementation of the business-day rules is delegated and
stores a pointer to one of its instances. The non-virtual
isBusinessDay
method of the Calendar
class forwards to the
corresponding method in Calendar::Impl
; following somewhat the
Template Method pattern, the other Calendar
methods are also
non-virtual and implemented (directly or indirectly) in terms of
isBusinessDay
.19
Coming back to this after all these years, though, I’m thinking that
we might have implemented all public methods in terms of
isHoliday
instead. Why? Because all calendars are defined
by stating which days are holidays (e.g., Christmas on December 25th
in a lot of places, or MLK Day on the third Monday in January in the
United States). Having isBusinessDay
in the Impl
interface instead forces all derived classes to negate that logic
instead of implementing it directly. It’s like having them implement
isNotHoliday
.
Derived calendar classes can provide specialized behavior by defining
an inner class derived from Calendar::Impl
; their constructor
will create a shared pointer to an Impl
instance and store it
in the impl_
data member of the base class. The resulting
calendar can be safely copied by any class that need to store a
Calendar
instance; even when sliced, it will maintain the
correct behavior thanks to the contained pointer to the polymorphic
Impl
class. Finally, we can note that instances of the same
derived calendar class can share the same Impl
instance. This
can be seen as an implementation of the Flyweight pattern—bringing
the grand total to about two and a half patterns for one deceptively
simple class.
Enough with the implementation of Calendar
, and back to its
behavior. Here’s the surprise I mentioned in the previous section.
Remember Period(1,Weeks)
being equal to Period(7,Days)
?
Except that for the advance
method of a calendar, 7 days means 7
business days. Thus, we have a situation in which two periods
p1
and p2
are equal (that is, p1 == p2
returns
true
) but calendar.advance(p1)
differs from
calendar.advance(p2)
. Yay, us.
I’m not sure I have a good idea for a solution here. Since we want
backwards compatibility, the current uses of Days
must keep
working in the same way; so it’s not possible, say, to start
interpreting calendar.advance(7, Days)
as 7 calendar days.
One way out might be to keep the current situation, introduce two new
enumeration cases BusinessDays
and CalendarDays
that
remove the ambiguity, and deprecate Days
. Another is to just
remove the inconsistency by dictating that a 7-days period do not, in
fact, equal one week; I’m not overly happy about this one.
As I said, no obvious solution. If you have any other suggestions, I’m all ears.
Day-count conventions
The DayCounter
class provides the means to calculate the
distance between two dates, either as a number of days or a fraction
of an year, according to different conventions. Derived classes such
as Actual360
or Thirty360
exist; they implement
polymorphic behavior by means of the same technique used by the
Calendar
class and described in the previous section.
Unfortunately, the interface has a bit of a rough edge. Instead of
just taking two dates, the yearFraction
method is declared as
Time
yearFraction
(
const
Date
&
,
const
Date
&
,
const
Date
&
refPeriodStart
=
Date
(),
const
Date
&
refPeriodEnd
=
Date
())
const
;
The two optional dates are required by one specific day-count convention (namely, the ISMA actual/actual convention) that requires a reference period to be specified besides the two input dates. To keep a common interface, we had to add the two additional dates to the signature of the method for all day counters (most of which happily ignore them). This is not the only mischief caused by this day counter; you’ll see another in the next section.
Schedules
The Schedule
class, shown in the next listing, is
used to generate sequences of coupon dates.
Schedule
class.
class
Schedule
{
public
:
Schedule
(
const
Date
&
effectiveDate
,
const
Date
&
terminationDate
,
const
Period
&
tenor
,
const
Calendar
&
calendar
,
BusinessDayConvention
convention
,
BusinessDayConvention
terminationDateConvention
,
DateGeneration
::
Rule
rule
,
bool
endOfMonth
,
const
Date
&
firstDate
=
Date
(),
const
Date
&
nextToLastDate
=
Date
());
Schedule
(
const
std
::
vector
<
Date
>&
,
const
Calendar
&
calendar
=
NullCalendar
(),
BusinessDayConvention
convention
=
Unadjusted
,
...
/* `other optional parameters` */
);
Size
size
()
const
;
bool
empty
()
const
;
const
Date
&
operator
[](
Size
i
)
const
;
const
Date
&
at
(
Size
i
)
const
;
const_iterator
begin
()
const
;
const_iterator
end
()
const
;
const
Calendar
&
calendar
()
const
;
const
Period
&
tenor
()
const
;
bool
isRegular
(
Size
i
)
const
;
Date
previousDate
(
const
Date
&
refDate
)
const
;
Date
nextDate
(
const
Date
&
refDate
)
const
;
...
// other inspectors and utilities
};
Following practice and ISDA conventions, this class has to accept a lot of parameters; you can see them as the argument list of its constructor. (Oh, and you’ll forgive me if I don’t go and explain all of them. I’m sure you can guess what they mean.) They’re probably too many, which is why the library uses the Named Parameter Idiom (already described in chapter 4) to provide a less unwieldy factory class. With its help, a schedule can be instantiated as
Schedule
s
=
MakeSchedule
().
from
(
startDate
).
to
(
endDate
)
.
withFrequency
(
Semiannual
)
.
withCalendar
(
TARGET
())
.
withNextToLastDate
(
stubDate
)
.
backwards
();
Other methods include on the one hand, inspectors for the stored data;
and on the other hand, methods to give the class a sequence interface,
e.g., size
, operator[]
, begin
, and end
.
The Schedule
class has a second constructor, taking a
precomputed vector of dates and a number of optional parameters, which
might be passed to help the library use the resulting schedule
correctly. Such information includes the date generation rule or
whether the dates are aligned to the end of the month, but mainly,
you’ll probably need to pass the tenor
and an isRegular
vector of bools, about which I need to spend a couple of words.
What does “regular” mean? The boolean isRegular(i)
doesn’t refer to
the i
-th date, but to the i
-th interval; that is, the one between
the i
-th and (i+1)
-th dates. When a schedule is built based on a
tenor, most intervals correspond to the passed tenor (and thus are
regular) but the first and last intervals might be shorter or longer
depending on whether we passed an explicit first or next-to-last date.
We might do this, e.g., when we want to specify a short first coupon.
If we build the schedule with a precomputed set of dates, we don’t have the tenor information and we can’t tell if a given interval is regular unless those pieces of information are passed to the schedule.20 In turn, this means that using that schedule to build a sequence of coupons (by passing it, say, to the constructor of a fixed-rate bond) might give us the wrong result. And why, oh, why does the bond needs this missing info in order to build the coupons? Again, because the day-count convention of the bond might be ISMA actual/actual, which needs a reference period; and in order to calculate the reference period, we need to know the coupon tenor. In absence of this information, all the bond can do is assume that the coupon is regular, that is, that the distance between the passed start and end dates of the coupon also corresponds to its tenor.
Finance-related classes
Given our domain, it is only to be expected that a number of classes directly model financial concepts. A few such classes are described in this section.
Market quotes
There are at least two possibilities to model quoted values. One is to model quotes as a sequence of static values, each with an associated timestamp, with the current value being the latest; the other is to model the current value as a quoted value that changes dynamically.
Both views are useful; and in fact, both were implemented in the
library. The first model corresponds to the TimeSeries
class,
which I won’t describe in detail here; it is basically a map between
dates and values, with methods to retrieve values at given dates and
to iterate on the existing values, and it was never really used in
other parts of the library. The second resulted in the Quote
class, shown in the following listing.
Quote
class.
class
Quote
:
public
virtual
Observable
{
public
:
virtual
~
Quote
()
{}
virtual
Real
value
()
const
=
0
;
virtual
bool
isValid
()
const
=
0
;
};
Its interface is slim enough. The class inherits from the
Observable
class, so that it can notify its dependent objects
when its value change. It declares the isValid
method, that
tells whether or not the quote contains a valid value (as opposed to,
say, no value, or maybe an expired value) and the value
method,
which returns the current value.
These two methods are enough to provide the needed behavior. Any other object whose behavior or value depends on market values (for example, the bootstrap helpers of chapter 2) can store handles to the corresponding quotes and register with them as an observer. From that point onwards, it will be able to access the current values at any time.
The library defines a number of quotes—that is, of implementations
of the Quote
interface. Some of them return values which are
derived from others; for instance, ImpliedStdDevQuote
turns
option prices into implied-volatility values. Others adapt other
objects; ForwardValueQuote
returns forward index fixings as the
underlying term structures change, while LastFixingQuote
returns the latest value in a time series.
At this time, only one implementation is an genuine source of external
values; that would be the SimpleQuote
class, shown in
the next listing.
SimpleQuote
class.
class
SimpleQuote
:
public
Quote
{
public
:
SimpleQuote
(
Real
value
=
Null
<
Real
>
())
:
value_
(
value
)
{}
Real
value
()
const
{
QL_REQUIRE
(
isValid
(),
"invalid SimpleQuote"
);
return
value_
;
}
bool
isValid
()
const
{
return
value_
!=
Null
<
Real
>
();
}
Real
setValue
(
Real
value
)
{
Real
diff
=
value
-
value_
;
if
(
diff
!=
0.0
)
{
value_
=
value
;
notifyObservers
();
}
return
diff
;
}
private
:
Real
value_
;
};
It is simple in the sense that it
doesn’t implement any particular data-feed interface: new values are
set manually by calling the appropriate method. The latest value
(possibly equal to Null<Real>()
to indicate no value) is stored
in a data member. The Quote
interface is implemented by having
the value
method return the stored value, and the
isValid
method checking whether it’s null. The method used to
feed new values is setValue
; it takes the new value, notifies
its observers if it differs from the latest stored one, and returns
the increment between the old and new values.21
I’ll conclude this post with a few short notes. The first is that
the type of the quoted values is constrained to Real
. This has
not been a limitation so far, and besides, it’s now too late to define
Quote
as a class template; so it’s unlikely that this will ever
change.
The second is that the original idea was that the Quote
interface would act as an adapter to actual data feeds, with different
implementations calling the different API and allowing QuantLib to use
them in a uniform way. So far, however, nobody provided such
implementations; the closer we got was to use data feeds in Excel and
set their values to instances of SimpleQuote
.
The last (and a bit longer) note is that the interface of
SimpleQuote
might be modified in future to allow more advanced
uses. When setting new values to a group of related quotes (say, the
quotes interest rates used for bootstrapping a curve) it would be
better to only trigger a single notification after all values are set,
instead of having each quote send a notification when it’s updated.
This behavior would be both faster, since chains of notifications turn
out to be quite the time sink, and safer, since no observer would risk
to recalculate after only a subset of the quotes are updated. The
change (namely, an additional silent
parameter to
setValue
that would mute notifications when equal to
true
) has already been implemented in a fork of the library,
and could be added to QuantLib too.
Interest rates
The InterestRate
class (shown in the listing that follows)
encapsulates general interest-rate calculations. Instances of this
class are built from a rate, a day-count convention, a compounding
convention, and a compounding frequency (note, though, that the value
of the rate is always annualized, whatever the frequency). This
allows one to specify rates such as “5%, actual/365, continuously
compounded” or “2.5%, actual/360, semiannually compounded.” As can be
seen, the frequency is not always needed. I’ll return to this later.
InterestRate
class.
enum
Compounding
{
Simple
,
// 1+rT
Compounded
,
// (1+r)^T
Continuous
,
// e^{rT}
SimpleThenCompounded
};
class
InterestRate
{
public
:
InterestRate
(
Rate
r
,
const
DayCounter
&
,
Compounding
,
Frequency
);
// inspectors
Rate
rate
()
const
;
const
DayCounter
&
dayCounter
();
Compounding
compounding
()
const
;
Frequency
frequency
()
const
;
// automatic conversion
operator
Rate
()
const
;
// implied discount factor and compounding after a given time
// (or between two given dates)
DiscountFactor
discountFactor
(
Time
t
)
const
;
DiscountFactor
discountFactor
(
const
Date
&
d1
,
const
Date
&
d2
)
const
;
Real
compoundFactor
(
Time
t
)
const
;
Real
compoundFactor
(
const
Date
&
d1
,
const
Date
&
d2
)
const
;
// other calculations
static
InterestRate
impliedRate
(
Real
compound
,
const
DayCounter
&
,
Compounding
,
Frequency
,
Time
t
);
...
// same with dates
InterestRate
equivalentRate
(
Compounding
,
Frequency
,
Time
t
)
const
;
...
// same with dates
};
Besides the obvious inspectors, the class provides a number of
methods. One is the conversion operator to Rate
, i.e., to
double
. On afterthought, this is kind of risky, as the
converted value loses any day-count and compounding information; this
might allow, say, a simply-compounded rate to slip undetected where a
continuously-compounded one was expected. The conversion was added
for backward compatibility when the InterestRate
class was
first introduced; it might be removed in a future revision of the
library, dependent on the level of safety we want to force on
users.22
Other methods complete a basic set of calculations. The
compoundFactor
returns the unit amount compounded for a time
\(t\) (or equivalently, between two dates \(d_1\) and
\(d_2\)) according to the given interest rate; the
discountFactor
method returns the discount factor between two dates
or for a time, i.e., the reciprocal of the compound factor; the
impliedRate
method returns a rate that, given a set of conventions,
yields a given compound factor over a given time; and the
equivalentRate
method converts a rate to an equivalent one with
different conventions (that is, one that results in the same
compounded amount).
Like the InterestRate
constructor, some of these methods take a
compounding frequency. As I mentioned, this doesn’t always make
sense; and in fact, the Frequency
enumeration has a
NoFrequency
item just to cover this case.
Obviously, this is a bit of a smell. Ideally, the frequency should be
associated only with those compounding conventions that need it, and
left out entirely for those (such as Simple
and
Continuous
) that don’t. If C++ supported it, we would write
something like
enum
Compounding
{
Simple
,
Compounded
(
Frequency
),
Continuous
,
SimpleThenCompounded
(
Frequency
)
};
which would be similar to algebraic data types in functional
languages, or case classes in Scala;23 but unfortunately
that’s not an option. To have something of this kind, we’d have to go
for a full-featured Strategy pattern and turn Compounding
into
a class hierarchy. That would probably be overkill for the needs of
this class, so we’re keeping both the enumeration and the smell.
Indexes
Like other classes such as Instrument
and TermStructure
,
the Index
class is a pretty wide umbrella: it covers concepts
such as interest-rate indexes, inflation indexes, stock indexes—you
get the drift.
Needless to say, the modeled entities are diverse enough that the
Index
class has very little interface to call its own. As
shown in the following listing, all its methods have to do with
index fixings.
Index
class.
class
Index
:
public
Observable
{
public
:
virtual
~
Index
()
{}
virtual
std
::
string
name
()
const
=
0
;
virtual
Calendar
fixingCalendar
()
const
=
0
;
virtual
bool
isValidFixingDate
(
const
Date
&
fixingDate
)
const
=
0
;
virtual
Real
fixing
(
const
Date
&
fixingDate
,
bool
forecastTodaysFixing
=
false
)
const
=
0
;
virtual
void
addFixing
(
const
Date
&
fixingDate
,
Real
fixing
,
bool
forceOverwrite
=
false
);
void
clearFixings
();
};
The isValidFixingDate
method tells us whether a
fixing was (or will be made) on a given date; the
fixingCalendar
method returns the calendar used to determine
the valid dates; and the fixing
method retrieves a fixing for a
past date or forecasts one for a future date. The remaining methods
deal specifically with past fixings: the name
method, which
returns an identifier that must be unique for each index, is used to
index (pun not intended) into a map of stored fixings; the
addFixing
method stores a fixing (or many, in other overloads
not shown here); and the clearFixing
method clears all stored
fixings for the given index.
Why the map, and where is it in the Index
class? Well, we started
from the requirement that past fixings should be shared rather than
per-instance; if one stored, say, the 6-months Euribor fixing for a
date, we wanted the fixing to be visible to all instances of the same
index,24 and not just the particular one whose addFixing
method we called. This was done by defining and using an
IndexManager
singleton behind the curtains. Smelly? Sure, as all
singletons. An alternative might have been to define static class
variables in each derived class to store the fixings; but that would
have forced us to duplicate them in each derived class with no real
advantage (it would be as much against concurrency as the singleton).
Since the returned index fixings might change (either because their
forecast values depend on other varying objects, or because a newly
available fixing is added and replaces a forecast) the Index
class inherits from Observable
so that instruments can register
with its instances and be notified of such changes.
At this time, Index
doesn’t inherit from Observer
,
although its derived classes do (not surprisingly, since forecast
fixings will almost always depend on some observable market quote).
This was not an explicit design choice, but rather an artifact of the
evolution of the code and might change in future
releases. However, even if we were to inherit Index
from Observer
,
we would still be forced to have some code duplication in derived
classes, for a reason which is probably worth describing in more
detail.
I already mentioned that fixings can change for two reasons. One is
that the index depends on other observables to forecast its fixings;
in this case, it simply registers with them (this is done in each
derived class, as each class has different observables). The other
reason is that a new fixing might be made available, and that’s more
tricky to handle. The fixing is stored by a call to addFixing
on a particular index instance, so it seems like no external
notification would be necessary, and that the index can just call the
notifyObservers
method to notify its observers; but that’s not
the case. As I said, the fixings is shared; if we store today’s
3-months Euribor fixing, it will be available to all instances of such
index, and thus we want all of them to be aware of the
change. Moreover, instruments and curves might have registered with
any of those Index
instances, so all of them must send in turn
a notification.
The solution is to have all instances of the same index communicate by
means of a shared object; namely, we used the same IndexManager
singleton that stores all index fixings. As I said,
IndexManager
maps unique index tags to sets of fixings; also,
by making the sets instances of the ObservableValue
class, it
provides the means to register and receive notification when one or
more fixings are added for a specific tag (this class is described
later in this appendix. You don’t need the details here).
All pieces are now in place. Upon construction, any Index
instance will ask IndexManager
for the shared observable
corresponding to the tag returned by its name
method. When
we call addFixings
on, say, some particular 6-months Euribor
index, the fixing will be stored into IndexManager
; the
observable will send a notification to all 6-months Euribor indexes
alive at that time; and all will be well with the world.
However, C++ still throws a small wrench in our gears. Given the above, it would be tempting to call
registerWith
(
IndexManager
::
instance
().
notifier
(
name
()));
in the Index
constructor and be done with it. However, it
wouldn’t work; for the reason that in the constructor of the base
class, the call to the virtual method name
wouldn’t be
polymorphic.25 From here stems the code
duplication I mentioned a few paragraphs earlier; in order to work,
the above method call must be added to the constructor of each derived
index class which implements or overrides the name
method. The Index
class itself doesn’t have a constructor
(apart from the default one that the compiler provides).
As an example of a concrete class derived from Index
, the next
listing sketches the InterestRateIndex
class.
InterestRateIndex
class.
class
InterestRateIndex
:
public
Index
,
public
Observer
{
public
:
InterestRateIndex
(
const
std
::
string
&
familyName
,
const
Period
&
tenor
,
Natural
settlementDays
,
const
Currency
&
currency
,
const
Calendar
&
fixingCalendar
,
const
DayCounter
&
dayCounter
);
:
familyName_
(
familyName
),
tenor_
(
tenor
),
...
{
registerWith
(
Settings
::
instance
().
evaluationDate
());
registerWith
(
IndexManager
::
instance
().
notifier
(
name
()));
}
std
::
string
name
()
const
;
Calendar
fixingCalendar
()
const
;
bool
isValidFixingDate
(
const
Date
&
fixingDate
)
const
{
return
fixingCalendar
().
isBusinessDay
(
fixingDate
);
}
Rate
fixing
(
const
Date
&
fixingDate
,
bool
forecastTodaysFixing
=
false
)
const
;
void
update
()
{
notifyObservers
();
}
std
::
string
familyName
()
const
;
Period
tenor
()
const
;
...
// other inspectors
Date
fixingDate
(
const
Date
&
valueDate
)
const
;
virtual
Date
valueDate
(
const
Date
&
fixingDate
)
const
;
virtual
Date
maturityDate
(
const
Date
&
valueDate
)
const
=
0
;
protected
:
virtual
Rate
forecastFixing
(
const
Date
&
fixingDate
)
const
=
0
;
std
::
string
familyName_
;
Period
tenor_
;
Natural
fixingDays_
;
Calendar
fixingCalendar_
;
Currency
currency_
;
DayCounter
dayCounter_
;
};
std
::
string
InterestRateIndex
::
name
()
const
{
std
::
ostringstream
out
;
out
<<
familyName_
;
if
(
tenor_
==
1
*
Days
)
{
if
(
fixingDays_
==
0
)
out
<<
"ON"
;
else
if
(
fixingDays_
==
1
)
out
<<
"TN"
;
else
if
(
fixingDays_
==
2
)
out
<<
"SN"
;
else
out
<<
io
::
short_period
(
tenor_
);
}
else
{
out
<<
io
::
short_period
(
tenor_
);
}
out
<<
" "
<<
dayCounter_
.
name
();
return
out
.
str
();
}
Rate
InterestRateIndex
::
fixing
(
const
Date
&
d
,
bool
forecastTodaysFixing
)
const
{
QL_REQUIRE
(
isValidFixingDate
(
d
),
...);
Date
today
=
Settings
::
instance
().
evaluationDate
();
if
(
d
<
today
)
{
Rate
pastFixing
=
IndexManager
::
instance
().
getHistory
(
name
())[
d
];
QL_REQUIRE
(
pastFixing
!=
Null
<
Real
>
(),
...);
return
pastFixing
;
}
if
(
d
==
today
&&
!
forecastTodaysFixing
)
{
Rate
pastFixing
=
...;
if
(
pastFixing
!=
Null
<
Real
>
())
return
pastFixing
;
}
return
forecastFixing
(
d
);
}
Date
InterestRateIndex
::
valueDate
(
const
Date
&
d
)
const
{
QL_REQUIRE
(
isValidFixingDate
(
d
)
...);
return
fixingCalendar
().
advance
(
d
,
fixingDays_
,
Days
);
}
As you might expect, such class
defines a good deal of specific behavior besides what it inherits from
Index
. To begin with, it inherits from Observer
, too,
since Index
doesn’t. The InterestRateIndex
constructor
takes the data needed to specify the index: a family name, as in
“Euribor”, common to different indexes of the same family such as,
say, 3-months and 6-months Euribor; a tenor that specifies a
particular index in the family; and additional information such as the
number of settlement days, the index currency, the fixing calendar,
and the day-count convention used for accrual.
The passed data are, of course, copied into the corresponding data
members; then the index registers with a couple of observables. The
first is the global evaluation date; this is needed because, as I’ll
explain shortly, there’s a bit of date-specific behavior in the class
that is triggered when an instance is asked for today’s fixing. The
second observable is the one which is contained inside
IndexManager
and provides notifications when new fixings are
stored. We can identify this observable here: the
InterestRateIndex
class has all the information needed to
determine the index, so it can implement the name
method and
call it. However, this also means that classes deriving from
InterestRateIndex
must not override name
; since the
overridden method would not be called in the body of this constructor
(as explained earlier), they would register with
the wrong notifier. Unfortunately, this can’t be enforced in C++,
which doesn’t have a keyword like final
in Java or
sealed
in C#; but the alternative would be to require
that all classes derived from InterestRateIndex
register with
IndexManager
, which is equally not enforceable, probably more
error-prone, and certainly less convenient.
The other methods defined in InterestRateIndex
have different
purposes. A few implement the required Index
and
Observer
interfaces; the simplest are update
, which
simply forwards any notification, fixingCalendar
, which returns
a copy of the stored calendar instance, and isValidFixingDate
,
which checks the date against the fixing calendar.
The name
method is a bit more complicated. It stitches
together the family name, a short representation of the tenor, and the
day-count convention to get an index name such as “Euribor 6M
Act/360” or “USD Libor 3M Act/360”; special tenors such as
overnight, tomorrow-next and spot-next are detected so that the
corresponding acronyms are used.
The fixing
method contains the most logic. First, the required
fixing date is checked and an exception is raised if no fixing was
supposed to take place on it. Then, the fixing date is checked
against today’s date. If the fixing was in the past, it must be among
those stored in the IndexManager
singleton; if not, an
exception is raised since there’s no way we can forecast a past
fixing. If today’s fixing was requested, the index first tries
looking for the fixing in the IndexManager
and returns it if
found; otherwise, the fixing is not yet available. In this case, as
well as for a fixing date in the future, the index forecasts the value
of the fixing; this is done by calling the forecastFixing
method, which is declared as purely virtual in this class and
implemented in derived ones. The logic in the fixing
method is
also the reason why, as I mentioned, the index registers with the
evaluation date; the behavior of the index depends on the value of
today’s date, so it need to be notified when it changes.
Finally, the InterestRateIndex
class defines other methods that
are not inherited from Index
. Most of them are inspectors that
return stored data such as the family name or the tenor; a few others
deal with date calculations. The valueDate
method takes a
fixing date and returns the starting date for the instrument that
underlies the rate (for instance, the deposit underlying a
LIBOR, which for most currencies starts two business days
after the fixing date); the maturityDate
method takes a value
date and returns the maturity of the underlying instrument (e.g., the
maturity of the deposit); and the fixingDate
method is the
inverse of valueDate
, taking a value date and returning the
corresponding fixing date. Some of these methods are virtual, so that
their behavior can be overridden; for instance, while the default
behavior for valueDate
is to advance the given number of fixing
days on the given calendar, LIBOR index mandates first to
advance on the London calendar, then to adjust the resulting date on
the calendar corresponding to the index currency. For some reason,
fixingDate
is not virtual; this is probably an oversight that
should be fixed in a future release.
Exercises and payoffs
I’ll close this section with a couple of domain-related classes used in the definitions of a few instruments.
First, the Exercise
class, shown in the listing below.
Exercise
class and its derived classes.
class
Exercise
{
public
:
enum
Type
{
American
,
Bermudan
,
European
};
explicit
Exercise
(
Type
type
);
virtual
~
Exercise
();
Type
type
()
const
;
Date
date
(
Size
index
)
const
;
const
std
::
vector
<
Date
>&
dates
()
const
;
Date
lastDate
()
const
;
protected
:
std
::
vector
<
Date
>
dates_
;
Type
type_
;
};
class
EarlyExercise
:
public
Exercise
{
public
:
EarlyExercise
(
Type
type
,
bool
payoffAtExpiry
=
false
);
bool
payoffAtExpiry
()
const
;
};
class
AmericanExercise
:
public
EarlyExercise
{
public
:
AmericanExercise
(
const
Date
&
earliestDate
,
const
Date
&
latestDate
,
bool
payoffAtExpiry
=
false
);
};
class
BermudanExercise
:
public
EarlyExercise
{
public
:
BermudanExercise
(
const
std
::
vector
<
Date
>&
dates
,
bool
payoffAtExpiry
=
false
);
};
class
EuropeanExercise
:
public
Exercise
{
public
:
EuropeanExercise
(
const
Date
&
date
);
};
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.26 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 chapter 4. 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.
Payoff
class and a few derived classes.
class
Payoff
:
std
::
unary_function
<
Real
,
Real
>
{
public
:
virtual
~
Payoff
()
{}
virtual
std
::
string
name
()
const
=
0
;
virtual
std
::
string
description
()
const
=
0
;
virtual
Real
operator
()(
Real
price
)
const
=
0
;
virtual
void
accept
(
AcyclicVisitor
&
);
};
class
TypePayoff
:
public
Payoff
{
public
:
Option
::
Type
optionType
()
const
;
protected
:
TypePayoff
(
Option
::
Type
type
);
};
class
FloatingTypePayoff
:
public
TypePayoff
{
public
:
FloatingTypePayoff
(
Option
::
Type
type
);
Real
operator
()(
Real
price
)
const
;
// more Payoff interface
};
class
StrikedTypePayoff
:
public
TypePayoff
{
public
:
Real
strike
()
const
;
// more Payoff interface
protected
:
StrikedTypePayoff
(
Option
::
Type
type
,
Real
strike
);
};
class
PlainVanillaPayoff
:
public
StrikedTypePayoff
{
public
:
PlainVanillaPayoff
(
Option
::
Type
type
,
Real
strike
);
Real
operator
()(
Real
price
)
const
;
// more Payoff interface
};
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.27 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.
Math-related classes
The library also needs some mathematical tools, besides those provided by the C++ standard library. Here is a brief overview of some of them.
Interpolations
Interpolations belong to a kind of class which is not common in QuantLib: namely, the kind that might be unsafe to use.
The base class, Interpolation
, is shown in the listing below. It
interpolates two underlying random-access sequences of \(x\) and
\(y\) values, and provides an operator()
that returns the
interpolated values as well as a few other convenience methods. Like
the Calendar
class we saw in a previous
section, it implements polymorphic behavior
by means of the pimpl idiom: it declares an inner Impl
class whose
derived classes will implement specific interpolations and to which
the Interpolation
class forwards its own method calls. Another inner
class template, templateImpl
, implements the common machinery and
stores the underlying data.
Interpolation
class.
class
Interpolation
:
public
Extrapolator
{
protected
:
class
Impl
{
public
:
virtual
~
Impl
()
{}
virtual
void
update
()
=
0
;
virtual
Real
xMin
()
const
=
0
;
virtual
Real
xMax
()
const
=
0
;
virtual
Real
value
(
Real
)
const
=
0
;
virtual
Real
primitive
(
Real
)
const
=
0
;
virtual
Real
derivative
(
Real
)
const
=
0
;
};
template
<
class
I1
,
class
I2
>
class
templateImpl
:
public
Impl
{
public
:
templateImpl
(
const
I1
&
xBegin
,
const
I1
&
xEnd
,
const
I2
&
yBegin
);
Real
xMin
()
const
;
Real
xMax
()
const
;
protected
:
Size
locate
(
Real
x
)
const
;
I1
xBegin_
,
xEnd_
;
I2
yBegin_
;
};
shared_ptr
<
Impl
>
impl_
;
public
:
typedef
Real
argument_type
;
typedef
Real
result_type
;
bool
empty
()
const
{
return
!
impl_
;
}
Real
operator
()(
Real
x
,
bool
extrapolate
=
false
)
const
{
checkRange
(
x
,
extrapolate
);
return
impl_
->
value
(
x
);
}
Real
primitive
(
Real
x
,
bool
extrapolate
=
false
)
const
;
Real
derivative
(
Real
x
,
bool
extrapolate
=
false
)
const
;
Real
xMin
()
const
;
Real
xMax
()
const
;
void
update
();
protected
:
void
checkRange
(
Real
x
,
bool
extrapolate
)
const
;
};
As you can see, templateImpl
doesn’t copy the \(x\) and \(y\)
values; instead, it just provides a kind of view over them by storing
iterators into the two sequences. This is what makes interpolations
unsafe: on the one hand, we have to make sure that the lifetime of an
Interpolation
instance doesn’t exceed that of the underlying
sequences, to avoid pointing into a destroyed object; and on the other
hand, any class that stores an interpolation
instance will have
to take special care of copying.
The first requirement is not a big problem. An interpolation is seldom used on its own; it is usually stored as a data member of some other class, together with its underlying data. This takes care of the lifetime issues, as the interpolation and the data live and die together.
The second is not a big problem, either: but whereas the first issue
is usually taken care of automatically, this one requires some action
on the part of the developer. As I said, the usual case is to have an
Interpolation
instance stored in some class together with its
data. The compiler-generated copy constructor for the container class
would make new copies of the underlying data, which is correct; but it
would also make a new copy of the interpolation that would still be
pointing at the original data (since it would store copies of the
original iterators). This is, of course, not correct.
To avoid this, the developer of the host class needs to write a
user-defined copy constructor that not only copies the data, but also
regenerates the interpolation so that it points to the new
sequences—which might not be so simple. An object holding an
Interpolation
instance can’t know its exact type (which is
hidden in the Impl
class) and thus can’t just rebuild it to
point somewhere else.
One way out of this would have been to give interpolations some kind of
virtual clone
method to return a new one of the same type, or a
virtual rebind
method to change the underlying iterators once
copied. However, that wasn’t necessary, as most of the times we
already have interpolation traits laying around.
What’s that, you say? Well, it’s those Linear
or LogLinear
classes
I’ve been throwing around while I was explaining interpolated term
structures in chapter 3. An example is
in the following listing, together with its corresponding
interpolation class.
LinearInterpolation
class and of its traits class.
template
<
class
I1
,
class
I2
>
class
LinearInterpolationImpl
:
public
Interpolation
::
templateImpl
<
I1
,
I2
>
{
public
:
LinearInterpolationImpl
(
const
I1
&
xBegin
,
const
I1
&
xEnd
,
const
I2
&
yBegin
)
:
Interpolation
::
templateImpl
<
I1
,
I2
>
(
xBegin
,
xEnd
,
yBegin
),
primitiveConst_
(
xEnd
-
xBegin
),
s_
(
xEnd
-
xBegin
)
{}
void
update
();
Real
value
(
Real
x
)
const
{
Size
i
=
this
->
locate
(
x
);
return
this
->
yBegin_
[
i
]
+
(
x
-
this
->
xBegin_
[
i
])
*
s_
[
i
];
}
Real
primitive
(
Real
x
)
const
;
Real
derivative
(
Real
x
)
const
;
private
:
std
::
vector
<
Real
>
primitiveConst_
,
s_
;
};
class
LinearInterpolation
:
public
Interpolation
{
public
:
template
<
class
I1
,
class
I2
>
LinearInterpolation
(
const
I1
&
xBegin
,
const
I1
&
xEnd
,
const
I2
&
yBegin
)
{
impl_
=
shared_ptr
<
Interpolation
::
Impl
>
(
new
LinearInterpolationImpl
<
I1
,
I2
>
(
xBegin
,
xEnd
,
yBegin
));
impl_
->
update
();
}
};
class
Linear
{
public
:
template
<
class
I1
,
class
I2
>
Interpolation
interpolate
(
const
I1
&
xBegin
,
const
I1
&
xEnd
,
const
I2
&
yBegin
)
const
{
return
LinearInterpolation
(
xBegin
,
xEnd
,
yBegin
);
}
static
const
bool
global
=
false
;
static
const
Size
requiredPoints
=
2
;
};
The LinearInterpolation
class doesn’t have a lot of logic: its only method is
a template constructor (the class itself is not a template) that
instantiates an inner implementation class. The latter inherits
from templateImpl
and is the one that does the heavy lifting,
implementing the actual interpolation formulas (with the help of
methods, such as locate
, defined in its base class).
The Linear
traits class defines some static information,
namely, that we need at least two points for a linear interpolation,
and that changing a point only affects the interpolation locally; and
also defines an interpolate
method that can create an
interpolation of a specific type from a set of iterators into \(x\) and
\(y\). The latter method is implemented with the same interface by all
traits (when an interpolation, such as splines, needs more parameters,
they are passed to the traits constructor and stored) and is the one
that’s going to help in our copying problem. If you look, for
instance, at the listing of the InterpolatedZeroCurve
class back in
chapter 3, you’ll see that
we’re storing an instance of the traits class (it’s called
interpolator_
there) together with the interpolation and the
underlying data. If we do the same in any class that stores an
interpolation, we’ll be able to use the traits to create a new one in
the copy constructor.
Unfortunately, though, we have no way at this time to enforce writing
a copy constructor in a class that stores an interpolation, so its
developer will have to remember it. We have no way, that is, without
making the Interpolation
class non-copyable and thus also
preventing useful idioms (like returning an interpolation from a
method, as traits do). In C++11, we’d solve this by making it
non-copyable and movable.
A final note: the interpolation stores iterators into the original
data, but this is not enough to keep it up to date when any of the
data changes. When this happens, its update
method must be called
so that the interpolation can refresh its state; this is the
responsibility of the class that contains the interpolation and the
data (and which, probably, is registered as an observer with whatever
may change.) This holds also for those interpolations, such as
linear, that might just read the data directly: depending on the
implementation, they might precalculate some results and store them as
state to be kept updated. (The current LinearInterpolation
implementation does this for the slopes between the points, as well as
the values of its primitive at the nodes.28 Depending on how
frequently the data are updated, this might be either an optimization
or a pessimization.)
One-dimensional solvers
Solvers were used in the bootstrap routines described in chapter 3, the yield calculations mentioned in chapter 4, and any code that needs a calculated value to match a target; i.e, that needs, given a function \(f\), to find the \(x\) such that \(f(x) = \xi\) within a given accuracy.
The existing solvers will find the \(x\) such that \(f(x) = 0\); of course, this doesn’t make them any less generic, but it requires you to define the additional helper function \(g(x) \equiv f(x)-\xi\). There are a few of them, all based on algorithms which were taken from Numerical Recipes in C (Press et al, 1992) and duly reimplemented.29
The following listing shows the interface of the class template
Solver1D
, used as a base by the available solvers.
Solver1D
class template and of a few derived classes.
template
<
class
Impl
>
class
Solver1D
:
public
CuriouslyRecurringTemplate
<
Impl
>
{
public
:
template
<
class
F
>
Real
solve
(
const
F
&
f
,
Real
accuracy
,
Real
guess
,
Real
step
)
const
;
template
<
class
F
>
Real
solve
(
const
F
&
f
,
Real
accuracy
,
Real
guess
,
Real
xMin
,
Real
xMax
)
const
;
void
setMaxEvaluations
(
Size
evaluations
);
void
setLowerBound
(
Real
lowerBound
);
void
setUpperBound
(
Real
upperBound
);
};
class
Brent
:
public
Solver1D
<
Brent
>
{
public
:
template
<
class
F
>
Real
solveImpl
(
const
F
&
f
,
Real
xAccuracy
)
const
;
};
class
Newton
:
public
Solver1D
<
Newton
>
{
public
:
template
<
class
F
>
Real
solveImpl
(
const
F
&
f
,
Real
xAccuracy
)
const
;
};
It provides some boilerplate code, common to all of them: one overload
of the solve
method looks for lower and upper values of \(x\)
that bracket the solution, while the other checks that the solution is
actually bracketed by the passed minimum and maximum values. In both
cases, the actual calculation is delegated to the solveImpl
method
defined by the derived class and implementing a specific algorithm.
Other methods allow you to set constraints on the explored range, or
the number of function evaluations.
The forwarding to solveImpl
is implemented using the Curiously
Recurring Template Pattern, already described in chapter
7. When we wrote these classes, we were at the
height of our template craze (did I mention we even had an
implementation of expression templates? (Veldhuizen,
2000)) so you might suspect that the choice
was dictated by the fashion of that time. However, it wouldn’t have
been possible to use dynamic polymorphism. We wanted the solvers to
work with any function pointer or function object, and
boost::function
wasn’t around yet, which forced us to use a template
method. Since the latter couldn’t be virtual, CRTP was the only way
to put the boilerplate code in the base class and let it call a method
defined in derived ones.
A few notes to close this subsection. First: if you want to write a
function that takes a solver, the use of CRTP forces you to make it a
template, which might be awkward. To be honest, most of the times we
didn’t bother and just hard-coded an explicit choice of solver. I
won’t blame you if you do the same. Second: most solvers only use the
passed f
by calling f(x)
, so they work with anything that can be
called as a function, but Newton
and NewtonSafe
also require that
f.derivative(x)
be defined. This, too, might have been awkward if
we used dynamic polymorphism. Third, and last: the Solver1D
interface doesn’t specify if the passed accuracy \(\epsilon\)
should apply to \(x\) (that is, if the returned
\(\tilde{x}\) should be within \(\epsilon\) of the true
root) or to \(f(x)\) (that is, if \(f(\tilde{x})\) should be
within \(\epsilon\) of 0). However, all existing solvers treat
it as the accuracy on \(x\).
Optimizers
Multi-dimensional optimizers are more complex than 1-D solvers. In a nutshell, they find the set of variables \(\tilde{\mathbf{x}}\) for which the cost function \(f(\mathbf{x})\) returns its minimum value; but of course, there’s a bit more to it.
Unlike solvers, optimizers don’t use templates. They inherit from the
base class OptimizationMethod
, shown in the next listing.
OptimizationMethod
class.
class
OptimizationMethod
{
public
:
virtual
~
OptimizationMethod
()
{}
virtual
EndCriteria
::
Type
minimize
(
Problem
&
P
,
const
EndCriteria
&
endCriteria
)
=
0
;
};
Its only method, apart from the virtual
destructor, is minimize
. The method takes a reference to a
Problem
instance, which in turn contains references to the function
to minimize and to any constraints, and performs the calculations; at
the end of which, it has the problem of having too many things to
return. Besides the best-solution array \(\tilde{\mathbf{x}}\),
it must return the reason for exiting the calculation (did
it converge, or are we returning our best guess after
the maximum number of evaluations?) and it would be nice to return
\(f(\tilde{\mathbf{x}})\) as well, since
it’s likely that it was already calculated.
In the current implementation, the method returns the reason for
exiting and stores the other results inside the Problem
instance. This is also the reason for passing the problem as a
non-const
reference; an alternative solution might have been to
leave the Problem
instance alone and to return all required
values in a structure, but I see how this might be seen as more
cumbersome. On the other hand, I see no reason for minimize
itself to be non-const
: my guess is that it was an oversight on
our part (I’ll get back to this later).
Onward to the Problem
class, shown in the listing below.
Problem
class.
class
Problem
{
public
:
Problem
(
CostFunction
&
costFunction
,
Constraint
&
constraint
,
const
Array
&
initialValue
=
Array
());
Real
value
(
const
Array
&
x
);
Disposable
<
Array
>
values
(
const
Array
&
x
);
void
gradient
(
Array
&
grad_f
,
const
Array
&
x
);
// ... other calculations ...
Constraint
&
constraint
()
const
;
// ... other inspectors ...
const
Array
&
currentValue
();
Real
functionValue
()
const
;
void
setCurrentValue
(
const
Array
&
currentValue
);
Integer
functionEvaluation
()
const
;
// ... other results ...
};
As I mentioned, it groups together arguments such as the cost function to minimize, the constraints, and an optional guess. It provides methods that call the underlying cost function while keeping track of the number of evaluation, and that I’ll describe when talking about cost functions; a few inspectors for its components; and methods to retrieve the results (as well as to set them; the latter are used by the optimizers).
The problem (no pun intended) is that it takes and stores its
components as non-const
references. I’ll talk later about
whether they might be const
instead. The fact that they’re
reference is an issue in itself, since it puts the responsibility on
client code to make sure that their lifetimes last at least as much as
that of the Problem
instance.
In an alternative implementation in which the optimizer returned its
results in a structure, the issue might be moot: we might do away with
the Problem
class and pass its components directly to the
minimize
method. This would sidestep the lifetime issue, since
they wouldn’t be stored. The disadvantage would be that each
optimizer would have to keep track of the number of function
evaluations, causing some duplication in the code base.
Also unlike for 1-D solvers, the cost function is not a template
parameter for the minimization method. It needs to inherit from the
CostFunction
class, shown in the next listing.
CostFunction
class.
class
CostFunction
{
public
:
virtual
~
CostFunction
()
{}
virtual
Real
value
(
const
Array
&
x
)
const
=
0
;
virtual
Array
values
(
const
Array
&
x
)
const
=
0
;
virtual
void
gradient
(
Array
&
grad
,
const
Array
&
x
)
const
;
virtual
Real
valueAndGradient
(
Array
&
grad
,
const
Array
&
x
)
const
;
virtual
void
jacobian
(
Matrix
&
jac
,
const
Array
&
x
)
const
;
virtual
Array
valuesAndJacobian
(
Matrix
&
jac
,
const
Array
&
x
)
const
;
};
Unsurprisingly, its interface declares the value
method, which
returns, well, the value of the function for the given array of
arguments;30 but it also declares a
values
method returning an array, which is a bit more
surprising until you remember that optimizers are often used for
calibrating over a number of quotes. Whereas value
returns the
total error to minimize (let’s say, the sum of the squares of the
errors, or something like it), values
returns the set of errors
over each quote; there are algorithms that can make use of this
information to converge more quickly.
Other methods return the derivatives of the value, or the values, for
use by some specific algorithms: gradient
calculates the
derivative of value
with respect to each variable and stores it
in the array passed as first argument, jacobian
does the same
for values
filling a matrix, and the valueAndGradient
and valuesAndJacobian
methods calculate both values and
derivatives at the same time for efficiency. They have a default
implementation that calculates numerical derivatives; but of course
that’s costly, and derivative-based algorithms should only be used if
you can override the method with an analytic calculation.
A note: checking the interface of CostFunction
shows that all
its methods are declared as const
, so passing it as non-{const}
reference to the Problem
constructor was probably a goof.
Changing it to const
would widen the contract of the
constructor, so it should be possible without breaking backward
compatibility.
Finally, the Constraint
class, shown in the following listing.
Constraint
class.
class
Constraint
{
protected
:
class
Impl
;
public
:
bool
test
(
const
Array
&
p
)
const
;
Array
upperBound
(
const
Array
&
params
)
const
;
Array
lowerBound
(
const
Array
&
params
)
const
;
Real
update
(
Array
&
p
,
const
Array
&
direction
,
Real
beta
);
Constraint
(
const
shared_ptr
<
Impl
>&
impl
=
shared_ptr
<
Impl
>
());
};
It works as a base class for constraints to be applied to the domain
of the cost function; the library also defines a few predefined ones,
not shown here, as well as a CompositeConstraint
class that can be
used to merge a number of them into a single one.
Its main method is test
, which takes an array of variables and
returns whether or not they satisfy the constraint; that is, if the
array belongs to the domain we specified as valid. It also defines
the upperBound
and lowerBound
methods, which in theory should
specify the maximum and minimum value of the variables but in practice
can’t always specify them correctly; think of the case in which the
domain is a circle, and you’ll see that there are cases in which
\(x\) and \(y\) are both between their upper and lower bounds
but the resulting point is outside the domain.
A couple more notes. First, the Constraint
class also defines an
update
method. It’s not const
, which would make sense if it
updated the constraint; except it doesn’t. It takes an array of
variables and a direction, and extends the original array in the given
direction until it satisfies the constraint. It should have been
const
, it should have been named differently, and as the kids say I
can’t even. This might be fixed, though. Second, the class uses the
pimpl idiom (see a previous section) and the
default constructor also takes an optional pointer to the
implementation. Were I to write the class today, I’d have a default
constructor taking no arguments and an additional constructor taking
the implementation and declared as protected and to be used by derived
classes only.
Some short final thoughts on the const
-correctness of these classes.
In summary: it’s not great, with some methods that can be fixed and
some others that can’t. For instance, changing the minimize
method would break backwards compatibility (since it’s a virtual
method, and const
ness is part of its signature) as well as a few
optimizers, that call other methods from minimize
and use data
members as a way to transfer information between
methods.31 We could have avoided this if we had put some more
effort in reviewing code before version 1.0. Let this be a lesson for
you, young coders.
Statistics
The statistics classes were written mostly to collect samples from Monte Carlo simulations (you’ll remember them from chapter 6). The full capabilities of the library are implemented as a series of decorators, each one adding a layer of methods, instead of a monolith; as far as I can guess, that’s because you can choose one of two classes at the bottom of the whole thing. A layered design gives you the possibility to build more advanced capabilities just once, based on the common interface of the bottom layer.
The first class you can choose, shown below, is called
IncrementalStatistics
, and has the interface you would more or
less expect: it can return the number of samples, their combined
weight in case they were weighed, and a number of statistics results.
IncrementalStatistics
class.
class
IncrementalStatistics
{
public
:
typedef
Real
value_type
;
IncrementalStatistics
();
Size
samples
()
const
;
Real
weightSum
()
const
;
Real
mean
()
const
;
Real
variance
()
const
;
Real
standardDeviation
()
const
;
Real
errorEstimate
()
const
;
// skewness, kurtosis, min, max...
void
add
(
Real
value
,
Real
weight
=
1.0
);
template
<
class
DataIterator
>
void
addSequence
(
DataIterator
begin
,
DataIterator
end
);
};
Samples can be added one by one or as a sequence delimited by two iterators. The shtick of this class is that it doesn’t store the data it gets passed, but instead it updates the statistics on the fly; the idea was to save the memory that would be otherwise used for the storage, and in the year 2000 (when a computer might have 128 or 256 MB of RAM) it was a bigger concern than it is now. The implementation used to be homegrown; nowadays it’s written in terms of the Boost accumulator library.
The second class, GeneralStatistics
, implements the same
interface and adds a few other methods, made possible by the fact that
it stores (and thus can return) the passed data; for instance, it can
return percentiles or sort its data. It also provides a template
expectationValue
method that can be used for bespoke
calculations; if you’re interested, there’s more on that in the aside
at the end of this section.
GeneralStatistics
class.
class
GeneralStatistics
{
public
:
// ... same as IncrementalStatistics ...
const
std
::
vector
<
std
::
pair
<
Real
,
Real
>
>&
data
()
const
;
template
<
class
Func
,
class
Predicate
>
std
::
pair
<
Real
,
Size
>
expectationValue
(
const
Func
&
f
,
const
Predicate
&
inRange
)
const
;
Real
percentile
(
Real
y
)
const
;
// ... other inspectors ...
void
sort
()
const
;
void
reserve
(
Size
n
)
const
;
};
Next, the outer layers. The first ones add statistics associated with
risk, like expected shortfall or value at risk; the problem being that
you usually need the whole set of samples for those. In the case of
incremental statistics, therefore, we have to forgo the exact
calculation and look for approximations. One possibility is to take
the mean and variance of the samples, suppose they come from a
Gaussian distribution with the same moments, and get analytic results
based on this assumption; that’s what the
GenericGaussianStatistics
class does.
GaussianStatistics
class.
template
<
class
S
>
class
GenericGaussianStatistics
:
public
S
{
public
:
typedef
typename
S
::
value_type
value_type
;
GenericGaussianStatistics
()
{}
GenericGaussianStatistics
(
const
S
&
s
)
:
S
(
s
)
{}
Real
gaussianDownsideVariance
()
const
;
Real
gaussianDownsideDeviation
()
const
;
Real
gaussianRegret
(
Real
target
)
const
;
Real
gaussianPercentile
(
Real
percentile
)
const
;
Real
gaussianValueAtRisk
(
Real
percentile
)
const
;
Real
gaussianExpectedShortfall
(
Real
percentile
)
const
;
// ... other measures ...
};
typedef
GenericGaussianStatistics
<
GeneralStatistics
>
GaussianStatistics
;
As I mentioned, and as you can see, it’s implemented as a decorator; it
takes the class to decorate as a template parameter, inherits from it
so that it still has all the methods of its base class, and adds the
new methods. The library provides a default instantiation,
GaussianStatistics
, in which the template parameter is
GeneralStatistics
. Yes, I would have expected the incremental
version, too, but there’s a reason for this; bear with me for a
minute.
When the base class stores the full set of samples, we can write a
decorator that calculates the actual risk measures; that would be the
GenericRiskStatistics
class. As for the Gaussian statistics, I
won’t discuss the implementation (you can look them up in the
library).
RiskStatistics
class.
template
<
class
S
>
class
GenericRiskStatistics
:
public
S
{
public
:
typedef
typename
S
::
value_type
value_type
;
Real
downsideVariance
()
const
;
Real
downsideDeviation
()
const
;
Real
regret
(
Real
target
)
const
;
Real
valueAtRisk
(
Real
percentile
)
const
;
Real
expectedShortfall
(
Real
percentile
)
const
;
// ... other measures ...
};
typedef
GenericRiskStatistics
<
GaussianStatistics
>
RiskStatistics
;
As you can see, the layers can be combined; the default
instantiation provided by the library, RiskStatistics
, takes
GaussianStatistics
as its base and thus provides both Gaussian
and actual measures. This was also the reason why
GeneralStatistics
was used as the base for the latter.
On top of it all, it’s possible to have other decorators; the library
provides a few, but I won’t show their code here. One is
SequenceStatistics
, that can be used when a sample is an array
instead of a single number, uses internally a vector of instances of
scalar statistics classes, and also adds the calculation of the
correlation and covariance between the elements of the samples; it is
used, for instance, in the LIBOR market model, where each
sample usually collects cash flows at different times. Other two are
ConvergenceStatistics
and DiscrepancyStatistics
; they
provide information on the properties of the sequence of samples,
aren’t used anywhere else in the library, but at least we had the
decency of writing unit tests for both of them.
Linear algebra
I don’t have a lot to write about the current implementation of the
Array
and Matrix
classes, shown in the following listing.
Array
and Matrix
classes.
class
Array
{
public
:
explicit
Array
(
Size
size
=
0
);
// ... other constructors ...
Array
(
const
Array
&
);
Array
(
const
Disposable
<
Array
>&
);
Array
&
operator
=
(
const
Array
&
);
Array
&
operator
=
(
const
Disposable
<
Array
>&
);
const
Array
&
operator
+=
(
const
Array
&
);
const
Array
&
operator
+=
(
Real
);
// ... other operators ...
Real
operator
[](
Size
)
const
;
Real
&
operator
[](
Size
);
void
swap
(
Array
&
);
// ... iterators and other utilities ...
private
:
boost
::
scoped_array
<
Real
>
data_
;
Size
n_
;
};
Disposable
<
Array
>
operator
+
(
const
Array
&
,
const
Array
&
);
Disposable
<
Array
>
operator
+
(
const
Array
&
,
Real
);
// ... other operators and functions ...
class
Matrix
{
public
:
Matrix
(
Size
rows
,
Size
columns
);
// ... other constructors, assignment operators etc. ...
const_row_iterator
operator
[](
Size
)
const
;
row_iterator
operator
[](
Size
);
Real
&
operator
()(
Size
i
,
Size
j
)
const
;
// ... iterators and other utilities ...
};
Disposable
<
Matrix
>
operator
+
(
const
Matrix
&
,
const
Matrix
&
);
// ... other operators and functions ...
Their interface is what you would expect:
constructors and assignment operators, element access (the
Array
class provides the a[i]
syntax; the Matrix
class provides both m[i][j]
and m(i,j)
, because we aim
to please), a bunch of arithmetic operators, all working element by
element as usual,32 and a few utilities. There are no
methods for resizing, or for other operations suited for containers,
because this classes are not meant to be used as such; they’re
mathematical utilities. Storage is provided by a
scoped_ptr
, which manages the lifetime of the underlying memory.
In the case of Array
, we also provide a few functions such as
Abs
, Log
and their like; being good citizens, we’re not
overloading the corresponding functions in namespace std
because that’s forbidden by the standard. More complex functionality
(such as matricial square root, or various decompositions) can be
found in separate modules.
In short, a more or less straightforward implementation of arrays and
matrices. The one thing which is not obvious is the presence of the
Disposable
class template, which I’ll describe in more detail
in a further section of this appendix; for the time being, let me just
say that it’s a pre-C++11 attempt at move semantics.
The idea was to try and reduce the abstraction penalty. Operator
overloading is very convenient—after all, c = a+b
is much
easier to read than understand than add(a,b,c)
—but
doesn’t come for free: declaring addition as
Array
operator
+
(
const
Array
&
a
,
const
Array
&
b
);
means that the operator must create and return a new Array
instance, that is, must allocate and possibly copy its memory. When
the number of operations increases, so does the overhead.
In the first versions of the library, we tried to mitigate the problem
by using expression templates. The idea (that I will only describe
very roughly, so I suggest you read (Veldhuizen,
2000) for details) is that operators don’t
return an array, but some kind of parse tree holding references to the
terms of the expression; so, for instance, 2*a+b
won’t actually
perform any calculation but only create a small structure with the
relevant information. It is only when assigned that the expression is
unfolded; and at that point, the compiler would examine the whole
thing and generate a single loop that both calculates the result and
copies it into the array being assigned.
The technique is still relevant today (possibly even more so, given
the progress in compiler technology) but we abandoned it after a few
years. Not all compilers were able to process it, forcing us to
maintain both expression templates and the simpler implementation, and
it was difficult to read and maintain (compare the current
declaration of operator+
with
VectorialExpression
<
BinaryVectorialExpression
<
Array
::
const_iterator
,
Array
::
const_iterator
,
Add
>
>
operator
+
(
const
Array
&
v1
,
const
Array
&
v2
);
for a taste of what the code was like); therefore, when the C++ community
started talking of move semantics and some ideas for implementations
began to appear, we took the hint and switched to Disposable
.
As I said, compilers progressed a lot during these years; nowadays,
I’m guessing that all of them would support an expression-template
implementation, and the technique itself has probably improved.
However, if I were to write the code today (or if I started to change
things) the question might be whether to write classes such as
Array
or Matrix
at all. At the very least, I’d consider
implementing them in terms of std::valarray
, which is supposed
to provide facilities for just such a task. In the end, though, I’d
probably go for some existing library such as uBLAS: it is
available in Boost, it’s written by actual experts in numerical code,
and we already use it in some parts of the library for specialized
calculations.
Global settings
The Settings
class, outlined in the listing below, is
a singleton (see later) that holds information
global to the whole library.
Settings
class.
class
Settings
:
public
Singleton
<
Settings
>
{
private
:
class
DateProxy
:
public
ObservableValue
<
Date
>
{
DateProxy
();
operator
Date
()
const
;
...
};
...
// more implementation details
public
:
DateProxy
&
evaluationDate
();
const
DateProxy
&
evaluationDate
()
const
;
boost
::
optional
<
bool
>&
includeTodaysCashFlows
();
boost
::
optional
<
bool
>
includeTodaysCashFlows
()
const
;
...
};
Most of its data are flags that you can look up in the official documentation, or that you can simply live without; the one piece of information that you’ll need to manage is the evaluation date, which defaults to today’s date and is used for the pricing of instruments and the fixing of any other quantity.
This poses a challenge: instruments whose value can depend on the
evaluation date must be notified when the latter changes. This is
done by returning the corresponding information indirectly, namely,
wrapped inside a proxy class; this can be seen from the signature of
the relevant methods. The proxy inherits from the
ObservableValue
class template (outlined in
the next listing) which is implicitly convertible to
Observable
and overloads the assignment operator in order to
notify any changes. Finally, it allows automatic conversion of the
proxy class to the wrapped value.
ObservableValue
class template.
template
<
class
T
>
class
ObservableValue
{
public
:
// initialization and assignment
ObservableValue
(
const
T
&
t
)
:
value
(
t
),
observable_
(
new
Observable
)
{}
ObservableValue
<
T
>&
operator
=
(
const
T
&
t
)
{
value_
=
t
;
observable_
->
notifyObservers
();
return
*
this
;
}
// implicit conversions
operator
T
()
const
{
return
value_
;
}
operator
shared_ptr
<
Observable
>
()
const
{
return
observable_
;
}
private
:
T
value_
;
shared_ptr
<
Observable
>
observable_
;
};
This allows one to use the facility with a natural syntax. On the one hand, it is possible for an observer to register with the evaluation date, as in:
registerWith
(
Settings
::
instance
().
evaluationDate
());
on the other hand, it is possible to use the returned value just like
a Date
instance, as in:
Date
d2
=
calendar
.
adjust
(
Settings
::
instance
().
evaluationDate
());
which triggers an automatic conversion; and on the gripping hand, an assignment can be used for setting the evaluation date, as in:
Settings
::
instance
().
evaluationDate
()
=
d
;
which will cause all observers to be notified of the date change.
Of course, the elephant in the room is the fact that we have a global
evaluation date at all. The obvious drawback is that one can’t
perform two parallel calculations with two different evaluation dates,
at least in the default library configuration; but while this is true,
it is also not the whole story. On the one hand, there’s a
compilation flag that allows a program to have one distinct
Settings
instance per thread (with a bit of work on the part of
the user) but as we’ll see, this doesn’t solve all the issues. On the
other hand, the global data may cause unpleasantness even in a
single-threaded program: even if one wanted to evaluate just an
instrument on a different date, the change will trigger recalculation
for every other instrument in the system when the evaluation date is
set back to its original value.
This clearly points (that is, quite a few smart people had the same idea when we talked about it) to some kind of context class that should replace the global settings. But how would one select a context for any given calculation?
It would be appealing to add a setContext
method to the
Instrument
class, and to arrange things so that during
calculation the instrument propagates the context to its engine and in
turn to any term structures that need it. However, I don’t think this
can be implemented easily.
First, the instrument and its engine are not always aware of all the term structures that are involved in the calculation. For instance, a swap contains a number of coupons, any of which might or might not reference a forecast curve. We’re not going to reach them unless we add the relevant machinery to all the classes involved. I’m not sure that we want to set a context to a coupon.
Second, and more important, setting the context for an engine would be
a mutating operation. Leaving it to the instrument during
calculations would execute it at some point during the call to its
NPV
method, which is supposed to be const
. This would
make it way too easy to trigger a race condition; for instance with a
harmless-looking operation such as using the same discount curve for
two instruments and evaluating them at different dates. If you have a
minimum of experience in parallel programming, you wouldn’t dream of, say,
relinking the same handle in two concurrent threads; but when the
mutation is hidden inside a const
method, you might not be
aware of it. (But wait, you say. Aren’t there other mutating
operations possibly being done during the call to NPV
? Good
catch: see the aside at the end of this section.)
So it seems that we have to set up the context before starting the calculation. This rules out driving the whole thing from the instrument (because, again, we would be hiding the fact that setting a context to an instrument could undo the work done by another that shared a term structure with the first) and suggests that we’d have to set the context explicitly on the several term structures. On the plus side, we no longer run the risk of a race in which we unknowingly try to set the same context to the same object. The drawbacks are that our setup just got more complex, and that we’d have to duplicate curves if we want to use them concurrently in different contexts: two parallel calculations on different dates would mean, for instance, two copies of the overnight curve for discounting. And if we have to do this, we might as well manage with per-thread singletons.
Finally, I’m skipping over the scenario in which the context is passed but not saved. It would lead to method calls like
termStructure
->
discount
(
t
,
context
);
which would completely break caching, would cause discomfort to all parties involved, and if we wanted stuff like this we’d write in Haskell.
To summarize: I hate to close the section on a gloomy note, but all is not well. The global settings are a limitation, but I don’t have a solution; and what’s worse, the possible changes increase complexity. We would not only tell first-time users looking for the Black-Scholes formula that they needs term structures, quotes, an instrument and an engine: we’d also put contexts in the mix. A little help here?
Utilities
In QuantLib, there are a number of classes and functions which don’t model a financial concept. They are nuts and bolts, used to build some of the scaffolding for the rest of the library. This section is devoted to some of these facilities.
Smart pointers and handles
The use of run-time polymorphism dictates that many, if not most, objects be allocated on the heap. This raises the problem of memory management—a problem solved in other languages by built-in garbage collection, but left in C++ to the care of the developer.
I will not dwell on the many issues in memory management, especially since they are now mostly a thing of the past. The difficulty of the task (especially in the presence of exceptions) was enough to discourage manual management; therefore, ways were found to automate the process.
The weapons of choice in the C++ community came to be smart pointers:
classes that act like built-in pointers but that can take care of the
survival of the pointed objects while they are still needed and of
their destruction when this is no longer the case. Several
implementations of such classes exist which use different techniques;
we chose the smart pointers from the Boost libraries (most notably
shared_ptr
, now included in the ANSI/ISO C++ standard). Don’t
look for boost::shared_ptr
in the library, though: the relevant
classes are imported in an internal QuantLib namespace and used, e.g.,
as ext::shared_ptr
. This will allow us to switch to the C++11
implementation painlessly when the time comes.
I won’t go into details on shared pointers; you can browse the Boost
site for documentation. Here, I’ll just mention that
their use in QuantLib completely automated memory management. Objects
are dynamically allocated all over the place; however, there is not
one single delete
statement in all the tens of thousands of lines of
which the library consists.
Pointers to pointers (if you need a quick refresher, see the aside at the end of the section for their purpose and semantics) were also replaced by smart equivalents. We chose not to just use smart pointers to smart pointers; on the one hand, because having to write
ext
::
shared_ptr
<
ext
::
shared_ptr
<
YieldTermStructure
>
>
gets tiresome very quickly—even in Emacs; on the other hand, because
the inner shared_ptr
would have to be allocated dynamically,
which just didn’t felt right; and on the gripping hand, because it
would make it difficult to implement observability. Instead, a class
template called Handle
was provided for this purpose. Its
implementation, shown in the listing that follows, relies on an
intermediate inner class called Link
which stores a smart
pointer. In turn, the Handle
class stores a smart pointer to a
Link
instance, decorated with methods that make it easier to
use it. Since all copies of a given handle share the same link, they
are all given access to the new pointee when any one of them is linked
to a new object.
Handle
class template.
template
<
class
Type
>
class
Handle
{
protected
:
class
Link
:
public
Observable
,
public
Observer
{
public
:
explicit
Link
(
const
shared_ptr
<
Type
>&
h
=
shared_ptr
<
Type
>
());
void
linkTo
(
const
shared_ptr
<
Type
>&
);
bool
empty
()
const
;
void
update
()
{
notifyObservers
();
}
private
:
shared_ptr
<
Type
>
h_
;
};
shared_ptr
<
Link
<
Type
>
>
link_
;
public
:
explicit
Handle
(
const
shared_ptr
<
Type
>&
h
=
shared_ptr
<
Type
>
());
const
shared_ptr
<
Type
>&
operator
->
()
const
;
const
shared_ptr
<
Type
>&
operator
*
()
const
;
bool
empty
()
const
;
operator
shared_ptr
<
Observable
>
()
const
;
};
template
<
class
Type
>
class
RelinkableHandle
:
public
Handle
<
Type
>
{
public
:
explicit
RelinkableHandle
(
const
shared_ptr
<
Type
>&
h
=
shared_ptr
<
Type
>
());
void
linkTo
(
const
shared_ptr
<
Type
>&
);
};
The contained shared_ptr<Link>
also gives the handle the means
to be observed by other classes. The Link
class is both an
observer and an observable; it receives notifications from its pointee
and forwards them to its own observers, as well as sending its own
notification each time it is made to point to a different
pointee. Handles take advantage of this behavior by defining an
automatic conversion to shared_ptr<Observable>
which simply
returns the contained link. Thus, the statement
registerWith
(
h
);
is legal and works as expected; the registered observer will receive notifications from both the link and (indirectly) the pointed object.
You might have noted that the means of relinking a handle (i.e., to
have all its copies point to a different object) were not given to the
Handle
class itself, but to a derived RelinkableHandle
class. The rationale for this is to provide control over which handle
can be used for relinking—and especially over which handle can’t.
In the typical use case, a Handle
instance will be instantiated
(say, to store a yield curve) and passed to a number of instruments,
pricing engines, or other objects that will store a copy of the handle
and use it when needed. The point is that an object (or client code
getting hold of the handle, if the object exposes it via an inspector)
must not be allowed to relink the handle it stores, whatever the
reason; doing so would affect a number of other object.33
The link should only be changed from the original handle—the main handle, if you like.
Given the frailty of human beings, we wanted this to be enforced by
the compiler. Making the linkTo
method a const
one and
returning const
handles from our inspectors wouldn’t work;
client code could simply make a copy to obtain a non-const
handle. Therefore, we removed linkTo
from the Handle
interface and added it to a derived class. The type system works
nicely to our advantage. On the one hand, we can instantiate the
main handle as a RelinkableHandle
and pass it to any object
expecting a Handle
; automatic conversion from derived to base
class will occur, leaving the object with a sliced but fully
functional handle. On the other hand, when a copy of a Handle
instance is returned from an inspector, there’s no way to downcast it
to RelinkableHandle
.
Error reporting
There are a great many places in the library where some condition must be checked. Rather than doing it as
if
(
i
>=
v
.
size
())
throw
Error
(
"index out of range"
);
we wanted to express the intent more clearly, i.e., with a syntax like
require
(
i
<
v
.
size
(),
"index out of range"
);
where on the one hand, we write the condition to be satisfied and not
its opposite; and on the other hand, terms such as require
,
ensure
, or assert
—which have a somewhat canonical
meaning in programming—would tell whether we’re checking a
precondition, a postcondition, or a programmer error.
We provided the desired syntax with macros. “Get behind thee,” I hear you say. True, macros have a bad name, and in fact they caused us a problem or two, as we’ll see below. But in this case, functions had a big disadvantage: they evaluate all their arguments. Many times, we want to create a moderately complex error message, such as
require
(
i
<
v
.
size
(),
"index "
+
to_string
(
i
)
+
" out of range"
);
If require
were a function, the message would be built whether
or not the condition is satisfied, causing a performance hit that
would not be acceptable. With a macro, the above is textually
replaced by something like
if
(
!
(
i
<
v
.
size
()))
throw
Error
(
"index "
+
to_string
(
i
)
+
" out of range"
);
which builds the message only if the condition is violated.
The next listing shows the current version of one of the
macros, namely, QL_REQUIRE
; the other macros are defined in a
similar way.
QL_REQUIRE
macro.
#define QL_REQUIRE(condition,message) \
if (!(condition)) { \
std::ostringstream _ql_msg_stream; \
_ql_msg_stream << message; \
throw QuantLib::Error(__FILE__,__LINE__, \
BOOST_CURRENT_FUNCTION, \
_ql_msg_stream.str()); \
} else
Its definition has a few more bells and whistles that
might be expected. Firstly, we use an ostringstream
to build the
message string. This allows one to use a syntax like
QL_REQUIRE
(
i
<
v
.
size
(),
"index "
<<
i
<<
" out of range"
);
to build the message (you can see how that works by replacing the
pieces in the macro body). Secondly, the Error
instance is
passed the name of the current function as well as the line and file
where the error is thrown. Depending on a compilation flag, this
information can be included in the error message to help developers;
the default behavior is to not include it, since it’s of little
utility for users. Lastly, you might be wondering why we added an
else
at the end of the macro. That is due to a common macro
pitfall, namely, its lack of a lexical scope. The else
is
needed by code such as
if
(
someCondition
())
QL_REQUIRE
(
i
<
v
.
size
(),
"index out of bounds"
);
else
doSomethingElse
();
Without the else
in the macro, the above would not work as
expected. Instead, the else
in the code would pair with
the if
in the macro and the code would translate into
if
(
someCondition
())
{
if
(
!
(
i
<
v
.
size
()))
throw
Error
(
"index out of bounds"
);
else
doSomethingElse
();
}
which has a different behavior.
As a final note, I have to describe a disadvantage of these macros. As they are now, they throw exceptions that can only return their contained message; no inspector is defined for any other relevant data. For instance, although an out-of-bounds message might include the passed index, no other method in the exception returns the index as an integer. Therefore, the information can be displayed to the user but would be unavailable to recovery code in catch clauses—unless one parses the message, that is; but that is hardly worth the effort. There’s no planned solution at this time, so drop us a line if you have one.
Disposable objects
The Disposable
class template was an attempt to implement move
semantics in C++03 code. To give credit where it’s due, we took the
idea and technique from an article by Andrei Alexandrescu
(Alexandrescu, 2003) in which he described how
to avoid copies when returning temporaries.
The basic idea is the one that was starting to float around in those
years and that was given its final form in C++11: when passing a
temporary object, copying it into another one is often less efficient
than swapping its contents with those of the target. You want to move
a temporary vector? Copy into the new object the pointer to its
storage, instead of allocating a new one and copying the elements. In
modern C++, the language itself supports move semantics with the
concept of rvalue reference (Hinnant et al, 2006); the
compiler knows when it’s dealing with a temporary, and we can use
std::move
in the few cases when we want to turn an object into one.
In our implementation, shown in the following listing, we don’t
have such support; you’ll see the consequences of this in a minute.
Disposable
class template.
template
<
class
T
>
class
Disposable
:
public
T
{
public
:
Disposable
(
T
&
t
)
{
this
->
swap
(
t
);
}
Disposable
(
const
Disposable
<
T
>&
t
)
:
T
()
{
this
->
swap
(
const_cast
<
Disposable
<
T
>&>
(
t
));
}
Disposable
<
T
>&
operator
=
(
const
Disposable
<
T
>&
t
)
{
this
->
swap
(
const_cast
<
Disposable
<
T
>&>
(
t
));
return
*
this
;
}
};
The class itself is not much to look at. It relies on the template
argument implementing a swap
method; this is where any resource
contained inside the class are swapped (hopefully in a cheap way)
instead of copied. The constructors and the assignment operator all
use this to move stuff around without copies—with a difference,
depending on what is passed. When building a Disposable
from
another one, we take it by const
reference because we want the
argument to bind to temporaries; that’s what most disposables will be.
This forces us to use a const_cast
in the body, when it’s time
to call swap
and take the resources from the disposable. When
building a Disposable
from a non-disposable object, instead, we
take is as a non-const
reference; this is to prevent ourselves
from triggering unwanted destructive conversions and from finding
ourselves with the empty husk of an object when we thought to have a
usable one. This, however, has a disadvantage; I’ll get to it in a
minute.
The next listing shows how to retrofit Disposable
to a
class; Array
, in this case.
Disposable
class template in the Array
class.
Array
::
Array
(
const
Disposable
<
Array
>&
from
)
:
data_
((
Real
*
)(
0
)),
n_
(
0
)
{
swap
(
const_cast
<
Disposable
<
Array
>&>
(
from
));
}
Array
&
Array
::
operator
=
(
const
Disposable
<
Array
>&
from
)
{
swap
(
const_cast
<
Disposable
<
Array
>&>
(
from
));
return
*
this
;
}
void
Array
::
swap
(
Array
&
from
)
{
data_
.
swap
(
from
.
data_
);
std
::
swap
(
n_
,
from
.
n_
);
}
As you see, we need to add a
constructor and an assignment operator taking a Disposable
(in
C++11, they would be a move constructor and a move assignment
operators taking an rvalue reference) as well as the swap
method that will be used in all of them. Again, the constructors take
the Disposable
by const
reference and cast it later, in
order to bind to temporaries—although now that I think of it, they
could take it by copy, adding another cheap swap.
Finally, the way Disposable
is used is by returning it from
function, like in the following code:
Disposable
<
Array
>
ones
(
Size
n
)
{
Array
result
(
n
,
1.0
);
return
result
;
}
Array
a
=
ones
(
10
);
Returning the array causes it to be converted to Disposable
,
and assigning the returned object causes its contents to be swapped
into a
.
Now, you might remember that I talked about a disadvantage when I
showed you the Disposable
constructor being safe and taking an
object by non-const
reference. It’s that it can’t bind to temporaries; therefore, the function above can’t be written more simply as:
Disposable
<
Array
>
ones
(
Size
n
)
{
return
Array
(
n
,
1.0
);
}
because that wouldn’t compile. This forces us to take the more verbose route and give the array a name.34
Nowadays, of course, we’d use rvalue references and move constructors
and forget all about the above. To tell the truth, I’ve a nagging
suspicion that Disposable
might be getting in the way of the
compiler and doing more harm than good. Do you know the best way to
write code like the above and avoid abstraction penalty in modern
C++? It’s this one:
Array
ones
(
Size
n
)
{
return
Array
(
n
,
1.0
);
}
Array
a
=
ones
(
10
);
In C++17, the copies that might have been done when returning the
array and when assigning it are guaranteed to be elided (that is, the
compiler will generate code that builds the returned array directly
inside the one we’re assigning); most recent compilers have been doing
that for a while, without waiting for the standard to bind them. It’s
called RVO, for Return Value Optimization, and using
Disposable
prevents it and thus might make the code slower
instead of faster.
Design patterns
A few design patterns were implemented in QuantLib. You can refer to the Gang of Four book (Gamma et al, 1995) for a description of such patterns; so why do I write about them? Well, as once noted by G. K. Chesterton,
[p]oets have been mysteriously silent on the subject of cheese
and the Gang was just as silent on a number of issues that come up when you write actual implementations—through no fault of them, mind you. The variations are almost limitless, and they were only four.
Thus, I will use this final section to point out a few ways in which our implementations were tailored to the requirements of the library.
The Observer pattern
The use of the Observer pattern in the QuantLib library is widespread; you’ve seen it used in chapter 2 and chapter 3 to let financial instruments and term structures keep track of changes and recalculate when needed.
Our version of the pattern (sketched in the next listing) is close enough to that described in the Gang of Four book; but as I mentioned, there are questions and problems that weren’t discussed there.
Observable
and Observer
classes.
class
Observable
{
friend
class
Observer
;
public
:
void
notifyObservers
()
{
for
(
iterator
i
=
observers_
.
begin
();
i
!=
observers_
.
end
();
++
i
)
{
try
{
(
*
i
)
->
update
();
}
catch
(
std
::
exception
&
e
)
{
// store information for later
}
}
}
private
:
void
registerObserver
(
Observer
*
o
)
{
observers_
.
insert
(
o
);
}
void
unregisterObserver
(
Observer
*
);
list
<
Observer
*>
observers_
;
};
class
Observer
{
public
:
virtual
~
Observer
()
{
for
(
iterator
i
=
observables_
.
begin
();
i
!=
observables_
.
end
();
++
i
)
(
*
i
)
->
unregisterObserver
(
this
);
}
void
registerWith
(
const
shared_ptr
<
Observable
>&
o
)
{
o
->
registerObserver
(
this
);
observables_
.
insert
(
o
);
}
void
unregisterWith
(
const
shared_ptr
<
Observable
>&
);
virtual
void
update
()
=
0
;
private
:
list
<
shared_ptr
<
Observable
>
>
observables_
;
};
For instance: what information should we include in the notification?
In our implementation, we went for minimalism—all that an observer
gets to know is that something changed. It would have been possible to
provide more information (e.g., by having the update
method
take the notifying observable as an argument) so that observers could
select what to recalculate and save a few cycles; but I don’t think
that this feature was worth the added complexity.
Another question: what happens if an observer raises an exception from
its update
method? This would happen when an observable is
sending a notification, i.e., while the observable is iterating over
its observers, calling update
on each one. If the exception
were to propagate, the loop would be aborted and a number of observers
would not receive the notification—bad. Our solution was to catch
any such exception, complete the loop, and raise an exception at the
end if anything went wrong. This causes the original exceptions to be
lost, which is not good either; however, we felt this to be the lesser
of the two evils.
Onwards to the third issue: that is, copy behavior. It is not very clear what should happen when an observer or an observable are copied. Currently, what seemed a sensible choice is implemented: on the one hand, copying an observable results in the copy not having any observer; on the other hand, copying an observer results in the copy being registered with the same observables as the original. However, other behaviors might be considered; as a matter of fact, the right choice might be to inhibit copying altogether.
The big problems, however, were two. First: we obviously had to make sure that the lifetimes of the observer and observables were managed properly, meaning that no notification should be sent to an already deleted object. To do so, we had observers store shared pointers to their observables, which ensures that no observable is deleted before an observer is done with it. The observers will unregister with any observable before being deleted, which in turn makes it safe for observables to store a list of raw pointers to their observers.
This, however, is only guaranteed to work in a single-threaded setting; and we are exporting QuantLib bindings to C# and Java, where unfortunately there is always another thread where the garbage collector is busy deleting stuff. Every once in a while, this caused random crashes as a notification was sent to a half-deleted object. Once the problem was understood, it was fixed (hi, Klaus); however, the fix slows down the code, so it’s inactive by default and can be enabled by a compilation switch. Use it if you need the C# or Java bindings.
The second big problem is35 that, like in the Jerry Lee Lewis’
song, there’s a whole lotta notifyin’ going on. A change of date can
easily trigger tens or hundreds of notifications; and even if most of
the update
methods only set a flag and forward the call, the time
adds up.
People using QuantLib in applications where calculation time is
paramount, such as CVA/XVA (hi, Peter) have worked
around the problem by disabling notifications and recalculating
explicitly. A step towards reducing notification time would be to
remove the middlemen, and shorten the chains of notifications;
however, this is not possible due to the ubiquitous presence of the
Handle
class in the chains. Handles can be relinked, and thus
chains of dependencies can change even after objects are built.
In short, the problem is still not solved. You know where to find us if you have any bright ideas.
The Singleton pattern
The Gang of Four devoted the first part of their book to creational patterns. While logically sound, this choice turned out to have an unfortunate side effect: all too often, overzealous programmers would start to read the book and duly proceed to sprinkle their code with abstract factories and singletons. Needless to say, this does less than intended for the clarity of the code.
You might suspect the same reason for the presence of a
Singleton
class template in QuantLib. (Quite maliciously, I
might add. Shame on you.) Fortunately, we can base our defense on
version-control logs; such class was added to the library later than,
say, Observer (a behavioral pattern) or Composite (a structural one).
Our default implementation is shown in the following listing.
Singleton
class template.
template
<
class
T
>
class
Singleton
:
private
noncopyable
{
public
:
static
T
&
instance
();
protected
:
Singleton
();
};
#if defined(QL_ENABLE_SESSIONS)
// the definition must be provided by the user
Integer
sessionId
();
#endif
template
<
class
T
>
T
&
Singleton
<
T
>::
instance
()
{
static
map
<
Integer
,
shared_ptr
<
T
>
>
instances_
;
#if defined(QL_ENABLE_SESSIONS)
Integer
id
=
sessionId
();
#else
Integer
id
=
0
;
#endif
shared_ptr
<
T
>&
instance
=
instances_
[
id
];
if
(
!
instance
)
instance
=
shared_ptr
<
T
>
(
new
T
);
return
*
instance
;
}
It’s based on the Curiously Recurring Template Pattern, that I
described in chapter 7; to be a singleton, a class C
needs to inherit from Singleton<C>
, to provide a private constructor
taking on arguments, and to make Singleton<C>
a friend so that it
can use it. You can see an example in the Settings
class.
As suggested by Scott Meyers (Meyers, 2005), the map
holding the instances (bear with me) is defined as a static variable
inside the instance
method. This prevents the so-called static
initialization order fiasco, in which the variable is used before
being defined, and in C++11 it has the additional guarantee that the
initialization is thread-safe (even though that’s not the whole story,
as we’ll see).
Now, you might have a few questions; e.g., why a map of instances if
this is supposed to be a singleton? Well, that’s because having a
single instance might be limiting; for instance—no pun
intended—you might want to perform simultaneous calculations on
different evaluation dates. Thus, we tried to mitigate the problem by
allowing one Singleton
instance per thread. This is enabled by
a compilation flag, and causes the instance
method to use the
#if
branch in which it gets an id from a sessionId
function and uses it to index into the map. If you enable per-thread
singletons, you must also provide the latter function; it will
probably be something like
Integer
sessionId
()
{
return
/* `some unique thread id from your system API` */
;
}
in which you will identify the thread using the functions made
available by your operating system (or some threading library), turn
the identifier into a unique integer, and return it. In turn, this
will cause the instance
method to return a unique instance per each
thread. If you don’t enable the feature, instead, the id will always
ever be 0
and you’ll always get the same instance. In this case,
you probably don’t want to use threads at all—and in the other case,
you obviously do, but you have to be careful anyway: see the
discussion in the section on global settings.
You might also be asking yourself why I said that this is our
default implementation. Nice catch. There are others, which I
won’t show here and which are enabled by a number of compilation
flags. On the one hand, it turned out that the static-variable
implementation didn’t work when compiled as managed C++ code under
.NET (at least with older Visual Studio compilers), so in
that case we switch it with one in which the map is a static class
variable. On the other hand, if you want to use a global
Singleton
instance in a multi-threaded setting, you want to
make sure that the initialization of the Singleton
instance is
thread-safe (I’m talking about the instance itself, not the map
containing it; it’s where the new T
is executed). This
requires locks, mutexes and stuff, and we don’t want to go near any of
that in the default single-threaded setting; therefore, that code is
behind yet another compilation flag. You can look at it in the
library, if you’re interested.
Your last question might be whether we should have a Singleton
class
at all—and it’s a tough one. Again, I refer you to the previous
discussion of global settings. At this time, much like democracy
according to Winston Churchill, it seems to be the worst solution
except for all the others.
The Visitor pattern
Our implementation, shown in the next listing, follows the
Acyclic Visitor pattern (Martin, 1997) rather than
the one in the Gang of Four book: we defined a degenerate
AcyclicVisitor
class, to be used in the interfaces, and a class
template Visitor
which defines the pure virtual visit
method for
its template argument.
AcyclicVisitor
class and of the Visitor
class template.
class
AcyclicVisitor
{
public
:
virtual
~
AcyclicVisitor
()
{}
};
template
<
class
T
>
class
Visitor
{
public
:
virtual
~
Visitor
()
{}
virtual
void
visit
(
T
&
)
=
0
;
};
The pattern also needs support from any class hierarchy that we want to be visitable; an example is shown in the listing that follows.
void
Event
::
accept
(
AcyclicVisitor
&
v
)
{
Visitor
<
Event
>*
v1
=
dynamic_cast
<
Visitor
<
Event
>*>
(
&
v
);
if
(
v1
!=
0
)
v1
->
visit
(
*
this
);
else
QL_FAIL
(
"not an event visitor"
);
}
void
CashFlow
::
accept
(
AcyclicVisitor
&
v
)
{
Visitor
<
CashFlow
>*
v1
=
dynamic_cast
<
Visitor
<
CashFlow
>*>
(
&
v
);
if
(
v1
!=
0
)
v1
->
visit
(
*
this
);
else
Event
::
accept
(
v
);
}
void
Coupon
::
accept
(
AcyclicVisitor
&
v
)
{
Visitor
<
Coupon
>*
v1
=
dynamic_cast
<
Visitor
<
Coupon
>*>
(
&
v
);
if
(
v1
!=
0
)
v1
->
visit
(
*
this
);
else
CashFlow
::
accept
(
v
);
}
Each of the classes in the hierarchy (or at least, those that we want
to be specifically visitable) need to define an accept
method
that takes a reference to AcyclicVisitor
. Each of the methods
tries to cast the passed visitor to the specific Visitor
instantiation for the corresponding class. A successful cast means
that the visitor defines a visit
method taking this specific
class, so we invoke it. A failed cast means that we have to look for
an fallback. If the class is not the root of the hierarchy (like
CashFlow
or Coupon
in the listing) we can call the
base-class implementation of accept
, which in turn will try the
cast. If we’re at the root, like the Event
class, we have no
further fallback and we raise an exception.36
Finally, a visitor is implemented as the BPSCalculator
class in
chapter 4. It inherits from
AcyclicVisitor
, so that it can be passed to the various accept
methods, as well as from an instantiation of the Visitor
template
for each class for which it will provide a visit
method. It will be
passed to the accept
method of some instance, which will eventually
call one of the visit
methods or raise an exception.
I already discussed the usefulness of the Visitor pattern in chapter 4, so I refer you to it (the unsurprising summary: it depends). Therefore, I will only spend a couple of words on why we chose Acyclic Visitor.
In short, the Gang-of-Four version of Visitor might be a bit faster,
but it’s a lot more intrusive; in particular, every time you add a new
class to the visitable hierarchy you’re also forced to go and add the
corresponding visit
method to each existing visitor (where by
“forced” I mean that your code wouldn’t compile if you didn’t). With
Acyclic Visitor, you don’t need to do it; the accept
method in your
new class will fail the cast and fallback to its base class.37
Mind you, this is convenient but not necessarily a good thing (like a
lot of things in life, I might add): you should review existing
visitors anyway, check whether the fallback implementation makes sense
for your class, and add a specific one if it doesn’t. But I think
that the disadvantage of not having the compiler warn you is more than
balanced by the advantage of not having to write a visit
method for
each cash-flow class when, as in BPSCalculator
, a couple will
suffice.
B. Code conventions
Every programmer team has a number of conventions to be used while writing code. Whatever the conventions (several exist, which provides a convenient casus belli for countless wars) adhering to them helps all developers working on the same project,38 as they make it easier to understand the code; a reader familiar with their use can distinguish at a glance between a macro and a function, or between a variable and a type name.
The following listing briefly illustrates the conventions used throughout the QuantLib library. Following the advice in Sutter and Alexandrescu, 2004, we tried to reduce their number at a minimum, enforcing only those conventions which enhance readability.
#define SOME_MACRO
typedef
double
SomeType
;
class
SomeClass
{
public
:
typedef
Real
*
iterator
;
typedef
const
Real
*
const_iterator
;
};
class
AnotherClass
{
public
:
void
method
();
Real
anotherMethod
(
Real
x
,
Real
y
)
const
;
Real
member
()
const
;
// getter, no "get"
void
setMember
(
Real
);
// setter
private
:
Real
member_
;
Integer
anotherMember_
;
};
struct
SomeStruct
{
Real
foo
;
Integer
bar
;
};
Size
someFunction
(
Real
parameter
,
Real
anotherParameter
)
{
Real
localVariable
=
0.0
;
if
(
condition
)
{
localVariable
+=
3.14159
;
}
else
{
localVariable
-=
2.71828
;
}
return
42
;
}
Macros are in all uppercase, with words separated by underscores. Type
names start with a capital and are in the so-called camel case; words
are joined together and the first letter of each word is
capitalized. This applies to both type declarations such as
SomeType
and class names such as SomeClass
and
AnotherClass
. However, an exception is made for type
declarations that mimic those found in the C++ standard library;
this can be seen in the declaration of the two iterator types in
SomeClass
. The same exception might be made for inner classes.
About everything else (variables, function and method names, and
parameters) are in camel case and start with a lowercase
character. Data members of a class follow the same convention, but are
given a trailing underscore; this makes it easier to distinguish them
from local variables in the body of a method (an exception is often
made for public data members, especially in structs or struct-like
classes). Among methods, a further convention is used for getters and
setters. Setter names are created by adding a leading set
to
the member name and removing the trailing underscore. Getter names
equal the name of the returned data member without the trailing
underscore; no leading get
is added. These conventions are
exemplified in AnotherClass
and SomeStruct
.
A much less strict convention is that the opening brace after a
function declaration or after an if
, else
, for
,
while
, or do
keyword are on the same line as the
preceding declaration or keyword; this is shown in
someFunction
. Moreover, else
keywords are on the same
line as the preceding closing brace; the same applies to the
while
ending a do
statement. However, this is more a
matter of taste than of readability; therefore, developers are free to
use their own conventions if they cannot stand this one. The shown
function exemplifies another convention aimed at improving
readability, namely, that function and method arguments should be
aligned vertically if they do not fit a single line.
QuantLib license
QuantLib is
- © 2000, 2001, 2002, 2003 RiskMap srl
- © 2001, 2002, 2003 Nicolas Di Césaré
- © 2001, 2002, 2003 Sadruddin Rejeb
- © 2002, 2003, 2004 Decillion Pty(Ltd)
- © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2014, 2015 Ferdinando Ametrano
- © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2014, 2016, 2017, 2018, 2019 StatPro Italia srl
- © 2003, 2004, 2007 Neil Firth
- © 2003, 2004 Roman Gitlin
- © 2003 Niels Elken Sønderby
- © 2003 Kawanishi Tomoya
- © 2004 FIMAT Group
- © 2004 M-Dimension Consulting Inc.
- © 2004 Mike Parker
- © 2004 Walter Penschke
- © 2004 Gianni Piolanti
- © 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 Klaus Spanderen
- © 2004 Jeff Yu
- © 2005, 2006, 2008 Toyin Akin
- © 2005 Sercan Atalik
- © 2005, 2006 Theo Boafo
- © 2005, 2006, 2007, 2009 Piter Dias
- © 2005, 2013 Gary Kennedy
- © 2005, 2006, 2007 Joseph Wang
- © 2005 Charles Whitmore
- © 2006, 2007 Banca Profilo S.p.A.
- © 2006, 2007 Marco Bianchetti
- © 2006 Yiping Chen
- © 2006 Warren Chou
- © 2006, 2007 Cristina Duminuco
- © 2006, 2007 Giorgio Facchinetti
- © 2006, 2007 Chiara Fornarola
- © 2006 Silvia Frasson
- © 2006 Richard Gould
- © 2006, 2007, 2008, 2009, 2010 Mark Joshi
- © 2006, 2007, 2008 Allen Kuo
- © 2006, 2007, 2008, 2009, 2012 Roland Lichters
- © 2006, 2007 Katiuscia Manzoni
- © 2006, 2007 Mario Pucci
- © 2006, 2007 François du Vignaud
- © 2007 Affine Group Limited
- © 2007 Richard Gomes
- © 2007, 2008 Laurent Hoffmann
- © 2007, 2008, 2009, 2010, 2011 Chris Kenyon
- © 2007 Gang Liang
- © 2008, 2009, 2014, 2015, 2016 Jose Aparicio
- © 2008 Yee Man Chan
- © 2008, 2011 Charles Chongseok Hyun
- © 2008 Piero Del Boca
- © 2008 Paul Farrington
- © 2008 Lorella Fatone
- © 2008, 2009 Andreas Gaida
- © 2008 Marek Glowacki
- © 2008 Florent Grenier
- © 2008 Frank Hövermann
- © 2008 Simon Ibbotson
- © 2008 John Maiden
- © 2008 Francesca Mariani
- © 2008, 2009, 2010, 2011, 2012, 2014 Master IMAFA - Polytech’Nice Sophia - Université de Nice Sophia Antipolis
- © 2008, 2009 Andrea Odetti
- © 2008 J. Erik Radmall
- © 2008 Maria Cristina Recchioni
- © 2008, 2009, 2012, 2014 Ralph Schreyer
- © 2008 Roland Stamm
- © 2008 Francesco Zirilli
- © 2009 Nathan Abbott
- © 2009 Sylvain Bertrand
- © 2009 Frédéric Degraeve
- © 2009 Dirk Eddelbuettel
- © 2009 Bernd Engelmann
- © 2009, 2010, 2012 Liquidnet Holdings, Inc.
- © 2009 Bojan Nikolic
- © 2009, 2010 Dimitri Reiswich
- © 2009 Sun Xiuxin
- © 2010 Kakhkhor Abdijalilov
- © 2010 Hachemi Benyahia
- © 2010 Manas Bhatt
- © 2010 DeriveXperts SAS
- © 2010, 2014 Cavit Hafizoglu
- © 2010 Michael Heckl
- © 2010 Slava Mazur
- © 2010, 2011, 2012, 2013 Andre Miemiec
- © 2010 Adrian O’ Neill
- © 2010 Robert Philipp
- © 2010 Alessandro Roveda
- © 2010 SunTrust Bank
- © 2011, 2013, 2014 Fabien Le Floc’h
- © 2012, 2013 Grzegorz Andruszkiewicz
- © 2012, 2013, 2014, 2015, 2016, 2017, 2018 Peter Caspers
- © 2012 Mateusz Kapturski
- © 2012 Simon Shakeshaft
- © 2012 Édouard Tallent
- © 2012 Samuel Tebege
- © 2013 BGC Partners L.P.
- © 2013, 2014 Cheng Li
- © 2013 Yue Tian
- © 2014, 2017 Francois Botha
- © 2014, 2015 Johannes Goettker-Schnetmann
- © 2014 Michal Kaut
- © 2014, 2015 Bernd Lewerenz
- © 2014, 2015, 2016 Paolo Mazzocchi
- © 2014, 2015 Thema Consulting SA
- © 2014, 2015, 2016 Michael von den Driesch
- © 2015 Riccardo Barone
- © 2015 CompatibL
- © 2015, 2016 Andres Hernandez
- © 2015 Dmitri Nesteruk
- © 2015 Maddalena Zanzi
- © 2016 Nicholas Bertocchi
- © 2016 Stefano Fondi
- © 2016, 2017 Fabrice Lecuyer
- © 2016, 2019 Eisuke Tani
- © 2017 BN Algorithms Ltd
- © 2017 Paul Giltinan
- © 2017 Werner Kuerzinger
- © 2017 Oleg Kulkov
- © 2017 Joseph Jeisman
- © 2018 Tom Anderson
- © 2018 Alexey Indiryakov
- © 2018 Jose Garcia
- © 2018 Matthias Groncki
- © 2018 Matthias Lungwitz
- © 2018 Sebastian Schlenkrich
- © 2018 Roy Zywina
- © 2019 Aprexo Limited
- © 2019 Wojciech Slusarski
QuantLib includes code taken from Peter Jäckel’s book “Monte Carlo Methods in Finance”.
QuantLib includes software developed by the University of Chicago, as Operator of Argonne National Laboratory.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the names of the copyright holders nor the names of the QuantLib Group and its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
This software is provided by the copyright holders and contributors “as is” and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright holders or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.
Bibliography
D. Abrahams, Want Speed? Pass by Value. In C++ Next, 2009.
D. Adams, So Long, and Thanks for all the Fish. 1984.
A. Alexandrescu, Move Constructors. In C/C++ Users Journal, February 2003.
F. Ametrano and M. Bianchetti, Everything You Always Wanted to Know About Multiple Interest Rate Curve Bootstrapping but Were Afraid to Ask. SSRN working papers series n.2219548, 2013.
J. Barton and L.R. Nackman, Dimensional Analysis. In C++ Report, January 1995.
T. Becker, On the Tension Between Object-Oriented and Generic Programming in C++. 2007.
Boost C++ libraries. http://boost.org.
M. K. Bowen and R. Smith, Derivative formulae and errors for non-uniformly spaced points. In Proceedings of the Royal Society of London A: Mathematical, Physical and Engineering Sciences, volume 461, pages 1975–1997. The Royal Society, 2005.
D. Brigo and F. Mercurio, Interest Rate Models –— Theory and Practice, 2nd edition. Springer, 2006.
Mel Brooks (director), Young Frankenstein. Twentieth Century Fox, 1974.
W.E. Brown, Toward Opaque Typedefs for C++1Y, v2. C++ Standards Committee Paper N3741, 2013.
L. Carroll, The Hunting of the Snark. 1876.
G.K. Chesterton, Alarms and Discursions. 1910.
M.P. Cline, G. Lomow and M. Girou, C++ FAQs, 2nd edition. Addison-Wesley, 1998.
J.O. Coplien, A Curiously Recurring Template Pattern. In S.B. Lippman, editor, C++ Gems. Cambridge University Press, 1996.
C.S.L. de Graaf. Finite Difference Methods in Derivatives Pricing under Stochastic Volatility Models. Master’s thesis, Mathematisch Instituut, Universiteit Leiden, 2012.
C. Dickens, Great Expectations. 1860.
P. Dimov, H.E. Hinnant and D. Abrahams, The Forwarding Problem: Arguments. C++ Standards Committee Paper N1385, 2002.
M. Dindal (director), The Emperor’s New Groove. Walt Disney Pictures, 2000.
D.J. Duffy, Finite Difference Methods in Financial Engineering: A Partial Differential Equation Approach. John Wiley and Sons, 2006.
M. Fowler, UML Distilled: A Brief Guide to the Standard Object Modeling Language, 3rd edition. Addison-Wesley, 2003.
M. Fowler, Fluent Interface. 2005.
M. Fowler, K. Beck, J. Brant, W. Opdyke and D. Roberts, Refactoring: Improving the Design of Existing Code. Addison-Wesley, 1999.
E. Gamma, R. Helm, R. Johnson and J. Vlissides, Design Patterns: Element of Reusable Object-Oriented Software. Addison-Wesley, 1995.
P. Glasserman, Monte Carlo Methods in Financial Engineering. Springer, 2003.
D. Gregor, A Brief Introduction to Variadic Templates. C++ Standards Committee Paper N2087, 2006.
H.E. Hinnant, B. Stroustrup and B. Kozicki, A Brief Introduction to Rvalue References. C++ Standards Committee Paper N2027, 2006.
A. Hunt and D. Thomas, The Pragmatic Programmer: From Journeyman to Master. Addison-Wesley, 1999.
International Standards Organization, Programming Languages — C++. International Standard ISO/IEC 14882:2014.
International Swaps and Derivatives Associations, Financial products Markup Language.
P. Jäckel, Monte Carlo Methods in Finance. John Wiley and Sons, 2002.
J. Kerievsky, Refactoring to Patterns. Addison-Wesley, 2004.
R. Kleiser (director), Grease. Paramount Pictures, 1978.
H.P. Lovecraft, The Call of Cthulhu. 1928.
R.C. Martin, Acyclic Visitor. In Pattern Languages of Program Design 3. Addison-Wesley, 1997.
S. Meyers, Effective C++, 3rd edition. Addison-Wesley, 2005.
N.C. Myers, Traits: a new and useful template technique. In The C++ Report, June 1995.
G. Orwell, Animal Farm. 1945.
W.H. Press, S.A. Teukolsky, W.T. Vetterling and B.P. Flannery, Numerical Recipes in C, 2nd edition. Cambridge University Press, 1992.
QuantLib. http://quantlib.org.
E. Queen, The Roman Hat Mystery. 1929.
V. Simonis and R. Weiss, Exploring Template Template Parameters. In Perspectives of System Informatics, number 2244 in Lecture Notes in Computer Science. Springer Berlin / Heidelberg, 2001.
B. Stroustrup, The C++ Programming Language, 4th edition. Addison-Wesley, 2013.
H. Sutter, You don’t know const and mutable. In Sutter’s Mill, 2013.
H. Sutter and A. Alexandrescu, C++ Coding Standards. Addison-Wesley, 2004.
T. Veldhuizen, Techniques for Scientific C++. Indiana University Computer Science Technical Report TR542, 2000.
H.G. Wells, The Shape of Things to Come. 1933.
P.G. Wodehouse, My Man Jeeves. 1919.
Notes
1A few gentle users happened to refer to our library as “the QuantLib.” As much as I like the expression, modesty and habit have so far prevented me from using it.↩
2In this case, of course, it would be “How we did it.”↩
3A more thorough exposition of this point can be found in Kerievsky, 2004.↩
4Even fancy new C++11 stuff like variadic templates won’t help.↩
5This does not excuse you from reading the Gang of Four book.↩
6Most likely, such objects ultimately depend on Quote
instances, e.g., a yield term structure might depend on the quoted
deposit and swap rates used for bootstrapping.↩
7The Instrument
class also defines an errorEstimate_
member, which is omitted here for clarity of exposition. The
discussion of NPV_
applies to both.↩
8The implementation shown in this section is somewhat outdated. However, I’m still using it here since it provides a simpler example.↩
9If you happen to feel slightly cheated, consider that the point of this example is to show how to package calculations into a class—not to show how to implement the calculations. Your curiosity will be satisfied in a later chapter devoted to cash flows and related functions.↩
10NaN
might be a better choice, but the means of
detecting it are not portable. Another possibility still to be
investigated would be to use boost::optional
.↩
11Beside being conceptually clearer, this would prove useful to external functions implementing serialization and deserialization of the instrument—for instance, to and from the FpML format.↩
12The Instrument::results
class also contains a
std::map
where pricing engines can store additional results. The
relevant code is here omitted for clarity.↩
13dynamic_pointer_cast
is
the equivalent of dynamic_cast
for shared pointers.↩
14You can find the full code of the engine in the QuantLib sources.↩
15No, it’s not a mistake. It is an inaccurately-named trilogy of five books. It’s a long story.↩
16Nor automatic, nor hydromatic. That would be the Grease Lightning.↩
17For instance, a simple expression like Time t = 2.0;
wouldn’t compile. You’d also have to write f(Time(1.5))
instead
of just f(1.5)
, even if f
wasn’t overloaded.↩
18I won’t explain it here, but go read it. It’s almost insanely cool.↩
19The same technique is used in a number of other classes,
such as DayCounter
in the next section or Parameter
from
chapter 5.↩
20Well, we could use heuristics, but it could get ugly fast.↩
21The choice to return the latest increment is kind of unusual; the idiomatic choice in C and C++ would be to return the old value.↩
22There are different views on safety among the core developers, ranging from “babysit the user and don’t let him hurt himself” to “give him his part of the inheritance, pat him on his back, and send him to find his place in the world.”↩
23Both support pattern matching on an object, which is like a
neater switch
on steroids. Go have a look when you have some
time.↩
24Note that by “instances of the same index” I mean here
instances of the same specific index, not of the same class (which
might group different indexes); for instance, USDLibor(3*Months)
and USDLibor(6*Months)
are not instances of the same index;
two different USDLibor(3*Months)
are.↩
25If you’re not familiar with the darker corners of C++: when the constructor of a base class is executed, any data members defined in derived classes are not yet built. Since any behavior specific to the derived class is likely to depend on such yet-not-existing data, C++ bails out and uses the base-class implementation of any virtual method called in the base-class constructor body.↩
26Lovers 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.↩
27This 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”.↩
28The presence of the primitive
and derivative
methods
is a bit of implementation leak. They were required by
interpolated interest-rate curves in order to pass from zero rates
to forwards and back.↩
29This also goes for a few multi-dimensional optimizers. In that case, apart from the obvious copyright issues, we also rewrote them in order to use idiomatic C++ and start indexing arrays from 0.↩
30Although you might be a bit surprised that it
doesn’t declare operator()
instead.↩
31Some of them declare those data members as
mutable
, suggesting the methods might have been
const
in the past. As I write this, I haven’t investigated
this further.↩
32It always bothered me that a*b
returns the element-wise
product and not the dot product, but I seem to be alone among
programmers.↩
33This is not as far-fetched as it might seem; we’ve been bitten by it.↩
34Well, it doesn’t actually force us, but writing
return Disposable<Array>(Array(n, 10))
is even uglier than the
alternative.↩
35Notice that I didn’t say was.↩
36Another alternative would be to do nothing, but we preferred not to fail silently.↩
37In fact, you’re not even required to define an accept
method; you could just inherit it. However, this would prevent
visitors to target this specific class.↩
38However, the QuantLib developers are human. As such, they sometimes fail to follow the rules I am describing.↩