久仰黑先生 (Black-Scholes) 的神奇公式,從來沒有搞懂過,但在金融數學裡到處出現,這次下決心來好好研究一下。以我的駑鈍資質,要了解的多深入是不可能的,只能依公式照樣計算而已。感謝數學前輩的貢獻,原來是有兩位:Black (1938~1995) and Scholes (1941~) 偉大的經濟學家,我們致敬之後,就直接使用他們的發明吧。
選擇權理論價格
這就是大名鼎鼎的原始公式:


但這裡面有一個麻煩的東西,就是 q,股利因子。一個指數,包涵眾多股票,每個時期各自除權息金額大小不一,根本無法固定,若只用一個簡單的數字去假設,顯然是會產生誤差,偏離事實,因此就有了修正版的公式:

這裡面 q 不見了,而 S 變成 F (期貨) 了。聰明的期貨市場參與者,會自動把配息因素考慮進去,計算起來方便多了。正想著如何寫程式,chatGPT 已經服務到家了:
from scipy.stats import norm
def black_76_option_price(F, K, T, r, sigma, option_type='call'):
"""
計算期貨標的選擇權的理論價(Black-76)
F: 期貨價格(標的)
K: 履約價
T: 剩餘時間(以年為單位)
r: 無風險利率(年化)
sigma: 年化波動率
option_type: 'call' 或 'put'
"""
d1 = (np.log(F / K) + 0.5 * sigma**2 * T) / (sigma * np.sqrt(T))
d2 = d1 - sigma * np.sqrt(T)
discount_factor = np.exp(-r * T)
if option_type == 'call':
price = discount_factor * (F * norm.cdf(d1) - K * norm.cdf(d2))
elif option_type == 'put':
price = discount_factor * (K * norm.cdf(-d2) - F * norm.cdf(-d1))
else:
raise ValueError("option_type 必須是 'call' 或 'put'")
return price
關鍵技術的門檻,已經被 ai 弭平了,剩下的只是修修補補的雜事。依序取得公式所需要的參數:
F:近期期貨最新報價,透過券商 api 取得。
K:履約價,透過券商 api 取得選擇權所有的 ticker,找到最近到期的月選,從該契約再篩選期貨最新報價正負 1000 點之內的 ticker list 取出備用。取得報價和下單都需要指定正確的 ticker。
T:剩餘時間 (以年為單位)。計算「當時」與「到期日之結算時間 13:30」 的精確秒數,除以 86400,再除以 365 得之。
r:無風險利率,設定為 0.015
sigma:使用近月期貨的「歷史波動率」,從 期交所 網站可爬得 30 天收盤資料,計算標準差再年化而得。記住了,只要是關於「波動」的,在選擇權領域都是重要的。
以上資料已經足夠,計算所有的買賣權理論價格了。
市價倒推「隱含波動率」
理論價格既已得出,實際價格應該與之接近,但是不可能一模一樣,有時候市價偏高,有時候偏低,這就產生了解讀空間,有可能挖掘出市場隱藏的秘密嗎?選擇權的意義在於為未來的風險,也就是波動,埋單;所付出的代價,用來規避,或對沖可能的損失,也就等同於「保險費」的概念。當「所願意付出」的保險費更高,代表交易者對未來的不確定性更爲擔憂,心中所認定的風險,比已發生的歷史事實更高,反之亦然。這個「心中所認知的風險」 就是「隱含波動率」。
在此開始體驗到此公式厲害的地方,可以正的用,也可以倒過來用。當我們把波動率 sigma 留空,公式結果帶入「市價」,用「最佳化」的方法中最簡單的「暴力窮舉法」,就可以反推出公式中的 sigma 的值,這正是電腦所擅長的工作。所以沒有新公式,只有新的程式,chatGPT 秒給出答案,注意有用到 scipy.optimize 套件:
from scipy.stats import norm
from scipy.optimize import brentq
def implied_volatility(option_quote, option_type, F, K, T, r):
try:
# 定義方程式:理論價 - 市場價 = 0
f = lambda sigma: black_76_option_price(F, K, T, r, sigma, option_type) - option_quote
# 在 0.01 到 3 之間搜尋解(即 1% ~ 300% 年化波動率)
result = brentq(f, 1e-4, 3.0)
if np.isnan(result):
return 0
else:
return result
except:
return 0
以上,觀察隱含波動率 (Implied Volatility,後續簡稱 IV),對比歷史波動率 (Historical Volatility,後續簡稱 HV),可知目前的「市場氛圍」是否朝向更大或更小的不確定性發展。因為有選擇權商品,讓這種虛無飄渺的東西可以量化,變大可以說「市場覺得恐慌」,變小可以說「情緒歸於穩定」。而這種恐慌或穩定,理論上是沒有方向性的,向上或向下震盪,都是震盪,與一般純做多投資人所思考的「上漲或下跌」很不一樣,選擇權所打開的,好像是另一個世界,數字遊戲進到新的境界。
計算市場整體 IV,引出 Vega and Delta...
以上所計算的 IV,區分買權與賣權,且每一個履約價都有,那如何用「單一數字」去呈現出全市場狀態?顯然「價平」選擇權的 IV 是最具參考性的。舉例假設說,正 1000 點的選擇權,雖然有人交易,但那些「極端樂觀」與極端悲觀的人,相對較少,要反映出「整體市場」狀態,顯然有權重大小之分。以「對大盤的影響力」作為權重是合理的,而「每跳一點」影響可分兩階段,一階段是「對選擇權價格」的影響,就是奧秘的 Vega,而每一點選擇權價格,「對大盤或標的的影響」就是 Delta 的倒數。這再度表現出 Black-Scholes 模型的威力,他可以正的用,倒著用,現在就來「側著用」,或切入一塊結構,取出關鍵意義指標,這模型真是全身都是寶!
雖然我認為要以大盤的影響為權重,就是 Vega / Delta,但也許整體影響不大,chatGPT 教的是用 Vega-weighted IV 來代表整體 IV 就可以了。若要得到更精確的值,還有 CBOE 的 VIX 算法,但實在太複雜了,就不再折磨自己了。Vega 意思就是,選擇權價格,對波動率,的偏微分。推導過程當然是交給偉大的數學家了,我們恭敬的套用他們所產生的美麗公式:

