返回目錄
A
金融資料科學:從數據到決策的完整流程 - 第 5 章
第 5 章 時間序列分析基礎
發布於 2026-03-07 12:48
# 第 5 章 時間序列分析基礎
時間序列分析是金融資料科學的核心工具之一,能夠揭示資產價格、波動率、收益分布等隱藏在歷史數據中的結構與規律。本章將從理論基礎入手,逐步導入實務應用,並以 Python 範例說明如何完成完整的時間序列建模流程。
---
## 5.1 時間序列的特性與預備知識
| 特性 | 說明 | 影響
|------|------|------|
| **自相關** | 觀測值之間存在依賴關係 | 需選擇適當的差分或模型
| **非平穩** | 均值、變異數隨時間變化 | 影響預測精度,需做平穩化處理
| **季節性** | 周期性波動 | 需要分解或加入季節成分
| **波動率聚集** | 波動率高低呈現聚集現象 | 適合使用 GARCH 等模型
### 5.1.1 基本概念
- **白噪音 (White Noise)**:期望值為 0,且相互之間獨立且同分布。
- **平穩序列 (Stationary)**:統計特性(均值、變異數、協方差)不隨時間變化。
- **自迴歸 (AR)**:過去值對現在值的線性影響。
- **滑動平均 (MA)**:過去誤差對現在值的線性影響。
## 5.2 ARIMA 模型
### 5.2.1 理論框架
ARIMA(p,d,q) 是 **自迴歸 + 差分 + 滑動平均** 的組合。
- **p**:AR 項數量(過去值個數)。
- **d**:差分階數(使序列平穩)。
- **q**:MA 項數量(過去誤差個數)。
> **公式**:
>
> \[\phi(B)(1-B)^d y_t = \theta(B)\epsilon_t\]
>
> 其中 \(\phi(B)\) 與 \(\theta(B)\) 分別為 AR 與 MA 的多項式。
### 5.2.2 模型選擇步驟
| 步驟 | 目的 | 工具 | 範例代碼 |
|------|------|------|-----------|
| 1 | 繪製時序圖 | `matplotlib` | `plt.plot(df['Close'])` |
| 2 | 檢測非平穩性 | ADF 檢驗 (`statsmodels.tsa.stattools.adfuller`) | `adfuller(df['Close'])` |
| 3 | 差分 | `df.diff().dropna()` | `df['Close_diff'] = df['Close'].diff()` |
| 4 | 自相關圖 (ACF) 與偏自相關圖 (PACF) | `plot_acf`, `plot_pacf` | `plot_acf(df['Close_diff'])` |
| 5 | 模型擬合 | `statsmodels.tsa.arima.model.ARIMA` | `model = ARIMA(df['Close'], order=(p,d,q)).fit()` |
| 6 | 檢查殘差 | 白噪音檢驗 (`adfuller`, Ljung-Box) | `adfuller(model.resid)` |
| 7 | 預測 | `model.forecast(steps=n)` | `model.forecast(steps=30)` |
#### 5.2.3 Python 範例
python
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.stattools import adfuller
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
from statsmodels.tsa.arima.model import ARIMA
# 讀取台股日收盤價
url = "https://raw.githubusercontent.com/selva86/datasets/master/GOOG.csv"
df = pd.read_csv(url, parse_dates=['Date'], index_col='Date')
close = df['Close']
# 1. 觀察時序圖
close.plot(title='台股日收盤價')
plt.show()
# 2. ADF 檢驗
adf = adfuller(close)
print('ADF p-value:', adf[1])
# 3. 若非平穩,進行一次差分
if adf[1] > 0.05:
close_diff = close.diff().dropna()
# 4. ACF/PACF
fig, ax = plt.subplots(1,2, figsize=(12,4))
plot_acf(close_diff, ax=ax[0])
plot_pacf(close_diff, ax=ax[1])
plt.show()
# 5. 透過 ACF/PACF 找 p, q
p = 1 # 以 PACF 觀察
q = 1 # 以 ACF 觀察
d = 1
# 6. 擬合 ARIMA
model = ARIMA(close, order=(p,d,q)).fit()
print(model.summary())
# 7. 預測未來 30 天
forecast = model.forecast(steps=30)
forecast.plot(title='30 天預測')
plt.show()
### 5.3 GARCH 模型
#### 5.3.1 為何需要 GARCH?
金融時間序列的波動率往往呈現**聚集性**(高波動期與低波動期交替出現)。ARIMA 只捕捉均值動態,無法描述波動率的時間變化。GARCH(Generalized Autoregressive Conditional Heteroskedasticity)模型將波動率視為條件異方差,能有效建模此類現象。
#### 5.3.2 GARCH(1,1) 公式
\[\sigma_t^2 = \omega + \alpha \epsilon_{t-1}^2 + \beta \sigma_{t-1}^2\]
- \(\epsilon_t = r_t - \mu\) 為收益殘差。
- \(\sigma_t^2\) 為條件異方差。
#### 5.3.3 模型擬合流程
| 步驟 | 目的 | 工具 | 範例代碼 |
|------|------|------|-----------|
| 1 | 計算對數收益 | `np.log` | `log_ret = np.log(df['Close']).diff().dropna()` |
| 2 | 擬合 GARCH | `arch.arch_model` | `am = arch_model(log_ret, p=1, q=1).fit()` |
| 3 | 檢查殘差 | `arch.__init__` | `am.conditional_volatility.plot()` |
| 4 | 產生波動率預測 | `am.forecast(horizon=30)` | `forecast = am.forecast(horizon=30)` |
#### 5.3.4 Python 範例
python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from arch import arch_model
# 讀取資料
url = "https://raw.githubusercontent.com/selva86/datasets/master/GOOG.csv"
df = pd.read_csv(url, parse_dates=['Date'], index_col='Date')
close = df['Close']
# 1. 計算對數收益
log_ret = np.log(close).diff().dropna()
# 2. 擬合 GARCH(1,1)
am = arch_model(log_ret, p=1, q=1, vol='Garch', dist='Normal')
res = am.fit(disp='off')
print(res.summary())
# 3. 畫條件異方差
res.conditional_volatility.plot(figsize=(10,4), title='GARCH(1,1) Conditional Volatility')
plt.show()
# 4. 未來 30 天波動率預測
fore = res.forecast(horizon=30)
print('Predicted volatilities:', fore.variance.values[-1])
### 5.4 季節性分解 (STL)
#### 5.4.1 為何分解?
將序列拆解為趨勢、季節與隨機成分,能更清晰理解市場行為,並在建模前做適當處理。
#### 5.4.2 STL(Seasonal and Trend decomposition using Loess)
Python `statsmodels.tsa.seasonal.STL` 可直接執行。
#### 5.4.3 範例
python
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.seasonal import STL
# 讀取資料
url = "https://raw.githubusercontent.com/selva86/datasets/master/GOOG.csv"
df = pd.read_csv(url, parse_dates=['Date'], index_col='Date')
close = df['Close']
# STL 分解
stl = STL(close, period=252) # 一年交易日
result = stl.fit()
# 觀察結果
fig = result.plot()
fig.set_size_inches(10,6)
plt.show()
### 5.5 因子模型與主成分分析
#### 5.5.1 因子模型
- **CAPM**:單因子模型,預測預期報酬。
- **Fama‑French 三因子**:加入規模、價值因子。
- **多因子**:可擴充到任意數量。
#### 5.5.2 主成分分析 (PCA)
用於降維與去相關性,可作為因子提取的基礎。
python
import pandas as pd
import numpy as np
from sklearn.decomposition import PCA
# 讀取多支股票日收盤價
urls = {
'AAPL': 'https://raw.githubusercontent.com/selva86/datasets/master/AAPL.csv',
'MSFT': 'https://raw.githubusercontent.com/selva86/datasets/master/MSFT.csv',
'GOOG': 'https://raw.githubusercontent.com/selva86/datasets/master/GOOG.csv'
}
frames = []
for name, url in urls.items():
df = pd.read_csv(url, parse_dates=['Date'], index_col='Date')[['Close']].rename(columns={'Close': name})
frames.append(df)
prices = pd.concat(frames, axis=1).dropna()
returns = np.log(prices).diff().dropna()
# 5.5.2 PCA
pca = PCA(n_components=2)
pcs = pca.fit_transform(returns)
print('Explained variance:', pca.explained_variance_ratio_)
## 5.6 實務操作要點
| 觀念 | 實務提示 |
|------|-----------|
| **資料對齊** | 交易日缺失時,使用前一日或補值法避免跳點。 |
| **季節性** | 觀察多種頻率(日、周、月)確定季節週期,避免過度擬合。 |
| **模型診斷** | 殘差自相關檢驗(Ljung‑Box)、正態性檢驗、波動率聚集檢驗。 |
| **超參數搜尋** | 對 ARIMA 使用 AIC/BIC,對 GARCH 使用 QML,並搭配貝葉斯優化。 |
| **可視化** | 使用 `plotly` 交互式圖表提升洞察力。 |
| **部署** | 將模型包裝成 REST API,並加入版本控制與自動化測試。 |
## 5.7 小結
- **ARIMA**:適合描述均值動態,需先平穩化。
- **GARCH**:專注於波動率建模,對風險管理極為關鍵。
- **STL**:有效拆解季節與趨勢,利於後續模型擬合。
- **因子模型與 PCA**:提供多維度的風險因子與降維工具。
掌握上述工具,讀者即可構建出能夠捕捉市場價格、波動率、季節性及因子結構的完整時間序列模型,為後續的機器學習、風險管理與策略回測奠定堅實基礎。
---
> **練習題**:
> 1. 以台股日收盤價為例,完成 ARIMA 模型的自動化選擇流程,並比較 AIC、BIC 的結果。
> 2. 對同一個資料集建立 GARCH(1,1) 與 GJR‑GARCH 模型,比較波動率預測的 RMSE。
> 3. 將三支大型科技股的日收益進行 PCA,並觀察第一主成分對整體市場波動的解釋力。