Uncertain Volatility

  • August 12, 2026
  • 12 min read
Binary Options Under Uncertain Volatility — QuantHedgeAI
QuantHedgeAI · Research Note
Derivatives · Numerical Methods No. 001 — Uncertain Volatility

Pricing a Binary Option Under Uncertain Volatility

The full nonlinear PDE, a gamma-sign volatility switch, and an explicit finite-difference scheme — worked end to end, with runnable code.

The Black–Scholes framework treats volatility as a known constant. In practice it is the one input we are least sure of, and for instruments whose value depends sharply and locally on it, that uncertainty cannot be swept into a single number. The binary (digital) option is the sharpest such case: its payoff is discontinuous, its gamma is unbounded near the strike, and its sensitivity to volatility is therefore violent and localized. This note prices it honestly — treating volatility as a bounded but unknown quantity, following Avellaneda, Levy and Parás (1995), and solving the resulting nonlinear PDE numerically.

1 The instrument and why it resists hedging

A cash-or-nothing binary call with strike \(K\) and cash amount \(C\) pays

$$ \Lambda(S_T) \;=\; C\,\mathbf{1}_{\{S_T \ge K\}} \;=\; C\,H(S_T - K), $$

where \(H(\cdot)\) is the Heaviside step. The put replaces the argument with \(K - S_T\). Unlike a vanilla, whose payoff is continuous and piecewise linear, the binary payoff jumps by \(C\) at the strike — it has, in effect, infinite slope at \(S=K\).

This single fact drives everything. A vanilla option can be delta-hedged and, under Black–Scholes assumptions, perfectly replicated. A binary cannot be replicated by any finite portfolio of vanillas. One can approximate it with a vertical spread — long a call at \(K-\varepsilon\), short a call at \(K+\varepsilon\), scaled by \(C/2\varepsilon\) — and in the limit

$$ C\,H(S-K) \;=\; \lim_{\varepsilon \to 0}\; \frac{C}{2\varepsilon}\Big[\,\text{Call}(S;K-\varepsilon) - \text{Call}(S;K+\varepsilon)\,\Big], $$

but this requires ever-larger positions in ever-tighter spreads and is not a tradable strategy. Any real static hedge is an approximation whose error concentrates at the strike.

The crux A binary's gamma \(\Gamma = \partial^2 V/\partial S^2\) changes sign and grows without bound as \(S\to K\) near expiry. Since volatility enters the pricing equation multiplied by \(\Gamma\), the option's dependence on volatility is not merely large near the strike — it flips sign there. A single volatility number is therefore most wrong exactly where the option is most sensitive.

2 Uncertain volatility and the nonlinear PDE

Rather than fixing \(\sigma\) or imposing a stochastic-volatility process, Avellaneda–Levy–Parás assume only that realized volatility lies in a band

$$ \sigma^- \le \sigma \le \sigma^+, $$

with the bounds taken from extremes of liquid implied vols or historical volatility spikes. We then price for the worst case over that band. For a short position (we have sold the option and must hold enough), the value \(V^+\) solves a Black–Scholes–Barenblatt equation in which the diffusion coefficient itself depends on the sign of gamma:

$$ \frac{\partial V^{+}}{\partial t} \;+\; \tfrac{1}{2}\,\Sigma(\Gamma)^2 S^2\,\frac{\partial^2 V^{+}}{\partial S^2} \;+\; rS\,\frac{\partial V^{+}}{\partial S} \;-\; rV^{+} \;=\; 0, $$

with the volatility selector

$$ \Sigma(\Gamma) \;=\; \begin{cases} \sigma^{+}, & \Gamma \ge 0,\\[4pt] \sigma^{-}, & \Gamma < 0, \end{cases} \qquad \Gamma = \frac{\partial^2 V^{+}}{\partial S^2}. $$

For the long position (the worst case when we hold the option) the inequalities reverse, giving the lower value \(V^-\):

$$ \Sigma(\Gamma) = \begin{cases}\sigma^{-}, & \Gamma \ge 0,\\[2pt]\sigma^{+}, & \Gamma < 0.\end{cases} $$

The two solutions \(V^-\le V^+\) bracket the price: the model itself produces a bid–ask spread as a direct function of the volatility band, with no market convention imposed on top. The equation is nonlinear because \(\Sigma\) depends on \(\Gamma\), which depends on the solution — so no closed form exists and we solve numerically.

