Skip to content
All library documents

Discounted P&L in Volatility-Mismatch Delta Hedging

Article Quant Q&A · Author: Enrico

Summary

The document examines a simulation intended to reproduce the result that a call priced with implied volatility and hedged using realized volatility produces cumulative discounted gains or losses related to the difference between the two option values. It outlines a discrete-time method: revalue the call, hedge with the prior step’s delta, account for changes in the stock, include the financing effect on the cash balance, and discount the resulting increments.

The accepted explanation identifies cash financing at the risk-free rate as a required part of the hedge accounting. The cash position is the option value less the value of the stock hedge, and its interest over each interval affects the path. The discussion also notes that the usual Black–Scholes replication assumption permits borrowing or investing at that rate; if this is unavailable, hedging results need not match the model value. The material is an implementation discussion rather than a full derivation, and its simulation setup does not establish how outcomes change under alternative financing constraints.

Key ideas

  • A delta-hedged option’s P&L includes changes in both the option and the underlying hedge.
  • The cash balance created by the option and stock positions earns or pays financing over each time interval.
  • Cumulative P&L increments must be discounted consistently when comparing them with an initial value difference.
  • Black–Scholes replication assumes borrowing and lending at the risk-free rate.
  • Financing constraints can cause realized hedging results to depart from the model’s replication value.

Tags

Full text
# Option Hedging: simulation of cumulative hedged paths and discounting


# Option Hedging: simulation of cumulative hedged paths and discounting












I'm trying to replicate the Figure 5.1 at pag 96 of Volatility Smile by Derman & Miller where they show that the (cumulative discounted) P&Ls of a hedged portfolio, where the call is evaluated at the implied volatility and the hedge is done at the true realized one, should converge to $C(\sigma_R,t_0)-C(\sigma_I,t_0)$, where $C(\cdot)$ is the BSM call value.

I don't understand if I'm doing something wrong in the discounting of the cumulative P&L or if I'm calculating in the wrong way the hedged portfolio variations in presence of a constant risk-free $r$.

In my Monte Carlo set up, for each simulation along a log-normal path for the stock price, I do these steps:

- calculate, for each time step $t_i$, $C(S_{t_i},\sigma_I)$ and $\Delta_{t_i}(\sigma_R)$

- calculate, for each $t_i$, the change in the P&L of the hedged position: $$ d\pi(t_i) = \pi({t_i}) - \pi({t_{i-1}}) = C(S_{t_i},\sigma_I)-C(S_{t_{i-1}},\sigma_I)-\Delta_{t_{i-1}}(\sigma_R)(S_{t_i}-S_{t_{i-1}}) \color{red}{-\big[C(S_{t_{i-1}},\sigma_I)-\Delta_{t_{i-1}}(\sigma_R)S_{t_{i-1}}\big](t_i-t_{i-1})r}$$

- calculate the discounted cumulated P&L: $P\&L^{\text{cum}}_{t_i} = \sum_{u=0}^{t_i} e^{-ru}\pi(u)$

When I plot these $P\&L^{\text{cum}}$ paths they don't converge to the correct value. If instead I use value zero as the risk free rate, these steps seem correct.

Thanks for the help. Let me know if more details are needed.

Edit

I think I've forgotten to add the variation in the "cash" instrument used both to establish the initial position and to finance/invest the hedge at each step. Above in red this term. I've also updated the code accordingly.

A new question arises: what happen to these paths if I can't invest or borrow at the risk-free rate?

Below the Python code I'm using if someone is interested in it.

