股指分散交易 (Dispersion Trading) 算法解析与中国市场 Python 实现
一、中国市场适配的分散交易算法核心逻辑
1. 核心适配点(中国市场)
二、Python 代码实现(沪深 300 为例)
前置说明
数据来源:tushare(免费股票 / 指数数据)、Wind API(期权数据,需授权,无授权可改用历史波动率替代演示)
核心工具:Black-Scholes模型(计算 IV 和希腊字母)、牛顿迭代法(反推 IV)
简化假设:忽略交易滑点、手续费,用历史波动率替代部分缺失的隐含波动率
第一步:安装依赖库
pip install pandas numpy scipy tushare baostock
import pandas as pd
import numpy as np
from scipy.stats import norm
from scipy.optimize import newton
import tushare as ts
import baostock as bs
import matplotlib.pyplot as plt
# -------------------------- 1. 工具函数定义(Black-Scholes + 希腊字母 + IV计算)--------------------------
class OptionPricing:
"""Black-Scholes期权定价模型,计算价格、希腊字母、隐含波动率"""
def __init__(self, S, K, T, r, q, sigma):
self.S = S # 标的价格
self.K = K # 行权价
self.T = T # 剩余期限(年)
self.r = r # 无风险利率
self.q = q # 股息率
self.sigma = sigma # 波动率
def d1(self):
return (np.log(self.S/self.K) + (self.r - self.q + 0.5*self.sigma**2)*self.T) / (self.sigma*np.sqrt(self.T))
def d2(self):
return self.d1() - self.sigma*np.sqrt(self.T)
# 看涨期权价格
def call_price(self):
return self.S*np.exp(-self.q*self.T)*norm.cdf(self.d1()) - self.K*np.exp(-self.r*self.T)*norm.cdf(self.d2())
# 看跌期权价格
def put_price(self):
return self.K*np.exp(-self.r*self.T)*norm.cdf(-self.d2()) - self.S*np.exp(-self.q*self.T)*norm.cdf(-self.d1())
# 希腊字母计算
def delta(self, option_type='call'):
if option_type == 'call':
return np.exp(-self.q*self.T)*norm.cdf(self.d1())
else: # put
return np.exp(-self.q*self.T)*(norm.cdf(self.d1()) - 1)
def vega(self):
# Vega:波动率每变动1%,期权价格变动(单位:元)
return self.S*np.exp(-self.q*self.T)*np.sqrt(self.T)*norm.pdf(self.d1()) * 0.01
# 反推隐含波动率(牛顿迭代法)
def implied_volatility(option_price, S, K, T, r, q, option_type='call'):
def price_diff(sigma):
model = OptionPricing(S, K, T, r, q, sigma)
if option_type == 'call':
return model.call_price() - option_price
else:
return model.put_price() - option_price
# 初始猜测值(10%-80%)
iv = newton(price_diff, x0=0.3, tol=1e-6, maxiter=100)
return iv
# -------------------------- 2. 数据获取(中国市场:沪深300 + 成分股 + 期权数据)--------------------------
def get_csi300_data(start_date='2023-01-01', end_date='2023-12-31'):
"""获取沪深300指数、成分股价格及权重"""
# 初始化baostock(免费获取指数成分股)
bs.login()
# 1. 获取沪深300指数价格(000300.SH)
index_df = bs.query_history_k_data_plus(
"sh.000300", "date,close",
start_date=start_date, end_date=end_date,
frequency="d", adjustflag="3"
).get_data()
index_df['close'] = index_df['close'].astype(float)
index_df['date'] = pd.to_datetime(index_df['date'])
index_df.set_index('date', inplace=True)
# 2. 获取沪深300成分股列表及权重(tushare需token)
ts.set_token("你的tushare token") # 替换为自己的tushare token
pro = ts.pro_api()
weight_df = pro.index_weight(index_code='000300.SH', trade_date='20231201') # 最新权重
weight_df['weight'] = weight_df['weight'].astype(float)
stock_codes = weight_df['con_code'].tolist()[:50] # 取前50只权重股
stock_weights = weight_df['weight'].tolist()[:50]
stock_weights = np.array(stock_weights) / sum(stock_weights) # 归一化权重
# 3. 获取成分股价格
stock_prices = {}
for code in stock_codes:
try:
df = bs.query_history_k_data_plus(
f"sh.{code}" if code.startswith('6') else f"sz.{code}",
"date,close", start_date=start_date, end_date=end_date,
frequency="d", adjustflag="3"
).get_data()
df['close'] = df['close'].astype(float)
df['date'] = pd.to_datetime(df['date'])
df.set_index('date', inplace=True)
stock_prices[code] = df['close']
except:
continue
bs.logout()
stock_df = pd.DataFrame(stock_prices).dropna()
return index_df, stock_df, stock_weights
def get_option_data(index_price, stock_prices, stock_weights, r=0.025, q=0.02):
"""模拟/获取期权数据(中国市场:沪深300指数期权IO + 成分股期权)"""
# 假设当前日期为2023-12-01,期权到期日为2024-01-15(剩余45天)
today = pd.Timestamp('2023-12-01')
expiry_date = pd.Timestamp('2024-01-15')
T = (expiry_date - today).days / 365 # 剩余期限(年)
# 1. 指数期权(IO合约:平值ATM,合约单位10000)
K_index = index_price.loc[today] # 行权价=当前指数价格(ATM)
# 模拟指数期权价格(或用Wind API获取真实IO合约价格:wind.wset("optioncontractdetail","windcode=IO2401-C-3800.SH"))
sigma_index_impl = 0.22 # 假设指数隐含波动率22%
index_option = OptionPricing(S=K_index, K=K_index, T=T, r=r, q=q, sigma=sigma_index_impl)
call_price_index = index_option.call_price()
put_price_index = index_option.put_price()
index_iv = sigma_index_impl # 隐含波动率
index_vega = index_option.vega() * 10000 # 合约单位10000,转换为每手Vega
# 2. 成分股期权(ATM,合约单位100)
stock_ivs = []
stock_vegas = []
for i, (code, price) in enumerate(stock_prices.items()):
S_stock = price.loc[today]
K_stock = S_stock # ATM行权价
# 模拟个股隐含波动率(比指数高3-8%,符合中国市场特性)
sigma_stock_impl = sigma_index_impl + np.random.uniform(0.03, 0.08)
stock_option = OptionPricing(S=S_stock, K=K_stock, T=T, r=r, q=q, sigma=sigma_stock_impl)
stock_ivs.append(sigma_stock_impl)
# 个股期权合约单位100,转换为每手Vega
stock_vegas.append(stock_option.vega() * 100)
return {
'T': T,
'K_index': K_index,
'index_iv': index_iv,
'index_vega': index_vega,
'stock_ivs': np.array(stock_ivs),
'stock_vegas': np.array(stock_vegas),
'stock_weights': stock_weights
}
# -------------------------- 3. 核心算法:隐含相关系数计算 + 头寸构建 --------------------------
def calculate_implied_correlation(option_data):
"""计算隐含相关系数ρ_impl"""
index_iv = option_data['index_iv']
stock_ivs = option_data['stock_ivs']
stock_weights = option_data['stock_weights']
# 公式:σ²(指数) = Σ(w_i²·σ_i²) + 2·Σ(w_i·w_j·ρ·σ_i·σ_j)
# 简化为:ρ_impl = [σ_index² - Σ(w_i²·σ_i²)] / [Σ(w_i·σ_i)² - Σ(w_i²·σ_i²)]
sigma_index_sq = index_iv ** 2
sum_w2_s2 = np.sum(stock_weights ** 2 * stock_ivs ** 2)
sum_ws = np.sum(stock_weights * stock_ivs)
sum_ws_sq = sum_ws ** 2
if sum_ws_sq - sum_w2_s2 == 0:
return 0.5 # 避免除零,默认中性相关系数
rho_impl = (sigma_index_sq - sum_w2_s2) / (sum_ws_sq - sum_w2_s2)
return np.clip(rho_impl, 0, 1) # 相关系数范围[0,1]
def build_position(option_data, rho_impl, rho_hist_mean=0.5, rho_hist_std=0.15):
"""构建Vega中性头寸(中国市场:IO期权 + 个股期权 + IF期货)"""
index_vega = option_data['index_vega']
stock_vegas = option_data['stock_vegas']
stock_weights = option_data['stock_weights']
T = option_data['T']
r = 0.025
q = 0.02
# 1. 判断交易方向
if rho_impl > rho_hist_mean + rho_hist_std:
strategy = 'short_dispersion' # 做空分散:卖指数期权,买成分股期权
index_action = 'sell'
stock_action = 'buy'
elif rho_impl < rho_hist_mean - rho_hist_std:
strategy = 'long_dispersion' # 做多分散:买指数期权,卖成分股期权
index_action = 'buy'
stock_action = 'sell'
else:
return None, "无交易信号(隐含相关系数在合理区间)"
# 2. 计算头寸数量(Vega中性)
index_contracts = 1 # 指数期权初始1手(IO合约)
total_index_vega = index_contracts * index_vega * (1 if index_action == 'buy' else -1)
# 个股期权头寸:按权重分配Vega,抵消指数Vega
stock_contracts = (-total_index_vega * stock_weights) / stock_vegas
stock_contracts = np.round(stock_contracts).astype(int) # 合约数量取整
# 3. Delta对冲(IF期货:合约乘数300)
# 计算指数期权Delta(ATM跨式:Call Delta + Put Delta ≈ 0.5 + (-0.5) = 0,简化为0)
index_delta = 0 # 跨式组合Delta近似为0
# 计算个股期权Delta(ATM Call Delta ≈ 0.5,Put Delta ≈ -0.5,跨式组合Delta≈0)
stock_delta_per_contract = 0 # 简化处理
total_delta = index_delta * index_contracts + np.sum(stock_contracts * stock_delta_per_contract)
# IF期货Delta=1(1手IF对应300点指数),计算对冲合约数
if_futures_multiplier = 300
if_contracts = -total_delta / if_futures_multiplier
if_contracts = round(if_contracts)
return {
'strategy': strategy,
'index_contracts': index_contracts,
'index_action': index_action,
'stock_contracts': stock_contracts,
'if_contracts': if_contracts,
'rho_impl': rho_impl
}, None
# -------------------------- 4. 回测与结果分析 --------------------------
def backtest_dispersion_trading():
"""回测中国市场沪深300分散交易策略"""
# 1. 获取基础数据
index_df, stock_df, stock_weights = get_csi300_data()
today = pd.Timestamp('2023-12-01')
index_price = index_df.loc[today:today]
stock_prices = stock_df.loc[today:today]
# 2. 获取期权数据
option_data = get_option_data(index_price, stock_prices, stock_weights)
# 3. 计算隐含相关系数
rho_impl = calculate_implied_correlation(option_data)
print(f"当前隐含相关系数:{rho_impl:.4f}")
# 4. 构建头寸
position, error = build_position(option_data, rho_impl)
if error:
print(error)
return
# 5. 模拟收益(假设1个月后平仓,ρ_impl回归均值)
rho_final = 0.5 # 回归历史均值
index_iv_final = np.sqrt(
np.sum(stock_weights**2 * option_data['stock_ivs']**2) +
(rho_final) * (np.sum(stock_weights * option_data['stock_ivs'])**2 - np.sum(stock_weights**2 * option_data['stock_ivs']**2))
)
# 指数期权收益(卖开仓:IV下降盈利)
index_vega = option_data['index_vega']
index_profit = index_contracts * index_vega * (option_data['index_iv'] - index_iv_final)
# 个股期权收益(买开仓:IV不变,或因分散度上升盈利)
stock_profit = np.sum(position['stock_contracts'] * option_data['stock_vegas'] * (option_data['stock_ivs'] - option_data['stock_ivs'])) # 简化:IV不变
total_profit = index_profit + stock_profit
# 6. 输出结果
print("\n=== 中国市场沪深300分散交易头寸 ===")
print(f"策略方向:{position['strategy']}")
print(f"沪深300指数期权(IO):{position['index_action']} {position['index_contracts']}手")
print(f"成分股期权:{position['stock_action']} 合计{np.sum(np.abs(position['stock_contracts']))}手(前5只:{position['stock_contracts'][:5]})")
print(f"沪深300股指期货(IF):对冲 {position['if_contracts']}手")
print(f"\n模拟1个月后收益:{total_profit:.2f}元")
print(f"策略夏普比率(年化):{total_profit / (np.std([index_profit, stock_profit]) * np.sqrt(12)):.2f}")
# -------------------------- 5. 执行与可视化 --------------------------
if __name__ == "__main__":
# 执行回测
backtest_dispersion_trading()
# 可视化隐含相关系数与历史区间
rho_impl = 0.72 # 示例计算结果
rho_hist_mean = 0.5
rho_hist_std = 0.15
plt.figure(figsize=(10, 6))
plt.axhline(y=rho_hist_mean, color='green', label='历史均值')
plt.axhline(y=rho_hist_mean + rho_hist_std, color='orange', linestyle='--', label='+1σ')
plt.axhline(y=rho_hist_mean - rho_hist_std, color='orange', linestyle='--', label='-1σ')
plt.scatter(1, rho_impl, color='red', s=100, label=f'当前隐含相关系数:{rho_impl:.2f}')
plt.ylim(0, 1)
plt.xlim(0.5, 1.5)
plt.xlabel('时间')
plt.ylabel('隐含相关系数')
plt.title('中国沪深300分散交易信号判断')
plt.legend()
plt.show()
764

被折叠的 条评论
为什么被折叠?



