Esempio n. 1
0
 def get_industry_factor(self):
     # 读取行业信息数据
     industry = data.read_data(['Industry'], ['Industry'])
     self.industry = industry.ix['Industry']
     # 对第一个拥有所有行业的日期取虚拟变量,以建立储存数据的panel
     industry_num = self.industry.apply(lambda x: x.unique().size, axis=1)
     # 注意所有行业28个,加上nan有29个
     first_valid_index = industry_num[industry_num == 29].index[0]
     temp_dum = pd.get_dummies(self.industry.ix[first_valid_index],
                               prefix='Industry')
     industry_dummies = pd.Panel(data=None,
                                 major_axis=temp_dum.index,
                                 minor_axis=temp_dum.columns)
     # 开始循环
     for time, ind_data in self.industry.iterrows():
         industry_dummies[time] = pd.get_dummies(ind_data,
                                                 prefix='Industry')
     # 转置
     industry_dummies = industry_dummies.transpose(2, 0, 1)
     # 将行业因子暴露与风格因子暴露的索引对其
     industry_dummies = data.align_index(self.bb_data.factor_expo.ix[0],
                                         industry_dummies)
     # 将nan填成0,主要是有些行业在某一时间点,没有一只股票属于它,这会造成在这个行业上的暴露是nan
     # 因此需要把这个行业的暴露填成0,而uninv的nan同样会被填上,但会在之后的filter中再次变成nan
     industry_dummies = industry_dummies.fillna(0)
     # 将行业因子暴露与风格因子暴露衔接在一起
     self.bb_data.factor_expo = pd.concat(
         [self.bb_data.factor_expo, industry_dummies])
Esempio n. 2
0
    def reset_bkt_benchmark(self, new_bkt_benchmark_data):
        self.bkt_data.benchmark_price = data.read_data(new_bkt_benchmark_data, ['ClosePrice_adj'])

        # 将benchmark price数据期调整为回测期
        self.bkt_data.benchmark_price = data.align_index(self.tar_pct_position.holding_matrix,
                                                         self.bkt_data.benchmark_price, axis='major')

        # 重置回测数据
        self.reset_bkt_data()
