Github代码复现-IVIX中国波指计算
目錄
參考資料
VIX介紹
總公式
T的計算:
σ計算:
代碼復現
參考資料
數據、代碼:GitHub - Alexdachen/ivix: 中國波指的計算
閱讀文章:小C:VIX指數的來龍去脈和iVIX的計算方法 - 知乎
數據的介紹:vixDate是計算當日的日期
VIX介紹
VIX計算的是未來30天的加權波動率
總公式
總公式如上圖所示,可以看出有2個未知變量T、σ:
T1:VixDate到近月合約到期日之間的時間間隔
T2:VixDate到次近月合約到期日之間的時間間隔
σ1:近月合約貢獻的波動率
σ2:次近月合約貢獻的波動率
時間間隔T
- 使用EXE_ENDDATE篩選找到近月和次近月合約
- 計算VixDate與合約到期日之間的時間間隔T
波動率σ
?未知變量:遠期價格F、
:篩選出近月合約和次近月合約后,分別使用這2個月份的數據,計算每個月份下每個執行價格下的看漲期權的價格和看跌期權之間的價格差,選擇價格差最小的那個期權的執行價格和看漲期權價格和看跌期權價格來計算遠期價格,計算公式如下所示:
:選擇離F最近且小于F的期權執行價,找到K0后再進行一次數據篩選,選擇執行價格小于K0的看跌期權,選擇執行價格大于K0的看漲期權
:區分好近月和次近月合約后,篩選好用于計算的期權數據后,數據按照看漲或者看跌類型排序,這個指標表示執行價格為Ki的期權的上一個期權執行價格與下一個期權執行價格之間的間隔的二分之一,如果這個價格是最高的執行價格或者最低的執行價格,這個指標取執行價Ki和臨近執行價的均值。
表示執行價格為Ki的期權價格
R:無風險利率,使用插值法獲得。
代碼復現
導入庫
from datetime import datetime import numpy as np import pandas as pd from scipy.interpolate import make_interp_spline #原代碼中的spline已經不兼容了,代碼會報錯讀取數據
#讀取利率數據 shibor_rate = pd.read_csv("shibor.csv",index_col=0,encoding='GBK') #index_col參數注明index列 #讀取期權交易數據options_data options_data = pd.read_csv("options.csv",index_col=0,encoding='GBK') #讀取交易日數據,畫圖時作為橫坐標,一共829天 tradeday = pd.read_csv('tradeday.csv',encoding='GBK') #讀取真實披露的ivix true_ivix = pd.read_csv("ivixx.csv",encoding='GBK')篩選當天交易的期權數據
def getHistDayOptions(vixDate,options_data):#提取交易日為VixDate的期權數據options_data = options_data.loc[vixDate,:] #loc是index索引,提取所有index為vixDate的行return options_data計算近月合約和次近月合約
def getNearNextOptExpDate(options,vixDate):#輸入:期權交易數據、vixDate#找到options中的當月和次月期權到期日#如果options中近月合約期權離到期日僅剩7天以內,則選擇次近月期權為近月期權#返回:近月合約和次近月合約的到期日vixDate = datetime.strptime(vixDate,'%Y/%m/%d') #strptime是將字符串格式的日期轉換為datetime格式#ravel:將到期日期數組去重后放入列表中 optionsExpDate = list(pd.Series(options.EXE_ENDDATE.values.ravel()).unique())optionsExpDate = [datetime.strptime(i,'%Y/%m/%d %H:%M')for i in optionsExpDate]near = min(optionsExpDate)optionsExpDate.remove(near) #remove方法是刪除元素#如果近月合約到期時間小于7天,將選擇次近月合約當做近月合約if near.day-vixDate.day < 7:near = min(optionsExpDate)optionsExpDate.remove(near)nt = min(optionsExpDate)#篩選options中屬于近月和次近月合約之間數據#near、nt是datetime格式return near,nt無風險利率計算
def periodsSplineRiskFreeInterestRate(options,date):#options是date日交易的期權數據#返回date到每個到期日exoDate間的無風險利率date = datetime.strptime(date,'%Y/%m/%d')#這個地方的options是vixDate日交易的期權數據exp_dates = np.sort(options.EXE_ENDDATE.unique()) #使用sort方法對所有的到期日期進行排序#periods鍵是到期日期、值是到期日期與vixDate之間的時間間隔periods = {}for epd in exp_dates:epd = pd.to_datetime(epd)periods[epd] = (epd - date).days*1.0/365.0 #datetime格式的數據是一個三元組,時間元組之間求時間間隔shibor_date = datetime.strptime(shibor_rate.index[0],"%Y-%m-%d") #shibor_rate.index[0]是指的是第1和索引“2018-07-13 ”#插值之前先進行判斷:#shibor是按照時間降序排序,shibor_date是可獲取到的最大日期#VixDate日期大于shibor_date,使用shibor_date來插值,即“2018-07-13那日的shibor數據if date>=shibor_date:date_str = shibor_rate.index[0]shibor_values = shibor_rate.ix[0].valueselse:#vixDate日期小于shibor_date時,使用VixDate日披露的shibor數據進行插值date_str = date.strftime("%Y-%m-%d")shibor_values = shibor_rate.loc[date_str].values shibor = {}period = np.array([1.0,7.0,14.0,30.0,90.0,180.0,270.0,360.0])/360.0for p in periods.keys():#之前寫成了key報錯了tmp = periods[p]#使用shibor插值#使用spline方法會報錯:module 'scipy.interpolate' has no attribute 'spline'sh = make_interp_spline(period,shibor_values)(tmp)#sh = interpolate.spline(period,shibor_values,tmp,order=3)shibor[p] = sh/100.0return shibor #shibor是一個字典,鍵是時間間隔,值是插值得到的無風險利率計算價格差距
def getStrikeMinCallMinusPutClosePrice(options):#返回同一期權行權價對應的call和put期權價格差call = options[options.EXE_MODE==u"認購"].set_index(u"EXE_PRICE").sort_index() #sort_index默認按照行標簽升序排列put = options[options.EXE_MODE==u"認沽"].set_index(u"EXE_PRICE").sort_index()callMinusPut = call.CLOSE-put.CLOSEstrike = abs(callMinusPut).idxmin() #idxmin()計算能夠獲得獲取到的最小值的索引位置(整數)priceDiff = callMinusPut[strike].min()return strike,priceDiff波動率
def calSigmaSquare( options, FF, R, T):# 計算某個到期日期權對于VIX的貢獻sigma;# 輸入為期權數據options,FF為forward index price,# R為無風險利率, T為期權剩余到期時間#輸出:近期合約或者遠期合約的sigma"""params: options:近月合約和次近月合約的交易數據FF: 根據上一步計算得來的strike,然后再計算得到的forward index price, 根據它對所需要的看漲看跌合約進行劃分。取小于FF的第一個行權價為中間行權價K0, 然后選取大于等于K0的所有看漲合約, 選取小于等于K0的所有看跌合約。對行權價為K0的看漲看跌合約,刪除看漲合約,不過看跌合約的價格為兩者的均值。R: 這部分期權合約到期日對應的無風險利率 shiborT: 還有多久到期(年化)return:Sigma:得到的結果是傳入該到期日數據的Sigma"""callAll = options[options.EXE_MODE==u"認購"].set_index(u"EXE_PRICE").sort_index()putAll = options[options.EXE_MODE==u"認沽"].set_index(u"EXE_PRICE").sort_index()callAll['deltaK'] = 0.05putAll['deltaK'] = 0.05# 按照看漲、看跌期權排序時,計算執行價格間隔index = callAll.indexif len(index) < 3:#只有2個看漲期權或者1個看漲期權callAll['deltaK'] = index[-1] - index[0]else:for i in range(1,len(index)-1):callAll['deltaK'].loc[index[i]] = (index[i+1]-index[i-1])/2.0 #執行價格間隔等于前后兩個執行價格之差的一半callAll['deltaK'].loc[index[0]] = index[1]-index[0] #第一個和最后一個執行價格使用它們與鄰近的執行價格之間的差callAll['deltaK'].loc[index[-1]] = index[-1] - index[-2]#原代碼中的ix需要替換為loc,因為會報 'Series' object has no attribute 'ix' ix替換為locindex = putAll.indexif len(index) < 3:putAll['deltaK'] = index[-1] - index[0]else:for i in range(1,len(index)-1):putAll['deltaK'].loc[index[i]] = (index[i+1]-index[i-1])/2.0putAll['deltaK'].loc[index[0]] = index[1]-index[0]putAll['deltaK'].loc[index[-1]] = index[-1] - index[-2]call = callAll[callAll.index > FF]put = putAll[putAll.index < FF]FF_idx = FF #FF是計算出來的遠期價格 FF_idx是K0if put.empty:#如果用于計算的看跌期權的數據為FF_idx = call.index[0]callComponent = call.CLOSE*call.deltaK/call.index/call.indexsigma = (sum(callComponent))*np.exp(T*R)*2/Tsigma = sigma - (FF/FF_idx - 1)**2/Telif call.empty: #如果用于計算的看漲期權數據為空FF_idx = put.index[-1]putComponent = put.CLOSE*put.deltaK/put.index/put.indexsigma = (sum(putComponent))*np.exp(T*R)*2/Tsigma = sigma - (FF/FF_idx - 1)**2/Telse:FF_idx = put.index[-1]#當看漲期權和看跌期權都擁有的時候,平值期權被劃分為了看跌期權,此時這個期權的價格應該去看漲期權和看跌期權價格的一半#我把try-except去掉后,出現setting an array element with a sequence問題#去原始數據查看了一下,有這樣一種情況:執行價格相同,但是有兩個看漲期權收盤價和兩個看跌期權,這是因為這是兩個不同的合約#因為一開始篩選數據是通過日期和執行價格篩選的,不能區分出來,只能通過合約名字看得出來#put['CLOSE'].iloc[-1] = (putAll.loc[FF_idx].CLOSE + callAll.loc[FF_idx].CLOSE)/2.0 try:if len(putAll.loc[FF_idx].CLOSE.values) > 1:put['CLOSE'].iloc[-1] = (putAll.loc[FF_idx].CLOSE.values[1] + callAll.loc[FF_idx].CLOSE.values[0])/2.0except:put['CLOSE'].iloc[-1] = (putAll.loc[FF_idx].CLOSE + callAll.loc[FF_idx].CLOSE)/2.0callComponent = call.CLOSE*call.deltaK/call.index/call.indexputComponent = put.CLOSE*put.deltaK/put.index/put.indexsigma = (sum(callComponent)+sum(putComponent))*np.exp(T*R)*2/Tsigma = sigma - (FF/FF_idx - 1)**2/Treturn sigma時間轉換函數
唯一有點迷糊的是這里為什么要分類討論,但是不區分又會報錯
def changeste(t):if t.month>=10:str_t = t.strftime('%Y/%m/%d ')+'0:00'else:str_t = t.strftime('%Y/%m/%d ')str_t = str_t[:5]+str_t[6:]+'0:00'return str_t計算每日的Vix
def calDayVix(vixDate):#提取在vixDate日交易的期權數據options = getHistDayOptions(vixDate,options_data)#獲取近月合約和次近月合約的到期日期,格式為datetime格式near,nexts = getNearNextOptExpDate(options,vixDate)#提取近期合約期權數據和次近月合約期權數據str_near = changeste(near)str_next = changeste(nexts)optionsNearTerm = options[options.EXE_ENDDATE==str_near]optionsNextTerm = options[options.EXE_ENDDATE==str_next]#獲取vixDate到近月合約和次近月合約之間的無風險利率shibor_near = periodsSplineRiskFreeInterestRate(optionsNearTerm,vixDate)shibor_next = periodsSplineRiskFreeInterestRate(optionsNextTerm,vixDate)R_near = shibor_near[datetime(near.year,near.month,near.day)]R_next = shibor_next[datetime(nexts.year,nexts.month,nexts.day)]#計算剩余到期時間vixDate = datetime.strptime(vixDate,'%Y/%m/%d')T_near = (near-vixDate).days/365.0T_next = (nexts-vixDate).days/365.0#計算遠期價格nearPriceDiff = getStrikeMinCallMinusPutClosePrice(optionsNearTerm)nextPriceDiff = getStrikeMinCallMinusPutClosePrice(optionsNextTerm)near_F = nearPriceDiff[0] + np.exp(T_near*R_near)*nearPriceDiff[1]next_F = nextPriceDiff[0] + np.exp(T_next*R_next)*nextPriceDiff[1]#計算不同到期日期權對于VIX的貢獻near_sigma = calSigmaSquare(optionsNearTerm,near_F,R_near,T_near)next_sigma = calSigmaSquare(optionsNextTerm,next_F,R_next,T_next)w = (T_next - 30.0/365.0)/(T_next - T_near)vix = T_near*w*near_sigma + T_next*(1-w)*next_sigmareturn 100*np.sqrt(abs(vix)*365.0/30.0)主函數
因為一開始程序有錯誤,只能計算400多個指數,于是我把日期和指數對應起來放到了一個字典中,下面作圖的時候需要列表格式,我重新寫了一個格式轉換函數
ivix = {} for day in tradeday['DateTime']:#ivix.append(calDayVix(day))ivix[day] = calDayVix(day)繪圖
def change2f(old_list):#保留2位小數,是因為畫圖的時候發現顯示數據很亂#主函數中計算出來的數據的指數是series格式,不是list格式,需要轉換一下mid_np = np.array(old_list) #列表轉數組mid_np_2f = np.round(mid_np,2) #對數組中的元素保留兩位小數new_list = list(mid_np_2f) #數組轉列表return new_list#畫圖 #pyecharts因為原代碼使用的版本不兼容,需要用以下方式導入,是去看pyecharts的官方文檔的例子寫的 from pyecharts.charts import Line from pyecharts import options as opts datetime = list(tradeday['DateTime']) data1 = true_ivix[u"收盤價(元)"] data1 = change2f(data1) data2 = list(ivix.values()) data2 = change2f(data2) line1 = Line() line1.add_xaxis(datetime) line1.add_yaxis(series_name='中證指數發布',y_axis=data1,label_opts=opts.LabelOpts(is_show=False),markpoint_opts=opts.MarkPointOpts(data=[opts.MarkPointItem(type_="max", name="最大值"),opts.MarkPointItem(type_="min", name="最小值"),]),markline_opts=opts.MarkLineOpts(data=[opts.MarkLineItem(type_="average", name="平均值")])) line1.add_yaxis(series_name='手動計算', y_axis=data2,label_opts=opts.LabelOpts(is_show=False),markpoint_opts=opts.MarkPointOpts(data=[opts.MarkPointItem(type_="max", name="最大值"),opts.MarkPointItem(type_="min", name="最小值"),]),markline_opts=opts.MarkLineOpts(data=[opts.MarkLineItem(type_="average", name="平均值")]))放一個自己畫的圖:
總結
以上是生活随笔為你收集整理的Github代码复现-IVIX中国波指计算的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: U盘分区方法
- 下一篇: 入门HTML之表格属性bgcolor b