class_docstr: A stock trading environment for OpenAI gym
  This environment penalizes the model for not maintaining a reserve of cash.
  This enables the model to manage cash reserves in addition to performing trading procedures.
  Reward at any step is given as follows
      r_i = (sum(cash, asset_value) - initial_cash - max(0, sum(cash, asset_value)*cash_penalty_proportion-cash))/(days_elapsed)
      This reward function takes into account a liquidity requirement, as well as long-term accrued rewards.
  Parameters:
      df (pandas.DataFrame): Dataframe containing data
      buy_cost_pct (float): cost for buying shares
      sell_cost_pct (float): cost for selling shares
      hmax (int, array): maximum cash to be traded in each trade per asset. If an array is provided, then each index correspond to each asset
      discrete_actions (bool): option to choose whether perform dicretization on actions space or not
      shares_increment (int): multiples number of shares can be bought in each trade. Only applicable if discrete_actions=True
      turbulence_threshold (float): Maximum turbulence allowed in market for purchases to occur. If exceeded, positions are liquidated
      print_verbosity(int): When iterating (step), how often to print stats about state of env
      initial_amount: (int, float): Amount of cash initially available
      daily_information_columns (list(str)): Columns to use when building state space from the dataframe. It could be OHLC columns or any other variables such as technical indicators and turbulence index
      cash_penalty_proportion (int, float): Penalty to apply if the algorithm runs out of cash
      patient (bool): option to choose whether end the cycle when we're running out of cash or just don't buy anything until we got additional cash
  
  RL Inputs and Outputs
      action space: [<n_assets>,] in range {-1, 1}
      state space: {start_cash, [shares_i for in in assets], [[indicator_j for j in indicators] for i in assets]]}
  TODO:
      Organize functions
      Write README
      Document tests
code: def __init__(
      self,
      df,
      buy_cost_pct=3e-3,
      sell_cost_pct=3e-3,
      date_col_name="date",
      hmax=10,
      discrete_actions=False,
      shares_increment=1,
      turbulence_threshold=None,
      print_verbosity=10,
      initial_amount=1e6,
      daily_information_cols=["open", "close", "high", "low", "volume"],
      cache_indicator_data=True,
      cash_penalty_proportion=0.1,
      random_start=True,
      patient=False,
      currency="$",
  ):
      self.df = df
      self.stock_col = "tic"
      self.assets = df[self.stock_col].unique()
      self.dates = df[date_col_name].sort_values().unique()
      self.random_start = random_start
      self.discrete_actions = discrete_actions
      self.patient = patient
      self.currency = currency
      self.df = self.df.set_index(date_col_name)
      self.shares_increment = shares_increment
      self.hmax = hmax
      self.initial_amount = initial_amount
      self.print_verbosity = print_verbosity
      self.buy_cost_pct = buy_cost_pct
      self.sell_cost_pct = sell_cost_pct
      self.turbulence_threshold = turbulence_threshold
      self.daily_information_cols = daily_information_cols
      self.state_space = (
          1
          + len(self.assets)
          + len(self.assets) * len(self.daily_information_cols)
      )
      self.action_space = spaces.Box(
          low=-1, high=1, shape=(len(self.assets),)
      )
      self.observation_space = spaces.Box(
          low=-np.inf, high=np.inf, shape=(self.state_space,)
      )
      self.turbulence = 0
      self.episode = -1  # initialize so we can call reset
      self.episode_history = []
      self.printed_header = False
      self.cache_indicator_data = cache_indicator_data
      self.cached_data = None
      self.cash_penalty_proportion = cash_penalty_proportion
      if self.cache_indicator_data:
          print("caching data")
          self.cached_data = [
              self.get_date_vector(i) for i, _ in enumerate(self.dates)
          ]
          print("data cached!")
  
imports: from __future__ import annotations
  import random
  import time
  from copy import deepcopy
  import gym
  import matplotlib
  import numpy
  import pandas
  from gym import spaces
  from stable_baselines3.common import logger
  from stable_baselines3.common.vec_env import DummyVecEnv
  from stable_baselines3.common.vec_env import SubprocVecEnv
label_desc: Use image node_id 1 to create a new StockTradingEnvCashpenalty object from inherited base classes: gym with example: obj = StockTradingEnvCashpenalty(df, buy_cost_pct, sell_cost_pct, date_col_name, hmax, discrete_actions, shares_increment, turbulence_threshold, print_verbosity, initial_amount, daily_information_cols, cache_indicator_data, cash_penalty_proportion, random_start, patient, currency)