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 get_multiproc_env(self, n=10):
      def get_self():
          return deepcopy(self)
  
      e = SubprocVecEnv(
          [get_self for _ in range(n)], start_method="fork"
      )
      obs = e.reset()
      return e, obs
  
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 16 for calling the StockTradingEnvCashpenalty obj's underlying member method code with example usage: obj.get_multiproc_env(n) and returns: e,  obs, deepcopy