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: clean the raw data
  deal with missing values
  reasons: stocks could be delisted, not incorporated at the time step
  :param data: (df) pandas dataframe
  :return: (df) pandas dataframe
docstr_tok: ["clean","the","raw","data","deal","with","missing","values","reasons",":","stocks","could","be","delisted",",","not","incorporated","at","the","time","step",":","param","data",":","(","df",")","pandas","dataframe",":","return",":","(","df",")","pandas","dataframe"]
code: def clean_data(self, data):
      """
      clean the raw data
      deal with missing values
      reasons: stocks could be delisted, not incorporated at the time step
      :param data: (df) pandas dataframe
      :return: (df) pandas dataframe
      """
      df = data.copy()
      df = df.sort_values(["date", "tic"], ignore_index=True)
      df.index = df.date.factorize()[0]
      merged_closes = df.pivot_table(
          index="date", columns="tic", values="close"
      )
      merged_closes = merged_closes.dropna(axis=1)
      tics = merged_closes.columns
      df = df[df.tic.isin(tics)]
      # df = data.copy()
      # list_ticker = df["tic"].unique().tolist()
      # only apply to daily level data, need to fix for minute level
      # list_date = list(pd.date_range(df['date'].min(),df['date'].max()).astype(str))
      # combination = list(itertools.product(list_date,list_ticker))
  
      # df_full = pd.DataFrame(combination,columns=["date","tic"]).merge(df,on=["date","tic"],how="left")
      # df_full = df_full[df_full['date'].isin(df['date'])]
      # df_full = df_full.sort_values(['date','tic'])
      # df_full = df_full.fillna(0)
      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 3 for calling the FeatureEngineer obj's underlying member method code with example usage: obj.clean_data(data) and returns: df