聊天視窗

金融資料科學:從數據到決策的完整流程 - 第 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,並觀察第一主成分對整體市場波動的解釋力。