至於 Delta,意義很容易理解,就是標的物變動一點時,對應的選擇權會變動幾點,或說「選擇權相對於標的物價格的敏感度」,通常價平的 Delta 大約是 0.5 左右。算法都是出自於 Black-Schole 原始公式的變形,非常強大:

從 Delta 意義來解讀,可說是「選擇權對標的物價格」的敏感度,可說是微分,或說「選擇權隨標的價變化」的「速度」。竟有好事者再把它作「二次微分」,就是「加速度」的概念,又造出一個指標 Gamma!說是「Delta 對標的物價格」的敏感度,真是快瘋了!覺得實在沒什麼用,就不再紀錄混淆視聽了。「對利率」的敏感度稱為 Rho,同樣沒什麼用,予以忽略。Vega and Delta 程式備妥如下:
def black76_vega(F, K, T, r, sigma):
d1 = (np.log(F / K) + 0.5 * sigma ** 2 * T) / (sigma * np.sqrt(T))
vega = F * np.exp(-r * T) * norm.pdf(d1) * np.sqrt(T)
return vega/100 # 每變動 0.01 (即 1%) 的 σ 對應多少價格點變化
def black76_delta(F, K, T, r, sigma, option_type):
d1 = (np.log(F / K) + 0.5 * sigma**2 * T) / (sigma * np.sqrt(T))
if option_type.lower() == 'call':
delta = np.exp(-r * T) * norm.cdf(d1)
elif option_type.lower() == 'put':
delta = -np.exp(-r * T) * norm.cdf(-d1)
else:
raise ValueError("option_type must be 'call' or 'put'")
return delta
還有一個 Theta,代表時間價值,每天會衰減多少。這對於賣方有一定重要性,所以也算一下。

def black76_theta(option_type, F, K, T, r, sigma):
"""
option_type: 'call' or 'put'
F: 期貨價格
K: 履約價
T: 到期時間(以年為單位,例如 10 天 = 10/365)
r: 無風險利率
sigma: 年化波動率
回傳:每日 Theta
"""
d1 = (np.log(F / K) + 0.5 * sigma**2 * T) / (sigma * np.sqrt(T))
d2 = d1 - sigma * np.sqrt(T)
Nd1_prime = norm.pdf(d1)
first_term = - (F * np.exp(-r * T) * Nd1_prime * sigma) / (2 * np.sqrt(T))
if option_type.lower() == 'call':
second_term = r * K * np.exp(-r * T) * norm.cdf(d2)
elif option_type.lower() == 'put':
second_term = - r * K * np.exp(-r * T) * norm.cdf(-d2)
else:
raise ValueError("option_type must be 'call' or 'put'")
theta_annual = first_term + second_term
theta_daily = theta_annual / 365 # 每日衰減
return theta_daily
以上修修補補,勤能補拙,不知不覺我已經擁有一個大表了,一堆資訊巨細彌遺,應有盡有:

初步觀察以上表格,現在是 5/23 下午盤,五月份期貨剛剛結算不久,進到新的月份,到期日還遠,觀察 Theta,權利金一天會衰減 7 點多,偏大;IV 21.6 % 也稍微大於 HV 18.3 %。自己掌握所有數據的好處,就是下午盤可以繼續跳動,而證交所提供的波動率指數只有上午盤有,且只有一個數字,看不到「內部結構」。
IV 結構,微笑或歪斜...
IV 作為解讀市場氛圍的重要數據,如何榨出更多資訊?既然每一個履約價都有一組 IV,代表價外 (OTM,Out of The Money),價內 (ITM,In The Money),價平 (ATM,At The Money),的市場參與者,對風險的期望有些微不同,一般而言,價外的 IV 較高,所以連起來會呈現一個微笑,在此領域已經成為標準術語了,觀察 IV Smile Chart 兩端翹得多高,若很高適合做賣方;或是歪斜 (Skew),通常空方翹得比較高,若非常斜,可能是相當強的看空訊號。把所有 OTM IV 串起來,計算一次回歸直線,其「斜率」就成為好用的指標 skew index,越斜越偏空。
另一個指標也會微笑,只是它是倒著笑,哈哈,就是 Vega,在 ATM 通常較高,兩側遞減。若 Vega Smile 非常扁平,表示市場預期市況穩定;反之若非常集中突起,表示預期有重大風險。所有的圖,都畫出來如下:

從上圖可見 IV Smile 明顯左傾,表示市場的偏空情緒,Vega Smile 只是正常的鐘型,各項數據需要觀察一陣子才會比較有感。雖然算出一堆數據,下單程序也都已經準備好了,但要組成有用的交易策略,還是非常不容易,賺錢難啊!但至少在開發過程,對模型的各種邏輯結構,了解更多一些,日後思考時可以有更多的素材。
Newman 2025/5/21
導覽頁:精明管家