├── docs ├── output_11_0.png └── output_14_1.png ├── .gitignore ├── gym_anytrading ├── envs │ ├── __init__.py │ ├── stocks_env.py │ ├── forex_env.py │ └── trading_env.py ├── datasets │ ├── __init__.py │ └── utils.py └── __init__.py ├── setup.py ├── LICENSE ├── README.md └── README.ipynb /docs/output_11_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AminHP/gym-anytrading/HEAD/docs/output_11_0.png -------------------------------------------------------------------------------- /docs/output_14_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AminHP/gym-anytrading/HEAD/docs/output_14_1.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | .ipynb_checkpoints/ 4 | .DS_Store 5 | *.pyc 6 | venv*/ 7 | 8 | dist/ 9 | *.egg-info/ 10 | build/ 11 | -------------------------------------------------------------------------------- /gym_anytrading/envs/__init__.py: -------------------------------------------------------------------------------- 1 | from .trading_env import TradingEnv, Actions, Positions 2 | from .forex_env import ForexEnv 3 | from .stocks_env import StocksEnv 4 | -------------------------------------------------------------------------------- /gym_anytrading/datasets/__init__.py: -------------------------------------------------------------------------------- 1 | from .utils import load_dataset as _load_dataset 2 | 3 | 4 | # Load FOREX datasets 5 | FOREX_EURUSD_1H_ASK = _load_dataset('FOREX_EURUSD_1H_ASK', 'Time') 6 | 7 | # Load Stocks datasets 8 | STOCKS_GOOGL = _load_dataset('STOCKS_GOOGL', 'Date') 9 | -------------------------------------------------------------------------------- /gym_anytrading/datasets/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pandas as pd 3 | 4 | 5 | def load_dataset(name, index_name): 6 | base_dir = os.path.dirname(os.path.abspath(__file__)) 7 | path = os.path.join(base_dir, 'data', name + '.csv') 8 | df = pd.read_csv(path, parse_dates=True, index_col=index_name) 9 | return df 10 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='gym_anytrading', 5 | version='2.0.0', 6 | packages=find_packages(), 7 | 8 | author='AminHP', 9 | author_email='mdan.hagh@gmail.com', 10 | license='MIT', 11 | 12 | install_requires=[ 13 | 'gymnasium>=0.29.1', 14 | 'numpy>=1.16.4', 15 | 'pandas>=0.24.2', 16 | 'matplotlib>=3.1.1' 17 | ], 18 | 19 | package_data={ 20 | 'gym_anytrading': ['datasets/data/*'] 21 | } 22 | ) 23 | -------------------------------------------------------------------------------- /gym_anytrading/__init__.py: -------------------------------------------------------------------------------- 1 | from gymnasium.envs.registration import register 2 | from copy import deepcopy 3 | 4 | from . import datasets 5 | 6 | 7 | register( 8 | id='forex-v0', 9 | entry_point='gym_anytrading.envs:ForexEnv', 10 | kwargs={ 11 | 'df': deepcopy(datasets.FOREX_EURUSD_1H_ASK), 12 | 'window_size': 24, 13 | 'frame_bound': (24, len(datasets.FOREX_EURUSD_1H_ASK)) 14 | } 15 | ) 16 | 17 | register( 18 | id='stocks-v0', 19 | entry_point='gym_anytrading.envs:StocksEnv', 20 | kwargs={ 21 | 'df': deepcopy(datasets.STOCKS_GOOGL), 22 | 'window_size': 30, 23 | 'frame_bound': (30, len(datasets.STOCKS_GOOGL)) 24 | } 25 | ) 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Mohammad Amin Haghpanah 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /gym_anytrading/envs/stocks_env.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from .trading_env import TradingEnv, Actions, Positions 4 | 5 | 6 | class StocksEnv(TradingEnv): 7 | 8 | def __init__(self, df, window_size, frame_bound, render_mode=None): 9 | assert len(frame_bound) == 2 10 | 11 | self.frame_bound = frame_bound 12 | super().__init__(df, window_size, render_mode) 13 | 14 | self.trade_fee_bid_percent = 0.01 # unit 15 | self.trade_fee_ask_percent = 0.005 # unit 16 | 17 | def _process_data(self): 18 | prices = self.df.loc[:, 'Close'].to_numpy() 19 | 20 | prices[self.frame_bound[0] - self.window_size] # validate index (TODO: Improve validation) 21 | prices = prices[self.frame_bound[0]-self.window_size:self.frame_bound[1]] 22 | 23 | diff = np.insert(np.diff(prices), 0, 0) 24 | signal_features = np.column_stack((prices, diff)) 25 | 26 | return prices.astype(np.float32), signal_features.astype(np.float32) 27 | 28 | def _calculate_reward(self, action): 29 | step_reward = 0 30 | 31 | trade = False 32 | if ( 33 | (action == Actions.Buy.value and self._position == Positions.Short) or 34 | (action == Actions.Sell.value and self._position == Positions.Long) 35 | ): 36 | trade = True 37 | 38 | if trade: 39 | current_price = self.prices[self._current_tick] 40 | last_trade_price = self.prices[self._last_trade_tick] 41 | price_diff = current_price - last_trade_price 42 | 43 | if self._position == Positions.Long: 44 | step_reward += price_diff 45 | 46 | return step_reward 47 | 48 | def _update_profit(self, action): 49 | trade = False 50 | if ( 51 | (action == Actions.Buy.value and self._position == Positions.Short) or 52 | (action == Actions.Sell.value and self._position == Positions.Long) 53 | ): 54 | trade = True 55 | 56 | if trade or self._truncated: 57 | current_price = self.prices[self._current_tick] 58 | last_trade_price = self.prices[self._last_trade_tick] 59 | 60 | if self._position == Positions.Long: 61 | shares = (self._total_profit * (1 - self.trade_fee_ask_percent)) / last_trade_price 62 | self._total_profit = (shares * (1 - self.trade_fee_bid_percent)) * current_price 63 | 64 | def max_possible_profit(self): 65 | current_tick = self._start_tick 66 | last_trade_tick = current_tick - 1 67 | profit = 1. 68 | 69 | while current_tick <= self._end_tick: 70 | position = None 71 | if self.prices[current_tick] < self.prices[current_tick - 1]: 72 | while (current_tick <= self._end_tick and 73 | self.prices[current_tick] < self.prices[current_tick - 1]): 74 | current_tick += 1 75 | position = Positions.Short 76 | else: 77 | while (current_tick <= self._end_tick and 78 | self.prices[current_tick] >= self.prices[current_tick - 1]): 79 | current_tick += 1 80 | position = Positions.Long 81 | 82 | if position == Positions.Long: 83 | current_price = self.prices[current_tick - 1] 84 | last_trade_price = self.prices[last_trade_tick] 85 | shares = profit / last_trade_price 86 | profit = shares * current_price 87 | last_trade_tick = current_tick - 1 88 | 89 | return profit 90 | -------------------------------------------------------------------------------- /gym_anytrading/envs/forex_env.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from .trading_env import TradingEnv, Actions, Positions 4 | 5 | 6 | class ForexEnv(TradingEnv): 7 | 8 | def __init__(self, df, window_size, frame_bound, unit_side='left', render_mode=None): 9 | assert len(frame_bound) == 2 10 | assert unit_side.lower() in ['left', 'right'] 11 | 12 | self.frame_bound = frame_bound 13 | self.unit_side = unit_side.lower() 14 | super().__init__(df, window_size, render_mode) 15 | 16 | self.trade_fee = 0.0003 # unit 17 | 18 | def _process_data(self): 19 | prices = self.df.loc[:, 'Close'].to_numpy() 20 | 21 | prices[self.frame_bound[0] - self.window_size] # validate index (TODO: Improve validation) 22 | prices = prices[self.frame_bound[0]-self.window_size:self.frame_bound[1]] 23 | 24 | diff = np.insert(np.diff(prices), 0, 0) 25 | signal_features = np.column_stack((prices, diff)) 26 | 27 | return prices.astype(np.float32), signal_features.astype(np.float32) 28 | 29 | def _calculate_reward(self, action): 30 | step_reward = 0 # pip 31 | 32 | trade = False 33 | if ( 34 | (action == Actions.Buy.value and self._position == Positions.Short) or 35 | (action == Actions.Sell.value and self._position == Positions.Long) 36 | ): 37 | trade = True 38 | 39 | if trade: 40 | current_price = self.prices[self._current_tick] 41 | last_trade_price = self.prices[self._last_trade_tick] 42 | price_diff = current_price - last_trade_price 43 | 44 | if self._position == Positions.Short: 45 | step_reward += -price_diff * 10000 46 | elif self._position == Positions.Long: 47 | step_reward += price_diff * 10000 48 | 49 | return step_reward 50 | 51 | def _update_profit(self, action): 52 | trade = False 53 | if ( 54 | (action == Actions.Buy.value and self._position == Positions.Short) or 55 | (action == Actions.Sell.value and self._position == Positions.Long) 56 | ): 57 | trade = True 58 | 59 | if trade or self._truncated: 60 | current_price = self.prices[self._current_tick] 61 | last_trade_price = self.prices[self._last_trade_tick] 62 | 63 | if self.unit_side == 'left': 64 | if self._position == Positions.Short: 65 | quantity = self._total_profit * (last_trade_price - self.trade_fee) 66 | self._total_profit = quantity / current_price 67 | 68 | elif self.unit_side == 'right': 69 | if self._position == Positions.Long: 70 | quantity = self._total_profit / last_trade_price 71 | self._total_profit = quantity * (current_price - self.trade_fee) 72 | 73 | def max_possible_profit(self): 74 | current_tick = self._start_tick 75 | last_trade_tick = current_tick - 1 76 | profit = 1. 77 | 78 | while current_tick <= self._end_tick: 79 | position = None 80 | if self.prices[current_tick] < self.prices[current_tick - 1]: 81 | while (current_tick <= self._end_tick and 82 | self.prices[current_tick] < self.prices[current_tick - 1]): 83 | current_tick += 1 84 | position = Positions.Short 85 | else: 86 | while (current_tick <= self._end_tick and 87 | self.prices[current_tick] >= self.prices[current_tick - 1]): 88 | current_tick += 1 89 | position = Positions.Long 90 | 91 | current_price = self.prices[current_tick - 1] 92 | last_trade_price = self.prices[last_trade_tick] 93 | 94 | if self.unit_side == 'left': 95 | if position == Positions.Short: 96 | quantity = profit * (last_trade_price - self.trade_fee) 97 | profit = quantity / current_price 98 | 99 | elif self.unit_side == 'right': 100 | if position == Positions.Long: 101 | quantity = profit / last_trade_price 102 | profit = quantity * (current_price - self.trade_fee) 103 | 104 | last_trade_tick = current_tick - 1 105 | 106 | return profit 107 | -------------------------------------------------------------------------------- /gym_anytrading/envs/trading_env.py: -------------------------------------------------------------------------------- 1 | from time import time 2 | from enum import Enum 3 | 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | 7 | import gymnasium as gym 8 | 9 | 10 | class Actions(Enum): 11 | Sell = 0 12 | Buy = 1 13 | 14 | 15 | class Positions(Enum): 16 | Short = 0 17 | Long = 1 18 | 19 | def opposite(self): 20 | return Positions.Short if self == Positions.Long else Positions.Long 21 | 22 | 23 | class TradingEnv(gym.Env): 24 | 25 | metadata = {'render_modes': ['human'], 'render_fps': 3} 26 | 27 | def __init__(self, df, window_size, render_mode=None): 28 | assert df.ndim == 2 29 | assert render_mode is None or render_mode in self.metadata['render_modes'] 30 | 31 | self.render_mode = render_mode 32 | 33 | self.df = df 34 | self.window_size = window_size 35 | self.prices, self.signal_features = self._process_data() 36 | self.shape = (window_size, self.signal_features.shape[1]) 37 | 38 | # spaces 39 | self.action_space = gym.spaces.Discrete(len(Actions)) 40 | INF = 1e10 41 | self.observation_space = gym.spaces.Box( 42 | low=-INF, high=INF, shape=self.shape, dtype=np.float32, 43 | ) 44 | 45 | # episode 46 | self._start_tick = self.window_size 47 | self._end_tick = len(self.prices) - 1 48 | self._truncated = None 49 | self._current_tick = None 50 | self._last_trade_tick = None 51 | self._position = None 52 | self._position_history = None 53 | self._total_reward = None 54 | self._total_profit = None 55 | self._first_rendering = None 56 | self.history = None 57 | 58 | def reset(self, seed=None, options=None): 59 | super().reset(seed=seed, options=options) 60 | self.action_space.seed(int((self.np_random.uniform(0, seed if seed is not None else 1)))) 61 | 62 | self._truncated = False 63 | self._current_tick = self._start_tick 64 | self._last_trade_tick = self._current_tick - 1 65 | self._position = Positions.Short 66 | self._position_history = (self.window_size * [None]) + [self._position] 67 | self._total_reward = 0. 68 | self._total_profit = 1. # unit 69 | self._first_rendering = True 70 | self.history = {} 71 | 72 | observation = self._get_observation() 73 | info = self._get_info() 74 | 75 | if self.render_mode == 'human': 76 | self._render_frame() 77 | 78 | return observation, info 79 | 80 | def step(self, action): 81 | self._truncated = False 82 | self._current_tick += 1 83 | 84 | if self._current_tick == self._end_tick: 85 | self._truncated = True 86 | 87 | step_reward = self._calculate_reward(action) 88 | self._total_reward += step_reward 89 | 90 | self._update_profit(action) 91 | 92 | trade = False 93 | if ( 94 | (action == Actions.Buy.value and self._position == Positions.Short) or 95 | (action == Actions.Sell.value and self._position == Positions.Long) 96 | ): 97 | trade = True 98 | 99 | if trade: 100 | self._position = self._position.opposite() 101 | self._last_trade_tick = self._current_tick 102 | 103 | self._position_history.append(self._position) 104 | observation = self._get_observation() 105 | info = self._get_info() 106 | self._update_history(info) 107 | 108 | if self.render_mode == 'human': 109 | self._render_frame() 110 | 111 | return observation, step_reward, False, self._truncated, info 112 | 113 | def _get_info(self): 114 | return dict( 115 | total_reward=self._total_reward, 116 | total_profit=self._total_profit, 117 | position=self._position 118 | ) 119 | 120 | def _get_observation(self): 121 | return self.signal_features[(self._current_tick-self.window_size+1):self._current_tick+1] 122 | 123 | def _update_history(self, info): 124 | if not self.history: 125 | self.history = {key: [] for key in info.keys()} 126 | 127 | for key, value in info.items(): 128 | self.history[key].append(value) 129 | 130 | def _render_frame(self): 131 | self.render() 132 | 133 | def render(self, mode='human'): 134 | 135 | def _plot_position(position, tick): 136 | color = None 137 | if position == Positions.Short: 138 | color = 'red' 139 | elif position == Positions.Long: 140 | color = 'green' 141 | if color: 142 | plt.scatter(tick, self.prices[tick], color=color) 143 | 144 | start_time = time() 145 | 146 | if self._first_rendering: 147 | self._first_rendering = False 148 | plt.cla() 149 | plt.plot(self.prices) 150 | start_position = self._position_history[self._start_tick] 151 | _plot_position(start_position, self._start_tick) 152 | 153 | _plot_position(self._position, self._current_tick) 154 | 155 | plt.suptitle( 156 | "Total Reward: %.6f" % self._total_reward + ' ~ ' + 157 | "Total Profit: %.6f" % self._total_profit 158 | ) 159 | 160 | end_time = time() 161 | process_time = end_time - start_time 162 | 163 | pause_time = (1 / self.metadata['render_fps']) - process_time 164 | assert pause_time > 0., "High FPS! Try to reduce the 'render_fps' value." 165 | 166 | plt.pause(pause_time) 167 | 168 | def render_all(self, title=None): 169 | window_ticks = np.arange(len(self._position_history)) 170 | plt.plot(self.prices) 171 | 172 | short_ticks = [] 173 | long_ticks = [] 174 | for i, tick in enumerate(window_ticks): 175 | if self._position_history[i] == Positions.Short: 176 | short_ticks.append(tick) 177 | elif self._position_history[i] == Positions.Long: 178 | long_ticks.append(tick) 179 | 180 | plt.plot(short_ticks, self.prices[short_ticks], 'ro') 181 | plt.plot(long_ticks, self.prices[long_ticks], 'go') 182 | 183 | if title: 184 | plt.title(title) 185 | 186 | plt.suptitle( 187 | "Total Reward: %.6f" % self._total_reward + ' ~ ' + 188 | "Total Profit: %.6f" % self._total_profit 189 | ) 190 | 191 | def close(self): 192 | plt.close() 193 | 194 | def save_rendering(self, filepath): 195 | plt.savefig(filepath) 196 | 197 | def pause_rendering(self): 198 | plt.show() 199 | 200 | def _process_data(self): 201 | raise NotImplementedError 202 | 203 | def _calculate_reward(self, action): 204 | raise NotImplementedError 205 | 206 | def _update_profit(self, action): 207 | raise NotImplementedError 208 | 209 | def max_possible_profit(self): # trade fees are ignored 210 | raise NotImplementedError 211 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gym-anytrading 2 | 3 | `AnyTrading` is a collection of [OpenAI Gym](https://github.com/openai/gym) environments for reinforcement learning-based trading algorithms. 4 | 5 | Trading algorithms are mostly implemented in two markets: [FOREX](https://en.wikipedia.org/wiki/Foreign_exchange_market) and [Stock](https://en.wikipedia.org/wiki/Stock). AnyTrading aims to provide some Gym environments to improve and facilitate the procedure of developing and testing RL-based algorithms in this area. This purpose is obtained by implementing three Gym environments: **TradingEnv**, **ForexEnv**, and **StocksEnv**. 6 | 7 | TradingEnv is an abstract environment which is defined to support all kinds of trading environments. ForexEnv and StocksEnv are simply two environments that inherit and extend TradingEnv. In the future sections, more explanations will be given about them but before that, some environment properties should be discussed. 8 | 9 | **Note:** For experts, it is recommended to check out the [gym-mtsim](https://github.com/AminHP/gym-mtsim) project. 10 | 11 | ## Installation 12 | 13 | ### Via PIP 14 | ```bash 15 | pip install gym-anytrading 16 | ``` 17 | 18 | ### From Repository 19 | ```bash 20 | git clone https://github.com/AminHP/gym-anytrading 21 | cd gym-anytrading 22 | pip install -e . 23 | 24 | ## or 25 | 26 | pip install --upgrade --no-deps --force-reinstall https://github.com/AminHP/gym-anytrading/archive/master.zip 27 | ``` 28 | 29 | ## Environment Properties 30 | First of all, **you can't simply expect an RL agent to do everything for you and just sit back on your chair in such complex trading markets!** 31 | Things need to be simplified as much as possible in order to let the agent learn in a faster and more efficient way. In all trading algorithms, the first thing that should be done is to define **actions** and **positions**. In the two following subsections, I will explain these actions and positions and how to simplify them. 32 | 33 | ### Trading Actions 34 | If you search on the Internet for trading algorithms, you will find them using numerous actions such as **Buy**, **Sell**, **Hold**, **Enter**, **Exit**, etc. 35 | Referring to the first statement of this section, a typical RL agent can only solve a part of the main problem in this area. If you work in trading markets you will learn that deciding whether to hold, enter, or exit a pair (in FOREX) or stock (in Stocks) is a statistical decision depending on many parameters such as your budget, pairs or stocks you trade, your money distribution policy in multiple markets, etc. It's a massive burden for an RL agent to consider all these parameters and may take years to develop such an agent! In this case, you certainly will not use this environment but you will extend your own. 36 | 37 | So after months of work, I finally found out that these actions just make things complicated with no real positive impact. In fact, they just increase the learning time and an action like **Hold** will be barely used by a well-trained agent because it doesn't want to miss a single penny. Therefore there is no need to have such numerous actions and only `Sell=0` and `Buy=1` actions are adequate to train an agent just as well. 38 | 39 | ### Trading Positions 40 | If you're not familiar with trading positions, refer [here](https://en.wikipedia.org/wiki/Position_\(finance\)). It's a very important concept and you should learn it as soon as possible. 41 | 42 | In a simple vision: **Long** position wants to buy shares when prices are low and profit by sticking with them while their value is going up, and **Short** position wants to sell shares with high value and use this value to buy shares at a lower value, keeping the difference as profit. 43 | 44 | Again, in some trading algorithms, you may find numerous positions such as **Short**, **Long**, **Flat**, etc. As discussed earlier, I use only `Short=0` and `Long=1` positions. 45 | 46 | ## Trading Environments 47 | As I noticed earlier, now it's time to introduce the three environments. Before creating this project, I spent so much time to search for a simple and flexible Gym environment for any trading market but didn't find one. They were almost a bunch of complex codes with many unclear parameters that you couldn't simply look at them and comprehend what's going on. So I concluded to implement this project with a great focus on simplicity, flexibility, and comprehensiveness. 48 | 49 | In the three following subsections, I will introduce our trading environments and in the next section, some IPython examples will be mentioned and briefly explained. 50 | 51 | ### TradingEnv 52 | TradingEnv is an abstract class which inherits `gym.Env`. This class aims to provide a general-purpose environment for all kinds of trading markets. Here I explain its public properties and methods. But feel free to take a look at the complete [source code](https://github.com/AminHP/gym-anytrading/blob/master/gym_anytrading/envs/trading_env.py). 53 | 54 | * Properties: 55 | > `df`: An abbreviation for **DataFrame**. It's a **pandas'** DataFrame which contains your dataset and is passed in the class' constructor. 56 | > 57 | > `prices`: Real prices over time. Used to calculate profit and render the environment. 58 | > 59 | > `signal_features`: Extracted features over time. Used to create *Gym observations*. 60 | > 61 | > `window_size`: Number of ticks (current and previous ticks) returned as a *Gym observation*. It is passed in the class' constructor. 62 | > 63 | > `action_space`: The *Gym action_space* property. Containing discrete values of **0=Sell** and **1=Buy**. 64 | > 65 | > `observation_space`: The *Gym observation_space* property. Each observation is a window on `signal_features` from index **current_tick - window_size + 1** to **current_tick**. So `_start_tick` of the environment would be equal to `window_size`. In addition, initial value for `_last_trade_tick` is **window_size - 1** . 66 | > 67 | > `shape`: Shape of a single observation. 68 | > 69 | > `history`: Stores the information of all steps. 70 | 71 | * Methods: 72 | > `seed`: Typical *Gym seed* method. 73 | > 74 | > `reset`: Typical *Gym reset* method. 75 | > 76 | > `step`: Typical *Gym step* method. 77 | > 78 | > `render`: Typical *Gym render* method. Renders the information of the environment's current tick. 79 | > 80 | > `render_all`: Renders the whole environment. 81 | > 82 | > `close`: Typical *Gym close* method. 83 | 84 | * Abstract Methods: 85 | > `_process_data`: It is called in the constructor and returns `prices` and `signal_features` as a tuple. In different trading markets, different features need to be obtained. So this method enables our TradingEnv to be a general-purpose environment and specific features can be returned for specific environments such as *FOREX*, *Stocks*, etc. 86 | > 87 | > `_calculate_reward`: The reward function for the RL agent. 88 | > 89 | > `_update_profit`: Calculates and updates total profit which the RL agent has achieved so far. Profit indicates the amount of units of currency you have achieved by starting with *1.0* unit (Profit = FinalMoney / StartingMoney). 90 | > 91 | > `max_possible_profit`: The maximum possible profit that an RL agent can obtain regardless of trade fees. 92 | 93 | ### ForexEnv 94 | This is a concrete class which inherits TradingEnv and implements its abstract methods. Also, it has some specific properties for the *FOREX* market. For more information refer to the [source code](https://github.com/AminHP/gym-anytrading/blob/master/gym_anytrading/envs/forex_env.py). 95 | 96 | * Properties: 97 | > `frame_bound`: A tuple which specifies the start and end of `df`. It is passed in the class' constructor. 98 | > 99 | > `unit_side`: Specifies the side you start your trading. Containing string values of **left** (default value) and **right**. As you know, there are two sides in a currency pair in *FOREX*. For example in the *EUR/USD* pair, when you choose the `left` side, your currency unit is *EUR* and you start your trading with 1 EUR. It is passed in the class' constructor. 100 | > 101 | > `trade_fee`: A default constant fee which is subtracted from the real prices on every trade. 102 | 103 | 104 | ### StocksEnv 105 | Same as ForexEnv but for the *Stock* market. For more information refer to the [source code](https://github.com/AminHP/gym-anytrading/blob/master/gym_anytrading/envs/stocks_env.py). 106 | 107 | * Properties: 108 | > `frame_bound`: A tuple which specifies the start and end of `df`. It is passed in the class' constructor. 109 | > 110 | > `trade_fee_bid_percent`: A default constant fee percentage for bids. For example with trade_fee_bid_percent=0.01, you will lose 1% of your money every time you sell your shares. 111 | > 112 | > `trade_fee_ask_percent`: A default constant fee percentage for asks. For example with trade_fee_ask_percent=0.005, you will lose 0.5% of your money every time you buy some shares. 113 | 114 | Besides, you can create your own customized environment by extending TradingEnv or even ForexEnv or StocksEnv with your desired policies for calculating reward, profit, fee, etc. 115 | 116 | ## Examples 117 | 118 | 119 | ### Create an environment 120 | 121 | 122 | ```python 123 | import gymnasium as gym 124 | import gym_anytrading 125 | 126 | env = gym.make('forex-v0') 127 | # env = gym.make('stocks-v0') 128 | ``` 129 | 130 | - This will create the default environment. You can change any parameters such as dataset, frame_bound, etc. 131 | 132 | ### Create an environment with custom parameters 133 | I put two default datasets for [*FOREX*](https://github.com/AminHP/gym-anytrading/blob/master/gym_anytrading/datasets/data/FOREX_EURUSD_1H_ASK.csv) and [*Stocks*](https://github.com/AminHP/gym-anytrading/blob/master/gym_anytrading/datasets/data/STOCKS_GOOGL.csv) but you can use your own. 134 | 135 | 136 | ```python 137 | from gym_anytrading.datasets import FOREX_EURUSD_1H_ASK, STOCKS_GOOGL 138 | 139 | custom_env = gym.make( 140 | 'forex-v0', 141 | df=FOREX_EURUSD_1H_ASK, 142 | window_size=10, 143 | frame_bound=(10, 300), 144 | unit_side='right' 145 | ) 146 | 147 | # custom_env = gym.make( 148 | # 'stocks-v0', 149 | # df=STOCKS_GOOGL, 150 | # window_size=10, 151 | # frame_bound=(10, 300) 152 | # ) 153 | ``` 154 | 155 | - It is to be noted that the first element of `frame_bound` should be greater than or equal to `window_size`. 156 | 157 | ### Print some information 158 | 159 | 160 | ```python 161 | print("env information:") 162 | print("> shape:", env.unwrapped.shape) 163 | print("> df.shape:", env.unwrapped.df.shape) 164 | print("> prices.shape:", env.unwrapped.prices.shape) 165 | print("> signal_features.shape:", env.unwrapped.signal_features.shape) 166 | print("> max_possible_profit:", env.unwrapped.max_possible_profit()) 167 | 168 | print() 169 | print("custom_env information:") 170 | print("> shape:", custom_env.unwrapped.shape) 171 | print("> df.shape:", custom_env.unwrapped.df.shape) 172 | print("> prices.shape:", custom_env.unwrapped.prices.shape) 173 | print("> signal_features.shape:", custom_env.unwrapped.signal_features.shape) 174 | print("> max_possible_profit:", custom_env.unwrapped.max_possible_profit()) 175 | ``` 176 | 177 | env information: 178 | > shape: (24, 2) 179 | > df.shape: (6225, 5) 180 | > prices.shape: (6225,) 181 | > signal_features.shape: (6225, 2) 182 | > max_possible_profit: 4.054407219413578 183 | 184 | custom_env information: 185 | > shape: (10, 2) 186 | > df.shape: (6225, 5) 187 | > prices.shape: (300,) 188 | > signal_features.shape: (300, 2) 189 | > max_possible_profit: 1.1228998536878634 190 | 191 | 192 | - Here `max_possible_profit` signifies that if the market didn't have trade fees, you could have earned **4.054414887146572** (or **1.1229001800089833**) units of currency by starting with **1.0**. In other words, your money is almost *quadrupled*. 193 | 194 | ### Plot the environment 195 | 196 | 197 | ```python 198 | env.reset() 199 | env.render() 200 | ``` 201 | 202 | 203 | 204 | ![png](docs/output_11_0.png) 205 | 206 | 207 | 208 | - **Short** and **Long** positions are shown in `red` and `green` colors. 209 | - As you see, the starting *position* of the environment is always **Short**. 210 | 211 | ### A complete example 212 | 213 | 214 | ```python 215 | import numpy as np 216 | import matplotlib.pyplot as plt 217 | 218 | import gymnasium as gym 219 | import gym_anytrading 220 | from gym_anytrading.envs import TradingEnv, ForexEnv, StocksEnv, Actions, Positions 221 | from gym_anytrading.datasets import FOREX_EURUSD_1H_ASK, STOCKS_GOOGL 222 | 223 | 224 | env = gym.make('forex-v0', frame_bound=(50, 100), window_size=10) 225 | # env = gym.make('stocks-v0', frame_bound=(50, 100), window_size=10) 226 | 227 | observation = env.reset(seed=2023) 228 | while True: 229 | action = env.action_space.sample() 230 | observation, reward, terminated, truncated, info = env.step(action) 231 | done = terminated or truncated 232 | 233 | # env.render() 234 | if done: 235 | print("info:", info) 236 | break 237 | 238 | plt.cla() 239 | env.unwrapped.render_all() 240 | plt.show() 241 | ``` 242 | 243 | info: {'total_reward': 27.89616584777832, 'total_profit': 0.989812615901, 'position': } 244 | 245 | 246 | 247 | 248 | ![png](docs/output_14_1.png) 249 | 250 | 251 | 252 | - You can use `render_all` method to avoid rendering on each step and prevent time-wasting. 253 | - As you see, the first **10** points (`window_size`=10) on the plot don't have a *position*. Because they aren't involved in calculating reward, profit, etc. They just display the first observations. So the environment's `_start_tick` and initial `_last_trade_tick` are **10** and **9**. 254 | 255 | #### More examples 256 | 257 | [Here](https://github.com/AminHP/gym-anytrading/blob/master/examples) are some examples that mix `gym-anytrading` with some well-known libraries, such as `Stable-Baselines3` and `QuantStats`, and show how to utilize our trading environments in other RL or trading libraries. 258 | 259 | ### Extend and manipulate TradingEnv 260 | 261 | In case you want to process data and extract features outside the environment, it can be simply done by two methods: 262 | 263 | **Method 1 (Recommended):** 264 | 265 | 266 | ```python 267 | def my_process_data(env): 268 | start = env.frame_bound[0] - env.window_size 269 | end = env.frame_bound[1] 270 | prices = env.df.loc[:, 'Low'].to_numpy()[start:end] 271 | signal_features = env.df.loc[:, ['Close', 'Open', 'High', 'Low']].to_numpy()[start:end] 272 | return prices, signal_features 273 | 274 | 275 | class MyForexEnv(ForexEnv): 276 | _process_data = my_process_data 277 | 278 | 279 | env = MyForexEnv(df=FOREX_EURUSD_1H_ASK, window_size=12, frame_bound=(12, len(FOREX_EURUSD_1H_ASK))) 280 | ``` 281 | 282 | **Method 2:** 283 | 284 | 285 | ```python 286 | def my_process_data(df, window_size, frame_bound): 287 | start = frame_bound[0] - window_size 288 | end = frame_bound[1] 289 | prices = df.loc[:, 'Low'].to_numpy()[start:end] 290 | signal_features = df.loc[:, ['Close', 'Open', 'High', 'Low']].to_numpy()[start:end] 291 | return prices, signal_features 292 | 293 | 294 | class MyStocksEnv(StocksEnv): 295 | 296 | def __init__(self, prices, signal_features, **kwargs): 297 | self._prices = prices 298 | self._signal_features = signal_features 299 | super().__init__(**kwargs) 300 | 301 | def _process_data(self): 302 | return self._prices, self._signal_features 303 | 304 | 305 | prices, signal_features = my_process_data(df=STOCKS_GOOGL, window_size=30, frame_bound=(30, len(STOCKS_GOOGL))) 306 | env = MyStocksEnv(prices, signal_features, df=STOCKS_GOOGL, window_size=30, frame_bound=(30, len(STOCKS_GOOGL))) 307 | ``` 308 | 309 | ## Related Projects 310 | 311 | * A more complicated version of `anytrading` with five actions, three positions, and a better reward function is developed in the [DI-engine](https://github.com/opendilab/DI-engine/tree/main/dizoo/gym_anytrading) project. It is a mid-level tool (somewhere between `anytrading` and `mtsim`), appropriate for semi-experts. More information and documentation can be found [here](https://github.com/opendilab/DI-engine/tree/main/dizoo/gym_anytrading/envs). 312 | -------------------------------------------------------------------------------- /README.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# gym-anytrading\n", 8 | "\n", 9 | "`AnyTrading` is a collection of [OpenAI Gym](https://github.com/openai/gym) environments for reinforcement learning-based trading algorithms.\n", 10 | "\n", 11 | "Trading algorithms are mostly implemented in two markets: [FOREX](https://en.wikipedia.org/wiki/Foreign_exchange_market) and [Stock](https://en.wikipedia.org/wiki/Stock). AnyTrading aims to provide some Gym environments to improve and facilitate the procedure of developing and testing RL-based algorithms in this area. This purpose is obtained by implementing three Gym environments: **TradingEnv**, **ForexEnv**, and **StocksEnv**.\n", 12 | "\n", 13 | "TradingEnv is an abstract environment which is defined to support all kinds of trading environments. ForexEnv and StocksEnv are simply two environments that inherit and extend TradingEnv. In the future sections, more explanations will be given about them but before that, some environment properties should be discussed.\n", 14 | "\n", 15 | "**Note:** For experts, it is recommended to check out the [gym-mtsim](https://github.com/AminHP/gym-mtsim) project.\n", 16 | "\n", 17 | "## Installation\n", 18 | "\n", 19 | "### Via PIP\n", 20 | "```bash\n", 21 | "pip install gym-anytrading\n", 22 | "```\n", 23 | "\n", 24 | "### From Repository\n", 25 | "```bash\n", 26 | "git clone https://github.com/AminHP/gym-anytrading\n", 27 | "cd gym-anytrading\n", 28 | "pip install -e .\n", 29 | "\n", 30 | "## or\n", 31 | "\n", 32 | "pip install --upgrade --no-deps --force-reinstall https://github.com/AminHP/gym-anytrading/archive/master.zip\n", 33 | "```\n", 34 | "\n", 35 | "## Environment Properties\n", 36 | "First of all, **you can't simply expect an RL agent to do everything for you and just sit back on your chair in such complex trading markets!**\n", 37 | "Things need to be simplified as much as possible in order to let the agent learn in a faster and more efficient way. In all trading algorithms, the first thing that should be done is to define **actions** and **positions**. In the two following subsections, I will explain these actions and positions and how to simplify them.\n", 38 | "\n", 39 | "### Trading Actions\n", 40 | "If you search on the Internet for trading algorithms, you will find them using numerous actions such as **Buy**, **Sell**, **Hold**, **Enter**, **Exit**, etc.\n", 41 | "Referring to the first statement of this section, a typical RL agent can only solve a part of the main problem in this area. If you work in trading markets you will learn that deciding whether to hold, enter, or exit a pair (in FOREX) or stock (in Stocks) is a statistical decision depending on many parameters such as your budget, pairs or stocks you trade, your money distribution policy in multiple markets, etc. It's a massive burden for an RL agent to consider all these parameters and may take years to develop such an agent! In this case, you certainly will not use this environment but you will extend your own.\n", 42 | "\n", 43 | "So after months of work, I finally found out that these actions just make things complicated with no real positive impact. In fact, they just increase the learning time and an action like **Hold** will be barely used by a well-trained agent because it doesn't want to miss a single penny. Therefore there is no need to have such numerous actions and only `Sell=0` and `Buy=1` actions are adequate to train an agent just as well.\n", 44 | "\n", 45 | "### Trading Positions\n", 46 | "If you're not familiar with trading positions, refer [here](https://en.wikipedia.org/wiki/Position_\\(finance\\)). It's a very important concept and you should learn it as soon as possible.\n", 47 | "\n", 48 | "In a simple vision: **Long** position wants to buy shares when prices are low and profit by sticking with them while their value is going up, and **Short** position wants to sell shares with high value and use this value to buy shares at a lower value, keeping the difference as profit.\n", 49 | "\n", 50 | "Again, in some trading algorithms, you may find numerous positions such as **Short**, **Long**, **Flat**, etc. As discussed earlier, I use only `Short=0` and `Long=1` positions.\n", 51 | "\n", 52 | "## Trading Environments\n", 53 | "As I noticed earlier, now it's time to introduce the three environments. Before creating this project, I spent so much time to search for a simple and flexible Gym environment for any trading market but didn't find one. They were almost a bunch of complex codes with many unclear parameters that you couldn't simply look at them and comprehend what's going on. So I concluded to implement this project with a great focus on simplicity, flexibility, and comprehensiveness.\n", 54 | "\n", 55 | "In the three following subsections, I will introduce our trading environments and in the next section, some IPython examples will be mentioned and briefly explained.\n", 56 | "\n", 57 | "### TradingEnv\n", 58 | "TradingEnv is an abstract class which inherits `gym.Env`. This class aims to provide a general-purpose environment for all kinds of trading markets. Here I explain its public properties and methods. But feel free to take a look at the complete [source code](https://github.com/AminHP/gym-anytrading/blob/master/gym_anytrading/envs/trading_env.py).\n", 59 | "\n", 60 | "* Properties:\n", 61 | "> `df`: An abbreviation for **DataFrame**. It's a **pandas'** DataFrame which contains your dataset and is passed in the class' constructor.\n", 62 | ">\n", 63 | "> `prices`: Real prices over time. Used to calculate profit and render the environment.\n", 64 | ">\n", 65 | "> `signal_features`: Extracted features over time. Used to create *Gym observations*.\n", 66 | ">\n", 67 | "> `window_size`: Number of ticks (current and previous ticks) returned as a *Gym observation*. It is passed in the class' constructor.\n", 68 | ">\n", 69 | "> `action_space`: The *Gym action_space* property. Containing discrete values of **0=Sell** and **1=Buy**.\n", 70 | ">\n", 71 | "> `observation_space`: The *Gym observation_space* property. Each observation is a window on `signal_features` from index **current_tick - window_size + 1** to **current_tick**. So `_start_tick` of the environment would be equal to `window_size`. In addition, initial value for `_last_trade_tick` is **window_size - 1** .\n", 72 | ">\n", 73 | "> `shape`: Shape of a single observation.\n", 74 | ">\n", 75 | "> `history`: Stores the information of all steps.\n", 76 | "\n", 77 | "* Methods:\n", 78 | "> `seed`: Typical *Gym seed* method.\n", 79 | ">\n", 80 | "> `reset`: Typical *Gym reset* method.\n", 81 | ">\n", 82 | "> `step`: Typical *Gym step* method.\n", 83 | ">\n", 84 | "> `render`: Typical *Gym render* method. Renders the information of the environment's current tick.\n", 85 | ">\n", 86 | "> `render_all`: Renders the whole environment.\n", 87 | ">\n", 88 | "> `close`: Typical *Gym close* method.\n", 89 | "\n", 90 | "* Abstract Methods:\n", 91 | "> `_process_data`: It is called in the constructor and returns `prices` and `signal_features` as a tuple. In different trading markets, different features need to be obtained. So this method enables our TradingEnv to be a general-purpose environment and specific features can be returned for specific environments such as *FOREX*, *Stocks*, etc.\n", 92 | ">\n", 93 | "> `_calculate_reward`: The reward function for the RL agent.\n", 94 | ">\n", 95 | "> `_update_profit`: Calculates and updates total profit which the RL agent has achieved so far. Profit indicates the amount of units of currency you have achieved by starting with *1.0* unit (Profit = FinalMoney / StartingMoney).\n", 96 | ">\n", 97 | "> `max_possible_profit`: The maximum possible profit that an RL agent can obtain regardless of trade fees.\n", 98 | "\n", 99 | "### ForexEnv\n", 100 | "This is a concrete class which inherits TradingEnv and implements its abstract methods. Also, it has some specific properties for the *FOREX* market. For more information refer to the [source code](https://github.com/AminHP/gym-anytrading/blob/master/gym_anytrading/envs/forex_env.py).\n", 101 | "\n", 102 | "* Properties:\n", 103 | "> `frame_bound`: A tuple which specifies the start and end of `df`. It is passed in the class' constructor.\n", 104 | ">\n", 105 | "> `unit_side`: Specifies the side you start your trading. Containing string values of **left** (default value) and **right**. As you know, there are two sides in a currency pair in *FOREX*. For example in the *EUR/USD* pair, when you choose the `left` side, your currency unit is *EUR* and you start your trading with 1 EUR. It is passed in the class' constructor.\n", 106 | ">\n", 107 | "> `trade_fee`: A default constant fee which is subtracted from the real prices on every trade.\n", 108 | "\n", 109 | "\n", 110 | "### StocksEnv\n", 111 | "Same as ForexEnv but for the *Stock* market. For more information refer to the [source code](https://github.com/AminHP/gym-anytrading/blob/master/gym_anytrading/envs/stocks_env.py).\n", 112 | "\n", 113 | "* Properties:\n", 114 | "> `frame_bound`: A tuple which specifies the start and end of `df`. It is passed in the class' constructor.\n", 115 | ">\n", 116 | "> `trade_fee_bid_percent`: A default constant fee percentage for bids. For example with trade_fee_bid_percent=0.01, you will lose 1% of your money every time you sell your shares.\n", 117 | ">\n", 118 | "> `trade_fee_ask_percent`: A default constant fee percentage for asks. For example with trade_fee_ask_percent=0.005, you will lose 0.5% of your money every time you buy some shares.\n", 119 | "\n", 120 | "Besides, you can create your own customized environment by extending TradingEnv or even ForexEnv or StocksEnv with your desired policies for calculating reward, profit, fee, etc.\n", 121 | "\n", 122 | "## Examples\n" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "### Create an environment" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": 1, 135 | "metadata": {}, 136 | "outputs": [], 137 | "source": [ 138 | "import gymnasium as gym\n", 139 | "import gym_anytrading\n", 140 | "\n", 141 | "env = gym.make('forex-v0')\n", 142 | "# env = gym.make('stocks-v0')" 143 | ] 144 | }, 145 | { 146 | "cell_type": "markdown", 147 | "metadata": {}, 148 | "source": [ 149 | "- This will create the default environment. You can change any parameters such as dataset, frame_bound, etc." 150 | ] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "metadata": {}, 155 | "source": [ 156 | "### Create an environment with custom parameters\n", 157 | "I put two default datasets for [*FOREX*](https://github.com/AminHP/gym-anytrading/blob/master/gym_anytrading/datasets/data/FOREX_EURUSD_1H_ASK.csv) and [*Stocks*](https://github.com/AminHP/gym-anytrading/blob/master/gym_anytrading/datasets/data/STOCKS_GOOGL.csv) but you can use your own." 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": 2, 163 | "metadata": {}, 164 | "outputs": [], 165 | "source": [ 166 | "from gym_anytrading.datasets import FOREX_EURUSD_1H_ASK, STOCKS_GOOGL\n", 167 | "\n", 168 | "custom_env = gym.make(\n", 169 | " 'forex-v0',\n", 170 | " df=FOREX_EURUSD_1H_ASK,\n", 171 | " window_size=10,\n", 172 | " frame_bound=(10, 300),\n", 173 | " unit_side='right'\n", 174 | ")\n", 175 | "\n", 176 | "# custom_env = gym.make(\n", 177 | "# 'stocks-v0',\n", 178 | "# df=STOCKS_GOOGL,\n", 179 | "# window_size=10,\n", 180 | "# frame_bound=(10, 300)\n", 181 | "# )" 182 | ] 183 | }, 184 | { 185 | "cell_type": "markdown", 186 | "metadata": {}, 187 | "source": [ 188 | "- It is to be noted that the first element of `frame_bound` should be greater than or equal to `window_size`." 189 | ] 190 | }, 191 | { 192 | "cell_type": "markdown", 193 | "metadata": {}, 194 | "source": [ 195 | "### Print some information" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": 3, 201 | "metadata": {}, 202 | "outputs": [ 203 | { 204 | "name": "stdout", 205 | "output_type": "stream", 206 | "text": [ 207 | "env information:\n", 208 | "> shape: (24, 2)\n", 209 | "> df.shape: (6225, 5)\n", 210 | "> prices.shape: (6225,)\n", 211 | "> signal_features.shape: (6225, 2)\n", 212 | "> max_possible_profit: 4.054407219413578\n", 213 | "\n", 214 | "custom_env information:\n", 215 | "> shape: (10, 2)\n", 216 | "> df.shape: (6225, 5)\n", 217 | "> prices.shape: (300,)\n", 218 | "> signal_features.shape: (300, 2)\n", 219 | "> max_possible_profit: 1.1228998536878634\n" 220 | ] 221 | } 222 | ], 223 | "source": [ 224 | "print(\"env information:\")\n", 225 | "print(\"> shape:\", env.unwrapped.shape)\n", 226 | "print(\"> df.shape:\", env.unwrapped.df.shape)\n", 227 | "print(\"> prices.shape:\", env.unwrapped.prices.shape)\n", 228 | "print(\"> signal_features.shape:\", env.unwrapped.signal_features.shape)\n", 229 | "print(\"> max_possible_profit:\", env.unwrapped.max_possible_profit())\n", 230 | "\n", 231 | "print()\n", 232 | "print(\"custom_env information:\")\n", 233 | "print(\"> shape:\", custom_env.unwrapped.shape)\n", 234 | "print(\"> df.shape:\", custom_env.unwrapped.df.shape)\n", 235 | "print(\"> prices.shape:\", custom_env.unwrapped.prices.shape)\n", 236 | "print(\"> signal_features.shape:\", custom_env.unwrapped.signal_features.shape)\n", 237 | "print(\"> max_possible_profit:\", custom_env.unwrapped.max_possible_profit())" 238 | ] 239 | }, 240 | { 241 | "cell_type": "markdown", 242 | "metadata": {}, 243 | "source": [ 244 | "- Here `max_possible_profit` signifies that if the market didn't have trade fees, you could have earned **4.054414887146572** (or **1.1229001800089833**) units of currency by starting with **1.0**. In other words, your money is almost *quadrupled*." 245 | ] 246 | }, 247 | { 248 | "cell_type": "markdown", 249 | "metadata": {}, 250 | "source": [ 251 | "### Plot the environment" 252 | ] 253 | }, 254 | { 255 | "cell_type": "code", 256 | "execution_count": 4, 257 | "metadata": {}, 258 | "outputs": [ 259 | { 260 | "data": { 261 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjUAAAHNCAYAAADmPIpxAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAACAF0lEQVR4nO3dd3hT1f8H8HeSNuluKV0UyqZsSmVZZGqhVERQRFFUcPEDcQAucOAWUFFQEVARvg5wiygIAjJlj7I3LS3Qltm9m/P7o81tblbTNmma9P16njwk955778mlTT4943MUQggBIiIiIiendHQFiIiIiGyBQQ0RERG5BAY1RERE5BIY1BAREZFLYFBDRERELoFBDREREbkEBjVERETkEhjUEBERkUtgUENEREQugUENWbRp0yYoFAps2rTJ0VWpExQKBd544w1HV4NcWF38Gfv222/Rrl07uLu7IyAgAAAwYMAADBgwwKH1IjLEoKYOUigUVj2sCTTee+89rFixwu51Xrp0qaxubm5uaNy4McaNG4eLFy/a/frOorCwEC+99BLCw8Ph6emJXr16Yd26dVYff/HiRdx7770ICAiAn58fhg8fjnPnzpksu3jxYrRv3x4eHh5o06YNPv30U6c+p70MGDDAqt83awKNZcuWYe7cuXavc1JSkqxuKpUKTZs2xV133YWEhASbXuvEiRMYN24cWrVqhS+//BJffPGFyXKXLl3CG2+8UePrnzx5ElOmTEHv3r3h4eEBhUKBpKSkKp3j+PHjGDJkCHx8fBAYGIiHHnoIV65cMSqn1Wrx/vvvo0WLFvDw8ECXLl2wfPlypz5nvSeozvn2229lj0GDBgkARtvT0tIqPZe3t7cYO3ZsteuyceNGAUBs3LjRYrklS5YIAOKtt94S3377rfjyyy/FY489JlQqlWjVqpXIz8+vdh3qEgDi9ddfr/bxo0ePFm5ubuL5558XixYtEjExMcLNzU1s3bq10mOzs7NFmzZtREhIiJg9e7b46KOPREREhGjSpIm4evWqrOzChQsFADFy5EjxxRdfiIceekgAELNmzXLKc9rTP//8I/u9euaZZwQA8fLLL8u2Hzx4sNJzDR06VDRr1qxG9bHmZywxMVEAEPfff7/49ttvxdKlS8VLL70k/Pz8hEajEQcOHKhRHfQtWLBAABCnT5+WbS8sLBSFhYXS6z179ggAYsmSJTW63pIlS4RSqRSdOnUSXbt2FQBEYmKi1cenpKSIoKAg0apVKzFv3jzx7rvvigYNGoioqChZfYUQYtq0aQKAeOKJJ8QXX3whhg4dKgCI5cuXO+U5SQgGNU5g0qRJorrxZ20HNXv27JFtf+mllwQA8eOPP1a7DrUpJyfH4v6aBDW7du0SAMQHH3wgbcvPzxetWrUSMTExlR4/e/ZsAUDs3r1b2nb8+HGhUqnE9OnTpW15eXmiYcOGYujQobLjx4wZI7y9vcX169ed7py16eeff7bqZ96U2g5q9H+WhBBi5cqVAoAYP3682WMr+xk39OabbwoA4sqVKxbL2SqouXbtmsjKyhJCCPHBBx9UOaiZOHGi8PT0FOfPn5e2rVu3TgAQixYtkrZduHBBuLu7i0mTJknbtFqt6Nu3r2jSpIkoKSlxunMSgxqnYCqoycnJEVOnThVNmjQRarVaREZGig8++EBotVqpDACjhy7ASUpKEhMnThSRkZHCw8NDBAYGinvuucfow6OmQc1ff/0lAIj33ntPtv348eNi5MiRokGDBkKj0Yhu3bqJP/74Q9p/48YNoVQqxbx586RtV65cEQqFQgQGBsre54QJE0RoaKj0esuWLeKee+4RERERQq1WiyZNmojJkyeLvLw8WR3Gjh0rvL29xZkzZ0R8fLzw8fERw4cPF0IIUVBQICZPniyCgoKEj4+PGDZsmEhJSTH5hXP8+HHZB5M5L7zwglCpVCIzM1O2/b333hMARHJyssXje/ToIXr06GG0ffDgwaJVq1bS61WrVgkAYtWqVbJy27dvl1r8nO2ctclcUDN//nzRoUMHoVarRaNGjcSTTz4pbty4Ie3v37+/0e+bLsApLCwUr732mrjpppuEn5+f8PLyEn369BH//vuv0fVrEtTk5OQIAGLQoEFCiIrfy02bNomJEyeK4OBgERAQYPV7atasmdF70tWtf//+on///kKIis8Jw4cuwMnNzRXHjx+vNDAyVJ2gJiQkRIwaNcpoe2RkpLjttttk7x2AOHr0qKzcsmXLBABZ66mznJOE4JgaJySEwJ133omPP/4YQ4YMwUcffYS2bdvihRdewNSpU6Vy3377LTQaDfr27Ytvv/0W3377Lf7v//4PALBnzx5s374do0ePxieffIIJEyZgw4YNGDBgAPLy8mxWV11feIMGDaRtR48exc0334zjx49j2rRpmDNnDry9vTFixAj8/vvvAICAgAB06tQJW7ZskY7btm0bFAoFrl+/jmPHjknbt27dir59+0qvf/75Z+Tl5WHixIn49NNPERcXh08//RQPP/ywUf1KSkoQFxeHkJAQfPjhhxg5ciQA4PHHH8fcuXMxePBgzJo1C+7u7hg6dKjJ99i+fXuT5zZ04MABREZGws/PT7a9Z8+eAGBxLIJWq8WhQ4fQvXt3o309e/bE2bNnkZ2dLV0HgFHZbt26QalUSvud5Zx1wRtvvIFJkyYhPDwcc+bMwciRI7Fo0SIMHjwYxcXFAIBXXnkFXbt2RVBQkPT7phtfk5WVha+++goDBgzA7Nmz8cYbb+DKlSuIi4uz6RiYs2fPAgAaNmwo2/7kk0/i2LFjmDFjBqZNm2b1e5o7dy7uuusuAMCCBQvw7bff4u677za6bvv27fHWW28BAMaPHy+9/379+gEAdu/ejfbt2+Ozzz6z2Xs15eLFi7h8+bLZnz/9n6kDBw7A29sb7du3Nyqn2+9M56Qybo6uAFXdypUr8e+//+Kdd97BK6+8AgCYNGkSRo0ahXnz5uGpp55Cq1at8OCDD2LChAlo2bIlHnzwQdk5hg4dinvuuUe2bdiwYYiJicGvv/6Khx56qFp1y8zMxNWrV1FQUIBdu3bhzTffhEajwR133CGVefbZZ9G0aVPs2bMHGo0GQNmHbp8+ffDSSy9JH6J9+/bFL7/8Ih23detW9OnTBydOnMDWrVvRsWNHKcAZP368VG727Nnw9PSUXo8fPx6tW7fGyy+/jOTkZDRt2lTaV1hYiFGjRmHmzJnStoMHD+K7777Dk08+ifnz50v3d8yYMTh06FC17gsApKamolGjRkbbddsuXbpk9tjr16+jsLCw0uPbtm2L1NRUqFQqhISEyMqp1Wo0bNhQuo6znNMaubm50Gg0cHMz/kgrKSlBUVERvLy8rD6fvitXrmDmzJkYPHgw/v77byiVZX8LtmvXDk899RS+++47PPLIIxg0aBAaN26MGzduGP2+NWjQAElJSVCr1dK2J554Au3atcOnn36KxYsXV6tueXl5uHr1KkpLS3HixAlMmTIFADBq1ChZucDAQGzYsAEqlapK72nEiBFISEjA77//jnvuuQdBQUEm6xEaGor4+HjMmDEDMTExRu+/tqSmpgKA2Z8/3c+nRqNBamoqQkNDoVAojMoBFb+PznJOKsOWGie0evVqqFQqPPPMM7Ltzz33HIQQ+Pvvvys9h/6XfnFxMa5du4bWrVsjICAA+/fvr3bdYmNjERwcjIiICNxzzz3w9vbGypUr0aRJEwBlX3r//vsv7r33XmRnZ+Pq1au4evUqrl27hri4OJw+fVqaLdW3b1+kp6fj5MmTAMqCmn79+qFv377YunUrgLLWGyGErKVG/73l5ubi6tWr6N27N4QQJv+qmThxouz16tWrAcDo/k6ePNnkexZCWDUTLT8/Xwri9Hl4eEj7LR0LwKrj8/PzZV+ehmX1yznDOc3RarVYtGgR2rVrBx8fH3h4eOCWW27BBx98gH379uH8+fP46aef0L17d5w6dcriuSxZv349ioqKMHnyZOnLHygLSvz8/LBq1apKz6FSqaT3qtVqcf36dZSUlKB79+41+n17/fXXERwcjLCwMAwYMABnz57F7NmzjVpTnnjiCSmgsdV7qooBAwZACGH3qepV/fmztpwznJPKsKXGCZ0/fx7h4eHw9fWVbdc1T54/f77Sc+Tn52PmzJlYsmQJLl68CCGEtC8zM7PadZs/fz4iIyORmZmJr7/+Glu2bJH9Qp45cwZCCLz22mt47bXXTJ7j8uXLaNy4sRSobN26FU2aNMGBAwfwzjvvIDg4GB9++KG0z8/PD1FRUdLxycnJmDFjBlauXIkbN27Izm343tzc3KSAS+f8+fNQKpVo1aqVbHvbtm2reDfkPD09UVhYaLS9oKBA2m/pWABWHe/p6YmioiKT5ykoKJCVc4ZzmrN161bMmDEDTz31FKKjo5GWloY1a9bgrbfewosvvihd48knn0S7du0snssS3e+T4f+/Wq1Gy5Ytrfp9A4D//e9/mDNnDk6cOCF17wBAixYtql238ePHY9SoUVAqlQgICEDHjh1NfgEaXsNW76muqerPn7XlnOGcVIZBTT319NNPY8mSJZg8eTJiYmLg7+8PhUKB0aNHQ6vVVvu8PXv2lPqJR4wYgT59+uCBBx7AyZMn4ePjI537+eefR1xcnMlztG7dGgAQHh6OFi1aYMuWLWjevDmEEIiJiUFwcDCeffZZnD9/Hlu3bkXv3r2lvzZLS0sxaNAgXL9+HS+99BLatWsHb29vXLx4EePGjTN6bxqNRvaXqj01atTIZM4eXVN0eHi42WMDAwOlpujKjm/UqBFKS0tx+fJlWddOUVERrl27JpVzlnOaExkZiRMnTsjGaz3++OMoKirC4cOHUVhYiC5dusDHx8fieWrDd999h3HjxmHEiBF44YUXEBISApVKhZkzZ0rjYKqjTZs2iI2NrbRcffni03XJmPv50/186spu3LgRQghZ146pn1NnOCeVYfeTE2rWrBkuXbokDbjUOXHihLRfx7AfVueXX37B2LFjMWfOHNxzzz0YNGgQ+vTpg4yMDJvVU/ehfenSJWmAYMuWLQEA7u7uiI2NNfnQb4HSdTVt3boVXbt2ha+vL6KiouDv7481a9Zg//790mBEADh8+DBOnTqFOXPm4KWXXsLw4cMRGxtbpV/8Zs2aQavVGn3Z6LrBqqtr1644deoUsrKyZNt37dol7TdHqVSic+fO2Lt3r9G+Xbt2oWXLltJ9053HsOzevXuh1Wql/c5yTnMaNWokC2h01Go1unXrht69e9skoNH9Phn+/xcVFSExMdHq37eWLVvit99+w0MPPYS4uDjExsZKf23Xtqq8J2uZe++1qXHjxggODjb587d7927Zz1TXrl2Rl5eH48ePy8oZ/j46yzmpDIMaJ3T77bejtLTUaCbBxx9/DIVCgfj4eGmbt7e3yUBFpVLJupwA4NNPP0VpaalN6zpgwAD07NkTc+fORUFBAUJCQjBgwAAsWrTI5F8phtk0+/bti6SkJPz4449Sd5RSqUTv3r3x0Ucfobi4WDaeRjduQP+9CSEwb948q+usu3+ffPKJbLu5TLEnTpxAcnJypee95557UFpaKsvIWlhYiCVLlqBXr16IiIiQticnJ0tBqv7xe/bskX0Qnjx5Ev/++69sYOitt96KwMBALFiwQHb8ggUL4OXlJZvF5SzndKTY2Fio1Wp88sknsp+rxYsXIzMzU1ZPb29vk923pn4ud+3ahR07dtix5uZV5T1Zy9vbGwBMft7k5eXhxIkTuHr1arXrbMrZs2eN/vgYOXIk/vrrL6SkpEjbNmzYgFOnTsl+/oYPHw53d3d8/vnn0jYhBBYuXIjGjRujd+/eTndOAjMKOwPDPDWlpaVi4MCBQqFQiPHjx4v58+eL4cOHCwBi8uTJsmNvv/124e3tLebMmSOWL18udu7cKYQQ4uGHHxYqlUo8++yzYtGiRWLcuHGiSZMmomHDhrJkfTXNUyNERd6PBQsWCCGEOHr0qGjQoIFo2LChmDZtmvjiiy/E22+/LW6//XbRpUsX2bEnTpyQcl78+uuv0vaZM2cKAEKj0YiCggJpe1FRkWjVqpUICgoS7777rvj000/FgAEDRFRUlFFiMF2eGlPuv/9+AUCMGTNGzJ8/X9x9992iS5cuJnOIAJDydVRm1KhRws3NTbzwwgti0aJFonfv3sLNzU1s3rxZVk6X80RfVlaWaNWqlQgJCRHvv/+++Pjjj0VERIQIDw8Xly9flpXV5ba45557xJdffikefvhhAUC8++67TnnO2mQqT83rr78uAIjBgweLzz77TDz99NNCpVKJHj16iKKiIqnc+++/LwCIKVOmiGXLlomVK1cKIYT4+uuvBQBx5513ikWLFolp06aJgIAA0bFjR6NkfaZ+xgyZy1NjyNLvpbXvSVfOMMeMfp4aIcp+9wICAkTbtm3FV199JZYvXy7OnTsnhKj4HLEmcWVGRoZ4++23xdtvvy2GDBkiAIjnnntOvP322+LTTz+VlW3WrJnR/UtOThYNGzYUrVq1Ep988ol47733RIMGDUTnzp1lnxVClOWOQnmywi+//FLK1Pv999875TmJyfecgqnke9nZ2WLKlCkiPDxcuLu7izZt2hgl3xOiLCjo16+f8PT0lCXfu3HjhnjkkUek5HJxcXHixIkTolmzZjYPakpLS0WrVq1Eq1atpOyXZ8+eFQ8//LAICwsT7u7uonHjxuKOO+4Qv/zyi9HxISEhAoBIT0+Xtm3btk0AEH379jUqf+zYMREbGyt8fHxEUFCQeOKJJ8TBgwerFNTk5+eLZ555RjRs2FB4e3tbTL5XlaAmPz9fPP/88yIsLExoNBrRo0cPsWbNGqNypoIaIcpSq99zzz3Cz89P+Pj4iDvuuMMofb3OF198Idq2bSvUarVo1aqV+Pjjj41+PpzpnLXFXPK9zz77TLRr1064u7uL0NBQMXHiRFmiOiHKkt898MADIiAgQJZ8T6vVivfee080a9ZMaDQaER0dLf766y8xduxYhwU11r4na4MaIYT4448/RIcOHYSbm5vs960qQY3uvZl6GN4rU0GNEEIcOXJEDB48WHh5eYmAgAAxZswYk8vKlJaWSv8varVadOzYUXz33Xcm6+Us56zvFEIY9EEQEREROSGOqSEiIiKXwKCGiIiIXAKDGiIiInIJDGqIiIjIJTCoISIiIpfAoIaIiIhcAoMaIiIicgkMaoiIiMglMKghIiIil8CghoiIiFwCgxoiIiJyCQxqiIiIyCUwqCEiIiKXwKCGiIiIXAKDGiIiInIJDGqIiIjIJTCoISIiIpfAoIaIiIhcAoMaIiIicgkMaoiIiMglMKghIiIil8CghoiIiFwCgxoiIiJyCQxqiIiIyCUwqCEiIiKXwKCGiIiIXAKDGiIiInIJDGqIiIjIJTCoISIiIpfAoIaIiIhcAoMaIiIicgkMaoiIiMgluDm6ArVFq9Xi0qVL8PX1hUKhcHR1iIiIyApCCGRnZyM8PBxKpeW2mHoT1Fy6dAkRERGOrgYRERFVQ0pKCpo0aWKxTL0Janx9fQGU3RQ/Pz8H14aIiIiskZWVhYiICOl73JJ6E9Toupz8/PwY1BARETkZa4aOcKAwERERuQQGNUREROQSGNQQERGRS2BQQ0RERC6BQQ0RERG5BAY1RERE5BIY1BAREZFLYFBDRERELoFBDREREbkEBjVERETkEhjUEBERkUtgUENEREQugUENERGZVFSixZdbzuFkWrajq0JkFQY1RERk0uJtiXh39XHEzd3i6KoQWYVBDRERmTR7zQlHV4GoShjUEBFRpc5fy3V0FYgqxaCGiKgeWLDpLD7793SVjtG4VXxFXLyRb+sqEdmcm6MrQERE9pVbWCJ1Jd3fsyka+misOq6wRCs9V7vxb2Cq+/hTSkTk4rRCSM/zi0utPk4/kBEWyhHVFQxqiIhcnLaiwQWlWuvDE6Wi4nlxqdZ8QaI6gkENEZGLK9VrqTlhZc6ZTzacRkFxRSCTnlVg83oR2RqDGiIiF1ei11SzfHeyVcd8tO6U7PWUHw/atE5E9sCghojIxel3P3UK93dcRYjsjEENEZGL02+pyS0qcWBNyFUJIXA1p9DR1WBQQ0Tk6vRbakQNpjF9vS2x5pUhl/T1f0no/s56/LjHuu5Ne6lyULNlyxYMGzYM4eHhUCgUWLFihcXyv/32GwYNGoTg4GD4+fkhJiYGa9euNSo3f/58NG/eHB4eHujVqxd2794t219QUIBJkyahYcOG8PHxwciRI5Genl7V6hMR1Tv6LTX6uWeq6q2/jllV7vCFTIz+YgcWbT6LgipMISfnpUvs+NKvhx1ajyoHNbm5uYiKisL8+fOtKr9lyxYMGjQIq1evxr59+zBw4EAMGzYMBw4ckMr8+OOPmDp1Kl5//XXs378fUVFRiIuLw+XLl6UyU6ZMwZ9//omff/4ZmzdvxqVLl3D33XdXtfpERPWOfp6awloIMoZ9tg07z13HzL9P4I2VR+1+PXK8G3nFjq4CgGpkFI6Pj0d8fLzV5efOnSt7/d577+GPP/7An3/+iejoaADARx99hCeeeAKPPPIIAGDhwoVYtWoVvv76a0ybNg2ZmZlYvHgxli1bhltvvRUAsGTJErRv3x47d+7EzTffXNW3QURUb5To5aapSUtNdfywJwWzRnap1WtS/VXrY2q0Wi2ys7MRGBgIACgqKsK+ffsQGxtbUSmlErGxsdixYwcAYN++fSguLpaVadeuHZo2bSqVMVRYWIisrCzZg4ioPiop1Q9q2B1ErqvWg5oPP/wQOTk5uPfeewEAV69eRWlpKUJDQ2XlQkNDkZaWBgBIS0uDWq1GQECA2TKGZs6cCX9/f+kRERFh+zdDROQE9Luf9BPqWWvVM30AACG+la8ZNfmHA0bb1h1Lx8gF25F0lSt9k33ValCzbNkyvPnmm/jpp58QEhJi12tNnz4dmZmZ0iMlJcWu1yMiqqv0u5+qM3DXS102UiG/qOxYIYTJ81zOKsCKhEuybSqlAk98sxf7zt/Ay787dhAp2Z81ga891VpQ88MPP+Dxxx/HTz/9JOtGCgoKgkqlMprJlJ6ejrCwMABAWFgYioqKkJGRYbaMIY1GAz8/P9mDiKg+0uoFNQkpGRBVnNftpVYBKMtxczm7AC2mr0a719bg2CV5t76pVqBHejeXnmfm143BpGQ/t7UPrbyQHdVKULN8+XI88sgjWL58OYYOHSrbp1ar0a1bN2zYsEHaptVqsWHDBsTExAAAunXrBnd3d1mZkydPIjk5WSpDRESmjVuyR3peohVYZuVSCTqe5UGNVpStCVVx3rLUG1kFxcgpLEGBifE6Kr1VMTVuTI3mivRb7Z4c0MqBNanG7KecnBycOXNGep2YmIiEhAQEBgaiadOmmD59Oi5evIhvvvkGQFmX09ixYzFv3jz06tVLGgPj6ekJf/+ydN1Tp07F2LFj0b17d/Ts2RNz585Fbm6uNBvK398fjz32GKZOnYrAwED4+fnh6aefRkxMDGc+ERFZoAs49C3emogxvZpZfQ4vd5X0/LudFQFRRl4xCktK0eWNf6BQAD/9n/EfmfptQu4qBjWu6HJWRSbh8ABPB9akGkHN3r17MXDgQOn11KlTAQBjx47F0qVLkZqaiuTkih/6L774AiUlJZg0aRImTZokbdeVB4D77rsPV65cwYwZM5CWloauXbtizZo1ssHDH3/8MZRKJUaOHInCwkLExcXh888/r/IbJiKqT/IKjVtPqjKtu2O4H9zMBCNBPmqkZ5Z9oQkBnLuSY1RGv6trV+J1q69LzqO4PLmjt1ola5lzhCoHNQMGDLDYH6sLVHQ2bdpk1XmfeuopPPXUU2b3e3h4YP78+VYn/SMiIiDPxFpP3Zo1qPS4QG81rucW4cUh7cyWuadbEwi9tpiZf5+Qng/pGIY1R9NqtCwDOYfC8rFUnuoqhxQ2x7ZAIiIXlldk3FLjqdedZI6vR9kXlI/GfFmFQiFr9ckozyob6K1G8yBvAGXjcKhmLmcXYMYfR3AqPdvRVTHp6//K1gTT/cw4EoMaIiIXVVKqxaaTFcvNvD6sAwAYjbExpbg8WLE0DqZUa3pq90M3N4OivBciPbugKlUmE6b+eBDf7DiPOz/b5uiqGNmdeB2/7LsAAPDzdHdwbRjUEBG5rF/3X8CH/5wCAHRu7C9NuV51OLXSY4vLm1jclOa/Jkq0Qspdo++s3tiac1eYcK+m9p4vG4tUncSJ9nYirWJav1rl2PE0AIMaIiKXpT/GxVujwpZTVyo95nR6Nl7+/TCuZJcNAHYv/6J6cUhbo7KlWi3yTbTUPNqnBXRfb8dT5blsjlzMtLb6VK64tO724c0pD5oBYE/SDQfWpAyDGiIiF6VUVPzl7OmuwshuTSo9ZsT8/7BsV8UMVl330/i+LY3KlmiFydaDcH9PKMz80X7WxAwpMi8jrwildXhgUl1LqMighojIRenHFRtPXsGAtsHSa3OzWHMNupPcyltq3FRKRDcNkO0r1QrkFxuPz/FUq6CA6ahG1wJE1un61jpHV8GsklJ5QDv/gZscVJMKDGqIiOoJ/Yy+RaXWjc/QHyisNGh+KdEK3Mg1/kvdz8IsmHdWHbfquiRf3sKcyT8cwK1zNhkFGLXBMAAe2qVRrdfBEIMaIiIXdS23SPZarRfUbD11FVdz5K0mxSa+GNV6Qc2LcfJxNaWlwqg7qVeLQCgUCrPdT2Q9XVI7c37bfwErEi7h3JVc/HnoksWy9pCrN4tu36uxFkrWHsdPKiciIrt77Y4OsgDl8W/2AgBm3NEB+cWlKC7V4uttiUbH6ece6dWyIfa/Ngg/7U3BrL9P4PDFTBzTGwiscVNi5t2dAcBM5xNVRYnBAGH9/4v0rAJM/emg9Fp/qYLaogtqArzc0dDHsatz6zCoISJyUT2aN5BmpDx6S3MoTDSfvPXXMbPH+3q4GS2REOithlt5Knz9gOb/+rXE83FtK7qr2FRTY4YtZ2F+HtJzwyDGEdO9s8uDGu86kElYh91PREQuStfdNPe+riYDmsrMHtnF5HY3E+v7/JFwSTb+xrDEuN7NpeeWltqhCobjnk5fzsHOc9cAAIbpg9QOWAF9x9myuoT41Y1WGoBBDRGRy9INyVDqBSFqK1fKnjSwFeI7hZncpzJxjriOoSZKVpgc20Z6bk1GYzKdn2b0FzsBwGjhyNlrThiVtaeTadn4YO1JAPIWJEdjUENE5KK05S0i+t9/8Z1NByqGXohrZ7Z1x1RLzWt3dJC9Njw0wEstPTe1HhUZKzazmnpJqRYqE/83ppassJc/D1YMTA72ZUsNERHZma6XR38qdtK1vEqPa9bQy+J+w1aCMb2aGo290S1uCQC3tG4IAPBWly2OWVgH0/3XRSXlTW3+BmsqXcooMDlkadvpq7VRLQDA0UsVmaHTs+rO+l4MaoiIXNTJ8lWd9WOQgykZlR732tAOFvcbttSYSqfyl94U4z6ty5L+acpXBy8oYUuNNYpKym6sxk2JN+/sKG2fsfIIHJCWRqaBXstbfh0KUhnUEBHZ2Y3cIryx8iiOXMxE75kb0HzaKuxJum736+pS2J+7WrVFJQ0zBxs6kZYte11koptk0sDW0vMbeWX5cnTJ/9hSYx3d7Cd3lRK3tguRtu9NumFy6YRQG49t2Xr6Ch5bugcXbuQZDe4O9K4Iau7vEWHT69YEgxoiIjt788+jWLo9CXd8ug2XMsua6l/85VCtXV9/EUn9v/jNqSznyMYTl2WvTWUn7tM6SHqu+wJOLX/vp9KzjcrXxNkrOXh31TGXW4KhIqhRICKwokswp7BEGi+lr0SrtSoLsbUeWrwbG05cRp/ZG3Hfop0o1QocuZiJlQcv4avynEZ9WgdhiJkB5Y7AoIaIyM6OXsoy2pZYxdaTmnjo5ubS84djmlksG+5f+V/7Y/WmZwNAkYnuJP28Kfd2l/8l/9zPBw2L18jgj7fgy62J+GjdqcoLO5EivZYaQynXjcdG3fX5dkS/vc4uP1u7k67jld8P445Pt+GZ5Qek7afSs6uVLsBeGNQQEdmZIz7z9bsLIkN99OpiujKrn+mLXS/fhm0v3VrpuQd3kE/f7h8ZYlSmdUjFNVsFe1d6zuoSQkgtQVtPX8GB5BvIyCuq5CjncO5KWXBy+rLxyuYTv99v8pjM/GIs+c84M7Qt/LAnxWhbXVtBvO6kASQiIpvR/64xnK0U2z4U64+ny7YpFNaPyQjx88DeV2ORV1iKY6lZGNTBOEeNp1qFo2/GQe2mlGZGNfRW41puEbo08YcQwiZ/4a8+nCY9v3AjH3d9vh2B3mrsf21Qjc/taK+uOCJ7HdXEHwcvZJopXeGbHefx1vBONb5+yyDvSsdjPRzTvMbXsSW21BAR2Zmpv2Z105xr45qGwUOoiQywpgb7WhLko0HThl4Y0inMKGjS8da4ybpOHujVFABw6EImlu1OrtL1zPlxr3HrwfVc12ipMaS7f7XFTVV50Pn0ra0rLVObGNQQEdmRVitw9orxX7uVLMBc8+vqdT+ZCzr0dWnib8/qAAA8yqd0A5Cy0dbUf2esy80ihMC9C3dg8MebUegEU8oNV1AHAI2bykRJ+ym0ItBVWvGzVZsY1BAR2dHFjHyT203NXrEl/fMbfu/oNxwtGHMT1k/tVyuDPfWDmqaBlhP8WcvaMR1/HkrF7qTrOJWeg0eW7LHJtasqv6gUa4+mSatbW9L9nfVG28wFp0O7NKpx3Qxl5hXjfHmiRnPJGH+ZEGPz69YUgxoiIjtIupqLRZvPGq20rGP/oKbiudIgYNGf9hvfuRFah/jatS46565UDHg9ZMXYEFs6qjetfXv5Qoy1bcxXO/F/3+7DLbP/tVhur0EOo3u7NwFgvovw3RHG42dGf7Gj0mUTTqdn44stZ43KZeYXI+qtf6TXfh7uhocCALo3D7R4fkfgQGEiIjsY+slW5BaVYuPJyyb3W2pgEELgu53n0aVJAKIiAqp1ff0WDMOgxlRemdqg31JT2/Rvd3XvaU3tT84AIF9CwpR/jskHcesG/V64YbrVT39dLZ2d565j1aFUjOzWxOx1Bn28BUDZwpn6yRI3n7oiKzdlUBs8unSv9Prl29sh0LvurPekjy01RER2kFu+aOPOc6YzB1tqqVl7NB2v/XEUw+f/V+3xH8LCmJpuzRpU65w19X/9WkrPbbUIYuMAT6vK6bdyqK0YAOtI+i1aG57rLwWDljI9H5wxGO8YtNiYayU0ZDi+yc9D3t7Rq0VD2f/d+H6tcI+FYMmRGNQQEdWiF+LaArDcUqOfcbftq2uqdR15S4183+geEfjgni7Y8sLAap27ukL8PPDBPV0AAB3D/WT7vt6WiFs/3IRLZsYgmSKEMDtmyTCtf5BPRWtGTqFxoHgwJQPNp63CQ4t3WX19e1l/vKJ1r1VwRb6fvm2C8N1jvWRldY1w/l7uePBmeWLFs1eM89uYs+98RfD916FU2T5vjRse69sCjQM88dTAujXbyRCDGiIiGysx8xfyxAGt0KFR2Ze5pXT2VWmd+eifk7h3YcX4iSvZhSgu1UpBk0JhPKXbTaXEqO4RaFrJatz2oGs1Mhzg+9Zfx3Duai7mrT9t9bn2JN2Qnt9nkLU422Awrv7q5C2CjN/38Pn/AQC21tJK10IIZJrohkrLNL/itUKhQJ82QUiaNRS7Xr4N9/dsitXP9DVb/sut5pPwGY6j0V9i4pd9F6TnuiA8xNcD214aiOfLX9dVDGqIiGxM1/Vk6KGbm0lTYLVCILewBH8evIQkgwRnnlUYe/LJv2ewO+k6/ki4iMSruejx7npEvfkP8orKvtTtPB65ynTx1V69gESfqbwzptzILcK9i3ZIr5++rbWsS+v7nfI8OPpf1KsPpxm15NS2N/88hqi3/sHW0/LxK19tPSc9f+X29maPD/XzwMy7O6N9Iz+zZSzRLXaqE26mG09/rE1dWg7BHAY1REQ2ZmrmbVREAMIDPKV9JaUCE77bh6eXH8CADzfhzOWKLqcP/7FuDSP9L+aXfj2MgR9uAgDkFZWi/webqlt9u1p/rKxrJb+SmTmVeWXFYdlrjZsKbno3/rN/T0vjaEwFMBv0ungM9+sCQntauj0JAPB/3+6Tbb+5ZUVSxhATSRKrQr/LzVCJQUtZcamAEAI/WxlU1lUMaoiIbMxUz9LBlAwAFTORTqZny7o63vzzmMlzWRpQm2emRagu0895sifJ9CDqHCvyuPxzVD5DSK1SymZ55RaV4oEvdwIAtplI0PfSrxWrpL+z6rhs32/7L1Z6fVsx/D/Uz+I7oK3xmlpVYWmWlWH354HkG/j7SBpeqMXV4+2BQQ0RkY1ZGi9jrgW/X5tgk9stdZNYO7ulLtGfYvxoeRI8w/d4Nds4m64hw5YGjbsS/SLl93Dv+bIurrf/Mg4Y9cfc/HMsTbbP2hlVVWHp/0q/ZUg31qhTYz/4e5rOD2OtEq3AqIXbpdeXswpwIPmGtE/fO6uO40mDRTLr+qBgUxjUEBHZmKXp2oY5Y3TMdTVczSkyG9g4Kt9MTejnqtF1QaVnyYMYwy9ca8/76lDTY1BOpRvPAnpGb82i2PbyBTntkU8n9qPNZvd1mLEW28tbk3TvXa2q3tfzVw93RwOvimBoT9INzFx9HJn5xej53gbc9fl2HLmYiVIr1umYOiiyWnVwJAY1REQ2VmoiCPnr6T4AzKe6tzRu9Vcz3SHFpXVsFLAVNG4VXzslWoF/T6Tj5pkbZGVKqrgw1pE34wCUTT3e/MIA2b6U63my1z2al+XocdMLGjqGy9e9ske25/PX8izuf+CrsqnkullJ1Q2sYjuEYskjPWXbFm05h083VMwq+2ZHUqWBo7tKUefWdbIGgxoiIhvTfSe6KRXY80oszrwbj06Ny744zX1PWFrD6PmfDwIATqRlYcjcLfjnaFl3SXEVV9auC9wNWiD0M9XqFJdULajw0VQkizM8v+EsH92SEBfLs/PmF5Xi2KUsWZnqtBTZyo3yFcYbmMgSbC2VidbAy3pdej/tvYDfKxk35IwBM8CghojI5nR/6SsVCgT7amStAuamxeq3DtzeOcxo/8WMfDz/80GcSMvG+PIZM87Y/eSrqXx1nmJtxayly1nm87aYYvhVvCuxYjByuzBfbClfAuDbnecBAHcv2I6v/5Pnc7E0Jqq63K3MYqxLB+CtqX4XmKn8Q4ZJDRdtOWdUxhUwqCEisjFdq4up+MVwTI1uUKp+j8dFE2v87Dh7DUcuylsUzC1w2LdNkPS8QzXzmNiLNV0aurw9M/44ip7vbcC89adlyeH0B9be3FK+qGKorwYNvStaOfQHCb81vJMsA7EQAsdT5fcUsE9LTYsgb6vKFZb/n2rcqh/U+Hu6Y/fLt8m26QZNW8vSkgx1GYMaIiIb0wUopsbPGHYNtG9U1h2ia6mZ8O0+HDSxgrWpLMPmZtTMva8r1k7uh7tvaoyFD3arUt3rgpd/P4xd565JrSkfrz+FHu+ulwZM669V9ETflrJj3VRKbJ9+q8nzGnZFmZrqDVjuCqyuEiu7c/LLAza1W82+nkP8PGp0/FKDcTnOgkENEZGN6b4UTc100t80vl9LqTtK9z265mjF9OIuTSoGsCpgfC5z4x78PN3RNswXH93b1SFLIdTU0M7hmLPOOAGh7v3qryJt6stf46aCh7vx9k6N/eClrmgBeWjxbpPXt0dQU2zF4GetVkhLGxiODaqOndNvq7yQCT9PiKnxdHJHqfJd27JlC4YNG4bw8HAoFAqsWLHCYvnU1FQ88MADiIyMhFKpxOTJk43KDBgwAAqFwugxdOhQqcy4ceOM9g8ZMqSq1ScisqtLGfl49scEAKYHBesHOvGdwnDkYlmrzBdbzspyigDAPd2aoE/rsq4kwxlBZy7nyJYJ0OfmhLNWgLK1sQAgwMvdZLdZQXlrlf5sJf0gRV+4v3GumRBfD/z0fzGV1sPU7LWa0rXU6N6jKdfKBwkDtslqHOZfvdYab3Xl457qqioHNbm5uYiKisL8+fOtKl9YWIjg4GC8+uqriIqKMlnmt99+Q2pqqvQ4cuQIVCoVRo0aJSs3ZMgQWbnly5dXtfpERDZXWFKK73aeR8r1PNwy+9+K7MEmggul3qeuxk2F1PIFDJOu5ckWaASAs5dz4Fn+pZ1osD7UZ/+aX/ixrq/R8/ME48Bizqgoabr34m2J0jIC+grKB9HqpmUDwE1NGxiVA8qmdxtSKRXSLDRL7DFQWDdVe0TXxmbL6A/8NpfPqKbeGt5R9rpNiA/G9W4u2+bv5ZytNEA1gpr4+Hi88847uOuuu6wq37x5c8ybNw8PP/ww/P1N/zAFBgYiLCxMeqxbtw5eXl5GQY1Go5GVa9DA9A8zEVFtWrDpLF5dcQRD5m6RDfg1NbVWX2XjJjTuKqw7VrYcwJL/kmRjdK7kVJ51t67q0TwQd3RpJNsWHuBpsotNX0Fx2Zf+V+VdNEO7NDIbwD0U06zK9erZomzQsa0HCgshcKN8yQJ/T3f8OjEGD97cFF+P6y4rl5Fn25YaAPhwlLwxIdhHnuTx9OUcvHGnPNCxR0bl2lInx9QsXrwYo0ePhre3fLT4pk2bEBISgrZt22LixIm4du2a2XMUFhYiKytL9iAisoe568taTQxX5zb11ZhbWFGmmYXxLmqVEk/fKk9Tr9/w898Z859/ziDpmrzlSe2mrDTpXX5xKXIKS5BcnlBv1aFUs2VH6S3HYKh7M/kfxJGhPnh7eEcp342tW2r0Z24FeLmjW7NAvDOiMwIMctH8uq8id8zfR+RLN1SXfnZhL7XKJmN16rI69+52796NI0eO4PHHH5dtHzJkCL755hts2LABs2fPxubNmxEfH4/SUtMLus2cORP+/v7SIyIiojaqT0Qkua43RkInOiIAd0c3xrT4dha/YBJeHwRfD3k3gKWEaKN7lH3G/TqxdzVrW7sMp6dr3JRYe9T0F7mu5SAzvxidXl9r1fktdcFNjpWn//9nSn88FNNcagmzdUuN/qKV+pmCDWdE6efL0V+tuyb0p4Z/en803M20DkaG+tjkeo5W54KaxYsXo3PnzujZUz6dbPTo0bjzzjvRuXNnjBgxAn/99Rf27NmDTZs2mTzP9OnTkZmZKT1SUpx7OXUicg1KpQIf3dcVE/qbHzAKAF7lgzVNJeIzZdbILkiaNRTdmjlnt7y7wSrbOn4ebtCUz2T6fNMZ2b74TpbvzbzRXU1uN5zaraPrLrT1QGHdWJlAb3nLzI0846BX5+5o82NvqkJ/2n9hidZoTamWwWU9Im8P7wRAviaWM6pTQ5xzc3Pxww8/4K233qq0bMuWLREUFIQzZ87gttuMp61pNBpoNKYXiCMichYT+7fG6sOWuyIMF2R0Rmo3JUZEh+OYQTK8h2KaYeOJsincm05eke1702DQq6HhXRtjWJdw/Hf2KjrrDRDWXzxUvytKVZ71t9TGmZp1SRINswpbGgw8pJKAzVp+nhVf815qFdRu8mt+PbYHAKBXy4Y4/MZg2ZITzqhOtdT8/PPPKCwsxIMPPlhp2QsXLuDatWto1KhRpWWJiGqLp41XeFZW8im9Y/qt+Gpsd8uF6qCXb28ne61UAG1CfY3KPXtbpDQDzFCIb+VTlpVKBfq2CZaNX+nerAGCfMpef/9EL2l7RUtN5fWvCl1LjeHA8AFtg02WXzO5r81msEVHNICvxg1qNyX6tQmWdXlueK4/mutlOvb1cK/zM+cqU+WQLCcnB2fOVDQBJiYmIiEhAYGBgWjatCmmT5+Oixcv4ptvvpHKJCQkSMdeuXIFCQkJUKvV6NChg+zcixcvxogRI9CwobwvMScnB2+++SZGjhyJsLAwnD17Fi+++CJat26NuLi4qr4FIiK7yS82Pc6vKvS/VyqbEdTIRD4WZ2A45VrjpsKAyGA83qcFvtpWNrbk/ZFdoHZTmkyk98gtzat9bYVCgb2vDjLarhtTY+uBwrqWGsOuH3eVEhP6t8LCzWdl29uF2W5pC6VSgcNvVnxP6o/TCvJ2vd6MKrfU7N27F9HR0YiOjgYATJ06FdHR0ZgxYwaAsmR7ycnJsmN05fft24dly5YhOjoat99+u6zMyZMnsW3bNjz22GNG11SpVDh06BDuvPNOREZG4rHHHkO3bt2wdetWdjERkcOZ+uM2zM8D/0zpV+mx3z3WC+3C5C0U+kM6LLXU6AYHOyM/vS/Xt0d0Qpi/BxQKBZ7oV7HsgVt5d41h69fzgyPx+jDLXU/VYa+BwgnleYvOXsk12jctvh0SZ95utN1emgZ6ISoiANFNA2RdU66iyu9owIAB0vobpixdutRom6XyOm3btjVbztPTE2vXWjfinYiotnm4qYxaaLa9NFC2Orc5fdoEYeVTfRD56t8m91sad3Fru5CqVbQOie8UhodjmqFXi4YYqpezRn9Mh252kGG3TWwH+4whUklLVtg2qEm6ahzM6FMoFLildUP8d+YaQnzt+4e6SqnAiid7Q4i6n6SxOlwvTCMiqmWmvhusCWiksgaZh4dFhUvPLS2EOMhOX+61wU2lxFvlM2706S97kFNYloDOcKC0vXKt6AYKW7v4pLUiy8cK9Y80PYYGAD6+rysWb0vEAz2b2vTappQtNWT3yzgEgxoiohqq6QKISqUCG57rj2s5RUi5noc4vZkvBSZW5waAfpHBLvmXtv57Mpdx2b2y0dPVJA0UtmLxyarQrdvV0GBKt74QXw9Mj29v0+vWR3Vq9hMRkbMQQuD9NSewfHeyTVZ1bhXsg54tAjGyWxNZF4y5c/935mqNr1lXjelV1lqhGzP025PyhIJuKvsEc16aslainMKaD/bWp0uaaK96UwW21BARVUNCSgY+31Q2a8WeDSbmukJsEUjVVe/e1Rnv3tVZem24aGVla2ZVl095wkNbrbuko0uA5+pLFNQFvMNERNWQkVeRldbG40plWoVU5BEZ2qUR+pWPy1j51C32u2gdpMsrA8BuCeJ0mYt1U7BtRReYMqixP7bUEBFVwZojaTiVno2O4bbLJWJJiK8H1k3pBx8PNyknjRDCJcfTWPLd470wZO5WxHUMla2fZEu6PDKFNs4oXFw+psZwQDjZHoMaIiIrXckuxITv9gEAxsY0M1uun4VZLtVhmGm3vgU0QFlCuqRZQ+16DXX54o+FxXZaJsFO3WZUgXeYiMhK3+5Ikp7vSrxuttwEvQRy5Dx0Y3WKLLTU/LLvAh5buge5hdaNu0m5nocl/yUBAA5fyKxxHckyttQQEVlJP8HeibRss+V6tw6qjeqQjUlBjZlp9ADw/M8HAQAPfLULK57sbbbVTAiBFtNXy7Ztc+EZa3UFW2qIiKxQqhX4cmuio6tBdqQbU2M4ULiwpBRCCOxPviFtO5iSgXXH0s2ea8T8/4y2xbZ33gzQzoItNUREVth3/kblhcip6WY/XckpBABsPHkZX2w+hx3nriG+Uxj+PiLPbPzr/gsY3DHM6DwAcNBEV9OskV1sXGMyxKCGiMgKJVbOiIkIdM5Vs6lincKU6/nQagUeWbJH2mcY0ADA2qOmW2r+PWG8vVeLQAT5cAFme2NQQ0RkBWtS0Uwc0KpW1u4h+7iUUSA9z61mAr5SrcCjS/cabWcrTe1gUENEZIUSKzL4vjSkXS3UhOxloN6q59UdP2Wum7JxAFvwagMHChMRWeHHPcmOrgLZmX7g8cmG09U6xwdrT5jcbq+lHUiOLTVERJXYd/46Vh82HlNB9Vuon/EYmT1J8paacH8PvDK0Q21Vqd5jUENEVIljl7Jkr5NmDcX13CIEeLqj5curzRxFrqplsDfOXclFZT2SUU388cvE3lzzqRbxThMRVSLEz8NoW6C3Gkqu5VMvvTOiEwBAW0lU88dTfRjQ1DLebSKiSij1ssaun9pPtm/GHR2gcVMabSfntPyJm422/ToxRvZaU75G1LXcIny07hT2J9+QpoOTYzGoISKqhLb8C+umpgFoHSJfXPLRPi1w8p14o+3knNqGGf8/dmsWKHutn7Pokw2ncffn2zH9t8N2rxtVjkENEVEldH+FK+vh6tj1jcZgltIbw8oG+U6Lbwe1Som/nu5jcibTD3tSkHI9r1bqSOZxoDARUSV0PQuMaVyfYVDzcExzAMCE/q3weJ8WcFMpzXY19X1/o72rR5VgSw0RUSW0UlDDqMbVuRkM7NUfDK7bp1AocGs7Lk5ZFzGoISKqhFbqfnJwRajO+HpcD4v74zqG1lJNSB+DGiKiSmg5pqZeahnsbXG/m4Uo9/2RUbauDlmBQQ0RkQk7zl7D638cQUFxqTSmhkFN/dKrRUOL+x/oZXrx0nu6NYG/l7s9qkSV4EBhIiIT7v9yJwDAQ61C29Cyab6MaeqH9o38cDw1Cz1bNLBY7pWh7ZGWWYB/jqVL2167owMe69PC3lUkM9hSQ0RkwfHUbGmgMFtq6od5o7vi4/uiMDyqscVyGjcVJg1sLdvGgMax2FJDRGRBqVarl6fGwZWhWhEZ6ovIUOuSKbqpKn4onrm1tYWSVBvYUkNEZEFxqeCYGjIrTG9dMK4F5ngMaoiIDOgvVHg1p1Ca/cSYhgw19NHg/p5NEeKrwdjyRH3kOOx+IiIycD2vSHp+7kouk++RRTPv7gygs6OrQWBLDRFRpZh8j8g5MKghIjJQrLcKMwBkF5QA4JgaorqOQQ0RkYGSUvmChTvOXQPAoIaormNQQ0Rk4PTlbNnrLaeuAOBAYaK6jkENEZGBR5fuNbmdLTVEdRuDGiIiK6VlFTi6CkRkQZWDmi1btmDYsGEIDw+HQqHAihUrLJZPTU3FAw88gMjISCiVSkyePNmozNKlS6FQKGQPDw8PWRkhBGbMmIFGjRrB09MTsbGxOH36dFWrT0RUKf2Eavp2J16v5ZoQUVVUOajJzc1FVFQU5s+fb1X5wsJCBAcH49VXX0VUlPml2P38/JCamio9zp8/L9v//vvv45NPPsHChQuxa9cueHt7Iy4uDgUF/MuJiGyrTagPAF3+ESJyFlVOvhcfH4/4+Hiryzdv3hzz5s0DAHz99ddmyykUCoSFhZncJ4TA3Llz8eqrr2L48OEAgG+++QahoaFYsWIFRo8eXYV3QERkmS4vjZdahX+f649b52x2cI2IyBp1ZkxNTk4OmjVrhoiICAwfPhxHjx6V9iUmJiItLQ2xsbHSNn9/f/Tq1Qs7duwweb7CwkJkZWXJHkRE1tCWp6lRKhRo1tDbsZUhIqvViaCmbdu2+Prrr/HHH3/gu+++g1arRe/evXHhwgUAQFpaGgAgNDRUdlxoaKi0z9DMmTPh7+8vPSIiIuz7JojIZVRkEFZAxTTCRE6jTgQ1MTExePjhh9G1a1f0798fv/32G4KDg7Fo0aJqn3P69OnIzMyUHikpKTasMRG5sopVuR1bDyKqmjoR1Bhyd3dHdHQ0zpw5AwDSWJv09HRZufT0dLPjcDQaDfz8/GQPIiKds1dycPfn/+HfE+lG+3QLWnIBSyLnUieDmtLSUhw+fBiNGjUCALRo0QJhYWHYsGGDVCYrKwu7du1CTEyMo6pJRE7stjmbsT85wyjRXmZeMc5czgEAqevpkVuaAwDiO5n+I4qI6oYqz37KycmRWlCAskG8CQkJCAwMRNOmTTF9+nRcvHgR33zzjVQmISFBOvbKlStISEiAWq1Ghw4dAABvvfUWbr75ZrRu3RoZGRn44IMPcP78eTz++OMAyv5amjx5Mt555x20adMGLVq0wGuvvYbw8HCMGDGiBm+fiOqjguJSk9tTM/Mx6fv90uvcwrKFLKfHt8dt7ULRvXmDWqkfEVVPlYOavXv3YuDAgdLrqVOnAgDGjh2LpUuXIjU1FcnJybJjoqOjpef79u3DsmXL0KxZMyQlJQEAbty4gSeeeAJpaWlo0KABunXrhu3bt0tBDwC8+OKLyM3Nxfjx45GRkYE+ffpgzZo1Rkn6iIgqs+nkZZPbY2b+K3vtriprzFa7KdGnTZDd60VENaMQQojKizm/rKws+Pv7IzMzk+NriOq51YdT8aRei8zayf3QJsQHLV9eLSt35t14uKnqZC89Ub1Rle/vKrfUEBE5O8OFKbeduYob5YODde7o0ogBDZGT4W8sEdU7DX3UstdqlQIz/z4h2za0c6ParBIR2QCDGiKqd4pLtbLX13OLUVAkHzzs6+Fem1UiIhtg9xMRObUjFzNxObsAt7YLrbxwuZJS+VDCj9efMirjqVbVuG5EVLsY1BCR07qcXYA7Pt0GAGgR5I2Nzw+w6rgSrbbSMl4MaoicDrufiMhpHbmYKT1PvJpr9XFFJZYnfTYO8ESrYJ9q14uIHINBDRE5LYOhMcgrKrHqOHPJ94CyrMGbXhgAtRs/HomcDX9richpPfn9PtnrDjPWwjD1VmFJKR7/3x6sOZIKrbZsX16R+aDGXaWUku4RkXPhby4ROa3iUuNupLSsAtnr1/84ivXHL2PCd/vx/M8HIYRAvoWWGq7MTeS8GNQQkUtJuZ4ve31Ob6zNbwcuIvrtdUhIyTB7/MqDl+xVNSKyMwY1ROSUzK3wcu+iHbLXbULkA34z8orxp4XARVsvFo4hck0MaojIKWXmF1tVTrfStrU83PmxSOSs+NtLRE5p6+mrVpXLtTAoeOqgSAyLCpdtm3tf15pUi4gciEENETmlEF+Nye23tguRvbbUUpN0NRcNveXrQA00OJ6InAeDGiJySrnlOWn8Pd1x7r3b8dG9UQCM13WyFNQ0aeAJN73pTiqlAho3ZhImclYMaojIKV3JLgQAdAz3g1IvGCkskQc1upw0vVs1NDrHo31aoGNjP+n1OyM62au6RFQLGNQQkdNJupqLl349DADS9GxdBuAig6CmoKQsqHk+rq3UmqPj7+mO4VGNpdddIwLsVGMiqg1c0JKInM5tH22WnutaYnRTvBNSMtD5jbVImDEYKqUCBcVlQY6Hmwp339QEd3QJx85z1+Dr4QaFQgGFAtj0/ACkZhagfSM/44sRkdNgSw0ROZ1SvWQyS8b1AFCWf0Ynu6AEwz7dhuyCYmmdJ91UbbWbEv0igxHdtIFUvnmQN2JMdE8RkXNhSw0ROTVdl1F00wDZ9mOpWej8xj9Ql6/j5OHOAcBEro4tNUTk1Lw1ZX+bhQd4mtxfVD4bikENketjUENETk03QFgX3JjDTMFEro+/5UTkVC5m5Jvd9+n90Wb3eTD/DJHLY1BDRE5l0vf7ze4zXPJAn1IvyR4RuSYGNUTkVHR5acyZFt+udipCRHUOgxoicilP9G2JGXd0cHQ1iMgBGNQQkVPp3Njf4n6VUoFH+7SQbWtsZmYUEbkWBjVE5FQOX8yUnn9wTxerjvntyd72qg4R1SEMaojIaY3qHmFVuVA/DzvXhIjqAgY1ROSSxvVuDgD4+L4oywWJyGVwmQQickohvhqL+9+4syPeuLNjLdWGiOoCttQQkVN6lTOciMgAgxoicirtG/kBAAI83R1cEyKqaxjUEJFTEUIAAJQKZggmIjkGNURUbUIIlGpFrV7zRFp2rV6PiJwHgxoiqraHFu/GrXM2oaC4FBdu5OHHPckoKtHWyrVPpGXVynWIyHkwqCGiaikoLsW2M1dx/loe9p+/gT6zN+KlXw9j1MLtFo87kHwDt87ZhPXH0mt0/dvah9boeCJyPQxqiKhavt1xXnr+wFe7pOcHL2RabK154ZdDOHclF49/s7fK19Tv6vLnQGEiMsCghoiqZeHms2b3Rb76t9mxNjXpnsovLpWee6lV1T4PEbmmKgc1W7ZswbBhwxAeHg6FQoEVK1ZYLJ+amooHHngAkZGRUCqVmDx5slGZL7/8En379kWDBg3QoEEDxMbGYvfu3bIy48aNg0KhkD2GDBlS1eoTkY0Miwq3uL/Vy6tNble7Vf9vqbyiEgCAQgFoanAeInJNVf5UyM3NRVRUFObPn29V+cLCQgQHB+PVV19FVJTpdOWbNm3C/fffj40bN2LHjh2IiIjA4MGDcfHiRVm5IUOGIDU1VXosX768qtUnIj3LdiXj7b+OYeJ3+5CeVVClY63p/inQa1nR0U3Jro78orLzebmroOCUbiIyUOVlEuLj4xEfH291+ebNm2PevHkAgK+//tpkme+//172+quvvsKvv/6KDRs24OGHH5a2azQahIWFVbXKRGTCuSs5ePn3w9LrwhItvh7Xw+rjtVYEJ9vPXsWt7eQDemsQ0yCvPKjxZNcTEZlQJ9tv8/LyUFxcjMDAQNn2TZs2ISQkBG3btsXEiRNx7do1s+coLCxEVlaW7EFEFW7kFcle/3vicqXHXLiRh+bTVuHJ7/dZFdR4uNk2+NCNx9HY+LxE5BrqZFDz0ksvITw8HLGxsdK2IUOG4JtvvsGGDRswe/ZsbN68GfHx8SgtNW7eBoCZM2fC399fekRERNRW9Ymcgkpp/Ou/J+k6mk9bhdf/OGLymD6zNwIAVh9Ow4oDlyq9hpvK+Bo1SdVXqssmXCc/uYjI0ercR8OsWbPwww8/4Pfff4eHh4e0ffTo0bjzzjvRuXNnjBgxAn/99Rf27NmDTZs2mTzP9OnTkZmZKT1SUlJq6R0QOQfDlpaWQd4YtXAHAOB/etO1zbmYkS97PaKr8cBh3cBefTUZU6Mtn1Gl4ngaIjKhTgU1H374IWbNmoV//vkHXbp0sVi2ZcuWCAoKwpkzZ0zu12g08PPzkz2IqEJhsXxq9bmrudU6T/tGfhjXuzk+urcrQnw1sn26gb36atJSo5slrlQyqCEiY3UmqHn//ffx9ttvY82aNejevXul5S9cuIBr166hUaNGtVA7ItdTXGqb5QwGtg3GG3d2hFKpQKC3Wrbvam4RhBA4mZaNwpKyAKcmA4V1uW+4mCURmVLloCYnJwcJCQlISEgAACQmJiIhIQHJyckAyrp99GcsAZDK5+Tk4MqVK0hISMCxY8ek/bNnz8Zrr72Gr7/+Gs2bN0daWhrS0tKQk5MjXfOFF17Azp07kZSUhA0bNmD48OFo3bo14uLiqvveieq1yhaibD5tFZb+lyi9NtdtpB9glBic87UVR7Dy4CXEzd2CZ5cnAACSr+dJ+w+mZFSpzrouM3Y/EZEpVQ5q9u7di+joaERHRwMApk6diujoaMyYMQNAWbI9XYCjoyu/b98+LFu2DNHR0bj99tul/QsWLEBRURHuueceNGrUSHp8+OGHAACVSoVDhw7hzjvvRGRkJB577DF069YNW7duhUYjb+4mIutY01Lzxp8Vf3wUmSn/56GKAcMlJsrM23AaALDmaBqaT1sl2zd8/n9W1VVHaqlh9xMRmVDlPDUDBgywONBv6dKlRtsqGxiYlJRkcb+npyfWrl1rTfWIyAqFJaXYftZ8SgRTzC1vcP5aRctLcanx7/q5K9Ubq2PoSnYh1hxNAwCYmFRFRFR3xtQQUe15ZvkBLN2eVKVjCs0ENQFeFZmFh5uYAWWNqzmF+HzTGWQY5M7RN+zTbVi2q6wVmN1PRGQKgxqiemjt0XTpubeV2Xl1QY3aTYnb2oVI2zuGV8wsfOa2Nlgw5ibsf22QVefs0sQfRy5movs76/H+mpMY/cVOk+UuZuQjTW8ZBy6RQESmVLn7iYhci6dahVwTU68NFZav46RxU2LxuB44mZaNb3cm4elb20hlPNxViO9s/YxErRC449Nt0usTadlGZQ5fyMSwz7bJtplaU4qIiC01RPXc1RzzXT4VZQrx+4GyBWZ1SxS0DfPFOyM6I9TPw9KhJvVsXrYESl5h5cHJuuPpRttMBT9ERGypISKLDGcsXc0prPE5pw6OxOgvdiLXRMZhQ438qx40EVH9xJYaIrK7t4Z3lJ7/M6Uf3MunLxm21AzpGGZ0bJ4VXWNERACDGqJ6yTDzryH9IKS69MfyZuYVI2HGIKyf2g+Rob5wV5XtzC6Ut9To58JJzyrAnwcv4ejFTKNzv3tXpxrXj4hcD4Maonoo1yCYGHlTE9lrjVvNPxp+mdBben4jrxgBXmq0DvEFAKmlxpD+WlH3LNyOp5cfwG/lY3n0jenVrMb1IyLXw6CGqJ64cCMPX2w5i6ISrVHOmTn3RqFNiA8A4MfxN0OlrPlHg/5U7yBfecuQrqXGUEH5+lAXbuQh5Xq+0f4/n+qDpFlDa1w3InJNHChMVE/0mb0RAPDe6hOy7RP6twIArJvaX9rWIrsA5rx8ezurrufhrsL9PSOw/vhlo5aVohLTWcYPJGfg+Z8PItjXePmTPq2D0LmJv1XXJqL6iUENUT3lrlJg64u3ItTPOIAI8fXAv8/1x61zNsu2d2rshyf6trT6GjPv7oKZJra7mWmpAYBf9l3AUBO5bj66L8rq6xJR/cTuJ6J6oLDEeAaRl9oNYf4eZrPztgz2QdKsobIp1WN6NbNJNl9dV5c5289eNdoW4sup3URkGYMaonogu8A4H4yXlcsj6K9HO7SL9dmCLTEMjPpFBste38grtsl1iKh+YVBDVA/8tDfFaJu3puq9z77VOMYaWfmWgxg3Jdd6IqLKMaghqgdOmlhWwNqFLPXZciHJF+LaSs/H9W6OZU/0Mlt220u32uy6ROS6OFCYqB7IMdn9ZN2vv/7q2LY0aWBr3H1TY2TkFaNdmC8UCgXGxjTD/3acl8qceiceAkJab4qIyBK21BDVAzGtGhpt89Y4PlBo5O+J9o38pBaglBvy3DRqNyUDGiKyGoMaonrAVIZga1tqalPK9Tzp+drJ/RxYEyJyRgxqiOqBgmKt0TZrW2rC/GpvKvXc0V0BAPf3jEDbMN9auy4RuYa696caEdmUVivw7urjRtutbal5c3hH/N+3+2xdLZM6hvtzGQQiqjYGNUQu7lKm8RpKAHAg+YZVxw/uEIpP749GB721nIiI6iIGNUQuTmvc8wQA2J+cYdXxCoUCw6LCbVchIiI74ZgaIhd3Ii3L5PZP7o+u5ZoQEdkXgxoiFzfezHiYO9n6QkQuhkENERERuQSOqSGqRx65pTlu5BYhvrNtFqYkIqpLGNQQubDiUvko4ZeGtIOHOzP0EpFrYvcTkQvLLaxY8+nt4R0Z0BCRS2NQQ+TCcotKpee3tg91YE2IiOyPQQ2RC8vTa6lpHODpwJoQEdkfgxoiF5ZTHtQ0acCAhohcH4MaIheWV9795F0HV+QmIrI1BjVELuz8tTwAQOLVXAfXhIjI/hjUELmwFQkXAQBFpWYWgCIiciEMaohc2M0tAgEALYO8HVwTIiL7Y1BD5MLUbmW/4j3LgxsiIlfGoIbIhRWXCgCAu4q/6kTk+vhJR+TCdMskuKkUDq4JEZH9VTmo2bJlC4YNG4bw8HAoFAqsWLHCYvnU1FQ88MADiIyMhFKpxOTJk02W+/nnn9GuXTt4eHigc+fOWL16tWy/EAIzZsxAo0aN4OnpidjYWJw+fbqq1SdyelqtQMr1PKvKlmjZUkNE9UeVP+lyc3MRFRWF+fPnW1W+sLAQwcHBePXVVxEVFWWyzPbt23H//ffjsccew4EDBzBixAiMGDECR44ckcq8//77+OSTT7Bw4ULs2rUL3t7eiIuLQ0FBQVXfApFTm/pTAvq+vxF/HrxUaVmppUbJlhoicn0KIYSo9sEKBX7//XeMGDHCqvIDBgxA165dMXfuXNn2++67D7m5ufjrr7+kbTfffDO6du2KhQsXQgiB8PBwPPfcc3j++ecBAJmZmQgNDcXSpUsxevToSq+dlZUFf39/ZGZmws/Pz+r3SFTXNJ+2CgDQvpEf/n62r8Wyr604gm93nsezt7XBlEGRtVE9IiKbqsr3d51ok96xYwdiY2Nl2+Li4rBjxw4AQGJiItLS0mRl/P390atXL6mMocLCQmRlZckeRK5EN7PJEl1LjTvH1BBRPVAngpq0tDSEhspXEA4NDUVaWpq0X7fNXBlDM2fOhL+/v/SIiIiwQ82JHOdgSkalZXSzn9w4poaI6gGX/aSbPn06MjMzpUdKSoqjq0RUY4YDhLVay73HJVqOqSGi+qNOBDVhYWFIT0+XbUtPT0dYWJi0X7fNXBlDGo0Gfn5+sgeRM7ucXYCJ3++Tbats+QNdEBTsq7FbvYiI6oo6EdTExMRgw4YNsm3r1q1DTEwMAKBFixYICwuTlcnKysKuXbukMkSubs7aUzhyUT42rLDYfFBzPDUL+5MzAAAdwxnUE5Hrc6vqATk5OThz5oz0OjExEQkJCQgMDETTpk0xffp0XLx4Ed98841UJiEhQTr2ypUrSEhIgFqtRocOHQAAzz77LPr37485c+Zg6NCh+OGHH7B371588cUXAMpmWU2ePBnvvPMO2rRpgxYtWuC1115DeHi41TOviJzdj3uNu1AttdTEz9sqPW8R5GOXOhER1SVVDmr27t2LgQMHSq+nTp0KABg7diyWLl2K1NRUJCcny46Jjo6Wnu/btw/Lli1Ds2bNkJSUBADo3bs3li1bhldffRUvv/wy2rRpgxUrVqBTp07ScS+++CJyc3Mxfvx4ZGRkoE+fPlizZg08PDyq+haIXEaxlatvqzimhojqgRrlqXEmzFNDzk6Xn0bf5hcGoFlD0ytw65dPmjXUbvUiIrInp8tTQ0TVU1RiuqVG/2+VyhL0ERG5CgY1RE6icYCn0TZzY2ryikql580aetmtTkREdQmDGiIH+e/MVfxkYvCvOYUlpUbbrmQXmiybXVACoGwsjae7qnoVJCJyMgxqiBxkzFe78OIvh9D/g42wZmhbfnnry6bnB0jbxi3ZY7JsdkExAMDXww0KBQcJE1H9wKCGyMHOX8vD+WtlSfL2J9/A+Wu5JssVlI+f8VRX3vKSVd5S4+tR5QmOREROi0ENkQOUGixvoFIqcOFGHu7+fDv6f7BJ2r7qUCqaT1uFPxIuSsd4uFUe1Gw4XpZ921fjbrtKExHVcQxqiBzAcHxMcakWiVcrWmh03VGTlu0HADz7Q4K0T+Mu/7XdePKy7HVBcSk+33QWAHAslavTE1H9waCGyAEKDJY3KCrVQqU39qWoVGt2urbGTQkPvcDmEYNxNVtOXbFhTYmInAeDGiIHMGqpKZF3R2Xll2DZrvMmj1UoFNgx7Taz5x7/bcWil6O6NalBLYmInAtHERI5gGErTFGpPMjp8e56k8epVWV/hzTwVlt1ndkju1SjdkREzoktNUQOUGgQ1IxcsAO/7L9Q6XE9WwRKz2Pbh5osM7pHRPn+ECi55hMR1SMMaogcwNR4md/2X6z0OHdVRZCiP11bqzebShfIdG4cUIMaEhE5HwY1RLVof/INJKRkmMwObA13VcWv7LO3tZGe67f86AIcFX+7iaie4cce1QuHL2Tio39OoqC4esGELeQVleDuz7djxPz/kJFXbPVx/SODpecHUjKk500DK9Z0aj9jDYrL14EqkYIa/noTUf3CgcJULwz7bBuAsplDUwZFOqQO+otMvrriiFXHbH1xIFIzC7C5fJq2/lpPhuNlbp2zCX9M6iMl6XPjeBoiqmf4pxzVK38fSXXYtfW7jlIzCyotPzamGSICvVBiZiVuQynX83HT2+uw69w1AMZBDxGRq2NQQ/XKqfQch127KutK3tQ0AOP7twIANA/ylrZbs5bTpfKAiS01RFTfsPuJqJZYsRA3AGBE13DMHR0tvQ4P8MTisd3xw54UTI9vZ/X1LmXmV7WKREROjS01VO+sPHjJMRc2EdRo3Kz7FbytfSi+fLg7Wgb7WH25r7clWl2WiMgVMKiheueZ5Qew4+w1R1cDAPDK0PaI7xQm27Yiwfqga3jXcLP7ZgzrWO16ERE5IwY15PJM5YQ5czm71ushTDTVuKuUWPBgt2qf8607O2Fc7+Ym993WLqTa5yUickYMasjlmcoJ46muG8PJ2oSUdSd98VBFYPPrxBirj/f3cscbd3Y0OYA42FdT8woSETmRuvHJTmRHprqavNSqWq+HqYHCnZv4AwAGdwxD4szbUaIVsqnf1vpj0i24dc5m6fXul2+r1nmIiJwZP/XI5U3+McFoW12Z7KxxqwiuFApFtQORlsE+2P3KbbildUN8PuYmhPh52KqKREROgy01VK/0btUQ289eQ5GVCe1sybChpm2or03PH+Lrge8fv9mm5yQiciZsqaF6I8hHLbWEFJdamTTGhoRB/9PCh6o/QJiIiIwxqCGX176RHwBg9sguUlBTVFL7LTX6jr81BC30MgUTEVHNMaghl6drIVG7KaVkd8UO7n7ydMBAZSIiV8egpp44mZaNQxcyHF0NuxNC4EZuEQ6mZEjbdKtWq5QKuKvKhgi/vvKotJ2IiFwDBwrXAynX8xA3dwsA4OCMwfD3cndwjexj5t/HsWjzOen1D+Nvxs0tG0rBi5tSieOpFUn3Dl/MRNeIgFqrn7VrPxERUfWwpcZFpVzPw6iF2/HP0TRM/SlB2n41t9BxlbIz/YAGAN5YeRRCCJy7mgugrKXmZHpFUHMl23XvBRFRfcSWGhf1yooj2JN0A3uS9sm2G87AcWUn0rIx8MNN0mulAhjYNhgbT14BABQUGy+fYE+6ZRIUdSVJDhGRi2FLjYvaaWbBxoJix876qW1J1/Kk5wXFWnx0b1fpda3PgKo/8SQRkUMwqHFRxVrTX9jZBSX1qrXGUANvNWLbly306IgEfEDdyWZMRORqGNS4KA8301OG7/9yJ1pMXw2ti838sSZQa9rQCwCw/vhlAMDCzWftWidDrnXHiYjqHgY1LqrETEuNzkNf76qlmtiPViuQdDUXQggUVtKV9HBMMzQO8JRtO6/XNVWbFBxUQ0RkFwxqXFR00wYW9/93xvSYG2cyY+URDPhwE/63PanSoGZguxDpuW4at79n7U5t1zUmMaQhIrIPBjVOpqRUi+93nce5KzkWy93SKqiWauQ43+1MBgC88ecxFJbPZFIqgMSZtyPEVyMrq9Zb/Xp0jwgAZQOF6/P4IiIiV8Ogxsl8t/M8Xvn9CG6ds9liuVIX/7L+69Al2eves/4FAGjcVFAoFGjSQN7V5KasaB9Rlj/PLy7FL/su2LmmwFdbz6HDjDVILM+Xw94nIiL7qHJQs2XLFgwbNgzh4eFQKBRYsWJFpcds2rQJN910EzQaDVq3bo2lS5fK9jdv3hwKhcLoMWnSJKnMgAEDjPZPmDChqtV3ejvPXZeeZ+YVI+lqLpb8l4jCEnnOldJKxtQ4u5d/Oyx7XVI+8Dm/vMXm4/u6yvZfyy2Snusvj/DCL4fsVMMK76w6jryiUtz/5U67X4uIqD6rclCTm5uLqKgozJ8/36ryiYmJGDp0KAYOHIiEhARMnjwZjz/+ONauXSuV2bNnD1JTU6XHunXrAACjRo2SneuJJ56QlXv//ferWn2nl1tUIj2Peusf3P7JVrz55zG8+ecxWTlrZis7YlFHW3nklhYW9zdr6I05o6Kk17qVugHgp70psrKXMvJtW7lKKDiqhojILqqcUTg+Ph7x8fFWl1+4cCFatGiBOXPmAADat2+Pbdu24eOPP0ZcXBwAIDg4WHbMrFmz0KpVK/Tv31+23cvLC2FhYVWtsksxDETyispaJpbtSsZ7d3WWtmut6H66mlOIRv6elZari/ysGOR7V3RjrD6ciohAL7QI8pa2X8spkpW7nluE8ADnvA9ERFTB7mNqduzYgdjYWNm2uLg47Nixw2T5oqIifPfdd3j00UeNpr5+//33CAoKQqdOnTB9+nTk5ZmfkltYWIisrCzZw9mduZwj636y5Ist5yot02f2RqdtrTGXZ0dv6AyUSgUWj+uBN+7sKCsTGeoje+2uquWhZWyoISKyC7t/mqelpSE0NFS2LTQ0FFlZWcjPN272X7FiBTIyMjBu3DjZ9gceeADfffcdNm7ciOnTp+Pbb7/Fgw8+aPa6M2fOhL+/v/SIiIiwyftxpNiPzA8Ors7g01KtQPJ1x+RqqakSM0HNU7e2qfTYOaO6yl7X+nIJRERkF3Vu9tPixYsRHx+P8PBw2fbx48cjLi4OnTt3xpgxY/DNN9/g999/x9mzprPCTp8+HZmZmdIjJSXFZDlXoett2nr6Ck6mZaNXi0CrjssuKKm8UB3z5ZZzmL3mhMl9+85X3pLl7+WOPa9UtB4evJBhq6pZhQ01RET2YfegJiwsDOnp6bJt6enp8PPzg6enQYbX8+exfv16PP7445Wet1evXgCAM2fOmNyv0Wjg5+cne7i6pKu5eGjxbsTN3QIP97JlEsb1bo5G/h6YN7orNG7G/905ThbUCCHw7urjZvenZxVadZ5gvTw2r644UuN6GUq8mothn27DB2tNB19ERGR7dg9qYmJisGHDBtm2devWISYmxqjskiVLEBISgqFDh1Z63oSEBABAo0aNbFJPV/Df2avS8/zyAcRdmvhjx/TbMLxrYzQrX/tIX3ZBca3VzxaKS427nfTHyDw3KLI2q2PWR+tO4fDFTMzfaNySyDw1RET2UeWgJicnBwkJCVJQkZiYiISEBCQnl2V3nT59Oh5++GGp/IQJE3Du3Dm8+OKLOHHiBD7//HP89NNPmDJliuy8Wq0WS5YswdixY+HmJp+UdfbsWbz99tvYt28fkpKSsHLlSjz88MPo168funTpUtW34LIOJGdIzy+WT1NW6n2DzhsdjZuaBsiOyS50rpYaUwObf3vyFun54I51Y3acu9J85MIp3URE9lHlKd179+7FwIEDpddTp04FAIwdOxZLly5FamqqFOAAQIsWLbBq1SpMmTIF8+bNQ5MmTfDVV19J07l11q9fj+TkZDz66KNG11Sr1Vi/fj3mzp2L3NxcREREYOTIkXj11VerWn2nUFKqRW5hKfy9KqYtW5POX5d4DqgIavRbBdo38sNvT96C5tNWSducrfvJ1KBeH40b1kzuC3eVEioLwURt0v+/M2TNdHsiIqq6Kgc1AwYMsPgFa5gtWHfMgQMHLJ538ODBZs8bERGBzZstLwvgSm6Z/S/Sswrxyu3t8US/lgAgW7Dx0BuDkXI9D0M/2SY7LszPw+hcykr6Oo6n1u2p7mmZBfh5bwpG92yKYF+N2Sno7cKqPmbq/ZFd8OKvh+Cusn0gVFBcanZfZYtvEhFR9dS52U/1XalWSINd9QfE6sbIAICXuwodw/2l134eZbHp+Wu5RufbnWg8G2iIXhfNz/suYMfZa1hzJLXmlbeDR5buwZx1pzBp2X4AxgHB4rHdq33uViFlY3HskXhP//+LiIhqB4OaOsbc6tvH0ypaVNzKk8VtfXEg/nq6D5o1LMuWu/74ZaPjTE1Xnju6q+z1o0v3YMJ3+/HaiiN17stY15KkC87089PEdwrDbe1DTR5nDd1ssMJi27ec5Bu01Bgm/CMiIttjUFPHGHbAZeaVzU5asMl4Fk1EoBc6NfZHXpH5cTGRob5G2zzcVXizPMuuQlHxBfztzvOY88/Jata8dugWo/TzcMP8B26q0bl0QU1aVgE+XndKGodkC4YtZP9M6W+mJBER2QqDmjrGcHRH1Fv/oFQr0NBbbfaYs1eMu510ppiZ4hxQPpDVcBjT5lNXrKpnbTA1LkU37kqlVEBZw0HBGjeV9HzehtN4ZMnuGp1P343yYPTpW1sjaVblKQqIiKjmGNQ4geyCYnRq7F95QROCfTQmt6vNrHdUl+blfLLhtPS8aWBZjh1d75MtZjlp3OX34FS66a6/mriUUWDzcxIRkWkMauoYU5OVFm05h3dWlQ0ajmnZ0OpzLXu8F9QmsggDMLv9zGXbf7FX1+d6XW6iPNzSTYc2XOy0OjzVKrP78otKbdId5a2puEaQT1lr29DOTBhJRGQPVZ7STbZVUqqVBv4Cpr+s9cfT7Dh3zepz97Cw/pO5oKYye5KuIzzAE43tMGPIkkb+ZdfTjamxRToaL3fjoGbb6av4cW8K/jx4Sdq25JEeGNg2pFrXeOjmZtLzzS8MxNbTV9E/Mrha5yIiIsvYUuNAN3KL0OPd9Xj+54PSNlvOxHGz8M1vrvvJkoMpGRi1cAdumfUvNp00nmllT7qBt7oxQCobtNS4mbgHDy7eJQtoAOCRJXvMnmP57mQMn/8fLmdVdDOV6s3QCtQbC+WtccOQTmEWW4iIiKj6GNQ40I97U3Ajrxi/7Lsgbcsvtpzht12Y8Wwmcyx10bib65ayEOz8uLdipfNZf9t/ocYHb24qe51VUGzT7icAuKNL9buCrucWYfpvh3EwJQOfbaxYWFV/gLOXmo2hRES1hUGNA+nPPEov/0s/r5I8MXdFNzba9tkD0VW+tqnlBgCgqFSLbaevmty3bFfF8hfV7b6qCsOZWWsOp6G0fKPSRpf/7IGbMLBt5d1BmfnGC3+uOlyRsDC3sOz/7dilLIxcsF3abmpldCIisg9+4jqQ0JtrdPhCJgDgZFo2APMtJqYaKO7oEo5lT/RC6xAfq8e6aLXm5zk9uHhXpcfXxgpLhtmDX/z1kBRc2KL7SWd4V+NA0dD+8zeMtukvS9G4Qdl9f2r5fpwo/z/0cFfWeNo5ERFZj0GNAyVdrcgvo/sCf39NWfK7olKtyQGlxaWmg5HerYKwfmp/dG/ewKpr92gRiGiDFbv1VbaA5sELmbiaU2jVtarL1BpJ646lA6h8Tauq8POsvIvokaV7cDo9W7ZNf+yMrsvpwo2KGVNct5KIqHYxqHGgtnqLME5ath9PfLMXvVuXTdkO8tGYXGixxExQY+1+HXeVEr8/eQti25ue1dNi+upKx80cu2TfxTAPm1jiQRc82DCmwYDIEDxyS/NKyw36eIvstX7gp1teQr+7iQtXEhHVLgY1DpRyPU/2et2xdGw6WZbR94GeESYTzJVoLX9RVrbfkP41Dr8xWLZv4Wb50gy9DKaI2yIBniVJ18ruj/5g3t/2XwRgOYtyVSmVCrw+rCMSZ95epeNK9YIa3RpV9r4nRERkHoMaB1q6PcnsPrWb0uTg1BILY2EAeZeINZ6+tQ0aeqvx5p0d4WEib4v+TJ5dBusZ1dbXt2HwZy8KhQL/168lgLJZZh/fFyXb76uRd1Pp32vds4y8iv+z+7pH2KeiRERkEuebOlDrEB+zGXzdVUrsPHfdaHtJqeWWmKoGNZ0a+2Pvq7Fmp0j/b3sSRvdsCj8P4x+V3Fpa0bs2B9tOGRSJ9o380C8yGIHeatwZ1Ri93tuAqzmFCPB2l5XVHzNz/lqebIwUAMy+p0ttVJmIiMqxpcaBejSvesbfylpqqjOA1lLOl5l/n0DUm/+YvO4lK5YROJmWjSvZhdh+9qpRQJZbWIKjlzKRU2g5N8+E/q3QqbGfxTK24uGuwojoxlLSPJVSgQ9HlQUnKdfzkVdUgiMXMzHoo83451iadNzVnEIM+HCT9HrGHR1qpb5ERFSBQY0DFZpYhVpH7aZE21DjRHuD2odaPOcrQ9sjxFeD12z8pXo5u2Kmk+4L//WVR9F82iqzx2w/exVxc7egx7vr8cCXuzB/o3yMTsfX12LoJ9vQ6fW1Jo8P8S1bjDOigRfmja56Lh5b8dTrlruWU4RJy/bj9OUcrD6cZvaYIF/TC4kSEZH9MKipRaVagTVHUqVEewUlFoIalRI/jL9Ztm3lU7egd+sgi9doGeyDXS/fhsf6tKh5hfXcMutf6fn13CLZvrwi45YWIYTRcgNLtyfK9ldGq5dor1WwT5Xqa0sBXmrZ6/PXKh/jc3unMHtVh4iIzGBQU4t+2JOMCd/tx+DyqcG7E40Tuumo3ZRo4K1GrF7LTJcmAVZdpyZLCFQnO/Gv5TOSdK7nFuGWWf9i+e4U2Xb96ebm8u3oFBSX4mpOWfBkKtGe4Uwse4oMrQiorJ2mbWpdKSIisi9+8tpRYUkppv16CE9+vw8p1/Ok6dq6WU2Wktfp8p2UVnGKdk3d0SUcv0yIqdIx89aflr1etOUsLmUWGJXTnwJdVMmA5z1JxoOk9X18X1frK1hDCoVC6go7d8X0wG59+otYEhFR7WFQY0fjv9mHH/akYPXhNAz4cJMsMdv68sy45uwrT8tf2cBge+jePBAe7uZ/NOINulb6lCcM1LmcZTpY019vynDtKcPuKP1xLBkGU9sf69MC4VYuB2ErmvL7Mf7bfSb3b592q/T8tnamExoSEZF9Maixo82nrkjPS7VCNgX48W/2Wjw2plVZoGBthmBbKyg23ZLSLzIYbw3vhMmxbfB4+bgdwxqmZpqeFaUfoBkGNXd9vl3WCnKofC0sAGjfqGzm0wtxbdE21BeTBra2+n3YioebcQ4ffeEBnvhlQgzuvqkxXhzSrpZqRURE+hjU1CJh9PVf5p8p/XDgtUGybQMiy/7ar2qGYHsrLC5FsK8Gk2Mj0SzIG4A8Qd8bK4+azK9jqNig+ykhJQMPfFm2kOb6Y+l4669j0j6f8qR3kwa2xtop/RzSvXPaTD4hfd2bB+Kje7simDOfiIgcgkFNLTI3Bdjf012WYK5lkLf0umO4f63UzdCfT/UxuX1s7+bSc1132tqj6VIOGktZkvWZGnCbVj4rrLJWrLrik/ujoVYp8eGoqMoLExGR3TGjsB11a9ZAGhtjidpgpox+98rzcW3h6+GG2zs3MjzMrjo38cfhNwaj8xv/yLYbjqfROXMlB+3CrE+QZ9j9ZM44vSCqrrkzKhxDOoaZTZRIRES1i0GNHblZmd7fz1Oefn9413DpuY/GDc8NbmvTelnLVHZi/eni+t0s1mYyPn8tF80aeht1P+lsO31V9rp3q4Ymy9U2X40bsvUyH/eLDAZgPvMzERHVPn4i25HWigRzQFkqfpVSgRNvD8GJt4fUmRwnlQUq/dsES89zC0vw3c7zlZ7zj4SyhHzmgppvdiRhYNuK84b4eVhTVbt7565O0vPuzRrgm0d7OrA2RERkSt349nRBmfnF2JNUedeTPg93lcmVsh2lssYXpVKBlsFlg4V/2puCV1ccMVlOf7rzR+tO4XpuEX47cNFk2X+OpaNB+UBgd5UCUU0cM6bI0PCujfHILc3RwMsdrwxt7+jqEBGRCex+spO5609ZVW5KbKSda1J91vQo6fLJHEzJNFvGcLbSB2tPYvnuZOl1dNMAHEjOkF7/Vp6heOqgtjXKjmxrrw/riNeHdXR0NYiIyAy21NhJaoZxRl1TrBx24xCaSnKzABVBjZvK/BsxbH06kZYle21u9tCCTWcqvT4REZEOgxo7UVp5Z5V1OaoBkDRrqMX9uoBFP1leZa7lyBfEbBXsg8SZtxuVe/DmZlafk4iIiEGNnVg5RtjqWUOOZGkW185z10xuf6p8Wvr4fi2N9iVfz4O7QcuOQqHAgjE3ybbd2z2iqlUlIqJ6jGNq7OT8tTyrytXxhhoAZV1M+tOZ9Zlbm2rqoEjcEdUIkSG+Jve3DvHF8dQszLijg7Sts8GgYI2F9aeIiIgM8VvDTo6lZpnd9+XD3aXnztBS46Gu2oys1c/0hVKpQLswP7Pda8fL74+/Xo4ewySE1ozpISIi0mFQ4wCtQ3yk504Q0yCigfUrYvt7uqNDuHFm4SXjepgsr5+8ztdDnoTQ0krhREREhvitUUOlWoFRC7fjjk+3SusfVUb/i1zlBP1PH9/XFT2bB+Lrcd2N9ulW6tZ55JbmJs9hKtAB5PfCU63C9PiKFa691OwdJSIi6/Fbo4bSswqkJHtHLmWha0RApcfod7PU/ZAGaNbQGz9NiDG576X4dgj21WDm3ycAmB9UrFtp25DWYEzO431bwlOtQu9WQTWoMRER1UdVbqnZsmULhg0bhvDwcCgUCqxYsaLSYzZt2oSbbroJGo0GrVu3xtKlS2X733jjDSgUCtmjXbt2sjIFBQWYNGkSGjZsCB8fH4wcORLp6elVrb7NZeYXS89XmMmS+9kD0dJzpUIe1JRaOUuqrnJXKRGjtz6TuTE03ho3zL2vq9H2q7ny6d0qpQIPxzSXddERERFZo8pBTW5uLqKiojB//nyryicmJmLo0KEYOHAgEhISMHnyZDz++ONYu3atrFzHjh2RmpoqPbZt2ybbP2XKFPz555/4+eefsXnzZly6dAl33313Vatvc/pfvptPXTFZpnlDbyx8sBtCfDVY9sTNsi6XjLwik8c4E/0BveuOmQ80R0Q3xuge8mnafVqzRYaIiGyjyt1P8fHxiI+Pt7r8woUL0aJFC8yZMwcA0L59e2zbtg0ff/wx4uLiKiri5oawsDCT58jMzMTixYuxbNky3Hpr2TpCS5YsQfv27bFz507cfPPNVX0bNuOuUuKJvi3w5dZE3NS0gckySoUCQzqFYUinsvdXqtflsupQqsNW4bYV/Zwz+ssdmKK/snd00wC0CPK2V7WIiKiesftA4R07diA2Nla2LS4uDjt27JBtO336NMLDw9GyZUuMGTMGyckVawPt27cPxcXFsvO0a9cOTZs2NTqPI4SWryRdqq0YKKyxMBjYGQYHV0WR3gDpvm0st7y0b1QxYDjYR2OhJBERUdXYfaBwWloaQkNDZdtCQ0ORlZWF/Px8eHp6olevXli6dCnatm2L1NRUvPnmm+jbty+OHDkCX19fpKWlQa1WIyAgwOg8aWlpJq9bWFiIwsJC6XVWlvm8MTXlXj5GplhvgIx+/hlL6yK5wtiRpoFe0vNRlWQBju9U0Rr34hDnbqEiIqK6pU7MftLvzurSpQt69eqFZs2a4aeffsJjjz1WrXPOnDkTb775pq2qaJFujExWQTGuZBci2FeD/OJSab+vmZk/AODjUSf+C2rES+2GF4e0xcGUDNzeyXQXoo5CocDB1wfjak4hWgU7f0BHRER1h927n8LCwoxmKaWnp8PPzw+enqaTugUEBCAyMhJnzpyRzlFUVISMjAyj85gbhzN9+nRkZmZKj5SUlJq/GTN0LTVbT19Fj3fX48zlbNl+bwtBjbVrRNV1Tw5ojUUPdYebqvIfKX9PdwY0RERkc3YPamJiYrBhwwbZtnXr1iEmxnTeEwDIycnB2bNn0ahRIwBAt27d4O7uLjvPyZMnkZycbPY8Go0Gfn5+soe9eLrL0/m//tka6fn9PSMsBjVaV4lqiIiIHKzKQU1OTg4SEhKQkJAAoGzKdkJCgjSwd/r06Xj44Yel8hMmTMC5c+fw4osv4sSJE/j888/x008/YcqUKVKZ559/Hps3b0ZSUhK2b9+Ou+66CyqVCvfffz8AwN/fH4899himTp2KjRs3Yt++fXjkkUcQExPj0JlPOt7fLZW9bpiwW3r+9vBOFo8tNbMgJBEREVVNlYOavXv3Ijo6GtHRZQnlpk6diujoaMyYMQMAkJqaKpu51KJFC6xatQrr1q1DVFQU5syZg6+++ko2nfvChQu4//770bZtW9x7771o2LAhdu7cieDgYKnMxx9/jDvuuAMjR45Ev379EBYWht9++63ab9xmXnwRPsu+lW1a2WGA9Nxt+jSLhwdxBhAREZFNKISoH/0fWVlZ8Pf3R2Zmpu26ooqKAC8vHAxuieFjPzZZJOnD4UBeHqBWy7avPZqG3/dfxOyRXeDv5W7yWCIiovquKt/fXNCyJj7/HCgtRdMM+bTyoJwbFS9KS8vKGYjrGIaFD3VjQENERGQjDGpq4uxZAECDAvlsJ7/CXADAiKMbZeWIiIjIfhjU1ESrVtLTdpcTpecF7mVdTYF5mUbliIiIyD4Y1NTEk08CqrLp3D99/5K0Od+tbPCvu7a0bP+TTzqkekRERPUJg5qaUKuBqVMBAH5FeYg9vQsAcMPLHwDgVlpStt9gkDARERHZnvPn6He0998v+/ejj6DSlsp2pcQMAGY/Wvt1IiIiqofYUmML778P5OXBLaqzbPN/PpYXdyQiIiLbYVBjK2o1lG3lq06/dkcHB1WGiIio/mFQY0P5RfLup54tAh1UEyIiovqHQY0NGS5QrXHj7SUiIqot/Na1Ix8PjsMmIiKqLQxq7EjjpnJ0FYiIiOoNBjVERETkEhjU2FD9WO+ciIiobmJQY0PeGo6hISIichQGNTYUEeglPV/51C0OrAkREVH9w6DGhm5p1VB63qVJgOMqQkREVA+xv8SGerVsiHmju8pabIiIiKh2MKixseFdGzu6CkRERPUSu5+IiIjIJTCoISIiIpfAoIaIiIhcAoMaIiIicgkMaoiIiMglMKghIiIil8CghoiIiFwCgxoiIiJyCQxqiIiIyCUwqCEiIiKXwKCGiIiIXAKDGiIiInIJDGqIiIjIJdSbVbqFEACArKwsB9eEiIiIrKX73tZ9j1tSb4Ka7OxsAEBERISDa0JERERVlZ2dDX9/f4tlFMKa0McFaLVaXLp0Cb6+vlAoFDY9d1ZWFiIiIpCSkgI/Pz+bntsV8P5UjvfIMt6fyvEeWcb7U7m6eo+EEMjOzkZ4eDiUSsujZupNS41SqUSTJk3seg0/P7869YNQ1/D+VI73yDLen8rxHlnG+1O5uniPKmuh0eFAYSIiInIJDGqIiIjIJTCosQGNRoPXX38dGo3G0VWpk3h/Ksd7ZBnvT+V4jyzj/amcK9yjejNQmIiIiFwbW2qIiIjIJTCoISIiIpfAoIaIiIhcAoMaIiIicgkMampo/vz5aN68OTw8PNCrVy/s3r3b0VWyiy1btmDYsGEIDw+HQqHAihUrZPuFEJgxYwYaNWoET09PxMbG4vTp07Iy169fx5gxY+Dn54eAgAA89thjyMnJkZU5dOgQ+vbtCw8PD0REROD999+391uziZkzZ6JHjx7w9fVFSEgIRowYgZMnT8rKFBQUYNKkSWjYsCF8fHwwcuRIpKeny8okJydj6NCh8PLyQkhICF544QWUlJTIymzatAk33XQTNBoNWrdujaVLl9r77dnEggUL0KVLFymxV0xMDP7++29pf32/P4ZmzZoFhUKByZMnS9vq+z164403oFAoZI927dpJ++v7/QGAixcv4sEHH0TDhg3h6emJzp07Y+/evdJ+l/+sFlRtP/zwg1Cr1eLrr78WR48eFU888YQICAgQ6enpjq6aza1evVq88sor4rfffhMAxO+//y7bP2vWLOHv7y9WrFghDh48KO68807RokULkZ+fL5UZMmSIiIqKEjt37hRbt24VrVu3Fvfff7+0PzMzU4SGhooxY8aII0eOiOXLlwtPT0+xaNGi2nqb1RYXFyeWLFkijhw5IhISEsTtt98umjZtKnJycqQyEyZMEBEREWLDhg1i79694uabbxa9e/eW9peUlIhOnTqJ2NhYceDAAbF69WoRFBQkpk+fLpU5d+6c8PLyElOnThXHjh0Tn376qVCpVGLNmjW1+n6rY+XKlWLVqlXi1KlT4uTJk+Lll18W7u7u4siRI0II3h99u3fvFs2bNxddunQRzz77rLS9vt+j119/XXTs2FGkpqZKjytXrkj76/v9uX79umjWrJkYN26c2LVrlzh37pxYu3atOHPmjFTG1T+rGdTUQM+ePcWkSZOk16WlpSI8PFzMnDnTgbWyP8OgRqvVirCwMPHBBx9I2zIyMoRGoxHLly8XQghx7NgxAUDs2bNHKvP3338LhUIhLl68KIQQ4vPPPxcNGjQQhYWFUpmXXnpJtG3b1s7vyPYuX74sAIjNmzcLIcruh7u7u/j555+lMsePHxcAxI4dO4QQZYGjUqkUaWlpUpkFCxYIPz8/6Z68+OKLomPHjrJr3XfffSIuLs7eb8kuGjRoIL766iveHz3Z2dmiTZs2Yt26daJ///5SUMN7VBbUREVFmdzH+1P2edmnTx+z++vDZzW7n6qpqKgI+/btQ2xsrLRNqVQiNjYWO3bscGDNal9iYiLS0tJk98Lf3x+9evWS7sWOHTsQEBCA7t27S2ViY2OhVCqxa9cuqUy/fv2gVqulMnFxcTh58iRu3LhRS+/GNjIzMwEAgYGBAIB9+/ahuLhYdo/atWuHpk2byu5R586dERoaKpWJi4tDVlYWjh49KpXRP4eujLP9zJWWluKHH35Abm4uYmJieH/0TJo0CUOHDjV6H7xHZU6fPo3w8HC0bNkSY8aMQXJyMgDeHwBYuXIlunfvjlGjRiEkJATR0dH48ssvpf314bOaQU01Xb16FaWlpbJfDgAIDQ1FWlqag2rlGLr3a+lepKWlISQkRLbfzc0NgYGBsjKmzqF/DWeg1WoxefJk3HLLLejUqROAsvqr1WoEBATIyhreo8rev7kyWVlZyM/Pt8fbsanDhw/Dx8cHGo0GEyZMwO+//44OHTrw/pT74YcfsH//fsycOdNoH+8R0KtXLyxduhRr1qzBggULkJiYiL59+yI7O5v3B8C5c+ewYMECtGnTBmvXrsXEiRPxzDPP4H//+x+A+vFZXW9W6SaqLZMmTcKRI0ewbds2R1elzmnbti0SEhKQmZmJX375BWPHjsXmzZsdXa06ISUlBc8++yzWrVsHDw8PR1enToqPj5eed+nSBb169UKzZs3w008/wdPT04E1qxu0Wi26d++O9957DwAQHR2NI0eOYOHChRg7dqyDa1c72FJTTUFBQVCpVEYj69PT0xEWFuagWjmG7v1auhdhYWG4fPmybH9JSQmuX78uK2PqHPrXqOueeuop/PXXX9i4cSOaNGkibQ8LC0NRUREyMjJk5Q3vUWXv31wZPz8/p/hQV6vVaN26Nbp164aZM2ciKioK8+bN4/1BWffJ5cuXcdNNN8HNzQ1ubm7YvHkzPvnkE7i5uSE0NLTe3yNDAQEBiIyMxJkzZ/gzBKBRo0bo0KGDbFv79u2lLrr68FnNoKaa1Go1unXrhg0bNkjbtFotNmzYgJiYGAfWrPa1aNECYWFhsnuRlZWFXbt2SfciJiYGGRkZ2Ldvn1Tm33//hVarRa9evaQyW7ZsQXFxsVRm3bp1aNu2LRo0aFBL76Z6hBB46qmn8Pvvv+Pff/9FixYtZPu7desGd3d32T06efIkkpOTZffo8OHDsg+UdevWwc/PT/qgiomJkZ1DV8ZZf+a0Wi0KCwt5fwDcdtttOHz4MBISEqRH9+7dMWbMGOl5fb9HhnJycnD27Fk0atSIP0MAbrnlFqNUEqdOnUKzZs0A1JPPakePVHZmP/zwg9BoNGLp0qXi2LFjYvz48SIgIEA2st5VZGdniwMHDogDBw4IAOKjjz4SBw4cEOfPnxdClE0TDAgIEH/88Yc4dOiQGD58uMlpgtHR0WLXrl1i27Ztok2bNrJpghkZGSI0NFQ89NBD4siRI+KHH34QXl5edWKaYGUmTpwo/P39xaZNm2TTTfPy8qQyEyZMEE2bNhX//vuv2Lt3r4iJiRExMTHSft1008GDB4uEhASxZs0aERwcbHK66QsvvCCOHz8u5s+f7zTTTadNmyY2b94sEhMTxaFDh8S0adOEQqEQ//zzjxCC98cU/dlPQvAePffcc2LTpk0iMTFR/PfffyI2NlYEBQWJy5cvCyF4f3bv3i3c3NzEu+++K06fPi2+//574eXlJb777jupjKt/VjOoqaFPP/1UNG3aVKjVatGzZ0+xc+dOR1fJLjZu3CgAGD3Gjh0rhCibKvjaa6+J0NBQodFoxG233SZOnjwpO8e1a9fE/fffL3x8fISfn5945JFHRHZ2tqzMwYMHRZ8+fYRGoxGNGzcWs2bNqq23WCOm7g0AsWTJEqlMfn6+ePLJJ0WDBg2El5eXuOuuu0RqaqrsPElJSSI+Pl54enqKoKAg8dxzz4ni4mJZmY0bN4quXbsKtVotWrZsKbtGXfboo4+KZs2aCbVaLYKDg8Vtt90mBTRC8P6YYhjU1Pd7dN9994lGjRoJtVotGjduLO677z5ZDpb6fn+EEOLPP/8UnTp1EhqNRrRr10588cUXsv2u/lmtEEIIx7QREREREdkOx9QQERGRS2BQQ0RERC6BQQ0RERG5BAY1RERE5BIY1BAREZFLYFBDRERELoFBDREREbkEBjVERETkEhjUEBERkUtgUENEREQugUENERERuQQGNUREROQS/h9+epJV0fVAOgAAAABJRU5ErkJggg==", 262 | "text/plain": [ 263 | "
" 264 | ] 265 | }, 266 | "metadata": {}, 267 | "output_type": "display_data" 268 | } 269 | ], 270 | "source": [ 271 | "env.reset()\n", 272 | "env.render()" 273 | ] 274 | }, 275 | { 276 | "cell_type": "markdown", 277 | "metadata": {}, 278 | "source": [ 279 | "- **Short** and **Long** positions are shown in `red` and `green` colors.\n", 280 | "- As you see, the starting *position* of the environment is always **Short**." 281 | ] 282 | }, 283 | { 284 | "cell_type": "markdown", 285 | "metadata": {}, 286 | "source": [ 287 | "### A complete example" 288 | ] 289 | }, 290 | { 291 | "cell_type": "code", 292 | "execution_count": 5, 293 | "metadata": {}, 294 | "outputs": [ 295 | { 296 | "name": "stdout", 297 | "output_type": "stream", 298 | "text": [ 299 | "info: {'total_reward': 27.89616584777832, 'total_profit': 0.989812615901, 'position': }\n" 300 | ] 301 | }, 302 | { 303 | "data": { 304 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjUAAAHNCAYAAADmPIpxAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAB2MElEQVR4nO3deVyU1f4H8M8zw74MKDsOCLnimloqKglpoZlZiF6zrvarNL1WoJXlrW7rzW5lQlfTbnW1zcyIzK5luaGYS7mQlrui7CgqDCDrzPn9MczIwAAzwDAwfN6vFy+b85x5njNPzMyXc77nHEkIIUBERETUwcms3QAiIiKi1sCghoiIiGwCgxoiIiKyCQxqiIiIyCYwqCEiIiKbwKCGiIiIbAKDGiIiIrIJDGqIiIjIJjCoISIiIpvAoIYAACkpKZAkCSkpKdZuSrsgSRJefvllazeDqF2+N6urq7F48WIEBQVBJpPh3nvvBcD3DVkfgxorkiTJpB9TPszeeOMNbNy40eJtXrt2rUHb7Ozs0K1bNzz00EPIzs62+PU7gt9++w2PP/44+vfvD1dXVwQHB2P69Ok4ffp0vbqN/X+/4447mrxWeXk5li5din79+sHFxQXdunXDtGnT8Oeff9aru3XrVowZMwYuLi7o0qULYmNjceHCBaPnLS4uxuLFixEaGgpHR0d069YNsbGxuH79ur5Obm4unnvuOURFRcHd3b3J39XKykq88cYb6Nu3L5ycnODn54dJkyYhKyurXt3Dhw/jnnvuQdeuXeHi4oIBAwbgvffea/J+tBZbeG86OTmhd+/eePzxx5Gfn9+q1/rvf/+Lt99+G7Gxsfjkk0+wcOFCo/X27t2Ll19+GYWFhS2+5t69e/W/v/7+/njyySdRUlJi0nPz8/Pxf//3f/D19YWzszOGDh2Kr7/+2mjdbdu2ISoqCt7e3vD09MTw4cPx2Weftck5V61ahWnTpiE4OBiSJOGhhx4yer7du3fjnnvuQVBQEJycnODv748JEybgl19+Mel+2DI7azegM6v7S/3pp59i69at9crDwsKaPNcbb7yB2NhY/V9Mlvbqq68iNDQU5eXl2L9/P9auXYs9e/bgjz/+gJOTU5u0ob3617/+hV9++QXTpk3DoEGDkJeXhxUrVmDo0KHYv38/BgwYoK9r7IPt4MGDSExMxJ133tnktR544AFs2rQJc+bMwdChQ5GTk4OVK1ciPDwcx44dQ/fu3QEA//vf/zBlyhQMHToUb775JlQqFRITEzFmzBgcOXIEPj4++nMWFRVh7NixyMrKwty5c9GzZ09cvnwZqampqKiogIuLCwDg1KlT+Ne//oVevXph4MCB2LdvX4PtrKqqwqRJk7B3717MmTMHgwYNwrVr13DgwAEUFRVBqVTq6/7888+YPHkyhgwZghdffBFubm44d+6c0eDHUmzlvblnzx6sWrUKP/zwA/744w/9/7uW2rFjB7p164bly5cblJeVlcHO7sbXyt69e/HKK6/goYcegqenZ7Ovl5aWhnHjxiEsLAzvvvsusrKy8M477+DMmTP48ccfG32uSqXCmDFjkJ+fj7i4OPj7+2PDhg2YPn06vvjiC8ycOVNfd9OmTbj33nsRHh6Ol19+GZIkYcOGDZg1axYKCgr0wZslzgloPzuKi4sxfPhw5ObmNviaTp8+DZlMhnnz5sHf3x/Xrl3D559/jttuuw2bN2/GhAkTmnurOz5B7caCBQtEc/+XuLq6itmzZzf72jt37hQAxM6dOxutt2bNGgFA/Pbbbwblzz77rAAgvvrqq2a3oS2VlJQ0ehyAeOmll5p17l9++UVUVFQYlJ0+fVo4OjqKBx54oMnnP/LII0KSJJGZmdlovaysLAFAPP300wblO3bsEADEu+++qy/r16+f6Nmzp0G70tLShEwmE4sWLTJ4/vz584Wnp6c4f/58o9dXqVTiypUrQgghvv7660Z/f/71r38Je3t7ceDAgUbPWVRUJPz8/MR9990n1Gp1o3XbUkd+by5atEgAEOvWrWvwuU29H+qKiooS/fv3b7Le22+/LQCI9PR0s85f18SJE0VAQIAoKirSl3344YcCgPjpp58afe5bb70lAIjt27fry9Rqtbj11luFv7+/wXvijjvuEIGBgaK8vFxfVlVVJXr06CEGDRpk0XMKIcSFCxeERqMRQpj/e1NaWir8/PxEdHS0yc+xRRx+audKS0vx1FNPISgoCI6OjujTpw/eeecdiFqbq0uShNLSUnzyySf6rmddt+XFixfxt7/9DX369IGzszO8vLwwbdq0BocdmisiIgIAcO7cOYPykydPIjY2Fl27doWTkxNuueUWbNq0SX+8sLAQcrncYGihoKAAMpkMXl5eBq9z/vz58Pf31z9O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9xqcU/cF9fHHHxuc46OPPoKdnV2T+RdtpSO9N01lzmsylaurKwAYff2mTun28PDA+PHj8fnnn+uXMQC0M8tKSkowbdo0fdn169dx8uRJg99dY86cOYPVq1fj7rvv1r+PfH194enpiW+//VafawZo87y+//579O3bt9FAwRLnbMilS5fqlRUWFuKbb75BUFCQvmezM2JPTTs2efJkREVF4fnnn8eFCxcwePBg/Pzzz/juu+8QHx9vkAsybNgwbNu2De+++65+OGfEiBG4++678dlnn8HDwwP9+vXDvn37sG3bNoPpna3lmWeewbRp07B27VrMmzcPK1euxJgxYzBw4EDMmTMHN910E/Lz87Fv3z5kZWXh999/1z9XF7CcOnUKb7zxhr78tttuw48//ghHR0eDv4j69u2LHj164Omnn0Z2djYUCgW++eYbs4ZHbr75Ztx///14//33UVRUhFGjRmH79u04e/as0fphYWEYO3Zsk8nCTz31FDZt2oTJkyfj6tWr+Pzzzw2OP/jgg/We88UXXyAwMLDBL+3s7GyEhYVh9uzZWLt2LQDt70f//v3x6quv4uLFixg5ciTOnj2LFStWICAgAI888oj++Z9//jm++eYb3HbbbXBzc8O2bduwYcMGPProo/rp7TrLly/HHXfcgTFjxuCxxx5DUVER3n33XfTu3btecunrr78OAPptGT777DPs2bMHAPDCCy/o673xxhvYvn07br/9dn0i6nvvvYeuXbvi73//u77ekCFD8PDDD+O///0vqqur9ff766+/xpIlS0waamgLHe292dqvyVS6nr/nn38eM2bMgL29PSZPngxXV1esWLECr7zyCnbu3NlksPrPf/4To0aNwtixYzF37lxkZWVh2bJluPPOOw1Wz/31118RFRWFl156yWAPqn79+unXtEpPT8eqVavQtWtXrF69Wl9HLpfj6aefxgsvvICRI0di1qxZUKvV+Pjjj5GVlVXvfWyJc37//ff6z8WqqiocPXpU/x675557MGjQIADaP8iUSiVGjBgBX19fZGRkYM2aNcjJycFXX31l4v8dG9XW062oYcZWLS0uLhYLFy4UgYGBwt7eXvTq1Uu8/fbb9aaqnjx5Utx2223C2dlZANBPBbx27Zr4v//7P+Ht7S3c3NxEdHS0OHnypOjevbvBdMGWrloqhHbqcI8ePUSPHj1EdXW1EEKIc+fOiVmzZgl/f39hb28vunXrJu6++26RlJRU7/m+vr4CgMjPz9eX7dmzRwAQERER9eofP35cjB8/Xri5uQlvb28xZ84c8fvvvwsAYs2aNfp6s2fPFq6urkZfT1lZmXjyySeFl5eXcHV1FZMnTxaZmZktmtI9duxYgynWdX/qOnnypABQb2Xf2tLT0w3+v+pcvXpVLFy4UPTu3Vs4OjoKb29vMWPGjHqrAR84cEDcdtttokuXLsLJyUkMHjxYrF69usEpz1u3bhUjR44UTk5OomvXruKvf/2rwTTx2vfE1Nd56NAhMX78eOHq6irc3d3FlClTxOnTp+vVq6ysFC+//LLo3r27sLe3Fz179hTLly9v8N60hY7+3qytsfeDqa/J1CndQgjx2muviW7dugmZTGYwvdvUKd06qampYtSoUcLJyUn4+PiIBQsWCJVKZVBHd6/qtmHGjBkiKChIODg4iMDAQDFv3jyDz5navvjiCzF8+HDh6ekpnJ2dxYgRI4x+XlninLrp9sZ+an+mrVixQowZM0Z4e3sLOzs74ePjIyZPnmx02ntnIwlRp/+TiIiIqANiTg0RERHZBAY1REREZBMY1BAREZFNYFBDRERENoFBDREREdkEBjVERERkExjUEBERkU1gUENEREQ2gUENERER2QQGNURERGQTGNQQERGRTWBQQ0RERDaBQQ0RERHZBAY1REREZBMY1BAREZFNYFBDRERENoFBDREREdkEBjVERERkExjUEBERkU1gUENEREQ2gUENERER2QQGNURERGQTGNQQERGRTWBQQ0RERDaBQQ0RERHZBAY1REREZBMY1BAREZFNYFBDRERENoFBDREREdkEBjVERERkExjUEBERkU1gUENEREQ2wc7aDWgrGo0GOTk5cHd3hyRJ1m4OERERmUAIgeLiYgQGBkIma7wvptMENTk5OQgKCrJ2M4iIiKgZMjMzoVQqG63TaYIad3d3ANqbolAorNwaIiIiMoVKpUJQUJD+e7wxnSao0Q05KRQKBjVEREQdjCmpI0wUJiIiIpvAoIaIiIhsAoMaIiIisgkMaoiIiMgmMKghIiIim8CghoiIiGwCgxoiIiKyCQxqiIiIyCZ0msX3iIiIbJlao0ZqRipyi3MR4B6AiOAIyGVyazerTTGoISIi6uCSTyQjbkscslRZ+jKl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djOIiKgD4vBTC3m6OOCLR0egX4ACBSWVuP/DA/rp2zrMpyEiIrI8BjWtwDCwqcCM/xzQBzJA7ZlPnXA6NxERURthUNNKurhqA5uwmsDm/g/36wObTr07NxERURthUNOKdIFNX393XC7WBjZ/ZBch65p25hN7aoiIiCyHQU0r61onsPnLB/sgBODl6oCurpz5REREZCkMaizAy80RXzw6An383FFaqd1ZutOvT0NERGRhDGosxMvNEV/MGYHeNdsi9AtUWLlFREREto3r1FiQt5sjvpobju/SsnH34EBrN4eIiMimMaixsC6uDnhodKi1m0FERGTzOPxERERENoFBDREREdkEBjVERERkExjUEBERkU1gUENEREQ2gUENERER2QQGNURERGQTGNQQERGRTWBQQ0RERDaBQQ0RERHZBAY1REREZBMY1BAREZFNYFBDRERENqHT7NIthAAAqFQqK7eEiIiITKX73tZ9jzem0wQ1xcXFAICgoCArt4SIiIjMVVxcDA8Pj0brSMKU0McGaDQa5OTkwN3dHZIkteq5VSoVgoKCkJmZCYVC0arntkW8X+bjPTMP75f5eM/Mw/tlnpbcLyEEiouLERgYCJms8ayZTtNTI5PJoFQqLXoNhULBX24z8H6Zj/fMPLxf5uM9Mw/vl3mae7+a6qHRYaIwERER2QQGNURERGQTGNS0AkdHR7z00ktwdHS0dlM6BN4v8/GemYf3y3y8Z+bh/TJPW92vTpMoTERERLaNPTVERERkExjUEBERkU1gUENEREQ2gUENERER2QQGNS20cuVKhISEwMnJCSNGjMCvv/5q7Sa1G7t378bkyZMRGBgISZKwceNGg+NCCPzjH/9AQEAAnJ2dMX78eJw5c8Y6jW0Hli5diltvvRXu7u7w9fXFvffei1OnThnUKS8vx4IFC+Dl5QU3NzdMnToV+fn5Vmqxda1atQqDBg3SL+YVHh6OH3/8UX+c96ppb775JiRJQnx8vL6M9+2Gl19+GZIkGfz07dtXf5z3yrjs7Gw8+OCD8PLygrOzMwYOHIiDBw/qj1vys59BTQt89dVXWLRoEV566SUcPnwYgwcPRnR0NC5dumTtprULpaWlGDx4MFauXGn0+FtvvYX33nsPq1evxoEDB+Dq6oro6GiUl5e3cUvbh127dmHBggXYv38/tm7diqqqKtx5550oLS3V11m4cCG+//57fP3119i1axdycnIQExNjxVZbj1KpxJtvvolDhw7h4MGDuP322zFlyhT8+eefAHivmvLbb7/hgw8+wKBBgwzKed8M9e/fH7m5ufqfPXv26I/xXtV37do1jB49Gvb29vjxxx9x/PhxLFu2DF26dNHXsehnv6BmGz58uFiwYIH+sVqtFoGBgWLp0qVWbFX7BEB8++23+scajUb4+/uLt99+W19WWFgoHB0dxZdffmmFFrY/ly5dEgDErl27hBDa+2Nvby++/vprfZ0TJ04IAGLfvn3Wama70qVLF/HRRx/xXjWhuLhY9OrVS2zdulWMHTtWxMXFCSH4O1bXSy+9JAYPHmz0GO+Vcc8++6wYM2ZMg8ct/dnPnppmqqysxKFDhzB+/Hh9mUwmw/jx47Fv3z4rtqxjSE9PR15ensH98/DwwIgRI3j/ahQVFQEAunbtCgA4dOgQqqqqDO5Z3759ERwc3OnvmVqtxvr161FaWorw8HDeqyYsWLAAkyZNMrg/AH/HjDlz5gwCAwNx00034YEHHkBGRgYA3quGbNq0CbfccgumTZsGX19fDBkyBB9++KH+uKU/+xnUNFNBQQHUajX8/PwMyv38/JCXl2elVnUcunvE+2ecRqNBfHw8Ro8ejQEDBgDQ3jMHBwd4enoa1O3M9+zYsWNwc3ODo6Mj5s2bh2+//Rb9+vXjvWrE+vXrcfjwYSxdurTeMd43QyNGjMDatWuxZcsWrFq1Cunp6YiIiEBxcTHvVQPOnz+PVatWoVevXvjpp58wf/58PPnkk/jkk08AWP6zv9Ps0k3UkSxYsAB//PGHwfg91denTx+kpaWhqKgISUlJmD17Nnbt2mXtZrVbmZmZiIuLw9atW+Hk5GTt5rR7EydO1P/3oEGDMGLECHTv3h0bNmyAs7OzFVvWfmk0Gtxyyy144403AABDhgzBH3/8gdWrV2P27NkWvz57aprJ29sbcrm8XqZ7fn4+/P39rdSqjkN3j3j/6nv88cfxv//9Dzt37oRSqdSX+/v7o7KyEoWFhQb1O/M9c3BwQM+ePTFs2DAsXboUgwcPRmJiIu9VAw4dOoRLly5h6NChsLOzg52dHXbt2oX33nsPdnZ28PPz431rhKenJ3r37o2zZ8/yd6wBAQEB6Nevn0FZWFiYftjO0p/9DGqaycHBAcOGDcP27dv1ZRqNBtu3b0d4eLgVW9YxhIaGwt/f3+D+qVQqHDhwoNPePyEEHn/8cXz77bfYsWMHQkNDDY4PGzYM9vb2Bvfs1KlTyMjI6LT3rC6NRoOKigreqwaMGzcOx44dQ1pamv7nlltuwQMPPKD/b963hpWUlODcuXMICAjg71gDRo8eXW8pitOnT6N79+4A2uCzv8Wpxp3Y+vXrhaOjo1i7dq04fvy4mDt3rvD09BR5eXnWblq7UFxcLI4cOSKOHDkiAIh3331XHDlyRFy8eFEIIcSbb74pPD09xXfffSeOHj0qpkyZIkJDQ0VZWZmVW24d8+fPFx4eHiIlJUXk5ubqf65fv66vM2/ePBEcHCx27NghDh48KMLDw0V4eLgVW209zz33nNi1a5dIT08XR48eFc8995yQJEn8/PPPQgjeK1PVnv0kBO9bbU899ZRISUkR6enp4pdffhHjx48X3t7e4tKlS0II3itjfv31V2FnZyf++c9/ijNnzogvvvhCuLi4iM8//1xfx5Kf/QxqWujf//63CA4OFg4ODmL48OFi//791m5Su7Fz504BoN7P7NmzhRDaqX0vvvii8PPzE46OjmLcuHHi1KlT1m20FRm7VwDEmjVr9HXKysrE3/72N9GlSxfh4uIi7rvvPpGbm2u9RlvRww8/LLp37y4cHByEj4+PGDdunD6gEYL3ylR1gxretxv+8pe/iICAAOHg4CC6desm/vKXv4izZ8/qj/NeGff999+LAQMGCEdHR9G3b1/xn//8x+C4JT/7JSGEaHl/DxEREZF1MaeGiIiIbAKDGiIiIrIJDGqIiIjIJjCoISIiIpvAoIaIiIhsAoMaIiIisgkMaoiIiMgmMKghIiIim8CghoiIiGwCgxoiIiKyCQxqiIiIyCYwqCEiIiKb8P8cNHmu1PyZwQAAAABJRU5ErkJggg==", 305 | "text/plain": [ 306 | "
" 307 | ] 308 | }, 309 | "metadata": {}, 310 | "output_type": "display_data" 311 | } 312 | ], 313 | "source": [ 314 | "import numpy as np\n", 315 | "import matplotlib.pyplot as plt\n", 316 | "\n", 317 | "import gymnasium as gym\n", 318 | "import gym_anytrading\n", 319 | "from gym_anytrading.envs import TradingEnv, ForexEnv, StocksEnv, Actions, Positions \n", 320 | "from gym_anytrading.datasets import FOREX_EURUSD_1H_ASK, STOCKS_GOOGL\n", 321 | "\n", 322 | "\n", 323 | "env = gym.make('forex-v0', frame_bound=(50, 100), window_size=10)\n", 324 | "# env = gym.make('stocks-v0', frame_bound=(50, 100), window_size=10)\n", 325 | "\n", 326 | "observation = env.reset(seed=2023)\n", 327 | "while True:\n", 328 | " action = env.action_space.sample()\n", 329 | " observation, reward, terminated, truncated, info = env.step(action)\n", 330 | " done = terminated or truncated\n", 331 | "\n", 332 | " # env.render()\n", 333 | " if done:\n", 334 | " print(\"info:\", info)\n", 335 | " break\n", 336 | "\n", 337 | "plt.cla()\n", 338 | "env.unwrapped.render_all()\n", 339 | "plt.show()" 340 | ] 341 | }, 342 | { 343 | "cell_type": "markdown", 344 | "metadata": {}, 345 | "source": [ 346 | "- You can use `render_all` method to avoid rendering on each step and prevent time-wasting.\n", 347 | "- As you see, the first **10** points (`window_size`=10) on the plot don't have a *position*. Because they aren't involved in calculating reward, profit, etc. They just display the first observations. So the environment's `_start_tick` and initial `_last_trade_tick` are **10** and **9**." 348 | ] 349 | }, 350 | { 351 | "cell_type": "markdown", 352 | "metadata": {}, 353 | "source": [ 354 | "#### More examples\n", 355 | "\n", 356 | "[Here](https://github.com/AminHP/gym-anytrading/blob/master/examples) are some examples that mix `gym-anytrading` with some well-known libraries, such as `Stable-Baselines3` and `QuantStats`, and show how to utilize our trading environments in other RL or trading libraries." 357 | ] 358 | }, 359 | { 360 | "cell_type": "markdown", 361 | "metadata": {}, 362 | "source": [ 363 | "### Extend and manipulate TradingEnv\n", 364 | "\n", 365 | "In case you want to process data and extract features outside the environment, it can be simply done by two methods:\n", 366 | "\n", 367 | "**Method 1 (Recommended):**" 368 | ] 369 | }, 370 | { 371 | "cell_type": "code", 372 | "execution_count": 6, 373 | "metadata": {}, 374 | "outputs": [], 375 | "source": [ 376 | "def my_process_data(env):\n", 377 | " start = env.frame_bound[0] - env.window_size\n", 378 | " end = env.frame_bound[1]\n", 379 | " prices = env.df.loc[:, 'Low'].to_numpy()[start:end]\n", 380 | " signal_features = env.df.loc[:, ['Close', 'Open', 'High', 'Low']].to_numpy()[start:end]\n", 381 | " return prices, signal_features\n", 382 | "\n", 383 | "\n", 384 | "class MyForexEnv(ForexEnv):\n", 385 | " _process_data = my_process_data\n", 386 | "\n", 387 | "\n", 388 | "env = MyForexEnv(df=FOREX_EURUSD_1H_ASK, window_size=12, frame_bound=(12, len(FOREX_EURUSD_1H_ASK)))" 389 | ] 390 | }, 391 | { 392 | "cell_type": "markdown", 393 | "metadata": {}, 394 | "source": [ 395 | "**Method 2:**" 396 | ] 397 | }, 398 | { 399 | "cell_type": "code", 400 | "execution_count": 7, 401 | "metadata": {}, 402 | "outputs": [], 403 | "source": [ 404 | "def my_process_data(df, window_size, frame_bound):\n", 405 | " start = frame_bound[0] - window_size\n", 406 | " end = frame_bound[1]\n", 407 | " prices = df.loc[:, 'Low'].to_numpy()[start:end]\n", 408 | " signal_features = df.loc[:, ['Close', 'Open', 'High', 'Low']].to_numpy()[start:end]\n", 409 | " return prices, signal_features\n", 410 | "\n", 411 | "\n", 412 | "class MyStocksEnv(StocksEnv):\n", 413 | " \n", 414 | " def __init__(self, prices, signal_features, **kwargs):\n", 415 | " self._prices = prices\n", 416 | " self._signal_features = signal_features\n", 417 | " super().__init__(**kwargs)\n", 418 | "\n", 419 | " def _process_data(self):\n", 420 | " return self._prices, self._signal_features\n", 421 | "\n", 422 | " \n", 423 | "prices, signal_features = my_process_data(df=STOCKS_GOOGL, window_size=30, frame_bound=(30, len(STOCKS_GOOGL)))\n", 424 | "env = MyStocksEnv(prices, signal_features, df=STOCKS_GOOGL, window_size=30, frame_bound=(30, len(STOCKS_GOOGL)))" 425 | ] 426 | }, 427 | { 428 | "cell_type": "markdown", 429 | "metadata": {}, 430 | "source": [ 431 | "## Related Projects\n", 432 | "\n", 433 | "* A more complicated version of `anytrading` with five actions, three positions, and a better reward function is developed in the [DI-engine](https://github.com/opendilab/DI-engine/tree/main/dizoo/gym_anytrading) project. It is a mid-level tool (somewhere between `anytrading` and `mtsim`), appropriate for semi-experts. More information and documentation can be found [here](https://github.com/opendilab/DI-engine/tree/main/dizoo/gym_anytrading/envs)." 434 | ] 435 | } 436 | ], 437 | "metadata": { 438 | "kernelspec": { 439 | "display_name": "Python 3 (ipykernel)", 440 | "language": "python", 441 | "name": "python3" 442 | }, 443 | "language_info": { 444 | "codemirror_mode": { 445 | "name": "ipython", 446 | "version": 3 447 | }, 448 | "file_extension": ".py", 449 | "mimetype": "text/x-python", 450 | "name": "python", 451 | "nbconvert_exporter": "python", 452 | "pygments_lexer": "ipython3", 453 | "version": "3.9.6" 454 | } 455 | }, 456 | "nbformat": 4, 457 | "nbformat_minor": 4 458 | } 459 | --------------------------------------------------------------------------------