Happy new year, folks. I think that includes the year of the Rooster, too.

And now, what you came here for: a critique of numerical Greeks in QuantLib.

### Numerical Greeks

This post was prompted by a question asked on Stack Overflow (if you’re reading this, thanks, Ivan). In short, not all pricing engines provide results for the corresponding Greeks; for instance, the analytic Black-Scholes engine for European options provides all of them, whereas the finite-difference engine only returns delta, gamma and theta and the Monte Carlo engine doesn’t calculate any.

Now, it’s possible to calculate the missing ones numerically, as I described in this video. So why don’t engines use this method when Greeks are not available otherwise?

It’s a good question; there are a few layers to it, and in the end it will lead us to some insights into the architecture of the library.

The first answer is that numerical Greeks are costly: each one needs to reprice the instrument once or twice, depending on the accuracy we want. Therefore, we don’t want them to be calculated by default. You should incur the cost only if you need them.

That doesn’t need to stop us, though. We could configure the engine by passing argument to its constructor that specify which Greeks to calculate. It would add a bit of housekeeping, but it’s doable, so let’s assume we went ahead with the plan. (I won’t bother showing you the code keeping track of the configuration. That’s boring stuff, and moreover, I don’t have it.)

How would we write the calculation of the Greeks, then? As an example, let’s take the MCEuropeanEngine class. It takes as its first input a shared_ptr to a GeneralizedBlackScholesProcess; in turn, this contains a Handle<Quote> containing the value of the underlying, two Handle<YieldTermStructure> holding the risk-free rate and dividend yield curves, and a Handle<BlackVolTermStructure> holding the volatility. Those are the inputs we want to bump before repricing the option.

It turns out that it’s fairly difficult to modify them. Even in the simplest case, that is, the Handle<Quote> containing the underlying, we have no guarantee that the contained Quote can be cast to a SimpleQuote and set a new value. The same holds for the other handles; without knowledge of the actual derived class of the contained YieldTermStructure or BlackVolTermStructure instances, there’s no way we can modify them.

We don’t actually need to do it, though, right? Let’s take, e.g., the risk-free curve; we can wrap the current instance in, say, a ZeroSpreadedTermStructure instance to shift it upwards or backwards a given increment. Something like:

Handle<Quote> dr(make_shared<SimpleQuote>(1e-4)); // 1 bp

shared_ptr<YieldTermStructure> new_curve =


The same goes for the underlying quote: we can ask for the current value, perturb it, and store it in a new SimpleQuote instance, as in:

Real du = 0.001;

shared_ptr<Quote> new_u =
make_shared<SimpleQuote>(process_->x0() + du);


However, the problem now is that we can’t put the new inputs into the process. It contains Handle instances, and those can’t be relinked so that they store the new curve and quote; only RelinkableHandle instances can (see this post for a refresher, in case you need it). Hmm. It seems like we went out of our way to make this operation difficult, doesn’t it?

Well, in fact, we did—we wrote the code explicitly so that the inputs couldn’t be changed. We had a reason for that, though: the instrument might not be the only one currently using them. If we had a bunch of options on the same underlying, for instance, they could be sharing the same process instance; and instruments in the same currency would probably share the same risk-free curve. There’s no point creating and maintaining an instance for each instrument.

This means that any one instrument isn’t free to modify its inputs: it could affect other, unrelated instruments. That’s why inputs are only ever stored and made available through handles. (As a historical side note, we were once bitten by this before we started coding this way. In fact, we were bitten so badly we had to retire a release because it was too dangerous to use: version 0.3.5 was removed from the download page and version 0.3.6 replaced it as soon as we found out. You can check the fix here if you’re curious.)

If you’re determined enough and really want to add numerical Greeks to your engine, you can still find a way. Instead of modifying the process, you’ll have to clone it. For instance, if you want to shift the risk-free rate, you can create a shifted curve as above and write:

shared_ptr<GeneralizedBlackScholesProcess> new_process =
make_shared<GeneralizedBlackScholesProcess>(
process_->stateVariable(),
process_->dividendYield(),
Handle<YieldTermStructure>(new_curve),
process_->blackVolatility());


after which the engine can perform the calculations using the cloned process. You’ll probably need to refactor the calculations in the engine’s calculate method so that you can call them repeatedly, as in:

void SomeEngine::calculate() const {
results_.value = calculate(process_);
... // create the new process
Real shiftedPrice = calculate(new_process);
... // calculate a Greek based on the difference
... // create another process
Real anotherPrice = calculate(another_process);
... // calculate another Greek
}


You might also want to think what should happen if the Greeks calculations were to raise an exception. Do you throw away the calculated price, or keep it and just bail on the Greeks, and maybe try the next one? Granted, this requires some thought even for ordinary Greeks; but those are usually calculated together with the price, and it’s more likely that they succeed or fail together.

But in the end, ask yourself: is this really necessary, or should you leave it to users to calculate missing Greeks? On the one hand, they can do it much more easily by perturbing the inputs that they set up and probably still hold on to; and on the other hand, unlike you, they’re also able to optimize the calculation when multiple instruments depend on the same process. There’s no need for each of them to create a new process; users can modify the relevant input once, read the new prices from all the instruments, and restore the input to its former value.

See you next time!

Subscribe to my Substack to receive my posts in your inbox, or follow me on Twitter or LinkedIn if you want to be notified of new posts, or subscribe via RSS if you’re the tech type: the buttons for all that are in the footer. Also, I’m available for training, both online and (when possible) on-site: visit my Training page for more information.