Monte Carlo Option Pricing: Sampling Error and Confidence Intervals
Summary
The document examines why repeated Monte Carlo estimates of a European call price can vary, even when the same calculation is implemented in different programming languages. It compares sample estimates with the known analytical price and argues that the variation is explained by sampling uncertainty and the central limit theorem, rather than by Python itself. Plotting running estimates against the analytical value is suggested as a way to see convergence.
It also outlines a confidence interval based on the sample variance of discounted payoffs and the standard error, using multipliers for approximate 95% and 99% intervals. The reported runs fall within the stated 99% interval, illustrating that visible variation can be consistent with finite-sample uncertainty. The example uses one option setup and a limited number of runs; the interval is an estimate, not a guarantee that each simulation result will be close to the true price.
Key ideas
- Monte Carlo price estimates vary because of sampling error, regardless of programming language.
- A running estimate plotted against the analytical price can help show convergence.
- A confidence interval can be estimated from discounted payoff variance and the standard error.
- The example’s repeated estimates are reported as consistent with its stated 99% interval.
- Confidence intervals characterize uncertainty and do not ensure that each estimate is close to the true price.
Tags
Full text
# Python Monte-Carlo Convergence
# Python Monte-Carlo Convergence
Edited to include VBA code for comparison
Also, we know the analytical value of the simple Call option, which is 8.021, towards which the Monte-Carlo should converge, which makes the comparison easier.
Excel VBA gives 8.067 based on averaging 5 Monte-Carlo simulations (7.989, 8.187, 8.045, 8.034, 8.075)
Python gives 7.973 based on 5 MCs (7.913, 7.915, 8.203, 7.739, 8.095) and a larger Variance!
The VBA code is using a rather bad way to produce samples from Standard Normal...
I am running a super simple code in Python to price European Call Option via Monte Carlo, and I am surprised at how "bad" the convergence is with 10,000 "simulated paths". Usually, when running a Monte-Carlo for this simple problem in C++ or even VBA, I get better convergence.
I show the code below (the code is taken from Textbook "Python for Finance" and I run in in Visual Studio Code under Python 3.7.7, 64-bit version): I get the following results, as an example: Run 1 = 7.913, Run 2 = 7.915, Run 3 = 8.203, Run 4 = 7.739, Run 5 = 8.095,
Results such as the above, that differ by so much, would be unacceptable. How can the convergence be improved??? (Obviously by running more paths, but as I said: for 10,000 paths, the result should already have converged much better):
```
#MonteCarlo valuation of European Call Option
import math
import numpy as np
#Parameter Values
S_0 = 100. # initial value
K = 105. # strike
T = 1.0 # time to maturity
r = 0.05 # short rate (constant)
sigma = 0.2 # vol
nr_simulations = 10000
#Valuation Algo:
# Notice the vectorization below, instead of a loop
z = np.random.standard_normal(nr_simulations)
# Notice that the S_T below is a VECTOR!
S_T = S_0 * np.exp((r-0.5*sigma**2)*T+math.sqrt(T)*sigma*z)
#Call option pay-off at maturity (Vector!)
C_T = np.maximum((S_T-K),0)
# C_0 is a scalar
C_0 = math.exp(-r*T)*np.average(C_T)
print('Value of the European Call is: ', C_0)
```
I also include VBA code, which produces slightly better results (in my opinion): with the VBA code below, I get 7.989, 8.187, 8.045, 8.034, 8.075.
```
Option Explicit
Sub monteCarlo()
' variable declaration
' stock initial & final values, option pay-off at maturity
Dim stockInitial, stockFinal, optionFinal As Double
' r = rate, sigma = volatility, strike = strike price
Dim r, sigma, strike As Double
'maturity of the option
Dim maturity As Double
' instatiate variables
stockInitial = 100#
r = 0.05
maturity = 1#
sigma = 0.2
strike = 105#
' normal is Standard Normal
Dim normal As Double
' randomNr is randomly generated nr via "rnd()" function, between 0 & 1
Dim randomNr As Double
' variable for storing the final result value
Dim result As Double
Dim i, j As Long, monteCarlo As Long
monteCarlo = 10000
For j = 1 To 5
result = 0#
For i = 1 To monteCarlo
' get random nr between 0 and 1
randomNr = Rnd()
'max(Rnd(), 0.000000001)
' standard Normal
normal = Application.WorksheetFunction.Norm_S_Inv(randomNr)
stockFinal = stockInitial * Exp((r - (0.5 * (sigma ^ 2)))*maturity + (sigma * Sqr(maturity) * normal))
optionFinal = max((stockFinal - strike), 0)
result = result + optionFinal
Next i
result = result / monteCarlo
result = result * Exp(-r * maturity)
Worksheets("sheet1").Cells(j, 1) = result
Next j
MsgBox "Done"
End Sub
Function max(ByVal number1 As Double, ByVal number2 As Double)
If number1 > number2 Then
max = number1
Else
max = number2
End If
End Function
```
## Answer by David Duarte (score 3, accepted)
https://quant.stackexchange.com/a/53528
The convergence of your monte carlo has little to do with the programming language you are using and is explained by the distribution and the central limit theorem. You should be able to implement the exact same thing in different languages.
The best way to get a feel for the convergence would be to visualize it.
```
import matplotlib.pyplot as plt
from scipy.stats import norm
N = norm.cdf
def call(S, K, T, r, vol):
d1 = (np.log(S/K) + (r + 0.5*vol**2)*T) / (vol*np.sqrt(T))
d2 = d1 - vol * np.sqrt(T)
return S * N(d1) - np.exp(-r * T) * K * N(d2)
df = np.exp(-r*T)
prices = [df*C_T[:n].mean() for n in range(1,nr_simulations)]
plt.plot(prices)
plt.axhline(call(S_0, K, T, r, sigma), c='r')
```
## Answer by djahiz (score 2)
https://quant.stackexchange.com/a/53534
In order to test the convergence you can add the computation of confidence interval at a given probability. We add in the code the computation of the variance and the interval.
$V\_T = \frac{1}{M-1} \times \sum (e^{-rT}C\_T - C\_0)^2$
$I_M = [C_0-q\times \sqrt{\frac{V\_T}{M}}; C_0+q\times \sqrt{\frac{V\_T}{M}}]$
With $M$ the number of simulation and a confidence level of 95% for $q = 2$ and 99% for $q = 3$
```
V_T = 1/(nr_simulations-1)*sum((np.exp(-r*T)*C_T-C_0)**2)
q=3
I_m = C_0 - q*math.sqrt(V_T/nr_simulations)
I_M = C_0 + q*math.sqrt(V_T/nr_simulations)
print("Price between " + str(I_m) + " and " + str(I_M) + " with probability 99%.")
```
Result for $q = 3$ $I_M = [7.59; 8.37]$
So the values
> Run 1 = 7.913, Run 2 = 7.915, Run 3 = 8.203, Run 4 = 7.739, Run 5 = 8.095,
are coherent with a confidence interval at 99% for 10000 simulations.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.