Reiner-Rubinstein binary options#

Mark Rubinstein and Eric Reiner’s “Unscrambling the Binary Code” (1991) priced the all-or-nothing payoffs and showed that every vanilla and barrier option is assembled from them. A cash-or-nothing call pays 1 if \(S_T > K\) and is worth \(e^{-rT}N(d_2)\); an asset-or-nothing call pays the share and is worth \(Se^{-qT}N(d_1)\). A vanilla call is one asset-or-nothing call minus \(K\) cash-or-nothing calls. A gap option uses different trigger and payoff strikes and can be worth less than nothing, and a one-touch pays as soon as a barrier is hit.

import matplotlib.pyplot as plt
import numpy as np

from financialkit.exotic_options import asset_or_nothing_price, cash_or_nothing_price, gap_option_price, one_touch_price
from financialkit.options import black_scholes_price

K, T, r, sigma = 100.0, 0.5, 0.05, 0.25
S = np.linspace(60.0, 150.0, 300)

A vanilla call from two digitals#

asset = asset_or_nothing_price(S, K, T, r, sigma)
cash = cash_or_nothing_price(S, K, T, r, sigma, cash=K)
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
axes[0].plot(S, asset, label="asset-or-nothing call")
axes[0].plot(S, cash, label=f"{K:g} cash-or-nothing calls")
axes[0].plot(S, asset - cash, lw=3, label="difference")
axes[0].plot(S, black_scholes_price(S, K, T, r, sigma), "k--", label="Black-Scholes call")
axes[0].set_xlabel("spot S")
axes[0].legend()
plot 01 digital options
<matplotlib.legend.Legend object at 0x1171f06e0>

The digital’s delta spikes at the strike near expiry#

for tau in (0.5, 0.1, 0.02):
    axes[1].plot(S, cash_or_nothing_price(S, K, tau, r, sigma), label=f"T = {tau:g}")
axes[1].set_xlabel("spot S")
axes[1].set_ylabel("cash-or-nothing call (pays 1)")
axes[1].legend()
<matplotlib.legend.Legend object at 0x116f112b0>

Gap options and one-touches#

Haug (2007), Section 4.19.1: a gap call triggered at 50 that pays S_T - 57 is worth a fraction of a cent less than zero. The one-touch pays 1 the first time the price reaches 120 and is worth roughly twice the digital that pays at expiry if S_T > 120 (the reflection principle).

print(f"gap call: {gap_option_price(50.0, 50.0, 57.0, 0.5, 0.09, 0.20):.4f}")
axes[2].plot(S[S < 120], one_touch_price(S[S < 120], 120.0, T, r, sigma), label="one-touch at 120")
axes[2].plot(S, cash_or_nothing_price(S, 120.0, T, r, sigma), label="digital call, K = 120")
axes[2].plot(S[S < 120], 2 * cash_or_nothing_price(S[S < 120], 120.0, T, r, sigma), ":", label="twice the digital")
axes[2].set_xlabel("spot S")
axes[2].legend()
fig.tight_layout()
plt.show()
gap call: -0.0053

Total running time of the script: (0 minutes 0.093 seconds)

Gallery generated by Sphinx-Gallery