docs: def prepare_data(instrument, data, output_path=None,
                   index=None, colsmap=None, kind="BAR", resample="1T"):
      """
      Converts given DataFrame to a QTPyLib-compatible format and timezone
  
      :Parameters:
          instrument : mixed
              IB contract tuple / string (same as that given to strategy)
          data : pd.DataFrame
              Pandas DataDrame with that instrument's market data
          output_path : str
              Path to where the resulting CSV should be saved (optional)
          index : pd.Series
              Pandas Series that will be used for df's index (optioanl)
          colsmap : dict
              Dict for mapping df's columns to those used by QTPyLib
              (default assumes same naming convention as QTPyLib's)
          kind : str
              Is this ``BAR`` or ``TICK`` data
          resample : str
              Pandas resolution (defaults to 1min/1T)
  
      :Returns:
          data : pd.DataFrame
              Pandas DataFrame in a QTPyLib-compatible format and timezone
      """
  
      global _TICKS_COLSMAP, _BARS_COLSMAP
  
      # work on copy
      df = data.copy()
  
      # ezibpy's csv?
      if set(df.columns) == set([
              'datetime', 'C', 'H', 'L', 'O', 'OI', 'V', 'WAP']):
          df.rename(columns={
              'datetime': 'datetime',
              'O': 'open',
              'H': 'high',
              'L': 'low',
              'C': 'close',
              'OI': 'volume',
          }, inplace=True)
          df.index = pd.to_datetime(df['datetime'])
          df.index = df.index.tz_localize(tools.get_timezone()).tz_convert("UTC")
          index = None
  
      # lower case columns
      df.columns = map(str.lower, df.columns)
  
      # set index
      if index is None:
          index = df.index
  
      # set defaults columns
      if not isinstance(colsmap, dict):
          colsmap = {}
  
      _colsmap = _TICKS_COLSMAP if kind == "TICK" else _BARS_COLSMAP
      for el in _colsmap:
          if el not in colsmap:
              colsmap[el] = _colsmap[el]
  
      # generate a valid ib tuple
      instrument = tools.create_ib_tuple(instrument)
  
      # create contract string (no need for connection)
      ibConn = ezIBpy()
      contract_string = ibConn.contractString(instrument)
      asset_class = tools.gen_asset_class(contract_string)
      symbol_group = tools.gen_symbol_group(contract_string)
  
      # add symbol data
      df.loc[:, 'symbol'] = contract_string
      df.loc[:, 'symbol_group'] = symbol_group
      df.loc[:, 'asset_class'] = asset_class
  
      # validate columns
      valid_cols = validate_columns(df, kind)
      if not valid_cols:
          raise ValueError('Invalid Column list')
  
      # rename columns to map
      df.rename(columns=colsmap, inplace=True)
  
      # force option columns on options
      if asset_class == "OPT":
          df = tools.force_options_columns(df)
  
      # remove all other columns
      known_cols = list(colsmap.values()) + \
          ['symbol', 'symbol_group', 'asset_class', 'expiry']
      for col in df.columns:
          if col not in known_cols:
              df.drop(col, axis=1, inplace=True)
  
      # set UTC index
      df.index = pd.to_datetime(index)
      df = tools.set_timezone(df, "UTC")
      df.index.rename("datetime", inplace=True)
  
      # resample
      if resample and kind == "BAR":
          df = tools.resample(df, resolution=resample, tz="UTC")
  
      # add expiry
      df.loc[:, 'expiry'] = np.nan
      if asset_class in ("FUT", "OPT", "FOP"):
          df.loc[:, 'expiry'] = contract_expiry_from_symbol(contract_string)
  
      # save csv
      if output_path is not None:
          output_path = output_path[
              :-1] if output_path.endswith('/') else output_path
          df.to_csv("%s/%s.%s.csv" % (output_path, contract_string, kind))
  
      # return df
      return df