Esempio n. 3
0
    def __init__(self, bkt_position, *, initial_money = 100000000, trade_ratio = 0.95, 
                 buy_cost = 1.5/1000, sell_cost = 1.5/1000, bkt_start = 'default', bkt_end = 'default',
                 risk_free_rate = 0.0, bkt_stock_data = 'default', bkt_benchmark_data = 'default',
                 infinitesimal=1e-4):
        """ Initialize backtest object.
        
        foo
        """
        # 初始化传入的持仓类,是要回测的策略构造出的持仓矩阵对象,是回测的目标持仓,注意此日期为调仓日
        self.bkt_position = bkt_position

        # 只支持正杠杆,即买空卖空的持仓比例之和必须大于0
        greater_than_zero_condition = self.bkt_position.holding_matrix.sum(1) > infinitesimal
        # 确保这些持仓比例和为0的股票并非全是0,以免将全是0的持仓判断为非法持仓
        # all_zeros_condition = self.bkt_position.holding_matrix.ix[~greater_than_zero_condition].prod(1) == 0.0
        all_zeros_condition = (self.bkt_position.holding_matrix == 0.0).all(1)
        assert np.logical_or(greater_than_zero_condition, all_zeros_condition).all(), \
            'Sum of the holding matrix are no greater than 0 for at least 1 timestamp, this is not supported by this ' \
            'backtest system. Note that the timestamp whose holdings are all 0 has been excluded from this error.\n'

        
        # 初始化回测用到的股价数据类
        self.bkt_data = backtest_data()
        # 初始化股价数据,包括收盘开盘价等
        if bkt_stock_data == 'default':
            self.bkt_data.stock_price = data.read_data(['ClosePrice_adj','OpenPrice_adj'], 
                                                  ['ClosePrice_adj','OpenPrice_adj'])
        else:
            self.bkt_data.stock_price = data.read_data(bkt_stock_data)
        # 初始化基准价格数据,默认设为中证500,只需要收盘数据, 开盘数据只是为了初始化序列的第一个值
        # 注意, 因为做空期货实际上做空的是指数的全收益序列, 因此我们要计算基准的全收益价格序列
        # 基准指数的全收益价格序列没有开盘价, 因此只能全部用收盘价替代
        if bkt_benchmark_data == 'default':
            self.bkt_data.benchmark_price = data.read_data(['ClosePrice_adj_zz500'], ['ClosePrice_adj'])
        else:
            self.bkt_data.benchmark_price = data.read_data([bkt_benchmark_data], [bkt_benchmark_data])
        # 读取股票上市退市停牌数据,并生成标记股票是否可交易的矩阵
        self.bkt_data.generate_if_tradable()
            
        # 根据传入的持仓类,校准回测股价和基准股价的数据,将股票代码对齐
        self.bkt_data.stock_price = data.align_index(self.bkt_position.holding_matrix, self.bkt_data.stock_price, 
                                                     axis = 'minor')
        self.bkt_data.if_tradable = data.align_index(self.bkt_position.holding_matrix, self.bkt_data.if_tradable, 
                                                     axis = 'minor')
        
        # 检测股票代码是否都包含在回测数据中,当有一只股票的某一个回测数据全是nan,且对这只股票有持仓时,
        # 则认为有股票代码没有全部包含在回测数据中
        stock_in_condition = np.logical_and(self.bkt_data.stock_price.isnull().all(1).any(1),
                                            self.bkt_position.holding_matrix.sum()>0)
        assert not stock_in_condition.any(), \
               'Some stocks in the input holding matrix are NOT included in the backtest database, '\
               'please check it carefully!\n'
        # 检测回测数据是否覆盖了回测时间段
        # 检测起始时间
        if bkt_start == 'default':
            assert self.bkt_data.stock_price.major_axis[0]<=self.bkt_position.holding_matrix.index[0], \
                   'The default start time of backtest is earlier than the start time in backtest database, '\
                   'please try to set a later start time which must be a trading day\n'
        else:
            assert self.bkt_data.stock_price.major_axis[0]<=bkt_start, \
                   'The input start time of backtest is earlier than the start time in backteset database, '\
                   'please try to set a later start time which must be a trading day, or try to set it as default\n'
        # 检测结束时间
        if bkt_end == 'default':
            # 如果回测数据中的最后一天直接在最后一个调仓日前,则直接报错
            assert self.bkt_data.stock_price.major_axis[-1]>self.bkt_position.holding_matrix.index[-1], \
                   'The default end time of backtest is later than the end time in backtest database, '\
                   'please try to set an earlier end time which must be a trading day\n'
            # 回测数据中的最后一天在最后一个调仓日后,现在判断是否之后有60个交易日可取
            last_holding_loc = self.bkt_data.stock_price.major_axis.get_loc(self.bkt_position.holding_matrix.index[-1])
            total_size = self.bkt_data.stock_price.major_axis.size
            assert total_size>=last_holding_loc+1+60, \
                   'The default end time of backtest is later than the end time in backtest database, '\
                   'please try to set an earlier end time which must be a trading day\n'
        else:
            assert self.bkt_data.stock_price.major_axis[-1]>bkt_end, \
                   'The input end time of backtest is later than the end time in backtest database, '\
                   'please try to set an earlier end time which must be a trading day, or try to set it as default\n'
        
        # 设置回测的起止时间,这里要注意默认的时间可能超过回测数据的范围
        # 起始时间:默认为第一个调仓日,如有输入数据,则为输入数据和默认时间的较晚日期
        default_start = self.bkt_data.stock_price.major_axis[self.bkt_data.stock_price.major_axis.get_loc(self.bkt_position.holding_matrix.index[0])]
        if bkt_start == 'default':
            self.bkt_start = default_start
        else:
            self.bkt_start = max(default_start, bkt_start)
        # 停止时间:默认为最后一个调仓日后的21个交易日,如有输入数据,则以输入数据为准
        if bkt_end == 'default':
            default_end = self.bkt_data.stock_price.major_axis[self.bkt_data.stock_price.major_axis.get_loc(self.bkt_position.holding_matrix.index[-1])+21]
            self.bkt_end = default_end
        else:
            self.bkt_end = bkt_end
            
        # 对回测的其他数据进行初始化
        self.initial_money = initial_money
        self.trade_ratio = trade_ratio
        self.buy_cost = buy_cost
        self.sell_cost = sell_cost
        self.risk_free_rate = risk_free_rate
        
        # 以回测期(而不是回测数据期或调仓期)为时间索引的持仓量矩阵,注意vol的持仓单位为手,pct的持仓单位为百分比
        start_loc = self.bkt_data.stock_price.major_axis.get_loc(self.bkt_start)
        end_loc = self.bkt_data.stock_price.major_axis.get_loc(self.bkt_end)
        backtest_period_holding_matrix = self.bkt_data.stock_price.ix[0,start_loc:end_loc+1,:]
        self.tar_pct_position = position(backtest_period_holding_matrix)
        # 初始化持仓目标矩阵
        self.tar_pct_position.holding_matrix = self.bkt_position.holding_matrix.reindex(
                                               index = self.tar_pct_position.holding_matrix.index, 
                                               method = 'ffill')
        # 初始化实际持仓矩阵
        self.real_vol_position = position(backtest_period_holding_matrix)
        # 初始化实际持仓的百分比
        self.real_pct_position = position(backtest_period_holding_matrix)
        # 初始化目标持仓矩阵,单位为手,这个持仓量矩阵主要作为参考
        self.tar_vol_position = position(backtest_period_holding_matrix)
        
        # 将回测数据期也调整为回测期
        self.bkt_data.stock_price = data.align_index(self.tar_pct_position.holding_matrix, self.bkt_data.stock_price, 
                                                     axis = 'major')
        self.bkt_data.benchmark_price = data.align_index(self.tar_pct_position.holding_matrix, self.bkt_data.benchmark_price, 
                                                         axis = 'major')
        self.bkt_data.if_tradable = data.align_index(self.tar_pct_position.holding_matrix, self.bkt_data.if_tradable, 
                                                     axis = 'major')
        
        # 初始化回测要用到的现金数据:
        self.cash = pd.Series(np.zeros(self.real_vol_position.holding_matrix.shape[0]), 
                                 index = self.real_vol_position.holding_matrix.index)
        self.cash.ix[0] = self.initial_money*self.trade_ratio
        # 初始化回测得到的账户价值数据:
        self.account_value = []
        # 初始化计算业绩指标及作图用到的benchmark价值数据
        self.benchmark_value = self.bkt_data.benchmark_price.ix['ClosePrice_adj', :, 0]
        # 初始化其他信息序列,包括换手率,持有的股票数等
        self.info_series = pd.DataFrame(0, index=self.cash.index, columns=['holding_value', 'sell_value',
                                            'buy_value', 'trading_value', 'turnover_ratio', 'cost_value',
                                            'holding_num'])
        
        # 暂时用一个警告的string初始化performance对象,防止提前调用此对象出错
        self.bkt_performance = 'The performance object of this backtest object has NOT been initialized, '\
                               'please try to call this attribute after call backtest.get_performance()\n'

        # 初始化结束时,目标和实际持仓矩阵、回测数据都是一样的时间股票索引(即策略持仓股票为股票索引,回测期间为时间索引),
        # 传入的bkt_position股票索引是一样的,但是时间索引为调仓日的时间

        # 控制回测对象是否需要输出提示用户的警告
        self.enable_warning = True

        print('The backtest system has been successfully initialized!\n')
