Spaces:
Sleeping
Sleeping
File size: 9,368 Bytes
2dacaa6 42f8189 2dacaa6 976a6a9 42f8189 2dacaa6 42f8189 2dacaa6 42f8189 2dacaa6 42f8189 a3a5e98 2dacaa6 42f8189 2dacaa6 42f8189 |
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 188 189 190 191 192 193 194 195 196 |
import gradio as gr
import pandas as pd
from datetime import datetime
# 從 bond.py 導入重構後的函數和數據
from bond import create_bond_database, run_simulation
# 1. 數據準備
BOND_DATABASE = create_bond_database()
BOND_CHOICES = list(BOND_DATABASE.keys())
# --- Gradio 介面輔助函數 ---
def get_bond_info_html(bond_name):
"""
根據下拉選單選擇的債券名稱,生成用於顯示其詳細資訊的 HTML 字串。
:param bond_name: str, 使用者從下拉選單選擇的債券名稱。
:return: str, 一個 HTML 格式的字串,用於在 Gradio 介面中顯示。
"""
if not bond_name:
return "<p>請從上方選擇一個債券</p>"
bond = BOND_DATABASE[bond_name]
issue_date_str = bond['issueDate'].strftime('%Y-%m-%d')
maturity_date_str = bond['maturity'].strftime('%Y-%m-%d')
# **重要**: 根據資料庫中的百分比格式,調整顯示方式
html = f"""
<div style="border: 1px solid #e0e0e0; padding: 15px; border-radius: 5px;">
<h4 style="margin-top:0;">債券: {bond_name}</h4>
<table style="width:100%;">
<tr><td style="width:50%;"><strong>平均殖利率 (avgYld):</strong> {bond['avgYld']:.3f}%</td><td style="width:50%;"><strong>票面利率 (coupon):</strong> {bond['coupon']:.3f}%</td></tr>
<tr><td><strong>發行日 (issueDate):</strong> {issue_date_str}</td><td><strong>到期日 (maturity):</strong> {maturity_date_str}</td></tr>
<tr><td><strong>每年付息 (payPerYr):</strong> {bond['payPerYr']} 次</td><td><strong>每年複利 (compPerYr):</strong> {bond['compPerYr']} 次</td></tr>
<tr><td><strong>存續期間 (durT):</strong> {bond['durT']}</td><td><strong>修正存續期間 (mdurT):</strong> {bond['mdurT']}</td></tr>
<tr><td><strong>價格曲度 (convT):</strong> {bond['convT']}</td><td></td></tr>
</table>
</div>
"""
return html
def run_monte_carlo(
bond_name, n_simulations, dt,
kappa_rf, theta_rf, sigma_rf,
initial_spread, sigma_spread, correlation,
mu_r, sigma_r,
kappa_cir, theta_cir, sigma_cir
):
"""
Gradio 的主要執行函數。
它負責收集所有來自介面的輸入參數,將百分比單位轉換為小數,
調用後端的 `run_simulation` 核心函式,並將計算結果回傳給介面的各個輸出元件。
:param bond_name: str, 選擇的債券名稱。 (e.g., 'CGB10Y')
:param n_simulations: int, 模擬次數。 (e.g., 100)
:param dt: float, 時間步長。 (e.g., 1/252)
:param kappa_rf: float, 均值回歸速度。 (e.g., 0.3)
:param theta_rf: float, 長期平均利率 (%)。 (e.g., 2.2)
:param sigma_rf: float, 波動率 (%)。 (e.g., 0.5)
:param initial_spread: float, 初始信用利差 (%)。 (e.g., 0.8)
:param sigma_spread: float, 利差波動率 (%)。 (e.g., 0.3)
:param correlation: float, drf與利差的相關係數。 (e.g., 0.6)
:param mu_r: float, 長期漂移趨勢 (%)。 (e.g., 0.05)
:param sigma_r: float, 年化波動率 (%)。 (e.g., 0.6)
:param kappa_cir: float, CIR 均值回歸速度。
:param theta_cir: float, CIR 長期平均利率 (%)。
:param sigma_cir: float, CIR 波動率 (%)。
:return: tuple, 包含所有結果的元組,用於更新 Gradio 介面的各個輸出元件。
"""
if not bond_name:
raise gr.Error("請先選擇一個債券!")
bond_data = BOND_DATABASE[bond_name]
# **重要**: 將來自UI的百分比參數轉換為小數以進行計算
params = {
'kappa_rf': kappa_rf,
'theta_rf': theta_rf / 100.0,
'sigma_rf': sigma_rf / 100.0,
'initial_spread': initial_spread / 100.0,
'sigma_spread': sigma_spread / 100.0,
'correlation': correlation,
'mu_r': mu_r / 100.0,
'sigma_r': sigma_r / 100.0,
'kappa_cir': kappa_cir,
'theta_cir': theta_cir / 100.0,
'sigma_cir': sigma_cir / 100.0,
"n_simulations":int(n_simulations),
}
rate_stats_df, price_stats_df, fig_rate_paths, fig_rate_dist, fig_price_paths, fig_price_dist, report_filepath = run_simulation(
bond_data=bond_data, n_simulations=int(n_simulations), dt=dt, params=params
)
return (
rate_stats_df, price_stats_df,
fig_rate_paths, fig_rate_dist,
fig_price_paths, fig_price_dist,
gr.update(value=report_filepath, visible=True)
)
# --- 建立 Gradio 介面 ---
with gr.Blocks(theme=gr.themes.Soft(), title="債券利率蒙地卡羅模擬") as app:
gr.Markdown("# 債券利率與價格蒙地卡羅模擬")
# --- 步驟 1 & 2: 參數設定 ---
with gr.Row():
with gr.Column(scale=2):
gr.Markdown("## 步驟 1: 選擇債券")
bond_selector = gr.Dropdown(BOND_CHOICES, label="選擇債券", info="從模擬的資料庫中選擇一個債券以載入其初始參數。", value=BOND_CHOICES[0])
bond_info_display = gr.HTML(get_bond_info_html(BOND_CHOICES[0]))
with gr.Column(scale=3):
gr.Markdown("## 步驟 2: 設定模擬參數")
with gr.Tabs():
with gr.Tab("債券風險利率 (drf)"):
kappa_rf = gr.Slider(0.01, 1.0, value=0.3, step=0.01, label="均值回歸速度 (kappa_rf)")
theta_rf = gr.Slider(1.0, 5.0, value=2.2, step=0.1, label="長期平均利率 (%)")
sigma_rf = gr.Slider(0.1, 2.0, value=0.5, step=0.1, label="波動率 (%)")
with gr.Tab("有風險利率 (drs)"):
initial_spread = gr.Slider(0.1, 3.0, value=0.8, step=0.1, label="初始信用利差 (%)")
sigma_spread = gr.Slider(0.1, 1.0, value=0.3, step=0.05, label="利差波動率 (%)")
correlation = gr.Slider(-1.0, 1.0, value=0.6, step=0.05, label="drf與利差的相關係數")
with gr.Tab("綜合利率 (dr)"):
mu_r = gr.Slider(0.0, 0.2, value=0.05, step=0.01, label="長期漂移趨勢 (%)")
sigma_r = gr.Slider(0.1, 2.0, value=0.6, step=0.1, label="年化波動率 (%)")
with gr.Tab("CIR 利率 (dr_cir)"):
gr.Markdown("#### Cox-Ingersoll-Ross (CIR) 模型\n此模型 `dr = k(θ - r)dt + σ√r dW` 確保利率為正。")
kappa_cir = gr.Slider(0.01, 1.0, value=0.2, step=0.01, label="均值回歸速度 (kappa_cir)")
theta_cir = gr.Slider(1.0, 5.0, value=2.5, step=0.1, label="長期平均利率 (%)")
sigma_cir = gr.Slider(0.1, 2.0, value=0.4, step=0.1, label="波動率 (%)")
n_simulations = gr.Slider(50, 100000, value=50, step=50, label="模擬次數 (n_simulations)")
dt = gr.Number(value=1/252, label="時間步長 (dt)", info="以年為單位,預設為一個交易日(1/252年)。")
# --- 步驟 3: 執行 ---
run_button = gr.Button("開始模擬", variant="primary")
# --- 步驟 4: 結果呈現 ---
with gr.Tabs():
with gr.Tab("利率分析"):
gr.Markdown("## 利率模擬結果")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("#### 利率統計數據")
rate_results_table = gr.DataFrame(headers=["指標", "數值"], datatype=["str", "str"])
with gr.Column(scale=2):
gr.Markdown("#### 利率路徑與相關性圖")
plot_rate_paths = gr.Plot()
gr.Markdown("#### 利率變化分佈圖")
plot_rate_distribution = gr.Plot()
with gr.Tab("債券價格分析"):
gr.Markdown("## 債券價格模擬結果")
gr.Markdown("使用修正存續期間與曲度來估算價格變化。初始價格假設為 100。")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("#### 價格統計數據 (含 VaR)")
price_results_table = gr.DataFrame()
with gr.Column(scale=2):
gr.Markdown("#### 價格模擬路徑")
plot_price_paths = gr.Plot()
gr.Markdown("#### 最終價格分佈")
plot_price_distribution = gr.Plot()
# --- 步驟 5: 下載資料 ---
with gr.Column():
gr.Markdown("--- \n ## 下載完整報告,(若模擬次數超過1000,則不會包含模擬路徑與價格資料,以避免檔案過大)")
report_file = gr.File(label="下載完整報告 (Excel)", visible=False)
# --- 事件監聽 ---
bond_selector.change(fn=get_bond_info_html, inputs=bond_selector, outputs=bond_info_display)
all_inputs = [
bond_selector, n_simulations, dt,
kappa_rf, theta_rf, sigma_rf,
initial_spread, sigma_spread, correlation,
mu_r, sigma_r,
kappa_cir, theta_cir, sigma_cir
]
run_button.click(
fn=run_monte_carlo,
inputs=all_inputs,
outputs=[
rate_results_table, price_results_table,
plot_rate_paths, plot_rate_distribution,
plot_price_paths, plot_price_distribution,
report_file
]
)
if __name__ == "__main__":
app.launch()
|