示例#1
0
 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()
示例#2
0
 def __init__(self, df_index=None, df_future_c1=None, df_future_all=None):
     if df_index is not None:
         self.df_base = df_index
         self.base = BaseInstrument(df_index)
     else:
         self.df_base = df_future_c1
         self.base = BaseFutureCoutinuous(
             df_future_c1=df_future_c1, df_futures_all_daily=df_future_all)
     self.base.init()
     self.account = BaseAccount(c.Util.BILLION * 10)
     self._prepare_data()
     self.leverage = 0.75  # Will be multiplied to delta equivalent position
     self.rf = 0.03
     self.slippage_date = 1 / 1000.0
     self.hedge_multiplier = 1
     self.hold_unit = 0
     self.target_option = None
     self.cd_model = 'ww'  # Delta调仓模型
     self.delta_criterian = 0.1  # 固定delta变化值调仓条件
     self.delta_last_rebalanced = 0.0
     self.ttm = 50  # 期权固定期限
     self.k = 1.1  # 期权行权价比率
     self.H = None
     self.delta_upper_bound = 0.99
     self.flag_fix_ttm = True
     self.rho = 0.01
示例#3
0
 def __init__(self, df_c1, df_all, df_baseindex):
     self.slippage = 0
     self.cd_trade_price = c.CdTradePrice.VOLUME_WEIGHTED
     dt_start = max(df_baseindex[c.Util.DT_DATE].values[0],
                    df_c1[c.Util.DT_DATE].values[0])
     self.end_date = min(df_baseindex[c.Util.DT_DATE].values[-1],
                         df_c1[c.Util.DT_DATE].values[-1])
     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.index = BaseInstrument(df_baseindex)
     self.index.init()
     self.account = BaseAccount(init_fund=c.Util.BILLION,
                                leverage=1.0,
                                rf=0.03)
    def __init__(self, start_date, end_date):
        self.ttm = 30
        self.buywrite = BuyWrite.BUY
        self.fund = Util.BILLION
        self.invest_underlying_ratio = 0.7
        # self.invest_underlying_ratio = 1
        self.slippage = 2
        self.start_date = start_date
        self.end_date = end_date
        hist_date = start_date - datetime.timedelta(days=40)
        df_future_c1 = get_dzqh_cf_c1_minute(start_date, end_date, 'if')
        df_future_c1_daily = get_dzqh_cf_c1_daily(hist_date, end_date, 'if')
        df_futures_all_daily = get_dzqh_cf_daily(
            start_date, end_date, 'if')  # daily data of all future contracts
        df_index = get_index_mktdata(
            start_date, end_date,
            'index_300sh')  # daily data of underlying index
        df_index = df_index[df_index[Util.DT_DATE].isin(
            Util.DZQH_CF_DATA_MISSING_DATES) == False].reset_index(drop=True)
        # df_index.to_csv('df_index.csv')
        self.trade_dates = sorted(df_future_c1_daily[Util.DT_DATE].unique())
        self.df_vol_1m = Histvol.hist_vol(df_future_c1_daily)
        # df_parkinson_1m = Histvol.parkinson_number(df_future_c1_daily)
        self.df_garman_klass = Histvol.garman_klass(df_future_c1_daily)
        # df_hist_vol = self.df_vol_1m.join(self.df_garman_klass, how='left')
        # df_hist_vol.to_csv('../../data/df_hist_vol.csv')
        self.underlying = BaseInstrument(df_data=df_index)
        self.underlying.init()
        self.synthetic_option = SytheticOption(
            df_c1_data=df_future_c1,
            # df_c1_daily=df_future_c1_daily,
            df_futures_all_daily=df_futures_all_daily,
            df_index_daily=df_index)
        self.synthetic_option.init()

        self.account = BaseAccount(self.fund, leverage=20.0, rf=0.0)
        self.trading_desk = Trade()
        self.init_spot = self.synthetic_option.underlying_state_daily[
            Util.AMT_CLOSE]
        self.df_analysis = pd.DataFrame()
示例#5
0
 def init(self):
     self.underlying = None
     self.futureset = None
     self.baseindex = None
     self.optionset = BaseOptionSet(self.df_option)
     self.optionset.init()
     if self.name_code == c.Util.STR_50ETF:
         if self.df_etf is not None:
             self.underlying = BaseInstrument(self.df_etf) # 50ETF
             self.underlying.init()
         if self.df_future_all is not None:
             self.futureset = BaseFutureSet(self.df_future_all) # IH
             self.futureset.init()
             self.future_unit_ratio = 1/1000.0
         if self.df_index is not None:
             self.baseindex = BaseInstrument(self.df_index) # SH50
             self.baseindex.init()
     else: # 商品期权
         if self.df_future_all is not None:
             self.futureset = BaseFutureSet(self.df_future_all) # IH
             self.futureset.init()
             self.future_unit_ratio = 1.0
示例#6
0
class ParityArbitrage(object):
    def __init__(self,name_code, df_option, df_etf=None, df_future_all=None,df_index=None):
        self.name_code = name_code
        self.df_option = df_option
        self.df_etf = df_etf
        self.df_future_all = df_future_all
        self.df_index = df_index
        self.rf = 0.03
        self.m = 0.9
        self.account = BaseAccount(c.Util.BILLION / 10,rf=self.rf)
        self.unit = 50
        self.min_holding = 6 # 50ETF与IH到期日相差5天
        self.nbr_maturity = 0
        self.rank = 3
        self.slippage = 0
        self.aggregate_costs = 0.5/100.0
        self.cd_price = c.CdTradePrice.CLOSE
        self.df_arbitrage_window = pd.DataFrame()

    def init(self):
        self.underlying = None
        self.futureset = None
        self.baseindex = None
        self.optionset = BaseOptionSet(self.df_option)
        self.optionset.init()
        if self.name_code == c.Util.STR_50ETF:
            if self.df_etf is not None:
                self.underlying = BaseInstrument(self.df_etf) # 50ETF
                self.underlying.init()
            if self.df_future_all is not None:
                self.futureset = BaseFutureSet(self.df_future_all) # IH
                self.futureset.init()
                self.future_unit_ratio = 1/1000.0
            if self.df_index is not None:
                self.baseindex = BaseInstrument(self.df_index) # SH50
                self.baseindex.init()
        else: # 商品期权
            if self.df_future_all is not None:
                self.futureset = BaseFutureSet(self.df_future_all) # IH
                self.futureset.init()
                self.future_unit_ratio = 1.0
            # self.optionset = BaseOptionSet(self.df_option)
            # self.optionset.init()

    def update_sythetics(self):
        if self.name_code == c.Util.STR_50ETF:
            dt_maturity = self.optionset.select_maturity_date(nbr_maturity=self.nbr_maturity, min_holding=self.min_holding)
            contract_month = self.optionset.get_dict_options_by_maturities()[dt_maturity][0].contract_month()

            self.t_quote = self.optionset.get_T_quotes(dt_maturity, self.cd_price)
            self.t_quote.loc[:, 'diff'] = abs(
                self.t_quote.loc[:, c.Util.AMT_APPLICABLE_STRIKE] - self.t_quote.loc[:, c.Util.AMT_UNDERLYING_CLOSE])
            self.t_quote.loc[:, 'rank'] = self.t_quote.index - self.t_quote['diff'].idxmin()
            discount = c.PricingUtil.get_discount(self.optionset.eval_date, dt_maturity, self.rf)
            self.t_quote.loc[:, 'sythetic_underlying'] = self.t_quote.loc[:, c.Util.AMT_CALL_QUOTE] \
                                                    - self.t_quote.loc[:,c.Util.AMT_PUT_QUOTE] \
                                                    + self.t_quote.loc[:,c.Util.AMT_APPLICABLE_STRIKE] * discount
            df_window = self.t_quote[(self.t_quote['rank']<=self.rank)&(self.t_quote['rank']>=-self.rank)] # 只考虑rank以内期权
            self.row_max_sythetic = df_window.loc[df_window['sythetic_underlying'].idxmax()]
            self.row_min_sythetic = df_window.loc[df_window['sythetic_underlying'].idxmin()]
            self.df_arbitrage_window.loc[self.optionset.eval_date,'50etf'] = self.underlying.mktprice_close()
            self.df_arbitrage_window.loc[self.optionset.eval_date,'sythetic_underlying_max'] = self.row_max_sythetic['sythetic_underlying']
            self.df_arbitrage_window.loc[self.optionset.eval_date,'sythetic_underlying_min'] = self.row_min_sythetic['sythetic_underlying']
            if self.futureset is not None:
                future = self.futureset.select_future_by_contract_month(contract_month)
                self.row_max_sythetic['future'] = future
                if future is None:
                    return
                self.basis_to_etf = future.mktprice_close() - self.underlying.mktprice_close()/self.future_unit_ratio
                self.df_arbitrage_window.loc[self.optionset.eval_date, 'basis_to_etf'] = self.basis_to_etf
                self.df_arbitrage_window.loc[self.optionset.eval_date, 'ih'] = future.mktprice_close()
                if self.baseindex is not None:
                    self.basis_to_index = future.mktprice_close() - self.baseindex.mktprice_close()
                    self.tracking_error = self.underlying.mktprice_close()/self.future_unit_ratio - self.baseindex.mktprice_close()
                    self.df_arbitrage_window.loc[self.optionset.eval_date, 'basis_to_index'] = self.basis_to_index
                    self.df_arbitrage_window.loc[self.optionset.eval_date, 'tracking_error'] = self.tracking_error
                    self.df_arbitrage_window.loc[self.optionset.eval_date, 'index_50'] = self.baseindex.mktprice_close()
        else:
            dt_maturity = self.optionset.select_maturity_date(nbr_maturity=self.nbr_maturity, min_holding=self.min_holding)
            contract_month = self.optionset.get_dict_options_by_maturities()[dt_maturity][0].contract_month()
            self.t_quote = self.optionset.get_T_quotes(dt_maturity, self.cd_price)
            self.t_quote.loc[:, 'diff'] = abs(
                self.t_quote.loc[:, c.Util.AMT_APPLICABLE_STRIKE] - self.t_quote.loc[:, c.Util.AMT_UNDERLYING_CLOSE])
            self.t_quote.loc[:, 'rank'] = self.t_quote.index - self.t_quote['diff'].idxmin()
            discount = c.PricingUtil.get_discount(self.optionset.eval_date, dt_maturity, self.rf)
            self.t_quote.loc[:, 'sythetic_underlying'] = self.t_quote.loc[:, c.Util.AMT_CALL_QUOTE] \
                                                    - self.t_quote.loc[:,c.Util.AMT_PUT_QUOTE] \
                                                    + self.t_quote.loc[:,c.Util.AMT_APPLICABLE_STRIKE] * discount
            df_window = self.t_quote[(self.t_quote['rank']<=self.rank)&(self.t_quote['rank']>=-self.rank)] # 只考虑rank以内期权
            self.row_max_sythetic = df_window.loc[df_window['sythetic_underlying'].idxmax()]
            self.row_min_sythetic = df_window.loc[df_window['sythetic_underlying'].idxmin()]
            future = self.futureset.select_future_by_contract_month(contract_month)
            self.underlying = future
            self.df_arbitrage_window.loc[self.optionset.eval_date, 'underlying'] = self.underlying.mktprice_close()
            self.df_arbitrage_window.loc[self.optionset.eval_date, 'sythetic_underlying_max'] = self.row_max_sythetic[
                'sythetic_underlying']
            self.df_arbitrage_window.loc[self.optionset.eval_date, 'sythetic_underlying_min'] = self.row_min_sythetic[
                'sythetic_underlying']


    def open_signal(self,cd_strategy):
        if cd_strategy == 'box':
            if (self.row_max_sythetic['sythetic_underlying'] - self.row_min_sythetic['sythetic_underlying'])/self.underlying.mktprice_close() > self.aggregate_costs:
                df = pd.DataFrame(columns=['dt_date','id_instrument','base_instrument','long_short'])
                df = self.short_sythetic(df)
                df = self.long_sythetic(df)
                return df
            else:
                return None
        elif cd_strategy == 'conversion': # Converion : Short Sythetic, Long ETF
            if (self.row_max_sythetic['sythetic_underlying'] - self.underlying.mktprice_close())/self.underlying.mktprice_close() > self.aggregate_costs:
                df = pd.DataFrame(columns=['dt_date','id_instrument','base_instrument','long_short'])
                df = self.short_sythetic(df)
                df = self.long_etf(df)
                return df
            else:
                return None
        elif cd_strategy == 'conversion_ih': # Converion : Short Sythetic, Long IH # 主要布局IH负基差套利
            if self.optionset.eval_date.month ==5: return None #5月由于股票集中现金分红不做空Synthetic
            future = self.row_max_sythetic['future']
            if future is None: return None
            self.future = future
            if (self.row_max_sythetic['sythetic_underlying']/self.future_unit_ratio - future.mktprice_close()-
                    self.df_arbitrage_window.loc[self.optionset.eval_date,'tracking_error'])/future.mktprice_close() > self.aggregate_costs:
                df = pd.DataFrame(columns=['dt_date', 'id_instrument', 'base_instrument', 'long_short'])
                df = self.short_sythetic(df)
                df = self.long_ih(df,future)
                return df
            else:
                return None
        elif cd_strategy == 'ih_basis_arbitrage':
            future = self.row_max_sythetic['future']
            if future is None: return None
            self.future = future
            if self.df_arbitrage_window.loc[self.optionset.eval_date, 'basis_to_index'] < -self.aggregate_costs:  # 期货贴水
                df = pd.DataFrame(columns=['dt_date', 'id_instrument', 'base_instrument', 'long_short'])
                df = self.short_sythetic(df)
                df = self.long_ih(df,future)
                return df
            else:
                return None
        elif cd_strategy == 'may_effect': # Reverse: Long Sythetic, Short IH # 5-9月分红期
            if self.optionset.eval_date.month ==5: #5月由于股票集中现金分红不做空Synthetic
                df = pd.DataFrame(columns=['dt_date', 'id_instrument', 'base_instrument', 'long_short'])
                df = self.long_sythetic(df)
                df = self.short_ih(df)
                return df
            else:
                return None

    def close_signal(self,cd_strategy,df_position):
        if cd_strategy == 'box':
            if self.reverse_call.maturitydt() == self.optionset.eval_date or self.conversion_call.maturitydt() == self.optionset.eval_date :
                return True
            discount_r = c.PricingUtil.get_discount(self.optionset.eval_date, self.reverse_put.maturitydt(), self.rf)
            discount_c = c.PricingUtil.get_discount(self.optionset.eval_date, self.conversion_put.maturitydt(), self.rf)
            reverse_sythetic = self.reverse_call.mktprice_close()-self.reverse_put.mktprice_close()+self.reverse_put.applicable_strike()*discount_r # Longed
            conversion_sythetic = self.conversion_call.mktprice_close()-self.conversion_put.mktprice_close()+self.conversion_put.applicable_strike()*discount_c # shorted
            if conversion_sythetic <= reverse_sythetic:
                return True
            else:
                return False
        elif cd_strategy == 'conversion': # Short Sythetic, Long Underlying # 主要布局IH负基差套利
            if self.conversion_call.maturitydt() == self.optionset.eval_date:
                return True
            discount_c = c.PricingUtil.get_discount(self.optionset.eval_date, self.conversion_put.maturitydt(), self.rf)
            conversion_sythetic = self.conversion_call.mktprice_close() - self.conversion_put.mktprice_close() + self.conversion_put.applicable_strike() * discount_c  # shorted
            if conversion_sythetic/self.future_unit_ratio <= self.underlying.mktprice_close():
                return True
            else:
                return False
        elif cd_strategy == 'conversion_ih': # Short Sythetic, Long IH # 主要布局IH负基差套利
            if self.conversion_call.maturitydt() == self.optionset.eval_date or self.future.maturitydt() == self.optionset.eval_date:
                return True
            discount_c = c.PricingUtil.get_discount(self.optionset.eval_date, self.conversion_put.maturitydt(), self.rf)
            conversion_sythetic = self.conversion_call.mktprice_close() - self.conversion_put.mktprice_close() + self.conversion_put.applicable_strike() * discount_c  # shorted
            if conversion_sythetic/self.future_unit_ratio <= self.future.mktprice_close():
                return True
            else:
                return False
        elif cd_strategy == 'ih_basis_arbitrage':
            if self.df_arbitrage_window.loc[self.optionset.eval_date, 'basis_to_index'] >= 0:
                return True
            else:
                return False
        elif cd_strategy == 'may_effect': # Reverse: Long Sythetic, Short IH # 5-9月分红期
            if self.optionset.eval_date.month !=5: #5月由于股票集中现金分红不做空Synthetic
                return True
            else:
                return False

    def open_excute(self,open_signal):
        if open_signal is None:
            return False
        else:
            fund_per_unit = open_signal['fund_requirement'].sum()
            unit = np.floor(self.account.cash*self.m/fund_per_unit)
            for (idx,row) in open_signal.iterrows():
                option = row['base_instrument']
                order = self.account.create_trade_order(option, row['long_short'], unit*row['unit_ratio'],
                                                        cd_trade_price=self.cd_price)
                record = option.execute_order(order, slippage=self.slippage)
                self.account.add_record(record, option)
            print(self.optionset.eval_date, ' open position')
            return True


    def close_excute(self):
        self.close_out()
        print(self.optionset.eval_date, ' close position')
        return True
        # else:
        #     for (idx,row) in close_signal.iterrows():
        #         option = row['base_instrument']
        #         order = self.account.create_trade_order(option, row['long_short'], row['unit'],
        #                                                 cd_trade_price=self.cd_price)
        #         record = option.execute_order(order, slippage=self.slippage)
        #         self.account.add_record(record, option)
        #     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=self.slippage, execute_type=c.ExecuteType.EXECUTE_ALL_UNITS)
            self.account.add_record(execution_record, self.account.dict_holding[order.id_instrument])


    def short_sythetic(self,df):
        # Short Sythetic
        self.conversion_call = self.optionset.get_baseoption_by_id(self.row_max_sythetic[c.Util.ID_CALL])
        fund_requirement = self.conversion_call.get_fund_required(c.LongShort.SHORT)
        df = df.append({'dt_date': self.optionset.eval_date,
                        'cd_posiiton': 'C_call',
                        'id_instrument': self.row_max_sythetic[c.Util.ID_CALL],
                        'base_instrument': self.conversion_call,
                        'long_short': c.LongShort.SHORT,
                        'fund_requirement': fund_requirement,
                        'cashflow_t0': self.conversion_call.mktprice_close() * self.conversion_call.multiplier(),
                        'unit_ratio' : 1},
                       ignore_index=True)
        self.conversion_put = self.optionset.get_baseoption_by_id(self.row_max_sythetic[c.Util.ID_PUT])
        fund_requirement = self.conversion_put.get_fund_required(c.LongShort.LONG)
        df = df.append({'dt_date': self.optionset.eval_date,
                        'cd_posiiton': 'C_put',
                        'id_instrument': self.row_max_sythetic[c.Util.ID_PUT],
                        'base_instrument': self.conversion_put,
                        'long_short': c.LongShort.LONG,
                        'fund_requirement': fund_requirement,
                        'cashflow_t0': -self.conversion_put.mktprice_close() * self.conversion_put.multiplier(),
                        'unit_ratio' : 1},
                       ignore_index=True)
        return df

    def long_sythetic(self,df):
        # Reverse : Long Sythetic
        self.reverse_call = self.optionset.get_baseoption_by_id(self.row_min_sythetic[c.Util.ID_CALL])
        fund_requirement = self.reverse_call.get_fund_required(c.LongShort.LONG)
        df = df.append({'dt_date': self.optionset.eval_date,
                        'cd_posiiton': 'R_call',
                        'id_instrument': self.row_min_sythetic[c.Util.ID_CALL],
                        'base_instrument': self.reverse_call,
                        'long_short': c.LongShort.LONG,
                        'fund_requirement': fund_requirement,
                        'cashflow_t0': -self.reverse_call.mktprice_close() * self.reverse_call.multiplier()},
                       ignore_index=True)
        self.reverse_put = self.optionset.get_baseoption_by_id(self.row_min_sythetic[c.Util.ID_PUT])
        fund_requirement = self.reverse_put.get_fund_required(c.LongShort.SHORT)
        df = df.append({'dt_date': self.optionset.eval_date,
                        'cd_posiiton': 'R_put',
                        'id_instrument': self.row_min_sythetic[c.Util.ID_PUT],
                        'base_instrument': self.reverse_put,
                        'long_short': c.LongShort.SHORT,
                        'fund_requirement': fund_requirement,
                        'cashflow_t0': self.reverse_put.mktprice_close() * self.reverse_put.multiplier()},
                       ignore_index=True)
        return df

    def long_etf(self,df):
        unit_ratio = self.conversion_put.multiplier()
        fund_requirement = self.conversion_put.multiplier() * self.underlying.mktprice_close()
        df = df.append({'dt_date': self.optionset.eval_date,
                        'cd_posiiton': 'underlying',
                        'id_instrument': self.underlying.id_instrument(),
                        'base_instrument': self.underlying,
                        'long_short': c.LongShort.LONG,
                        'fund_requirement': fund_requirement,
                        'cashflow_t0': -self.underlying.mktprice_close() * self.underlying.multiplier()*unit_ratio,
                        'unit_ratio' : unit_ratio},
                       ignore_index=True)
        return df

    def long_ih(self,df,future):
        unit_ratio = self.conversion_put.multiplier()/future.multiplier()/1000.0
        fund_requirement = future.mktprice_close()*future.multiplier()*unit_ratio
        df = df.append({'dt_date': self.optionset.eval_date,
                        'cd_posiiton': 'underlying',
                        'id_instrument': future.id_instrument(),
                        'base_instrument': future,
                        'long_short': c.LongShort.LONG,
                        'fund_requirement': fund_requirement,
                        'cashflow_t0': -future.mktprice_close() * future.multiplier()*unit_ratio,
                        'unit_ratio' : unit_ratio},
                       ignore_index=True)
        return df

    def short_ih(self,df):
        unit_ratio = self.reverse_put.multiplier()/self.future.multiplier()/1000.0
        fund_requirement = self.future.mktprice_close()*self.future.multiplier()*unit_ratio
        df = df.append({'dt_date': self.optionset.eval_date,
                        'cd_posiiton': 'underlying',
                        'id_instrument': self.future.id_instrument(),
                        'base_instrument': self.future,
                        'long_short': c.LongShort.SHORT,
                        'fund_requirement': fund_requirement,
                        'cashflow_t0': -self.future.mktprice_close() * self.future.multiplier()*unit_ratio,
                        'unit_ratio' : unit_ratio},
                       ignore_index=True)
        return df


    def back_test(self, cd_strategy):
        # TODO: DIVIDEND
        empty_position = True
        df_position = None
        while self.optionset.has_next():
            # if self.optionset.eval_date == datetime.date(2015,5,18):
            #     print('')
            self.update_sythetics()
            if empty_position:
                df_position = self.open_signal(cd_strategy)
                empty_position = not self.open_excute(df_position)
            elif self.close_signal(cd_strategy,df_position):
                empty_position = self.close_excute()
            # if isinstance(self.underlying,BaseFutureCoutinuous):
            #     self.underlying.shift_contract_month(self.account,self.slippage)
            self.account.daily_accounting(self.optionset.eval_date)
            self.optionset.next()
            self.underlying.next()
            if self.futureset is not None: self.futureset.next()
            if self.baseindex is not None: self.baseindex.next()
            # print(self.optionset.eval_date)
        return self.account

    def back_test_comdty(self, cd_strategy):
        empty_position = True
        df_position = None
        while self.optionset.has_next():
            # if self.optionset.eval_date == datetime.date(2015,5,18):
            #     print('')
            self.update_sythetics()
            if empty_position:
                df_position = self.open_signal(cd_strategy)
                empty_position = not self.open_excute(df_position)
            elif self.close_signal(cd_strategy,df_position):
                empty_position = self.close_excute()
            # if isinstance(self.underlying,BaseFutureCoutinuous):
            #     self.underlying.shift_contract_month(self.account,self.slippage)
            self.account.daily_accounting(self.optionset.eval_date)
            self.optionset.next()
            self.futureset.next()
            # if self.baseindex is not None: self.baseindex.next()
            # print(self.optionset.eval_date)
        return self.account
