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`
code: def _fetch_data(
      self,
      data_storage,
      selector: Union[pd.Timestamp, slice, str, pd.Index] = slice(
          None, None
      ),
      level: Union[str, int] = "datetime",
      col_set: Union[str, List[str]] = CS_ALL,
      squeeze: bool = False,
      proc_func: Callable = None,
  ):
      # This method is extracted for sharing in subclasses
      from .storage import BaseHandlerStorage  # pylint: disable=C0415
  
      # Following conflicts may occur
      # - Does [20200101", "20210101"] mean selecting this slice or these two days?
      # To solve this issue
      #   - slice have higher priorities (except when level is none)
      if isinstance(selector, (tuple, list)) and level is not None:
          # when level is None, the argument will be passed in directly
          # we don't have to convert it into slice
          try:
              selector = slice(*selector)
          except ValueError:
              get_module_logger("DataHandlerLP").info(
                  f"Fail to converting to query to slice. It will used directly"
              )
  
      if isinstance(data_storage, pd.DataFrame):
          data_df = data_storage
          if proc_func is not None:
              # FIXME: fetching by time first will be more friendly to `proc_func`
              # Copy in case of `proc_func` changing the data inplace....
              data_df = proc_func(
                  fetch_df_by_index(
                      data_df,
                      selector,
                      level,
                      fetch_orig=self.fetch_orig,
                  ).copy()
              )
              data_df = fetch_df_by_col(data_df, col_set)
          else:
              # Fetch column  first will be more friendly to SepDataFrame
              data_df = fetch_df_by_col(data_df, col_set)
              data_df = fetch_df_by_index(
                  data_df, selector, level, fetch_orig=self.fetch_orig
              )
      elif isinstance(data_storage, BaseHandlerStorage):
          if not data_storage.is_proc_func_supported():
              if proc_func is not None:
                  raise ValueError(
                      f"proc_func is not supported by the storage {type(data_storage)}"
                  )
              data_df = data_storage.fetch(
                  selector=selector,
                  level=level,
                  col_set=col_set,
                  fetch_orig=self.fetch_orig,
              )
          else:
              data_df = data_storage.fetch(
                  selector=selector,
                  level=level,
                  col_set=col_set,
                  fetch_orig=self.fetch_orig,
                  proc_func=proc_func,
              )
      else:
          raise TypeError(
              f"data_storage should be pd.DataFrame|HashingStockStorage, not {type(data_storage)}"
          )
  
      if squeeze:
          # squeeze columns
          data_df = data_df.squeeze()
          # squeeze index
          if isinstance(selector, (str, pd.Timestamp)):
              data_df = data_df.reset_index(level=level, drop=True)
      return data_df
  
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 5 for calling the DataHandler obj's underlying member method code with example usage: obj._fetch_data(data_storage, selector, level, col_set, squeeze, proc_func) and returns: data_df