Esempio n. 4
0
 def read_original_data(self):
     # 先读取市值
     if self.bb_data.stock_price.empty:
         self.bb_data.stock_price = data.read_data(['FreeMarketValue'],
                                                   ['FreeMarketValue'])
     elif 'FreeMarketValue' not in self.bb_data.stock_price.items:
         mv = data.read_data(['FreeMarketValue'], ['FreeMarketValue'])
         self.bb_data.stock_price['FreeMarketValue'] = mv.ix[
             'FreeMarketValue']
     # 初始化无风险利率序列
     if os.path.isfile('const_data.csv'):
         self.bb_data.const_data = pd.read_csv('const_data.csv',
                                               index_col=0,
                                               parse_dates=True,
                                               encoding='GB18030')
         if 'risk_free' not in self.bb_data.const_data.columns:
             self.bb_data.const_data['risk_free'] = 0
         else:
             print('risk free rate successfully loaded')
     else:
         self.bb_data.const_data = pd.DataFrame(
             0,
             index=self.bb_data.stock_price.major_axis,
             columns=['risk_free'])
     # 读取价格数据
     if 'ClosePrice_adj' not in self.bb_data.stock_price.items:
         temp_closeprice = data.read_data(['ClosePrice_adj'],
                                          ['ClosePrice_adj'])
         self.bb_data.stock_price['ClosePrice_adj'] = temp_closeprice.ix[
             'ClosePrice_adj']
     # 计算每只股票的日对数收益率
     if 'daily_return' not in self.bb_data.stock_price.items:
         self.bb_data.stock_price['daily_return'] = np.log(
             self.bb_data.stock_price.ix['ClosePrice_adj'].div(
                 self.bb_data.stock_price.ix['ClosePrice_adj'].shift(1)))
     # 计算每只股票的日超额收益
     if 'daily_excess_return' not in self.bb_data.stock_price.items:
         self.bb_data.stock_price[
             'daily_excess_return'] = self.bb_data.stock_price.ix[
                 'daily_return'].sub(
                     self.bb_data.const_data.ix[:, 'risk_free'], axis=0)
     # 读取交易量数据
     if 'Volume' not in self.bb_data.stock_price.items:
         volume = data.read_data(['Volume'], ['Volume'])
         self.bb_data.stock_price['Volume'] = volume.ix['Volume']
     # 读取流通股数数据
     if 'FreeShares' not in self.bb_data.stock_price.items:
         shares = data.read_data(['FreeShares'], ['FreeShares'])
         self.bb_data.stock_price['FreeShares'] = shares.ix['FreeShares']
     # 读取pb
     if self.bb_data.raw_data.empty:
         self.bb_data.raw_data = data.read_data(['PB'], ['PB'])
         # 一切的数据标签都以stock_price为准
         self.bb_data.raw_data = data.align_index(
             self.bb_data.stock_price.ix[0],
             self.bb_data.raw_data,
             axis='both')
     elif 'PB' not in self.bb_data.raw_data.items:
         pb = data.read_data(['PB'], ['PB'])
         self.bb_data.raw_data['PB'] = pb.ix['PB']
     # 读取ni_fy1, ni_fy2
     if 'NetIncome_fy1' not in self.bb_data.raw_data.items:
         NetIncome_fy1 = data.read_data(['NetIncome_fy1'],
                                        ['NetIncome_fy1'])
         self.bb_data.raw_data['NetIncome_fy1'] = NetIncome_fy1.ix[
             'NetIncome_fy1']
     if 'NetIncome_fy2' not in self.bb_data.raw_data.items:
         NetIncome_fy2 = data.read_data(['NetIncome_fy2'],
                                        ['NetIncome_fy2'])
         self.bb_data.raw_data['NetIncome_fy2'] = NetIncome_fy2.ix[
             'NetIncome_fy2']
     # 读取cash_earnings_ttm,现金净流入的ttm
     if 'CashEarnings_ttm' not in self.bb_data.raw_data.items:
         CashEarnings_ttm = data.read_data(['CashEarnings_ttm'],
                                           ['CashEarnings_ttm'])
         self.bb_data.raw_data['CashEarnings_ttm'] = CashEarnings_ttm.ix[
             'CashEarnings_ttm']
     # 读取pe_ttm
     if 'PE_ttm' not in self.bb_data.raw_data.items:
         pe_ttm = data.read_data(['PE_ttm'], ['PE_ttm'])
         self.bb_data.raw_data['PE_ttm'] = pe_ttm.ix['PE_ttm']
     # 读取净利润net income ttm
     if 'NetIncome_ttm' not in self.bb_data.raw_data.items:
         ni_ttm = data.read_data(['NetIncome_ttm'], ['NetIncome_ttm'])
         self.bb_data.raw_data['NetIncome_ttm'] = ni_ttm.ix['NetIncome_ttm']
     # 读取ni ttm的2年增长率,用ni增长率代替eps增长率,因为ni增长率的数据更全
     if 'NetIncome_ttm_growth_8q' not in self.bb_data.raw_data.items:
         ni_ttm_growth_8q = data.read_data(['NetIncome_ttm_growth_8q'],
                                           ['NetIncome_ttm_growth_8q'])
         self.bb_data.raw_data[
             'NetIncome_ttm_growth_8q'] = ni_ttm_growth_8q.ix[
                 'NetIncome_ttm_growth_8q']
     # 读取revenue ttm的2年增长率
     if 'Revenue_ttm_growth_8q' not in self.bb_data.raw_data.items:
         Revenue_ttm_growth_8q = data.read_data(['Revenue_ttm_growth_8q'],
                                                ['Revenue_ttm_growth_8q'])
         self.bb_data.raw_data[
             'Revenue_ttm_growth_8q'] = Revenue_ttm_growth_8q.ix[
                 'Revenue_ttm_growth_8q']
     # 读取总资产和总负债,用资产负债率代替复杂的leverage因子
     if 'TotalAssets' not in self.bb_data.raw_data.items:
         TotalAssets = data.read_data(['TotalAssets'], ['TotalAssets'])
         self.bb_data.raw_data['TotalAssets'] = TotalAssets.ix[
             'TotalAssets']
     if 'TotalLiability' not in self.bb_data.raw_data.items:
         TotalLiability = data.read_data(['TotalLiability'],
                                         ['TotalLiability'])
         self.bb_data.raw_data['TotalLiability'] = TotalLiability.ix[
             'TotalLiability']
     # 生成可交易及可投资数据
     self.bb_data.generate_if_tradable()
     self.bb_data.handle_stock_pool()
     # 读取完所有数据后,过滤数据
     # 注意:在之后的因子计算中,中间计算出的因子之间相互依赖的,都要再次过滤,如一个需要从另一个中算出
     # 或者回归,正交化,而且凡是由多个因子加权得到的因子,都属于这一类
     # 以及因子的计算过程中用到非此时间点的原始数据时,如在a时刻的因子值要用到在a-t时刻的原始数据
     # 需要算暴露的时候,一定要过滤uninv的数据,因为暴露是在股票池中计算的,即正交化的时候也需要过滤uninv
     # 在barra base中,事实上只有beta需要不依赖于股票池的全局计算,在beta因子计算过后,即可过滤uninv
     # 但同时注意,一旦过滤uninv,数据就不能再作为一般的因子值储存了
     self.bb_data.discard_untradable_data()
