execute_type=c.ExecuteType.EXECUTE_ALL_UNITS)
            account.add_record(execution_record,
                               account.dict_holding[order.id_instrument])

        account.daily_accounting(optionset.eval_date)
        print(optionset.eval_date, ' close out ')
        print(optionset.eval_date, hedging.eval_date,
              account.account.loc[optionset.eval_date, c.Util.PORTFOLIO_NPV],
              int(account.cash))
        break
    if not empty_position:
        moneyness_put = optionset.get_option_moneyness(atm_put)
        moneyness_call = optionset.get_option_moneyness(atm_call)
        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()
Example #2
0
                 order,
                 slippage=0,
                 execute_type=c.ExecuteType.EXECUTE_ALL_UNITS)
         account.add_record(
             execution_record,
             account.dict_holding[order.id_instrument])
     account.daily_accounting(optionset.eval_date)
     # print(optionset.eval_date, ' close out option')
     # print(optionset.eval_date, underlying.eval_date,
     #       account.account.loc[optionset.eval_date, c.Util.PORTFOLIO_NPV],
     #       int(account.cash))
     break
 # if optionset.eval_date == maturity1:
 if optionset.eval_date > maturity1 - datetime.timedelta(
         days=30):  # Roll to next maturity
     order = account.create_close_order(atm_put)
     execution_record = account.dict_holding[
         order.id_instrument].execute_order(
             order,
             slippage=slippage,
             execute_type=c.ExecuteType.EXECUTE_ALL_UNITS)
     account.add_record(execution_record,
                        account.dict_holding[order.id_instrument])
     # print('roll', optionset.eval_date, maturity1, account.cash)
     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(
         moneyness, maturity1)
     if list_atm_put is None:
         # list_atm_call, list_atm_put = optionset.get_options_list_by_moneyness_mthd1(0, maturity1)
         list_atm_put = optionset.get_deepest_otm_put_list(
Example #3
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
Example #4
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
Example #5
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
Example #6
0
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