示例#7
0
list_pct_option_amt = []

for moneyness in [0, -1, -2, -3, -4]:
    # for moneyness in [-1, -2, -3]:
    # for moneyness in [-3]:
    dict_annualized_yield = {'m': moneyness}
    dict_annualized_volatility = {'m': moneyness}
    dict_max_drawdown = {'m': moneyness}
    dict_sharpe_ratio = {'m': moneyness}
    dict_pct_option_amt = {'m': moneyness}
    # for target_delta in [-0.1,-0.2,-0.3,-0.4]:
    for target_delta in [None]:
        option_amt = []
        optionset = BaseOptionSet(df_metrics)
        optionset.init()
        underlying = BaseInstrument(df_underlying)
        underlying.init()
        account = BaseAccount(init_fund=10000000, leverage=1.0, rf=0.03)
        """ init open position """
        unit_underlying = np.floor(pct_underlying_invest * account.cash /
                                   underlying.mktprice_close() /
                                   underlying.multiplier())
        order_underlying = account.create_trade_order(underlying,
                                                      c.LongShort.LONG,
                                                      unit_underlying)
        record_underlying = underlying.execute_order(order_underlying,
                                                     slippage=slippage)
        account.add_record(record_underlying, underlying)
        maturity1 = optionset.select_maturity_date(nbr_maturity=nbr_maturity,
                                                   min_holding=min_holding)
        list_atm_call, list_atm_put = optionset.get_options_list_by_moneyness_mthd1(
示例#8
0
df_stocks[c.Util.AMT_CLOSE] = df_stocks['close']
df_stocks[c.Util.ID_INSTRUMENT] = '十大龙头股组合'
df_metrics = get_data.get_50option_mktdata(start_date, end_date)
df_index = get_data.get_index_mktdata(start_date, end_date, c.Util.STR_INDEX_50ETF)
d1 = df_index[c.Util.DT_DATE].values[0]
d2 = df_metrics[c.Util.DT_DATE].values[0]
d = max(d1, d2)
df_metrics = df_metrics[df_metrics[c.Util.DT_DATE] >= d].reset_index(drop=True)
df_index = df_index[df_index[c.Util.DT_DATE] >= d].reset_index(drop=True)
init_index = df_index[c.Util.AMT_CLOSE].values[0]
benchmark = [1]
benchmark1 = [1]
""" Collar """
optionset = BaseOptionSet(df_metrics)
optionset.init()
index = BaseInstrument(df_index)
index.init()
stock = BaseInstrument(df_stocks)
stock.init()
account = BaseAccount(init_fund=c.Util.BILLION, leverage=1.0, rf=0.0)
maturity = optionset.select_maturity_date(nbr_maturity=nbr_maturity, min_holding=min_holding)

# 标的指数开仓
# unit_index =  np.floor(m*account.cash/index.mktprice_close()/index.multiplier())
# init_mktvalue = unit_index*index.mktprice_close()*index.multiplier()
# order_index = account.create_trade_order(index, c.LongShort.LONG, unit_index, cd_trade_price=c.CdTradePrice.CLOSE)
# record_index = index.execute_order(order_index, slippage=slippage)
# account.add_record(record_index, index)
# 标的换成股票指数
init_mktvalue = m*account.cash
unit_stock = np.floor(init_mktvalue/stock.mktprice_close()/stock.multiplier())
df_index = df_index[df_index[Util.DT_DATE].isin(
    Util.DZQH_CF_DATA_MISSING_DATES) == False].reset_index(drop=True)
df_vol_1m = Histvol.hist_vol(df_future_c1_daily)
# df_parkinson_1m = Histvol.parkinson_number(df_future_c1_daily)
# df_garman_klass = Histvol.garman_klass(df_future_c1_daily)
# df_hist_vol = df_vol_1m.join(df_parkinson_1m, how='left')
# df_hist_vol = df_hist_vol.join(df_garman_klass, how='left')

df_future_c1_daily = df_future_c1_daily[
    df_future_c1_daily[Util.DT_DATE] >= start_date].reset_index(drop=True)
synthetic_option = SytheticOption(df_c1_minute=df_future_c1,
                                  df_c1_daily=df_future_c1_daily,
                                  df_futures_all_daily=df_futures_all_daily,
                                  df_index_daily=df_index)
synthetic_option.init()
underlying = BaseInstrument(df_data=df_index)
underlying.init()
account = BaseAccount(2 * Util.BILLION, leverage=10.0, rf=0.0)
trading_desk = Trade()

#####################################################################
# """ Init position """
strike = synthetic_option.underlying_index_state_daily[Util.AMT_CLOSE]
dt_maturity = synthetic_option.eval_date + datetime.timedelta(days=30)
vol = 0.2
Option = EuropeanOption(strike, dt_maturity, OptionType.PUT)
delta = synthetic_option.get_black_delta(Option, vol)
id_future = synthetic_option.current_state[Util.ID_INSTRUMENT]
synthetic_unit = synthetic_option.get_synthetic_unit(delta)
if synthetic_unit > 0:
    long_short = LongShort.LONG
示例#10
0
class HoldFutureContinuous(object):
    def __init__(self, df_c1, df_all, df_baseindex):
        self.slippage = 0
        self.cd_trade_price = c.CdTradePrice.VOLUME_WEIGHTED
        dt_start = max(df_baseindex[c.Util.DT_DATE].values[0],
                       df_c1[c.Util.DT_DATE].values[0])
        self.end_date = min(df_baseindex[c.Util.DT_DATE].values[-1],
                            df_c1[c.Util.DT_DATE].values[-1])
        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.index = BaseInstrument(df_baseindex)
        self.index.init()
        self.account = BaseAccount(init_fund=c.Util.BILLION,
                                   leverage=1.0,
                                   rf=0.03)

    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)
        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)
        init_index = self.index.mktprice_close()
        index_npv = []
        while self.invst_portfolio.eval_date <= self.end_date:
            if self.invst_portfolio.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.invst_portfolio.eval_date)
                index_npv.append(self.index.mktprice_close() / init_index)
                print(self.invst_portfolio.eval_date, ' close out ')
                break

            #移仓换月
            self.invst_portfolio.shift_contract_month(self.account,
                                                      self.slippage)

            self.account.daily_accounting(self.invst_portfolio.eval_date)
            index_npv.append(self.index.mktprice_close() / init_index)
            if not self.invst_portfolio.has_next(): break
            self.invst_portfolio.next()
            self.index.next()
        self.account.account['baseindex_npv'] = index_npv
        return self.account
