# Why does it take so many lines of code to price even the simplest of options with QuantLib

I have been looking at QuantLib I am trying to figure out why I need to write so much boilerplate code even when pricing the "simplest" of European Options using the analytical Black-Scholes formula (note: I am new to option pricing and I might be missing what the following code provides beyond the textbook option pricing examples. In other words if this code takes into account "real-world" option issues it would be great to point this out):

#include <ql/quantlib.hpp>

using namespace QuantLib;

int main(int, char* []) {

// date set up
Calendar calendar = TARGET();
Date todaysDate(27, Jan, 2011);
Date settlementDate(27, Jan, 2011);
Settings::instance().evaluationDate() = todaysDate;

// option parameters
Option::Type type(Option::Call);
Real stock = 47;
Real strike = 40;
Rate riskFreeRate = 0.05;
Volatility volatility = 0.20;
Date maturity(27, May, 2011);
DayCounter dayCounter = Actual365Fixed();

boost::shared_ptr<Exercise>
europeanExercise(new EuropeanExercise(maturity));

Handle<Quote>
underlyingH(boost::shared_ptr<Quote>(new SimpleQuote(stock)));

// bootstrap the yield/dividend/vol curves
Handle<YieldTermStructure> flatTermStructure(boost::shared_ptr<YieldTermStructure>(
new FlatForward(
settlementDate,
riskFreeRate,
dayCounter)));

Handle<YieldTermStructure> flatDividendTS(boost::shared_ptr<YieldTermStructure>(
new FlatForward(settlementDate,
dividendYield,
dayCounter)));

Handle<BlackVolTermStructure> flatVolTS(boost::shared_ptr<BlackVolTermStructure>(
new BlackConstantVol(
settlementDate,
calendar,
volatility,
dayCounter)));

boost::shared_ptr<StrikedTypePayoff> payoff(
new PlainVanillaPayoff(
type,
strike));

boost::shared_ptr<BlackScholesMertonProcess> bsmProcess(
new BlackScholesMertonProcess(
underlyingH,
flatDividendTS,
flatTermStructure,
flatVolTS));

// our option is European-style
VanillaOption europeanOption(
payoff,
europeanExercise);

// computing the option price with the analytic Black-Scholes formulae
europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
new AnalyticEuropeanEngine(
bsmProcess)));

// outputting
std::cout << "Option type = " << type << std::endl;
std::cout << "Maturity = " << maturity << std::endl;
std::cout << "Stock price = " << stock << std::endl;
std::cout << "Strike = " << strike << std::endl;
std::cout << "Risk-free interest rate = " << io::rate(riskFreeRate) << std::endl;
std::cout << "Dividend yield = " << io::rate(dividendYield) << std::endl;
std::cout << "Volatility = " << io::volatility(volatility) <<  std::endl << std::endl;
std::cout<<"European Option value = " << europeanOption.NPV() << std::endl;
return 0;
}


I've been using QuantLib for quite a while. Let me share some experience:

QuantLib is a highly sophisticated quantitative framework. It can do much and much more than a simple pricing of European option. For example, in your example, you could have changed the payoff to binary payoff or giving a monte-carlo pricing engine (rather than AnalyticEuropeanEngine).

It's complicated because the library is designed for flexibility and powers, not simplicity. The QuantLib team does that by clever Object-Oriented and C++ templating. What you see in the code is already fairly advanced.

To appreciate the design, what would you do if you want to incorporate stochastic volatility into the model? You'd modify:

1. boost::shared_ptr < BlackScholesMertonProcess > to point to some other stochastic process

2. Pass a different pricing engine to setPricingEngine()

Can you see the pattern? Pricing with a stochastic model is highly mathematical demanded, but the actual coding would look pretty simple. We'd simply need to give a different pricing engine and a different stochastic process.

I understand that you were a bit worried because you were expecting a much simpler pricing code. However, real world pricing code is usually not as simple as you see in the textbook. A real-world pricing library needs to handle calendar, day-counting, dates and many more. QuantLib must be able to do all consistently. If you think QuantLib is hard to learn, try OpenGamma...

Of course, this is a bit overkill for a simple text-book European option pricing. If I was Dr Ballabio, I'd write a simple wrapper.

