用 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 庫。本筆記只轉述技術內容,推廣訊息從略。

學習重點

詳細筆記

波動率曲面是甚麼

曲面由歐式交易所上市期權的報價構成:ATM 行使價以上取 OTM call,以下取 OTM put,再以到期時間作第三維。講者強調一個關鍵觀察:即使距 ATM 的絕對幅度相同,下行方向的期權隱含波動率仍偏高——下行保護被定價得更貴,交易者要求更高的隱含波動率才能在 Black-Scholes 模型裡重現該合約的均衡價。這正是 1987 年股災後模型競賽的起點:1994 年局部波動率模型、其離散時間版本、Heston 隨機波動率模型(見第 7 講)等,皆以擬合此偏斜為目標。右邊的 2D 圖只是曲面在近月到期日的一個切面,行使價之間以簡單線性插值連接。

TWS/IB Gateway 事前設定

要自行運行此項目,需有 IBKR 帳戶,並在 Trader Workstation(或 IB Gateway)完成設定:

架構設計:動手寫代碼之前

講者先列出最終成品所需的組件:

  1. 實時數據源:IBKR。
  2. 3D 曲面的網格:行使價與到期日不能硬編碼——今日的 0DTE 明日已到期無可報價;標的價格亦隨時間改變,硬編碼的行使價範圍很快失去意義。程式必須在每次運行時,就當前價位與當前到期日動態產生相關範圍。
  3. 視覺化:3D 曲面+2D 偏斜切面+鎖定按鈕。

引入的套件:threading(API 調用)、time(實時儀表板顯示時間)、pandas(存放與查找報價)、numpy(網格與插值運算)、matplotlib.pyplotmpl_toolkits.mplot3d.Axes3Dmatplotlib.widgets.Button(鎖定更新),以及 IBAPI 的 EClientEWrapperContract

IBAPI 核心模式:EClient 請求、EWrapper 接收

IBAPI 的運作模式是:

程式建立 LiveSurfaceApp(EWrapper, EClient) 類,同時充當請求者與數據倉庫:構造函數內準備好現貨價欄位(初始 0)、IV 字典(以 request ID 為鍵)、ID 映射(request ID → (到期日, 行使價)),以及 threading.Event 事件(resolvedchain_resolved)用於阻塞程式直至伺服器回應。

幾個關鍵回調:

講者順帶提出一個很好的練習:Black-Scholes 模型有五個輸入(波動率、行使價、標的價、到期時間、無風險利率代理),可以自行由期權價格反解隱含波動率,與 IBKR 供應的 IV 對照——兩者應一致。

啟動流程與動態網格

start_app(symbol="SPY") wrapper 的流程:

  1. 建立 LiveSurfaceApp,連接本機 port 7497(模擬倉),client ID 自訂(講者用 35)。
  2. threading.Threadapp.run() 主迴圈處理訊息,等待初始化。
  3. 建立標的 Contract(symbol=SPY、secType=STK、exchange=SMART、currency=USD)。
  4. reqContractDetails(reqId=1)取合約 ID,resolved.wait(timeout=5) 阻塞等待。
  5. reqMktData(reqId=999,generic tick list 留空,非 snapshot——要的是實時流)取現貨價;while app.spot_price == 0: sleep(0.1) 等待首個報價。
  6. reqSecDefOptParams(reqId=2,標明是股票期權、附上標的合約 ID),chain_resolved.wait(timeout=5)

取得全部到期日與行使價後,進行動態篩選

這個設計的好處是完全動態:下星期重跑程式,網格會自動以當時價位重新定基。

為每張期權合約建立數據流

IBKR 沒有「給我整張波動率曲面」的請求——必須逐張合約訂閱。以 reqId 由 1000 起(之前所有請求 ID 均小於 1000),以巢狀迴圈遍歷目標到期日 × 目標行使價($O(n^2)$,但網格有界,規模可控):