示例#11
0
            df_underlying_with_alpha.loc[
                idx, 'close_alpha'] = df_underlying_with_alpha.loc[
                    idx - 1, 'close_alpha'] * np.exp(
                        df_underlying_with_alpha.loc[idx, 'r1'])

    df_underlying_with_alpha = df_underlying_with_alpha[[
        c.Util.DT_DATE, c.Util.ID_INSTRUMENT, c.Util.AMT_CLOSE, 'close_alpha'
    ]].rename(columns={c.Util.AMT_CLOSE: 'etf_close'})
    df_underlying_with_alpha = df_underlying_with_alpha.rename(
        columns={'close_alpha': c.Util.AMT_CLOSE})
    # df_underlying_with_alpha.to_csv('../accounts_data/df_underlying_with_alpha='+str(alpha)+'.csv')
    """ Init Portfolio and Account """
    init_fund = 10000000
    optionset = BaseOptionSet(df_metrics_1)
    optionset.init()
    underlying = BaseInstrument(df_underlying_with_alpha)
    underlying.init()
    account = BaseAccount(init_fund, leverage=1.0, rf=0.03)
    """ 初始开仓 """
    unit_underlying = np.floor(pct_underlying_invest * account.cash /
                               underlying.mktprice_close() /
                               underlying.multiplier())
    order_underlying = account.create_trade_order(underlying, c.LongShort.LONG,
                                                  unit_underlying)
    record_underlying = underlying.execute_order(order_underlying,
                                                 slippage=slippage)
    account.add_record(record_underlying, underlying)
    maturity1 = optionset.select_maturity_date(nbr_maturity=nbr_maturity,
                                               min_holding=min_holding)
    equal_50etf_unit = unit_underlying * underlying.mktprice_close(
    ) / optionset.eligible_options[0].underlying_close()