Where this comes from Start from Itô on \(V(S,t)\) with \(dS = \mu S\,dt + \sigma S\,dX\), form the delta-hedged portfolio \(\Pi = V - \Delta S\), and impose \(d\Pi = r\Pi\,dt\). The drift drops out and the \(\tfrac12\sigma^2 S^2 \Gamma\) term remains. Minimizing \(d\Pi\) over \(\sigma\in[\sigma^-,\sigma^+]\) picks \(\sigma\) by the sign of \(\Gamma\) — which is exactly the selector above.

3 Explicit finite-difference scheme

Discretize on a grid \(S_i = i\,\delta S\) for \(i=0,\dots,N\) and \(t = T - k\,\delta t\), marching backward from the known payoff at expiry. Write \(V_i^{k} = V(i\,\delta S,\; T - k\,\delta t)\). Approximate the Greeks with central differences in \(S\) and a forward difference in time:

$$ \Delta_i = \frac{V_{i+1}^{k} - V_{i-1}^{k}}{2\,\delta S}, \qquad \Gamma_i = \frac{V_{i+1}^{k} - 2V_i^{k} + V_{i-1}^{k}}{\delta S^{2}}, $$ $$ \Theta_i = -\tfrac{1}{2}\,\Sigma(\Gamma_i)^2 S_i^2\,\Gamma_i \;-\; r S_i \Delta_i \;+\; r V_i^{k}, $$

and step back in time with the forward-difference update

$$ V_i^{k+1} = V_i^{k} - \delta t\,\Theta_i. $$

The volatility selector is applied pointwise inside the loop: at each node we compute \(\Gamma_i\), choose \(\sigma^-\) or \(\sigma^+\) by its sign, and use that in \(\Theta_i\). This is what makes the scheme nonlinear in practice — the coefficient is decided cell by cell from the current solution's convexity.

Stability. The explicit scheme is conditionally stable; the timestep must satisfy roughly

$$ \delta t \;\lesssim\; \frac{(\delta S)^2}{(\sigma^{+})^2 S_{\max}^2}, $$

which in the implementation below is enforced by choosing \(\delta t \propto 1/(N^2 \sigma^2)\). Boundaries: at \(S=0\) the PDE collapses to \(\partial_t V = rV\), so \(V_0^{k+1} = V_0^{k}(1 - r\,\delta t)\); at the top of the grid we impose \(\Gamma\to 0\) (locally linear value), giving \(V_N = 2V_{N-1} - V_{N-2}\).

Original implementation (VBA / Excel)

VBA — original
' binary option, uncertain volatility, explicit finite differences
Function binary_option(VolMin, VolMax, intrate, Expn, Strike, Cash, Payoff, EarlyEx, NAS)
    ReDim S(0 To NAS): ReDim VOld(0 To NAS): ReDim VNew(0 To NAS)

    dS = 2 * Strike / NAS
    dt = 0.9 / NAS / NAS / VolMax / VolMax   ' stability-driven step
    NTS = Int(Expn / dt) + 1
    dt = Expn / NTS
    q = 1: If Payoff = "P" Then q = -1

    ' seed payoff at expiry
    For i = 0 To NAS
        S(i) = i * dS
        If S(i) >= Strike Then VOld(i) = Cash Else VOld(i) = 0
    Next i

    ' march backward in time
    For k = 1 To NTS
        For i = 1 To NAS - 1
            Delta = (VOld(i + 1) - VOld(i - 1)) / 2 / dS
            Gamma = (VOld(i + 1) - 2 * VOld(i) + VOld(i - 1)) / dS / dS
            ' --- gamma-sign volatility selector (short position) ---
            Vol = VolMax
            If Gamma > 0 Then Vol = VolMin
            Theta = -0.5 * Vol * Vol * S(i) * S(i) * Gamma _
                    - intrate * S(i) * Delta + intrate * VOld(i)
            VNew(i) = VOld(i) - dt * Theta
        Next i
        VNew(0) = VOld(0) * (1 - intrate * dt)              ' S=0 boundary
        VNew(NAS) = 2 * VNew(NAS - 1) - VNew(NAS - 2)        ' upper boundary
        For i = 0 To NAS: VOld(i) = VNew(i): Next i
    Next k

    binary_option = VOld
End Function

Modern implementation (Python / NumPy)

