---
title: "Chapter 23. Exotic Options"
---
::: {.chapter-objectives}
## Learning Goals
By the end of this chapter, students should be able to:
- explain what makes an option exotic from a mathematical point of view;
- write exotic payoffs as functionals of the price path $(S_t)_{0\le t\le T}$;
- distinguish path-independent, path-dependent, discontinuous, and multi-state payoffs;
- price exotic options under the risk-neutral measure using conditional expectation;
- formulate Asian, barrier, lookback, and digital options precisely;
- understand why some exotic options require an enlarged Markov state;
- derive PDEs for path-dependent claims after state augmentation;
- implement Monte Carlo pricing for Asian, barrier, lookback, and digital options;
- identify common numerical difficulties such as barrier monitoring bias and digital payoff discontinuity;
- use AI tools to audit payoff definitions, simulations, and financial interpretation.
:::
::: {.hero-box}
A standard European option depends only on the terminal stock price $S_T$. Many real derivatives depend on the whole path: the running average, the running maximum, whether a barrier was crossed, or whether the terminal price landed above a threshold.
The unifying formula is
$$
V_t
=
B_t\mathbb E^{\mathbb Q}\left[\frac{H((S_u)_{t\le u\le T})}{B_T}\mid \mathcal F_t\right],
$$
where $H$ is a payoff functional of the future price path. Exotic option pricing is therefore not a new principle; it is the martingale-pricing principle applied to richer payoff functionals.
:::
## 23.1 What Makes an Option Exotic?
In the Black--Scholes chapters, the main examples were European calls and puts:
$$
C_T=(S_T-K)^+,
\qquad
P_T=(K-S_T)^+.
$$
These are **plain-vanilla** payoffs because they depend only on $S_T$. In mathematical terms, the payoff is a function
$$
H = h(S_T).
$$
An exotic option has a payoff that is still a financial claim, but the function $h(S_T)$ is replaced by a more complicated object. The payoff may depend on:
- the time average of the asset price;
- the maximum or minimum reached before maturity;
- whether the asset touched a barrier;
- several assets at once;
- a discontinuous indicator event;
- a nonlinear combination of exercise features.
A clean way to define exotic options is to view the payoff as a functional on the path space.
::: {.definition}
## Path functional
Let $S=(S_t)_{0\le t\le T}$ be a price process. A **path functional** is a map
$$
H: \{\text{admissible price paths on }[0,T]\}\to \mathbb R.
$$
The corresponding claim pays $H(S)$ at maturity $T$.
:::
For example, the arithmetic Asian call payoff is
$$
H(S)=\left(\frac{1}{T}\int_0^T S_u\,du-K\right)^+.
$$
The up-and-out call payoff is
$$
H(S)=(S_T-K)^+\mathbf 1_{\{\max_{0\le u\le T}S_u<B\}}.
$$
The fixed-strike lookback call payoff is
$$
H(S)=\left(\max_{0\le u\le T}S_u-K\right)^+.
$$
These examples are mathematically different from vanilla options because the current stock price $S_t$ alone is usually not enough information to value the claim at time $t$.
## 23.2 Risk-Neutral Pricing of Path-Dependent Claims
Assume the risk-neutral Black--Scholes model
$$
dS_t=rS_t\,dt+\sigma S_t\,dW_t^{\mathbb Q},
\qquad
S_0>0,
$$
and the money-market account
$$
B_t=e^{rt}.
$$
For any square-integrable payoff functional $H(S)$ paid at time $T$, the no-arbitrage price at time $0$ is
$$
V_0=e^{-rT}\mathbb E^{\mathbb Q}[H(S)].
$$
At time $t$, the value process is
$$
V_t=e^{-r(T-t)}\mathbb E^{\mathbb Q}[H(S)\mid\mathcal F_t]
$$
when the risk-free rate is constant.
::: {.remark}
## Same principle, larger state
The risk-neutral pricing formula is unchanged. What changes is the state variable needed to compute the conditional expectation.
For a vanilla option, the state $S_t$ is enough. For an Asian option, we also need the accumulated integral. For a barrier or lookback option, we need the running maximum or minimum.
:::
The mathematical challenge is therefore not the pricing principle but the representation of the payoff and the state.
## 23.3 State Augmentation
Suppose the stock price is Markov. A vanilla option value can be written as
$$
V_t=v(t,S_t).
$$
A path-dependent option may not admit such a representation. But many path-dependent claims become Markovian after adding suitable state variables.
::: {.definition}
## Markovian state augmentation
A path-dependent payoff is **Markovian after state augmentation** if there exists a process $Y_t$ such that $(S_t,Y_t)$ is Markov and the remaining payoff can be valued from $(t,S_t,Y_t)$.
:::
Examples:
| Claim type | Additional state variable | State dynamics |
|---|---|---|
| Arithmetic Asian | $A_t=\int_0^t S_u\,du$ | $dA_t=S_t\,dt$ |
| Up-and-out barrier | $M_t=\max_{0\le u\le t}S_u$ or alive flag | $M_t=\max(M_{t-},S_t)$ |
| Lookback call | $M_t=\max_{0\le u\le t}S_u$ | running maximum |
| Lookback put | $m_t=\min_{0\le u\le t}S_u$ | running minimum |
| Discrete Asian | partial sum $A_n=\sum_{i=1}^n S_{t_i}$ | recursion $A_{n+1}=A_n+S_{t_{n+1}}$ |
This is one of the most important mathematical ideas in the chapter: path dependence often becomes ordinary Markov dependence in a higher-dimensional state space.
## 23.4 Asian Options
Asian options depend on an average of the underlying asset price. The averaging feature reduces sensitivity to short-term price spikes and often makes the option cheaper than a comparable vanilla option.
::: {.definition}
## Arithmetic Asian call and put
The continuously monitored arithmetic average is
$$
\bar S_T=\frac{1}{T}\int_0^T S_u\,du.
$$
The arithmetic Asian call and put payoffs are
$$
C_T^{\text{Asian}}=(\bar S_T-K)^+,
\qquad
P_T^{\text{Asian}}=(K-\bar S_T)^+.
$$
:::
In practice, many Asian options use discrete monitoring dates $0<t_1<\cdots<t_m=T$:
$$
\bar S_T^{(m)}=\frac{1}{m}\sum_{i=1}^m S_{t_i}.
$$
Then the payoff is
$$
C_T^{\text{Asian},m}=\left(\frac{1}{m}\sum_{i=1}^m S_{t_i}-K\right)^+.
$$
### Arithmetic versus geometric averages
The arithmetic average is natural but analytically difficult because a sum of lognormal random variables is not lognormal. The geometric average is
$$
G_T=\left(\prod_{i=1}^m S_{t_i}\right)^{1/m}.
$$
Since
$$
\log G_T=\frac{1}{m}\sum_{i=1}^m \log S_{t_i},
$$
the geometric average is lognormal under the Black--Scholes model. This gives a closed-form benchmark.
::: {.theorem}
## Discrete geometric Asian option under Black--Scholes
Let $t_i=iT/m$ and assume risk-neutral dynamics
$$
dS_t=rS_t\,dt+\sigma S_t\,dW_t^{\mathbb Q}.
$$
Then $\log G_T$ is normally distributed with variance
$$
\sigma_G^2
=
\sigma^2 T\frac{(m+1)(2m+1)}{6m^2}
$$
and mean
$$
\mu_G
=
\log S_0+
\left(r-\frac12\sigma^2\right)T\frac{m+1}{2m}.
$$
Therefore a geometric Asian call has price
$$
C_0^{\text{geom}}
=e^{-rT}\left(e^{\mu_G+\frac12\sigma_G^2}N(d_1)-K N(d_2)\right),
$$
where
$$
d_1=\frac{\mu_G-\log K+\sigma_G^2}{\sigma_G},
\qquad
d_2=d_1-\sigma_G.
$$
:::
The geometric Asian formula is useful for two reasons. First, it is a clear example of turning a path-dependent payoff into a distributional calculation. Second, it is commonly used as a control variate when pricing arithmetic Asian options by Monte Carlo.
## 23.5 PDE for Arithmetic Asian Options
Define the accumulated average variable
$$
A_t=\int_0^t S_u\,du.
$$
Then under the risk-neutral measure,
$$
\begin{aligned}
dS_t &= rS_t\,dt+\sigma S_t\,dW_t,\\
dA_t &= S_t\,dt.
\end{aligned}
$$
The pair $(S_t,A_t)$ is Markov. Let
$$
V_t=v(t,S_t,A_t).
$$
By Itô's formula,
$$
dv
=
\left(v_t+rsv_s+s v_a+\frac12\sigma^2s^2v_{ss}\right)dt
+\sigma s v_s\,dW_t.
$$
The discounted value $e^{-rt}v(t,S_t,A_t)$ must be a martingale under $\mathbb Q$. Therefore the drift must vanish:
$$
v_t+rsv_s+s v_a+\frac12\sigma^2s^2v_{ss}-rv=0.
$$
The terminal condition for a continuously averaged Asian call is
$$
v(T,s,a)=\left(\frac{a}{T}-K\right)^+.
$$
::: {.remark}
## Why this PDE is two-dimensional
A vanilla option PDE has variables $(t,s)$. The Asian PDE has variables $(t,s,a)$ because the accumulated average matters. This is the cost of path dependence.
:::
## 23.6 Barrier Options
Barrier options turn on or off depending on whether the asset crosses a specified level.
::: {.definition}
## Barrier options
Let $B>0$ be a barrier. Define the running maximum and minimum by
$$
M_T=\max_{0\le u\le T}S_u,
\qquad
m_T=\min_{0\le u\le T}S_u.
$$
An **up-and-out call** with barrier $B>K$ pays
$$
(S_T-K)^+\mathbf 1_{\{M_T<B\}}.
$$
An **up-and-in call** pays
$$
(S_T-K)^+\mathbf 1_{\{M_T\ge B\}}.
$$
A **down-and-out put** with barrier $B<K$ pays
$$
(K-S_T)^+\mathbf 1_{\{m_T>B\}}.
$$
:::
The words have precise meanings:
- **up** means the relevant barrier lies above the initial price;
- **down** means the barrier lies below the initial price;
- **out** means the option disappears after the barrier is hit;
- **in** means the option becomes active only after the barrier is hit.
### In-out parity
For a fixed vanilla payoff, knock-in and knock-out claims decompose the vanilla payoff:
$$
(S_T-K)^+
=
(S_T-K)^+\mathbf 1_{\{M_T<B\}}
+(S_T-K)^+\mathbf 1_{\{M_T\ge B\}}.
$$
Therefore, when the contracts have the same strike, maturity, and barrier convention,
$$
C^{\text{vanilla}}=C^{\text{up-out}}+C^{\text{up-in}}.
$$
This parity is a powerful consistency check.
### Barrier boundary condition
For an up-and-out call with barrier $B$, the value must satisfy
$$
v(t,B)=0.
$$
If the stock reaches the barrier, the option is knocked out. The Black--Scholes PDE holds in the continuation region $0<s<B$:
$$
v_t+rsv_s+\frac12\sigma^2s^2v_{ss}-rv=0,
\qquad 0<s<B.
$$
The terminal condition is
$$
v(T,s)=(s-K)^+\mathbf 1_{\{s<B\}}.
$$
## 23.7 Lookback Options
Lookback options depend on the maximum or minimum of the asset price.
::: {.definition}
## Lookback options
The running maximum and minimum are
$$
M_T=\max_{0\le u\le T}S_u,
\qquad
m_T=\min_{0\le u\le T}S_u.
$$
A fixed-strike lookback call pays
$$
(M_T-K)^+.
$$
A floating-strike lookback call pays
$$
S_T-m_T.
$$
A floating-strike lookback put pays
$$
M_T-S_T.
$$
:::
Lookback options are expensive because they allow the holder to benefit from the best price observed over the life of the contract. Mathematically, they require tracking an extreme-value process such as $M_t$ or $m_t$.
For a fixed-strike lookback call, the augmented state is $(S_t,M_t)$. The terminal condition is
$$
v(T,s,m)=(m-K)^+,
\qquad m\ge s.
$$
For $s<m$, the PDE has the usual Black--Scholes form in the $s$ variable:
$$
v_t+rsv_s+\frac12\sigma^2s^2v_{ss}-rv=0.
$$
At the boundary $s=m$, the running maximum can increase. This creates an additional boundary condition, often written informally as a reflection-type condition. The detailed continuous-time analysis is more advanced, but the state-augmentation idea is already visible in discrete simulation.
## 23.8 Digital Options
Digital options have discontinuous payoffs. A cash-or-nothing digital call pays a fixed amount if the asset finishes above the strike.
::: {.definition}
## Cash-or-nothing digital call
A cash-or-nothing digital call with cash payoff $Q>0$ pays
$$
Q\mathbf 1_{\{S_T>K\}}.
$$
:::
Under Black--Scholes,
$$
\log S_T\sim N\left(\log S_0+\left(r-\frac12\sigma^2\right)T,\,\sigma^2T\right)
$$
under $\mathbb Q$. Therefore
$$
\mathbb Q(S_T>K)=N(d_2),
$$
where
$$
d_2=\frac{\log(S_0/K)+(r-\frac12\sigma^2)T}{\sigma\sqrt T}.
$$
The price is
$$
D_0=Qe^{-rT}N(d_2).
$$
Digital options are important because they reveal the relationship between option prices and risk-neutral probabilities.
Indeed, for a European call
$$
C(K)=e^{-rT}\mathbb E^{\mathbb Q}[(S_T-K)^+],
$$
one has formally
$$
-\frac{\partial C}{\partial K}=e^{-rT}\mathbb Q(S_T>K).
$$
Thus digital prices are related to strike derivatives of call prices.
::: {.warning}
## Discontinuous payoff warning
Digital payoffs are discontinuous at $S_T=K$. This makes Monte Carlo convergence visually noisy near the strike and makes finite-difference Greeks unstable. Smooth payoff intuition from vanilla calls does not directly carry over.
:::
## 23.9 Interactive Demo: Asian Averaging Smooths Payoffs
The following demo simulates risk-neutral GBM paths. For each path, it computes both the European call payoff $(S_T-K)^+$ and the arithmetic Asian payoff $(\bar S_T-K)^+$. Increase volatility to see how averaging changes the payoff distribution.
::: {.interactive-card}
### Simulated European versus Asian payoffs
<div class="mf-control-row">
<label for="ch23-asian-sigma">Volatility $\sigma$</label>
<input id="ch23-asian-sigma" type="range" min="0.05" max="0.70" step="0.01" value="0.25">
<span id="ch23-asian-sigma-val" class="mf-badge">0.25</span>
</div>
<div class="mf-control-row">
<label for="ch23-asian-k">Strike $K$</label>
<input id="ch23-asian-k" type="range" min="70" max="130" step="1" value="100">
<span id="ch23-asian-k-val" class="mf-badge">100</span>
</div>
<div id="ch23-asian-demo" class="plotly-demo"></div>
<div id="ch23-asian-summary" class="mf-output"></div>
:::
<script>
window.mfPlotlyReady(function() {
var sigmaSlider = document.getElementById("ch23-asian-sigma");
var kSlider = document.getElementById("ch23-asian-k");
var sigmaVal = document.getElementById("ch23-asian-sigma-val");
var kVal = document.getElementById("ch23-asian-k-val");
var summary = document.getElementById("ch23-asian-summary");
var el = document.getElementById("ch23-asian-demo");
if (!el || !sigmaSlider || !kSlider) return;
function randn(seedObj) {
seedObj.seed = (1664525 * seedObj.seed + 1013904223) % 4294967296;
var u1 = (seedObj.seed + 1) / 4294967297;
seedObj.seed = (1664525 * seedObj.seed + 1013904223) % 4294967296;
var u2 = (seedObj.seed + 1) / 4294967297;
return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
}
function simulatePaths(sigma, K) {
var S0 = 100, r = 0.04, T = 1, n = 120, paths = 1200, dt = T / n;
var seedObj = {seed: 234567};
var time = Array.from({length: n + 1}, function(_, i) { return i * dt; });
var sampleTraces = [];
var european = [];
var asian = [];
for (var p = 0; p < paths; p++) {
var S = S0;
var path = [S0];
var sum = 0;
for (var i = 1; i <= n; i++) {
var z = randn(seedObj);
S = S * Math.exp((r - 0.5 * sigma * sigma) * dt + sigma * Math.sqrt(dt) * z);
path.push(S);
sum += S;
}
var avg = sum / n;
european.push(Math.max(S - K, 0));
asian.push(Math.max(avg - K, 0));
if (p < 12) {
sampleTraces.push({x: time, y: path, mode: "lines", line: {width: 1}, opacity: 0.55, showlegend: false});
}
}
var meanEuro = european.reduce(function(a, b) { return a + b; }, 0) / paths;
var meanAsian = asian.reduce(function(a, b) { return a + b; }, 0) / paths;
var histEuro = {x: european, type: "histogram", opacity: 0.62, name: "European payoff", xaxis: "x2", yaxis: "y2", nbinsx: 30};
var histAsian = {x: asian, type: "histogram", opacity: 0.62, name: "Asian payoff", xaxis: "x2", yaxis: "y2", nbinsx: 30};
var traces = sampleTraces.concat([histEuro, histAsian]);
var layout = {
title: "Risk-neutral paths and payoff distributions",
grid: {rows: 1, columns: 2, pattern: "independent"},
xaxis: {title: "time"},
yaxis: {title: "stock price"},
xaxis2: {title: "payoff"},
yaxis2: {title: "frequency"},
barmode: "overlay",
margin: {l: 55, r: 25, t: 55, b: 50},
legend: {orientation: "h", y: -0.18}
};
Plotly.react(el, traces, layout, {responsive: true});
summary.innerHTML = "Mean European payoff: <strong>" + meanEuro.toFixed(3) +
"</strong>. Mean Asian payoff: <strong>" + meanAsian.toFixed(3) +
"</strong>. Averaging usually reduces extreme payoff variation.";
}
function update() {
var sigma = Number(sigmaSlider.value);
var K = Number(kSlider.value);
sigmaVal.textContent = sigma.toFixed(2);
kVal.textContent = K.toFixed(0);
simulatePaths(sigma, K);
}
sigmaSlider.addEventListener("input", update);
kSlider.addEventListener("input", update);
update();
});
</script>
## 23.10 Interactive Demo: Barrier Survival Probability
Barrier options are sensitive to the whole path. The next demo estimates the probability that a risk-neutral GBM path stays below an upper barrier. Notice how the estimated survival probability changes when the barrier moves closer to $S_0$.
::: {.interactive-card}
### Up-barrier survival under GBM
<div class="mf-control-row">
<label for="ch23-barrier-level">Barrier $B$</label>
<input id="ch23-barrier-level" type="range" min="105" max="170" step="1" value="130">
<span id="ch23-barrier-level-val" class="mf-badge">130</span>
</div>
<div class="mf-control-row">
<label for="ch23-barrier-sigma">Volatility $\sigma$</label>
<input id="ch23-barrier-sigma" type="range" min="0.05" max="0.70" step="0.01" value="0.25">
<span id="ch23-barrier-sigma-val" class="mf-badge">0.25</span>
</div>
<div id="ch23-barrier-demo" class="plotly-demo"></div>
<div id="ch23-barrier-summary" class="mf-output"></div>
:::
<script>
window.mfPlotlyReady(function() {
var bSlider = document.getElementById("ch23-barrier-level");
var sigmaSlider = document.getElementById("ch23-barrier-sigma");
var bVal = document.getElementById("ch23-barrier-level-val");
var sigmaVal = document.getElementById("ch23-barrier-sigma-val");
var summary = document.getElementById("ch23-barrier-summary");
var el = document.getElementById("ch23-barrier-demo");
if (!el || !bSlider || !sigmaSlider) return;
function randn(seedObj) {
seedObj.seed = (1103515245 * seedObj.seed + 12345) % 2147483648;
var u1 = (seedObj.seed + 1) / 2147483649;
seedObj.seed = (1103515245 * seedObj.seed + 12345) % 2147483648;
var u2 = (seedObj.seed + 1) / 2147483649;
return Math.sqrt(-2 * Math.log(u1)) * Math.sin(2 * Math.PI * u2);
}
function updateDemo() {
var B = Number(bSlider.value);
var sigma = Number(sigmaSlider.value);
var S0 = 100, r = 0.04, T = 1, n = 160, paths = 900, dt = T / n;
var seedObj = {seed: 76543};
var time = Array.from({length: n + 1}, function(_, i) { return i * dt; });
var traces = [];
var survived = 0;
var terminalAlive = [];
var terminalKnocked = [];
for (var p = 0; p < paths; p++) {
var S = S0;
var path = [S0];
var alive = true;
for (var i = 1; i <= n; i++) {
var z = randn(seedObj);
S = S * Math.exp((r - 0.5 * sigma * sigma) * dt + sigma * Math.sqrt(dt) * z);
if (S >= B) alive = false;
if (p < 14) path.push(S);
}
if (alive) {
survived += 1;
terminalAlive.push(S);
} else {
terminalKnocked.push(S);
}
if (p < 14) {
traces.push({x: time, y: path, mode: "lines", line: {width: 1}, opacity: 0.55, showlegend: false});
}
}
traces.push({x: [0, T], y: [B, B], mode: "lines", name: "barrier", line: {dash: "dash", width: 3}});
traces.push({x: terminalAlive, type: "histogram", name: "alive terminal prices", xaxis: "x2", yaxis: "y2", opacity: 0.65, nbinsx: 28});
traces.push({x: terminalKnocked, type: "histogram", name: "knocked-out terminal prices", xaxis: "x2", yaxis: "y2", opacity: 0.55, nbinsx: 28});
var layout = {
title: "Barrier survival depends on the whole path",
grid: {rows: 1, columns: 2, pattern: "independent"},
xaxis: {title: "time"},
yaxis: {title: "stock price"},
xaxis2: {title: "terminal price"},
yaxis2: {title: "frequency"},
barmode: "overlay",
margin: {l: 55, r: 25, t: 55, b: 50},
legend: {orientation: "h", y: -0.18}
};
Plotly.react(el, traces, layout, {responsive: true});
bVal.textContent = B.toFixed(0);
sigmaVal.textContent = sigma.toFixed(2);
summary.innerHTML = "Estimated discrete-monitoring survival probability: <strong>" +
(survived / paths).toFixed(3) + "</strong>. Continuous monitoring would usually give a lower survival probability.";
}
bSlider.addEventListener("input", updateDemo);
sigmaSlider.addEventListener("input", updateDemo);
updateDemo();
});
</script>
## 23.11 Python Example: Simulating Risk-Neutral Paths
The following helper function simulates risk-neutral geometric Brownian motion paths on an equally spaced grid.
```{python}
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
rng = np.random.default_rng(2023)
def simulate_gbm_paths(S0=100.0, r=0.04, sigma=0.25, T=1.0, steps=252, paths=10_000, seed=2023):
rng_local = np.random.default_rng(seed)
dt = T / steps
z = rng_local.normal(size=(paths, steps))
increments = (r - 0.5 * sigma**2) * dt + sigma * np.sqrt(dt) * z
log_paths = np.cumsum(increments, axis=1)
S = S0 * np.exp(log_paths)
S = np.column_stack([np.full(paths, S0), S])
grid = np.linspace(0.0, T, steps + 1)
return grid, S
t, S = simulate_gbm_paths(paths=5, steps=12, seed=7)
pd.DataFrame(S, columns=[f"t={x:.2f}" for x in t]).round(2)
```
The path matrix has one row per Monte Carlo path and one column per monitoring date.
## 23.12 Python Example: Arithmetic Asian Call by Monte Carlo
```{python}
def price_arithmetic_asian_call_mc(S0=100, K=100, r=0.04, sigma=0.25, T=1.0,
steps=252, paths=100_000, seed=11):
_, S = simulate_gbm_paths(S0, r, sigma, T, steps, paths, seed)
average = S[:, 1:].mean(axis=1) # exclude S0 from monitoring dates
payoff = np.maximum(average - K, 0.0)
disc_payoff = np.exp(-r * T) * payoff
price = disc_payoff.mean()
stderr = disc_payoff.std(ddof=1) / np.sqrt(paths)
return price, stderr
asian_price, asian_se = price_arithmetic_asian_call_mc(paths=50_000, seed=12)
asian_price, 1.96 * asian_se
```
The second number is the approximate half-width of a $95\%$ Monte Carlo confidence interval.
```{python}
# Compare with a European call Monte Carlo price using the same model.
def price_european_call_mc(S0=100, K=100, r=0.04, sigma=0.25, T=1.0,
steps=252, paths=100_000, seed=13):
_, S = simulate_gbm_paths(S0, r, sigma, T, steps, paths, seed)
payoff = np.maximum(S[:, -1] - K, 0.0)
disc_payoff = np.exp(-r * T) * payoff
return disc_payoff.mean(), disc_payoff.std(ddof=1) / np.sqrt(paths)
euro_price, euro_se = price_european_call_mc(paths=50_000, seed=12)
pd.DataFrame({
"claim": ["European call", "Arithmetic Asian call"],
"price estimate": [euro_price, asian_price],
"95% half-width": [1.96 * euro_se, 1.96 * asian_se]
}).round(4)
```
The Asian call is typically cheaper because the average $\bar S_T$ is less volatile than the terminal price $S_T$.
## 23.13 Python Example: Geometric Asian Closed Form
The next code implements the discrete geometric Asian formula. This gives a useful benchmark for Monte Carlo simulations.
```{python}
from math import erf, sqrt, log, exp
def norm_cdf(x):
return 0.5 * (1.0 + erf(x / sqrt(2.0)))
def geometric_asian_call_price(S0=100, K=100, r=0.04, sigma=0.25, T=1.0, m=252):
var_g = sigma**2 * T * (m + 1) * (2 * m + 1) / (6 * m**2)
sig_g = sqrt(var_g)
mu_g = log(S0) + (r - 0.5 * sigma**2) * T * (m + 1) / (2 * m)
d1 = (mu_g - log(K) + var_g) / sig_g
d2 = d1 - sig_g
return exp(-r * T) * (exp(mu_g + 0.5 * var_g) * norm_cdf(d1) - K * norm_cdf(d2))
def price_geometric_asian_call_mc(S0=100, K=100, r=0.04, sigma=0.25, T=1.0,
steps=252, paths=80_000, seed=14):
_, S = simulate_gbm_paths(S0, r, sigma, T, steps, paths, seed)
log_average = np.log(S[:, 1:]).mean(axis=1)
geom_average = np.exp(log_average)
payoff = np.maximum(geom_average - K, 0.0)
disc_payoff = np.exp(-r * T) * payoff
return disc_payoff.mean(), disc_payoff.std(ddof=1) / np.sqrt(paths)
closed = geometric_asian_call_price(m=252)
mc, se = price_geometric_asian_call_mc(paths=50_000, seed=15)
closed, mc, 1.96 * se
```
The Monte Carlo estimate should be close to the closed-form price within sampling error.
## 23.14 Python Example: Up-and-Out Barrier Call
Discrete simulation checks the stock only at the grid points. This creates monitoring bias: a path may cross the barrier between grid points and still be counted as alive.
```{python}
def price_up_and_out_call_mc(S0=100, K=100, B=130, r=0.04, sigma=0.25, T=1.0,
steps=252, paths=100_000, seed=16):
_, S = simulate_gbm_paths(S0, r, sigma, T, steps, paths, seed)
running_max = S.max(axis=1)
alive = running_max < B
payoff = np.maximum(S[:, -1] - K, 0.0) * alive
disc_payoff = np.exp(-r * T) * payoff
return disc_payoff.mean(), disc_payoff.std(ddof=1) / np.sqrt(paths), alive.mean()
barrier_rows = []
for B in [115, 125, 140, 160]:
price, se, survival = price_up_and_out_call_mc(B=B, paths=40_000, seed=100 + B)
barrier_rows.append((B, price, 1.96 * se, survival))
pd.DataFrame(barrier_rows, columns=["barrier", "price", "95% half-width", "survival probability"]).round(4)
```
The option value increases as the barrier moves farther away, because knock-out becomes less likely.
## 23.15 Python Example: In-Out Parity Check
For matching terms,
$$
C^{\text{vanilla}}=C^{\text{up-out}}+C^{\text{up-in}}.
$$
We can verify this identity path by path.
```{python}
def in_out_parity_mc(S0=100, K=100, B=130, r=0.04, sigma=0.25, T=1.0,
steps=252, paths=60_000, seed=17):
_, S = simulate_gbm_paths(S0, r, sigma, T, steps, paths, seed)
vanilla_payoff = np.maximum(S[:, -1] - K, 0.0)
knocked = S.max(axis=1) >= B
up_out_payoff = vanilla_payoff * (~knocked)
up_in_payoff = vanilla_payoff * knocked
discount = np.exp(-r * T)
return {
"vanilla": discount * vanilla_payoff.mean(),
"up_out": discount * up_out_payoff.mean(),
"up_in": discount * up_in_payoff.mean(),
"difference": discount * (vanilla_payoff - up_out_payoff - up_in_payoff).mean()
}
in_out_parity_mc()
```
The difference should be numerically zero up to floating-point arithmetic.
## 23.16 Python Example: Lookback Payoffs
```{python}
def price_lookback_mc(S0=100, r=0.04, sigma=0.25, T=1.0,
steps=252, paths=80_000, seed=18):
_, S = simulate_gbm_paths(S0, r, sigma, T, steps, paths, seed)
running_max = S.max(axis=1)
running_min = S.min(axis=1)
fixed_call = np.maximum(running_max - 100, 0.0)
floating_call = S[:, -1] - running_min
floating_put = running_max - S[:, -1]
discount = np.exp(-r * T)
return pd.DataFrame({
"claim": ["fixed-strike lookback call", "floating-strike lookback call", "floating-strike lookback put"],
"price estimate": [discount * fixed_call.mean(), discount * floating_call.mean(), discount * floating_put.mean()],
"payoff standard deviation": [fixed_call.std(ddof=1), floating_call.std(ddof=1), floating_put.std(ddof=1)]
})
price_lookback_mc(paths=40_000).round(4)
```
Lookback claims can have large payoff variability because the maximum or minimum is an extreme statistic of the path.
## 23.17 Python Example: Digital Calls and Strike Sensitivity
```{python}
def black_scholes_call(S0=100, K=100, r=0.04, sigma=0.25, T=1.0):
d1 = (log(S0 / K) + (r + 0.5 * sigma**2) * T) / (sigma * sqrt(T))
d2 = d1 - sigma * sqrt(T)
return S0 * norm_cdf(d1) - K * exp(-r * T) * norm_cdf(d2)
def digital_call_price(S0=100, K=100, r=0.04, sigma=0.25, T=1.0, Q=1.0):
d2 = (log(S0 / K) + (r - 0.5 * sigma**2) * T) / (sigma * sqrt(T))
return Q * exp(-r * T) * norm_cdf(d2)
K0 = 100
h = 0.10
finite_difference_digital = -(black_scholes_call(K=K0 + h) - black_scholes_call(K=K0 - h)) / (2 * h)
closed_digital = digital_call_price(K=K0, Q=1.0)
finite_difference_digital, closed_digital
```
The finite-difference approximation estimates the derivative $-\partial C/\partial K$, which equals the unit-cash digital price in the Black--Scholes model.
## 23.18 Monte Carlo Error and Variance Reduction
Monte Carlo pricing estimates
$$
V_0=e^{-rT}\mathbb E^{\mathbb Q}[H]
$$
by the sample mean
$$
\widehat V_N=e^{-rT}\frac{1}{N}\sum_{i=1}^N H^{(i)}.
$$
The standard error is
$$
\operatorname{SE}(\widehat V_N)
=\frac{e^{-rT}\widehat{\operatorname{sd}}(H)}{\sqrt N}.
$$
This $N^{-1/2}$ convergence rate can be slow. Exotic options often benefit from variance reduction:
- **antithetic variables:** simulate paired shocks $Z$ and $-Z$;
- **control variates:** use a related claim with known price, such as a geometric Asian option;
- **stratified sampling:** force samples into representative regions;
- **importance sampling:** simulate rare important events more often and reweight;
- **Brownian bridge correction:** adjust barrier-crossing probabilities between monitoring dates.
For MA Applied Math and MS Statistics students, the main lesson is that simulation is a statistical estimator. It needs bias analysis, variance analysis, and diagnostic checks.
::: {.ai-box}
## AI-assisted simulation audit
Ask an AI assistant to review a Monte Carlo exotic option code using the following checklist.
1. Does the simulation use the risk-neutral drift $r$, not the physical drift $\mu$?
2. Are cash flows discounted by $e^{-rT}$?
3. Is the payoff definition exactly the contract definition?
4. For path-dependent claims, are all needed path variables stored or updated correctly?
5. For barrier options, does the code specify discrete or continuous monitoring?
6. Is the reported uncertainty a standard error or a confidence interval?
7. Are random seeds used only for reproducibility, not for hiding instability?
:::
## 23.19 Common Mathematical Mistakes
::: {.warning}
## Mistake 1: Forgetting the path functional
Writing an Asian payoff as $(S_T-K)^+$ changes the contract. The correct arithmetic Asian call payoff is
$$
\left(\frac{1}{T}\int_0^T S_u\,du-K\right)^+.
$$
:::
::: {.warning}
## Mistake 2: Using physical drift for pricing
Historical drift $\mu$ may be used for risk forecasting or statistical estimation. Risk-neutral pricing in Black--Scholes uses drift $r$ under $\mathbb Q$.
:::
::: {.warning}
## Mistake 3: Treating discrete and continuous monitoring as identical
A barrier monitored daily is not the same as a barrier monitored continuously. Continuous monitoring gives more opportunities to hit the barrier.
:::
::: {.warning}
## Mistake 4: Ignoring discontinuity
Digital and barrier payoffs can be discontinuous. This affects numerical differentiation, hedging, and Monte Carlo variance.
:::
## 23.20 AI Component: Contract-to-Math Translation
A useful AI workflow in mathematical finance is not to ask for a price immediately. Instead, first ask the AI system to translate the contract into precise mathematical objects.
::: {.ai-box}
## Prompt: translate contract language into mathematics
A contract pays the positive part of the average closing price over 30 observation dates minus a strike. Please translate this contract into mathematical notation. Specify:
- the monitoring dates;
- the average;
- the payoff;
- the risk-neutral pricing formula;
- the state variables needed for dynamic pricing;
- at least two numerical issues that may arise.
:::
A good response should produce something like
$$
\bar S=\frac{1}{30}\sum_{i=1}^{30}S_{t_i},
\qquad
H=(\bar S-K)^+,
\qquad
V_0=e^{-rT}\mathbb E^{\mathbb Q}[H].
$$
Then the student can check whether the notation actually matches the contract.
## 23.21 Summary
Exotic options are not outside the mathematical finance framework. They use the same pillars:
1. define the payoff precisely;
2. identify the information available over time;
3. choose the correct risk-neutral model;
4. write the price as a discounted conditional expectation;
5. enlarge the state when the payoff is path-dependent;
6. choose an analytical, PDE, tree, or Monte Carlo method;
7. audit numerical bias and variance.
The core message is:
$$
\boxed{\text{Exotic option pricing = martingale pricing + careful payoff/state modeling.}}
$$
## 23.22 Exercises
### Conceptual exercises
1. Explain why an arithmetic Asian option is path-dependent.
2. Explain why a digital call is path-independent but still considered exotic.
3. Give one example of a payoff depending on the running maximum and one depending on the running minimum.
4. Explain the difference between a knock-in and a knock-out option.
5. Why is an up-and-out call usually cheaper than a vanilla call with the same strike and maturity?
6. Explain why a continuously monitored barrier option is not the same as a daily monitored barrier option.
### Mathematical exercises
7. Let $A_t=\int_0^t S_u\,du$. Under risk-neutral GBM, derive the generator of the two-dimensional process $(S_t,A_t)$.
8. Derive the PDE for the arithmetic Asian call value $v(t,s,a)$.
9. Prove in-out parity for an up-and-out and up-and-in call.
10. Derive the cash-or-nothing digital call price under Black--Scholes.
11. Show that $-\partial C/\partial K=e^{-rT}\mathbb Q(S_T>K)$ for a smooth call price function.
12. For discrete monitoring dates $t_i=iT/m$, derive the mean and variance of $\log G_T$.
### Computational exercises
13. Write Python code to price an arithmetic Asian put by Monte Carlo.
14. Compare arithmetic and geometric Asian call prices for several volatilities.
15. Implement an antithetic-variable estimator for an Asian call and compare the standard error with ordinary Monte Carlo.
16. Estimate an up-and-out call price using 50, 100, 250, and 1000 monitoring steps. Describe the monitoring bias.
17. Use simulation to estimate the price of a floating-strike lookback put.
18. Approximate a unit digital call price by finite differences of Black--Scholes call prices for several step sizes $h$.
### AI-assisted exercises
19. Give an AI assistant a contract description for a barrier option and ask it to produce the payoff. Identify any ambiguity in the contract language.
20. Ask an AI assistant to review your Asian option Monte Carlo code. Which issues did it catch? Which issues did it miss?
21. Ask an AI assistant to explain why barrier options require careful monitoring. Rewrite the explanation in mathematically precise language.
22. Ask an AI assistant for three variance-reduction methods for Asian options. Verify each method mathematically before using it.