Esempio n. 5
0
    def __init__(self, bkt_position, *, initial_money=100000000, buy_cost=1.5/1000,
                 sell_cost=1.5/1000, bkt_start=None, bkt_end=None, risk_free_rate=None,
                 bkt_stock_data=None, bkt_benchmark_data=None, infinitesimal=1e-4):
        """ Initialize backtest object.
        
        foo
        """
        # 初始化传入的持仓类,是要回测的策略构造出的持仓矩阵对象,是回测的目标持仓,注意此日期为调仓日
        self.bkt_position = bkt_position

        # 只支持正杠杆,即买空卖空的持仓比例之和必须大于0
        greater_than_zero_condition = self.bkt_position.holding_matrix.sum(1) > infinitesimal
        # 确保这些持仓比例和为0的股票并非全是0,以免将全是0的持仓判断为非法持仓
        # all_zeros_condition = self.bkt_position.holding_matrix.ix[~greater_than_zero_condition].prod(1) == 0.0
        all_zeros_condition = (self.bkt_position.holding_matrix == 0.0).all(1)
        assert np.logical_or(greater_than_zero_condition, all_zeros_condition).all(), \
            'Sum of the holding matrix are no greater than 0 for at least 1 timestamp, this is not supported by this ' \
            'backtest system. Note that the timestamp whose holdings are all 0 has been excluded from this error.\n'

        # 将持仓进行归一化
        self.bkt_position.to_percentage()
        
        # 初始化回测用到的股价数据类
        self.bkt_data = backtest_data()
        # 初始化股价数据,包括收盘价, vwap(交易量加权平均价)等
        if bkt_stock_data is None:
            self.bkt_data.stock_price = data.read_data(['ClosePrice_adj','vwap_adj'],
                                                       item_name=['ClosePrice_adj', 'vwap_adj'])
        else:
            self.bkt_data.stock_price = data.read_data(bkt_stock_data,
                                                       item_name=['ClosePrice_adj', 'vwap_adj'])
        # self.bkt_data.stock_price['vwap_adj'] = self.bkt_data.stock_price['vwap_adj'].shift(1)
        # 初始化基准价格数据,默认设为中证500,只需要收盘数据, 开盘数据只是为了初始化序列的第一个值
        # 注意, 因为做空期货实际上做空的是指数的全收益序列, 因此我们要计算基准的全收益价格序列
        # 基准指数的全收益价格序列没有开盘价, 因此只能全部用收盘价替代
        if bkt_benchmark_data is None:
            self.bkt_data.benchmark_price = data.read_data(['ClosePrice_adj_zz500'],
                                                           item_name=['ClosePrice_adj'])
        else:
            self.bkt_data.benchmark_price = data.read_data([bkt_benchmark_data],
                                                           item_name=['ClosePrice_adj'])
        # 读取股票上市退市停牌数据,并生成标记股票是否可交易的矩阵
        self.bkt_data.generate_if_tradable()
        # 生成标记股票是否涨跌停, 是否可买入卖出的矩阵
        self.bkt_data.generate_if_buyable_sellable()
            
        # 根据传入的持仓类,校准回测股价和基准股价的数据,将股票代码对齐
        self.bkt_data.stock_price = data.align_index(self.bkt_position.holding_matrix, self.bkt_data.stock_price, 
                                                     axis = 'minor')
        self.bkt_data.if_tradable = data.align_index(self.bkt_position.holding_matrix, self.bkt_data.if_tradable, 
                                                     axis = 'minor')
        
        # 检测股票代码是否都包含在回测数据中,当有一只股票的某一个回测数据全是nan,且对这只股票有持仓时,
        # 则认为有股票代码没有全部包含在回测数据中
        stock_in_condition = np.logical_and(self.bkt_data.stock_price.isnull().all(1).any(1),
                                            self.bkt_position.holding_matrix.sum()>0)
        assert not stock_in_condition.any(), \
               'Some stocks in the input holding matrix are NOT included in the backtest database, '\
               'please check it carefully!\n'

        # 读取无风险利率数据
        if isinstance(risk_free_rate, pd.Series):
            self.bkt_data.const_data = pd.DataFrame(risk_free_rate.values, index=risk_free_rate.index,
                                                    columns=['risk_free_rate'])
        else:
            self.bkt_data.const_data = data.read_data('const_data')
            if 'risk_free' not in self.bkt_data.const_data.columns:
                self.bkt_data.const_data['risk_free_rate'] = 0.0
            else:
                self.bkt_data.const_data = pd.DataFrame(0.0, index=self.bkt_data.stock_price.major_axis,
                                                        columns=['risk_free_rate'])

        # 检测回测数据是否覆盖了回测时间段
        # 检测起始时间
        if bkt_start is None:
            assert self.bkt_data.stock_price.major_axis[0]<=self.bkt_position.holding_matrix.index[0], \
                   'The default start time of backtest is earlier than the start time in backtest database, '\
                   'please try to set a later start time which must be a trading day\n'
        else:
            assert self.bkt_data.stock_price.major_axis[0]<=bkt_start, \
                   'The input start time of backtest is earlier than the start time in backteset database, '\
                   'please try to set a later start time which must be a trading day, or try to set it as default\n'
        # 检测结束时间
        if bkt_end is None:
            # 如果回测数据中的最后一天直接在最后一个调仓日前,则直接报错
            assert self.bkt_data.stock_price.major_axis[-1]>self.bkt_position.holding_matrix.index[-1], \
                   'The default end time of backtest is later than the end time in backtest database, '\
                   'please try to set an earlier end time which must be a trading day\n'
            # 回测数据中的最后一天在最后一个调仓日后,现在判断是否之后有21个交易日可取
            last_holding_loc = self.bkt_data.stock_price.major_axis.get_loc(self.bkt_position.holding_matrix.index[-1])
            total_size = self.bkt_data.stock_price.major_axis.size
            assert total_size>=last_holding_loc+1+21, \
                   'The default end time of backtest is later than the end time in backtest database, '\
                   'please try to set an earlier end time which must be a trading day\n'
        else:
            assert self.bkt_data.stock_price.major_axis[-1]>bkt_end, \
                   'The input end time of backtest is later than the end time in backtest database, '\
                   'please try to set an earlier end time which must be a trading day, or try to set it as default\n'
        
        # 设置回测的起止时间,这里要注意默认的时间可能超过回测数据的范围
        # 起始时间:默认为第一个调仓日,如有输入数据,则为输入数据和默认时间的较晚日期
        default_start = self.bkt_data.stock_price.major_axis[self.bkt_data.stock_price.major_axis.
                            get_loc(self.bkt_position.holding_matrix.index[0])]
        if bkt_start is None:
            self.bkt_start = default_start
        else:
            self.bkt_start = max(default_start, bkt_start)
        # 停止时间:默认为最后一个调仓日后的21个交易日,如有输入数据,则以输入数据为准
        if bkt_end is None:
            default_end = self.bkt_data.stock_price.major_axis[self.bkt_data.stock_price.major_axis.
                            get_loc(self.bkt_position.holding_matrix.index[-1])+21]
            self.bkt_end = default_end
        else:
            self.bkt_end = bkt_end

        # 对回测的其他数据进行初始化
        self.initial_money = initial_money
        self.buy_cost = buy_cost
        self.sell_cost = sell_cost
        
        # 以回测期(而不是回测数据期或调仓期)为时间索引的持仓量矩阵,注意vol的持仓单位为手,pct的持仓单位为百分比
        start_loc = self.bkt_data.stock_price.major_axis.get_loc(self.bkt_start)
        end_loc = self.bkt_data.stock_price.major_axis.get_loc(self.bkt_end)
        backtest_period_holding_matrix = self.bkt_data.stock_price.ix[0,start_loc:end_loc+1,:]
        self.tar_pct_position = position(backtest_period_holding_matrix)
        # 初始化持仓目标矩阵
        self.tar_pct_position.holding_matrix = self.bkt_position.holding_matrix.reindex(
            index=self.tar_pct_position.holding_matrix.index, method='ffill')
        self.tar_pct_position.cash = self.bkt_position.cash.reindex(
            index=self.tar_pct_position.cash.index, method='ffill')

        # 初始化实际持仓矩阵
        self.real_vol_position = position(backtest_period_holding_matrix)
        # 初始化实际持仓的百分比
        self.real_pct_position = position(backtest_period_holding_matrix)
        # 初始化目标持仓矩阵,单位为手,这个持仓量矩阵主要作为参考
        self.tar_vol_position = position(backtest_period_holding_matrix)
        
        # 将回测数据期也调整为回测期
        self.bkt_data.stock_price = data.align_index(self.tar_pct_position.holding_matrix,
            self.bkt_data.stock_price, axis='major')
        self.bkt_data.benchmark_price = data.align_index(self.tar_pct_position.holding_matrix,
            self.bkt_data.benchmark_price, axis='major')
        self.bkt_data.if_tradable = data.align_index(self.tar_pct_position.holding_matrix,
            self.bkt_data.if_tradable, axis='major')
        self.bkt_data.const_data = self.bkt_data.const_data.reindex(index=self.tar_pct_position.holding_matrix.index)
        
        # 初始化回测要用到的现金数据:
        # 总的现金资产, 总现金资产中分为不能用来购买股票的, 和可以购买股票的
        # 可以用来购买股票的现金资产. 在回测期间, 可能不为0
        # 第一: 持仓期间非调仓日可能有股票的卖出, 如退市, 但是这个时候并不调仓, 因此不买入股票, 而是持有现金
        # 第二: 买卖股票可能由于手数限制, 导致会有残余现金剩下(暂时没有设置手续限制)
        self.real_vol_position.cash.iloc[0] = self.initial_money * 1

        # 初始化回测得到的账户价值数据:
        self.account_value = pd.Series(0.0, index=self.tar_pct_position.holding_matrix.index)
        # 初始化计算业绩指标及作图用到的benchmark价值数据
        self.benchmark_value = self.bkt_data.benchmark_price.ix['ClosePrice_adj', :, 0]
        # 初始化其他信息序列,包括换手率,持有的股票数等
        self.info_series = pd.DataFrame(0, index=self.real_vol_position.cash.index,
            columns=['holding_value', 'sell_value', 'buy_value', 'trading_value', 'turnover_ratio',
                     'cost_value', 'holding_num', 'holding_sus', 'target_sus', 'buy_cap',
                     'sell_bottom', 'holding_diff', 'cash_diff'])
        
        # 初始化performance对象
        self.bkt_performance = None

        # 初始化结束时,目标和实际持仓矩阵、回测数据都是一样的时间股票索引(即策略持仓股票为股票索引,回测期间为时间索引),
        # 传入的bkt_position股票索引是一样的,但是时间索引为调仓日的时间

        # 控制回测对象是否需要输出提示用户的警告
        self.show_warning = True
        # 标记是否执行本次换仓
        self.if_exec_this_trading = True

        print('The backtest system has been successfully initialized!\n')