聊天視窗

量化投資的藝術:策略設計、實作與風險控管 - 第 2 章

第二章:市場數據與資料倉儲

發布於 2026-03-06 06:44

# 第二章:市場數據與資料倉儲 在量化投資中,**資料是最核心的資產**。沒有可靠的數據來源、乾淨的數據結構,任何模型再先進也無法發揮實際效益。本章將帶領讀者從資料來源、品質、處理,到如何建構高效、可維護的資料倉儲與 API 獲取機制,為後續的因子挖掘與機器學習打下堅實基礎。 ## 2.1 市場資料來源 | 類型 | 典型來源 | 主要特點 | 適用場景 | |------|----------|----------|----------| | 股價/交易資料 | Yahoo Finance、Alpha Vantage、Tiingo、Polygon | 無需付費、頻率高(日、5min、1min) | 基礎行情、回測、低頻策略 | | 基本面資料 | Quandl、SEC EDGAR、Compustat | 週期性、結構化 | 基本面因子、價值投資 | | 替代資料 | FRED、Quandl 期貨、社交媒體 API、衛星影像 | 非傳統、稀疏、噪聲大 | 替代因子、情緒分析 | | 期貨/衍生品 | CME、ICE、IBKR | 高杠杆、波動性大 | 套利、風險管理 | | 匯率/商品 | OANDA、FXCM | 24/7、流動性高 | 全球配置、對沖 | > **實務提醒**:不同資料來源的時間戳格式、交易時段、缺失值處理方式都有差異,需在資料聚合前先統一時間基準與頻率。 ## 2.2 資料品質與預處理 ### 2.2.1 資料完整性 - **缺失值**:使用線性插值、時間窗口平均或向前/向後填充。對於開盤價與收盤價,常見做法是「向前填充」以保留交易訊號。 - **異常值**:利用 z‑score、IQR 或者布丁距離檢測離群點,決定是否修正或刪除。 ### 2.2.2 交易日曆與時間戳 | 步驟 | 目的 | |------|------| | 轉換時區 | 以 UTC 或本地時間為基準,避免跨時區套利錯誤 | | 交易時段統一 | 例如 09:30‑16:00 美股,確保所有時間序列對齊 | | 交易日曆生成 | 以 `pandas.tseries.offsets` 產生交易日索引,填補非交易日空白 | ### 2.2.3 資料格式化 - **欄位命名**:統一大小寫、下劃線命名,例如 `adj_close`, `volume`。 - **資料型別**:確保數值型別為 `float64`,避免因 `int64` 轉換造成精度損失。 - **時間序列索引**:設定為 `DatetimeIndex`,並設 `freq='B'`(交易日)或 `freq='1min'`。 ## 2.3 時間序列處理 ### 2.3.1 重新取樣 (Resampling) ```python import pandas as pd # 假設 df 為日頻資料 # 轉成 5 分鐘頻率 df_5min = df.resample('5T').agg({ 'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last', 'volume': 'sum', }) ``` ### 2.3.2 滑動窗口統計 ```python # 20 日移動平均 df['ma20'] = df['adj_close'].rolling(window=20).mean() # 20 日波動率 df['vol20'] = df['adj_close'].pct_change().rolling(window=20).std() ``` ### 2.3.3 滑動窗口交叉驗證 ```python from sklearn.model_selection import TimeSeriesSplit tsc = TimeSeriesSplit(n_splits=5) for train_index, test_index in tsc.split(df): X_train, X_test = df.iloc[train_index], df.iloc[test_index] # 進行模型訓練與評估 ``` ## 2.4 資料倉儲設計 ### 2.4.1 架構圖(Mermaid) ```mermaid graph TD A[資料來源] --> B[ETL 作業] B --> C[資料清洗] C --> D[資料倉儲] D --> E[資料服務層] E --> F[分析 & 回測] ``` ### 2.4.2 資料模型 | 表格 | 主鍵 | 主要欄位 | |------|------|----------| | `prices` | `ticker`, `datetime` | `open`, `high`, `low`, `close`, `adj_close`, `volume` | | `factors` | `ticker`, `datetime` | `momentum`, `volatility`, `sentiment_score` | | `meta` | `ticker` | `sector`, `market_cap` | > **最佳實踐**:採用 Columnar 格式(Parquet、Feather)可顯著降低 IO 時間,尤其在大規模歷史資料回測時。 ### 2.4.3 版本控制 - **DVC (Data Version Control)**:將資料檔案納入 Git 管理,保持版本可追蹤。 - **Delta Lake**:支援 ACID 交易、時間旅行,適合線上實盤資料。 ## 2.5 API 獲取機制 ### 2.5.1 使用 `yfinance` 取得歷史行情 ```python import yfinance as yf def fetch_historical(ticker: str, start: str, end: str, interval: str = '1d'): df = yf.download(ticker, start=start, end=end, interval=interval) df.reset_index(inplace=True) df['ticker'] = ticker return df # 範例:下載 AAPL 2018-2023 aapl = fetch_historical('AAPL', '2018-01-01', '2023-12-31') ``` ### 2.5.2 速率限制與重試機制 ```python import time import requests class RateLimitedClient: def __init__(self, limit: int, per: int): self.limit = limit self.per = per self.tokens = limit self.last = time.time() def acquire(self): now = time.time() elapsed = now - self.last self.tokens += elapsed * self.limit / self.per if self.tokens > self.limit: self.tokens = self.limit if self.tokens < 1: time.sleep((1 - self.tokens) * self.per / self.limit) self.tokens -= 1 self.last = time.time() client = RateLimitedClient(limit=5, per=60) # 每分鐘 5 次 client.acquire() resp = requests.get('https://api.example.com/data') ``` ### 2.5.3 資料快取 - **Redis**:用於快取近期行情,減少 API 呼叫。\n- **SQLite**:對於單機環境,適合小型資料集快取。\n ## 2.6 實務範例:建立「量化資料倉儲」 以下為完整流程範例,示範如何從資料下載、清洗、儲存,到查詢。 ```python import pandas as pd import yfinance as yf import pyarrow.parquet as pq import os # 1. 下載資料 symbols = ['AAPL', 'MSFT', 'GOOG'] all_data = [] for s in symbols: df = yf.download(s, start='2015-01-01', end='2023-12-31', interval='1d') df.reset_index(inplace=True) df['ticker'] = s all_data.append(df) # 2. 合併並清洗 prices = pd.concat(all_data, ignore_index=True) prices.dropna(subset=['Adj Close'], inplace=True) prices['datetime'] = pd.to_datetime(prices['Date']) prices.drop(['Date', 'Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume'], axis=1, inplace=True) # 3. 儲存為 Parquet output_dir = 'data/warehouse/prices' os.makedirs(output_dir, exist_ok=True) prices.to_parquet(os.path.join(output_dir, 'prices.parquet'), index=False, compression='snappy') # 4. 查詢範例 df = pd.read_parquet(os.path.join(output_dir, 'prices.parquet')) # 取得 AAPL 2020 年 6 月的資料 mask = (df['ticker'] == 'AAPL') & (df['datetime'] >= '2020-06-01') & (df['datetime'] < '2020-07-01') print(df.loc[mask]) ``` > **實務提醒**:在實盤環境中,需考慮資料同步頻率與交易時段。建議使用事件驅動型資料管道(如 Kafka)搭配即時快取。 ## 2.7 小結 本章闡述了量化投資資料的全流程:從多元資料來源、嚴謹的品質管控,到高效的時間序列處理與資料倉儲設計,並提供了實際可執行的 API 獲取與快取機制。透過這些基礎,讀者可自行構建可維護、可擴充的資料基礎設施,為後續因子挖掘與機器學習打下堅實基礎。