Why QuantLib Uses a Flexible Setup for Black–Scholes Option Pricing
Summary
The document explains why pricing a European option in QuantLib involves defining dates, calendars, day-count conventions, market curves, volatility, an underlying process, a payoff, an exercise style, and a pricing engine. This structure supports consistent handling of financial conventions and lets the same instrument framework work with different processes and engines, including alternative payoffs or numerical methods.
The answers contrast this framework with a compact textbook calculation: a lower-level calculator or a language wrapper can provide a shorter interface when only a direct Black–Scholes value is needed. They also describe how retaining linked market quotes lets an existing instrument update when inputs change. The example code and wrapper output illustrate the setup and possible results, but the discussion is about software design, not evidence that a particular option model is empirically accurate. The framework's flexibility comes with more setup than a one-off textbook calculation, and simplified inputs such as a quick maturity approximation may omit market conventions.
Key ideas
- QuantLib's setup represents market conventions and instrument components explicitly.
- Separating the stochastic process from the pricing engine allows components to be changed independently.
- A compact calculator or wrapper can suit simple one-off textbook pricing.
- Linked quotes can update an instrument's valuation when market inputs change.
- The framework's flexibility adds complexity and does not establish model accuracy.
Tags
Full text
# Why does it take so many lines of code to price even the simplest of options with QuantLib
# 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;
Spread dividendYield = 0.00;
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;
}
```
## Answer by SmallChess (score 15, accepted)
https://quant.stackexchange.com/a/21052
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:
- boost::shared_ptr < BlackScholesMertonProcess > to point to some other stochastic process
- 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.
## Answer by Dirk Eddelbuettel (score 6)
https://quant.stackexchange.com/a/21068
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.
## Answer by Luigi Ballabio (score 5)
https://quant.stackexchange.com/a/21056
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...
## Answer by wsw (score -1)
https://quant.stackexchange.com/a/21489
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-cShown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.