示例#12
0
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
示例#13
0
 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
class SyntheticOptionHedgedPortfolio():
    def __init__(self, start_date, end_date):
        self.ttm = 30
        self.buywrite = BuyWrite.BUY
        self.fund = Util.BILLION
        self.invest_underlying_ratio = 0.7
        # self.invest_underlying_ratio = 1
        self.slippage = 2
        self.start_date = start_date
        self.end_date = end_date
        hist_date = start_date - datetime.timedelta(days=40)
        df_future_c1 = get_dzqh_cf_c1_minute(start_date, end_date, 'if')
        df_future_c1_daily = get_dzqh_cf_c1_daily(hist_date, end_date, 'if')
        df_futures_all_daily = get_dzqh_cf_daily(
            start_date, end_date, 'if')  # daily data of all future contracts
        df_index = get_index_mktdata(
            start_date, end_date,
            'index_300sh')  # daily data of underlying index
        df_index = df_index[df_index[Util.DT_DATE].isin(
            Util.DZQH_CF_DATA_MISSING_DATES) == False].reset_index(drop=True)
        # df_index.to_csv('df_index.csv')
        self.trade_dates = sorted(df_future_c1_daily[Util.DT_DATE].unique())
        self.df_vol_1m = Histvol.hist_vol(df_future_c1_daily)
        # df_parkinson_1m = Histvol.parkinson_number(df_future_c1_daily)
        self.df_garman_klass = Histvol.garman_klass(df_future_c1_daily)
        # df_hist_vol = self.df_vol_1m.join(self.df_garman_klass, how='left')
        # df_hist_vol.to_csv('../../data/df_hist_vol.csv')
        self.underlying = BaseInstrument(df_data=df_index)
        self.underlying.init()
        self.synthetic_option = SytheticOption(
            df_c1_data=df_future_c1,
            # df_c1_daily=df_future_c1_daily,
            df_futures_all_daily=df_futures_all_daily,
            df_index_daily=df_index)
        self.synthetic_option.init()

        self.account = BaseAccount(self.fund, leverage=20.0, rf=0.0)
        self.trading_desk = Trade()
        self.init_spot = self.synthetic_option.underlying_state_daily[
            Util.AMT_CLOSE]
        self.df_analysis = pd.DataFrame()

    def next(self):
        if self.synthetic_option.is_last_minute():
            self.underlying.next()
        self.synthetic_option.next()

    def init_portfolio(self, fund):
        """ Init position """
        dt_maturity = self.synthetic_option.eval_date + datetime.timedelta(
            days=self.ttm)
        # strike = self.underlying.mktprice_close()
        strike = self.synthetic_option.mktprice_close()
        # dt_maturity = self.synthetic_option.eval_date + datetime.timedelta(days=30)
        self.Option = EuropeanOption(strike, dt_maturity, OptionType.PUT)
        # self.Option = EuropeanOption(strike, dt_maturity, OptionType.CALL)
        """ 用第一天的日收盘价开仓标的现货多头头寸 """
        underlying_unit = np.floor(fund * self.invest_underlying_ratio /
                                   self.underlying.mktprice_close())
        order_underlying = self.account.create_trade_order(
            self.underlying, LongShort.LONG, underlying_unit)
        execution_record = self.underlying.execute_order(
            order_underlying,
            slippage=0,
            execute_type=ExecuteType.EXECUTE_ALL_UNITS)
        self.account.add_record(execution_record, self.underlying)
        self.synthetic_option.amt_option = underlying_unit
        """ 用第一天的成交量加权均价开仓/调整复制期权头寸 """
        vol = self.get_vol()
        self.delta = self.synthetic_option.get_black_delta(self.Option, vol)
        synthetic_unit = self.synthetic_option.get_synthetic_unit(
            self.delta, self.buywrite)
        self.synthetic_option.synthetic_unit = synthetic_unit
        if synthetic_unit > 0:
            long_short = LongShort.LONG
        else:
            long_short = LongShort.SHORT
        order = self.account.create_trade_order(self.synthetic_option,
                                                long_short, synthetic_unit)
        execution_record = self.synthetic_option.execute_order_by_VWAP(
            order, slippage=0, execute_type=ExecuteType.EXECUTE_ALL_UNITS)
        self.account.add_record(execution_record, self.synthetic_option)
        self.account.daily_accounting(self.synthetic_option.eval_date)
        self.add_additional_to_account()
        self.disp()
        self.next()

    def rebalance_sythetic_option(self):
        """ Reset and Rebalance sythetic option  """
        dt_maturity = self.synthetic_option.eval_date + datetime.timedelta(
            days=self.ttm)
        # strike = self.underlying.mktprice_close()
        strike = self.synthetic_option.mktprice_close()
        self.Option = EuropeanOption(strike, dt_maturity, OptionType.PUT)
        # self.Option = EuropeanOption(strike, dt_maturity, OptionType.CALL)
        """ 用成交量加权均价调整复制期权头寸 """
        vol = self.get_vol()
        self.delta = self.synthetic_option.get_black_delta(self.Option, vol)
        synthetic_unit = self.synthetic_option.get_rebalancing_unit(
            self.delta, self.Option, vol,
            self.synthetic_option.mktprice_close(), DeltaBound.NONE,
            self.buywrite)
        self.synthetic_option.synthetic_unit += synthetic_unit
        if synthetic_unit > 0:
            long_short = LongShort.LONG
        else:
            long_short = LongShort.SHORT
        order = self.account.create_trade_order(self.synthetic_option,
                                                long_short, synthetic_unit)
        execution_record = self.synthetic_option.execute_order_by_VWAP(
            order, slippage=0, execute_type=ExecuteType.EXECUTE_ALL_UNITS)
        self.account.add_record(execution_record, self.synthetic_option)
        self.account.daily_accounting(self.synthetic_option.eval_date)
        self.add_additional_to_account()
        self.disp()
        self.next()
        return

    def hedge(self, dt_end=None):

        id_future = self.synthetic_option.current_state[Util.ID_INSTRUMENT]
        if dt_end is None:
            dt_end = self.Option.dt_maturity
        dt_time_end = datetime.datetime(dt_end.year, dt_end.month, dt_end.day,
                                        15, 00, 0)

        while self.synthetic_option.has_next(
        ) and self.synthetic_option.eval_datetime <= dt_time_end:

            if id_future != self.synthetic_option.current_state[
                    Util.ID_INSTRUMENT]:
                long_short = self.account.trade_book.loc[id_future,
                                                         Util.TRADE_LONG_SHORT]
                hold_unit = -self.account.trade_book.loc[id_future,
                                                         Util.TRADE_UNIT]
                spot = self.synthetic_option.mktprice_close()
                vol = self.get_vol()
                self.delta = self.synthetic_option.get_black_delta(
                    self.Option, vol, spot)
                synthetic_unit = self.synthetic_option.get_synthetic_unit(
                    self.delta, self.buywrite)  # 按照移仓换月日的收盘价计算Delta
                id_c2 = self.synthetic_option.current_state[Util.ID_INSTRUMENT]
                open_unit = synthetic_unit
                self.synthetic_option.synthetic_unit += open_unit - hold_unit

                close_execution_record, open_execution_record \
                    = self.synthetic_option.shift_contract_by_VWAP(id_c1=id_future,
                                                                   id_c2=id_c2,
                                                                   hold_unit=hold_unit,
                                                                   open_unit=open_unit,
                                                                   long_short=long_short,
                                                                   slippage=self.slippage,
                                                                   execute_type=ExecuteType.EXECUTE_ALL_UNITS
                                                                   )

                self.account.add_record(close_execution_record,
                                        self.synthetic_option)
                self.synthetic_option._id_instrument = id_c2
                self.account.add_record(open_execution_record,
                                        self.synthetic_option)
                """ 更新当前持仓头寸 """
                """ USE SAME UNIT TO SHIFT CONTRACT AND USE CLOSE PRICE TO REBALANCING DELTA CHANGE. """
                print(' Relancing after shift contract, ',
                      self.synthetic_option.eval_date)

                id_future = id_c2

            if self.synthetic_option.eval_datetime.time() == datetime.time(
                    9, 30, 0):
                self.next()

            if self.synthetic_option.eval_date == self.synthetic_option.get_next_state_date(
            ):
                if not self.if_hedge('5min'):
                    self.next()
                    continue
            self.rebalancing()

            if self.synthetic_option.is_last_minute():
                self.account.daily_accounting(
                    self.synthetic_option.eval_date)  # 该日的收盘结算
                self.add_additional_to_account()
                self.disp()
            if self.synthetic_option.eval_date == dt_end and self.synthetic_option.is_last_minute(
            ):
                self.close_out()
            self.next()

    def if_hedge(self, cd):
        if cd == '1h':
            """ 1h """
            if self.synthetic_option.eval_datetime.minute == 0:
                return True
            else:
                return False
        elif cd == '10min':
            """ 10min """
            if self.synthetic_option.eval_datetime.minute % 10 == 0:
                return True
            else:
                return False
        elif cd == '5min':
            """ 5min """
            if self.synthetic_option.eval_datetime.minute % 5 == 0:
                return True
            else:
                return False
        elif cd == '1min':
            return True
        elif cd == 'half_day':
            if self.synthetic_option.eval_datetime.time() == datetime.time(11, 29, 00) or \
                    self.synthetic_option.eval_datetime.time() == datetime.time(14, 59, 00):
                return True
            else:
                return False

    def rebalancing(self):
        vol = self.get_vol()
        self.delta = self.synthetic_option.get_black_delta(self.Option, vol)
        rebalance_unit = self.synthetic_option.get_rebalancing_unit(
            self.delta, self.Option, vol,
            self.synthetic_option.mktprice_close(),
            DeltaBound.WHALLEY_WILLMOTT, self.buywrite)
        self.synthetic_option.synthetic_unit += rebalance_unit
        if rebalance_unit > 0:
            long_short = LongShort.LONG
        else:
            long_short = LongShort.SHORT
        order = self.account.create_trade_order(self.synthetic_option,
                                                long_short, rebalance_unit)
        execution_record = self.synthetic_option.execute_order(
            order,
            slippage=self.slippage,
            execute_type=ExecuteType.EXECUTE_ALL_UNITS)
        self.account.add_record(execution_record, self.synthetic_option)

    def close_out(self):
        while not self.synthetic_option.is_last_minute():
            self.next()
        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=0,
                    execute_type=ExecuteType.EXECUTE_ALL_UNITS)
            self.account.add_record(
                execution_record,
                self.account.dict_holding[order.id_instrument])
        self.account.daily_accounting(self.synthetic_option.eval_date)
        self.add_additional_to_account()

        self.disp()
        self.synthetic_option.sythetic_unit = 0
        """ Final NPV check """
        self.df_records = pd.DataFrame(self.account.list_records)
        total_pnl = self.df_records[Util.TRADE_REALIZED_PNL].sum()
        final_npv = (self.fund + total_pnl) / self.fund
        print('calculate final npv from adding up realized pnl ; ', final_npv)

    def get_vol(self):

        date = self.synthetic_option.eval_date
        if date in self.df_vol_1m.index:
            # vol = self.df_vol_1m.loc[date, Util.AMT_HISTVOL]
            vol = self.df_garman_klass.loc[self.synthetic_option.eval_date,
                                           Util.AMT_GARMAN_KLASS]
        else:
            dt1 = Util.largest_element_less_than(port.trade_dates, date)
            vol = self.df_garman_klass.loc[dt1, Util.AMT_GARMAN_KLASS]
            # vol = self.df_vol_1m.loc[dt1, Util.AMT_HISTVOL]
        return vol

    def add_additional_to_account(self):
        # self.account.account.loc[
        #     self.synthetic_option.eval_date, 'underlying_npv_1'] = self.invest_underlying_ratio * self.underlying.mktprice_close() / self.init_spot + 1 - self.invest_underlying_ratio
        self.account.account.loc[
            self.synthetic_option.eval_date,
            'underlying_npv'] = self.underlying.mktprice_close(
            ) / self.init_spot
        self.account.account.loc[
            self.synthetic_option.eval_date,
            'underlying_price'] = self.underlying.mktprice_close()
        self.account.account.loc[
            self.synthetic_option.eval_date,
            'if_c1'] = self.synthetic_option.mktprice_close()
        self.account.account.loc[self.synthetic_option.eval_date, 'hedge_position'] \
            = - self.account.trade_book[self.account.trade_book[Util.TRADE_LONG_SHORT] == LongShort.SHORT][
            Util.TRADE_UNIT].sum()
        self.account.account.loc[self.synthetic_option.eval_date, 'hedge_ratio'] = \
            self.account.account.loc[self.synthetic_option.eval_date, Util.PORTFOLIO_SHORT_POSITION_SCALE] / \
            self.account.account.loc[self.synthetic_option.eval_date, Util.PORTFOLIO_LONG_POSITION_SCALE]
        self.account.account.loc[self.synthetic_option.eval_date, 'pct_margin_unrealized_pnl'] = \
            self.account.account.loc[
                self.synthetic_option.eval_date, Util.MARGIN_UNREALIZED_PNL] / self.account.init_fund
        self.account.account.loc[self.synthetic_option.eval_date, 'pct_nonmargin_unrealized_pnl'] = \
            self.account.account.loc[
                self.synthetic_option.eval_date, Util.NONMARGIN_UNREALIZED_PNL] / self.account.init_fund
        self.account.account.loc[self.synthetic_option.eval_date, 'pct_realized_pnl'] = \
            self.account.account.loc[self.synthetic_option.eval_date, Util.TRADE_REALIZED_PNL] / self.account.init_fund
        self.account.account.loc[self.synthetic_option.eval_date,
                                 'delta'] = self.delta

    def save_results(self):

        self.df_records.to_csv('../../data/trade_records.csv')
        self.account.account.to_csv('../../data/account.csv')
        # self.df_hedge_info = pd.DataFrame(self.list_hedge_info)
        # self.df_hedge_info.to_csv('../../data/hedge_info.csv')
        self.df_analysis.to_csv('../../data/df_analysis.csv')
        self.account.trade_book_daily.to_csv('../../data/trade_book_daily.csv')

    def disp(self):
        if self.synthetic_option.eval_date != self.underlying.eval_date:
            print('Date miss matched!')
        try:
            average_cost = int(self.account.trade_book[self.account.trade_book[
                Util.TRADE_LONG_SHORT] == LongShort.SHORT][
                    Util.AVERAGE_POSITION_COST].values[0])
        except:
            average_cost = 0
            pass
        print(
            self.synthetic_option.eval_datetime,
            self.account.account.loc[self.synthetic_option.eval_date,
                                     Util.PORTFOLIO_NPV],
            self.underlying.mktprice_close() / self.init_spot,
            # self.account.account.loc[self.synthetic_option.eval_date, 'hedge_position'],
            # self.synthetic_option.synthetic_unit,
            int(self.Option.strike),
            int(self.underlying.mktprice_close()),
            int(self.synthetic_option.mktprice_close()),
            average_cost,
            round(self.delta, 2),
            round(
                self.account.account.loc[self.synthetic_option.eval_date,
                                         'hedge_ratio'], 2),
            # self.account.cash,
            round(
                100 * self.account.account.loc[self.synthetic_option.eval_date,
                                               'pct_margin_unrealized_pnl'],
                1),
            '%',
            round(
                100 * self.account.account.loc[self.synthetic_option.eval_date,
                                               'pct_nonmargin_unrealized_pnl'],
                1),
            '%',
            round(
                100 * self.account.account.loc[self.synthetic_option.eval_date,
                                               'pct_realized_pnl'], 1),
            '%',
            self.underlying.eval_date,
        )

    def reset_option_maturity(self, dt_maturity=None):
        if dt_maturity is None:
            dt_maturity = self.synthetic_option.eval_date + datetime.timedelta(
                days=self.ttm)
        self.Option.dt_maturity = dt_maturity

    def reset_option_strike(self):
        strike = self.synthetic_option.mktprice_close()
        self.Option.strike = strike

    def analysis(self, dt_start, dt_end):
        """ Replicate Period Result Analysis """
        self.df_records = pd.DataFrame(self.account.list_records)
        analysis = pd.Series()
        df_hedge_records = self.df_records[
            (self.df_records[Util.ID_INSTRUMENT] != 'index_300sh')
            & (self.df_records[Util.DT_TRADE] >= dt_start) &
            (self.df_records[Util.DT_TRADE] <= dt_end)]
        init_stock_value = self.account.account.loc[
            dt_start, Util.PORTFOLIO_TRADES_VALUE]
        init_stock_price = \
            self.underlying.df_data[self.underlying.df_data[Util.DT_DATE] == dt_start][Util.AMT_CLOSE].values[0]
        terminal_stock_price = \
            self.underlying.df_data[self.underlying.df_data[Util.DT_DATE] == dt_end][Util.AMT_CLOSE].values[0]
        replicate_pnl = df_hedge_records[Util.TRADE_REALIZED_PNL].sum()
        option_payoff = self.synthetic_option.amt_option * max(
            init_stock_price - terminal_stock_price, 0)
        replicate_cost = replicate_pnl - option_payoff
        replicate_cost_future = replicate_pnl - self.synthetic_option.amt_option * max(
            self.Option.strike - terminal_stock_price, 0)
        pct_replicate_cost = replicate_cost / init_stock_value
        pct_replicate_pnl = replicate_pnl / init_stock_value
        transaction_cost = df_hedge_records[Util.TRANSACTION_COST].sum()
        init_portfolio_value = self.account.account.loc[dt_start,
                                                        Util.PORTFOLIO_VALUE]
        trade_value = np.abs(df_hedge_records[Util.TRADE_BOOK_VALUE]).sum()
        ratio = trade_value / init_portfolio_value
        pct_underlying_pnl = (terminal_stock_price -
                              init_stock_price) / init_stock_price
        analysis['ratio'] = ratio
        analysis['dt_start'] = dt_start
        analysis['dt_end'] = dt_end
        analysis['init_stock_value'] = init_stock_value
        analysis['replicate_pnl'] = replicate_pnl
        analysis['option_payoff'] = option_payoff
        analysis['replicate_cost_spot'] = replicate_cost
        analysis['replicate_cost_future'] = replicate_cost_future
        analysis['pct_replicate_cost'] = pct_replicate_cost
        analysis['pct_replicate_pnl'] = pct_replicate_pnl
        analysis['pct_underlying_pnl'] = pct_underlying_pnl
        analysis['transaction_cost'] = transaction_cost
        analysis['dt_maturity'] = self.Option.dt_maturity
        self.df_analysis = self.df_analysis.append(analysis, ignore_index=True)
示例#15
0
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