class Indexing(): def __init__(self, start_date, end_date): df_metrics = option_data(start_date, end_date) self.optionset = BaseOptionSet(df_metrics, rf=0.03) self.optionset.init() def fun_otm_quote(self, df): if df[Util.AMT_APPLICABLE_STRIKE] > df['mid_k']: quote = df['amt_call_quote'] elif df[Util.AMT_APPLICABLE_STRIKE] < df['mid_k']: quote = df['amt_put_quote'] else: quote = (df['amt_call_quote'] + df['amt_put_quote']) / 2.0 return quote def fun_for_p1(self, df): DK = df['amt_delta_k'] Q = df['amt_otm_quote'] K = df[Util.AMT_APPLICABLE_STRIKE] res = Q * DK / K**2 return Q * DK / K**2 def fun_for_p2(self, df): DK = df['amt_delta_k'] Q = df['amt_otm_quote'] K = df[Util.AMT_APPLICABLE_STRIKE] F = df['F'] return 2 * (1 - math.log(K / F, math.e)) * Q * DK / K**2 def fun_for_p3(self, df): DK = df['amt_delta_k'] Q = df['amt_otm_quote'] K = df[Util.AMT_APPLICABLE_STRIKE] F = df['F'] return 3 * (2 * math.log(K / F, math.e) - math.log(K / F, math.e)**2) * Q * DK / K**2 def fun_for_sigma(self, df): DK = df['amt_delta_k'] Q = df['amt_otm_quote'] K = df[Util.AMT_APPLICABLE_STRIKE] return Q * DK / K**2 def get_e1(self, F, K): return -(1 + math.log(F / K, math.e) - F / K) def get_e2(self, F, K): return 2 * math.log(K / F, math.e) * (F / K - 1) + 0.5 * math.log( K / F, math.e)**2 def get_e3(self, F, K): return 3 * (math.log(K / F, math.e)**2) * ( (1.0 / 3.0) * math.log(K / F, math.e) + F / K - 1) def get_S(self, p1, p2, p3): res = (p3 - 3 * p1 * p2 + 2 * p1**3) / (p2 - p1**2)**1.5 return res def get_T_quotes(self, df_mdt, eval_date): df_call = df_mdt[df_mdt[Util.CD_OPTION_TYPE] == Util.STR_CALL].rename( columns={Util.AMT_CLOSE: 'amt_call_quote'}) df_put = df_mdt[df_mdt[Util.CD_OPTION_TYPE] == Util.STR_PUT].rename( columns={Util.AMT_CLOSE: 'amt_put_quote'}) df_call = df_call.drop_duplicates( Util.AMT_APPLICABLE_STRIKE).reset_index(drop=True) df_put = df_put.drop_duplicates( Util.AMT_APPLICABLE_STRIKE).reset_index(drop=True) df = pd.merge(df_call[[ 'amt_call_quote', Util.AMT_APPLICABLE_STRIKE, Util.AMT_STRIKE ]], df_put[['amt_put_quote', Util.AMT_APPLICABLE_STRIKE]], how='inner', on=Util.AMT_APPLICABLE_STRIKE) # df = pd.concat([df_call[['amt_call_quote']], df_put[['amt_put_quote']]], axis=1, join='inner', verify_integrity=True) df[Util.AMT_UNDERLYING_CLOSE] = df_put[ Util.AMT_UNDERLYING_CLOSE].values[0] df['amt_cp_diff'] = abs(df['amt_call_quote'] - df['amt_put_quote']) maturitydt = df_put[Util.DT_MATURITY].values[0] df[Util.DT_MATURITY] = maturitydt ttm = ( (maturitydt - eval_date).total_seconds() / 60.0) / (365.0 * 1440) df['amt_ttm'] = ttm df['amt_fv'] = math.exp(self.optionset.rf * (ttm)) df = df.sort_values(by=Util.AMT_APPLICABLE_STRIKE).reset_index( drop=True) dk = df[Util.AMT_APPLICABLE_STRIKE].diff(periods=2).dropna() / 2.0 dk.loc[1] = df.loc[1, Util.AMT_APPLICABLE_STRIKE] - df.loc[ 0, Util.AMT_APPLICABLE_STRIKE] dk.loc[len(df)] = df.loc[len(df) - 1, Util.AMT_APPLICABLE_STRIKE] - df.loc[ len(df) - 2, Util.AMT_APPLICABLE_STRIKE] dk = dk.sort_index() dk = dk.reset_index(drop=True) df['amt_delta_k'] = dk return df def forward_cboe(self, t_quotes, eval_date): # ATM strike k0 -- First strike below the forward index level, F # Forward price F -- F, by identifying the strike price at which the absolute difference # between the call and put prices is smallest. df = t_quotes.set_index(Util.AMT_APPLICABLE_STRIKE) mid_k = df.sort_values(by='amt_cp_diff', ascending=True).index[0] p_call = df.loc[mid_k, 'amt_call_quote'] p_put = df.loc[mid_k, 'amt_put_quote'] F = df.loc[mid_k, 'amt_fv'] * (p_call - p_put) + mid_k df['k-f'] = df.index - F if len(df[df['k-f'] < 0]) > 0: K0 = df[df['k-f'] < 0].sort_values(by='amt_cp_diff', ascending=True).index[0] else: K0 = df.sort_values(by='amt_cp_diff', ascending=True).index[0] return mid_k, K0, F """ K0 is the 1st strike below F0 """ def for_calculation(self, df, eval_date): mid_k, k0, F = self.forward_cboe(df, eval_date) ttm = df.loc[0, 'amt_ttm'] S = df.loc[0, 'amt_underlying_close'] implied_r = math.log(F / S, math.e) / ttm self.implied_rf = implied_r df['k0'] = k0 df['mid_k'] = mid_k df['F'] = F df['amt_otm_quote'] = df.apply(self.fun_otm_quote, axis=1) return df def calculate_S_for_skew(self, df): k0 = df.loc[0, 'k0'] F = df.loc[0, 'F'] e1 = self.get_e1(F, k0) e2 = self.get_e2(F, k0) e3 = self.get_e3(F, k0) df['for_p1'] = df.apply(self.fun_for_p1, axis=1) df['for_p2'] = df.apply(self.fun_for_p2, axis=1) df['for_p3'] = df.apply(self.fun_for_p3, axis=1) fv = df.loc[0, 'amt_fv'] p1 = -fv * df['for_p1'].sum() + e1 p2 = fv * df['for_p2'].sum() + e2 p3 = fv * df['for_p3'].sum() + e3 S = self.get_S(p1, p2, p3) SKEW = 100 - 10 * S # S_r = self.get_S(-0.00173,0.003606,-0.00049) # SKEW_r = 100-10*S_r return S def calculate_sigma_for_vix(self, df): df['for_sigma'] = df.apply(self.fun_for_sigma, axis=1) k0 = df.loc[0, 'k0'] F = df.loc[0, 'F'] T = df.loc[0, 'amt_ttm'] fv = df.loc[0, 'amt_fv'] v1 = (1.0 / T) * (F / k0 - 1)**2 # v1 = (F / K - 1) ** 2 sum = df['for_sigma'].sum() sigma = (2.0 / T) * fv * sum - v1 # sigma = 2.0 * fv * sum - v1 return sigma def calculate(self, eval_date): df_daily_state = self.optionset.get_current_state() mdt = self.optionset.get_maturities_list()[0] if (mdt - self.optionset.eval_date).days <= 5: mdt1 = self.optionset.get_maturities_list()[1] mdt2 = self.optionset.get_maturities_list()[2] else: mdt1 = self.optionset.get_maturities_list()[0] mdt2 = self.optionset.get_maturities_list()[1] df_mdt1 = OptionUtil.get_df_by_mdt(df_daily_state, mdt1) df_mdt2 = OptionUtil.get_df_by_mdt(df_daily_state, mdt2) t_quotes1 = self.get_T_quotes(df_mdt1, eval_date) t_quotes2 = self.get_T_quotes(df_mdt2, eval_date) # t_quotes1.to_csv('t_quotes1.csv') # t_quotes2.to_csv('t_quotes2.csv') calculate1 = self.for_calculation(t_quotes1, eval_date) calculate2 = self.for_calculation(t_quotes2, eval_date) S1 = self.calculate_S_for_skew(calculate1) S2 = self.calculate_S_for_skew(calculate2) sigma1 = self.calculate_sigma_for_vix(calculate1) sigma2 = self.calculate_sigma_for_vix(calculate2) T1 = calculate1.loc[0, 'amt_ttm'] T2 = calculate2.loc[0, 'amt_ttm'] NT1 = (mdt1 - eval_date).total_seconds() / 60.0 NT2 = (mdt2 - eval_date).total_seconds() / 60.0 N30 = 30 * 1440.0 N365 = 365 * 1440.0 w = (NT2 - N30) / (NT2 - NT1) skew = 100 - 10 * (w * S1 + (1 - w) * S2) vix = 100 * math.sqrt( (T1 * sigma1 * w + T2 * sigma2 * (1 - w)) * N365 / N30) return vix, skew def get_atm_options(self, maturity): list_atm_call, list_atm_put = self.optionset.get_options_list_by_moneyness_mthd1( moneyness_rank=0, maturity=maturity) atm_call = self.optionset.select_higher_volume(list_atm_call) atm_put = self.optionset.select_higher_volume(list_atm_put) return atm_call, atm_put def get_atm_iv_average(self, maturity): atm_call, atm_put = self.get_atm_options(maturity) iv_call = atm_call.get_implied_vol() iv_put = atm_put.get_implied_vol() iv_avg = (iv_call + iv_put) / 2 return iv_avg def run(self): self.df_res = pd.DataFrame() print('=' * 120) print("%10s %20s %20s %20s %20s %20s" % ('eval_date', 'vix', 'skew', 'iv_htr', 'iv_avg', 'htbr')) print('-' * 120) while self.optionset.current_index < self.optionset.nbr_index: eval_date = self.optionset.eval_date try: maturity = self.optionset.select_maturity_date( nbr_maturity, min_holding=min_holding) iv_htr = self.optionset.get_atm_iv_by_htbr(maturity) iv_avg = self.get_atm_iv_average(maturity) htbr = self.optionset.get_htb_rate(maturity) vix, skew = self.calculate(eval_date) self.df_res.loc[eval_date, 'skew'] = skew self.df_res.loc[eval_date, 'vix'] = vix self.df_res.loc[ eval_date, '50ETF'] = self.optionset.get_underlying_close() self.df_res.loc[eval_date, 'htb_rate'] = htbr self.df_res.loc[eval_date, 'iv_atm_htr'] = iv_htr self.df_res.loc[eval_date, 'iv_atm_avg'] = iv_avg print("%10s %20s %20s %20s %20s %20s" % (eval_date, vix, skew, iv_htr, iv_avg, htbr)) except: pass if not self.optionset.has_next(): break self.optionset.next()
""" namecode : M/SR """ name_code = c.Util.STR_SR # name_code = c.Util.STR_M df_metrics = get_comoption_mktdata(start_date, end_date, name_code) exercise_type = c.OptionExerciseType.AMERICAN """ namecode : 50ETF """ # name_code = c.Util.STR_50ETF # df_metrics = get_50option_mktdata(start_date, end_date) # exercise_type = c.OptionExerciseType.EUROPEAN table_iv = admin.table_implied_volatilities() optionset = BaseOptionSet(df_metrics) optionset.init() dt_maturity = optionset.select_maturity_date(nbr_maturity=nbr_maturity, min_holding=min_holding) spot = optionset.get_underlying_close(maturitydt=dt_maturity) while optionset.current_index < optionset.nbr_index: print(optionset.eval_date, spot) try: call_list, put_list = optionset.get_options_list_by_moneyness_mthd1( moneyness_rank=moneyness, maturity=dt_maturity) except Exception as e: print(e) optionset.next() dt_maturity = optionset.select_maturity_date(nbr_maturity, min_holding=min_holding) spot = optionset.get_underlying_close(maturitydt=dt_maturity) continue base_option_call = call_list[0] if exercise_type == c.OptionExerciseType.AMERICAN: