Translating QuantLib Python examples to C++

It’s easy enough to translate the Python code shown in this 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.

In [1]: import QuantLib as ql

This line imports the QuantLib module and provides a shorter alias that can be used to qualify the classes and functions it contains. The C++ equivalent would be:

#include <ql/quantlib.hpp>

namespace ql = QuantLib;

In C++, however, I wouldn’t include the global quantlib.hpp header, which would inflate your compilation times; instead, you can include the specific headers for the classes you’ll use.

Moreover, in C++ is not as discouraged as in Python to import the whole contents of a namespace in a source file, that is, to use

using namespace QuantLib;

However, the above should not be used in header files; ask the nearest C++ guru if you’re not sure why.

In [2]: today = ql.Date(7, ql.March, 2014)
        ql.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 dot before instance by a double colon (the same goes for the namespace qualifications). 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:

ql::Date today(7, ql::March, 2014);
ql::Settings::instance().evaluationDate() = today;

Next:

In [3]: option = ql.EuropeanOption(ql.PlainVanillaPayoff(ql.Option.Call, 100.0),
                                   ql.EuropeanExercise(ql.Date(7, ql.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 (note also the double colon in Option::Call):

ql::EuropeanOption option(
    boost::make_shared<ql::PlainVanillaPayoff>(ql::Option::Call, 100.0),
    boost::make_shared<ql::EuropeanExercise(ql::Date(7, ql::June, 2014)));

(A note: in the remainder of the example, I’ll omit the boost:: and ql:: namespaces for brevity.)

In [4]: u = ql.SimpleQuote(100.0)
        r = ql.SimpleQuote(0.01)
        sigma = ql.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 = ql.FlatForward(0, ql.TARGET(),
                                       ql.QuoteHandle(r), ql.Actual360())
        volatility = ql.BlackConstantVol(0, ql.TARGET(),
                                         ql.QuoteHandle(sigma), ql.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 = ql.BlackScholesProcess(ql.QuoteHandle(u),
                                         ql.YieldTermStructureHandle(riskFreeCurve),
                                         ql.BlackVolTermStructureHandle(volatility))

turns into

shared_ptr<BlackScholesProcess> process =
    make_shared<BlackConstantVol>(
        Handle<Quote>(u),
        Handle<YieldTermStructure>(riskFreeCurve),
        Handle<BlackVolTermStructure>(volatility));

and

In [7]: engine = ql.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())

where option is an object in C++, 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)

since u is a (smart) pointer, 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 this cookbook, even if you’re programming in C++.