File size: 7,497 Bytes
faa7863
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
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 就沒問題了。