用 Python 與 IBKR API 建立實時隱含波動率曲面|Quant Math 第 9 講筆記
第 9 課・共 14 課
目錄
這是 Roman Paolucci 量化金融數學系列按發佈時序的第 9 講,屬於「Quant Build」實作篇:由零開始逐行編寫一個實時隱含波動率曲面應用程式,數據源為 Interactive Brokers(IBKR)API,標的預設為 SPY ETF。成品是一個深色主題的桌面介面:左邊是隨報價實時更新的 3D 波動率曲面(行使價 × 到期日 × 隱含波動率),右邊是近月(示例中為 0DTE)期權的偏斜 Skew 切面圖,並附「Lock Updates」按鈕可凍結畫面細看。
講者開宗明義指出:這些曲面可直接用來校準隨機模型(呼應第 7 講 Heston 校準與第 8 講由 Q 測度反推的框架),為期權交易決策提供依據。影片亦在描述欄提供完整源碼,並上載至講者的 GitHub 庫。本筆記只轉述技術內容,推廣訊息從略。
學習重點
- 波動率曲面的本質:以行使價與到期日索引的隱含波動率集合;同一到期日內,OTM put 的隱含波動率高於對稱位置的 OTM call——這就是所有隨機波動率模型試圖擬合的偏斜。
- 架構先行:動手前想清楚需要甚麼——實時數據源(IBKR)、動態網格(行使價與到期日不能硬編碼)、視覺化(matplotlib 3D + 2D 切面)。
- IBAPI 的請求/回調模式:
EClient負責發出請求,伺服器回應送至EWrapper的回調方法;配合threading事件與 request ID 映射,把非同步數據流組織成有序程式。 - 動態網格:由
reqSecDefOptParams取得全部行使價與到期日,再按現貨價篩選(例如現貨 ±2%、最近五個到期日),程式翌日重跑自動重定基準。 - 數據處理管線:IV 字典 → pandas
pivot_table(到期日 × 行使價)→ 線性插值+前後向填充 → meshgrid 繪製 3D 曲面與近月偏斜圖。
詳細筆記
波動率曲面是甚麼
曲面由歐式交易所上市期權的報價構成:ATM 行使價以上取 OTM call,以下取 OTM put,再以到期時間作第三維。講者強調一個關鍵觀察:即使距 ATM 的絕對幅度相同,下行方向的期權隱含波動率仍偏高——下行保護被定價得更貴,交易者要求更高的隱含波動率才能在 Black-Scholes 模型裡重現該合約的均衡價。這正是 1987 年股災後模型競賽的起點:1994 年局部波動率模型、其離散時間版本、Heston 隨機波動率模型(見第 7 講)等,皆以擬合此偏斜為目標。右邊的 2D 圖只是曲面在近月到期日的一個切面,行使價之間以簡單線性插值連接。
TWS/IB Gateway 事前設定
要自行運行此項目,需有 IBKR 帳戶,並在 Trader Workstation(或 IB Gateway)完成設定:
- Configure → Settings → API:勾選 Enable ActiveX and Socket Clients;如需落盤可關閉 Read-Only API;確認 socket port(講者用模擬倉 port 7497)。
- 在設定搜尋 Volatility and Analytics:確認 IBKR 供應的波動率單位為年化而非日度——若對年化單位再乘一次年化因子,會得出誇張的錯誤數值。
架構設計:動手寫代碼之前
講者先列出最終成品所需的組件:
- 實時數據源:IBKR。
- 3D 曲面的網格:行使價與到期日不能硬編碼——今日的 0DTE 明日已到期無可報價;標的價格亦隨時間改變,硬編碼的行使價範圍很快失去意義。程式必須在每次運行時,就當前價位與當前到期日動態產生相關範圍。
- 視覺化:3D 曲面+2D 偏斜切面+鎖定按鈕。
引入的套件:threading(API 調用)、time(實時儀表板顯示時間)、pandas(存放與查找報價)、numpy(網格與插值運算)、matplotlib.pyplot、mpl_toolkits.mplot3d.Axes3D、matplotlib.widgets.Button(鎖定更新),以及 IBAPI 的 EClient、EWrapper、Contract。
IBAPI 核心模式:EClient 請求、EWrapper 接收
IBAPI 的運作模式是:
EClient:向伺服器發出請求——連接、合約細節、期權參數、市場數據流。EWrapper:伺服器把回應送回這裡的回調方法(callback)。
程式建立 LiveSurfaceApp(EWrapper, EClient) 類,同時充當請求者與數據倉庫:構造函數內準備好現貨價欄位(初始 0)、IV 字典(以 request ID 為鍵)、ID 映射(request ID → (到期日, 行使價)),以及 threading.Event 事件(resolved、chain_resolved)用於阻塞程式直至伺服器回應。
幾個關鍵回調:
contractDetails:取得標的合約 ID(「SPY」三字不足以指明是股票本身還是其衍生工具)。tickPrice:以 request ID 999 接收標的實時價,存入spot_price——這是構建行使價網格的基準。securityDefinitionOptionParameter:接收交易所(篩 SMART)、交易類別、乘數、全部到期日與行使價,排序後存入 app,然後 set 事件解除阻塞。tickOptionComputation:接收每張期權合約的模型隱含波動率、delta、期權價、gamma、vega、theta 等;當 tick type = 13(模型計算的 IV)且數值非 None,就以 request ID 為鍵存入 IV 字典。
講者順帶提出一個很好的練習:Black-Scholes 模型有五個輸入(波動率、行使價、標的價、到期時間、無風險利率代理),可以自行由期權價格反解隱含波動率,與 IBKR 供應的 IV 對照——兩者應一致。
啟動流程與動態網格
start_app(symbol="SPY") wrapper 的流程:
- 建立
LiveSurfaceApp,連接本機 port 7497(模擬倉),client ID 自訂(講者用 35)。 - 開
threading.Thread跑app.run()主迴圈處理訊息,等待初始化。 - 建立標的
Contract(symbol=SPY、secType=STK、exchange=SMART、currency=USD)。 reqContractDetails(reqId=1)取合約 ID,resolved.wait(timeout=5)阻塞等待。reqMktData(reqId=999,generic tick list 留空,非 snapshot——要的是實時流)取現貨價;while app.spot_price == 0: sleep(0.1)等待首個報價。reqSecDefOptParams(reqId=2,標明是股票期權、附上標的合約 ID),chain_resolved.wait(timeout=5)。
取得全部到期日與行使價後,進行動態篩選:
- 到期日:以
time.strftime取今日(年月日格式),只保留 ≥ 今日的到期日(排除已過期),取排序後首五個(講者口誤說 six——切片語法不含尾端)。 - 行使價:只保留
0.98 × spot ≤ K ≤ 1.02 × spot的範圍。原因:曲面要圍繞 ATM;深度價外的行使價有流動性問題,報價質素成疑,處理缺失數據本身已是另一個項目。講者提醒:若把範圍放寬到 0.5–1.5 倍現貨,插值方式(前後向填充)會令圖像變得很怪。
這個設計的好處是完全動態:下星期重跑程式,網格會自動以當時價位重新定基。
為每張期權合約建立數據流
IBKR 沒有「給我整張波動率曲面」的請求——必須逐張合約訂閱。以 reqId 由 1000 起(之前所有請求 ID 均小於 1000),以巢狀迴圈遍歷目標到期日 × 目標行使價($O(n^2)$,但網格有界,規模可控):
- 每對 (到期日, 行使價) 建立一個期權
Contract(secType=OPT、SMART、USD、lastTradeDateOrContractMonth=到期日、strike=行使價)。 - 權利方向:行使價 ≥ 現貨取 call,否則取 put——曲面只用 OTM 合約。
id_map[reqId] = (expiration, strike),然後reqMktData,generic tick 設 106(隱含波動率)。- reqId 遞增並短暫 sleep,建立全部實時流。
當報價變動,伺服器觸發 tickOptionComputation,以相同 request ID 回傳——這正是 IV 字典與 ID 映射能輕易串連的原因。
視覺化:PlotState、3D 曲面與偏斜圖
鎖定狀態:PlotState 類只有一個欄位 is_locked(預設 False),toggle 方法在按鈕點擊時取反,並把按鈕文字切換為「Lock Updates」/「Unlock Updates」,再重繪圖表。鎖定後可細看曲面而不被更新打擾。
圖表佈局(live_desktop_plot(app)):plt.ion() 互動模式;16×9 圖窗,標題「The Quant Guild’s Live Volatility Surface」,深底色(#0B0D0F);用 subplot2grid 以 1×3 佈局放置——3D 圖佔 (0,0)、colspan=2、projection=‘3d’,偏斜圖放 (0,2)。按鈕以 plt.axes 定位,配色跟隨深色主題(#1F2329/hover #2D333B),白字。
主迴圈(while True):
- 拍快照:
req_ids = list(app.iv_dict.keys())(避免迭代時字典變動),逐 ID 取出 IV 並經id_map找到對應 (到期日, 行使價),組成current_data(dict 列表:expiry/strike/IV)。 - 數據量門檻:
len(current_data) > 10才進入視覺化,否則顯示數據不足。 - 若
state.is_locked為真則跳過更新。 - 以 pandas 建 DataFrame,
pivot_table(index=expiry, columns=strike, values=IV),兩軸排序——這張表本身就是表格形式的波動率曲面。 - 插值:
interpolate(method='linear', axis=0)再bfill+ffill。講者提醒這很快會變成數據插補(data imputation)問題,牽涉曲面的無套利性質(日曆套利 vs 蝶式套利),超出本片範圍;之所以需要插值,是因為行使價離散(本例間距 1),不插值就只有一堆空間中的點。 np.meshgrid(pivot 欄 × index 範圍)建網格,Z 為 pivot 值;繪圖前記下當前相機仰角與方位角(ax3d.elev/azim),重繪後還原——否則每次更新視角都跳回預設位置。- 偏斜圖:取最近到期日(index 第 0 個)的一列 IV,標題「Front-Month Skew」,以紅色虛線(
axvline)標出當前現貨價,圓點標記藍色折線。 - 每輪
plt.pause(0.5)避免洗版。講者補充:若真需要「實時」,就不該靠視覺化——校準應直接在 pivot table 上以計算完成,擬合誤差(多少個 vol points)在研究階段評估。 except KeyboardInterrupt:斷開連接、關閉圖窗。
if __name__ == "__main__":start_app("SPY") → 打印啟動訊息 → sleep(10) 讓數據流填充 → 啟動 live_desktop_plot。
實測與除錯
「沒有除錯就不算 Quant Build」。講者實測修正三個問題:
AttributeError: did you mean resolved?——threading 事件名打錯,改回構造函數定義的app.resolved。- 介面報錯——全局
button_label須指向按鈕真正的.label(btn.label),toggle才能更新文字。 - pivot table 報錯——DataFrame 忘了填入
current_data,對空表找 expiry/strike/IV 欄。
修正後成功運行:終端顯示 TWS Acknowledged Connection、App Started,3D 曲面與偏斜圖實時更新(示例:SPY 行使價約 675–700、到期日 2026-01-22 至 01-29,偏斜曲線在現貨價附近的紅色虛線兩側明顯不對稱,OTM put 一側 IV 高見 0.6 以上),按下按鈕即凍結畫面。
延伸方向(講者建議)
- 自行以 Black-Scholes 反解 IV,與 IBKR 供應值對照。
- 改善插值方法(線性以外),處理深度價外的缺失數據。
- 把隨機模型(如 Heston)校準到曲面,再外推奇異期權價格——把本項目擴展為實時交易引擎。
🙏 覺得內容有用?本站所有內容免費提供,使用推薦碼開戶就是支持我們繼續營運的最大鼓勵:
ZA Bank 邀請碼 P20923
—— 你享 HKD 2,000 迎新獎賞,本站獲得營運支持。
立即開戶 →
(聯盟連結|投資涉及風險,受條款及細則約束,此資料僅可於香港境內分發)
欣利克的看法
本講的價值不在金融數學的深度,而在工程思維的示範:動手前先架構(數據源、動態網格、視覺化三個問題逐一拆解),過程中每個「懶人做法」(硬編碼行使價)都被指出其明日即失效的原因。這種「今天寫的程式下星期還能用」的動態設計意識,正是業餘腳本與生產工具的分水嶺。
技術上有兩點特別值得香港讀者留意。其一,IBKR 的 tickOptionComputation 直接供應模型 IV,對散戶而言省卻了自行反解的麻煩,但講者建議的「自行反解對照」練習其實是驗證數據質素的好方法——免費數據源(如延遲報價)的 IV 欄位經常有坑。其二,「若真需要實時就不該視覺化」這句一語中的:圖表是給人看的,策略引擎應直接在 pivot table 層面運算;不少人把 matplotlib 當交易系統的核心,是本末倒置。
亦須提醒:本項目展示的是數據工程與視覺化,本身不產生任何交易優勢;波動率曲面是校準與定價的輸入,而非策略本身。把行使價範圍收窄至現貨 ±2% 亦意味著曲面的尾部(正是偏斜最誇張、資訊最豐富的位置)被裁掉,實戰應用需自行權衡流動性與覆蓋範圍。
名詞解釋
- 隱含波動率:由期權市場價反解 Black-Scholes 模型所得的波動率;按行使價與到期日排列即成波動率曲面。
- Black-Scholes 模型:五輸入(波動率、行使價、標的價、到期時間、無風險利率)的歐式期權定價模型,IV 由其反解而來。
- Heston 模型:隨機波動率模型,可校準至波動率曲面以擬合偏斜(見第 7 講)。
- 行使價:期權合約約定的執行價格;曲面的一個軸。
- 期權/認購期權/認沽期權:曲面由 ATM 以上的 OTM call 與 ATM 以下的 OTM put 構成。
- Python:本項目使用的編程語言,配合 IBKR 官方
ibapi套件。 - pandas:以
pivot_table把散點報價組織成到期日 × 行使價的 IV 表格。 - numpy:以
meshgrid建立 3D 繪圖所需的網格。 - 無套利:講者提及的曲面插補深層問題——插值後的曲面須保持無日曆套利與無蝶式套利。
- 流動性:深度價外行使價被排除的原因——報價稀疏且質素不穩。
常見問題 FAQ
Q:為何不直接用 IBKR 的 Option Trader 介面看波動率,要自己建曲面?
IBKR 介面提供的是逐鏈報價,而曲面是按行使價與到期日組織的整體結構——校準隨機模型、觀察偏斜形態、外推非掛牌合約價格,都需要這種結構化、可程式讀取的形式。自建曲面的真正目的是成為後續校準與定價管線的數據輸入。
Q:tick type 13 與 generic tick 106 是甚麼?
106 是 reqMktData 的 generic tick 標籤,要求伺服器附送期權計算數據;13 則是 tickOptionComputation 回調中標識「模型計算的隱含波動率」的 tick type。兩者配合,即可接收 IBKR 供應的 Black-Scholes 模型 IV。
Q:為甚麼曲面只用 OTM 合約?
OTM 合約的權利金主要反映波動率而非內在價值,報價對 IV 更敏感、流動性亦通常較 ITM 合約好,是市場構建曲面的標準做法。程式以現貨價為界:以上取 call、以下取 put。
Q:沒有 IBKR 帳戶可以做這個項目嗎?
本項目依賴 IBKR API 的實時期權報價,必須有帳戶(模擬倉亦可)並開啟 TWS/IB Gateway 的 socket 連接。講者提及自己使用 IBKR 及其 API 逾十年,影片描述欄有其推薦連結(推廣內容,本筆記僅註明)。讀者亦可把架構套用至其他數據源,但需自行處理 IV 計算與期權鏈獲取。
Q:線性插值會扭曲曲面嗎?
會,尤其在數據稀疏的區域。講者明言線性插值只是最簡方案,配合前後向填充處理缺失值;若把行使價範圍放寬至流動性差的區域,填充方式會令圖像明顯失真。更嚴謹的做法涉及無套利約束下的曲面插補(日曆套利、蝶式套利條件),屬進階課題。
原影片
本文整理自以下影片,版權屬原創作者所有,建議配合原片觀看: