ypchangchatgpt's picture
Upload 2 files
faa7863 verified
raw
history blame
7.5 kB
import numpy as np
from scipy.stats import qmc, norm
import plotly.graph_objects as go
import streamlit as st
# 設置 Streamlit 頁面的基本配置
st.set_page_config(page_title="European option", # 設定網頁標題
layout="wide", # 設定頁面布局為寬屏模式
initial_sidebar_state="expanded") # 初始時側邊欄狀態為展開
st.title("Geometric Brownian motion 模型下,利用模擬方法得到歐式選擇權 (European option) 評價")
with st.sidebar.form(key="my_form"):
option_type = st.radio("選擇權型態",
("Call", "Put"),
horizontal=True)
S0 = st.slider("選擇期初股價 S0",
min_value=50,
max_value=300,
value=100)
K = st.slider("選擇履約價格 K",
min_value=50,
max_value=300,
value=100)
T = st.slider("選擇到期時間 T (年)",
min_value=0.1,
max_value=2.0,
value=1.0,
step=0.1)
r = st.slider("選擇無風險利率 r",
min_value=0.01,
max_value=0.10,
value=0.03,
step=0.01)
sigma = st.slider("選擇波動率 σ",
min_value=0.1,
max_value=0.5,
value=0.2,
step=0.05)
n_simulations = st.slider("選擇模擬次數",
min_value=1000,
max_value=20000,
value=10000,
step=1000)
n_experiments = st.slider("選擇實驗重複次數",
min_value=100,
max_value=10000,
value=1000,
step=100)
seed = st.text_input(label="亂數種子", value="123457")
submit_button = st.form_submit_button(label="Submit")
#%%
# 計算歐式選擇權價格並進行折現
def European_option_price(S0, K, r, T, sigma, option_type="Call"):
# Black-Scholes 公式
d1 = (np.log(S0/K)+(r+0.5*sigma**2)*T)/(sigma*np.sqrt(T))
d2 = d1-sigma*np.sqrt(T)
# 真實的 call option 價格
if option_type == "Call":
true_option_price = S0*norm.cdf(d1)-K*np.exp(-r*T)*norm.cdf(d2)
# 真實的 put option 價格
if option_type == "Put":
true_option_price = K*np.exp(-r*T)*norm.cdf(-d2)-S0*norm.cdf(-d1)
return true_option_price
def European_option_price_simulation(S0, K, r, T, sigma, z, option_type="Call"):
ST = S0*np.exp((r-0.5*sigma**2)*T+sigma*np.sqrt(T)*z)
if option_type == "Call":
payoff = np.maximum(ST-K, 0)
if option_type == "Put":
payoff = np.maximum(K-ST, 0)
option_price = np.exp(-r*T)*np.mean(payoff)
return option_price
option_prices_pseudo, option_prices_quasi, option_prices_numpy = [], [], []
if submit_button:
bar = st.progress(20, "Start calculation")
# 顯示進度條
np.random.seed(int(seed))
sobol_engine = qmc.Sobol(d=1, scramble=True, seed=int(seed))
for _ in range(n_experiments):
# 使用 pseudo-random numbers
u_pseudo_uniform = np.random.uniform(0, 1, n_simulations)
z_pseudo_random = norm.ppf(u_pseudo_uniform)
option_price_pseudo = European_option_price_simulation(S0, K, r, T, sigma, z_pseudo_random, option_type=option_type)
option_prices_pseudo.append(option_price_pseudo)
# 使用 quasi-random numbers
u_quasi_random = sobol_engine.random(n=n_simulations).flatten()
z_quasi_random = norm.ppf(u_quasi_random)
option_price_quasi = European_option_price_simulation(S0, K, r, T, sigma, z_quasi_random, option_type=option_type)
option_prices_quasi.append(option_price_quasi)
# 使用 Numpy random
z_numpy_random = np.random.normal(0, 1, n_simulations)
option_price_numpy = European_option_price_simulation(S0, K, r, T, sigma, z_numpy_random, option_type=option_type)
option_prices_numpy.append(option_price_numpy)
true_option_price = European_option_price(S0, K, r, T, sigma, option_type=option_type)
bar = bar.progress(80, "計算中")
#%%
from plotly.subplots import make_subplots
st.subheader(option_type+" option 價格分布比較")
# 創建包含三個子圖的圖表
fig = make_subplots(rows=3, cols=1, subplot_titles=("pseudo-random numbers (np.random.uniform)",
"quasi-random numbers (qmc.Sobol)",
"pseudo-random numbers (np.random.normal)"))
nbinsx = 30
fig.add_trace(
go.Histogram(x=option_prices_pseudo,
nbinsx=nbinsx,
histnorm="probability density",
name="pseudo-random numbers (np.random.uniform)",
marker=dict(color="rgba(0,0,255,0.15)", line=dict(width=1, color="black")),
opacity=1.0),
row=1, col=1)
fig.add_trace(
go.Histogram(x=option_prices_quasi,
nbinsx=nbinsx,
histnorm="probability density",
name="quasi-random numbers (qmc.Sobol)",
marker=dict(color="rgba(255,0,0,0.15)", line=dict(width=1, color="black")),
opacity=1.0),
row=2, col=1)
fig.add_trace(
go.Histogram(x=option_prices_numpy,
nbinsx=nbinsx,
histnorm="probability density",
name="pseudo-random numbers (np.random.normal)",
marker=dict(color="rgba(0,255,0,0.15)", line=dict(width=1, color="black")),
opacity=1.0),
row=3, col=1)
fig.add_vline(x=true_option_price,
line_width=2,
line_dash="dash",
line_color="blue")
fig.update_layout(
title="不同方法計算的 "+option_type+" option 價格分布",
height=900)
fig.update_xaxes(title=option_type+" option 價格", row=1, col=1)
fig.update_xaxes(title=option_type+" option 價格", row=2, col=1)
fig.update_xaxes(title=option_type+" option 價格", row=3, col=1)
fig.update_yaxes(title="density", row=1, col=1)
fig.update_yaxes(title="density", row=2, col=1)
fig.update_yaxes(title="density", row=3, col=1)
st.plotly_chart(fig)
bar = bar.progress(100, "OK")
# 打開 "命令提示字元" 或 "終端機",開始執行程式 (或進入 Command Prompt 視窗):
# (a) Anaconda => Anaconda Prompt (or Anaconda Powershell Prompt)
# (b) WinPython => WinPython Command Prompt.exe
# Streamlit run "c:\work\subject\112\simulation\tronclass\(2024.03.21) pseudo-random numbers vs quasi-random numbers-european_option\03-pseudo-random numbers vs quasi-random numbers-european_option-streamlit2.py"
# Google Chrome => http://localhost:8501/
# Google Chrome 不關掉,程式修改存檔後,可直接執行 Google Chrome output 畫面右上角的 Return。
# 指定 localhost port => streamlit run app.py --server.port 8502
# 需要終止該應用程式只需在終端中按 Ctrl + C。
# 路徑和檔名不能有中文。
# Mac 不能使用 MacOS 內建的 Safari 瀏覽器,改用 Google Chrome 就沒問題了。