第一部分,封装了期权BS公式的接口(大部分网上抄来的,自己给集成并验证了下),提供了简单的测试代码。
第二部分,计算隐含波动率,并画出微笑图。
其中需要自定义的参数有
1:期权最后交易日,2:无风险利率(一般默认)
3:历史波动率
波动率计算如下:
1、计算对数收益率:R_i = log(p_i / p_i-1)
2、计算对数收益率序列的标准差std
3、计算年波动率 = std * sqrt(252)
网上搜到的这个方法算下来和很多期权软件得到的差异比较大,所以暂时只能手工录入
from math import log,sqrt,exp
from scipy import stats
import matplotlib.pyplot as plt
class option():
#参数分别表示
#标的价,行权价,无风险利率,到期天数,波动率,期权最新价,call_put为0表示认购,为1表示认沽
def __init__(self,s,k,r,t,sigma,close,call_put='call'):
self.s = s
self.k = k
self.r = r
self.T = t/365
self.sigma = sigma
self.close = close
self.call_put = call_put.lower()
self.d1 = (log(self.s / self.k) + (self.r + 1 / 2 * self.sigma ** 2) * self.T) / (self.sigma * sqrt(self.T))
#BS定价公式,返回期权理论价
def call(self):
'''
st,k,r,T,sigma(T以年为单位,天数应该除以365)
'''
d1 = self.d1
d2 = d1 - self.sigma * sqrt(self.T)
call = self.s * stats.norm.cdf(d1, 0.0, 1.0) - self.k * exp(-self.r * self.T) * stats.norm.cdf(d2, 0.0, 1.0)
return call
def put(self):
'''
st,k,r,T,sigma(T以年为单位,天数应该除以365)
'''
d1 = self.d1
d2 = d1 - self.sigma * sqrt(self.T)
put = self.k * exp(-self.r * self.T) * stats.norm.cdf(-1 * d2) - 1 * self.s * stats.norm.cdf(-1 * d1)
return put
#获得delta
def delta(self):
'''
n默认为1看涨期权的delta
n为-1为看跌期权的delta
'''
if self.call_put == 'call':
n = 1
else:
n = -1
d1 = self.d1
delta = n * stats.norm.cdf(n * d1)
return delta
# 获得gamma
def gamma(self):
d1 = self.d1
gamma = stats.norm.pdf(d1) / (self.s * self.sigma * sqrt(self.T))
return gamma
# 获得theta
def theta(self):
'''
n默认为1看涨期权的delta
n为-1为看跌期权的delta
'''
if self.call_put == 'call':
n = 1
else:
n = -1
d1 = self.d1
d2 = d1 - self.sigma * sqrt(self.T)
theta = -1 * (self.s * stats.norm.pdf(d1) * self.sigma) / (2 * sqrt(self.T)) - n * self.r * self.k * exp(-self.r * self.T) * stats.norm.cdf(n * d2)
return theta
# 获得veag
def vega(self):
d1 = self.d1
vega = self.s * sqrt(self.T) * stats.norm.pdf(d1)
return vega
#牛顿法迭代求隐含波动率
def imp_vol_newton(self, sigma_est=1, it=100):
if self.call_put == 'call':
for i in range(it):
d1 = (log(self.s / self.k) + (self.r + 1 / 2 * sigma_est ** 2) * self.T) / (sigma_est * sqrt(self.T))
d2 = d1 - sigma_est * sqrt(self.T)
call = self.s * stats.norm.cdf(d1, 0.0, 1.0) - self.k * exp(-self.r * self.T) * stats.norm.cdf(d2, 0.0,1.0)
vega = self.s * sqrt(self.T) * stats.norm.pdf(d1)
sigma_est -= (call - self.close) /vega
return sigma_est
else:
for i in range(it):
d1 = (log(self.s / self.k) + (self.r + 1 / 2 * sigma_est ** 2) * self.T) / (sigma_est * sqrt(self.T))
d2 = d1 - sigma_est * sqrt(self.T)
put = self.k * exp(-self.r * self.T) * stats.norm.cdf(-1 * d2) - 1 * self.s * stats.norm.cdf(-1 * d1)
vega = self.s * sqrt(self.T) * stats.norm.pdf(d1)
sigma_est -= (put - self.close) /vega
return sigma_est
# 二分法求隐含波动率
def imp_vol_dichotomy(self):
c_est = 0
top = 3 # 波动率上限
floor = 0 # 波动率下限
sigma = (floor + top) / 2 # 波动率初始值
if self.call_put == 'call':
while abs(self.close - c_est) > 1e-8:
d1 = (log(self.s / self.k) + (self.r + 1 / 2 * sigma ** 2) * self.T) / (sigma * sqrt(self.T))
d2 = d1 - sigma * sqrt(self.T)
call = self.s * stats.norm.cdf(d1, 0.0, 1.0) - self.k * exp(-self.r * self.T) * stats.norm.cdf(d2, 0.0,1.0)
c_est = call
# 根据价格判断波动率是被低估还是高估,并对波动率做修正
if self.close - c_est > 0: # f(x)>0
floor = sigma
sigma = (sigma + top) / 2
else:
top = sigma
sigma = (sigma + floor) / 2
return sigma
else:
while abs(self.close - c_est) > 1e-8:
d1 = (log(self.s / self.k) + (self.r + 1 / 2 * sigma ** 2) * self.T) / (sigma * sqrt(self.T))
d2 = d1 - sigma * sqrt(self.T)
put = self.k * exp(-self.r * self.T) * stats.norm.cdf(-1 * d2) - 1 * self.s * stats.norm.cdf(-1 * d1)
c_est = put
# 根据价格判断波动率是被低估还是高估,并对波动率做修正
if self.close - c_est > 0: # f(x)>0
floor = sigma
sigma = (sigma + top) / 2
else:
top = sigma
sigma = (sigma + floor) / 2
return sigma
parameter = {}
parameter['标的价格'] = 2.724
parameter['行权价'] = 2.7
parameter['无风险利率'] = 0.044
parameter['到期天数'] = 218
parameter['历史波动率'] = 0.2668
parameter['期权最新价'] = 0.2236
parameter['call_put'] = 'call'
p1 = option(parameter['标的价格'],parameter['行权价'],parameter['无风险利率'],parameter['到期天数'],parameter['历史波动率'],parameter['期权最新价'],parameter['call_put'])
print(p1.imp_vol_newton(),p1.imp_vol_dichotomy())
0.20927908375262202 0.20927904546260834
import datetime
today = str(datetime.datetime.now().year)+'-'+str(datetime.datetime.now().month)+'-'+str(datetime.datetime.now().day)
stop_date = '2019-6-26'
date1 = datetime.datetime.strptime(today,'%Y-%m-%d')
date2 = datetime.datetime.strptime(stop_date,'%Y-%m-%d')
diff = date2 - date1
#获取期权实时数据
from jqdata import *
df_CO = opt.run_query(query(opt.OPT_DAILY_PREOPEN.code,
opt.OPT_DAILY_PREOPEN.exercise_price,
opt.OPT_DAILY_PREOPEN.pre_close,
opt.OPT_DAILY_PREOPEN.contract_type,
opt.OPT_DAILY_PREOPEN.pre_close_underlying).
filter(opt.OPT_DAILY_PREOPEN.date==today,
opt.OPT_DAILY_PREOPEN.exercise_date==stop_date,
opt.OPT_DAILY_PREOPEN.contract_type=='CO').
order_by(opt.OPT_DAILY_PREOPEN.exercise_price))
df_PO = opt.run_query(query(opt.OPT_DAILY_PREOPEN.code,
opt.OPT_DAILY_PREOPEN.exercise_price,
opt.OPT_DAILY_PREOPEN.pre_close,
opt.OPT_DAILY_PREOPEN.contract_type,
opt.OPT_DAILY_PREOPEN.pre_close_underlying).
filter(opt.OPT_DAILY_PREOPEN.date==today,
opt.OPT_DAILY_PREOPEN.exercise_date==stop_date,
opt.OPT_DAILY_PREOPEN.contract_type=='PO').
order_by(opt.OPT_DAILY_PREOPEN.exercise_price))
df_CO.head()
code | exercise_price | pre_close | contract_type | pre_close_underlying | |
---|---|---|---|---|---|
0 | 10001671.XSHG | 2.050 | 0.6645 | CO | 2.709 |
1 | 10001645.XSHG | 2.100 | 0.6136 | CO | 2.709 |
2 | 10001637.XSHG | 2.150 | 0.5649 | CO | 2.709 |
3 | 10001629.XSHG | 2.200 | 0.5138 | CO | 2.709 |
4 | 10001521.XSHG | 2.205 | 0.5099 | CO | 2.709 |
call_imp_vol = []
for i in range(len(df_CO)):
parameter = {}
parameter['标的价格'] = df_CO.iloc[i,:].pre_close_underlying
parameter['行权价'] = df_CO.iloc[i,:].exercise_price
parameter['无风险利率'] = 0.044
parameter['到期天数'] = diff.days
parameter['历史波动率'] = 0.2668
parameter['期权最新价'] = df_CO.iloc[i,:].pre_close
parameter['call_put'] = 'call'
p1 = option(parameter['标的价格'],parameter['行权价'],
parameter['无风险利率'],parameter['到期天数'],
parameter['历史波动率'],parameter['期权最新价'],
parameter['call_put'])
call_imp_vol.append(p1.imp_vol_newton())
df_CO['imp_vol'] = call_imp_vol
df_CO.dropna(inplace=True)
df_CO.head()
code | exercise_price | pre_close | contract_type | pre_close_underlying | imp_vol | |
---|---|---|---|---|---|---|
14 | 10001601.XSHG | 2.450 | 0.2723 | CO | 2.709 | 0.206647 |
15 | 10001505.XSHG | 2.500 | 0.2227 | CO | 2.709 | 0.176049 |
16 | 10001602.XSHG | 2.500 | 0.2219 | CO | 2.709 | 0.166751 |
17 | 10001506.XSHG | 2.549 | 0.1841 | CO | 2.709 | 0.210203 |
18 | 10001603.XSHG | 2.550 | 0.1819 | CO | 2.709 | 0.202963 |
put_imp_vol = []
for i in range(len(df_PO)):
parameter = {}
parameter['标的价格'] = df_PO.iloc[i,:].pre_close_underlying
parameter['行权价'] = df_PO.iloc[i,:].exercise_price
parameter['无风险利率'] = 0.044
parameter['到期天数'] = diff.days
parameter['历史波动率'] = 0.2668
parameter['期权最新价'] = df_PO.iloc[i,:].pre_close
parameter['call_put'] = 'put'
p1 = option(parameter['标的价格'],parameter['行权价'],
parameter['无风险利率'],parameter['到期天数'],
parameter['历史波动率'],parameter['期权最新价'],
parameter['call_put'])
put_imp_vol.append(p1.imp_vol_newton())
df_PO['imp_vol'] = put_imp_vol
df_PO.dropna(inplace=True)
df_PO.head()
code | exercise_price | pre_close | contract_type | pre_close_underlying | imp_vol | |
---|---|---|---|---|---|---|
0 | 10001672.XSHG | 2.050 | 0.0012 | PO | 2.709 | 0.409649 |
1 | 10001646.XSHG | 2.100 | 0.0015 | PO | 2.709 | 0.390386 |
2 | 10001638.XSHG | 2.150 | 0.0013 | PO | 2.709 | 0.351936 |
3 | 10001630.XSHG | 2.200 | 0.0014 | PO | 2.709 | 0.325023 |
4 | 10001522.XSHG | 2.205 | 0.0014 | PO | 2.709 | 0.321990 |
plt.figure(figsize=[20,5])
plt.plot(df_CO.exercise_price,df_CO.imp_vol)
plt.plot(df_PO.exercise_price,df_PO.imp_vol)
my_x_ticks = np.arange(2,3.5,0.05)
plt.xticks(my_x_ticks)
plt.legend(labels=['call','put'],loc='best')
<matplotlib.legend.Legend at 0x7f6ce2bedfd0>
本社区仅针对特定人员开放
查看需注册登录并通过风险意识测评
5秒后跳转登录页面...