Pricing Discrete Geometric Asian Options with Analytics and Monte Carlo
Summary
The document presents a pricing discrepancy for a discrete geometric Asian call under a Black–Scholes setup. The author compares a closed-form calculation with a Monte Carlo estimate and provides the model inputs and implementation, including a simulation that compounds observed prices into a geometric average. The example reports an analytic value of 0.2170 and a Monte Carlo estimate near 0.2117.
The document is framed as a question and contains no answer explaining the difference or resolving whether it comes from the formula, sampling convention, or implementation. It therefore illustrates a comparison between analytic and simulated pricing but does not validate either result. The code and stated inputs may help diagnose the issue, though details such as the averaging dates and treatment of the initial price need careful alignment between the two methods.
Key ideas
- The example compares closed-form and Monte Carlo prices for a discrete geometric Asian call.
- The Monte Carlo procedure simulates price steps and compounds them into a geometric average.
- The two reported prices differ, but the document does not identify the cause.
- Analytical and simulated methods must use consistent averaging dates and initial-price conventions.
Tags
Full text
# Discrete geometric asian option, analytic vs MC
# Discrete geometric asian option, analytic vs MC
I am attempting to price a discrete geometric Asian option using both the closed form formula (can be found in section 3.2.2 of 'Monte Carlo methods in Financial Engineering' by Glasserman) and an MC estimator. I obtain different results, 0.2170 for the analytic and ~0.2117 for the mc estimator. I do not think it is an issue with two few trajectories. My parameters are the following:
nb_time_steps: 1001 (t0 = 0.0, T = 1.0), vol: 0.2, r: 0.05, spot: 1.0.
Please find below my code:
```
def bs_vanilla(tau, stock, K, r, div_yield, vol, *args):
normal = torch.distributions.normal.Normal(0.0, 1.0)
d_minus = (torch.log(stock / K) + (r - div_yield - vol**2 / 2) * tau) / (vol * torch.sqrt(tau))
d_plus = d_minus + vol * torch.sqrt(tau)
i1 = stock * torch.exp(-tau * div_yield) * (normal.cdf(d_plus)).to(device)
i2 = -torch.exp(-r * tau) * K * normal.cdf(d_minus).to(device)
return i1 + i2
def bs_discrete_geometric_asian(nb_steps, T, stock, K, r, vol):
time = torch.linspace(0, T, nb_steps, device=device)
inv_idx = torch.arange(time.size(0)-1, -1, -1, device=device).long()
indices = torch.arange(0, nb_steps, device=device)
mean_t = time.mean()
vol_mean = ((indices*2+1)*time[inv_idx]).sum()*vol**2/(nb_steps**2*mean_t)
div_yield = (1.0/2.0)*vol**2 - (1.0/2.0)*vol_mean
return bs_vanilla(mean_t, stock, K, r, div_yield, torch.sqrt(vol_mean))
def mc_discrete_geom(nb_traj, nb_steps, T, K, r, vol, s_0):
normal = torch.distributions.normal.Normal(0.0, 1.0)
dt = torch.tensor(T/(nb_steps-1), device=device)
running_stock = torch.ones(nb_traj, device=device)*s_0
running_stock_geom_mean = torch.ones(nb_traj, device=device)*s_0
samples = normal.sample((nb_traj, nb_steps-1)).to(device)
for t in range(1, nb_steps):
running_stock = running_stock*torch.exp((r-(1.0/2.0)*vol**2)*dt+vol*torch.sqrt(dt)*samples[:, t-1])
running_stock_geom_mean = running_stock_geom_mean * copy_tensor(running_stock)**(1/(nb_steps-1))
payoff_geom = torch.exp(-r * T) * torch.clamp(running_stock_geom_mean - K, min=0)
return payoff_geom.mean(dim=0)
print(mc_discrete_geom(100000, 1001, torch.tensor(1.0, device=device), torch.tensor(0.8), torch.tensor(0.05, device=device), torch.tensor(0.2, device=device), torch.tensor(1.0, device=device)))
print(bs_discrete_geometric_asian(1001, torch.tensor(1.0, device=device), torch.tensor(1.0, device=device), torch.tensor(0.8, device=device), torch.tensor(0.05, device=device), torch.tensor(0.2)))
```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.