Translating QuantLib Python examples to C++
Welcome back.
So, as I said, the QuantLib Python Cookbook is available on Leanpub; thanks to those who bought it so far. Does it mean that C++ users are left out in the cold? Well, no.
My choice of using Python notebooks to demonstrate QuantLib features
was due to a number of factors. (I’m speaking for myself, here, but
I’m guessing that Goutham thought something similar when he started
his series of
posts). Notebooks
are interactive, and so I can also use them live in my courses (the
next of which is in September, thanks
for asking); and the platform, more or less out of the box, provides
excellent modules like matplotlib
for graphing and pandas
for data
analysis. Add into the mix that I was already familiar with the
language, and notebooks were the winners, hands down.
However, it’s easy enough to translate the Python code shown in the book into the corresponding C++ code. As an example, I’ll go through a bit of code from the notebook on instruments and pricing engines (the complete notebook is in the free sample).
In [1]: from QuantLib import *
This line imports the QuantLib
module and adds the classes and
functions it contains to the global namespace. The C++ equivalent would be:
#include <ql/quantlib.hpp>
using namespace QuantLib;
Of course, the above is for illustration purposes. In production code,
you’re not forced (or even advised) to use the using
directory; you
can keep the names in their namespace and qualify them when you use
them. It’s also possible to include more specific headers, instead of
the global quantlib.hpp
.
In [2]: today = Date(7, March, 2014)
Settings.instance().evaluationDate = today
The code above has a couple of caveats. The first line is easy enough
to translate; you’ll have to declare the type to the variable (or use
auto
if you’re compiling in C++11 mode). The second line is
trickier. To begin with, the syntax to call static methods differs in
Python and C++, so you’ll have to replace the first dot by a double
colon. Then, evaluationDate
is a property in Python but a method in
C++; it was changed in the Python module to be more idiomatic, since
it’s not that usual in Python to assign to the result of a
method. Luckily, you won’t find many such cases. The translated code
is:
Date today(7, March, 2014);
Settings::instance().evaluationDate() = today;
Next:
In [3]: option = EuropeanOption(PlainVanillaPayoff(Option.Call, 100.0),
EuropeanExercise(Date(7, June, 2014)))
Again, you’ll have to declare the type of the variable. Furthermore,
the constructor of EuropeanOption
takes its arguments by pointer, or
more precisely, by boost::shared_ptr
. This is hidden in Python,
since there’s no concept of pointer in the language; the SWIG wrappers
take care of exporting boost::shared_ptr<T>
simply as T
.
The corresponding C++ code:
EuropeanOption option(
boost::make_shared<PlainVanillaPayoff>(Option.Call, 100.0),
boost::make_shared<EuropeanExercise(Date(7, June, 2014)));
(A note: in the remainder of the example, I’ll omit the boost::
namespace for brevity.)
In [4]: u = SimpleQuote(100.0)
r = SimpleQuote(0.01)
sigma = SimpleQuote(0.20)
Quotes, too, are stored and passed around as shared_ptr
instances;
this is the case for most polymorphic classes (when in doubt, you can
look at the C++ headers and check the signatures of the functions you
want to call). The above becomes:
shared_ptr<SimpleQuote> u = make_shared<SimpleQuote>(100.0);
shared_ptr<SimpleQuote> r = make_shared<SimpleQuote>(0.01);
shared_ptr<SimpleQuote> sigma = make_shared<SimpleQuote>(0.20);
Depending on what you need to do with them, the variables might also
be declared as shared_ptr<Quote>
. I used the above, since I’ll need
to call a method of SimpleQuote
in a later part of the code.
In [5]: riskFreeCurve = FlatForward(0, TARGET(),
QuoteHandle(r), Actual360())
volatility = BlackConstantVol(0, TARGET(),
QuoteHandle(sigma), Actual360())
The Handle
template class couldn’t be exported as such, because
Python doesn’t have templates. Thus, the SWIG wrappers have to declare
separate classes QuoteHandle
, YieldTermStructureHandle
and so on.
In C++, you can go back to the original syntax.
shared_ptr<YieldTermStructure> riskFreeCurve =
make_shared<FlatForward>(0, TARGET(),
Handle<Quote>(r), Actual360());
shared_ptr<BlackVolTermStructure> volatility =
make_shared<BlackConstantVol>(0, TARGET(),
Handle<Quote>(sigma), Actual360());
Next,
In [6]: process = BlackScholesProcess(QuoteHandle(u),
YieldTermStructureHandle(riskFreeCurve),
BlackVolTermStructureHandle(volatility))
turns into
shared_ptr<BlackScholesProcess> process =
make_shared<BlackConstantVol>(
Handle<Quote>(u),
Handle<YieldTermStructure>(riskFreeCurve),
Handle<BlackVolTermStructure>(volatility));
and
In [7]: engine = AnalyticEuropeanEngine(process)
into
shared_ptr<PricingEngine> engine =
make_shared<AnalyticEuropeanEngine>(process);
So far, we’ve been calling constructors. Method invocation works the same in Python and C++, except that in C++ we might be calling methods through a pointer. Therefore,
In [8]: option.setPricingEngine(engine)
In [9]: print option.NPV()
In [10]: print option.delta()
print option.gamma()
print option.vega()
becomes
option.setPricingEngine(engine);
cout << option.NPV() << endl;
cout << option.delta() << endl;
cout << option.gamma() << endl;
cout << option.vega() << endl;
whereas
In [11]: u.setValue(105.0)
turns into
u->setValue(105.0);
Of course, the direct translation I’ve been doing only applies to the
QuantLib code; I’m not able to point you to libraries that replace the
graphing functionality in matplotlib
, or the data-analysis
facilities in pandas
, or the parallel math functions in
numpy
. However, I hope that the above can still enable you to
extract value from the cookbook, even if you’re programming in C++.
Follow me on Twitter if you want to be notified of new posts, or add me to your Google+ circles, or subscribe via RSS: the buttons for that are in the footer. Also, make sure to check my Training page.