class_docstr: The steps to using a handler
  1. initialized data handler  (call by `init`).
  2. use the data.
  
  
  The data handler try to maintain a handler with 2 level.
  `datetime` & `instruments`.
  
  Any order of the index level can be supported (The order will be implied in the data).
  The order  <`datetime`, `instruments`> will be used when the dataframe index name is missed.
  
  Example of the data:
  The multi-index of the columns is optional.
  
  .. code-block:: text
  
                              feature                                                            label
                              $close     $volume  Ref($close, 1)  Mean($close, 3)  $high-$low  LABEL0
      datetime   instrument
      2010-01-04 SH600000    81.807068  17145150.0       83.737389        83.016739    2.741058  0.0032
                 SH600004    13.313329  11800983.0       13.313329        13.317701    0.183632  0.0042
                 SH600005    37.796539  12231662.0       38.258602        37.919757    0.970325  0.0289
  
  
  Tips for improving the performance of datahandler
  - Fetching data with `col_set=CS_RAW` will return the raw data and may avoid pandas from copying the data when calling `loc`
docstr: Set Up the data in case of running initialization for multiple time
  
  It is responsible for maintaining following variable
  1) self._data
  
  Parameters
  ----------
  enable_cache : bool
      default value is false:
  
      - if `enable_cache` == True:
  
          the processed data will be saved on disk, and handler will load the cached data from the disk directly
          when we call `init` next time
code: def setup_data(self, enable_cache: bool = False):
      """
      Set Up the data in case of running initialization for multiple time
  
      It is responsible for maintaining following variable
      1) self._data
  
      Parameters
      ----------
      enable_cache : bool
          default value is false:
  
          - if `enable_cache` == True:
  
              the processed data will be saved on disk, and handler will load the cached data from the disk directly
              when we call `init` next time
      """
      # Setup data.
      # _data may be with multiple column index level. The outer level indicates the feature set name
      with TimeInspector.logt("Loading data"):
          # make sure the fetch method is based on an index-sorted pd.DataFrame
          self._data = lazy_sort_index(
              self.data_loader.load(
                  self.instruments, self.start_time, self.end_time
              )
          )
  
imports: import warnings
  from typing import Callable, Union, Tuple, List, Iterator, Optional
  import pandas
  from qlib.typehint import Literal
  from ...log import get_module_logger, TimeInspector
  from ...utils import init_instance_by_config
  from ...utils.serial import Serializable
  from .utils import fetch_df_by_index, fetch_df_by_col
  from ...utils import lazy_sort_index
  from .loader import DataLoader
  from .None import processor
  from .None import loader
label_desc: Use image node_id 3 for calling the DataHandler obj's underlying member method code with example usage: obj.setup_data(enable_cache) without return types