class_docstr: Provides methods for preprocessing the stock price data
  
  Attributes
  ----------
      use_technical_indicator : boolean
          we technical indicator or not
      tech_indicator_list : list
          a list of technical indicator names (modified from neofinrl_config.py)
      use_turbulence : boolean
          use turbulence index or not
      user_defined_feature:boolean
          use user defined features or not
  
  Methods
  -------
  preprocess_data()
      main method to do the feature engineering
class_docstr_tok: ["Provides","methods","for","preprocessing","the","stock","price","data","Attributes","--","--","--","--","--","use_technical_indicator",":","boolean","we","technical","indicator","or","not","tech_indicator_list",":","list","a","list","of","technical","indicator","names","(","modified","from","neofinrl_config.py",")","use_turbulence",":","boolean","use","turbulence","index","or","not","user_defined_feature",":","boolean","use","user","defined","features","or","not","Methods","--","--","--","-","preprocess_data","(",")","main","method","to","do","the","feature","engineering"]
docstr: calculate technical indicators
  use stockstats package to add technical inidactors
  :param data: (df) pandas dataframe
  :return: (df) pandas dataframe
docstr_tok: ["calculate","technical","indicators","use","stockstats","package","to","add","technical","inidactors",":","param","data",":","(","df",")","pandas","dataframe",":","return",":","(","df",")","pandas","dataframe"]
code: def add_technical_indicator(self, data):
      """
      calculate technical indicators
      use stockstats package to add technical inidactors
      :param data: (df) pandas dataframe
      :return: (df) pandas dataframe
      """
      df = data.copy()
      df = df.sort_values(by=["tic", "date"])
      stock = Sdf.retype(df.copy())
      unique_ticker = stock.tic.unique()
  
      for indicator in self.tech_indicator_list:
          indicator_df = pd.DataFrame()
          for i in range(len(unique_ticker)):
              try:
                  temp_indicator = stock[stock.tic == unique_ticker[i]][
                      indicator
                  ]
                  temp_indicator = pd.DataFrame(temp_indicator)
                  temp_indicator["tic"] = unique_ticker[i]
                  temp_indicator["date"] = df[
                      df.tic == unique_ticker[i]
                  ]["date"].to_list()
                  # indicator_df = indicator_df.append(
                  #     temp_indicator, ignore_index=True
                  # )
                  indicator_df = pd.concat(
                      [indicator_df, temp_indicator],
                      axis=0,
                      ignore_index=True,
                  )
              except Exception as e:
                  print(e)
          df = df.merge(
              indicator_df[["tic", "date", indicator]],
              on=["tic", "date"],
              how="left",
          )
      df = df.sort_values(by=["date", "tic"])
      return df
  
imports: from __future__ import annotations
  import datetime
  from multiprocessing.sharedctypes import Value
  import numpy
  import pandas
  from stockstats import StockDataFrame
  from finrl import config
  from finrl.meta.preprocessor.yahoodownloader import YahooDownloader
label_desc: Use image node_id 4 for calling the FeatureEngineer obj's underlying member method code with example usage: obj.add_technical_indicator(data) and returns: df