def iv_at_the_money(dt_date, dt_last, name_code): dict_iv_call = {} dict_iv_put = {} df_metrics = get_data.get_comoption_mktdata(dt_last, dt_date, name_code) optionset = BaseOptionSet(df_metrics, rf=0.0) optionset.init() dt_maturity = optionset.select_maturity_date(0, 0) call_list, put_list = optionset.get_options_list_by_moneyness_mthd1( 0, dt_maturity) atm_call = optionset.select_higher_volume(call_list) atm_put = optionset.select_higher_volume(put_list) iv_call = atm_call.get_implied_vol() * 100 iv_put = atm_put.get_implied_vol() * 100 dict_iv_call.update({dt_last: iv_call}) dict_iv_put.update({dt_last: iv_put}) optionset.go_to(dt_date) dt_maturity = optionset.select_maturity_date(0, 0) call_list, put_list = optionset.get_options_list_by_moneyness_mthd1( 0, dt_maturity) atm_call = optionset.select_higher_volume(call_list) atm_put = optionset.select_higher_volume(put_list) iv_call = atm_call.get_implied_vol() * 100 iv_put = atm_put.get_implied_vol() * 100 dict_iv_call.update({dt_date: iv_call}) dict_iv_put.update({dt_date: iv_put}) return dict_iv_call, dict_iv_put
def implied_vol_avg(last_week, end_date, df_metrics, df_res, name_code): m = 100 optionset = BaseOptionSet(df_metrics, rf=0.03) optionset.init() list_res_iv = [] while optionset.current_index < optionset.nbr_index: dt_maturity = optionset.select_maturity_date(nbr_maturity=0, min_holding=min_holding) list_atm_call, list_atm_put = optionset.get_options_list_by_moneyness_mthd1( moneyness_rank=0, maturity=dt_maturity) atm_call = optionset.select_higher_volume(list_atm_call) atm_put = optionset.select_higher_volume(list_atm_put) iv_call = atm_call.get_implied_vol() iv_put = atm_put.get_implied_vol() iv = (iv_put + iv_call) / 2.0 list_res_iv.append({'date': optionset.eval_date, 'iv': iv}) if not optionset.has_next(): break optionset.next() df_iv = pd.DataFrame(list_res_iv).sort_values( by='date', ascending=False).reset_index(drop=True) df_res.loc[:, name_code + ':N:date'] = df_iv['date'] df_res.loc[:, name_code + ':O:iv'] = df_iv['iv'] * m return df_res
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()
if close_signal(optionset.eval_date, maturity1, df_iv_stats): for option in account.dict_holding.values(): order = account.create_close_order(option) record = option.execute_order(order, slippage=slippage) account.add_record(record, option) hedging.synthetic_unit = 0 empty_position = True if empty_position and open_signal(optionset.eval_date, df_iv_stats): buy_write = c.BuyWrite.WRITE long_short = c.LongShort.SHORT maturity1 = optionset.select_maturity_date(nbr_maturity=0, min_holding=15) list_atm_call, list_atm_put = optionset.get_options_list_by_moneyness_mthd1( 0, maturity1) atm_call = optionset.select_higher_volume(list_atm_call) atm_put = optionset.select_higher_volume(list_atm_put) atm_strike = atm_call.strike() spot = atm_call.underlying_close() # if abs(atm_strike-spot) < 0.5: # 存在平值期权 hedging.amt_option = 1 / 1000 # 50ETF与IH点数之比 unit_c = np.floor( np.floor(account.portfolio_total_value / atm_call.strike()) / atm_call.multiplier()) * m unit_p = np.floor( np.floor(account.portfolio_total_value / atm_put.strike()) / atm_put.multiplier()) * m order_c = account.create_trade_order(atm_call, long_short, unit_c) order_p = account.create_trade_order(atm_put, long_short, unit_p) record_call = atm_call.execute_order(order_c, slippage=slippage) record_put = atm_put.execute_order(order_p, slippage=slippage)
core_id = 'm_1901' end_date = datetime.date(2018, 9, 7) last_week = datetime.date(2018, 8, 31) start_date = last_week dt_histvol = start_date - datetime.timedelta(days=200) min_holding = 5 df_metrics = get_data.get_comoption_mktdata(start_date, end_date, name_code) """ T-quote IV """ optionset = BaseOptionSet(df_metrics, rf=0) optionset.init() optionset.go_to(end_date) dt_maturity = optionset.select_maturity_date(0, min_holding) call_list, put_list = optionset.get_options_list_by_moneyness_mthd1( 0, dt_maturity) atm_call = optionset.select_higher_volume(call_list) atm_put = optionset.select_higher_volume(put_list) print('atm call iv: ', atm_call.get_implied_vol()) print('atm put iv: ', atm_put.get_implied_vol()) print('atm strike: ', atm_put.strike()) t_quote = optionset.get_T_quotes(dt_maturity) ivs_c1 = optionset.get_call_implied_vol_curve(dt_maturity) ivs_p1 = optionset.get_put_implied_vol_curve(dt_maturity) ivs_c2 = optionset.get_call_implied_vol_curve_htbr(dt_maturity) ivs_p2 = optionset.get_put_implied_vol_curve_htbr(dt_maturity) iv_curve = optionset.get_implied_vol_curves(dt_maturity) iv_curve_htbr = optionset.get_implied_vol_curves_htbr(dt_maturity) iv_volume_weighted = optionset.get_volume_weighted_iv(dt_maturity) iv_volume_weighted_htbr = optionset.get_volume_weighted_iv_htbr(dt_maturity)
for order in close_out_orders: execution_record = account.dict_holding[order.id_instrument].execute_order(order, slippage=0, execute_type=c.ExecuteType.EXECUTE_ALL_UNITS) account.add_record(execution_record, account.dict_holding[order.id_instrument]) empty_position = True # 开仓 if empty_position: try: buy_write = c.BuyWrite.WRITE long_short = c.LongShort.SHORT maturity1 = optionset.select_maturity_date(nbr_maturity=0, min_holding=20) # 空两份平值 list_atm_call, list_atm_put = optionset.get_options_list_by_moneyness_mthd1(moneyness_rank=0, maturity=maturity1) atm_call = optionset.select_higher_volume(list_atm_put) # 多一份实值: 2 list_itm_call, list_itm_put = optionset.get_options_list_by_moneyness_mthd1(moneyness_rank=1, maturity=maturity1) itm_call = optionset.select_higher_volume(list_itm_put) # 多一份虚值: -2 list_otm_call, list_otm_put = optionset.get_options_list_by_moneyness_mthd1(moneyness_rank=-1, maturity=maturity1) otm_call = optionset.select_higher_volume(list_otm_put) # unit_fund = 2 * (atm_call.get_initial_margin(c.LongShort.SHORT) - atm_call.mktprice_last_settlement()*atm_call.multiplier()) \ # + itm_call.mktprice_close()*itm_call.multiplier() + otm_call.mktprice_close()*otm_call.multiplier() unit_fund = 2 * atm_call.get_initial_margin( c.LongShort.SHORT) + itm_call.mktprice_close() * itm_call.multiplier() + otm_call.mktprice_close() * otm_call.multiplier() total_unit = np.floor(account.cash / unit_fund/2) unit_atm = 2 * total_unit unit_itm = total_unit
def implied_vol_avg(df_metrics, df_res, dt_list_term_structure, name_code): if name_code == c.Util.STR_CU: m = 100 else: m = 1 optionset = BaseOptionSet(df_metrics, rf=0.03) optionset.init() list_res_iv = [] iv_term_structure = [] while optionset.current_index < optionset.nbr_index: dt_maturity = optionset.select_maturity_date(nbr_maturity=0, min_holding=min_holding) list_atm_call, list_atm_put = optionset.get_options_list_by_moneyness_mthd1( moneyness_rank=0, maturity=dt_maturity) atm_call = optionset.select_higher_volume(list_atm_call) atm_put = optionset.select_higher_volume(list_atm_put) iv_call = atm_call.get_implied_vol() iv_put = atm_put.get_implied_vol() iv = (iv_put + iv_call) / 2.0 list_res_iv.append({'date': optionset.eval_date, 'iv': iv}) if optionset.eval_date in dt_list_term_structure: mdt_2 = optionset.select_maturity_date(nbr_maturity=1, min_holding=0) list_atm_call, list_atm_put = optionset.get_options_list_by_moneyness_mthd1( moneyness_rank=0, maturity=mdt_2) atm_call = optionset.select_higher_volume(list_atm_call) atm_put = optionset.select_higher_volume(list_atm_put) iv_call = atm_call.get_implied_vol() iv_put = atm_put.get_implied_vol() iv_2 = (iv_put + iv_call) / 2.0 mdt_3 = optionset.select_maturity_date(nbr_maturity=2, min_holding=0) if mdt_3 is None: iv_3 = None else: list_atm_call, list_atm_put = optionset.get_options_list_by_moneyness_mthd1( moneyness_rank=0, maturity=mdt_3) atm_call = optionset.select_higher_volume(list_atm_call) atm_put = optionset.select_higher_volume(list_atm_put) iv_call = atm_call.get_implied_vol() iv_put = atm_put.get_implied_vol() iv_3 = (iv_put + iv_call) / 2.0 iv_term_structure.append({ 'date': optionset.eval_date, 'iv1': iv, 'iv2': iv_2, 'iv3': iv_3 }) if not optionset.has_next(): break optionset.next() df_iv = pd.DataFrame(list_res_iv).sort_values( by='date', ascending=False).reset_index(drop=True) df_res.loc[:, 'N:date'] = df_iv['date'] df_res.loc[:, 'O:iv'] = df_iv['iv'] * m df_res.loc[:, 'P'] = None df_res.loc[:, 'Q'] = None df_res.loc[:, 'R'] = None df_res.loc[:, 'S'] = None df_res.loc[:, 'T'] = None print(df_iv) df = pd.DataFrame(iv_term_structure) df.to_csv('../data/' + name_code + '_iv_term_structure.csv') return df_res
class HedgeIndexByOptions(object): def __init__(self, df_baseindex, df_option_metrics, df_c1=None, df_all=None, cd_direction_timing='ma', cd_strategy='bull_spread', cd_volatility='close_std', cd_short_ma='ma_5', cd_long_ma='ma_60', cd_std='std_10'): self.min_holding = 20 self.slippage = 0 self.nbr_maturity = 0 self.moneyness_rank = 0 self.cd_trade_price = c.CdTradePrice.VOLUME_WEIGHTED # self.cd_trade_price = c.CdTradePrice.CLOSE if df_c1 is None: dt_start = max(df_option_metrics[c.Util.DT_DATE].values[0], df_baseindex[c.Util.DT_DATE].values[0]) self.end_date = min(df_option_metrics[c.Util.DT_DATE].values[-1], df_baseindex[c.Util.DT_DATE].values[-1]) df_metrics = df_option_metrics[ df_option_metrics[c.Util.DT_DATE] >= dt_start].reset_index( drop=True) df_baseindex = df_baseindex[ df_baseindex[c.Util.DT_DATE] >= dt_start].reset_index( drop=True) self.invst_portfolio = BaseInstrument( df_baseindex) # e.g., top 50 low volatility index self.invst_portfolio.init() else: dt_start = max(df_option_metrics[c.Util.DT_DATE].values[0], df_baseindex[c.Util.DT_DATE].values[0], df_c1[c.Util.DT_DATE].values[0]) self.end_date = min(df_option_metrics[c.Util.DT_DATE].values[-1], df_baseindex[c.Util.DT_DATE].values[-1], df_c1[c.Util.DT_DATE].values[-1]) df_metrics = df_option_metrics[ df_option_metrics[c.Util.DT_DATE] >= dt_start].reset_index( drop=True) df_baseindex = df_baseindex[ df_baseindex[c.Util.DT_DATE] >= dt_start].reset_index( drop=True) df_c1 = df_c1[df_c1[c.Util.DT_DATE] >= dt_start].reset_index( drop=True) df_all = df_all[df_all[c.Util.DT_DATE] >= dt_start].reset_index( drop=True) self.invst_portfolio = BaseFutureCoutinuous( df_c1, df_futures_all_daily=df_all ) # e.g., top 50 low volatility index self.invst_portfolio.init() self.optionset = BaseOptionSet(df_metrics) self.index = BaseInstrument(df_baseindex) self.optionset.init() self.index.init() self.account = BaseAccount(init_fund=c.Util.BILLION, leverage=1.0, rf=0.03) self.prepare_timing(df_baseindex) self.cd_direction_timing = cd_direction_timing self.cd_strategy = cd_strategy self.cd_volatility = cd_volatility self.cd_short_ma = cd_short_ma self.cd_long_ma = cd_long_ma self.cd_std = cd_std self.dict_strategy = {} self.nbr_timing = 0 self.nbr_stop_loss = 0 self.nvp_adjustment = 0 self.sl_npv_high_point = 1.0 self.strategy_pause = False def prepare_timing(self, df_index): df_index['ma_3'] = c.Statistics.moving_average( df_index[c.Util.AMT_CLOSE], n=3).shift() df_index['ma_5'] = c.Statistics.moving_average( df_index[c.Util.AMT_CLOSE], n=5).shift() df_index['ma_10'] = c.Statistics.moving_average( df_index[c.Util.AMT_CLOSE], n=10).shift() df_index['ma_15'] = c.Statistics.moving_average( df_index[c.Util.AMT_CLOSE], n=15).shift() df_index['ma_20'] = c.Statistics.moving_average( df_index[c.Util.AMT_CLOSE], n=20).shift() df_index['ma_30'] = c.Statistics.moving_average( df_index[c.Util.AMT_CLOSE], n=30).shift() df_index['ma_40'] = c.Statistics.moving_average( df_index[c.Util.AMT_CLOSE], n=40).shift() df_index['ma_50'] = c.Statistics.moving_average( df_index[c.Util.AMT_CLOSE], n=50).shift() df_index['ma_60'] = c.Statistics.moving_average( df_index[c.Util.AMT_CLOSE], n=60).shift() df_index['ma_120'] = c.Statistics.moving_average( df_index[c.Util.AMT_CLOSE], n=120).shift() df_index['std_5'] = c.Statistics.standard_deviation( df_index[c.Util.AMT_CLOSE], n=5).shift() df_index['std_10'] = c.Statistics.standard_deviation( df_index[c.Util.AMT_CLOSE], n=10).shift() df_index['std_15'] = c.Statistics.standard_deviation( df_index[c.Util.AMT_CLOSE], n=15).shift() df_index['std_20'] = c.Statistics.standard_deviation( df_index[c.Util.AMT_CLOSE], n=20).shift() df_index['ma_3-20'] = df_index['ma_3'] - df_index['ma_20'] self.df_timing = df_index.set_index(c.Util.DT_DATE) def open_signal(self): if self.cd_direction_timing == 'ma': return self.open_position_ma() def close_signal(self): if self.cd_direction_timing == 'ma': return self.close_position_ma() def stop_loss_beg(self, drawdown, P_mdd): if drawdown.loc[self.optionset.eval_date, c.Util.DRAWDOWN] <= P_mdd: return True def stop_loss_end(self, drawdown, P_mdd): if drawdown.loc[ self.optionset.eval_date, c.Util.PORTFOLIO_NPV] >= self.account.account[ c.Util.PORTFOLIO_NPV].values[-1] + self.nvp_adjustment: self.nvp_adjustment = drawdown.loc[ self.optionset.eval_date, c.Util.PORTFOLIO_NPV] - self.account.account[ c.Util.PORTFOLIO_NPV].values[-1] self.nbr_stop_loss += 1 print(self.optionset.eval_date, ' stop loss end ') print(self.nvp_adjustment, self.account.account[c.Util.PORTFOLIO_NPV].values[-1], drawdown.loc[self.optionset.eval_date, c.Util.PORTFOLIO_NPV]) return True def strategy(self, cd_price=c.CdPriceType.OPEN): if self.cd_strategy == 'bull_spread': if self.cd_volatility == 'close_std': dt_date = self.optionset.eval_date std_close = self.df_timing.loc[dt_date, self.cd_std] k_short = self.index.mktprice_open() - std_close put_long, put_short = self.bull_spread(k_short, cd_price) return { c.LongShort.SHORT: put_short, c.LongShort.LONG: put_long } def shift(self): if self.cd_strategy == 'bull_spread': return self.shift_bull_spread() def open_position_ma(self): dt_date = self.optionset.eval_date if dt_date not in self.df_timing.index: return False ma_5 = self.df_timing.loc[dt_date, self.cd_short_ma] ma_60 = self.df_timing.loc[dt_date, self.cd_long_ma] if ma_5 < ma_60: return True else: return False def close_position_ma(self): dt_date = self.optionset.eval_date dt_maturity = None for option in self.account.dict_holding.values(): if isinstance(option, BaseOption) and option is not None: dt_maturity = option.maturitydt() break if dt_maturity is not None and (dt_maturity - dt_date).days <= 5: return True ma_5 = self.df_timing.loc[dt_date, self.cd_short_ma] ma_60 = self.df_timing.loc[dt_date, self.cd_long_ma] if ma_5 >= ma_60: print(self.optionset.eval_date) self.nbr_timing += 1 return True else: return False def bull_spread(self, k_short, cd_price=c.CdPriceType.OPEN): maturity = self.optionset.select_maturity_date( nbr_maturity=self.nbr_maturity, min_holding=self.min_holding) xx, list_put0 = self.optionset.get_options_list_by_moneyness_mthd1( moneyness_rank=self.moneyness_rank, maturity=maturity, cd_price=cd_price) put_long = self.optionset.select_higher_volume(list_put0) # if put_long is None: # xxx, list_put0 = optionset.get_options_list_by_moneyness_mthd1(moneyness_rank=0, # maturity=maturity, # cd_price=c.CdPriceType.OPEN) # put_long = optionset.select_higher_volume(list_put0) put_short = self.optionset.select_higher_volume( self.optionset.get_option_closest_strike(c.OptionType.PUT, k_short, maturity)) if put_short is not None: if put_short.strike() >= (k_short + put_long.strike()) / 2.0: put_short = None elif put_short.id_instrument() == put_long.id_instrument(): xx, list_put1 = self.optionset.get_options_list_by_moneyness_mthd1( moneyness_rank=-1, maturity=maturity, cd_price=cd_price) put_short = self.optionset.select_higher_volume(list_put1) # put_short = None return put_long, put_short def shift_bull_spread(self): option_short = self.dict_strategy[c.LongShort.SHORT] option_long = self.dict_strategy[c.LongShort.LONG] if option_short is None: return self.strategy() else: if self.index.mktprice_last_close() <= (option_long.strike() + option_short.strike()) / 2: return self.strategy() else: return None def excute(self, dict_strategy, cd_trade_price=None): if cd_trade_price is None: cd_trade_price = self.cd_trade_price if dict_strategy is None: return for long_short in dict_strategy.keys(): option = dict_strategy[long_short] if option is None: continue elif long_short in self.dict_strategy.keys() and self.dict_strategy[long_short] is not None \ and option.id_instrument() == self.dict_strategy[long_short].id_instrument(): continue unit = self.unit_index / option.multiplier() order = self.account.create_trade_order( option, long_short, unit, cd_trade_price=cd_trade_price) record = option.execute_order(order, slippage=self.slippage) self.account.add_record(record, option) self.dict_strategy = dict_strategy def close_options(self): for option in self.account.dict_holding.values(): if isinstance(option, BaseOption): order = self.account.create_close_order( option, cd_trade_price=self.cd_trade_price) record = option.execute_order(order, slippage=self.slippage) self.account.add_record(record, option) self.dict_strategy = {} def close_out(self): close_out_orders = self.account.creat_close_out_order( cd_trade_price=c.CdTradePrice.CLOSE) for order in close_out_orders: execution_record = self.account.dict_holding[order.id_instrument] \ .execute_order(order, slippage=self.slippage, execute_type=c.ExecuteType.EXECUTE_ALL_UNITS) self.account.add_record( execution_record, self.account.dict_holding[order.id_instrument]) def back_test(self): self.unit_index = np.floor(self.account.cash / self.index.mktprice_close() / self.index.multiplier()) unit_portfolio = np.floor(self.account.cash / self.invst_portfolio.mktprice_close() / self.invst_portfolio.multiplier()) order_index = self.account.create_trade_order( self.invst_portfolio, c.LongShort.LONG, unit_portfolio, cd_trade_price=c.CdTradePrice.CLOSE) record_index = self.invst_portfolio.execute_order( order_index, slippage=self.slippage) self.account.add_record(record_index, self.invst_portfolio) empty_position = True init_index = self.index.mktprice_close() init_portfolio = self.invst_portfolio.mktprice_close() base_npv = [] index_npv = [] while self.optionset.eval_date <= self.end_date: # if self.optionset.eval_date == datetime.date(2016,5,20): # print('') # print(self.optionset.eval_date) if self.optionset.eval_date >= self.end_date: # Final close out all. close_out_orders = self.account.creat_close_out_order() for order in close_out_orders: execution_record = self.account.dict_holding[order.id_instrument] \ .execute_order(order, slippage=self.slippage, execute_type=c.ExecuteType.EXECUTE_ALL_UNITS) self.account.add_record( execution_record, self.account.dict_holding[order.id_instrument]) self.account.daily_accounting(self.optionset.eval_date) base_npv.append(self.invst_portfolio.mktprice_close() / init_portfolio) index_npv.append(self.index.mktprice_close() / init_index) print(self.optionset.eval_date, ' close out ') break if not empty_position: if self.close_signal(): self.close_options() empty_position = True else: strategy = self.shift() if strategy is not None: self.close_options() self.excute(strategy) if empty_position and self.open_signal(): self.excute(self.strategy()) empty_position = False #TODO:移仓换月 if isinstance(self.invst_portfolio, BaseFutureCoutinuous): self.invst_portfolio.shift_contract_month( self.account, self.slippage) self.account.daily_accounting(self.optionset.eval_date) self.account.account.loc[self.optionset.eval_date, 'unit_index'] = self.unit_index self.account.account.loc[ self.optionset.eval_date, 'close_index'] = self.index.mktprice_close() # print(self.optionset.eval_date,estimated_npv1,estimated_npv2,estimated_npv3,self.account.account.loc[self.optionset.eval_date,c.Util.PORTFOLIO_NPV]) base_npv.append(self.invst_portfolio.mktprice_close() / init_portfolio) index_npv.append(self.index.mktprice_close() / init_index) # print(self.invst_portfolio.eval_date, self.account.account.loc[self.optionset.eval_date,c.Util.PORTFOLIO_NPV], # self.invst_portfolio.mktprice_close() / init_index) if not self.optionset.has_next(): break self.optionset.next() self.index.next() self.invst_portfolio.next() self.account.account['base_npv'] = base_npv self.account.account['index_npv'] = index_npv # active_npv = self.df_index[self.df_index[c.Util.DT_DATE]<=self.optionset.eval_date].reset_index(drop=True) # self.account.account['active_npv'] = active_npv[c.Util.AMT_CLOSE] self.account.nbr_timing = self.nbr_timing # print(self.account.account.loc[self.invst_portfolio.eval_date,c.Util.PORTFOLIO_NPV]) return self.account def back_test_with_stop_loss(self, drawdown, P_mdd): self.P_mdd = P_mdd self.unit_index = np.floor(self.account.cash / self.index.mktprice_close() / self.index.multiplier()) order_index = self.account.create_trade_order( self.index, c.LongShort.LONG, self.unit_index, cd_trade_price=c.CdTradePrice.CLOSE) record_index = self.index.execute_order(order_index, slippage=self.slippage) self.account.add_record(record_index, self.index) empty_position = True init_index = self.index.mktprice_close() base_npv = [] stop_loss_paused = False while self.optionset.eval_date <= self.end_date: # if self.optionset.eval_date == datetime.date(2016,5,20): # print('') # print(self.optionset.eval_date) if self.optionset.eval_date >= self.end_date: # Final close out all. close_out_orders = self.account.creat_close_out_order() for order in close_out_orders: execution_record = self.account.dict_holding[order.id_instrument] \ .execute_order(order, slippage=self.slippage, execute_type=c.ExecuteType.EXECUTE_ALL_UNITS) self.account.add_record( execution_record, self.account.dict_holding[order.id_instrument]) self.account.daily_accounting(self.optionset.eval_date) base_npv.append(self.index.mktprice_close() / init_index) print(self.optionset.eval_date, ' close out ') break # Option Hedge if not stop_loss_paused: if not empty_position: if self.close_signal(): self.close_all_options() empty_position = True else: strategy = self.shift() if strategy is not None: self.close_all_options() self.excute(strategy) if empty_position and self.open_signal(): self.excute(self.strategy()) empty_position = False estimated_npv = self.account.estimate_npv() self.sl_npv_high_point = max(self.sl_npv_high_point, estimated_npv) # if self.account.account.loc[self.optionset.eval_date, c.Util.DRAWDOWN]>= 0.0: # self.P_mdd = P_mdd # 止损控制 if (estimated_npv - self.sl_npv_high_point ) / self.sl_npv_high_point < self.P_mdd: self.close_out() self.sl_npv_high_point = estimated_npv self.strategy_npv_paused = drawdown.loc[ self.optionset.eval_date, c.Util.PORTFOLIO_NPV] print(self.optionset.eval_date, 'stop loss', self.sl_npv_high_point, self.P_mdd) self.account.daily_accounting(self.optionset.eval_date, False) base_npv.append(self.index.mktprice_close() / init_index) if not self.optionset.has_next(): break self.optionset.next() self.index.next() stop_loss_paused = True empty_position = True # self.P_mdd = -0.02 continue if stop_loss_paused: strategy_npv = drawdown.loc[self.optionset.eval_date, c.Util.PORTFOLIO_NPV] # 止损后空仓 # if strategy_npv <= self.strategy_npv_paused: if (strategy_npv - self.strategy_npv_paused ) / self.strategy_npv_paused < 0.01: self.account.daily_accounting(self.optionset.eval_date) base_npv.append(self.index.mktprice_close() / init_index) if not self.optionset.has_next(): break self.optionset.next() self.index.next() continue # 止损信号解除 else: order_index = self.account.create_trade_order( self.index, c.LongShort.LONG, self.unit_index, cd_trade_price=c.CdTradePrice.CLOSE) record_index = self.index.execute_order( order_index, slippage=self.slippage) self.account.add_record(record_index, self.index) stop_loss_paused = False self.nbr_stop_loss += 1 print(self.optionset.eval_date, 'stop loss end') # if empty_position and self.open_signal(): # self.excute(self.strategy(cd_price=c.CdPriceType.CLOSE),cd_trade_price=c.CdTradePrice.CLOSE) # empty_position = False # 每日结算 self.account.daily_accounting(self.optionset.eval_date) base_npv.append(self.index.mktprice_close() / init_index) if not self.optionset.has_next(): break self.optionset.next() self.index.next() self.account.account['base_npv'] = base_npv # active_npv = self.df_index[self.df_index[c.Util.DT_DATE] <= self.optionset.eval_date].reset_index(drop=True) # self.account.account['active_npv'] = active_npv[c.Util.AMT_CLOSE] self.account.nbr_timing = self.nbr_timing self.account.nbr_stop_loss = self.nbr_stop_loss return self.account
class NakedShort(object): def __init__(self, df_metrics, df_baseindex=None, cd_strategy='naked_put', id_baseindex=c.Util.STR_INDEX_300SH, cd_marutity_days=3): self.start_date = df_metrics[c.Util.DT_DATE].values[0] self.end_date = df_metrics[c.Util.DT_DATE].values[-1] if df_baseindex is None: df_baseindex = get_data.get_index_mktdata(self.start_date, self.end_date, c.Util.STR_INDEX_300SH) self.min_holding = 20 self.init_fund = c.Util.BILLION self.slippage = 0 self.m = 1 # 期权notional倍数 if cd_strategy == 'short_straddle': self.m = 0.5 self.moneyness_rank = -4 self.nbr_maturity = 0 self.cd_trade_price = c.CdTradePrice.VOLUME_WEIGHTED self.account = BaseAccount(init_fund=c.Util.BILLION, leverage=1.0, rf=0.03) self.optionset = BaseOptionSet(df_metrics) self.optionset.init() self.index = BaseInstrument(df_baseindex) self.index.init() self.cd_strategy = cd_strategy self.cd_maturity_days = cd_marutity_days self.init_index = self.index.mktprice_close() # w.start() # TODO: 统一 check dt_first; 将base_npv写入accout def close_signal(self): dt_maturity = None for option in self.account.dict_holding.values(): if isinstance(option, BaseOption) and option is not None: dt_maturity = option.maturitydt() break # t = w.tdayscount(self.optionset.eval_date.strftime("%Y-%m-%d"), dt_maturity.strftime("%Y-%m-%d"), "").Data[0][0] t = c.QuantlibUtil.get_business_between(self.optionset.eval_date, dt_maturity) if t <= self.cd_maturity_days: # if (dt_maturity - self.optionset.eval_date).days <= self.cd_maturity_days: return True else: return False def close_out(self): close_out_orders = self.account.creat_close_out_order( cd_trade_price=c.CdTradePrice.CLOSE) for order in close_out_orders: execution_record = self.account.dict_holding[order.id_instrument] \ .execute_order(order, slippage=self.slippage, execute_type=c.ExecuteType.EXECUTE_ALL_UNITS) self.account.add_record( execution_record, self.account.dict_holding[order.id_instrument]) def close_all_options(self): for option in self.account.dict_holding.values(): if isinstance(option, BaseOption): order = self.account.create_close_order( option, cd_trade_price=self.cd_trade_price) record = option.execute_order(order, slippage=self.slippage) self.account.add_record(record, option) def strategy(self): if self.cd_strategy == 'short_straddle': return self.short_straddle() elif self.cd_strategy == 'short_put': return self.short_put() elif self.cd_strategy == 'short_call': return self.short_call() def short_straddle(self): maturity1 = self.optionset.select_maturity_date( nbr_maturity=self.nbr_maturity, min_holding=self.min_holding) list_atm_call, list_atm_put = self.optionset.get_options_list_by_moneyness_mthd1( moneyness_rank=self.moneyness_rank, maturity=maturity1, cd_price=c.CdPriceType.OPEN) atm_call = self.optionset.select_higher_volume(list_atm_call) atm_put = self.optionset.select_higher_volume(list_atm_put) if atm_call is None or atm_put is None: return else: return [atm_call, atm_put] def short_put(self): maturity1 = self.optionset.select_maturity_date( nbr_maturity=self.nbr_maturity, min_holding=self.min_holding) list_atm_call, list_atm_put = self.optionset.get_options_list_by_moneyness_mthd1( moneyness_rank=self.moneyness_rank, maturity=maturity1, cd_price=c.CdPriceType.OPEN) atm_put = self.optionset.select_higher_volume(list_atm_put) if atm_put is None: return else: return [atm_put] def short_call(self): maturity1 = self.optionset.select_maturity_date( nbr_maturity=self.nbr_maturity, min_holding=self.min_holding) list_atm_call, list_atm_put = self.optionset.get_options_list_by_moneyness_mthd1( moneyness_rank=self.moneyness_rank, maturity=maturity1, cd_price=c.CdPriceType.OPEN) atm_call = self.optionset.select_higher_volume(list_atm_call) if atm_call is None: return else: return [atm_call] def excute(self, strategy): if strategy is None: return True else: pv = self.account.portfolio_total_value for option in strategy: unit = np.floor( np.floor(pv / option.strike()) / option.multiplier()) * self.m order = self.account.create_trade_order( option, c.LongShort.SHORT, unit, cd_trade_price=self.cd_trade_price) record = option.execute_order(order, slippage=self.slippage) self.account.add_record(record, option) return False def back_test(self): empty_position = True index_npv = [] while self.optionset.eval_date <= self.end_date: # print(optionset.eval_date) # if self.account.cash <= 0: break if self.optionset.eval_date >= self.end_date: # Final close out all. self.close_out() self.account.daily_accounting(self.optionset.eval_date) index_npv.append(self.index.mktprice_close() / self.init_index) break # 平仓 if not empty_position and self.close_signal(): self.close_all_options() empty_position = True # 开仓 if empty_position: empty_position = self.excute(self.strategy()) self.account.daily_accounting(self.optionset.eval_date) index_npv.append(self.index.mktprice_close() / self.init_index) # print(self.optionset.eval_date,self.account.account.loc[self.optionset.eval_date,c.Util.PORTFOLIO_NPV]) if not self.optionset.has_next(): break self.optionset.next() self.index.next() self.account.account['index_npv'] = index_npv
class VolTrading(object): def __init__(self, start_date, end_date, df_metrics, df_vol, df_future_c1_daily, df_futures_all_daily): self.min_holding = 20 self.slippage = 0 self.nbr_maturity = 0 self.moneyness_rank = 0 self.m_notional = 1 self.rf = 0.03 self.n_premium_std = 90 #用于计算权利金溢价1倍标准差的数据期限 self.n_hv = 20 # 用与期权期限相匹配的历史波动率期限 self.n_close_by_maturity = 5 self.min_premium = 2.0 / 100.0 # 对冲成本对应的开平仓最低隐含波动率溢价 self.n_llt = 5 self.cd_option_price = c.CdTradePrice.CLOSE self.cd_future_price = c.CdTradePrice.CLOSE self.cd_price = c.CdPriceType.CLOSE self.start_date = start_date self.end_date = end_date self.df_metrics = df_metrics self.df_vol = df_vol self.df_f_c1 = df_future_c1_daily self.df_f_all = df_futures_all_daily def init(self): df_future_histvol = self.df_f_c1.copy() df_future_histvol['amt_hv'] = histvol.hist_vol( df_future_histvol[c.Util.AMT_CLOSE], n=self.n_hv) self.dt_start = max(self.df_f_c1[c.Util.DT_DATE].values[0], self.df_metrics[c.Util.DT_DATE].values[0]) self.end_date = min(self.df_f_c1[c.Util.DT_DATE].values[-1], self.df_metrics[c.Util.DT_DATE].values[-1]) self.df_metrics = self.df_metrics[ self.df_metrics[c.Util.DT_DATE] >= self.dt_start].reset_index( drop=True) self.df_f_c1 = self.df_f_c1[ self.df_f_c1[c.Util.DT_DATE] >= self.dt_start].reset_index( drop=True) self.df_f_all = self.df_f_all[ self.df_f_all[c.Util.DT_DATE] >= self.dt_start].reset_index( drop=True) self.df_vol = pd.merge(self.df_vol, df_future_histvol[[c.Util.DT_DATE, 'amt_hv']], on=c.Util.DT_DATE).set_index(c.Util.DT_DATE) self.optionset = BaseOptionSet(self.df_metrics) self.optionset.init() self.hedging = SytheticOption(self.df_f_c1, rf=self.rf, df_futures_all_daily=self.df_f_all) self.hedging.init() self.hedging.amt_option = 1 / 1000 # 50ETF与IH点数之比 self.account = BaseAccount(init_fund=c.Util.BILLION, rf=self.rf) self.prepare_timing() def prepare_timing(self): self.timing_hviv() self.timing_llt() def timing_hviv(self): self.df_vol['amt_premium'] = self.df_vol[ c.Util.PCT_IMPLIED_VOL] - self.df_vol['amt_hv'] self.df_vol['amt_1std'] = c.Statistics.standard_deviation( self.df_vol['amt_premium'], n=self.n_premium_std) self.df_vol['amt_2std'] = 2 * c.Statistics.standard_deviation( self.df_vol['amt_premium'], n=self.n_premium_std) self.df_vol['percentile_95'] = c.Statistics.percentile( self.df_vol[c.Util.PCT_IMPLIED_VOL], n=252, percent=0.95) def timing_llt(self): self.df_vol['LLT_' + str(self.n_llt)] = LLT( self.df_vol[c.Util.PCT_IMPLIED_VOL], self.n_llt) self.df_vol['LLT_signal_' + str(self.n_llt)] = self.df_vol['LLT_' + str(self.n_llt)].diff() def open_signal(self): pass def close_signal(self): pass def open_signal_llt(self): dt_date = self.optionset.eval_date if dt_date not in self.df_vol.index: return False if self.df_vol.loc[dt_date, 'LLT_signal_5'] < 0: # 隐含波动率处于下行区间 return True else: return False def close_signal_llt(self): dt_date = self.optionset.eval_date if dt_date not in self.df_vol.index: return False if self.df_vol.loc[dt_date, 'LLT_signal_5'] > 0: # 隐含波动率处于上行区间 return True else: return False def open_signal_ivhv(self): dt_date = self.optionset.eval_date if dt_date not in self.df_vol.index: return False amt_premium = self.df_vol.loc[dt_date, 'amt_premium'] amt_1std = self.df_vol.loc[dt_date, 'amt_1std'] if amt_premium > amt_1std and amt_premium > self.min_premium: # 隐含波动率相比历史波动率具有一定溢价 return True else: return False def close_signal_ivhv(self): dt_date = self.optionset.eval_date amt_premium = self.df_vol.loc[dt_date, 'amt_premium'] if amt_premium <= self.min_premium: return True else: return False def close_signal_maturity(self): dt_maturity = None for option in self.account.dict_holding.values(): if isinstance(option, BaseOption) and option is not None: dt_maturity = option.maturitydt() break if (dt_maturity - self.optionset.eval_date).days <= self.n_close_by_maturity: return True else: return False def strategy(self): return self.short_straddle() def short_straddle(self): maturity = self.optionset.select_maturity_date( nbr_maturity=self.nbr_maturity, min_holding=self.min_holding) list_atm_call, list_atm_put = self.optionset.get_options_list_by_moneyness_mthd1( moneyness_rank=self.moneyness_rank, maturity=maturity, cd_price=self.cd_price) atm_call = self.optionset.select_higher_volume(list_atm_call) atm_put = self.optionset.select_higher_volume(list_atm_put) if atm_call is None or atm_put is None: return else: return [atm_call, atm_put] def excute(self, strategy): if strategy is None: return False else: pv = self.account.portfolio_total_value self.option_holding = {} for option in strategy: unit = np.floor( np.floor(pv / option.strike()) / option.multiplier()) * self.m_notional order = self.account.create_trade_order( option, c.LongShort.SHORT, unit, cd_trade_price=self.cd_option_price) record = option.execute_order(order, slippage_rate=self.slippage) self.account.add_record(record, option) self.option_holding.update({option: unit}) return True def close_out(self): close_out_orders = self.account.creat_close_out_order( cd_trade_price=c.CdTradePrice.CLOSE) for order in close_out_orders: execution_record = self.account.dict_holding[order.id_instrument] \ .execute_order(order, slippage_rate=self.slippage, execute_type=c.ExecuteType.EXECUTE_ALL_UNITS) self.account.add_record( execution_record, self.account.dict_holding[order.id_instrument]) def close_out_1(self): for option in self.account.dict_holding.values(): if isinstance(option, BaseOption): order = self.account.create_close_order( option, cd_trade_price=self.cd_option_price) else: order = self.account.create_close_order( option, cd_trade_price=self.cd_future_price) record = option.execute_order(order, slippage_rate=self.slippage) self.account.add_record(record, option) # TODO: Use Delta Bound Model def delta_hedge(self): option1 = list(self.option_holding.keys())[0] iv_htbr = self.optionset.get_iv_by_otm_iv_curve( dt_maturity=option1.maturitydt(), strike=option1.applicable_strike()) options_delta = 0 for option in self.option_holding.keys(): unit = self.option_holding[option] options_delta += unit * option.get_delta( iv_htbr) * option.multiplier() hedge_unit = self.hedging.get_hedge_rebalancing_unit( options_delta, c.BuyWrite.WRITE) self.hedging.synthetic_unit += -hedge_unit if hedge_unit > 0: long_short = c.LongShort.LONG else: long_short = c.LongShort.SHORT order_u = self.account.create_trade_order( self.hedging, long_short, hedge_unit, cd_trade_price=self.cd_future_price) record_u = self.hedging.execute_order(order_u, slippage_rate=self.slippage) self.account.add_record(record_u, self.hedging) # print('') def back_test(self): empty_position = True while self.optionset.eval_date <= self.end_date: # if self.optionset.eval_date == datetime.date(2017, 1, 19): # print('') if self.optionset.eval_date >= self.end_date: # Final close out all. self.close_out() self.account.daily_accounting(self.optionset.eval_date) print(self.optionset.eval_date, ' close out ') print( self.optionset.eval_date, self.hedging.eval_date, self.account.account.loc[self.optionset.eval_date, c.Util.PORTFOLIO_NPV], int(self.account.cash)) break # 标的移仓换月 if self.hedging.close_old_contract_month( self.account, self.slippage, cd_price=self.cd_future_price): self.hedging.synthetic_unit = 0 # 平仓 if not empty_position: if self.close_signal(): self.close_out_1() self.hedging.synthetic_unit = 0 empty_position = True # 开仓 if empty_position and self.open_signal(): s = self.strategy() empty_position = not self.excute(s) # Delta hedge if not empty_position: self.delta_hedge() self.account.daily_accounting(self.optionset.eval_date) if not self.optionset.has_next(): break self.optionset.next() self.hedging.next() return self.account