docstr: Downloads data using Alpaca's tradeapi.REST method.
  
  Parameters:
  - ticker_list : list of strings, each string is a ticker
  - start_date : string in the format 'YYYY-MM-DD'
  - end_date : string in the format 'YYYY-MM-DD'
  - time_interval: string representing the interval ('1D', '1Min', etc.)
  
  Returns:
  - pd.DataFrame with the requested data
docstr_tok: ["Downloads","data","using","Alpaca","'s","tradeapi.REST","method",".","Parameters",":","-","ticker_list",":","list","of","strings",",","each","string","is","a","ticker","-","start_date",":","string","in","the","format","'YYYY-MM-DD'","-","end_date",":","string","in","the","format","'YYYY-MM-DD'","-","time_interval",":","string","representing","the","interval","(","'1D","'",",","'1Min","'",",","etc",".",")","Returns",":","-","pd.DataFrame","with","the","requested","data"]
code: def download_data(
      self, ticker_list, start_date, end_date, time_interval
  ) -> pd.DataFrame:
      """
      Downloads data using Alpaca's tradeapi.REST method.
  
      Parameters:
      - ticker_list : list of strings, each string is a ticker
      - start_date : string in the format 'YYYY-MM-DD'
      - end_date : string in the format 'YYYY-MM-DD'
      - time_interval: string representing the interval ('1D', '1Min', etc.)
  
      Returns:
      - pd.DataFrame with the requested data
      """
      self.start = start_date
      self.end = end_date
      self.time_interval = time_interval
  
      NY = "America/New_York"
      start_date = pd.Timestamp(start_date + " 09:30:00", tz=NY)
      end_date = pd.Timestamp(end_date + " 15:59:00", tz=NY)
  
      # Use ThreadPoolExecutor to fetch data for multiple tickers concurrently
      with ThreadPoolExecutor(max_workers=10) as executor:
          futures = [
              executor.submit(
                  self._fetch_data_for_ticker,
                  ticker,
                  start_date,
                  end_date,
                  time_interval,
              )
              for ticker in ticker_list
          ]
          data_list = [future.result() for future in futures]
  
      # Combine the data
      data_df = pd.concat(data_list, axis=0)
  
      # Convert the timezone
      data_df = data_df.tz_convert(NY)
  
      # If time_interval is less than a day, filter out the times outside of NYSE trading hours
      if pd.Timedelta(time_interval) < pd.Timedelta(days=1):
          data_df = data_df.between_time("09:30", "15:59")
  
      # Reset the index and rename the columns for consistency
      data_df = data_df.reset_index().rename(
          columns={"index": "timestamp", "symbol": "tic"}
      )
  
      # Sort the data by both timestamp and tic for consistent ordering
      data_df = data_df.sort_values(by=["tic", "timestamp"])
  
      # Reset the index and drop the old index column
      data_df = data_df.reset_index(drop=True)
  
      return data_df
  
imports: from __future__ import annotations
  from concurrent.futures import ProcessPoolExecutor
  from concurrent.futures import ThreadPoolExecutor
  import alpaca_trade_api
  import exchange_calendars
  import numpy
  import pandas
  import pytz
  from stockstats import StockDataFrame
label_desc: Use image node_id 3 for calling the AlpacaProcessor obj's underlying member method code with example usage: obj.download_data(ticker_list, start_date, end_date, time_interval) and returns: data_df