當報價變動,伺服器觸發 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):

  1. 拍快照:req_ids = list(app.iv_dict.keys())(避免迭代時字典變動),逐 ID 取出 IV 並經 id_map 找到對應 (到期日, 行使價),組成 current_data(dict 列表:expiry/strike/IV)。
  2. 數據量門檻len(current_data) > 10 才進入視覺化,否則顯示數據不足。
  3. state.is_locked 為真則跳過更新。
  4. 以 pandas 建 DataFrame,pivot_table(index=expiry, columns=strike, values=IV),兩軸排序——這張表本身就是表格形式的波動率曲面。
  5. 插值interpolate(method='linear', axis=0)bfillffill。講者提醒這很快會變成數據插補(data imputation)問題,牽涉曲面的無套利性質(日曆套利 vs 蝶式套利),超出本片範圍;之所以需要插值,是因為行使價離散(本例間距 1),不插值就只有一堆空間中的點。
  6. np.meshgrid(pivot 欄 × index 範圍)建網格,Z 為 pivot 值;繪圖前記下當前相機仰角與方位角ax3d.elevazim),重繪後還原——否則每次更新視角都跳回預設位置。
  7. 偏斜圖:取最近到期日(index 第 0 個)的一列 IV,標題「Front-Month Skew」,以紅色虛線(axvline)標出當前現貨價,圓點標記藍色折線。
  8. 每輪 plt.pause(0.5) 避免洗版。講者補充:若真需要「實時」,就不該靠視覺化——校準應直接在 pivot table 上以計算完成,擬合誤差(多少個 vol points)在研究階段評估。
  9. except KeyboardInterrupt:斷開連接、關閉圖窗。

if __name__ == "__main__"start_app("SPY") → 打印啟動訊息 → sleep(10) 讓數據流填充 → 啟動 live_desktop_plot

實測與除錯

「沒有除錯就不算 Quant Build」。講者實測修正三個問題:

  1. AttributeError: did you mean resolved?——threading 事件名打錯,改回構造函數定義的 app.resolved
  2. 介面報錯——全局 button_label 須指向按鈕真正的 .labelbtn.label),toggle 才能更新文字。
  3. 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 以上),按下按鈕即凍結畫面。

延伸方向(講者建議)

🙏 覺得內容有用?本站所有內容免費提供,使用推薦碼開戶就是支持我們繼續營運的最大鼓勵:

ZA Bank 邀請碼 P20923 —— 你享 HKD 2,000 迎新獎賞,本站獲得營運支持。 立即開戶 → (聯盟連結|投資涉及風險,受條款及細則約束,此資料僅可於香港境內分發)

欣利克的看法

本講的價值不在金融數學的深度,而在工程思維的示範:動手前先架構(數據源、動態網格、視覺化三個問題逐一拆解),過程中每個「懶人做法」(硬編碼行使價)都被指出其明日即失效的原因。這種「今天寫的程式下星期還能用」的動態設計意識,正是業餘腳本與生產工具的分水嶺。

技術上有兩點特別值得香港讀者留意。其一,IBKR 的 tickOptionComputation 直接供應模型 IV,對散戶而言省卻了自行反解的麻煩,但講者建議的「自行反解對照」練習其實是驗證數據質素的好方法——免費數據源(如延遲報價)的 IV 欄位經常有坑。其二,「若真需要實時就不該視覺化」這句一語中的:圖表是給人看的,策略引擎應直接在 pivot table 層面運算;不少人把 matplotlib 當交易系統的核心,是本末倒置。

亦須提醒:本項目展示的是數據工程與視覺化,本身不產生任何交易優勢;波動率曲面是校準與定價的輸入,而非策略本身。把行使價範圍收窄至現貨 ±2% 亦意味著曲面的尾部(正是偏斜最誇張、資訊最豐富的位置)被裁掉,實戰應用需自行權衡流動性與覆蓋範圍。

名詞解釋

常見問題 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:線性插值會扭曲曲面嗎

會,尤其在數據稀疏的區域。講者明言線性插值只是最簡方案,配合前後向填充處理缺失值;若把行使價範圍放寬至流動性差的區域,填充方式會令圖像明顯失真。更嚴謹的做法涉及無套利約束下的曲面插補(日曆套利、蝶式套利條件),屬進階課題。

原影片

本文整理自以下影片,版權屬原創作者所有,建議配合原片觀看:

來源:https://www.youtube.com/watch?v=5JEeAsQqlro

🏦 ZA Bank 開戶優惠

本站所有內容免費提供。多使用網站的推薦碼,就是對我們繼續製作優質內容的最大鼓勵和支持 🙏

去 ZA Bank 開戶,享價值 HKD 2,000 迎新獎賞,仲有機會享 USD 30 指定美股兌換券!

  • 💛 全線上開戶,無需前往分行
  • 💛 無最低存款要求,零賬戶管理費
  • 💛 24x7 銀行服務

專屬邀請碼:P20923

立即開戶 → (聯盟連結)