Skip to content
All library documents

Binary Option Values from Call Prices and Volatility Skew

Article Quant Q&A · Author: Physcs Envy

Summary

The document asks how to obtain a binary option value from the change in call prices across nearby strikes. With implied volatility held constant, the limiting price difference corresponds to the negative strike derivative of the call price, which is the cash-or-nothing call value under the stated zero-rate setup. The response identifies the constant-volatility term as the cumulative normal probability associated with d2.

When implied volatility varies with strike, the response says the expression gains a correction involving call vega multiplied by the strike slope of implied volatility. This captures the effect of the volatility smile on the binary value. The post gives the key components but no derivation, full notation for the probability term, or discussion of payoff conventions and market details, so those should be specified when applying the result.

Key ideas

  • A binary option value can be expressed through the limiting strike difference of call prices.
  • Under constant implied volatility, the response identifies the probability term involving d2.
  • Strike-dependent implied volatility adds a correction equal to call vega times the volatility slope with respect to strike.
  • The payoff and pricing setup should be specified when applying the expression.

Tags

Full text
# Quantlib InterpolatedDiscountCurve zero forward-rate at endpoint


# Quantlib InterpolatedDiscountCurve zero forward-rate at endpoint












I use QuantLib Python to calibrate a curve based on interpolated discount factors (https://github.com/lballabio/QuantLib-SWIG/blob/master/SWIG/discountcurve.i). Using LogLinear interpolation on discount factors results in well-behaved forward rates:

(black line is zero rates, dotted line is 1d-tenor forward curve)

However, using MonotonicLogCubic or SplineCubic, my forward rates are not well behaved - in the end-point, the forwards are 0:

Now I understand that using these interpolation methods, second derivatives in end points are set to zero, however this shouldn't necessarily mean that forwards are zero in the end points. What gives? How to overcome this issue?

These are my input DFs:

```
February 27th, 2020: 1.0000102501050636
February 28th, 2020: 1.0000205003151919
March 10th, 2020: 1.000135036504439
March 17th, 2020: 1.0002140481753308
April 1st, 2020: 1.0003903064472135
April 30th, 2020: 1.0007383708059454
June 1st, 2020: 1.0011864659905814
July 1st, 2020: 1.001606452340883
July 30th, 2020: 1.0020206702099752
September 1st, 2020: 1.0025224673306345
September 30th, 2020: 1.003007005031276
October 30th, 2020: 1.0034783631568704
December 2nd, 2020: 1.0040034616057292
December 30th, 2020: 1.0045093968882473
February 1st, 2021: 1.0050604130179728
March 3rd, 2021: 1.0055474482943993
September 1st, 2021: 1.0087481016987652
March 2nd, 2022: 1.01191304422858
August 31st, 2022: 1.0148211625629888
March 2nd, 2023: 1.0174479377825414
March 1st, 2024: 1.0215623647797287
March 4th, 2025: 1.023909696966405
March 4th, 2026: 1.0242610376714252
March 3rd, 2027: 1.02262864674708
March 1st, 2028: 1.0191710493588495
March 2nd, 2029: 1.013740853380904
March 4th, 2030: 1.0061949692591952
March 4th, 2031: 0.998108130543049
March 3rd, 2032: 0.9884922702699311
March 2nd, 2035: 0.961448481843742
March 1st, 2040: 0.9138611360550781
March 2nd, 2045: 0.8771539172898629
March 2nd, 2050: 0.8489994312646763
```

And I construct the instance as such:

ql.NaturalCubicDiscountCurve(dates, discountfactors, ql.Actual365Fixed(),ql.UnitedStates())

where dates and discountfactors are lists based on the above.

To get the forwards, I call forwardRate(date, date+1, ql.Actual360(), ql.Simple()).rate() on the discount curve

## Answer by David Duarte (score 2, accepted)

https://quant.stackexchange.com/a/51379

I can't seem to replicate your problem...

```
import pandas as pd
import QuantLib as ql
import matplotlib.pyplot as plt

dates =  [
    '2020-02-26',  '2020-02-27',  '2020-02-28',  '2020-03-10',  '2020-03-17',  '2020-04-01',  '2020-04-30',  '2020-06-01',  '2020-07-01',  '2020-07-30',  '2020-09-01',
    '2020-09-30',  '2020-10-30',  '2020-12-01',  '2020-12-30',  '2021-02-01',  '2021-03-01',  '2021-09-01',  '2022-03-01',  '2022-08-31',  '2023-03-01',  '2024-03-01',
    '2025-03-01',  '2026-03-01',  '2027-03-01',  '2028-03-01',  '2029-03-01',  '2030-03-01',  '2031-03-01',  '2032-03-01',  '2035-03-01',  '2040-03-01',  '2045-03-01',
    '2050-03-01']

dfs = [1,  1.0000102501050636,  1.000020500315192,  1.000135036504439,  1.0002140481753308,  1.0003903064472135,  1.0007383708059454,  1.0011864659905814,  1.001606452340883,
 1.0020206702099752,  1.0025224673306343,  1.0030070050312758,  1.0034783631568704,  1.0040034616057292,  1.0045093968882473,  1.0050604130179728,  1.0055474482943991,
 1.0087481016987652, 1.01191304422858, 1.0148211625629888, 1.0174479377825414, 1.0215623647797287, 1.023909696966405, 1.0242610376714252, 1.02262864674708, 1.0191710493588495,
 1.013740853380904, 1.0061949692591952, 0.9981081305430491, 0.9884922702699313, 0.9614484818437421, 0.913861136055078, 0.8771539172898629, 0.8489994312646763]

qlDates = [ql.Date(dt, '%Y-%m-%d') for dt in dates]

params = [qlDates, dfs, ql.Actual365Fixed(),ql.UnitedStates()]
curves = {
    'DiscountCurve': ql.DiscountCurve(*params),
    'NaturalCubicDiscountCurve': ql.NaturalCubicDiscountCurve(*params),
    'MonotonicLogCubicDiscountCurve': ql.MonotonicLogCubicDiscountCurve(*params)   
}
plt.figure(figsize=(10,5))
for key in curves:
    crv = curves[key]
    crv.enableExtrapolation()
    times = crv.times()
    zeros = [crv.zeroRate(date, ql.Actual365Fixed(), ql.Continuous).rate() for date in crv.dates()]
    plt.plot(times, zeros, label=f"Spot {key}")
    fwds = [crv.forwardRate(date, date + ql.Period('1d'), ql.Actual360(), ql.Simple).rate() for date in crv.dates()]
    plt.plot(times, fwds, label=f"Fwd {key}")

plt.legend();
```

Which would result in :

Shown 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.