3. 波动率微笑与偏斜策略
任务目标
本案例将基于期权市场报价,识别隐含波动率(IV)随行权价变化呈现的“微笑”或“偏斜”形态,并据此构建垂直价差策略以捕捉定价偏差。你需要完成:从期权链数据中提取不同行权价的IV、判断形态类型、选择价差策略并计算最大收益/风险。
环境与依赖
使用Python 3.9+,依赖库:numpy、pandas、scipy(用于BSM反推IV)。若已有实时行情接口可替换下方静态示例数据,但核心逻辑不变。
核心实现
import numpy as np
import pandas as pd
from scipy.stats import norm
# ---------- 1. BSM隐含波动率计算 ----------
def bsm_price(S, K, T, r, sigma, option_type='call'):
d1 = (np.log(S/K) + (r + 0.5*sigma**2)*T) / (sigma*np.sqrt(T))
d2 = d1 - sigma*np.sqrt(T)
if option_type == 'call':
return S*norm.cdf(d1) - K*np.exp(-r*T)*norm.cdf(d2)
else:
return K*np.exp(-r*T)*norm.cdf(-d2) - S*norm.cdf(-d1)
def implied_vol(market_price, S, K, T, r, option_type='call'):
"""二分法反推IV"""
lo, hi = 0.01, 3.0
for _ in range(100):
mid = (lo+hi)/2
est = bsm_price(S, K, T, r, mid, option_type)
if est > market_price:
hi = mid
else:
lo = mid
return (lo+hi)/2
# ---------- 2. 识别波动率形态 ----------
def detect_smile_skew(iv_chain):
"""
输入: DataFrame含columns=['strike','iv']
输出: 'smile' / 'skew' / 'flat'
判定逻辑: 比较ATM两侧IV均值与ATM IV的偏离
"""
atm_idx = (iv_chain['strike'] - iv_chain['strike'].iloc[len(iv_chain)//2]).abs().idxmin()
atm_iv = iv_chain.loc[atm_idx, 'iv']
left_iv = iv_chain.loc[iv_chain['strike'] < iv_chain.loc[atm_idx,'strike'], 'iv'].mean()
right_iv = iv_chain.loc[iv_chain['strike'] > iv_chain.loc[atm_idx,'strike'], 'iv'].mean()
if left_iv > atm_iv*1.05 and right_iv > atm_iv*1.05:
return 'smile'
elif left_iv > atm_iv*1.05 and right_iv < atm_iv*0.95:
return 'skew'
else:
return 'flat'
# ---------- 3. 构建价差策略 ----------
def build_spread_strategy(spot, iv_chain, T, r, form_type):
"""
根据形态选择策略:
- smile: 买入低行权价call + 卖出高行权价call (牛市价差)
- skew: 买入高行权价put + 卖出低行权价put (熊市价差)
返回策略参数与希腊字母
"""
if form_type == 'smile':
# 选择偏离ATM最远的低行权价call和次远的高行权价call
low_strike = iv_chain.iloc[0]['strike']
high_strike = iv_chain.iloc[-1]['strike']
opt_type = 'call'
elif form_type == 'skew':
low_strike = iv_chain.iloc[0]['strike']
high_strike = iv_chain.iloc[-1]['strike']
opt_type = 'put'
else:
return None
# 获取对应IV
low_iv = iv_chain.loc[iv_chain['strike']==low_strike, 'iv'].values[0]
high_iv = iv_chain.loc[iv_chain['strike']==high_strike, 'iv'].values[0]
# 计算期权价格
if opt_type == 'call':
buy_price = bsm_price(spot, low_strike, T, r, low_iv, 'call')
sell_price = bsm_price(spot, high_strike, T, r, high_iv, 'call')
max_profit = (high_strike - low_strike) - (buy_price - sell_price)
max_loss = buy_price - sell_price
else:
buy_price = bsm_price(spot, high_strike, T, r, high_iv, 'put')
sell_price = bsm_price(spot, low_strike, T, r, low_iv, 'put')
max_profit = (high_strike - low_strike) - (buy_price - sell_price)
max_loss = buy_price - sell_price
return {
'strategy': f'{opt_type.upper()} {low_strike}/{high_strike} 价差',
'buy_strike': low_strike if opt_type=='call' else high_strike,
'sell_strike': high_strike if opt_type=='call' else low_strike,
'net_debit': buy_price - sell_price,
'max_profit': max_profit,
'max_loss': max_loss,
'break_even': (low_strike if opt_type=='call' else high_strike) + (buy_price - sell_price)
}
# ---------- 4. 主流程 ----------
def main():
# 示例数据: 50ETF期权(行权价3.0-3.4, 剩余30天)
spot = 3.2
T = 30/365
r = 0.02
option_chain = pd.DataFrame({
'strike': [3.0, 3.1, 3.2, 3.3, 3.4],
'call_price': [0.25, 0.18, 0.12, 0.07, 0.04],
'put_price': [0.05, 0.08, 0.12, 0.17, 0.24]
})
# 计算各行权价IV
iv_list = []
for _, row in option_chain.iterrows():
call_iv = implied_vol(row['call_price'], spot, row['strike'], T, r, 'call')
put_iv = implied_vol(row['put_price'], spot, row['strike'], T, r, 'put')
iv_list.append((call_iv + put_iv) / 2) # 取平均减少误差
iv_chain = pd.DataFrame({'strike': option_chain['strike'], 'iv': iv_list})
# 识别形态
form = detect_smile_skew(iv_chain)
print(f"检测到波动率形态: {form}")
# 构建策略
if form != 'flat':
strategy = build_spread_strategy(spot, iv_chain, T, r, form)
print(f"策略: {strategy['strategy']}")
print(f"净支出: {strategy['net_debit']:.4f}")
print(f"最大收益: {strategy['max_profit']:.4f}")
print(f"最大亏损: {strategy['max_loss']:.4f}")
print(f"盈亏平衡点: {strategy['break_even']:.4f}")
else:
print("市场无明显定价偏差,不建议交易")
if __name__ == "__main__":
main()
运行与验证
运行方式:将上述代码保存为vol_smile_strategy.py,在终端执行python vol_smile_strategy.py。
验证方法:
- 观察输出形态是否为
smile(示例数据中两端IV高于中间,符合微笑特征); - 确认策略为
CALL 3.0/3.4 价差,净支出约0.08~0.10; - 手动计算验证:买入3.0 call(0.25)卖出3.4 call(0.04),净支出0.21,最大收益=(3.4-3.0)-0.21=0.19,与代码输出一致。
通过条件:程序无报错运行,输出形态识别正确,策略参数满足max_profit > 0且net_debit为正(即构建的是借方价差)。
常见问题
- IV计算不收敛:检查市场价格是否在BSM理论价格范围内(实值期权价格过低会导致无解),可增加迭代次数或放宽二分法边界。
- 形态误判:阈值5%可根据市场波动调整,成熟市场通常3%~8%为正常范围。
- 实际交易注意:本策略未考虑交易成本与滑点,实盘需将成本计入
net_debit后再评估盈亏比。