Pricing Asian Style Options¶
In this cript we show how to use classes in QMCPy for Monte Carlo option pricing of options with Asian style payoffs and European exercise.
- The payoff depends on the whole asset price path, not only on the terminal asset price.
- The option is only exercised at expiry, unlike American options, which can be exercised at any time before expiry.
# Import necessary packages
import qmcpy as qp
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
import time
European Options¶
initPrice = 120 # initial stock price
interest = 0.02 # risk-free interest rate
vol = 0.5 # volatility
strike = 130 # strike price
tfinal = 1/4 # mature time
d = 12 # number of observations
absTol = 0.05 # absolute tolerance of a nickel
relTol = 0 # zero relative tolerance
sampleSize = 10**6 # number of smaple size
EuroCall = qp.FinancialOption(
qp.IIDStdUniform(dimension=d,seed=7),
option="EUROPEAN",
volatility=vol,
start_price=initPrice,
strike_price=strike,
interest_rate=interest,
t_final=tfinal,
call_put="call")
y = EuroCall(sampleSize)
print("The exact price of this European Call Option is ",f"{EuroCall.get_exact_value():.4f}")
print("After generate ", sampleSize,"iid points, the price of estimation of the fair price is",f"{y.mean():.4f}")
The exact price of this European Call Option is 8.2779 After generate 1000000 iid points, the price of estimation of the fair price is 8.2718
Arithmetic Mean Options¶
The payoff of the arithmetic mean option depends on the average of the stock price, not the final stock price. Here are the discounted payoffs:
$$\begin{array}{rcc} & \textbf{call} & \textbf{put} \\ \hline \textbf{payoff} & \displaystyle \max\biggl(\frac 1d \sum_{j=1}^d S(jT/d) - K,0 \biggr)\mathsf{e}^{-rT} & \displaystyle \max\biggl(K - \frac 1d \sum_{j=1}^d S(jT/d),0 \biggr)\mathsf{e}^{-rT} \end{array} $$
ArithMeanCall = qp.FinancialOption(
qp.IIDStdUniform(dimension=d,seed=7),
option="ASIAN",
volatility=vol,
start_price=initPrice,
strike_price=strike,
interest_rate=interest,
t_final=tfinal,
call_put="call",
asian_mean = 'arithmetic')
y = ArithMeanCall(sampleSize)
print("After generate ", sampleSize,"iid points, the price of this Arithmetic Mean Call Option is",f"{y.mean():.4f}")
After generate 1000000 iid points, the price of this Arithmetic Mean Call Option is 3.3856
The price of the Asian arithmetic mean call option is smaller than the price of the European call option.
We may also price the Asian arithmetic mean put option as follows:
ArithMeanPut = qp.FinancialOption(
qp.IIDStdUniform(dimension=d,seed=7),
option="ASIAN",
volatility=vol,
start_price=initPrice,
strike_price=strike,
interest_rate=interest,
t_final=tfinal,
call_put="put",
asian_mean = 'arithmetic')
y = ArithMeanPut(sampleSize)
print("After generate ", sampleSize,"iid points, the price of this Arithmetic Mean Put Option is ",f"{y.mean():.4f}")
After generate 1000000 iid points, the price of this Arithmetic Mean Put Option is 13.0233
Note that the price is greater. This is because one strike price is above the initial price, making the expected payoff greater.
In the limit of continuous monitoring $d \to \infty$, the payoff is
$$ \begin{array}{rcc} & \textbf{call} & \textbf{put} \\ \hline \textbf{payoff} & \displaystyle \max\biggl(\frac 1T \int_{0}^T S(t) \, {\rm d} t - K,0 \biggr)\mathsf{e}^{-rT} & \displaystyle \max\biggl(K - \frac 1T \int_{0}^T S(t) \, {\rm d} t,0 \biggr)\mathsf{e}^{-rT} \end{array} $$
Such an option can be approximated by taking smaller time steps:
ArithMeanPut = qp.FinancialOption(
qp.IIDStdUniform(dimension=62,seed=7), # weekly monitoring for 3 months
option="ASIAN",
volatility=vol,
start_price=initPrice,
strike_price=strike,
interest_rate=interest,
t_final=tfinal,
call_put="call",
asian_mean = 'arithmetic')
y = ArithMeanCall(sampleSize)
print("After generate ", sampleSize,"iid points, the price of this Arithmetic Mean Call Option is",f"{y.mean():.4f}")
After generate 1000000 iid points, the price of this Arithmetic Mean Call Option is 3.3858
The price is a bit lower, and the time is longer because more time steps are needed, which means more random variables are needed.
Geometric Mean Options¶
One can also base the payoff on a geometric mean rather than an arithmetic mean. Such options have a closed form solution.
The price of a geometric mean $ \begin{Bmatrix} \text{call} \\ \text{put} \end{Bmatrix}$ option is $\begin{Bmatrix} \le \\ \ge \end{Bmatrix}$ the price of an arithmetic mean $\begin{Bmatrix} \text{call} \\\text{put} \end{Bmatrix}$ option because a geometric mean is smaller than an arithmetic mean.
GeoMeanPut = qp.FinancialOption(
qp.IIDStdUniform(dimension=12,seed=7), #Weekly Monitoring for three months
option="ASIAN",
volatility=vol,
start_price=initPrice,
strike_price=strike,
interest_rate=interest,
t_final=tfinal,
call_put="put",
asian_mean='geometric',
asian_mean_quadrature_rule="RIGHT")
y = GeoMeanPut(sampleSize)
print("The exact price of this Geometric Asian Put Option is ",f"{GeoMeanPut.get_exact_value():.4f}")
print("After generate ", sampleSize,"iid points, the price of this Geometric Mean Put Option is ",f"{y.mean():.4f}")
The exact price of this Geometric Asian Put Option is 13.7841 After generate 1000000 iid points, the price of this Geometric Mean Put Option is 13.7663
GeoMeanCall = qp.FinancialOption(
qp.IIDStdUniform(dimension=12,seed=7), #Weekly Monitoring for three months
option="ASIAN",
volatility=vol,
start_price=initPrice,
strike_price=strike,
interest_rate=interest,
t_final=tfinal,
call_put="call",
asian_mean='geometric',
asian_mean_quadrature_rule="RIGHT")
y = GeoMeanCall(sampleSize)
print("The exact price of this Geometric Asian Put Option is ",f"{GeoMeanCall.get_exact_value():.4f}")
print("After generate ", sampleSize,"iid points, the price of this Geometric Mean Call Option is ",f"{y.mean():.4f}")
The exact price of this Geometric Asian Put Option is 3.5401 After generate 1000000 iid points, the price of this Geometric Mean Call Option is 3.5359
Barrier Option¶
In barrier options the payoff only occurs if the asset price crosses or fails to cross a barrier, $b$
$$ \begin{array}{rcc} & \textbf{up} (S(0) < b) & \textbf{down} (S(0) > b) \\ \hline \textbf{in} & \text{active if } S(t) \ge b & \text{active if } S(t) \le b \\ \textbf{out} & \text{inactive if } S(t) \ge b & \text{inactive if } S(t) \le b \end{array} $$
For the barrier option with a European call type payoff, this corresponds to
$$ \begin{array}{rcc} \textbf{payoff} & \textbf{up} (S(0) < b) & \textbf{down} (S(0) > b) \\ \hline \textbf{in} & 1_{[b,\infty)}(\max_{0 \le t \le T} S(t)) \max(S(T)-K,0)\mathsf{e}^{-rT} & 1_{[0,b]}(\min_{0 \le t \le T} S(t)) \max(S(T)-K,0)\mathsf{e}^{-rT} \\ \textbf{out} & 1_{[0,b)}(\max_{0 \le t \le T} S(t)) \max(S(T)-K,0)\mathsf{e}^{-rT} & 1_{[b,\infty)}(\min_{0 \le t \le T} S(t)) \max(S(T)-K,0)\mathsf{e}^{-rT} \end{array} $$
BarrierUpInCall = qp.FinancialOption(
qp.IIDStdUniform(dimension=12,seed=7), #Weekly Monitoring for three months
option="BARRIER",
volatility=vol,
start_price=initPrice,
strike_price=strike,
interest_rate=interest,
t_final=tfinal,
call_put="call",
barrier_in_out="in",
barrier_price=150)
y = BarrierUpInCall(sampleSize)
print("After generate ", sampleSize,"iid points, the price of this Barrier UpIn Call Option is ",f"{y.mean():.4f}")
After generate 1000000 iid points, the price of this Barrier UpIn Call Option is 7.4022
Note that this price is less than the European call option because the asset price must cross the barrier for the option to become active.
Lookback Options¶
Lookback options do not use a strike price but use the minimum or maximum asset price as their strike. The discounted payoffs are
$$ \begin{array}{rcc} & \textbf{call} & \textbf{put} \\ \hline \textbf{payoff} & \displaystyle \Bigl(S(T) - \min_{0 \le t \le T} S(t),0 \Bigr)\mathsf{e}^{-rT} & \displaystyle \Bigl(\max_{0 \le t \le T} S(t) - S(T),0 \Bigr)\mathsf{e}^{-rT} \end{array} $$
where the values of $t$ considered for the minimum or maximum are either discrete, $0, T/d, \dots, T$, or continuous. Note that we would expect the prices of these options to be greater than their out of the money European counterparts.
LookCall = qp.FinancialOption(
qp.IIDStdUniform(dimension=12,seed=7), #Weekly Monitoring for three months
option="LOOKBACK",
volatility=vol,
start_price=initPrice,
strike_price=strike,
interest_rate=interest,
t_final=tfinal,
call_put="call")
y = LookCall(sampleSize)
print("After generate ", sampleSize,"iid points, the price of this Lookback Call Option is ",f"{y.mean():.4f}")
After generate 1000000 iid points, the price of this Lookback Call Option is 17.7080