File size: 25,957 Bytes
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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 債券利率蒙地卡羅模擬教學\n",
    "\n",
    "本 Jupyter Notebook 旨在教學演示如何使用 Python 進行債券利率的蒙地卡羅模擬。\n",
    "程式碼中的變數名稱、公式與註解都對標 'equation.md' 文件,以利於學習與對照。\n",
    "\n",
    "## 內容包含:\n",
    "\n",
    "1.  **四種利率模型**:\n",
    "    *   **Vasicek 模型**:用於模擬無風險利率 (drf),具有均值回歸特性。\n",
    "    *   **Cox-Ingersoll-Ross (CIR) 模型**:Vasicek 的變體,確保利率為正。\n",
    "    *   **幾何布朗運動 (GBM) 模型**:一個常用於股價的簡化利率模型。\n",
    "    *   **有風險利率模型**:在無風險利率上疊加一個隨機的信用利差。\n",
    "2.  **債券價格近似計算**:\n",
    "    *   根據模擬出的利率路徑,使用「修正存續期間」與「價格曲度」來近似計算債券價格的變化。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 載入所需函式庫\n",
    "\n",
    "首先,我們載入 `numpy` 用於數值計算,`matplotlib.pyplot` 用於繪圖,以及 `datetime` 用於處理日期。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from datetime import datetime"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. 利率模擬函數\n",
    "\n",
    "此區塊定義了四種不同的利率模型函數,用於生成未來的利率路徑。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.1 Vasicek 模型\n",
    "\n",
    "**隨機微分方程 (SDE):** `dr_t = k(θ - r_t)dt + σdW_t`\n",
    "\n",
    "此模型描述了一個具有「均值回歸」特性的利率。利率 `r_t` 會傾向於回歸到其長期平均水平 `θ`,回歸的速度由 `k` 控制,而 `σ` 則代表其隨機波動的幅度。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def simulate_vasicek(r_0, k, theta, sigma, T, dt, n_simulations, dW_t):\n",
    "    \"\"\"\n",
    "    使用 Vasicek 模型模擬利率路徑。\n",
    "    \n",
    "    :param r_0: float, 初始利率 (r_t 在 t=0 的值)。\n",
    "    :param k: float, 均值回歸速度 (kappa),代表利率回復到長期均值的速度。\n",
    "    :param theta: float, 長期平均利率或均衡水平 (θ)。\n",
    "    :param sigma: float, 波動率 (σ),代表利率隨機波動的幅度。\n",
    "    :param T: float, 總模擬時長(年)。\n",
    "    :param dt: float, 每個時間步長(年)。\n",
    "    :param n_simulations: int, 模擬路徑的數量。\n",
    "    :param dW_t: np.ndarray, 預先生成的維納過程增量 (dW_t),代表隨機衝擊。\n",
    "    :return: np.ndarray, 模擬的利率路徑。\n",
    "    \"\"\"\n",
    "    num_steps = int(T / dt)\n",
    "    r_paths = np.zeros((n_simulations, num_steps + 1))\n",
    "    r_paths[:, 0] = r_0\n",
    "\n",
    "    for t in range(1, num_steps + 1):\n",
    "        r_paths[:, t] = r_paths[:, t-1] + k * (theta - r_paths[:, t-1]) * dt + sigma * dW_t[:, t-1]\n",
    "        r_paths[:, t] = np.maximum(0.0001, r_paths[:, t]) # 確保利率為正\n",
    "        \n",
    "    return r_paths"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2 Cox-Ingersoll-Ross (CIR) 模型\n",
    "\n",
    "**隨機微分方程 (SDE):** `dr_t = k(θ - r_t)dt + σ√r_t dW_t`\n",
    "\n",
    "CIR 模型是 Vasicek 的變體,其主要優點是能確保利率恆為正值(在 `2kθ > σ²` 的條件下)。波動項 `σ√r_t` 表示利率越高,其波動也越大。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def simulate_cir(r_0, k, theta, sigma, T, dt, n_simulations, dW_t):\n",
    "    \"\"\"\n",
    "    使用 Cox-Ingersoll-Ross (CIR) 模型模擬利率路徑。\n",
    "    \n",
    "    :param r_0: float, 初始利率。\n",
    "    :param k: float, 均值回歸速度。\n",
    "    :param theta: float, 長期平均利率。\n",
    "    :param sigma: float, 波動率。\n",
    "    :param T: float, 總模擬時長(年)。\n",
    "    :param dt: float, 每個時間步長(年)。\n",
    "    :param n_simulations: int, 模擬路徑的數量。\n",
    "    :param dW_t: np.ndarray, 預先生成的維納過程增量。\n",
    "    :return: np.ndarray, 模擬的利率路徑。\n",
    "    \"\"\"\n",
    "    num_steps = int(T / dt)\n",
    "    r_paths = np.zeros((n_simulations, num_steps + 1))\n",
    "    r_paths[:, 0] = r_0\n",
    "\n",
    "    for t in range(1, num_steps + 1):\n",
    "        sqrt_r = np.sqrt(np.maximum(0, r_paths[:, t-1]))\n",
    "        r_paths[:, t] = r_paths[:, t-1] + k * (theta - r_paths[:, t-1]) * dt + sigma * sqrt_r * dW_t[:, t-1]\n",
    "        r_paths[:, t] = np.maximum(0.0001, r_paths[:, t]) # 再次確保利率為正\n",
    "        \n",
    "    return r_paths"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.3 幾何布朗運動 (GBM) 模型\n",
    "\n",
    "**隨機微分方程 (SDE):** `dr_t = μr_t dt + σr_t dW_t`\n",
    "\n",
    "GBM 是金融領域中一個非常經典的模型,常用於模擬股價。它假設資產的報酬率服從常態分佈。雖然在利率模擬中不如均值回歸模型常用,但作為一個基礎模型仍有其教學價值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def simulate_gbm(r_0, mu, sigma, T, dt, n_simulations, dW_t):\n",
    "    \"\"\"\n",
    "    使用幾何布朗運動 (GBM) 模型模擬利率路徑。\n",
    "    \n",
    "    :param r_0: float, 初始利率。\n",
    "    :param mu: float, 長期漂移趨勢。\n",
    "    :param sigma: float, 年化波動率。\n",
    "    :param T: float, 總模擬時長(年)。\n",
    "    :param dt: float, 每個時間步長(年)。\n",
    "    :param n_simulations: int, 模擬路徑的數量。\n",
    "    :param dW_t: np.ndarray, 預先生成的維納過程增量。\n",
    "    :return: np.ndarray, 模擬的利率路徑。\n",
    "    \"\"\"\n",
    "    num_steps = int(T / dt)\n",
    "    r_paths = np.zeros((n_simulations, num_steps + 1))\n",
    "    r_paths[:, 0] = r_0\n",
    "\n",
    "    for t in range(1, num_steps + 1):\n",
    "        drift = (mu - 0.5 * sigma**2) * dt\n",
    "        diffusion = sigma * dW_t[:, t-1]\n",
    "        r_paths[:, t] = r_paths[:, t-1] * np.exp(drift + diffusion)\n",
    "        r_paths[:, t] = np.maximum(0.0001, r_paths[:, t]) # 確保利率為正\n",
    "        \n",
    "    return r_paths"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.4 有風險利率模型\n",
    "\n",
    "**公式:**\n",
    "1.  `drs_t = drf_t + spread_t`\n",
    "2.  `d(spread_t) = σ_s dW_s`\n",
    "3.  `dW_s = ρ * dW_f + √(1 - ρ²) * dZ_t`\n",
    "\n",
    "此模型在無風險利率 `drf_t` (例如由 Vasicek 模型生成) 的基礎上,疊加一個隨機變動的信用利差 `spread_t`,從而得到有風險利率 `drs_t`。\n",
    "\n",
    "信用利差的變動 `dW_s` 與無風險利率的變動 `dW_f` 之間存在相關性 `ρ`,這使得模型能更真實地反映市場情況(例如,經濟惡化時,無風險利率可能下降,而信用利差卻會擴大)。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def simulate_risky_rate(drf_paths, dW_f, initial_spread, sigma_s, rho, T, dt, n_simulations):\n",
    "    \"\"\"\n",
    "    模擬有風險利率 (drs),即在無風險利率(drf)上疊加信用利差(spread)。\n",
    "    \n",
    "    :param drf_paths: np.ndarray, 已模擬好的無風險利率路徑。\n",
    "    :param dW_f: np.ndarray, 生成 drf_paths 所使用的隨機衝擊。\n",
    "    :param initial_spread: float, 初始信用利差。\n",
    "    :param sigma_s: float, 信用利差的波動率。\n",
    "    :param rho: float, drf 與 spread 變動之間的相關係數。\n",
    "    :param T: float, 總模擬時長(年)。\n",
    "    :param dt: float, 每個時間步長(年)。\n",
    "    :param n_simulations: int, 模擬路徑的數量。\n",
    "    :return: tuple (np.ndarray, np.ndarray), 分別為模擬的有風險利率路徑和信用利差路徑。\n",
    "    \"\"\"\n",
    "    num_steps = int(T / dt)\n",
    "    \n",
    "    # 1. 生成一個獨立的隨機衝擊 dZ_t\n",
    "    dZ_t = np.random.normal(0, 1, (n_simulations, num_steps)) * np.sqrt(dt)\n",
    "    \n",
    "    # 2. 根據相關係數 rho,合成信用利差的隨機衝擊 dW_s\n",
    "    dW_s = rho * dW_f + np.sqrt(1 - rho**2) * dZ_t\n",
    "    \n",
    "    # 3. 模擬信用利差的路徑\n",
    "    spread_paths = np.zeros((n_simulations, num_steps + 1))\n",
    "    spread_paths[:, 0] = initial_spread\n",
    "    for t in range(1, num_steps + 1):\n",
    "        spread_paths[:, t] = spread_paths[:, t-1] + sigma_s * dW_s[:, t-1]\n",
    "        spread_paths[:, t] = np.maximum(0.0001, spread_paths[:, t]) # 確保利差為正\n",
    "        \n",
    "    # 4. 有風險利率 = 無風險利率 + 信用利差\n",
    "    drs_paths = drf_paths + spread_paths\n",
    "    \n",
    "    return drs_paths, spread_paths"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. 債券價格計算函數\n",
    "\n",
    "在得到利率路徑後,我們需要一個方法來估算債券價格的變化。這裡我們使用基於「修正存續期間 (Modified Duration)」和「曲度 (Convexity)」的二階泰勒展開式來近似計算。\n",
    "\n",
    "**近似公式:** `ΔP/P ≈ -D_mod * Δy + (C_mod / 2) * (Δy)²`\n",
    "\n",
    "其中:\n",
    "-   `ΔP/P` 是價格的變動百分比。\n",
    "-   `D_mod` 是修正存續期間,衡量價格對利率變動的敏感度(一階)。\n",
    "-   `C_mod` 是曲度,用於修正存續期間的線性估計誤差(二階)。\n",
    "-   `Δy` 是殖利率的變化量,我們在此用模擬的利率 `r` 來替代。\n",
    "\n",
    "**注意:** 在我們的系統中,資料庫提供的 `convT` 值是 `P * C_mod / 2`,因此我們需要先將其轉換回公式中所需的 `C_mod / 2` 項。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def calculate_price_paths(r_paths, D_mod, convT, initial_price=100.0):\n",
    "    \"\"\"\n",
    "    使用修正存續期間(D_mod)和價格曲度(convT)來近似計算債券價格路徑。\n",
    "    \n",
    "    :param r_paths: np.ndarray, 模擬的利率路徑 (y)。\n",
    "    :param D_mod: float, 債券的修正存續期間 (Modified Duration)。\n",
    "    :param convT: float, 來自資料庫的價格曲度值 (P * C_mod / 2)。\n",
    "    :param initial_price: float, 債券的初始價格。\n",
    "    :return: np.ndarray, 模擬的債券價格路徑。\n",
    "    \"\"\"\n",
    "    num_steps = r_paths.shape[1] - 1\n",
    "    price_paths = np.zeros_like(r_paths)\n",
    "    price_paths[:, 0] = initial_price\n",
    "    \n",
    "    # 從 convT 反解出公式中需要的 (C_mod / 2) 項\n",
    "    convexity_factor = convT / initial_price\n",
    "    \n",
    "    for t in range(1, num_steps + 1):\n",
    "        delta_y = r_paths[:, t] - r_paths[:, t-1]\n",
    "        prev_price = price_paths[:, t-1]\n",
    "        \n",
    "        # 計算價格變動百分比 (ΔP/P)\n",
    "        price_change_percentage = -D_mod * delta_y + convexity_factor * (delta_y ** 2)\n",
    "        \n",
    "        # 計算新價格 P(t) = P(t-1) * (1 + ΔP/P)\n",
    "        price_paths[:, t] = prev_price * (1 + price_change_percentage)\n",
    "        \n",
    "    return price_paths"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. 主執行區塊\n",
    "\n",
    "現在我們將前面定義的函數組合起來,進行完整的模擬流程。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.1 設定模擬參數\n",
    "\n",
    "首先,我們定義債券的基本資料和模擬的通用參數。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 債券基本資料 (此處為範例,實際應用中從資料庫讀取)\n",
    "bond_data = {\n",
    "    'name': 'CGB10Y',\n",
    "    'avgYld': 2.2,      # 平均殖利率 (%)\n",
    "    'mdurT': 8.9,       # 修正存續期間 (D_mod)\n",
    "    'convT': 45.5,      # 價格曲度 (P * C_mod / 2)\n",
    "    'maturity': datetime(2034, 5, 15)\n",
    "}\n",
    "\n",
    "# 模擬通用參數\n",
    "n_simulations = 100      # 模擬次數\n",
    "dt = 1/252               # 時間步長 (年),假設一年252個交易日\n",
    "\n",
    "# 計算剩餘到期年限,作為模擬總時長 T\n",
    "time_to_maturity = (bond_data['maturity'] - datetime.now()).days / 365.25\n",
    "T = max(time_to_maturity, 0.1) # 確保至少模擬一小段時間\n",
    "\n",
    "num_steps = int(T / dt)\n",
    "time_points = np.linspace(0, T, num_steps + 1) # 模擬的時間點數列\n",
    "\n",
    "# 初始利率 (將百分比轉為小數)\n",
    "r_0 = bond_data['avgYld'] / 100.0\n",
    "\n",
    "print(f\"債券: {bond_data['name']}\")\n",
    "print(f\"剩餘到期年限 (T): {T:.2f} 年\")\n",
    "print(f\"模擬次數: {n_simulations}\")\n",
    "print(f\"時間步長 (dt): {dt:.4f} 年\")\n",
    "print(f\"總步數: {num_steps}\")\n",
    "print(f\"初始利率 (r_0): {r_0*100:.2f}%\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.2 設定各模型參數\n",
    "\n",
    "為每個利率模型設定具體的參數值。這些值通常需要透過歷史數據進行校準。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 1. Vasicek & CIR 模型參數 (將百分比轉為小數)\n",
    "k_vasicek = 0.3\n",
    "theta_vasicek = 2.2 / 100.0\n",
    "sigma_vasicek = 0.5 / 100.0\n",
    "\n",
    "k_cir = 0.2\n",
    "theta_cir = 2.5 / 100.0\n",
    "sigma_cir = 0.4 / 100.0\n",
    "\n",
    "# 2. GBM 模型參數\n",
    "mu_r = 0.05 / 100.0\n",
    "sigma_r = 0.6 / 100.0\n",
    "\n",
    "# 3. 有風險利率模型參數\n",
    "initial_spread = 0.8 / 100.0\n",
    "sigma_s = 0.3 / 100.0\n",
    "rho = 0.6"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.3 生成隨機衝擊\n",
    "\n",
    "為了讓模擬結果可以重現,我們使用 `np.random.seed` 設定隨機種子。然後,為每個需要獨立隨機性的模型預先生成維納過程的增量 `dW_t`。\n",
    "\n",
    "`dW_t` 是一個服從 `N(0, dt)` 的隨機變數,等價於 `sqrt(dt) * N(0, 1)`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.random.seed(42) # 設定隨機種子以確保結果可重現\n",
    "dW_vasicek = np.random.normal(0, 1, (n_simulations, num_steps)) * np.sqrt(dt)\n",
    "dW_cir = np.random.normal(0, 1, (n_simulations, num_steps)) * np.sqrt(dt)\n",
    "dW_gbm = np.random.normal(0, 1, (n_simulations, num_steps)) * np.sqrt(dt)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.4 執行模擬\n",
    "\n",
    "呼叫前面定義的函數,生成利率路徑和對應的債券價格路徑。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"開始執行利率模擬...\")\n",
    "# 1. 模擬無風險利率 drf (Vasicek)\n",
    "drf_paths = simulate_vasicek(r_0, k_vasicek, theta_vasicek, sigma_vasicek, T, dt, n_simulations, dW_vasicek)\n",
    "\n",
    "# 2. 模擬有風險利率 drs (在 drf 基礎上增加信用利差)\n",
    "drs_paths, spread_paths = simulate_risky_rate(drf_paths, dW_vasicek, initial_spread, sigma_s, rho, T, dt, n_simulations)\n",
    "\n",
    "# 3. 模擬綜合利率 dr (GBM)\n",
    "dr_paths = simulate_gbm(r_0, mu_r, sigma_r, T, dt, n_simulations, dW_gbm)\n",
    "\n",
    "# 4. 模擬 CIR 利率 dr_cir\n",
    "dr_cir_paths = simulate_cir(r_0, k_cir, theta_cir, sigma_cir, T, dt, n_simulations, dW_cir)\n",
    "print(\"利率模擬完成。\")\n",
    "\n",
    "print(\"開始計算債券價格路徑...\")\n",
    "# 5. 計算對應的債券價格路徑\n",
    "price_drf_paths = calculate_price_paths(drf_paths, bond_data['mdurT'], bond_data['convT'])\n",
    "price_drs_paths = calculate_price_paths(drs_paths, bond_data['mdurT'], bond_data['convT'])\n",
    "price_dr_paths = calculate_price_paths(dr_paths, bond_data['mdurT'], bond_data['convT'])\n",
    "price_dr_cir_paths = calculate_price_paths(dr_cir_paths, bond_data['mdurT'], bond_data['convT'])\n",
    "print(\"價格計算完成。\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. 結果可視化\n",
    "\n",
    "將模擬結果繪製成圖表,以便直觀地理解利率和價格的動態變化。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.1 設定 Matplotlib 中文顯示\n",
    "\n",
    "為了讓圖表能正確顯示中文標題和標籤,我們需要設定 Matplotlib 的字體。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei'] # For Windows\n",
    "    plt.rcParams['axes.unicode_minus'] = False\n",
    "except:\n",
    "    print(\"未找到 'Microsoft JhengHei' 字體,圖表中的中文可能無法正常顯示。\")\n",
    "    print(\"您可以嘗試安裝 'Microsoft JhengHei' 或替換為系統中已有的中文字體,例如 'SimHei' (黑體) 或 'KaiTi' (楷體)。\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.2 利率模擬路徑圖\n",
    "\n",
    "下圖展示了四種模型生成的前 50 條利率路徑。可以觀察到不同模型的特徵:\n",
    "- **Vasicek** 和 **CIR** 顯示出向長期均值回歸的趨勢。\n",
    "- **GBM** 則沒有明顯的均值回歸特性,路徑發散較大。\n",
    "- **有風險利率** 的路徑整體高於無風險利率,反映了信用利差的存在。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig_rates, axes_rates = plt.subplots(2, 2, figsize=(14, 10), sharex=True)\n",
    "fig_rates.suptitle(f'四種利率模型的模擬路徑 (前 {min(50, n_simulations)} 條)', fontsize=16)\n",
    "\n",
    "axes_rates[0, 0].plot(time_points, drf_paths[:50, :].T * 100, lw=0.5)\n",
    "axes_rates[0, 0].set_title('1. 無風險利率 (drf) - Vasicek')\n",
    "axes_rates[0, 0].set_ylabel('利率 (%)')\n",
    "axes_rates[0, 0].grid(True, linestyle='--', alpha=0.6)\n",
    "\n",
    "axes_rates[0, 1].plot(time_points, drs_paths[:50, :].T * 100, lw=0.5)\n",
    "axes_rates[0, 1].set_title('2. 有風險利率 (drs) - Vasicek + Spread')\n",
    "axes_rates[0, 1].grid(True, linestyle='--', alpha=0.6)\n",
    "\n",
    "axes_rates[1, 0].plot(time_points, dr_paths[:50, :].T * 100, lw=0.5)\n",
    "axes_rates[1, 0].set_title('3. 綜合利率 (dr) - GBM')\n",
    "axes_rates[1, 0].set_xlabel('時間 (年)')\n",
    "axes_rates[1, 0].set_ylabel('利率 (%)')\n",
    "axes_rates[1, 0].grid(True, linestyle='--', alpha=0.6)\n",
    "\n",
    "axes_rates[1, 1].plot(time_points, dr_cir_paths[:50, :].T * 100, lw=0.5)\n",
    "axes_rates[1, 1].set_title('4. CIR 利率 (dr_cir)')\n",
    "axes_rates[1, 1].set_xlabel('時間 (年)')\n",
    "axes_rates[1, 1].grid(True, linestyle='--', alpha=0.6)\n",
    "\n",
    "fig_rates.tight_layout(rect=[0, 0, 1, 0.96])\n",
    "plt.savefig(\"tutorial_rate_paths.png\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.3 債券價格模擬路徑圖\n",
    "\n",
    "下圖展示了與上述利率路徑相對應的債券價格變化。我們可以看到,當利率上升時,債券價格下降,反之亦然,這符合債券的基本特性。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig_prices, axes_prices = plt.subplots(2, 2, figsize=(14, 10), sharex=True, sharey=True)\n",
    "fig_prices.suptitle(f'對應的債券價格模擬路徑 (前 {min(50, n_simulations)} 條)', fontsize=16)\n",
    "\n",
    "axes_prices[0, 0].plot(time_points, price_drf_paths[:50, :].T, lw=0.5)\n",
    "axes_prices[0, 0].set_title('基於 drf (Vasicek) 的價格')\n",
    "axes_prices[0, 0].set_ylabel('債券價格')\n",
    "axes_prices[0, 0].grid(True, linestyle='--', alpha=0.6)\n",
    "\n",
    "axes_prices[0, 1].plot(time_points, price_drs_paths[:50, :].T, lw=0.5)\n",
    "axes_prices[0, 1].set_title('基於 drs (Risky) 的價格')\n",
    "axes_prices[0, 1].grid(True, linestyle='--', alpha=0.6)\n",
    "\n",
    "axes_prices[1, 0].plot(time_points, price_dr_paths[:50, :].T, lw=0.5)\n",
    "axes_prices[1, 0].set_title('基於 dr (GBM) 的價格')\n",
    "axes_prices[1, 0].set_xlabel('時間 (年)')\n",
    "axes_prices[1, 0].set_ylabel('債券價格')\n",
    "axes_prices[1, 0].grid(True, linestyle='--', alpha=0.6)\n",
    "\n",
    "axes_prices[1, 1].plot(time_points, price_dr_cir_paths[:50, :].T, lw=0.5)\n",
    "axes_prices[1, 1].set_title('基於 dr_cir (CIR) 的價格')\n",
    "axes_prices[1, 1].set_xlabel('時間 (年)')\n",
    "axes_prices[1, 1].grid(True, linestyle='--', alpha=0.6)\n",
    "\n",
    "fig_prices.tight_layout(rect=[0, 0, 1, 0.96])\n",
    "plt.savefig(\"tutorial_price_paths.png\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.4 最終價格分佈圖\n",
    "\n",
    "此直方圖顯示了在模擬期結束時,所有模擬路徑的最終債券價格分佈。這對於評估風險至關重要,例如計算 VaR (Value at Risk)。\n",
    "\n",
    "從圖中可以看出,不同利率模型導致的最終價格分佈形狀和範圍有顯著差異。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure(figsize=(10, 6))\n",
    "plt.hist(price_drf_paths[:, -1], bins=50, alpha=0.7, label='基於 drf (Vasicek)', density=True)\n",
    "plt.hist(price_drs_paths[:, -1], bins=50, alpha=0.7, label='基於 drs (Risky)', density=True)\n",
    "plt.hist(price_dr_paths[:, -1], bins=50, alpha=0.7, label='基於 dr (GBM)', density=True)\n",
    "plt.hist(price_dr_cir_paths[:, -1], bins=50, alpha=0.7, label='基於 dr_cir (CIR)', density=True)\n",
    "plt.title('模擬結束時的債券價格分佈')\n",
    "plt.xlabel('最終價格')\n",
    "plt.ylabel('機率密度')\n",
    "plt.legend()\n",
    "plt.grid(True, linestyle='--', alpha=0.6)\n",
    "plt.savefig(\"tutorial_final_price_dist.png\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. 簡單統計分析\n",
    "\n",
    "最後,我們對兩種較為重要的模型(無風險和有風險)的最終價格進行統計分析,計算其平均值、標準差和 5% 分位數,並估算 95% VaR。\n",
    "\n",
    "**VaR (Value at Risk)**:在險價值,衡量在給定的信賴水準(此處為 95%)和持有期間內,預期的最大潛在損失。此處的 VaR 是從初始價格 100 計算的損失。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "final_prices_drf = price_drf_paths[:, -1]\n",
    "final_prices_drs = price_drs_paths[:, -1]\n",
    "\n",
    "print(\"--- 最終價格統計分析 ---\")\n",
    "print(f\"模型: 基於 drf (Vasicek)\")\n",
    "print(f\"  平均最終價格: {np.mean(final_prices_drf):.4f}\")\n",
    "print(f\"  價格標準差: {np.std(final_prices_drf):.4f}\")\n",
    "print(f\"  5% 分位數價格: {np.percentile(final_prices_drf, 5):.4f}\")\n",
    "print(f\"  95% VaR (從初始價100計算的潛在最大損失): {100 - np.percentile(final_prices_drf, 5):.4f}\")\n",
    "\n",
    "print(f\"模型: 基於 drs (Risky)\")\n",
    "print(f\"  平均最終價格: {np.mean(final_prices_drs):.4f}\")\n",
    "print(f\"  價格標準差: {np.std(final_prices_drs):.4f}\")\n",
    "print(f\"  5% 分位數價格: {np.percentile(final_prices_drs, 5):.4f}\")\n",
    "print(f\"  95% VaR (從初始價100計算的潛在最大損失): {100 - np.percentile(final_prices_drs, 5):.4f}\")\n",
    "print(\"------------------------\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}