```
 import numpy as np
 import pandas as pd
 import matplotlib.pyplot as plt
 import scipy as sp
 from numpy.ma.core import arange
    
 pd.options.mode.copy_on_write = True

# Paths simulation

rng = np.random.default_rng()

rf = .06 # risk free
sigma_r = .30 # realized vol
sigma_i = .20 # implied vol
TTM = 1
K=100
S0=100

B = 100 # bootstrap iterations
T= 1000 # time steps

dt = TTM/T

z = rng.normal(size=(T,B))

sims = pd.DataFrame(z)

names = ['b' + str(x) for x in range(B)]

sims.columns = names

out = sims.map(func= lambda x, rf, sigma, dt :np.exp((rf - .5 * sigma**2 ) * dt + sigma * np.sqrt(dt) * x),
               rf = rf, sigma = sigma_r, dt = dt)

# by column
out2 = S0 * out.cumprod()

out2 = np.vstack((np.repeat(S0,B),out2))

out2 = pd.DataFrame(out2)

# Delta BSM
def delta_bsm(S, K, sigma, rf, tau):
    d1 = (np.log(S/K)+(rf+.5*sigma**2)*tau)/(sigma*np.sqrt(tau))
    return sp.stats.norm.cdf(d1)

def call_bsm(S,K,sigma,rf,tau):
    d1 = (np.log(S/K)+(rf+.5*sigma**2)*tau)/(sigma*np.sqrt(tau))
    d2 = d1 - sigma*np.sqrt(tau)
    return S*sp.stats.norm.cdf(d1)-K*sp.stats.norm.cdf(d2)*np.exp(-rf*tau)

# call values
call_values = np.zeros(shape=out2.shape)

for b in np.arange(0,call_values.shape[1]):
    for i in np.arange(0, call_values.shape[0]):
        if(i < call_values.shape[0]-1):
            # evaluation at sigma implied
            call_values[i,b] = call_bsm(out2.iloc[i,b],K=K,sigma=sigma_i,rf=rf,tau=TTM-dt*i)
        else:
            call_values[i,b] = (out2.iloc[i,b]-K)*(out2.iloc[i,b]>K)

# Delta calculation
for b in np.arange(0,deltas.shape[1]):
    for i in np.arange(0,deltas.shape[0]-1):
            deltas[i,b] = delta_bsm(S=out2.iloc[i,b], K=K, sigma=sigma_r, rf=rf, tau=TTM-dt*(i))

# call PnLs
call_pnl = np.zeros(shape=call_values.shape)

for b in np.arange(0,call_pnl.shape[1]):
    for i in np.arange(1,call_pnl.shape[0]):
        call_pnl[i,b] = (call_values[i,b]-call_values[i-1,b])

paths = np.zeros(shape=call_values.shape)

for b in np.arange(0,call_pnl.shape[1]):
    for i in np.arange(0,call_pnl.shape[0]):
        if i == 0:
            paths[i,b] = 0
        else:
            paths[i,b] = (call_pnl[i,b] - deltas[i-1,b]*(out2.iloc[i,b]-out2.iloc[i-1,b]))

# Add cash variations to each path
for b in np.arange(0,paths.shape[1]):
        for i in np.arange(0,paths.shape[0]):
            if i == 0:
                paths[i,b] = paths[i,b]
            else:
                paths[i,b] = paths[i,b] - (call_values[i-1,b]-deltas[i-1,b]*out2.iloc[i-1,b])*rf*dt

    
cum_paths = np.zeros(shape=call_pnl.shape)

for b in np.arange(0,call_pnl.shape[1]):
    for i in np.arange(0,call_pnl.shape[0]):
        if i == 0:
            cum_paths[i,b] = 0
        else:
            cum_paths[i,b] = sum(paths[:(i+1),b]*(np.exp(-rf*dt*np.arange(0,i+1))))

plt.plot(cum_paths[:,:10],color='black',alpha=0.3)
plt.show()

# Value to which the cum. disc. paths should converge (pag. 97 di Vol Smile by Derman - Miller)
(call_bsm(S0,K,sigma_r,rf,TTM)-call_bsm(S0,K,sigma_i,rf,TTM))
```

## Answer by Enrico (score 0, accepted)

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

One of the hypothesis of the BSM model is the possibility to invest/borrow at the risk free rate the gains/losses of the delta hedging adjustements. If it's not possibile to do this, the sum of the delta hedging adjustements will diverge from the BSM option value and this will affect the paths.

The cumulative discounted P&Ls are correct. The missing term related to gains/losses in the risk free instrument, reported in red in the OP and added in the code in the "cash" section after the Edit, is:

$$ \big[C(S_{t_{i-1}},\sigma_I)-\Delta_{t_{i-1}}(\sigma_R)S_{t_{i-1}}\big]\times(t_i-t_{i-1})\times r $$

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.