Essentially, the powers of Quantlib comes when you dive further into quantitative finance.

EDITED for the comment:

You can price an option like a calculator in QuantLib. I don't do it because I consider it too low-level (also no day-counting), but it's possible.

For example, to price a European option like a text-book, you'd look into BlackScholesCalculator (https://github.com/lballabio/quantlib/blob/2a5c086844775b6c1b986df2a5631d2ba3b1e6a4/QuantLib/ql/pricingengines/blackscholescalculator.hpp)

This is as simple as you can get in QuantLib. If you're looking for pricing code, I'd recommend you OptionMatrix. It's like a cookbook for quantitative finance.

• This response nails it. QuantLib is not an option pricing calculator, but rather a framework mean to work in the context of bigger, more sophisticated systems. – Brian B Oct 5 '15 at 13:33
• The simple wrapper might help, but see my answer. – Luigi Ballabio Oct 5 '15 at 16:06
• Luigi, Student T, thanks for the clarifications. Perhaps it would be a good idea to introduce instrument builders in order to create instruments with reasonable default parameters. Then if the user wants to customize specific aspects of the instrument (pricing engine, volatility models, etc...,) he could use the the lower layers of the framework. – BigONotation Oct 6 '15 at 7:18
• @BigLudinski The idea sounds cool but unlikely to come anytime soon. The QuantLib developers have got used to the framework and probably won't scale down the design for newcomers. Furthermore, you can price an European option like a calculator, you just need to know where to go. Look at my EDITED answer. – SmallChess Oct 6 '15 at 7:24
• @StudenT Don't get me wrong. I want to use the Framework as more than a "simple calculator". However having reasonable defaults for various financial instruments is also a good thing. Besides adding factory methods or builders in client code is no big deal. Incorporating it in the core framework simply makes this available to a wider audience – BigONotation Oct 6 '15 at 9:40

And don't forget that there are wrappers as eq RQuantLib which I use on the command-line here:

edd@max:~$r -l RQuantLib -e 'print(EuropeanOption("call", 47, 40, 0.05, 0.0, 4/12, 0.2))' Concise summary of valuation for EuropeanOption value delta gamma vega theta rho divRho 6.4728 0.8899 0.0307 4.5139 0.7372 11.7849 -13.9425 edd@max:~$


where 4/12 is quick and dirty for the maturity. That should pick up your example.

At an R prompt, it looks a little more natural:

R> library(RQuantLib)
R> ans <- EuropeanOption("call", 47, 40, 0.05, 0.0, 4/12, 0.2)
R> ans
Concise summary of valuation for EuropeanOption
value    delta    gamma     vega    theta      rho   divRho
6.4728   0.8899   0.0307   4.5139   0.7372  11.7849 -13.9425
R> str(ans)
List of 7
$value : num 6.47$ delta : num 0.89
$gamma : num 0.0307$ vega  : num 4.51
$theta : num 0.737$ rho   : num 11.8
\$ divRho: num -13.9
- attr(*, "class")= chr [1:2] "EuropeanOption" "Option"
R>


This shows both the result of the default print method as well as the components of the returned object.

To add to Student T's answer, which I second: the complex setup starts making sense (and its cost gets amortized) once you start keeping the instruments around instead of throwing them away after the pricing.

For instance, once the option above is built, you can change the market price of the underlying (or its volatility, or the risk free rate) by just setting a new value to the corresponding quote, and the option will update its price accordingly. You can see an example of this at https://www.youtube.com/watch?v=__PBUqjCy6E, which also demonstrates changing the engine as Student T suggested; the code is in Python, but can be easily translated to C++.

As for the suggestion of a simple wrapper: instead of writing that and have it call the framework, I'd go the other way and try to extract a simple functional core that the framework would call. But how to do that and remain compatible with the current code is still an open problem...

You should also use make_shared() instead of calling new. See

https://stackoverflow.com/questions/20895648/difference-in-make-shared-and-normal-shared-ptr-in-c

• What does this have to do with that? – BigONotation Oct 29 '15 at 8:58
• I tried to improve OP's code quality. – wsw Oct 29 '15 at 15:07
• make_shared() will just shorten the code a bit but will not explain why the framework requires so many lines just to price a simple BS option. – SmallChess Oct 30 '15 at 4:02