|
|
|
|
|
|
|
import numpy as np |
|
from scipy import stats |
|
import matplotlib |
|
import matplotlib.pyplot as plt |
|
import mpltw |
|
|
|
matplotlib.use("Agg") |
|
|
|
|
|
font_size = 8 |
|
plt.rcParams.update({"font.size": font_size}) |
|
|
|
plt.close("all") |
|
|
|
|
|
|
|
|
|
|
|
def generate_Sobol(Sobol_seq, n=1, epsilon=0): |
|
quasi_random_numbers = Sobol_seq.random(n) |
|
|
|
d = Sobol_seq.d |
|
l_bounds = np.repeat(epsilon, d) |
|
u_bounds = np.repeat(1-epsilon, d) |
|
quasi_random_numbers = stats.qmc.scale(quasi_random_numbers, l_bounds, u_bounds) |
|
return quasi_random_numbers |
|
|
|
def Black_Scholes(S0, K, r, T, sigma, option_type): |
|
d1 = (np.log(S0/K)+(r+sigma**2/2)*T)/(sigma*np.sqrt(T)) |
|
d2 = d1-sigma*np.sqrt(T) |
|
if option_type == "call": |
|
return S0*stats.norm.cdf(d1)-K*np.exp(-r*T)*stats.norm.cdf(d2) |
|
if option_type == "put": |
|
return K*np.exp(-r*T)*stats.norm.cdf(-d2)-S0*stats.norm.cdf(-d1) |
|
|
|
def European_option_simulation(S0, K, r, T, sigma, Z): |
|
ST = S0*np.exp((r-0.5*sigma**2)*T+sigma*np.sqrt(T)*Z) |
|
payoff = np.maximum(ST-K, 0) |
|
prices = np.exp(-r*T)*np.mean(payoff, axis=0) |
|
return prices |
|
|
|
|
|
|
|
def plot_European_option(S0, K, r, T, sigma, n, m, seed, bins, alpha): |
|
S0 = np.float64(S0) |
|
K = np.float64(K) |
|
r = np.float64(r) |
|
T = np.float64(T) |
|
sigma = np.float64(sigma) |
|
|
|
price_true = Black_Scholes(S0, K, r, T, sigma, option_type="call") |
|
print("European call option price =", price_true) |
|
|
|
n = int(n) |
|
m = int(m) |
|
seed = int(seed) |
|
bins = int(bins) |
|
alpha = np.float64(alpha) |
|
|
|
|
|
np.random.seed(seed) |
|
u_pseudo = np.random.rand(n, m) |
|
Z_pseudo = stats.norm.ppf(u_pseudo) |
|
|
|
|
|
Sobol_seq = stats.qmc.Sobol(d=m, scramble=True, seed=seed) |
|
""" |
|
scramble 內建值為 True。 |
|
Scramble 是指將一個序列或集合中的元素加干擾的過程。 |
|
""" |
|
|
|
u_quasi = generate_Sobol(Sobol_seq, n=n, epsilon=1e-8) |
|
Z_quasi = stats.norm.ppf(u_quasi) |
|
""" |
|
Sobol_seq = stats.qmc.Sobol(d=n, seed=seed) |
|
u_quasi = Sobol_seq.random(m) |
|
Z_quasi = stats.norm.ppf(u_quasi).T |
|
# 這個寫法沒有明顯效果,所以要注意 d 的設定。 |
|
""" |
|
|
|
prices_pseudo = European_option_simulation(S0, K, r, T, sigma, Z_pseudo) |
|
prices_quasi = European_option_simulation(S0, K, r, T, sigma, Z_quasi) |
|
|
|
fig = plt.figure(figsize=(4, 8)) |
|
plt.subplot(211) |
|
plt.hist(prices_pseudo, |
|
bins=bins, |
|
density=True, |
|
alpha=alpha, |
|
ec="black") |
|
plt.axvline(x=price_true, color="red") |
|
plt.xlabel("simulate option price") |
|
plt.ylabel("density") |
|
plt.title("Simulate option prices with pseudo-random numbers") |
|
|
|
plt.subplot(212) |
|
plt.hist(prices_quasi, |
|
bins=bins, |
|
density=True, |
|
alpha=alpha, |
|
ec="black") |
|
plt.axvline(x=price_true, color="red") |
|
plt.xlabel("simulate option price") |
|
plt.ylabel("density") |
|
plt.title("Simulate option prices with quasi-random numbers") |
|
|
|
plt.tight_layout() |
|
return fig |
|
|
|
|
|
|
|
import gradio as gr |
|
|
|
S0 = gr.Textbox(value="100", label="S0") |
|
K = gr.Textbox(value="100", label="K") |
|
r = gr.Textbox(value="0.02", label="r") |
|
T = gr.Textbox(value="0.5", label="T") |
|
sigma = gr.Textbox(value="0.2", label="sigma") |
|
n = gr.Textbox(value="10000", label="n") |
|
m = gr.Textbox(value="1000", label="m") |
|
seed = gr.Textbox(value="123457", label="seed") |
|
bins = gr.Slider(minimum=10, maximum=60, step=5, value=40, label="bins") |
|
alpha = gr.Slider(minimum=0, maximum=1, step=0.1, value=0.6, label="alpha") |
|
inputs = [S0, K, r, T, sigma, n, m, seed, bins, alpha] |
|
|
|
outputs = [gr.Plot()] |
|
|
|
interface = gr.Interface(fn=plot_European_option, |
|
inputs=inputs, |
|
outputs=outputs, |
|
title="European call option") |
|
|
|
interface.launch(share=True) |
|
|
|
|
|
|