The same logic, vectorized: the whole asset vector advances per timestep, the selector becomes a boolean mask, and the structure extends naturally to Crank–Nicolson for unconditional stability.

Python — vectorized
import numpy as np

def binary_uvm(vol_min, vol_max, r, T, K, cash, N=200, payoff="C"):
    """Cash-or-nothing binary under uncertain volatility (worst case, short).
    Explicit finite differences, gamma-sign volatility selector."""
    dS = 2 * K / N
    dt = 0.9 / (N * N * vol_max ** 2)          # stability-driven
    NTS = int(T / dt) + 1
    dt = T / NTS
    S = np.arange(N + 1) * dS

    # payoff at expiry
    V = np.where(S >= K, cash, 0.0) if payoff == "C" \
        else np.where(S <= K, cash, 0.0)

    i = np.arange(1, N)                          # interior nodes
    for _ in range(NTS):
        delta = (V[i + 1] - V[i - 1]) / (2 * dS)
        gamma = (V[i + 1] - 2 * V[i] + V[i - 1]) / dS ** 2
        # worst-case (short): high vol where gamma>=0, low where gamma<0
        vol = np.where(gamma >= 0, vol_max, vol_min)
        theta = -0.5 * vol ** 2 * S[i] ** 2 * gamma - r * S[i] * delta + r * V[i]
        Vn = V.copy()
        Vn[i] = V[i] - dt * theta
        Vn[0]  = V[0] * (1 - r * dt)             # S=0
        Vn[-1] = 2 * Vn[-2] - Vn[-3]             # upper boundary, gamma->0
        V = Vn
    return S, V

# flip the selector (vol_min/vol_max swapped) for the long-position lower value.
# pricing to a band: run both, and V_minus <= price <= V_plus.

4 Results and what they teach

Running a binary call, \(K=100\), \(C=1\), \(r=5\%\), \(T=1\), with a band \(\sigma\in[15\%,25\%]\) produces the expected smooth approximation to the step payoff, and — the point of the exercise — a genuine price interval rather than a single value. Narrowing the band to \([20\%,25\%]\) tightens the interval; widening it to \([10\%,30\%]\) opens it. The spread is not bolted on; it falls out of the model as the honest cost of not knowing \(\sigma\).

The static vertical-spread hedge — short a call at 90, long a call at 110 against the binary at 100 — shows a sharp residual localized at the strike, precisely where the binary's infinite slope refuses to be matched by finite-slope vanillas. That residual is diagnostic: it quantifies where the hedge is weakest and how much the approximation leaves exposed.

Two extensions worth stating

  • Inhomogeneous maturities. If a hedging vanilla (say the 90-strike call) expires before the binary, a jump condition freezes its value at its own payoff for the remaining backward march: while \(k < N_{TS,1}\), set \(V_i \leftarrow V_{i-1}\) rather than stepping the PDE. The hedge portfolio is displaced from its equilibrium shape accordingly.
  • Bid–ask via best/worst case. To value the hedged binary net of transaction cost, reprice the long leg under its best case and the short leg under its worst case (swap the selector per leg), then back out \(B(\lambda_1,\lambda_2) = V(\lambda_1,\lambda_2) - \lambda_1 C_1 - \lambda_2 C_2\) and choose the hedge weights \(\lambda_1,\lambda_2\) that maximize the binary's value: \(\max_{\lambda_1,\lambda_2} B\).

5 The general lesson

The binary is a vehicle for a principle that generalizes far beyond exotics: when a model input is genuinely uncertain, solving for the worst case over a bounded set is more defensible than solving precisely for a guessed point. The payoff of that discipline is a price that is a range rather than a false certainty, a hedge whose weaknesses are explicit, and a valuation that survives an adverse realization of the unknown parameter rather than merely flattering the assumed one. Robustness over false precision — the digital option simply makes the failure of a single confident number impossible to ignore.

Luigi Piva (CQF) writes QuantHedgeAI, research notes on systematic strategy, derivatives and numerical methods. The VBA and Python above are complete and runnable; extensions to Crank–Nicolson and to the long/short bracketing are left as natural next steps.

Reference: M. Avellaneda, A. Levy, A. Parás, "Pricing and hedging derivative securities in markets with uncertain volatilities," Applied Mathematical Finance 2 (1995). — For education and technical discussion only; not investment advice.

Get the monthly Market Regime Note

Regimes, volatility and correlations across major futures markets — with the code behind the charts. Free.

Subscribe →