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: main method to do the feature engineering
  @:param config: source dataframe
  @:return: a DataMatrices object
docstr_tok: ["main","method","to","do","the","feature","engineering","@",":","param","config",":","source","dataframe","@",":","return",":","a","DataMatrices","object"]
code: def preprocess_data(self, df):
      """main method to do the feature engineering
      @:param config: source dataframe
      @:return: a DataMatrices object
      """
      # clean data
      df = self.clean_data(df)
  
      # add technical indicators using stockstats
      if self.use_technical_indicator:
          df = self.add_technical_indicator(df)
          print("Successfully added technical indicators")
  
      # add vix for multiple stock
      if self.use_vix:
          df = self.add_vix(df)
          print("Successfully added vix")
  
      # add turbulence index for multiple stock
      if self.use_turbulence:
          df = self.add_turbulence(df)
          print("Successfully added turbulence index")
  
      # add user defined feature
      if self.user_defined_feature:
          df = self.add_user_defined_feature(df)
          print("Successfully added user defined features")
  
      # fill the missing values at the beginning and the end
      df = df.ffill().bfill()
      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 2 for calling the FeatureEngineer obj's underlying member method code with example usage: obj.preprocess_data(df) and returns: df