Repository: davecliff/BristolStockExchange Branch: master Commit: 6ebc4155440b Files: 76 Total size: 1.2 MB Directory structure: gitextract_ke5l3892/ ├── .gitignore ├── .idea/ │ ├── .name │ ├── BristolStockExchange.iml │ ├── inspectionProfiles/ │ │ └── profiles_settings.xml │ ├── misc.xml │ ├── modules.xml │ ├── vcs.xml │ └── workspace.xml ├── BSE.py ├── BSE_VernonSmith1962_demo.ipynb ├── LICENSE.md ├── Offsets_BTC_USD/ │ └── empty_file ├── README.md ├── Trader_AA.py ├── ZhenZhang/ │ ├── README.md │ └── source/ │ ├── BSE2.py │ ├── BSE2_msg_classes.py │ ├── BSE_trader_agents.py │ ├── GDX.py │ ├── IAA_MLOFI.py │ ├── IAA_NEW.py │ ├── IGDX_MLOFI.py │ ├── IZIP_MLOFI.py │ ├── Simple_MLOFI.py │ ├── ZZISHV.py │ └── dataAnalysis/ │ ├── box_analysis.py │ ├── hypothesisTest.py │ └── matplotlib4.py ├── clean.sh ├── snashall2019.py └── wiki_images/ └── TIFFs/ ├── 2shockGVWY.tiff ├── 2shockSHVR.tiff ├── 2shockZIC.tiff ├── 2shockZIP.tiff ├── TwoShock4Up.tiff ├── fig4.1.tiff ├── fig4.2.tiff ├── fig4.3.tiff ├── fig4.4.tiff ├── fig4.5.tiff ├── fig4.6.tiff ├── fig4.7.tiff ├── fig5.1.tiff ├── fig5.2.tiff ├── gvwy_profit.tiff ├── gvwy_trans.tiff ├── shvr_profit.tiff ├── shvr_trans.tiff ├── sinetest.tiff ├── sinusoid_trans.tiff ├── snip4.1.tiff ├── snip4.2.tiff ├── snip5.1.tiff ├── snip5.2.tiff ├── snipBSEcode01.tiff ├── snip_ZIC.tiff ├── snip_bigtest.tiff ├── snip_gvwy.tiff ├── snip_lob01.tiff ├── snip_lob02.tiff ├── snip_lob03.tiff ├── snip_lob04.tiff ├── snip_shvr.tiff ├── snip_snpr.tiff ├── snip_zic_targetupdown.tiff ├── snip_zip_getorder.tiff ├── snip_zip_lobprocessor.tiff ├── snip_zip_profit_alter.tiff ├── snip_zip_willingtotrade.tiff ├── snip_zip_workbid.tiff ├── snpr_profit.tiff ├── snpr_trans.tiff ├── zic_profit.tiff ├── zic_trans.tiff ├── zip_profit.tiff └── zip_trans.tiff ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .DS_Store .ipynb_checkpoints __pycache__ *.csv ================================================ FILE: .idea/.name ================================================ BSE.py ================================================ FILE: .idea/BristolStockExchange.iml ================================================ ================================================ FILE: .idea/inspectionProfiles/profiles_settings.xml ================================================ ================================================ FILE: .idea/misc.xml ================================================ ================================================ FILE: .idea/modules.xml ================================================ ================================================ FILE: .idea/vcs.xml ================================================ ================================================ FILE: .idea/workspace.xml ================================================ <<<<<<< HEAD { "keyToString": { "RunOnceActivity.OpenProjectViewOnStart": "true", "RunOnceActivity.ShowReadmeOnStart": "true", "WebServerToolWindowFactoryState": "false", "node.js.detected.package.eslint": "true", "node.js.detected.package.tslint": "true", "node.js.selected.package.eslint": "(autodetect)", "node.js.selected.package.tslint": "(autodetect)", "settings.editor.selected.configurable": "org.jetbrains.plugins.notebooks.jupyter.connections.configuration.JupyterServerConfigurable" ======= >>>>>> fae9de5d8fd858b6ca07f57614c782d21246beaa } } 1665994552377 ================================================ FILE: BSE.py ================================================ # -*- coding: utf-8 -*- # # BSE: The Bristol Stock Exchange # # Version 1.91: November 2024 fixed PT1 + PT2 parameter passing/unpacking # Version 1.9: March 2024 added PT1+PT2, plus all the docstrings. # Version 1.8; March 2023 added ZIPSH # Version 1.7; September 2022 added PRDE # Version 1.6; September 2021 added PRSH # Version 1.5; 02 Jan 2021 -- was meant to be the final version before switch to BSE2.x, but that didn't happen :-) # Version 1.4; 26 Oct 2020 -- change to Python 3.x # Version 1.3; July 21st, 2018 (Python 2.x) # Version 1.2; November 17th, 2012 (Python 2.x) # # Copyright (c) 2012-2024, Dave Cliff # # # ------------------------ # # MIT Open-Source License: # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and # associated documentation files (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all copies or substantial # portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT # LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # ------------------------ # # # # BSE is a very simple simulation of automated execution traders # operating on a very simple model of a limit order book (LOB) exchange's matching engine. # # major simplifications in this version: # (a) only one financial instrument being traded # (b) traders can only trade contracts of size 1 # (c) each trader can have max of one order per single orderbook. # (d) traders can replace/overwrite earlier orders, and/or can cancel, with no fee/penalty imposed for doing so # (d) simply processes each order in sequence and republishes LOB to all traders # => no issues with exchange processing latency/delays or simultaneously issued orders. # # NB this code has been written to be readable/intelligible, not efficient! import sys import math import random import os import time as chrono import csv from datetime import datetime # a bunch of system constants (globals) bse_sys_minprice = 1 # minimum price in the system, in cents/pennies bse_sys_maxprice = 500 # maximum price in the system, in cents/pennies # ticksize should be a param of an exchange (so different exchanges can have different ticksizes) ticksize = 1 # minimum change in price, in cents/pennies # an Order/quote has a trader id, a type (buy/sell) price, quantity, timestamp, and unique i.d. class Order: """ An Order: this is used both for client-orders from exogenous customers to the robot traders acting as sales traders, and for the trader-orders (aka quotes) sent by the robot traders to the BSE exchange. In both use-cases, an order has a trader-i.d., a type (buy/sell), price, quantity, timestamp, and unique quote-i.d. """ def __init__(self, tid, otype, price, qty, time, qid): self.tid = tid # trader i.d. self.otype = otype # order type self.price = price # price self.qty = qty # quantity self.time = time # timestamp self.qid = qid # quote i.d. (unique to each quote) def __str__(self): return '[%s %s P=%03d Q=%s T=%5.2f QID:%d]' % \ (self.tid, self.otype, self.price, self.qty, self.time, self.qid) class OrderbookHalf: """ OrderbookHalf is one side of the book: a list of bids or a list of asks, each sorted best-price-first, and with orders at the same price arranged by arrival time (oldest first) for time-priority processing. """ def __init__(self, booktype, worstprice): """ Create one side of the LOB :param booktype: specifies bid or ask side of the LOB. :param worstprice: the initial value of the worst price currently showing on the LOB. """ # booktype: bids or asks? self.booktype = booktype # dictionary of orders received, indexed by Trader ID self.orders = {} # limit order book, dictionary indexed by price, with order info self.lob = {} # anonymized LOB, lists, with only price/qty info self.lob_anon = [] # summary stats self.best_price = None self.best_tid = None self.worstprice = worstprice self.session_extreme = None # most extreme price quoted in this session self.n_orders = 0 # how many orders? self.lob_depth = 0 # how many different prices on lob? def anonymize_lob(self): """ anonymize a lob, strip out order details, format as a sorted list NB for asks, the sorting should be reversed :return: """ self.lob_anon = [] for price in sorted(self.lob): qty = self.lob[price][0] self.lob_anon.append([price, qty]) def build_lob(self): """ Take a list of orders and build a limit-order-book (lob) from it NB the exchange needs to know arrival times and trader-id associated with each order also builds anonymized version (just price/quantity, sorted, as a list) for publishing to traders :return: lob as a dictionary (i.e., unsorted) """ lob_verbose = False self.lob = {} for tid in self.orders: order = self.orders.get(tid) price = order.price if price in self.lob: # update existing entry qty = self.lob[price][0] orderlist = self.lob[price][1] orderlist.append([order.time, order.qty, order.tid, order.qid]) self.lob[price] = [qty + order.qty, orderlist] else: # create a new dictionary entry self.lob[price] = [order.qty, [[order.time, order.qty, order.tid, order.qid]]] # create anonymized version self.anonymize_lob() # record best price and associated trader-id if len(self.lob) > 0: if self.booktype == 'Bid': self.best_price = self.lob_anon[-1][0] else: self.best_price = self.lob_anon[0][0] self.best_tid = self.lob[self.best_price][1][0][2] else: self.best_price = None self.best_tid = None if lob_verbose: print(self.lob) def book_add(self, order): """ Add order to the dictionary holding the list of orders for one side of the LOB. Either overwrites old order from this trader or dynamically creates new entry in the dictionary so there is a max of one order per trader per list checks whether length or order list has changed, to distinguish addition/overwrite :param order: the order to be added to the book :return: character-string indicating whether order-book was added to or overwritten. """ # if this is an ask, does the price set a new extreme-high record? if (self.booktype == 'Ask') and ((self.session_extreme is None) or (order.price > self.session_extreme)): self.session_extreme = int(order.price) # add the order to the book n_orders = self.n_orders self.orders[order.tid] = order self.n_orders = len(self.orders) self.build_lob() # print('book_add < %s %s' % (order, self.orders)) if n_orders != self.n_orders: return 'Addition' else: return 'Overwrite' def book_del(self, order): """ Delete order from the dictionary holding the orders for one half of the book. Assumes max of one order per trader per list. Checks that the Trader ID does actually exist in the dict before deletion. :param order: the order to be deleted. :return: """ if self.orders.get(order.tid) is not None: del (self.orders[order.tid]) self.n_orders = len(self.orders) self.build_lob() # print('book_del %s', self.orders) def delete_best(self): """ When the best bid/ask has been hit/lifted, delete it from the book. :return: TraderID of the deleted order is return-value, as counterparty to the trade. """ best_price_orders = self.lob[self.best_price] best_price_qty = best_price_orders[0] best_price_counterparty = best_price_orders[1][0][2] if best_price_qty == 1: # here the order deletes the best price del (self.lob[self.best_price]) del (self.orders[best_price_counterparty]) self.n_orders = self.n_orders - 1 if self.n_orders > 0: if self.booktype == 'Bid': self.best_price = max(self.lob.keys()) else: self.best_price = min(self.lob.keys()) self.lob_depth = len(self.lob.keys()) else: self.best_price = self.worstprice self.lob_depth = 0 else: # best_bid_qty>1 so the order decrements the quantity of the best bid # update the lob with the decremented order data self.lob[self.best_price] = [best_price_qty - 1, best_price_orders[1][1:]] # update the bid list: counterparty's bid has been deleted del (self.orders[best_price_counterparty]) self.n_orders = self.n_orders - 1 self.build_lob() return best_price_counterparty class Orderbook(OrderbookHalf): """ Orderbook for a single tradeable asset: list of bids and list of asks """ def __init__(self): """Construct a new orderbook""" self.bids = OrderbookHalf('Bid', bse_sys_minprice) self.asks = OrderbookHalf('Ask', bse_sys_maxprice) self.tape = [] self.tape_length = 10000 # max events on in-memory tape (older events can be written to tape_dump file) self.quote_id = 0 # unique ID code for each quote accepted onto the book self.lob_string = '' # character-string linearization of public lob items with nonzero quantities class Exchange(Orderbook): """ Exchange's matching engine and limit order book""" def add_order(self, order, vrbs): """ add an order to the exchange -- either match with a counterparty order on LOB, or add to LOB. :param order: the order to be added to the LOB :param vrbs: verbosity: if True, print a running commentary; if False, stay silent. :return: [order.qid, response] -- order.qid is the order's unique quote i.d., response is 'Overwrite'|'Addition' """ # add a quote/order to the exchange and update all internal records; return unique i.d. order.qid = self.quote_id self.quote_id = order.qid + 1 if vrbs: print('add_order QID=%d self.quote.id=%d' % (order.qid, self.quote_id)) if order.otype == 'Bid': response = self.bids.book_add(order) best_price = self.bids.lob_anon[-1][0] self.bids.best_price = best_price self.bids.best_tid = self.bids.lob[best_price][1][0][2] else: response = self.asks.book_add(order) best_price = self.asks.lob_anon[0][0] self.asks.best_price = best_price self.asks.best_tid = self.asks.lob[best_price][1][0][2] return [order.qid, response] def del_order(self, time, order, tape_file, vrbs): """ Delete an order from the exchange. :param time: the current time. :param order: the order to be deleted from the LOB. :param tape_file: if not None, write details of the cancellation to the tape file. :param vrbs: verbosity: if True, print a running commentary; if False, stay silent. :return: """ # delete a trader's quote/order from the exchange, update all internal records if vrbs: print('del_order QID=%d' % order.qid) if order.otype == 'Bid': self.bids.book_del(order) if self.bids.n_orders > 0: best_price = self.bids.lob_anon[-1][0] self.bids.best_price = best_price self.bids.best_tid = self.bids.lob[best_price][1][0][2] else: # this side of book is empty self.bids.best_price = None self.bids.best_tid = None cancel_record = {'type': 'Cancel', 'time': time, 'order': order} if tape_file is not None: tape_file.write('CAN, %f, %d, Bid, %d\n' % (time, order.qid, order.price)) self.tape.append(cancel_record) # right-truncate the tape so that it keeps only the most recent items self.tape = self.tape[-self.tape_length:] elif order.otype == 'Ask': self.asks.book_del(order) if self.asks.n_orders > 0: best_price = self.asks.lob_anon[0][0] self.asks.best_price = best_price self.asks.best_tid = self.asks.lob[best_price][1][0][2] else: # this side of book is empty self.asks.best_price = None self.asks.best_tid = None cancel_record = {'type': 'Cancel', 'time': time, 'order': order} if tape_file is not None: tape_file.write('CAN, %f, %d, Ask, %d\n' % (time, order.qid, order.price)) self.tape.append(cancel_record) # right-truncate the tape so that it keeps only the most recent items self.tape = self.tape[-self.tape_length:] else: # neither bid nor ask? sys.exit('bad order type in del_quote()') def process_order(self, time, order, tape_file, vrbs): """ Process an order from a trader -- this is the BSE Matching Engine. :param time: the current time. :param order: the order to be processed. :param tape_file: if is not None then write details of transaction to tape_file :param vrbs: verbosity: if True, print a running commentary; if False, stay silent. :return: transaction_record if the order results in a transaction, otherwise None. """ # receive an order and either add it to the relevant LOB (ie treat as limit order) # or if it crosses the best counterparty offer, execute it (treat as a market order) oprice = order.price counterparty = None price = None [qid, response] = self.add_order(order, vrbs) # add it to the order lists -- overwriting any previous order order.qid = qid if vrbs: print('QUID: order.quid=%d' % order.qid) print('RESPONSE: %s' % response) best_ask = self.asks.best_price best_ask_tid = self.asks.best_tid best_bid = self.bids.best_price best_bid_tid = self.bids.best_tid if order.otype == 'Bid': if self.asks.n_orders > 0 and best_bid >= best_ask: # bid lifts the best ask if vrbs: print("Bid $%s lifts best ask" % oprice) counterparty = best_ask_tid price = best_ask # bid crossed ask, so use ask price if vrbs: print('counterparty, price', counterparty, price) # delete the ask just crossed self.asks.delete_best() # delete the bid that was the latest order self.bids.delete_best() elif order.otype == 'Ask': if self.bids.n_orders > 0 and best_ask <= best_bid: # ask hits the best bid if vrbs: print("Ask $%s hits best bid" % oprice) # remove the best bid counterparty = best_bid_tid price = best_bid # ask crossed bid, so use bid price if vrbs: print('counterparty, price', counterparty, price) # delete the bid just crossed, from the exchange's records self.bids.delete_best() # delete the ask that was the latest order, from the exchange's records self.asks.delete_best() else: # we should never get here sys.exit('process_order() given neither Bid nor Ask') # NB at this point we have deleted the order from the exchange's records # but the two traders concerned still have to be notified if vrbs: print('counterparty %s' % counterparty) if counterparty is not None: # process the trade if vrbs: print('>>>>>>>>>>>>>>>>>TRADE t=%010.3f $%d %s %s' % (time, price, counterparty, order.tid)) transaction_record = {'type': 'Trade', 'time': time, 'price': price, 'party1': counterparty, 'party2': order.tid, 'qty': order.qty } if tape_file is not None: tape_file.write('TRD, %f, %d\n' % (time, price)) self.tape.append(transaction_record) # right-truncate the tape so that it keeps only the most recent items self.tape = self.tape[-self.tape_length:] return transaction_record else: return None def tape_dump(self, fname, fmode, tmode): """ Currently tape_dump only writes a list of transactions (i.e., it ignores any cancellations) :param fname: filename to write to. :param fmode: file-open write/append mode. :param tmode: if set to 'wipe', wipes the tape clean after writing it to file. :return: """ dumpfile = open(fname, fmode) dumpfile.write('Event Type, Time, Price\n') for tapeitem in self.tape: if tapeitem['type'] == 'Trade': dumpfile.write('Trd, %010.3f, %s\n' % (tapeitem['time'], tapeitem['price'])) dumpfile.close() if tmode == 'wipe': self.tape = [] def publish_lob(self, time, lob_file, vrbs): """ Returns the public LOB data published by the exchange, i.e. the version of the LOB that's accessible to the traders. :param time: the current time. :param lob_file: :param vrbs: verbosity: if True, print a running commentary; if False, stay silent. :return: the public LOB data. """ public_data = dict() public_data['time'] = time public_data['bids'] = {'best': self.bids.best_price, 'worst': self.bids.worstprice, 'n': self.bids.n_orders, 'lob': self.bids.lob_anon} public_data['asks'] = {'best': self.asks.best_price, 'worst': self.asks.worstprice, 'sess_hi': self.asks.session_extreme, 'n': self.asks.n_orders, 'lob': self.asks.lob_anon} public_data['QID'] = self.quote_id public_data['tape'] = self.tape if lob_file is not None: # build a linear character-string summary of only those prices on LOB with nonzero quantities lobstring = 'Bid:,' n_bids = len(self.bids.lob_anon) if n_bids > 0: lobstring += '%d,' % n_bids for lobitem in self.bids.lob_anon: price_str = '%d,' % lobitem[0] qty_str = '%d,' % lobitem[1] lobstring = lobstring + price_str + qty_str else: lobstring += '0,' lobstring += 'Ask:,' n_asks = len(self.asks.lob_anon) if n_asks > 0: lobstring += '%d,' % n_asks for lobitem in self.asks.lob_anon: price_str = '%d,' % lobitem[0] qty_str = '%d,' % lobitem[1] lobstring = lobstring + price_str + qty_str else: lobstring += '0,' # is this different to the last lob_string? if lobstring != self.lob_string: # write it lob_file.write('%.3f, %s\n' % (time, lobstring)) # remember it self.lob_string = lobstring if vrbs: vstr = 'publish_lob: t=%f' % time vstr += ' BID_lob=%s' % public_data['bids']['lob'] # vstr += 'best=%s; worst=%s; n=%s ' % (self.bids.best_price, self.bids.worstprice, self.bids.n_orders) vstr += ' ASK_lob=%s' % public_data['asks']['lob'] # vstr += 'qid=%d' % self.quote_id print(vstr) return public_data # #################--Traders below here--############# # Trader superclass # all Traders have a trader id, bank balance, blotter, and list of orders to execute class Trader: """The parent class for all types of robot trader in BSE""" def __init__(self, ttype, tid, balance, params, time): """ Initializes a generic trader with attributes common to all/most types of trader Some trader types (e.g. ZIP) then have additional specialised initialization steps :param ttype: the trader type :param tid: the trader I.D. (a non-negative integer) :param balance: how much money it has in the bank when it is created :param params: a set of parameter-values, for those trader-types that have parameters :param time: the time this trader was created """ self.ttype = ttype # what type / strategy this trader is self.tid = tid # trader unique ID code self.balance = balance # money in the bank self.params = params # parameters/extras associated with this trader-type or individual trader. self.blotter = [] # record of trades executed self.blotter_length = 100 # maximum length of blotter self.orders = [] # customer orders currently being worked (fixed at len=1 in BSE1.x) self.n_quotes = 0 # number of quotes live on LOB self.birthtime = time # used when calculating age of a trader/strategy self.profitpertime = 0 # profit per unit time self.profit_mintime = 60 # minimum duration in seconds for calculating profitpertime self.n_trades = 0 # how many trades has this trader done? self.lastquote = None # record of what its last quote was def __str__(self): """ return a character-string that summarises a trader """ return '[TID %s type %s balance %s blotter %s orders %s n_trades %s profitpertime %s]' \ % (self.tid, self.ttype, self.balance, self.blotter, self.orders, self.n_trades, self.profitpertime) def add_order(self, order, vrbs): """ What a trader calls when it receives a new customer order/assignment :param order: the customer order/assignment to be added :param vrbs: verbosity: if True, print a running commentary; if False, stay silent. :return response: string to indicate whether the trader needs to cancel its current order on the LOB """ # in this version, trader has at most one order, # if allow more than one, this needs to be self.orders.append(order) if self.n_quotes > 0: # this trader has a live quote on the LOB, from a previous customer order # need response to signal cancellation/withdrawal of that quote response = 'LOB_Cancel' else: response = 'Proceed' self.orders = [order] if vrbs: print('add_order < response=%s' % response) return response def del_order(self, order): """What a trader calls when it wants to delete an existing customer order/assignment """ if order is None: pass # this line is purely to stop PyCharm from warning about order being an unused parameter # this is lazy: assumes each trader has only one customer order with quantity=1, so deleting sole order self.orders = [] def profitpertime_update(self, time, birthtime, totalprofit): """ Calculates the trader's profit per unit time, but only if it has been alive longer than profit_mintime This is to avoid situations where a trader is created and then immediately makes a profit and hence the profit per unit time is a sky-high value, because the time_alive divisor is close to zero. :param time: the current time. :param birthtime: the time when the trader was created. :param totalprofit: the trader's current total accumulated profit. :return: profit per second. """ time_alive = (time - birthtime) if time_alive >= self.profit_mintime: profitpertime = totalprofit / time_alive else: # if it's not been alive long enough, divide it by mintime instead of actual time profitpertime = totalprofit / self.profit_mintime return profitpertime def bookkeep(self, time, trade, order, vrbs): """ Update trader's individual records of transactions, profit/loss etc. :param trade: details of the transaction that took place. :param order: details of the customer order that led to the transaction. :param vrbs: verbosity: if True, print a running commentary; if False, stay silent. :param time: the current time. :return: """ outstr = "" for order in self.orders: outstr = outstr + str(order) self.blotter.append(trade) # add trade record to trader's blotter self.blotter = self.blotter[-self.blotter_length:] # right-truncate to keep to length # NB What follows is **LAZY** -- assumes all orders are quantity=1 transactionprice = trade['price'] if self.orders[0].otype == 'Bid': profit = self.orders[0].price - transactionprice else: profit = transactionprice - self.orders[0].price self.balance += profit self.n_trades += 1 self.profitpertime = self.balance / (time - self.birthtime) if profit < 0: print(profit) print(trade) print(order) sys.exit('FAIL: negative profit') if vrbs: print('%s profit=%d balance=%d profit/time=%s' % (outstr, profit, self.balance, str(self.profitpertime))) self.del_order(order) # delete the order # if the trader has multiple strategies (e.g. PRSH/PRDE/ZIPSH/ZIPDE) then there is more work to do... if hasattr(self, 'strats') and hasattr(self, 'active_strat'): if self.strats is not None: self.strats[self.active_strat]['profit'] += profit totalprofit = self.strats[self.active_strat]['profit'] birthtime = self.strats[self.active_strat]['start_t'] self.strats[self.active_strat]['pps'] = self.profitpertime_update(time, birthtime, totalprofit) def respond(self, time, lob, trade, vrbs): """ Specify how a trader responds to events in the market. For Trader superclass, this is minimal action, but expect it to be overloaded by specific trading strategies. :param time: :param lob: :param trade: :param vrbs: verbosity: if True, print a running commentary; if False, stay silent. :return: """ # any trader subclass with custom respond() must include this update of profitpertime self.profitpertime = self.profitpertime_update(time, self.birthtime, self.balance) return None class TraderGiveaway(Trader): """ Trader subclass Giveaway (GVWY): even dumber than a ZI-U: just give the deal away (but never make a loss) """ def getorder(self, time, countdown, lob): """ Create this trader's order to be sent to the exchange. :param time: the current time. :param countdown: how much time before market closes (not used by GVWY). :param lob: the current state of the LOB. :return: a new order from this trader. """ # this test for negative countdown is purely to stop PyCharm warning about unused parameter value if countdown < 0: sys.exit('Negative countdown') if len(self.orders) < 1: order = None else: quoteprice = self.orders[0].price order = Order(self.tid, self.orders[0].otype, quoteprice, self.orders[0].qty, time, lob['QID']) self.lastquote = order return order class TraderZIC(Trader): """ Trader subclass ZI-C: after Gode & Sunder 1993 """ def getorder(self, time, countdown, lob): """ Create this trader's order to be sent to the exchange. :param time: the current time. :param countdown: how much time before market closes (not used by ZIC). :param lob: the current state of the LOB. :return: a new order from this trader. """ # this test for negative countdown is purely to stop PyCharm warning about unused parameter value if countdown < 0: sys.exit('Negative countdown') if len(self.orders) < 1: # no orders: return NULL order = None else: minprice = lob['bids']['worst'] maxprice = lob['asks']['worst'] qid = lob['QID'] limit = self.orders[0].price otype = self.orders[0].otype if otype == 'Bid': quoteprice = random.randint(int(minprice), int(limit)) else: quoteprice = random.randint(int(limit), int(maxprice)) # NB should check it == 'Ask' and barf if not order = Order(self.tid, otype, quoteprice, self.orders[0].qty, time, qid) self.lastquote = order return order class TraderShaver(Trader): """ Trader subclass Shaver: shaves a penny off the best price; but if there is no best price, creates "stub quote" at system max/min """ def getorder(self, time, countdown, lob): """ Create this trader's order to be sent to the exchange. :param time: the current time. :param countdown: how much time before market close (not used by SHVR). :param lob: the current state of the LOB. :return: a new order from this trader. """ # this test for negative countdown is purely to stop PyCharm warning about unused parameter value if countdown < 0: sys.exit('Negative countdown') if len(self.orders) < 1: order = None else: limitprice = self.orders[0].price otype = self.orders[0].otype if otype == 'Bid': if lob['bids']['n'] > 0: quoteprice = lob['bids']['best'] + 1 if quoteprice > limitprice: quoteprice = limitprice else: quoteprice = lob['bids']['worst'] else: if lob['asks']['n'] > 0: quoteprice = lob['asks']['best'] - 1 if quoteprice < limitprice: quoteprice = limitprice else: quoteprice = lob['asks']['worst'] order = Order(self.tid, otype, quoteprice, self.orders[0].qty, time, lob['QID']) self.lastquote = order return order class TraderSniper(Trader): """ Trader subclass Sniper (SNPR), inspired by Kaplan's Sniper, BSE version is based on Shaver, "lurks" until time remaining < threshold% of the trading session then gets increasing aggressive, increasing "shave thickness" as time runs out """ def getorder(self, time, countdown, lob): """ Create this trader's order to be sent to the exchange. :param time: the current time. :param countdown: how much time before market closes. :param lob: the current state of the LOB. :return: a new order from this trader. """ lurk_threshold = 0.2 shavegrowthrate = 3 shave = int(1.0 / (0.01 + countdown / (shavegrowthrate * lurk_threshold))) if (len(self.orders) < 1) or (countdown > lurk_threshold): order = None else: limitprice = self.orders[0].price otype = self.orders[0].otype if otype == 'Bid': if lob['bids']['n'] > 0: quoteprice = lob['bids']['best'] + shave if quoteprice > limitprice: quoteprice = limitprice else: quoteprice = lob['bids']['worst'] else: if lob['asks']['n'] > 0: quoteprice = lob['asks']['best'] - shave if quoteprice < limitprice: quoteprice = limitprice else: quoteprice = lob['asks']['worst'] order = Order(self.tid, otype, quoteprice, self.orders[0].qty, time, lob['QID']) self.lastquote = order return order class TraderPRZI(Trader): """ Cliff's Parameterized-Response Zero-Intelligence (PRZI) trader -- pronounced "prezzie" but with added adaptive strategies, currently either... ++ a k-point Stochastic Hill-Climber (SHC) hence PRZI-SHC, PRZI-SHC pronounced "prezzy-shuck". Ticker symbol PRSH pronounced "purrsh"; or ++ a simple differential evolution (DE) optimizer with pop_size=k, hence PRZE-DE or PRDE ('purdy") when optimizer == None then it implements plain-vanilla non-adaptive PRZI, with a fixed strategy-value. """ @staticmethod def strat_csv_str(strat): """ Return trader's strategy as a csv-format string (trivial in PRZI, but other traders with more complex strategies need this). :param strat: the strategy specification (for PRZI, a real number in [-1.0,+1.0] :return: the strategy as a scv-format string """ csv_str = 's=,%+5.3f, ' % strat return csv_str def mutate_strat(self, s, mode): """ How to mutate the PRZI strategy values when evolving / hill-climbing :param s: the strategy to be mutated :param mode: 'gauss'=> mutation is a draw from a zero-mean Gaussian; 'uniform_whole_range" => mutation is a draw from uniform distbn over [-1.0,+1.0]. 'uniform_bounded_range" => mutation is a draw from a bounded unifrom distbn. :return: the mutated strategy value """ s_min = self.strat_range_min s_max = self.strat_range_max if mode == 'gauss': sdev = 0.05 newstrat = s while newstrat == s: newstrat = s + random.gauss(0.0, sdev) # truncate to keep within range newstrat = max(-1.0, min(1.0, newstrat)) elif mode == 'uniform_whole_range': # draw uniformly from whole range newstrat = random.uniform(-1.0, +1.0) elif mode == 'uniform_bounded_range': # draw uniformly from bounded range newstrat = random.uniform(s_min, s_max) else: sys.exit('FAIL: bad mode in mutate_strat') return newstrat def strat_str(self): """ Pretty-print a string summarising this trader's strategy/strategies :return: the string """ string = '%s: %s active_strat=[%d]:\n' % (self.tid, self.ttype, self.active_strat) for s in range(0, self.k): strat = self.strats[s] stratstr = '[%d]: s=%+f, start=%f, $=%f, pps=%f\n' % \ (s, strat['stratval'], strat['start_t'], strat['profit'], strat['pps']) string = string + stratstr return string def __init__(self, ttype, tid, balance, params, time): """ Construct a PRZI trader :param ttype: the ticker-symbol for the type of trader (its strategy) :param tid: the trader id :param balance: the trader's bank balance :param params: if params == "landscape-mapper" then it generates data for mapping the fitness landscape :param time: the current time. """ vrbs = True Trader.__init__(self, ttype, tid, balance, params, time) # unpack the params # for all three of PRZI, PRSH, and PRDE params can include strat_min and strat_max # for PRSH and PRDE params should include values for optimizer and k # if no params specified then defaults to PRZI with strat values in [-1.0,+1.0] # default parameter values k = 1 optimizer = None # no optimizer => plain non-adaptive PRZI s_min = -1.0 s_max = +1.0 # did call provide different params? if type(params) is dict: if 'k' in params: k = params['k'] if 'optimizer' in params: optimizer = params['optimizer'] s_min = params['strat_min'] s_max = params['strat_max'] self.optmzr = optimizer # this determines whether it's PRZI, PRSH, or PRDE self.k = k # number of sampling points (cf number of arms on a multi-armed-bandit, or pop-size) self.theta0 = 100 # threshold-function limit value self.m = 4 # tangent-function multiplier self.strat_wait_time = 7200 # how many secs do we give any one strat before switching? self.strat_range_min = s_min # lower-bound on randomly-assigned strategy-value self.strat_range_max = s_max # upper-bound on randomly-assigned strategy-value self.active_strat = 0 # which of the k strategies are we currently playing? -- start with 0 self.prev_qid = None # previous order i.d. self.strat_eval_time = self.k * self.strat_wait_time # time to cycle through evaluating all k strategies self.last_strat_change_time = time # what time did we last change strategies? self.profit_epsilon = 0.0 * random.random() # min profit-per-sec difference between strategies that counts self.strats = [] # strategies awaiting initialization self.pmax = None # this trader's estimate of the maximum price the market will bear self.pmax_c_i = math.sqrt(random.randint(1, 10)) # multiplier coefficient when estimating p_max self.mapper_outfile = None # differential evolution parameters all in one dictionary self.diffevol = {'de_state': 'active_s0', # initial state: strategy 0 is active (being evaluated) 's0_index': self.active_strat, # s0 starts out as active strat 'snew_index': self.k, # (k+1)th item of strategy list is DE's new strategy 'snew_stratval': None, # assigned later 'F': 0.8 # differential weight -- usually between 0 and 2 } start_t = time profit = 0.0 profit_per_second = 0 lut_bid = None lut_ask = None for s in range(self.k + 1): # initialise each of the strategies in sequence: # for PRZI: only one strategy is needed # for PRSH, one random initial strategy, then k-1 mutants of that initial strategy # for PRDE, use draws from uniform distbn over whole range and a (k+1)th strategy is needed to hold s_new strategy = None if s == 0: strategy = random.uniform(self.strat_range_min, self.strat_range_max) else: if self.optmzr == 'PRSH': # simple stochastic hill climber: cluster other strats around strat_0 strategy = self.mutate_strat(self.strats[0]['stratval'], 'gauss') # mutant of strats[0] elif self.optmzr == 'PRDE': # differential evolution: seed initial strategies across whole space strategy = self.mutate_strat(self.strats[0]['stratval'], 'uniform_bounded_range') else: # plain PRZI -- do nothing pass # add to the list of strategies if s == self.active_strat: active_flag = True else: active_flag = False self.strats.append({'stratval': strategy, 'start_t': start_t, 'active': active_flag, 'profit': profit, 'pps': profit_per_second, 'lut_bid': lut_bid, 'lut_ask': lut_ask}) if self.optmzr is None: # PRZI -- so we stop after one iteration break elif self.optmzr == 'PRSH' and s == self.k - 1: # PRSH -- doesn't need the (k+1)th strategy break if self.params == 'landscape-mapper': # replace seed+mutants set of strats with regularly-spaced strategy values over the whole range self.strats = [] strategy_delta = 0.01 strategy = -1.0 k = 0 self.strats = [] while strategy <= +1.0: self.strats.append({'stratval': strategy, 'start_t': start_t, 'active': False, 'profit': profit, 'pps': profit_per_second, 'lut_bid': lut_bid, 'lut_ask': lut_ask}) k += 1 strategy += strategy_delta self.mapper_outfile = open('landscape_map.csv', 'w') self.k = k self.strat_eval_time = self.k * self.strat_wait_time if vrbs: print("%s\n" % self.strat_str()) def getorder(self, time, countdown, lob): """ Create this trader's order to be sent to the exchange. :param time: the current time. :param countdown: how much time before market close (not used by GVWY). :param lob: the current state of the LOB. :return: a new order from this trader. """ # this test for negative countdown is purely to stop PyCharm warning about unused parameter value if countdown < 0: sys.exit('Negative countdown') def shvr_price(order_type, lim, pub_lob): """ Return value is what price a SHVR would quote in these circumstances :param order_type: is the order bid or ask? :param lim: limit price on the order. :param pub_lob: the current state of the published LOB. :return: The price a SHVR would quote given this LOB and limit-price. """ if order_type == 'Bid': if pub_lob['bids']['n'] > 0: shvr_p = pub_lob['bids']['best'] + ticksize # BSE ticksize is global var if shvr_p > lim: shvr_p = lim else: shvr_p = pub_lob['bids']['worst'] else: if pub_lob['asks']['n'] > 0: shvr_p = pub_lob['asks']['best'] - ticksize # BSE ticksize is global var if shvr_p < lim: shvr_p = lim else: shvr_p = pub_lob['asks']['worst'] # print('shvr_p=%f; ' % shvr_p) return shvr_p def calc_cdf_lut(strategy, t0, m, dirn, pmin, pmax): """ calculate cumulative distribution function (CDF) look-up table (LUT) :param strategy: strategy-value in [-1,+1] :param t0: constant used in the threshold function :param m: constant used in the threshold function :param dirn: direction: 'buy' or 'sell' :param pmin: lower bound on discrete-valued price-range :param pmax: upper bound on discrete-valued price-range :return: {'strat': strategy, 'dirn': dirn, 'pmin': pmin, 'pmax': pmax, 'cdf_lut': cdf} """ # the threshold function used to clip def threshold(theta0, x): t = max(-1*theta0, min(theta0, x)) return t epsilon = 0.000001 # used to catch DIV0 errors lut_vrbs = False if (strategy > 1.0) or (strategy < -1.0): # out of range sys.exit('PRSH FAIL: strategy=%f out of range\n' % strategy) if (dirn != 'buy') and (dirn != 'sell'): # out of range sys.exit('PRSH FAIL: bad dirn=%s\n' % dirn) if pmax < pmin: # screwed sys.exit('PRSH FAIL: pmax %f < pmin %f \n' % (pmax, pmin)) if lut_vrbs: print('PRSH calc_cdf_lut: strategy=%f dirn=%d pmin=%d pmax=%d\n' % (strategy, dirn, pmin, pmax)) p_range = float(pmax - pmin) if p_range < 1: # special case: the SHVR-style strategy has shaved all the way to the limit price # the lower and upper bounds on the interval are adjacent prices; # so cdf is simply the limit-price with probability 1 if dirn == 'buy': cdf = [{'price': pmax, 'cum_prob': 1.0}] else: # must be a sell cdf = [{'price': pmin, 'cum_prob': 1.0}] if lut_vrbs: print('\n\ncdf:', cdf) return {'strat': strategy, 'dirn': dirn, 'pmin': pmin, 'pmax': pmax, 'cdf_lut': cdf} c = threshold(t0, m * math.tan(math.pi * (strategy + 0.5))) # catch div0 errors here if abs(c) < epsilon: if c > 0: c = epsilon else: c = -epsilon e2cm1 = math.exp(c) - 1 # calculate the discrete calligraphic-P function over interval [pmin, pmax] # (i.e., this is Equation 8 in the PRZI Technical Note) calp_interval = [] calp_sum = 0 for p in range(pmin, pmax + 1): # normalize the price to proportion of its range p_r = (p - pmin) / p_range # p_r in [0.0, 1.0] if strategy == 0.0: # special case: this is just ZIC cal_p = 1 / (p_range + 1) elif strategy > 0: if dirn == 'buy': cal_p = (math.exp(c * p_r) - 1.0) / e2cm1 else: # dirn == 'sell' cal_p = (math.exp(c * (1 - p_r)) - 1.0) / e2cm1 else: # self.strat < 0 if dirn == 'buy': cal_p = 1.0 - ((math.exp(c * p_r) - 1.0) / e2cm1) else: # dirn == 'sell' cal_p = 1.0 - ((math.exp(c * (1 - p_r)) - 1.0) / e2cm1) if cal_p < 0: cal_p = 0 # just in case calp_interval.append({'price': p, "cal_p": cal_p}) calp_sum += cal_p if calp_sum <= 0: print('calp_interval:', calp_interval) print('pmin=%f, pmax=%f, calp_sum=%f' % (pmin, pmax, calp_sum)) cdf = [] cum_prob = 0 # now go thru interval summing and normalizing to give the CDF for p in range(pmin, pmax + 1): cal_p = calp_interval[p-pmin]['cal_p'] prob = cal_p / calp_sum cum_prob += prob cdf.append({'price': p, 'cum_prob': cum_prob}) if lut_vrbs: print('\n\ncdf:', cdf) return {'strat': strategy, 'dirn': dirn, 'pmin': pmin, 'pmax': pmax, 'cdf_lut': cdf} vrbs = False if vrbs: print('t=%.1f PRSH getorder: %s, %s' % (time, self.tid, self.strat_str())) if len(self.orders) < 1: # no orders: return NULL order = None else: # unpack the assignment-order limit = self.orders[0].price otype = self.orders[0].otype qid = self.orders[0].qid if self.prev_qid is None: self.prev_qid = qid if qid != self.prev_qid: # customer-order i.d. has changed, so we're working a new customer-order now # this is the time to switch arms # print("New order! (how does it feel?)") pass # get extreme limits on price interval # lowest price the market will bear minprice = int(lob['bids']['worst']) # default assumption: worst bid price possible as defined by exchange # trader's individual estimate highest price the market will bear maxprice = self.pmax # default assumption if self.pmax is None: maxprice = int(limit * self.pmax_c_i + 0.5) # in the absence of any other info, guess self.pmax = maxprice elif lob['asks']['sess_hi'] is not None: if self.pmax < lob['asks']['sess_hi']: # some other trader has quoted higher than I expected maxprice = lob['asks']['sess_hi'] # so use that as my new estimate of highest self.pmax = maxprice # use the cdf look-up table # cdf_lut is a list of little dictionaries # each dictionary has form: {'cum_prob':nnn, 'price':nnn} # generate u=U(0,1) uniform disrtibution # starting with the lowest nonzero cdf value at cdf_lut[0], # walk up the lut (i.e., examine higher cumulative probabilities), # until we're in the range of u; then return the relevant price strat = self.strats[self.active_strat]['stratval'] # what price would a SHVR quote? p_shvr = shvr_price(otype, limit, lob) if otype == 'Bid': p_max = int(limit) if strat > 0.0: p_min = minprice else: # shade the lower bound on the interval # away from minprice and toward shvr_price p_min = int(0.5 + (-strat * p_shvr) + ((1.0 + strat) * minprice)) lut_bid = self.strats[self.active_strat]['lut_bid'] if (lut_bid is None) or \ (lut_bid['strat'] != strat) or (lut_bid['pmin'] != p_min) or (lut_bid['pmax'] != p_max): # need to compute a new LUT if vrbs: print('New bid LUT') self.strats[self.active_strat]['lut_bid'] = \ calc_cdf_lut(strat, self.theta0, self.m, 'buy', p_min, p_max) lut = self.strats[self.active_strat]['lut_bid'] else: # otype == 'Ask' p_min = int(limit) if strat > 0.0: p_max = maxprice else: # shade the upper bound on the interval # away from maxprice and toward shvr_price p_max = int(0.5 + (-strat * p_shvr) + ((1.0 + strat) * maxprice)) if p_max < p_min: # this should never happen, but just in case it does... p_max = p_min lut_ask = self.strats[self.active_strat]['lut_ask'] if (lut_ask is None) or \ (lut_ask['strat'] != strat) or \ (lut_ask['pmin'] != p_min) or \ (lut_ask['pmax'] != p_max): # need to compute a new LUT if vrbs: print('New ask LUT') self.strats[self.active_strat]['lut_ask'] = \ calc_cdf_lut(strat, self.theta0, self.m, 'sell', p_min, p_max) lut = self.strats[self.active_strat]['lut_ask'] vrbs = False if vrbs: print('PRZI strat=%f LUT=%s \n \n' % (strat, lut)) # for debugging: print a table of lut: price and cum_prob, with the discrete derivative (gives PMF). last_cprob = 0.0 for lut_entry in lut['cdf_lut']: cprob = lut_entry['cum_prob'] print('%d, %f, %f' % (lut_entry['price'], cprob - last_cprob, cprob)) last_cprob = cprob print('\n') # print ('[LUT print suppressed]') # do inverse lookup on the LUT to find the price quoteprice = None u = random.random() for entry in lut['cdf_lut']: if u < entry['cum_prob']: quoteprice = entry['price'] break order = Order(self.tid, otype, quoteprice, self.orders[0].qty, time, lob['QID']) self.lastquote = order return order def bookkeep(self, time, trade, order, vrbs): """ Update trader's individual records of transactions, profit/loss etc. :param trade: details of the transaction that took place :param order: details of the customer order that led to the transaction :param vrbs: if True then print a running commentary of what's going on :param time: the current time :return: (nothing) """ outstr = "" for order in self.orders: outstr = outstr + str(order) self.blotter.append(trade) # add trade record to trader's blotter self.blotter = self.blotter[-self.blotter_length:] # right-truncate to keep to length # NB What follows is **LAZY** -- assumes all orders are quantity=1 transactionprice = trade['price'] if self.orders[0].otype == 'Bid': profit = self.orders[0].price - transactionprice else: profit = transactionprice - self.orders[0].price self.balance += profit self.n_trades += 1 self.profitpertime = self.balance / (time - self.birthtime) if profit < 0: print(profit) print(trade) print(order) sys.exit('PRSH FAIL: negative profit') if vrbs: print('%s profit=%d balance=%d profit/time=%d' % (outstr, profit, self.balance, self.profitpertime)) self.del_order(order) # delete the order self.strats[self.active_strat]['profit'] += profit time_alive = time - self.strats[self.active_strat]['start_t'] if time_alive > 0: profit_per_second = self.strats[self.active_strat]['profit'] / time_alive self.strats[self.active_strat]['pps'] = profit_per_second else: # if it trades at the instant it is born then it would have infinite profit-per-second, which is insane # to keep things sensible when time_alive == 0 we say the profit per second is whatever the actual profit is self.strats[self.active_strat]['pps'] = profit def respond(self, time, lob, trade, vrbs): """ Respond to the current state of the LOB. For strategy-optimizers PRSH and PRDE, this can involve switching stratregy, and/or generating new strategies. :param time: the current time. :param lob: the current state of the LOB. :param trade: details of most recent trade, if any. :param vrbs: if True then print messages explaining what is going on. :return: """ # "PRSH" is a very basic form of stochastic hill-climber (SHC) that's v easy to understand and to code # it cycles through the k different strats until each has been operated for at least eval_time seconds # but a strat that does nothing will get swapped out if it's been running for no_deal_time without a deal # then the strats with the higher total accumulated profit is retained, # and mutated versions of it are copied into the other k-1 strats # then all counters are reset, and this is repeated indefinitely # # "PRDE" uses a basic form of Differential Evolution. This maintains a population of at least four strats # iterates indefinitely on: # shuffle the set of strats; # name the first four strats s0 to s3; # create new_strat=s1+f*(s2-s3); # evaluate fitness of s0 and new_strat; # if (new_strat fitter than s0) then new_strat replaces s0. # # todo: add in other optimizer algorithms that are cleverer than these # e.g. inspired by multi-arm-bandit algos like like epsilon-greedy, softmax, or upper confidence bound (UCB) def strat_activate(t, s_index): """ Activate a specified strategy :param t: the current time :param s_index: the index of the strategy in the list of strategies :return: """ # print('t=%f Strat_activate, index=%d, active=%s' % (t, s_index, self.strats[s_index]['active'] )) self.strats[s_index]['start_t'] = t self.strats[s_index]['active'] = True self.strats[s_index]['profit'] = 0.0 self.strats[s_index]['pps'] = 0.0 vrbs = False # first update each active strategy's profit-per-second (pps) value -- this is the "fitness" of each strategy for s in self.strats: # debugging check: make profit be directly proportional to strategy, no noise # s['profit'] = 100 * abs(s['stratval']) # update pps active_flag = s['active'] if active_flag: s['pps'] = self.profitpertime_update(time, s['start_t'], s['profit']) if self.optmzr == 'PRSH': if vrbs: # print('t=%f %s PRSH respond: shc_algo=%s eval_t=%f max_wait_t=%f' % # (time, self.tid, shc_algo, self.strat_eval_time, self.strat_wait_time)) pass # do we need to swap strategies? # this is based on time elapsed since last reset -- waiting for the current strategy to get a deal # -- otherwise a hopeless strategy can just sit there for ages doing nothing, # which would disadvantage the *other* strategies because they would never get a chance to score any profit. # NB this *cycles* through the available strats in sequence s = self.active_strat time_elapsed = time - self.last_strat_change_time if time_elapsed > self.strat_wait_time: # we have waited long enough: swap to another strategy self.strats[s]['active'] = False new_strat = s + 1 if new_strat > self.k - 1: new_strat = 0 self.active_strat = new_strat self.strats[new_strat]['active'] = True self.last_strat_change_time = time if vrbs: swt = self.strat_wait_time print('t=%.3f (%.2fdays), %s PRSHrespond: strat[%d] elpsd=%.3f; wait_t=%.3f, pps=%f, new strat=%d' % (time, time/86400, self.tid, s, time_elapsed, swt, self.strats[s]['pps'], new_strat)) # code below here deals with creating a new set of k-1 mutants from the best of the k strats # assume that all strats have had long enough, and search for evidence to the contrary all_old_enough = True for s in self.strats: lifetime = time - s['start_t'] if lifetime < self.strat_eval_time: all_old_enough = False break if all_old_enough: # all strategies have had long enough: which has made most profit? # sort them by profit strats_sorted = sorted(self.strats, key=lambda k: k['pps'], reverse=True) # strats_sorted = self.strats # use this as a control: unsorts the strats, gives pure random walk. if vrbs: print('PRSH %s: strat_eval_time=%f, all_old_enough=True' % (self.tid, self.strat_eval_time)) for s in strats_sorted: print('s=%f, start_t=%f, lifetime=%f, $=%f, pps=%f' % (s['stratval'], s['start_t'], time-s['start_t'], s['profit'], s['pps'])) if self.params == 'landscape-mapper': for s in self.strats: self.mapper_outfile.write('time, %f, strat, %f, pps, %f\n' % (time, s['stratval'], s['pps'])) self.mapper_outfile.flush() sys.exit() else: # if the difference between the top two strats is too close to call then flip a coin # this is to prevent the same good strat being held constant simply by chance cos it is at index [0] best_strat = 0 prof_diff = strats_sorted[0]['pps'] - strats_sorted[1]['pps'] if abs(prof_diff) < self.profit_epsilon: # they're too close to call, so just flip a coin best_strat = random.randint(0, 1) if best_strat == 1: # need to swap strats[0] and strats[1] tmp_strat = strats_sorted[0] strats_sorted[0] = strats_sorted[1] strats_sorted[1] = tmp_strat # the sorted list of strats replaces the existing list self.strats = strats_sorted # at this stage, strats_sorted[0] is our newly-chosen elite-strat, about to replicate # now replicate and mutate the elite into all the other strats for s in range(1, self.k): # note range index starts at one not zero (elite is at [0]) self.strats[s]['stratval'] = self.mutate_strat(self.strats[0]['stratval'], 'gauss') self.strats[s]['start_t'] = time self.strats[s]['profit'] = 0.0 self.strats[s]['pps'] = 0.0 # and then update (wipe) records for the elite self.strats[0]['start_t'] = time self.strats[0]['profit'] = 0.0 self.strats[0]['pps'] = 0.0 self.active_strat = 0 if vrbs: print('%s: strat_eval_time=%f, MUTATED:' % (self.tid, self.strat_eval_time)) for s in self.strats: print('s=%f start_t=%f, lifetime=%f, $=%f, pps=%f' % (s['stratval'], s['start_t'], time-s['start_t'], s['profit'], s['pps'])) elif self.optmzr == 'PRDE': # simple differential evolution # only initiate diff-evol once the active strat has been evaluated for long enough actv_lifetime = time - self.strats[self.active_strat]['start_t'] if actv_lifetime >= self.strat_wait_time: if self.k < 4: sys.exit('FAIL: k too small for diffevol') if self.diffevol['de_state'] == 'active_s0': self.strats[self.active_strat]['active'] = False # we've evaluated s0, so now we need to evaluate s_new self.active_strat = self.diffevol['snew_index'] strat_activate(time, self.active_strat) self.diffevol['de_state'] = 'active_snew' elif self.diffevol['de_state'] == 'active_snew': # now we've evaluated s_0 and s_new, so we can do DE adaptive step if vrbs: print('PRDE trader %s' % self.tid) i_0 = self.diffevol['s0_index'] i_new = self.diffevol['snew_index'] fit_0 = self.strats[i_0]['pps'] fit_new = self.strats[i_new]['pps'] if verbose: print('DiffEvol: t=%.1f, i_0=%d, i0fit=%f, i_new=%d, i_new_fit=%f' % (time, i_0, fit_0, i_new, fit_new)) if fit_new >= fit_0: # new strat did better than old strat0, so overwrite new into strat0 self.strats[i_0]['stratval'] = self.strats[i_new]['stratval'] # do differential evolution # pick four individual strategies at random, but they must be distinct stratlist = list(range(0, self.k)) # create sequential list of strategy-numbers random.shuffle(stratlist) # shuffle the list # s0 is next iteration's candidate for possible replacement self.diffevol['s0_index'] = stratlist[0] # s1, s2, s3 used in DE to create new strategy, potential replacement for s0 s1_index = stratlist[1] s2_index = stratlist[2] s3_index = stratlist[3] # unpack the actual strategy values s1_stratval = self.strats[s1_index]['stratval'] s2_stratval = self.strats[s2_index]['stratval'] s3_stratval = self.strats[s3_index]['stratval'] # this is the differential evolution "adaptive step": create a new individual new_stratval = s1_stratval + self.diffevol['F'] * (s2_stratval - s3_stratval) # clip to bounds new_stratval = max(-1, min(+1, new_stratval)) # record it for future use (s0 will be evaluated first, then s_new) self.strats[self.diffevol['snew_index']]['stratval'] = new_stratval if verbose: print('DiffEvol: t=%.1f, s0=%d, s1=%d, (s=%+f), s2=%d, (s=%+f), s3=%d, (s=%+f), sNew=%+f' % (time, self.diffevol['s0_index'], s1_index, s1_stratval, s2_index, s2_stratval, s3_index, s3_stratval, new_stratval)) # DC's intervention for fully converged populations # is the stddev of the strategies in the population equal/close to zero? strat_sum = 0.0 for s in range(self.k): strat_sum += self.strats[s]['stratval'] strat_mean = strat_sum / self.k sumsq = 0.0 for s in range(self.k): diff = self.strats[s]['stratval'] - strat_mean sumsq += (diff * diff) strat_stdev = math.sqrt(sumsq / self.k) if vrbs: print('t=,%.1f, MeanStrat=, %+f, stdev=,%f' % (time, strat_mean, strat_stdev)) if strat_stdev < 0.0001: # this population has converged # mutate one strategy at random randindex = random.randint(0, self.k - 1) self.strats[randindex]['stratval'] = random.uniform(-1.0, +1.0) if verbose: print('Converged pop: set strategy %d to %+f' % (randindex, self.strats[randindex]['stratval'])) # set up next iteration: first evaluate s0 self.active_strat = self.diffevol['s0_index'] strat_activate(time, self.active_strat) self.diffevol['de_state'] = 'active_s0' else: sys.exit('FAIL: self.diffevol[\'de_state\'] not recognized') elif self.optmzr is None: # this is PRZI -- nonadaptive, no optimizer, nothing to change here. pass else: sys.exit('FAIL: bad value for self.optmzr') class TraderZIP(Trader): """ The Zero-Intelligence-Plus (ZIP) adaptive trading strategy of Cliff (1997). The code here implements the original ZIP, and also the strategy-optimizing variuants ZIPSH and ZIPDE. """ # ZIP init key param-values are those used in Cliff's 1997 original HP Labs tech report # NB this implementation keeps separate margin values for buying & selling, # so a single trader can both buy AND sell # -- in the original, traders were either buyers OR sellers @staticmethod def strat_csv_str(strat): """ Take a ZIP strategy vector and return it as a csv-format string. :param strat: the vector of values for the ZIP trader's strategy :return: the csv-format string. """ if strat is None: csv_str = 'None, ' else: csv_str = 'mBuy=,%+5.3f, mSel=,%+5.3f, b=,%5.3f, m=,%5.3f, ca=,%6.4f, cr=,%6.4f, ' % \ (strat['m_buy'], strat['m_sell'], strat['beta'], strat['momntm'], strat['ca'], strat['cr']) return csv_str @staticmethod def mutate_strat(s, mode): """ How to mutate the strategy values when evolving / hill-climbing :param s: the strategy to be mutated. :param mode: specify Gaussian or some other form of distribution for the mutation delta (currently only Gauss). :return: the mutated strategy. """ def gauss_mutate_clip(value, sdev, range_min, range_max): """ Mutation of strategy-value by injection of zero-mean Gaussian noise, followed by clipping to keep in range. :param value: the value to be mutated. :param sdev: the standard deviation on the Gaussian noise. :param range_min: lower bound on the range. :param range_max: upper bound opn the range. :return: the mutated value. """ mut_val = value while mut_val == value: mut_val = value + random.gauss(0.0, sdev) if mut_val > range_max: mut_val = range_max elif mut_val < range_min: mut_val = range_min return mut_val # mutate each element of a ZIP strategy independently # and clip each to remain within bounds if mode == 'gauss': big_sdev = 0.025 small_sdev = 0.0025 margin_buy = gauss_mutate_clip(s['m_buy'], big_sdev, -1.0, 0) margin_sell = gauss_mutate_clip(s['m_sell'], big_sdev, 0.0, 1.0) beta = gauss_mutate_clip(s['beta'], big_sdev, 0.0, 1.0) momntm = gauss_mutate_clip(s['momntm'], big_sdev, 0.0, 1.0) ca = gauss_mutate_clip(s['ca'], small_sdev, 0.0, 1.0) cr = gauss_mutate_clip(s['cr'], small_sdev, 0.0, 1.0) new_strat = {'m_buy': margin_buy, 'm_sell': margin_sell, 'beta': beta, 'momntm': momntm, 'ca': ca, 'cr': cr} else: sys.exit('FAIL: bad mode in mutate_strat') return new_strat def __init__(self, ttype, tid, balance, params, time): """ Create a ZIP/ZIPSH/ZIPDE trader. :param ttype: the string identifying the trader-type (what strategy is this). :param tid: the trader i.d. string. :param balance: the starting bank balance for this trader. :param params: any additional parameters. :param time: the current time. """ Trader.__init__(self, ttype, tid, balance, params, time) # this set of one-liner functions named init_*() are just to make the init params obvious for ease of editing # for ZIP, a strategy is specified as a 6-tuple: (margin_buy, margin_sell, beta, momntm, ca, cr) # the 'default' values mentioned in comments below come from Cliff 1997 -- good ranges for most situations def init_beta(): """in Cliff 1997 the initial beta values are U(0.1, 0.5)""" return random.uniform(0.1, 0.5) def init_momntm(): """in Cliff 1997 the initial momentum values are U(0.0, 0.1)""" return random.uniform(0.0, 0.1) def init_ca(): # in Cliff 1997 c_a was a system constant, the same for all traders, set to 0.05 # here we take the liberty of introducing some variation return random.uniform(0.01, 0.05) def init_cr(): # in Cliff 1997 c_r was a system constant, the same for all traders, set to 0.05 # here we take the liberty of introducing some variation return random.uniform(0.01, 0.05) def init_margin(): # in Cliff 1997 the initial margin values are U(0.05, 0.35) return random.uniform(0.05, 0.35) def init_stratwaittime(): # not in Cliff 1997: use whatever limits you think best. return 7200 + random.randint(0, 3600) # unpack the params # for ZIPSH and ZIPDE params should include values for optimizer and k # if no params specified then defaults to ZIP with strat values as in Cliff1997 # default parameter values k = 1 optimizer = None # no optimizer => plain non-optimizing ZIP logging = False # did call provide different params? if type(params) is dict: if 'k' in params: k = params['k'] if 'optimizer' in params: optimizer = params['optimizer'] self.logfile = None if 'logfile' in params: logging = True logfilename = params['logfile'] + '_' + tid + '_log.csv' self.logfile = open(logfilename, 'w') # the following set of variables are needed for original ZIP *and* for its optimizing extensions e.g. ZIPSH self.logging = logging self.willing = 1 self.able = 1 self.job = None # this gets switched to 'Bid' or 'Ask' depending on order-type self.active = False # gets switched to True while actively working an order self.prev_change = 0 # this was called last_d in Cliff'97 self.beta = init_beta() self.momntm = init_momntm() self.ca = init_ca() # self.ca & self.cr were hard-coded in '97 but parameterised later self.cr = init_cr() self.margin = None # this was called profit in Cliff'97 self.margin_buy = -1.0 * init_margin() self.margin_sell = init_margin() self.price = None self.limit = None self.prev_best_bid_p = None # best bid price on LOB on previous update self.prev_best_bid_q = None # best bid quantity on LOB on previous update self.prev_best_ask_p = None # best ask price on LOB on previous update self.prev_best_ask_q = None # best ask quantity on LOB on previous update # the following set of variables are needed only by ZIP with added hyperparameter optimization (e.g. ZIPSH) self.k = k # how many strategies evaluated at any one time? self.optmzr = optimizer # what form of strategy-optimizer we're using self.strats = None # the list of strategies, each of which is a dictionary self.strat_wait_time = init_stratwaittime() # how many secs do we give any one strat before switching? self.strat_eval_time = self.k * self.strat_wait_time # time to cycle through evaluating all k strategies self.last_strat_change_time = time # what time did we last change strategies? self.active_strat = 0 # which of the k strategies are we currently playing? -- start with 0 self.profit_epsilon = 0.0 * random.random() # min profit-per-sec difference between strategies that counts if self.optmzr is not None and k > 1: # we're doing some form of k-armed strategy-optimization with multiple strategies self.strats = [] # strats[0] is whatever we've just assigned, and is the active strategy strategy = {'m_buy': self.margin_buy, 'm_sell': self.margin_sell, 'beta': self.beta, 'momntm': self.momntm, 'ca': self.ca, 'cr': self.cr} self.strats.append({'stratvec': strategy, 'start_t': time, 'active': True, 'profit': 0, 'pps': 0, 'evaluated': False}) # rest of *initial* strategy set is generated from same distributions, but these are all inactive for s in range(1, k): strategy = {'m_buy': -1.0 * init_margin(), 'm_sell': init_margin(), 'beta': init_beta(), 'momntm': init_momntm(), 'ca': init_ca(), 'cr': init_cr()} self.strats.append({'stratvec': strategy, 'start_t': time, 'active': False, 'profit': 0, 'pps': 0, 'evaluated': False}) if self.logging: self.logfile.write('ZIP, Tid, %s, ttype, %s, optmzr, %s, strat_wait_time, %f, n_strats=%d:\n' % (self.tid, self.ttype, self.optmzr, self.strat_wait_time, self.k)) for s in self.strats: self.logfile.write(str(s)+'\n') def getorder(self, time, countdown, lob): """ Create the next order for this trader :param time: the current time :param countdown: time remaining until market closes (not used in ZIP) :param lob: the current state of the LOB :return: this trader's next order. """ # this test for negative countdown is purely to stop PyCharm warning about unused parameter value if countdown < 0: sys.exit('Negative countdown') if len(self.orders) < 1: self.active = False order = None else: self.active = True self.limit = self.orders[0].price self.job = self.orders[0].otype if self.job == 'Bid': # currently a buyer (working a bid order) self.margin = self.margin_buy else: # currently a seller (working a sell order) self.margin = self.margin_sell quoteprice = int(self.limit * (1 + self.margin)) lastprice = -1 # dummy value for if there is no lastprice if self.lastquote is not None: lastprice = self.lastquote.price self.price = quoteprice order = Order(self.tid, self.job, quoteprice, self.orders[0].qty, time, lob['QID']) self.lastquote = order if self.logging and order.price != lastprice: self.logfile.write('%f, Order:, %s\n' % (time, str(order))) return order def respond(self, time, lob, trade, vrbs): """ Update ZIP profit margin on basis of what happened in market. For ZIPSH and ZIPDE, also maybe switch strategy and/or generate new strategies to evaluate. :param time: the current time. :param lob: the current state of the LOB. :param trade: details of most recent trade, if any. :param vrbs: if True then print a running commentary of what is going on. :return: snapshot: if Ture, then the caller of respond() should print the next frame of system snapshot data. """ # ZIP trader responds to market events, altering its margin # does this whether it currently has an order to work or not def target_up(price): """ Generate a higher target price by randomly perturbing given price""" ptrb_abs = self.ca * random.random() # absolute shift ptrb_rel = price * (1.0 + (self.cr * random.random())) # relative shift target = int(round(ptrb_rel + ptrb_abs, 0)) # # print('TargetUp: %d %d\n' % (price,target)) return target def target_down(price): """ Generate a lower target price by randomly perturbing given price""" ptrb_abs = self.ca * random.random() # absolute shift ptrb_rel = price * (1.0 - (self.cr * random.random())) # relative shift target = int(round(ptrb_rel - ptrb_abs, 0)) # # print('TargetDn: %d %d\n' % (price,target)) return target def willing_to_trade(price): """ Am I willing to trade at this price?""" willing = False if self.job == 'Bid' and self.active and self.price >= price: willing = True if self.job == 'Ask' and self.active and self.price <= price: willing = True return willing def profit_alter(price): """ ZIP profit-margin update on basis of target price -- updates self.margin. :param price: the target price. :return: """ oldprice = self.price diff = price - oldprice change = ((1.0 - self.momntm) * (self.beta * diff)) + (self.momntm * self.prev_change) self.prev_change = change newmargin = ((self.price + change) / self.limit) - 1.0 if self.job == 'Bid': if newmargin < 0.0: self.margin_buy = newmargin self.margin = newmargin else: if newmargin > 0.0: self.margin_sell = newmargin self.margin = newmargin # set the price from limit and profit-margin self.price = int(round(self.limit * (1.0 + self.margin), 0)) def load_strat(stratvec, birthtime): """ Copy the strategy vector into the ZIP trader's params and timestamp it. :param stratvec: the strategy vector. :param birthtime: the timestamp. :return: """ self.margin_buy = stratvec['m_buy'] self.margin_sell = stratvec['m_sell'] self.beta = stratvec['beta'] self.momntm = stratvec['momntm'] self.ca = stratvec['ca'] self.cr = stratvec['cr'] # bookkeeping self.n_trades = 0 self.birthtime = birthtime self.balance = 0 self.profitpertime = 0 def strat_activate(t, s_index): """ Activate a specified strategy-vector. :param t: the current time. :param s_index: the index of the strategy to be activated. :return: """ # print('t=%f Strat_activate, index=%d, active=%s' % (t, s_index, self.strats[s_index]['active'] )) self.strats[s_index]['start_t'] = t self.strats[s_index]['active'] = True self.strats[s_index]['profit'] = 0.0 self.strats[s_index]['pps'] = 0.0 self.strats[s_index]['evaluated'] = False # snapshot says whether the caller of respond() should print next frame of system snapshot data snapshot = False if self.optmzr == 'ZIPSH': # ZIP with simple-stochastic-hillclimber optimization of strategy (hyperparameter values) # NB this *cycles* through the available strats in sequence (i.e., it doesn't shuffle them) # first update the pps for each active strategy for s in self.strats: # update pps active_flag = s['active'] if active_flag: s['pps'] = self.profitpertime_update(time, s['start_t'], s['profit']) # have we evaluated all the strategies? # (could instead just compare active_strat to k, but checking them all in sequence is arguably clearer) # assume that all strats have been evaluated, and search for evidence to the contrary all_evaluated = True for s in self.strats: if s['evaluated'] is False: all_evaluated = False break if all_evaluated: # time to generate a new set/population of k candidate strategies # NB when the final strategy in the trader's set/popln is evaluated, the set is then sorted into # descending order of profitability, so when we get to here we know that strats[0] is elite if vrbs and self.tid == 'S00': print('t=%.3f, ZIPSH %s: strat_eval_time=%.3f,' % (time, self.tid, self.strat_eval_time)) for s in self.strats: print('%s, start_t=%f, $=%f, pps=%f' % (self.strat_csv_str(s['stratvec']), s['start_t'], s['profit'], s['pps'])) # if the difference between the top two strats is too close to call then flip a coin # this is to prevent the same good strat being held constant simply by chance cos it is at index [0] best_strat = 0 prof_diff = self.strats[0]['pps'] - self.strats[1]['pps'] if abs(prof_diff) < self.profit_epsilon: # they're too close to call, so just flip a coin best_strat = random.randint(0, 1) if best_strat == 1: # need to swap strats[0] and strats[1] tmp_strat = self.strats[0] self.strats[0] = self.strats[1] self.strats[1] = tmp_strat # at this stage, strats[0] is our newly-chosen elite-strat, about to replicate & mutate # now replicate and mutate the elite into all the other strats for s in range(1, self.k): # note range index starts at one not zero (elite is at [0]) self.strats[s]['stratvec'] = self.mutate_strat(self.strats[0]['stratvec'], 'gauss') strat_activate(time, s) # and then update (wipe) records for the elite strat_activate(time, 0) # load the elite into the ZIP trader params load_strat(self.strats[0]['stratvec'], time) self.active_strat = 0 if vrbs and self.tid == 'S00': print('%s: strat_eval_time=%f, best_strat=%d, MUTATED:' % (self.tid, self.strat_eval_time, best_strat)) for s in self.strats: print('%s start_t=%.3f, lifetime=%.3f, $=%.3f, pps=%f' % (self.strat_csv_str(s['stratvec']), s['start_t'], time - s['start_t'], s['profit'], s['pps'])) else: # we're still evaluating s = self.active_strat time_elapsed = time - self.strats[s]['start_t'] if time_elapsed >= self.strat_wait_time: # this strategy has had long enough: update records for this strategy, then swap to another strategy self.strats[s]['active'] = False self.strats[s]['profit'] = self.balance self.strats[s]['pps'] = self.profitpertime self.strats[s]['evaluated'] = True new_strat = s + 1 if new_strat > self.k - 1: # we've just evaluated the last of this trader's set of strategies # sort the strategies into order of descending profitability strats_sorted = sorted(self.strats, key=lambda k: k['pps'], reverse=True) # use this as a control: unsorts the strats, gives pure random walk. # strats_sorted = self.strats # the sorted list of strats replaces the existing list self.strats = strats_sorted # signal that we want to record a system snapshot because this trader's eval loop finished snapshot = True # NB not updating self.active_strat here because next call to respond() generates new popln else: # copy the new strategy vector into the trader's params load_strat(self.strats[new_strat]['stratvec'], time) self.strats[new_strat]['start_t'] = time self.active_strat = new_strat self.strats[new_strat]['active'] = True self.last_strat_change_time = time if vrbs and self.tid == 'S00': vstr = 't=%.3f (%.2fdays) %s ZIPSH respond:' % (time, time/86400, self.tid) vstr += ' strat[%d] elapsed=%.3f; wait_t=%.3f, pps=%f' % \ (s, time_elapsed, self.strat_wait_time, self.strats[s]['pps']) if new_strat > self.k - 1: print(vstr) else: vstr += ' switching to strat[%d]: %s' %\ (new_strat, self.strat_csv_str(self.strats[new_strat]['stratvec'])) elif self.optmzr is None: # this is vanilla ZIP -- nonadaptive, no optimizer, nothing to change here. pass # what, if anything, has happened on the bid LOB? bid_improved = False bid_hit = False lob_best_bid_p = lob['bids']['best'] lob_best_bid_q = None if lob_best_bid_p is not None: # non-empty bid LOB lob_best_bid_q = lob['bids']['lob'][-1][1] if (self.prev_best_bid_p is not None) and (self.prev_best_bid_p < lob_best_bid_p): # best bid has improved # NB doesn't check if the improvement was by self bid_improved = True elif trade is not None and ((self.prev_best_bid_p > lob_best_bid_p) or ( (self.prev_best_bid_p == lob_best_bid_p) and (self.prev_best_bid_q > lob_best_bid_q))): # previous best bid was hit bid_hit = True elif self.prev_best_bid_p is not None: # the bid LOB has been emptied: was it cancelled or hit? last_tape_item = lob['tape'][-1] if last_tape_item['type'] == 'Cancel': bid_hit = False else: bid_hit = True # what, if anything, has happened on the ask LOB? ask_improved = False ask_lifted = False lob_best_ask_p = lob['asks']['best'] lob_best_ask_q = None if lob_best_ask_p is not None: # non-empty ask LOB lob_best_ask_q = lob['asks']['lob'][0][1] if (self.prev_best_ask_p is not None) and (self.prev_best_ask_p > lob_best_ask_p): # best ask has improved -- NB doesn't check if the improvement was by self ask_improved = True elif trade is not None and ((self.prev_best_ask_p < lob_best_ask_p) or ( (self.prev_best_ask_p == lob_best_ask_p) and (self.prev_best_ask_q > lob_best_ask_q))): # trade happened and best ask price has got worse, or stayed same but quantity reduced # -- assume previous best ask was lifted ask_lifted = True elif self.prev_best_ask_p is not None: # the ask LOB is empty now but was not previously: canceled or lifted? last_tape_item = lob['tape'][-1] if last_tape_item['type'] == 'Cancel': ask_lifted = False else: ask_lifted = True if vrbs and (bid_improved or bid_hit or ask_improved or ask_lifted): print('ZIP respond: B_improved', bid_improved, 'B_hit', bid_hit, 'A_improved', ask_improved, 'A_lifted', ask_lifted) deal = bid_hit or ask_lifted if self.job == 'Ask': # seller if deal: tradeprice = trade['price'] if self.price <= tradeprice: # could sell for more? raise margin target_price = target_up(tradeprice) profit_alter(target_price) elif ask_lifted and self.active and not willing_to_trade(tradeprice): # wouldn't have got this deal, still working order, so reduce margin target_price = target_down(tradeprice) profit_alter(target_price) else: # no deal: aim for a target price higher than best bid if ask_improved and self.price > lob_best_ask_p: if lob_best_bid_p is not None: target_price = target_up(lob_best_bid_p) else: target_price = lob['asks']['worst'] # stub quote profit_alter(target_price) if self.job == 'Bid': # buyer if deal: tradeprice = trade['price'] if self.price >= tradeprice: # could buy for less? raise margin (i.e. cut the price) target_price = target_down(tradeprice) profit_alter(target_price) elif bid_hit and self.active and not willing_to_trade(tradeprice): # wouldn't have got this deal, still working order, so reduce margin target_price = target_up(tradeprice) profit_alter(target_price) else: # no deal: aim for target price lower than best ask if bid_improved and self.price < lob_best_bid_p: if lob_best_ask_p is not None: target_price = target_down(lob_best_ask_p) else: target_price = lob['bids']['worst'] # stub quote profit_alter(target_price) # remember the best LOB data ready for next response self.prev_best_bid_p = lob_best_bid_p self.prev_best_bid_q = lob_best_bid_q self.prev_best_ask_p = lob_best_ask_p self.prev_best_ask_q = lob_best_ask_q # return value of respond() tells caller whether to print a new frame of system-snapshot data return snapshot class TraderPT1(Trader): """ A minimally simple propreitary trader that buys & sells to make profit PT1 long-only buy-and-hold strategy in pseudocode: 1 wait until the market has been open for 5 minutes (to give prices a chance to settle) 2 then repeat forever: 2.1 if (I am not holding a unit) 2.1.1 and (best ask price is "cheap" -- i.e., less than average of recent transaction prices) 2.1.2 and (I have enough money in my bank to pay the asking price) 2.2 then 2.2.1 (buy the unit -- lift the ask) 2.2.2 (remember the purchase-price I paid for it) 2.3 else if (I am holding a unit) 2.4 then 2.4.1 (my asking-price is that unit’s purchase-price plus my profit margin) 2.4.1 if (best bid price is more than my asking price) 2.4.1 then 2.4.1.1 (sell my unit -- hit the bid) 2.4.1.2 (put the money in my bank) """ def __init__(self, ttype, tid, balance, params, time): """ Construct a PT1 trader :param ttype: the ticker-symbol for the type of trader (its strategy) :param tid: the trader id :param balance: the trader's bank balance :param params: a dictionary of optional parameter-values to override the defaults :param time: the current time. """ init_verbose = True Trader.__init__(self, ttype, tid, balance, params, time) self.job = 'Buy' # flag switches between 'Buy' & 'Sell'; shows what PT1 is currently trying to do self.last_purchase_price = None # Default parameter-values self.n_past_trades = 5 # how many recent trades used to compute average price (avg_p)? self.bid_percent = 0.9999 # what percentage of avg_p should best_ask be for this trader to bid self.ask_delta = 5 # how much (absolute value) to improve on purchase price # Did the caller provide different params? if type(params) is dict: if 'bid_percent' in params: self.bid_percent = params['bid_percent'] if self.bid_percent > 1.0 or self.bid_percent < 0.01: sys.exit('FAIL: self.bid_percent=%f not in range [0.01,1.0])' % self.bid_percent) if 'ask_delta' in params: self.ask_delta = params['ask_delta'] if self.ask_delta < 0: sys.exit('Fail: PT1 ask_delta can\'t be negative (it\'s an absolute value)') if 'n_past_trades' in params: self.n_past_trades = int(round(params['n_past_trades'])) if self.n_past_trades < 1: sys.exit('Fail: PT1 n_past trades must be 1 or more') if init_verbose: print('PT1 init: n_past_trades=%d, bid_percent=%6.5f, ask_delta=%d\n' % (self.n_past_trades, self.bid_percent, self.ask_delta)) def getorder(self, time, countdown, lob): """ return this trader's order when it is polled in the main market_session loop. :param time: the current time. :param countdown: the time remaining until market closes (not currently used). :param lob: the public lob. :return: trader's new order, or None. """ # this test for negative countdown is purely to stop PyCharm warning about unused parameter value if countdown < 0: sys.exit('Negative countdown') if len(self.orders) < 1 or time < 5 * 60: order = None else: quoteprice = self.orders[0].price order = Order(self.tid, self.orders[0].otype, quoteprice, self.orders[0].qty, time, lob['QID']) self.lastquote = order return order def respond(self, time, lob, trade, vrbs): """ Respond to the current state of the public lob. Buys if best bid is less than simple moving average of recent transcaction prices. Sells as soon as it can make an acceptable profit. :param time: the current time :param lob: the current public lob :param trade: :param vrbs: verbosity -- if True then print running commentary, else stay silent :return: """ vstr = 't=%f PT1 respond: ' % time # what is average price of most recent n trades? # work backwards from end of tape (most recent trade) tape_position = -1 n_prices = 0 sum_prices = 0 avg_price_ok = False avg_price = -1 while n_prices < self.n_past_trades and abs(tape_position) < len(lob['tape']): if lob['tape'][tape_position]['type'] == 'Trade': price = lob['tape'][tape_position]['price'] n_prices += 1 sum_prices += price tape_position -= 1 if n_prices == self.n_past_trades: # there's been enough trades to form an acceptable average avg_price = int(round(sum_prices / n_prices)) avg_price_ok = True vstr += "avg_price_ok=%s, avg_price=%d " % (avg_price_ok, avg_price) # buying? if self.job == 'Buy' and avg_price_ok: vstr += 'Buying - ' # see what's on the LOB if lob['asks']['n'] > 0: # there is at least one ask on the LOB best_ask = lob['asks']['best'] if best_ask / avg_price < self.bid_percent: # bestask is good value: send a spread-crossing bid to lift the ask bidprice = best_ask + 1 if bidprice < self.balance: # can afford to buy # create the bid by issuing order to self, which will be processed in getorder() order = Order(self.tid, 'Bid', bidprice, 1, time, lob['QID']) self.orders = [order] vstr += 'Best ask=%d, bidprice=%d, order=%s ' % (best_ask, bidprice, order) else: vstr += 'bestask=%d >= avg_price=%d' % (best_ask, avg_price) else: vstr += 'No asks on LOB' # selling? elif self.job == 'Sell': vstr += 'Selling - ' # see what's on the LOB if lob['bids']['n'] > 0: # there is at least one bid on the LOB best_bid = lob['bids']['best'] # sell single unit at price of purchaseprice+askdelta askprice = self.last_purchase_price + self.ask_delta if askprice < best_bid: # seems we have a buyer # lift the ask by issuing order to self, which will processed in getorder() order = Order(self.tid, 'Ask', askprice, 1, time, lob['QID']) self.orders = [order] vstr += 'Best bid=%d greater than askprice=%d order=%s ' % (best_bid, askprice, order) else: vstr += 'Best bid=%d too low for askprice=%d ' % (best_bid, askprice) else: vstr += 'No bids on LOB' self.profitpertime = self.profitpertime_update(time, self.birthtime, self.balance) if vrbs: print(vstr) def bookkeep(self, time, trade, order, vrbs): """ Update trader's records of its bank balance, current orders, and current job :param trade: the current time :param order: this trader's successful order :param vrbs: verbosity -- if True then print running commentary, else stay silent. :param time: the current time. :return: """ # output string outstr is printed if vrbs==True mins = int(time//60) secs = time - 60 * mins hrs = int(mins//60) mins = mins - 60 * hrs outstr = 't=%f (%dh%02dm%02ds) %s (%s) bookkeep: orders=' % (time, hrs, mins, secs, self.tid, self.ttype) for order in self.orders: outstr = outstr + str(order) self.blotter.append(trade) # add trade record to trader's blotter # NB What follows is **LAZY** -- assumes all orders are quantity=1 transactionprice = trade['price'] if self.orders[0].otype == 'Bid': # Bid order succeeded, remember the price and adjust the balance self.balance -= transactionprice self.last_purchase_price = transactionprice self.job = 'Sell' # now try to sell it for a profit elif self.orders[0].otype == 'Ask': # Sold! put the money in the bank self.balance += transactionprice self.last_purchase_price = 0 self.job = 'Buy' # now go back and buy another one else: sys.exit('FATAL: PT1 doesn\'t know .otype %s\n' % self.orders[0].otype) if vrbs: net_worth = self.balance + self.last_purchase_price print('%s Balance=%d NetWorth=%d' % (outstr, self.balance, net_worth)) self.del_order(order) # delete the order # end of PT1 definition class TraderPT2(Trader): """ A A minimally simple propreitary trader that buys & sells to make profit PT2 long-only buy-and-hold strategy in pseudocode: 1 wait until the market has been open for 5 minutes (to give prices a chance to settle) 2 then repeat forever: 2.1 if (I am not holding a unit) 2.1.1 and (best ask price is "cheap" -- i.e., less than average of recent transaction prices) 2.1.2 and (I have enough money in my bank to pay the asking price) 2.2 then 2.2.1 (buy the unit -- lift the ask) 2.2.2 (remember the purchase-price I paid for it) 2.3 else if (I am holding a unit) 2.4 then 2.4.1 (my asking-price is that unit’s purchase-price plus my profit margin) 2.4.1 if (best bid price is more than my asking price) 2.4.1 then 2.4.1.1 (sell my unit -- hit the bid) 2.4.1.2 (put the money in my bank) """ def __init__(self, ttype, tid, balance, params, time): """ Construct a PT2 trader :param ttype: the ticker-symbol for the type of trader (its strategy) :param tid: the trader id :param balance: the trader's bank balance :param params: a dictionary of optional parameter-values to override the defaults :param time: the current time. """ Trader.__init__(self, ttype, tid, balance, params, time) self.job = 'Buy' # flag switches between 'Buy' & 'Sell'; shows what PT2 is currently trying to do self.last_purchase_price = None init_verbose = True # Default parameter-values self.n_past_trades = 5 # how many recent trades used to compute average price (avg_p)? self.bid_percent = 0.9999 # what percentage of avg_p should best_ask be for this trader to bid self.ask_delta = 5 # how much (absolute value) to improve on purchase price # Did the caller provide different params? if type(params) is dict: if 'bid_percent' in params: self.bid_percent = params['bid_percent'] if self.bid_percent > 1.0 or self.bid_percent < 0.01: sys.exit('FAIL: PT2 self.bid_percent=%f not in range [0.01,1.0])' % self.bid_percent) if 'ask_delta' in params: self.ask_delta = params['ask_delta'] if self.ask_delta < 0: sys.exit('Fail: PT2 ask_delta can\'t be negative (it\'s an absolute value)') if 'n_past_trades' in params: self.n_past_trades = int(round(params['n_past_trades'])) if self.n_past_trades < 1: sys.exit('Fail: PT2 n_past trades must be 1 or more') if init_verbose: print('PT2 init: n_past_trades=%d, bid_percent=%6.5f, ask_delta=%d\n' % (self.n_past_trades, self.bid_percent, self.ask_delta)) def getorder(self, time, countdown, lob): """ return this trader's order when it is polled in the main market_session loop. :param time: the current time. :param countdown: the time remaining until market closes (not currently used). :param lob: the public lob. :return: trader's new order, or None. """ # this test for negative countdown is purely to stop PyCharm warning about unused parameter value if countdown < 0: sys.exit('Negative countdown') if len(self.orders) < 1 or time < 5 * 60: order = None else: quoteprice = self.orders[0].price order = Order(self.tid, self.orders[0].otype, quoteprice, self.orders[0].qty, time, lob['QID']) self.lastquote = order return order def respond(self, time, lob, trade, vrbs): """ Respond to the current state of the public lob. Buys if best bid is less than simple moving average of recent transcaction prices. Sells as soon as it can make an acceptable profit. :param time: the current time :param lob: the current public lob :param trade: :param vrbs: if True then print running commentary, else stay silent :return: """ vstr = 't=%f PT2 respond: ' % time # what is average price of most recent n trades? # work backwards from end of tape (most recent trade) tape_position = -1 n_prices = 0 sum_prices = 0 avg_price_ok = False avg_price = -1 while n_prices < self.n_past_trades and abs(tape_position) < len(lob['tape']): if lob['tape'][tape_position]['type'] == 'Trade': price = lob['tape'][tape_position]['price'] n_prices += 1 sum_prices += price tape_position -= 1 if n_prices == self.n_past_trades: # there's been enough trades to form an acceptable average avg_price = int(round(sum_prices / n_prices)) avg_price_ok = True vstr += "avg_price_ok=%s, avg_price=%d " % (avg_price_ok, avg_price) # buying? if self.job == 'Buy' and avg_price_ok: vstr += 'Buying - ' # see what's on the LOB if lob['asks']['n'] > 0: # there is at least one ask on the LOB best_ask = lob['asks']['best'] if best_ask / avg_price < self.bid_percent: # bestask is good value: send a spread-crossing bid to lift the ask bidprice = best_ask + 1 if bidprice < self.balance: # can afford to buy # create the bid by issuing order to self, which will be processed in getorder() order = Order(self.tid, 'Bid', bidprice, 1, time, lob['QID']) self.orders = [order] vstr += 'Best ask=%d, bidprice=%d, order=%s ' % (best_ask, bidprice, order) else: vstr += 'bestask=%d >= avg_price=%d' % (best_ask, avg_price) else: vstr += 'No asks on LOB' # selling? elif self.job == 'Sell': vstr += 'Selling - ' # see what's on the LOB if lob['bids']['n'] > 0: # there is at least one bid on the LOB best_bid = lob['bids']['best'] # sell single unit at price of purchaseprice+askdelta askprice = self.last_purchase_price + self.ask_delta if askprice < best_bid: # seems we have a buyer # lift the ask by issuing order to self, which will processed in getorder() order = Order(self.tid, 'Ask', askprice, 1, time, lob['QID']) self.orders = [order] vstr += 'Best bid=%d greater than askprice=%d order=%s ' % (best_bid, askprice, order) else: vstr += 'Best bid=%d too low for askprice=%d ' % (best_bid, askprice) else: vstr += 'No bids on LOB' self.profitpertime = self.profitpertime_update(time, self.birthtime, self.balance) if vrbs: print(vstr) def bookkeep(self, time, trade, order, vrbs): """ Update trader's records of its bank balance, current orders, and current job :param trade: the current time :param order: this trader's successful order :param vrbs: if True then print a running commentary, otherwise stay silent. :param time: the current time. :return: """ # output string outstr is printed if vrbs==True mins = int(time//60) secs = time - 60 * mins hrs = int(mins//60) mins = mins - 60 * hrs outstr = 't=%f (%dh%02dm%02ds) %s (%s) bookkeep: orders=' % (time, hrs, mins, secs, self.tid, self.ttype) for order in self.orders: outstr = outstr + str(order) self.blotter.append(trade) # add trade record to trader's blotter # NB What follows is **LAZY** -- assumes all orders are quantity=1 transactionprice = trade['price'] if self.orders[0].otype == 'Bid': # Bid order succeeded, remember the price and adjust the balance self.balance -= transactionprice self.last_purchase_price = transactionprice self.job = 'Sell' # now try to sell it for a profit elif self.orders[0].otype == 'Ask': # Sold! put the money in the bank self.balance += transactionprice self.last_purchase_price = 0 self.job = 'Buy' # now go back and buy another one else: sys.exit('FATAL: PT2 doesn\'t know .otype %s\n' % self.orders[0].otype) if vrbs: net_worth = self.balance + self.last_purchase_price print('%s Balance=%d NetWorth=%d' % (outstr, self.balance, net_worth)) self.del_order(order) # delete the order # end of PT2 definition # ########################---trader-types have all been defined now--################ # #########################---Below lies the experiment/test-rig---################## def trade_stats(expid, traders, dumpfile, time, lob): """ Dump CSV statistics on exchange data and trader population to file for later analysis. This makes no assumptions about the number of types of traders, or the number of traders of any one type -- allows either/both to change between successive calls, but that does make it inefficient as it has to re-analyse the entire set of traders on each call. :param expid: the experiment-I.D. character-string. :param traders: the list of traders in the market. :param dumpfile: the file that will be written to. :param time: the current time. :param lob: the current state of the LOB. :return: """ # Analyse the set of traders, to see what types we have trader_types = {} for t in traders: ttype = traders[t].ttype if ttype in trader_types.keys(): t_balance = trader_types[ttype]['balance_sum'] + traders[t].balance n = trader_types[ttype]['n'] + 1 else: t_balance = traders[t].balance n = 1 trader_types[ttype] = {'n': n, 'balance_sum': t_balance} # first two columns of output are the session_id and the time dumpfile.write('%s, %06d, ' % (expid, time)) # second two columns of output are the LOB best bid and best offer (or 'None' if they're undefined) if lob['bids']['best'] is not None: dumpfile.write('%d, ' % (lob['bids']['best'])) else: dumpfile.write('None, ') if lob['asks']['best'] is not None: dumpfile.write('%d, ' % (lob['asks']['best'])) else: dumpfile.write('None, ') # total remaining number of columns printed depends on number of different trader-types at this timestep # for each trader type we print FOUR columns... # TraderTypeCode, TotalProfitForThisTraderType, NumberOfTradersOfThisType, AverageProfitPerTraderOfThisType for ttype in sorted(list(trader_types.keys())): n = trader_types[ttype]['n'] s = trader_types[ttype]['balance_sum'] dumpfile.write('%s, %d, %d, %f, ' % (ttype, s, n, s / float(n))) dumpfile.write('\n') def populate_market(trdrs_spec, traders, shuffle, vrbs): """ Create a bunch of traders from traders-specification. Optionally shuffles the pack of buyers and the pack of sellers. :param trdrs_spec: the specification of the population of traders. :param traders: the list into which the newly-created traders traders will be written, as a return parameter :param shuffle: whether to shuffle the ordering of buyers/sellers within the respective list. :param vrbs: verbosity Boolean: if True, print a running commentary; if False, stay silent. :return: tuple (n_buyers, n_sellers) """ # trdrs_spec is a list of buyer-specs and a list of seller-specs # each spec is (, , optionally: ) def trader_type(robottype, name, parameters): """ Create a newly instantiated trader of the designated type. :param robottype: the 'ticker-symbol' abbreviation indicating what type of trader to create. :param name: this trader's trader-I.D. character string. :param parameters: a list of parameter values for this trader-type. :return: a newly created trader of the designated type. """ balance = 0.00 proptrader_balance = 500 # marketmakers start with zero inventory and a balance of $500 time0 = 0 if robottype == 'GVWY': return TraderGiveaway('GVWY', name, balance, parameters, time0) elif robottype == 'ZIC': return TraderZIC('ZIC', name, balance, parameters, time0) elif robottype == 'SHVR': return TraderShaver('SHVR', name, balance, parameters, time0) elif robottype == 'SNPR': return TraderSniper('SNPR', name, balance, parameters, time0) elif robottype == 'ZIP': return TraderZIP('ZIP', name, balance, parameters, time0) elif robottype == 'ZIPSH': return TraderZIP('ZIPSH', name, balance, parameters, time0) elif robottype == 'PRZI': return TraderPRZI('PRZI', name, balance, parameters, time0) elif robottype == 'PRSH': return TraderPRZI('PRSH', name, balance, parameters, time0) elif robottype == 'PRDE': return TraderPRZI('PRDE', name, balance, parameters, time0) elif robottype == 'PT1': return TraderPT1('PT1', name, proptrader_balance, parameters, time0) elif robottype == 'PT2': return TraderPT2('PT2', name, proptrader_balance, parameters, time0) else: sys.exit('FATAL: don\'t know trader type %s\n' % robottype) def shuffle_traders(ttype_char, n, trader_list): """ Shuffles the trader-I.D. character strings of the traders in trader_list :param ttype_char: the lead character on the trader-I.D. strings (B for buyer, S for seller, etc) :param n: how many traders of this type :param trader_list: the list of traders in which the shuffling happens :return: """ for swap in range(n): t1 = (n - 1) - swap t2 = random.randint(0, t1) t1name = '%c%02d' % (ttype_char, t1) t2name = '%c%02d' % (ttype_char, t2) trader_list[t1name].tid = t2name trader_list[t2name].tid = t1name temp = traders[t1name] trader_list[t1name] = trader_list[t2name] trader_list[t2name] = temp def unpack_params(trader_params, mapping): """ Unpack the parameters for those trader-types that have them :param trader_params: the paramaters being passed to this trader. :param mapping: Boolean flag: if True, enable fitness-landscape-mapping; otherwise do nothing for mapping. :return: the dictionary of parameters for this trader. """ parameters = None if ttype == 'ZIPSH' or ttype == 'ZIP': # parameters matter... if mapping: parameters = 'landscape-mapper' elif trader_params is not None: parameters = trader_params.copy() # trader-type determines type of optimizer used if ttype == 'ZIPSH': parameters['optimizer'] = 'ZIPSH' else: # ttype=ZIP parameters['optimizer'] = None if ttype == 'PRSH' or ttype == 'PRDE' or ttype == 'PRZI': # parameters matter... if mapping: parameters = 'landscape-mapper' elif trader_params is not None: # params determines type of optimizer used if ttype == 'PRSH': parameters = {'optimizer': 'PRSH', 'k': trader_params['k'], 'strat_min': trader_params['s_min'], 'strat_max': trader_params['s_max']} elif ttype == 'PRDE': parameters = {'optimizer': 'PRDE', 'k': trader_params['k'], 'strat_min': trader_params['s_min'], 'strat_max': trader_params['s_max']} else: # ttype=PRZI parameters = {'optimizer': None, 'k': 1, 'strat_min': trader_params['s_min'], 'strat_max': trader_params['s_max']} else: sys.exit('FAIL: PRZI/PRSH/PRDE trader needs one or more parameters to be specified') # for PT1/PT2 the parameters are optional... # ...and are unpacked in __init__, so here they're just passed straight on through if ttype == 'PT1': parameters = trader_params if ttype == 'PT2': parameters = trader_params return parameters landscape_mapping = False # set to true when mapping fitness landscape (for PRSH etc). # the code that follows is a bit of a kludge, needs tidying up. n_buyers = 0 for bs in trdrs_spec['buyers']: ttype = bs[0] for b in range(bs[1]): tname = 'B%02d' % n_buyers # buyer i.d. string if len(bs) > 2: # third part of the buyer-spec is params for this trader-type params = unpack_params(bs[2], landscape_mapping) else: params = unpack_params(None, landscape_mapping) traders[tname] = trader_type(ttype, tname, params) n_buyers = n_buyers + 1 if n_buyers < 1: sys.exit('FATAL: no buyers specified\n') if shuffle: shuffle_traders('B', n_buyers, traders) n_sellers = 0 for ss in trdrs_spec['sellers']: ttype = ss[0] for s in range(ss[1]): tname = 'S%02d' % n_sellers # buyer i.d. string if len(ss) > 2: # third part of the buyer-spec is params for this trader-type params = unpack_params(ss[2], landscape_mapping) else: params = unpack_params(None, landscape_mapping) traders[tname] = trader_type(ttype, tname, params) n_sellers = n_sellers + 1 if n_sellers < 1: sys.exit('FATAL: no sellers specified\n') if shuffle: shuffle_traders('S', n_sellers, traders) n_proptraders = 0 if 'proptraders' in trdrs_spec and len(trdrs_spec['proptraders']) > 0: for pts in trdrs_spec['proptraders']: ttype = pts[0] for pt in range(pts[1]): tname = 'P%02d' % n_proptraders # proptrader i.d. string if len(pts) > 2: # third part of the buyer-spec is params for this trader-type params = unpack_params(pts[2], landscape_mapping) else: params = unpack_params(None, landscape_mapping) traders[tname] = trader_type(ttype, tname, params) n_proptraders = n_proptraders + 1 # NB markets with zero proptraders don't cause a fatal error if n_proptraders > 0 and shuffle: shuffle_traders('P', n_proptraders, traders) if vrbs: for t in range(n_buyers): tname = 'B%02d' % t print(traders[tname]) for t in range(n_sellers): tname = 'S%02d' % t print(traders[tname]) for t in range(n_proptraders): tname = 'P%02d' % t print(traders[tname]) return {'n_buyers': n_buyers, 'n_sellers': n_sellers, 'n_proptraders': n_proptraders} def customer_orders(time, traders, trader_stats, orders_sched, pending, vrbs): """ Generate a list of new customer-orders to be issued to the traders in the immediate/near future, and a list of any existing customer-orders that need to be cancelled because they are overridden by new ones. :param time: the current time. :param traders: the population of traders. :param trader_stats: summary statistics about the population of traders. :param orders_sched: the supply/demand schedule from which the orders will be generated... os['timemode'] is either 'periodic', 'drip-fixed', 'drip-jitter', or 'drip-poisson'; os['interval'] is number of seconds for a full cycle of replenishment; drip-poisson sequences will be normalised to ensure time of last replenishment <= interval. If a supply or demand schedule mode is "random" and more than one range is supplied in ranges[], then each time a price is generated one of the ranges is chosen equiprobably and the price is then generated uniform-randomly from that range. if len(range)==2, interpreted as min and max values on the schedule, specifying linear supply/demand curve. if len(range)==3, first two vals are min & max for linear sup/dem curves, and third value should be a callable function that generates a dynamic price offset; he offset value applies equally to the min & max, so gradient of linear sup/dem curves doesn't vary, but equilibrium price does. if len(range)==4, the third value is function that gives dynamic offset for schedule min, and 4th is a function giving dynamic offset for schedule max, so gradient of sup/dem linear curve can vary dynamically along with the varying equilibrium price. :param pending: the list of currently pending future orders if this is empty, generates a new one). :param vrbs: verbosity Boolean: if True, print a running commentary; if False, stay silent. :return: [new_pending, cancellations]: new_pending is list of new orders to be issued; cancellations is list of previously-issued orders now cancelled. """ def sysmin_check(price): """ if price is less than system minimum price, issue a warning and clip the price to the minimum""" if price < bse_sys_minprice: print('WARNING: price < bse_sys_min -- clipped') price = bse_sys_minprice return price def sysmax_check(price): """ if price is greater than system maximum price, issue a warning and clip the price to the maximum""" if price > bse_sys_maxprice: print('WARNING: price > bse_sys_max -- clipped') price = bse_sys_maxprice return price def getorderprice(i, schedules, n, stepmode, orderissuetime): """ Generate a price for an order, using the given supply/demand schedule, and specified step-mode. :param i: index of trader (position in list of traders). :param schedules: the supply/demand schedules. :param n: the number of traders that this schedule sup/dem is being applied to. :param stepmode: what type of steps to have between successive prices on the sup/dem schedule. stepmode=='fixed' => all steps are equal at one fixed size -- a "uniform-step" (see "jittered", below); stepmode=='jittered' => all steps are random, constrained to be within 2 uniform-steps of each other; stepmode=='random' => all steps are generated from a uniform distribution. :param orderissuetime: the time that this order will be issued at. :return: the price. """ # does the first schedule range include optional dynamic offset function(s)? if len(schedules[0]) > 2: offsetfn = schedules[0][2] if callable(offsetfn[0]): # same offset for min and max offset_min = offsetfn[0](orderissuetime, *offsetfn[1]) offset_max = offset_min else: sys.exit('FAIL: 3rd argument of sched in getorderprice() not callable') if len(schedules[0]) > 3: # if second offset function is specified, that applies only to the max value offsetfn = schedules[0][3] if callable(offsetfn): # this function applies to max offset_max = offsetfn(orderissuetime) else: sys.exit('FAIL: 4th argument of sched in getorderprice() not callable') else: offset_min = 0.0 offset_max = 0.0 pmin = sysmin_check(offset_min + min(schedules[0][0], schedules[0][1])) pmax = sysmax_check(offset_max + max(schedules[0][0], schedules[0][1])) prange = pmax - pmin stepsize = prange / (n - 1) halfstep = round(stepsize / 2.0) if stepmode == 'fixed': order_price = pmin + int(i * stepsize) elif stepmode == 'jittered': order_price = pmin + int(i * stepsize) + random.randint(-halfstep, halfstep) elif stepmode == 'random': if len(schedules) > 1: # more than one schedule: choose one equiprobably s = random.randint(0, len(schedules) - 1) pmin = sysmin_check(min(schedules[s][0], schedules[s][1])) pmax = sysmax_check(max(schedules[s][0], schedules[s][1])) order_price = random.randint(int(pmin), int(pmax)) else: sys.exit('FAIL: Unknown mode in schedule') order_price = sysmin_check(sysmax_check(order_price)) return order_price def getissuetimes(n_traders, timemode, interval, shuffle, fittointerval): """ Generate a list of issue/arrival times for a set of future customer-orders, over a specified time-interval. :param n_traders: how many traders need issue times (i.e., the number of customer orders to be generated) :param timemode: character-string specifying the temporal spacing of orders: timemode=='periodic'=> orders issued to all traders at the same instant in time, every time-interval; timemode=='drip-fixed'=> order interarrival time is exactly one timestep, for all orders; timemode=='drip-jitter'=> order interarrival time is (1+r)*timestep, r=U[0,timestep], for all orders; timemode=='drip-poisson'=> order interarrival time is a Poisson random process, for all orders. :param interval: the time-interval between successive order issuals/arrivals. :param shuffle: if True then shuffle the arrival times, randomising the sequence in which traders get orders. :param fittointerval: if True then final order arrives at exactly t+interval; else may be slightly later. :return: the list of issue times. """ interval = float(interval) if n_traders < 1: sys.exit('FAIL: n_traders < 1 in getissuetime()') elif n_traders == 1: tstep = interval else: tstep = interval / (n_traders - 1) arrtime = 0 issue_times = [] for trdr in range(n_traders): if timemode == 'periodic': arrtime = interval elif timemode == 'drip-fixed': arrtime = trdr * tstep elif timemode == 'drip-jitter': arrtime = trdr * tstep + tstep * random.random() elif timemode == 'drip-poisson': # poisson requires a bit of extra work interarrivaltime = random.expovariate(n_traders / interval) arrtime += interarrivaltime else: sys.exit('FAIL: unknown time-mode in getissuetimes()') issue_times.append(arrtime) # at this point, arrtime is the last arrival time if fittointerval and ((arrtime > interval) or (arrtime < interval)): # generated sum of interarrival times longer than the interval # squish them back so that last arrival falls at t=interval for trdr in range(n_traders): issue_times[trdr] = interval * (issue_times[trdr] / arrtime) # optionally randomly shuffle the times if shuffle: for trdr in range(n_traders): i = (n_traders - 1) - trdr j = random.randint(0, i) tmp = issue_times[i] issue_times[i] = issue_times[j] issue_times[j] = tmp return issue_times def getschedmode(t_now, order_schedules): """ return the step-mode for supply/demand schedule at the current time :param t_now: the current time :param order_schedules: dictionary/list of order schedules :return: schedrange = the price range for this schedule; mode= the stepmode for this schedule """ got_one = False schedrange = None stepmode = None for schedule in order_schedules: if (schedule['from'] <= t_now) and (t_now < schedule['to']): # within the timezone for this schedule schedrange = schedule['ranges'] stepmode = schedule['stepmode'] got_one = True break # jump out the loop -- so the first matching timezone has priority over any others if not got_one: sys.exit('Fail: time=%5.2f not within any timezone in order_schedules=%s' % (t_now, order_schedules)) return schedrange, stepmode n_buyers = trader_stats['n_buyers'] n_sellers = trader_stats['n_sellers'] shuffle_times = True cancellations = [] if len(pending) < 1: # list of pending (to-be-issued) customer orders is empty, so generate a new one new_pending = [] # demand side (buyers) issuetimes = getissuetimes(n_buyers, orders_sched['timemode'], orders_sched['interval'], shuffle_times, True) ordertype = 'Bid' (sched, mode) = getschedmode(time, orders_sched['dem']) for t in range(n_buyers): issuetime = time + issuetimes[t] tname = 'B%02d' % t orderprice = getorderprice(t, sched, n_buyers, mode, issuetime) order = Order(tname, ordertype, orderprice, 1, issuetime, chrono.time()) new_pending.append(order) # supply side (sellers) issuetimes = getissuetimes(n_sellers, orders_sched['timemode'], orders_sched['interval'], shuffle_times, True) ordertype = 'Ask' (sched, mode) = getschedmode(time, orders_sched['sup']) for t in range(n_sellers): issuetime = time + issuetimes[t] tname = 'S%02d' % t orderprice = getorderprice(t, sched, n_sellers, mode, issuetime) # print('time %d sellerprice %d' % (time,orderprice)) order = Order(tname, ordertype, orderprice, 1, issuetime, chrono.time()) new_pending.append(order) else: # there are pending future orders: issue any whose timestamp is in the past new_pending = [] for order in pending: if order.time < time: # this order should have been issued by now # issue it to the trader tname = order.tid response = traders[tname].add_order(order, vrbs) if vrbs: print('Customer order: %s %s' % (response, order)) if response == 'LOB_Cancel': cancellations.append(tname) if vrbs: print('Cancellations: %s' % cancellations) # and then don't add it to new_pending (i.e., delete it) else: # this order stays on the pending list new_pending.append(order) return [new_pending, cancellations] def market_session(sess_id, starttime, endtime, trader_spec, order_schedule, dumpfile_flags, sess_vrbs): """ One session in the market. :param sess_id: the character-string ID for this session, used in naming output files. :param starttime: the time the session starts. :param endtime: the time the sessiom ends. :param trader_spec: specification of the traders populating the market for this session. :param order_schedule: specification of the "customer orders" assigned to traders, i.e. the supply/demand schedule. :param dumpfile_flags: a dictionary of Boolean flags specifying which output files to be written for this session. :param sess_vrbs: verbosity: if True, output a running commentary on what is going on; if False, stay silent. :return: . """ def dump_strats_frame(frametime, stratfile, trdrs): """ Write one frame of strategy snapshot :param frametime: the time that the frame snapshot is printed. :param stratfile: the file to write to. :param trdrs: the population of traders. :return: """ line_str = 't=,%.0f, ' % frametime best_buyer_id = None best_buyer_prof = 0 best_buyer_strat = None best_seller_id = None best_seller_prof = 0 best_seller_strat = None # loop through traders to find the best for trdr in traders: trader = trdrs[trdr] # print('PRSH/PRDE/ZIPSH strategy recording, t=%s' % trader) if trader.ttype == 'PRSH' or trader.ttype == 'PRDE' or trader.ttype == 'ZIPSH': line_str += 'id=,%s, %s,' % (trader.tid, trader.ttype) if trader.ttype == 'ZIPSH': # we know that ZIPSH sorts the set of strats into best-first act_strat = trader.strats[0]['stratvec'] act_prof = trader.strats[0]['pps'] else: act_strat = trader.strats[trader.active_strat]['stratval'] act_prof = trader.strats[trader.active_strat]['pps'] line_str += 'actvstrat=,%s ' % trader.strat_csv_str(act_strat) line_str += 'actvprof=,%f, ' % act_prof if trader.tid[:1] == 'B': # this trader is a buyer if best_buyer_id is None or act_prof > best_buyer_prof: best_buyer_id = trader.tid best_buyer_strat = act_strat best_buyer_prof = act_prof elif trader.tid[:1] == 'S': # this trader is a seller if best_seller_id is None or act_prof > best_seller_prof: best_seller_id = trader.tid best_seller_strat = act_strat best_seller_prof = act_prof else: # wtf? sys.exit('unknown trader id type in market_session') if best_buyer_id is not None: line_str += 'best_B_id=,%s, best_B_prof=,%f, best_B_strat=, ' % (best_buyer_id, best_buyer_prof) line_str += traders[best_buyer_id].strat_csv_str(best_buyer_strat) if best_seller_id is not None: line_str += 'best_S_id=,%s, best_S_prof=,%f, best_S_strat=, ' % (best_seller_id, best_seller_prof) line_str += traders[best_seller_id].strat_csv_str(best_seller_strat) line_str += '\n' if verbose: print('line_str: %s' % line_str) stratfile.write(line_str) stratfile.flush() os.fsync(stratfile) def blotter_dump(session_id, trdrs): """ Write the blotter for each trader. :param session_id: this market session's ID string (used for the filename). :param trdrs: the population of traders. :return: """ bdump = open(session_id+'_blotters.csv', 'w') for trdr in trdrs: bdump.write('%s, %d\n' % (trdrs[trdr].tid, len(trdrs[trdr].blotter))) for b in trdrs[trdr].blotter: bdump.write('%s, %s, %.3f, %d, %s, %s, %d\n' % (traders[trdr].tid, b['type'], b['time'], b['price'], b['party1'], b['party2'], b['qty'])) bdump.close() orders_verbose = False lob_verbose = False process_verbose = False respond_verbose = False bookkeep_verbose = False populate_verbose = False if dumpfile_flags['dump_strats']: strat_dump = open(sess_id + '_strats.csv', 'w') else: strat_dump = None if dumpfile_flags['dump_lobs']: lobframes = open(sess_id + '_LOB_frames.csv', 'w') else: lobframes = None if dumpfile_flags['dump_avgbals']: avg_bals = open(sess_id + '_avg_balance.csv', 'w') else: avg_bals = None if dumpfile_flags['dump_tape']: # NB writing transactions only -- not writing cancellations tape_dump = open(sess_id + '_tape.csv', 'w') else: tape_dump = None # initialise the exchange exchange = Exchange() # create a bunch of traders traders = {} trader_stats = populate_market(trader_spec, traders, True, populate_verbose) # timestep set so that can process all traders in one second # NB minimum interarrival time of customer orders may be much less than this!! timestep = 1.0 / float(trader_stats['n_buyers'] + trader_stats['n_sellers'] + trader_stats['n_proptraders']) session_duration = float(endtime - starttime) time = starttime pending_cust_orders = [] if sess_vrbs: print('\n%s; ' % sess_id) # frames_done is record of what frames we have printed data for thus far frames_done = set() while time < endtime: # how much time left, as a percentage? time_left = (endtime - time) / session_duration if sess_vrbs: print('\n\n%s; t=%08.2f (%4.1f/100) ' % (sess_id, time, time_left*100)) [pending_cust_orders, kills] = customer_orders(time, traders, trader_stats, order_schedule, pending_cust_orders, orders_verbose) # if any newly-issued customer orders mean quotes on the LOB need to be cancelled, kill them if len(kills) > 0: # if verbose : print('Kills: %s' % (kills)) for kill in kills: # if verbose : print('lastquote=%s' % traders[kill].lastquote) if traders[kill].lastquote is not None: # if verbose : print('Killing order %s' % (str(traders[kill].lastquote))) # NB if exchange.del_order() third argument = None then cancellations not written to tape file. # exchange.del_order(time, traders[kill].lastquote, tape_dump, sess_vrbs) exchange.del_order(time, traders[kill].lastquote, None, sess_vrbs) # get a limit-order quote (or None) from a randomly chosen trader tid = list(traders.keys())[random.randint(0, len(traders) - 1)] order = traders[tid].getorder(time, time_left, exchange.publish_lob(time, lobframes, lob_verbose)) if sess_vrbs: print('trader=%s order=%s' % (tid, order)) if order is not None: if order.otype == 'Ask' and order.price < traders[tid].orders[0].price: sys.exit('Bad ask') if order.otype == 'Bid' and order.price > traders[tid].orders[0].price: sys.exit('Bad bid') # send order to exchange traders[tid].n_quotes = 1 trade = exchange.process_order(time, order, tape_dump, process_verbose) if trade is not None: # trade occurred, # so the counterparties update order lists and blotters traders[trade['party1']].bookkeep(time, trade, order, bookkeep_verbose) traders[trade['party2']].bookkeep(time, trade, order, bookkeep_verbose) if dumpfile_flags['dump_avgbals']: trade_stats(sess_id, traders, avg_bals, time, exchange.publish_lob(time, lobframes, lob_verbose)) # traders respond to whatever happened lob = exchange.publish_lob(time, lobframes, lob_verbose) any_record_frame = False for t in traders: # NB respond just updates trader's internal variables # doesn't alter the LOB, so processing each trader in # sequence (rather than random/shuffle) isn't a problem record_frame = traders[t].respond(time, lob, trade, respond_verbose) if record_frame: any_record_frame = True # log all the PRSH/PRDE/ZIPSH strategy info for this timestep? if any_record_frame and dumpfile_flags['dump_strats']: # print one more frame to strategy dumpfile dump_strats_frame(time, strat_dump, traders) # record that we've written this frame frames_done.add(int(time)) time = time + timestep # session has ended # write trade_stats for this session (NB could use this to write end-of-session summary only) if dumpfile_flags['dump_avgbals']: trade_stats(sess_id, traders, avg_bals, time, exchange.publish_lob(time, lobframes, lob_verbose)) avg_bals.close() if dumpfile_flags['dump_blotters']: # record the blotter for each trader blotter_dump(sess_id, traders) if dumpfile_flags['dump_strats']: strat_dump.close() if dumpfile_flags['dump_lobs']: lobframes.close() ############################# # # Below here is where we set up and run a whole series of experiments if __name__ == "__main__": price_offset_filename = 'offset_BTC_USD_20250211.csv' # if called from the command line with one argument, the first argument is the price offset filename if len(sys.argv) > 1: price_offset_filename = sys.argv[1] # set up common parameters for all market sessions # 1000 days is often good, but 3*365=1095, so may as well go for three years. n_days = 1 hours_in_a_day = 24 # how many hours the exchange operates for in a working day (e.g. NYSE = 7.5) start_time = 0.0 end_time = 60.0 * 60.0 * hours_in_a_day * n_days duration = end_time - start_time def schedule_offsetfn_read_file(filename, col_t, col_p, scale_factor=75): """ Read in a CSV data-file for the supply/demand schedule time-varying price-offset value :param filename: the CSV file to read :param col_t: column in the CSV that has the time data :param col_p: column in the CSV that has the price data :param scale_factor: multiplier on prices :return: on offset value event-list: one item for each change in offset value -- each item is percentage time elapsed, followed by the new offset value at that time """ vrbs = True # does two passes through the file # assumes data file is all for one date, sorted in time order, in correct format, etc. etc. rwd_csv = csv.reader(open(filename, 'r')) # first pass: get time & price events, find out how long session is, get min & max price minprice = None maxprice = None firsttimeobj = None timesincestart = 0 priceevents = [] first_row_is_header = True this_is_first_row = True this_is_first_data_row = True first_date = None for line in rwd_csv: if vrbs: print(line) if this_is_first_row and first_row_is_header: this_is_first_row = False this_is_first_data_row = True continue row_date = line[col_t][:10] if this_is_first_data_row: first_date = row_date this_is_first_data_row = False if row_date != first_date: continue time = line[col_t][11:19] if firsttimeobj is None: firsttimeobj = datetime.strptime(time, '%H:%M:%S') timeobj = datetime.strptime(time, '%H:%M:%S') price_str = line[col_p] # delete any commas so 1,000,000 becomes 1000000 price_str_no_commas = price_str.replace(',', '') price = float(price_str_no_commas) if minprice is None or price < minprice: minprice = price if maxprice is None or price > maxprice: maxprice = price timesincestart = (timeobj - firsttimeobj).total_seconds() priceevents.append([timesincestart, price]) if vrbs: print(row_date, time, timesincestart, price) # second pass: normalise times to fractions of entire time-series duration # & normalise price range pricerange = maxprice - minprice endtime = float(timesincestart) offsetfn_eventlist = [] for event in priceevents: # normalise price normld_price = (event[1] - minprice) / pricerange # clip normld_price = min(normld_price, 1.0) normld_price = max(0.0, normld_price) # scale & convert to integer cents price = int(round(normld_price * scale_factor)) normld_event = [event[0] / endtime, price] if vrbs: print(normld_event) offsetfn_eventlist.append(normld_event) return offsetfn_eventlist def schedule_offsetfn_from_eventlist(time, params): """ Returns a price offset-value for the current time, by reading from an offset event-list. :param time: the current time :param params: a list of parameter values... params[1] is the final time (the end-time) of the current session. params[2] is the offset event-list: one item for each change in offset value -- each item is percentage time elapsed, followed by the new offset value at that time :return: integer price offset value """ final_time = float(params[0]) offset_events = params[1] # this is quite inefficient: on every call it walks the event-list percent_elapsed = time/final_time offset = None for event in offset_events: offset = event[1] if percent_elapsed < event[0]: break return offset def schedule_offsetfn_increasing_sinusoid(t, params): """ Returns sinusoidal time-dependent price-offset, steadily increasing in frequency & amplitude :param t: time :param params: set of parameters for the offsetfn: this is empty-set for this offsetfn but nonempty in others :return: the time-dependent price offset at time t """ if params is None: # this test of params is here only to prevent PyCharm from warning about unused parameters pass scale = -7500 multiplier = 7500000 # determines rate of increase of frequency and amplitude offset = ((scale * t) / multiplier) * (1 + math.sin((t*t)/(multiplier * math.pi))) return int(round(offset, 0)) # Here is an example of how to use the offset function # # range1 = (10, 190, (schedule_offsetfn, args)) # args is the list of arguments to the function # range2 = (200, 300, (schedule_offsetfn, args)) # Here is an example of how to switch from range1 to range2 and then back to range1, # introducing two "market shocks" # -- here the timings of the shocks are at 1/3 and 2/3 into the duration of the session. # # supply_schedule = [ {'from':start_time, 'to':duration/3, 'ranges':[range1], 'stepmode':'fixed'}, # {'from':duration/3, 'to':2*duration/3, 'ranges':[range2], 'stepmode':'fixed'}, # {'from':2*duration/3, 'to':end_time, 'ranges':[range1], 'stepmode':'fixed'} # ] offsetfn_events = None if price_offset_filename is not None: offsetfn_events = schedule_offsetfn_read_file(price_offset_filename, 0, 1) # supply schedule (defines the supply curve) range1 = (75, 110, (schedule_offsetfn_from_eventlist, [[end_time, offsetfn_events]])) supply_schedule = [{'from': start_time, 'to': end_time, 'ranges': [range1], 'stepmode': 'random'}] # demand schedule (defines the demand curve) range2 = (125, 90, (schedule_offsetfn_from_eventlist, [[end_time, offsetfn_events]])) demand_schedule = [{'from': start_time, 'to': end_time, 'ranges': [range2], 'stepmode': 'random'}] # new customer orders arrive at each trader approx once every order_interval seconds order_interval = 10 # order schedule wraps up the supply/demand schedules and details of how customer orders/assignments are issued order_sched = {'sup': supply_schedule, 'dem': demand_schedule, 'interval': order_interval, 'timemode': 'drip-poisson'} # now run a sequence of trials, one session per trial # if verbose = True, print a running commentary describing what's going on. verbose = False # n_trials is how many trials (i.e. market sessions) to run in total n_trials = 1 # n_recorded is how many trials (i.e. market sessions) to write full data-files for n_trials_recorded = 5 trial = 1 while trial < (n_trials+1): # create unique i.d. string for this trial trial_id = 'bse_d%03d_i%02d_%04d' % (n_days, order_interval, trial) # buyer_spec specifies the strategies played by buyers, and for each strategy how many such buyers to create buyers_spec = [('SHVR', 5), ('GVWY', 5), ('ZIC', 2), ('ZIP', 13)] # ('PRZI', 5, {'s_min': -1.0, 's_max': +1.0})] # seller_spec specifies the strategies played by sellers, and for each strategy how many such sellers to create sellers_spec = buyers_spec # proptraders_spec specifies strategies played by proprietary-traders, and how many of each proptraders_spec = [('PT1', 1, {'bid_percent': 0.95, 'ask_delta': 7}), ('PT2', 1, {'n_past_trades': 25})] # trader_spec wraps up the specifications for the buyers, sellers, and proptraders traders_spec = {'sellers': sellers_spec, 'buyers': buyers_spec, 'proptraders': proptraders_spec} if trial > n_trials_recorded: # switch off recording of detailed data-files dump_flags = {'dump_blotters': False, 'dump_lobs': False, 'dump_strats': False, 'dump_avgbals': False, 'dump_tape': False} else: # we're still recording all the required data-files dump_flags = {'dump_blotters': True, 'dump_lobs': False, 'dump_strats': True, 'dump_avgbals': True, 'dump_tape': True} # simulate the market session market_session(trial_id, start_time, end_time, traders_spec, order_sched, dump_flags, verbose) trial = trial + 1 # The code in comments below here is for illustration, in case you want to do an exhaustive sweep of all possible # combinations of some set of trading strategies: if its of no interest, it can be deleted. # # run a sequence of trials that exhaustively varies the ratio of four trader types # NB this has weakness of symmetric proportions on buyers/sellers -- combinatorics of varying that are quite nasty # # n_trader_types = 4 # equal_ratio_n = 4 # n_trials_per_ratio = 50 # # n_traders = n_trader_types * equal_ratio_n # # fname = 'balances_%03d.csv' % equal_ratio_n # # tdump = open(fname, 'w') # # min_n = 1 # # trialnumber = 1 # trdr_1_n = min_n # while trdr_1_n <= n_traders: # trdr_2_n = min_n # while trdr_2_n <= n_traders - trdr_1_n: # trdr_3_n = min_n # while trdr_3_n <= n_traders - (trdr_1_n + trdr_2_n): # trdr_4_n = n_traders - (trdr_1_n + trdr_2_n + trdr_3_n) # if trdr_4_n >= min_n: # buyers_spec = [('GVWY', trdr_1_n), ('SHVR', trdr_2_n), # ('ZIC', trdr_3_n), ('ZIP', trdr_4_n)] # sellers_spec = buyers_spec # traders_spec = {'sellers': sellers_spec, 'buyers': buyers_spec} # # print buyers_spec # trial = 1 # while trial <= n_trials_per_ratio: # trial_id = 'trial%07d' % trialnumber # market_session(trial_id, start_time, end_time, traders_spec, # order_sched, tdump, False, True) # tdump.flush() # trial = trial + 1 # trialnumber = trialnumber + 1 # trdr_3_n += 1 # trdr_2_n += 1 # trdr_1_n += 1 # tdump.close() # # print(trialnumber) ================================================ FILE: BSE_VernonSmith1962_demo.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" }, "tags": [] }, "source": [ "# Simple BSE demo/walkthrough\n", "Dave Cliff, University of Bristol, October 2022\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## BSE System Architecture\n", "\n", "The figure below shows a schematic illustration of the overall architecture of the *Bristol Stock Exchange* (BSE), a simulation of a contemporary fully electronic financial exchange with automated traders." ] }, { "attachments": { "8626ec3c-5c9c-4ae1-821a-31ee98123508.png": { "image/png": "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\nHm+Ton0A7OioGFP98ETvPm5Y5Cum2FxTA73+RrMwD/zDJvv67/b9zxkoVP82/wtGFg4dGvBaHgAA\nAFZlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA5KGAAcAAAASAAAARKACAAQAAAABAAAC\nnKADAAQAAAABAAACgAAAAABBU0NJSQAAAFNjcmVlbnNob3RF2l2VAAAB1mlUWHRYTUw6Y29tLmFk\nb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0i\nWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3Jn\nLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjph\nYm91dD0iIgogICAgICAgICAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYv\nMS4wLyI+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj42NDA8L2V4aWY6UGl4ZWxZRGlt\nZW5zaW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+NjY4PC9leGlmOlBpeGVsWERp\nbWVuc2lvbj4KICAgICAgICAgPGV4aWY6VXNlckNvbW1lbnQ+U2NyZWVuc2hvdDwvZXhpZjpVc2Vy\nQ29tbWVudD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1l\ndGE+CsCaQrwAAEAASURBVHgB7L0HYF7VfTb+vEOvtiwP2fK2sQ02YDYESCgQQiAkbSgZbVZJ06Rp\nku6m/bf9Jx3p/NKvX/q1SdOZNINmkCZpRgkQEkLY4IABgwfe25Zkbend3/P8zr3SKyFbNshGkn9H\neu8994zfOee555773N9ZiTIN3DgCjoAj4Ag4Ao6AI+AIOAInCYHkSZLrYh0BR8ARcAQcAUfAEXAE\nHAFDwAmnVwRHwBFwBBwBR8ARcAQcgZOKgBPOkwqvC3cEHAFHwBFwBBwBR8ARcMLpdcARcAQcAUfA\nEXAEHAFH4KQi4ITzpMLrwh0BR8ARcAQcAUfAEXAEnHB6HXAEHAFHwBFwBBwBR8AROKkIOOE8qfC6\ncEfAEXAEHAFHwBFwBBwBJ5xeBxwBR8ARcAQcAUfAEXAETioCTjhPKrwu3BFwBBwBR8ARcAQcAUfA\nCafXAUfAEXAEHAFHwBFwBByBk4qAE86TCq8LdwQcAUfAEXAEHAFHwBFwwul1wBFwBBwBR8ARcAQc\nAUfgpCLghPOkwuvCHQFHwBFwBBwBR8ARcASccHodcAQcAUfAEXAEHAFHwBE4qQg44Typ8LpwR8AR\ncAQcAUfAEXAEHAEnnF4HHAFHwBFwBBwBR8ARcAROKgJOOE8qvC7cEXAEHAFHwBFwBBwBR8AJp9cB\nR8ARcAQcAUfAEXAEHIGTioATzpMKrwt3BBwBR8ARcAQcAUfAEUgfE4LyIL3z/JWARBxyyEKHcuxI\nq+zir/RPMHzsV04F90QxclMY+pcVrlKW7IonE8ktx3yY1xY0Di9//iTDPBSPP/NWenE4uUtGJI+2\nkfY43Im6V8p7sTIq05wIGZIXy5mM+YvzVlnuyjyfqPtkLGNleSZj/vwe6A69+DZgIvCbCBkqQyxn\nMtazyZ6/GDvlcyLwmwgZlXk5HfJ3OpRxEj8HQ9wqrnfiSik+DTX8VZFalcF/tjL8q7xVCv4SzDEJ\nZynXiXzvHiZJ4jleouWY3JHwJQrMkggmyV45E84JEdfIbRThLBspZQIKw1Lq8VVhUyUBkEAxWTJu\nmRQADFtm/GKqhGQpDbkly4pXRCmRpF91iG9EWZJSlKCzG0fAEXAEHAFHwBFwBE5zBKisS5CfGTOi\ngjBJblYs1yBdtwTIzCW/EnMirTKYxiN/x4/lMQlnuZRHaXA/MomuKOGjCRZJVNZFMEU4iySIIp26\nrgpn5CijKBeaQDwTYtnxT84R4bRiimwKFDLPFFlliT/yToqWBALAUykRyGQikVNsEs8MqWhIP8H0\nxFotDUt14kCzxPzgCDgCjoAj4Ag4Ao7AFEPAGBspkXUSk7pJsVcu1aNY3YR0crau+BM/E5+auMId\nk3BKfZgqiyTmx1WrGuE08ijiSWZoXezSeooMKsckmSSiCWoh5RoT0gSJZYJpSKMZesgpSURRUWI0\nksoDCacAUMDIJI2gktiK3JbTlkrK4lgKdFNwhVfaw/Hi+H52BBwBR8ARcAQcAUfgdEJAvcRAQYyK\n3EhHMqxENZLUdoozSZVnWj35izpNEOk8NuFk4uzMNgKodI9tVADmqnL8puVUsUT46C8yWBJppFNS\nYcUIpe3UWeHSDClFLlW8JJFS+sojaEJpVfyYOMqL12UjsIqTJDlW8ALdREIZgGTWZPM4fLYLPzgC\njoAj4Ag4Ao6AI3D6IUDulKCSTvzKaBaVhQnxK7kbDSV3Ep+SG/8nyhybcCoVI3gki0OJDlnoqRwF\nI2oqjehQF7Y0i4xbJoVMiPxFQcumzVRh0nQinS2LHJJVKwALl7TxnIoa5JV0zbEFUu2WSCRNq8kk\nNZYTRRJTnssJjhO1cHTX2E1y2lJK+SR4SttIbZTROCN2OXZZFG/YDJdx2H0sN8V4se7jpXeisl9s\nPpTOWHkZy+1oYY/mPhEyKmVPxjJO9vz5PdAdenmfU78Hfg/Ga7tOhzpyOpRRNT0u53j3vDKs7OOF\nj+UeT9ixZBtJYu6k3QxGVFN/UvgpdXEn9UjzEIV46adxCKf68WV4rCz/ULrDGQkZZUgRRbFiM4yk\nqCWdMzh8ZACP/GQLsoPSXqZRlUyiubkRM2fV4Iyl81BPXqlxnomSjtXoHyjiyQ07sWnzAezesw8z\nZzbi/POW47yzl2JWPQkrCe1AFrj3oY3o788jbby4hHyJE4g4dFQ4nbNqCVavnEm7NKkycd7C1fDx\nRNxPJOyJpjkRsidCxtHyPRGyJ4uMyV7GyZ6/ibiPk72Mkz1/fg90h0aaE8HkRMIqlbHCj+V2tLAn\n6j4RsidCxtHyPRGyJ4uMyV7GCcyfMUoRSyNNFCyVn5R8ctMcHAaQ1tPq+9Huj/JzYuaYhFPJWJIa\nZzmO3IRN1hHZVMCINUfEM5GsQqlYjf/4/Lfxkf/9daQZxpS5KhMFt85rxC03vxof/b2fQ30ma277\nDw3ir//u6/jyf/0APQNRFzm1po21afzhh9+C3/jlG1BIJfH9dZvxll/6axTznNCvTDIL5KvIMwvK\nxe9+6E342O/eQiIcJhYpd24cAUfAEXAEHAFHwBE4PREg+eLcGBKjqPhkSzbfRddhxoxxOs3LGY/8\nnQCAxyScQY4YMH/jGtE7mTisMkq2rHIUyZ2pcvzx/euNj77tbVfgotVrMNjfjwce24zv0/3v/+m/\n8YrLluPm115K8pjCH33s3/HVb96PhUvn47d+4dVYungWtmzcg+/f+Riy3YPEqgrJVA6PPPQ0yWwZ\nr7h8JeNeiEwxgQLVwIVEFplUATdc8wp2w4tsxvmyTPrBEXAEHAFHwBFwBByB0xQBMklp/IxQir9p\nHCeHKupMlaCN6ZxgZI5JOMV1pS3UOebBY6dvOTYv6+6PAoviiTSXkykcPNyN/QcGkKHjh95zCy48\ncxnLVcKb2vvw1nf/JZ5c/zy27tlJWnghNu88gLvvfwLV9Rn8zV//Kq5/5Znsfh9AkWT0l95xPeoy\nBCWZQ7a/gJ2bD9tySbf8zGvwwV94JarZfa/ZViXNbOeyTlpKqczu9GPnf+xSuasj4Ag4Ao6AI+AI\nOALTCQExNiOXMXUjUUuYUo5DGjWs0QorEioCOnHmmITTqK8x4DhXx0hYQawLXZYQXkQvkaqiorOM\n7bsOoK2nF3W1wMozWjmmsp/hS8iQPKqoUqLOnz2DIBSxa9d+jt/MIVVTjSuvWEFNZa8peVFVxsK5\n1GxqjU5OIDrSNYgdu/Yhw/Ga55zVSlLKrveSJhYxXeWFYzk1KDaQzZCnY5TAvRwBR8ARcAQcAUfA\nEZj+CAx1p4eiiieNYEkjLiYGjmMSzrD456hMHCVdaTKVv3jyUFmMWQNQOZM8RTXp9h37cbizB5dc\nsIpEkmM6uTtQqViHL33p29i8cTsWz2vCtVdewjgJzJjRhKpUGj09A7j99rtx61uvQZrd46kUtZZC\nhSS2RIJ6uLuALdv2YmHrTLTOaSbBrGYGMtSqatgrF1jiGM9yUZOFpCJ24wg4Ao6AI+AIOAKOwOmN\nwAv4UOxgXdSByR0f8zsxHI9JOCUqnhEf5+dY4keHSXIsZZGaRm2ctGnLThQ4sSfHVYtu++q93DKz\nG+ufeB7fufspzlRvwJ/80a2YM7uRZLGEs89bifPOX4QH7n8eH/uzL2P3jgP4xXe+BssXN5k2tEgS\nK7Xm89sOoqufWtLBIm77yj2Yw5nrpgAmc1+7dglefc0FXDVf+tMXcPdjFcP9HAFHwBFwBBwBR8AR\nOM0QqCSboxndS4diXMJ5okmI2lHVGJiqTiSbBa65+dQzG432rVu/BU/81hbTVKo40j9eunopXnHh\nmVwIiS7sLm/MJPDxj30If/iRf8aPHtiIT3zqu7jzB+vw8T+7FVddcTbSVRyTyYlCz2x4nmvlA/s4\nPvQT//htW/hdMkU63//e1+BVV61FesJLSOFuHAFHwBFwBBwBR8ARcASOGwHuYqTO8LFNKbcdxbYn\nOF9pvL3UORyTZFG6TC31LqMxlGUOzCyxi3sgW401F70dWc4s//X3vxHLzpiJfDbP7vD9+NLXHsb+\n/Ufw+msvxOf++TdQV6ep+gV2mSfR05eiNvR+/MM/f4sTitqwqLUBX/rsR3DZ2lbrPr/5XX+FO+97\nCh/8lRtww3UXIMXuc2lVk8k0Vq5owfx59VyCiWNIozyNXUp3dQQcAUfAEXAEHAFH4PRCwNhapMgs\nlOuRbF7LIY8rOOlaW47bopjSGk6YmTD9n60TOpSxYClz7GaZ4yh3HzjC8ZslrFo2E+988zVYvKBB\njJTaySosWLwYv////ysefvwZ9GcHUNtA4koOnGJXeHNDCb/CJZHOWrUQH/y9f+QEoXbceefDuOz8\nN+NwRxd27W+n/hS4+cZX4lWXLkOyQNqr7ZoIVjkxwDlJWg6JkA7la8Jwc0GOgCPgCDgCjoAj4Ag4\nAseJgPjahBvjdzxQ10jSl8STz2zhWE5g9qw6ah2bkJIWslCi5jTPCT/NqEpTN6oA2u5S/JD+ZdsH\nXfuj9+LqV56FSy5dY5Pg9+zZT+1mGTv27MGR3i5kOE9o7dkrSTTzpJYFFLnmZrE0YGNPk7YIKAW6\ncQQcAUfAEXAEHAFHwBF42RCYUMIpajf8k40aTnatr1u/ycjiWk4GSlYXOaaTM4dSGWSzNfjWNx/C\nwEAJ519EQpmsxaH2EnKFGtprkC9wNnq6Dvs6+rBtzwED6ezVKznwM8lZ711ob+/FmtVLUCvWSaZa\n5kz2RHUeiXRYQd9GC5C8unEEHAFHwBFwBBwBR8ARePkQmLAudWOatmYRCxMNC9W26gPZLDZvPWhL\nirYuWIK9B/voncO+PUfwtdsfxde//SAammrw8++8gfum78DH//Y/8PrXXYo3vO5qjudswKGOTvzv\nT9+G9eu3cumkelz7qgvIY9PYuKkdHAaKxUuW4eChAVSTV5LKknTmbNzm7OZarvFJwsv1k7QJvRtH\nwBFwBBwBR8ARcAQcgZcHgQmbNKQtibS7j8ZL2iQdKTgLGew+eAQ/y73On3jmAGpJCjNVmrVeQj4a\nXjm3tRG/8Zu34H2/8Dp8778fwa/82iegrdPruGd6Y0M12rr67HphSz0+8afvxxtvvIBazwTe9J6P\n47vff9r2ZZ/Bhd+VapbjQrUzU0tjPT73T7+LV162hGSThFNd624cAUfAEXAEHIHTBAG9gmPjKpcY\nCT9XImB1JKocU2rSEFmduKYpOsNRE4bKqKmrwjVcymj+vAVG/BLc67yKC7jPmZPCmjOX46abrsWi\nRRzHyR2Cbrz+Yvz7pz6Er33rIeze22OLxp+3phFrz52Ht731Rqw6o4Vd8Vwwnt30l5/PSUI5Mk12\nmWvCUqC5Zc5SB+bMrMHypa3UqqbCQvGVCLvdEXAEHAFHwBGY5giUrV8xLuRIpYtxjJiRRoQjDuln\nR+BkITBxGk7LoRZFUi1mXzpJIVWetHMcJsdjFrRgJsdtihGWCoNckF1LGHG8JZdASlDjSapov0Qy\niTx3DOo40seJRCU0z6hHDbvGExikgCx5ZcqWTAJq2TXPSUa8FtnkoE+elbZkamvLLJdHot0eOoUZ\n+cDRwY0j4Ag4Ao6AIzBNEeB72IwY5UhWaVdOOCN8Tt+TVYGoakwtDadVaBHDuBbzrC7uMrek5Kzx\nJIkkSBo5jxzpjJYv0j7n+tHwoHBaJF594snEIDWgvCJhTSRIMotkqySmSW6VWeKYzBQJrMJr6aOE\n1Jsit9HkoDKvk5pAJOJJsmnjN9XP7sYRcAQcAUfAEThdENCr2N6NEaOITkPFH3095OEWR+DkIDCB\nk4ZUu1WD9ZO2UtsM0Y2azKLIZDRxJykSKGLJpY1EEsuRZtKWUKKPpGgNTvanmz8KJKUpEkZ9rDGO\nFnY3L8kxpWUYNxo0mExbciknwUWXdI6vZXXjCDgCjoAj4AicHgjYC/KoRdWbUsZfjQEHP558BCaM\ncIau9ED1Yo1jyQinutfpy27uJL+2bGH2As9cnF2Lc4aRn3FB6a44IqRF+pOMJtRlrjU6RTSp3TQS\nK1pqpFTpjS6CSKYeIcqI43AsKYXFifjZEXAEHAFHwBGY3gjoNVjJJvkejhU/07vgXrrJisBotvYi\n8ylCKBUkjWkstSWSflyKneRPW02qe7sYayXtIWAcckmLIjJIN5HSIWLImejacYhT3flTBIURQZVG\nU+5yolx1GUR+Q3FTccc+wymopacIbhwBR8ARcAQcgemNgF571OuQYPI9zF6/NB2SnCvBt+r0LriX\nblIjMHGEc6iY0mayQ531ujOXRVd+EDmtj0k3dYcP1Xc9EdGF9lynWpKXOotUyujBUJcAz0YqeTbC\nKV/pRRWuknBGMugro0lIcVxJ8qU4hYobR8ARcAQcgemOgN6SeiNqPkMVt3yeP7MFNQX1GtInvGDD\n65Fh9H504wicCgQmiHCK3EUTgGgTP8yxbj93aBce2LAeg0yllFaXOPdIN/IYimb13mq7yKYi6hGh\noT2mncGB8k1zqkAW0I7he03Xiidpwx30FcmYQlSh3DgCjoAj4Ag4AqcFAiSXab4a6/IJvPX612FO\nps5GolkHob0Q+c7Uhb1bTwtEvJAvMwITRDhVChLKiPCJ+hU5xjKXKqGfM9L7uVxmlmtvpjhuM1ob\n3uihYoW1wjRlSCbSSkZ0knPSI3cLGPnHpJKd6/bkBAqqrvUR3QV8oJSP+DiUoLn5wRFwBBwBR8AR\nmL4I6P2XUVcjFTk5vo/LWilG8yDMyDe8ISMHPzkCJx2BCSSc+mSyzyY7pvh1Fbq9VcGTSLFPu1Si\nFpTnF1ZzUUs9GCG+zgkN8ORlKR4bylgavxlorbyCFEVRCuoyl/ZUxDN+lHSOJFZY6DiGCdLCuFPr\ndhhKl4H1BajhAGbiBza6POopTl35DtKNEMdfkzY8IB5TM5TLo0pzD0fAEXAEHAFH4HgQsPciX4o5\nvnuKmtPA9a9t0u1Q5PBOil5NQ65ucQROJgITRjiHKBMtmkCe0qLvnGFe0JJGNEl+aYkURkpJWqIY\nVu9ljymk6GnQYpY03oReCiKeZtQtvpBMPlWaiFSkJlUShrWnSWo/5a8fY4ncKcDRjGSaLlV5Vdpc\niF6EUz8mrK0xNSY01q1qu8zxjMppoZiuZusbXWUmElwT1IqgyVDHytN4Cbi/I+AIOAKOgCMwBgJ6\ntdj7Ty8ZvX/4MuKotvDKsZeRHfwVNAZ27nTyEJgwwjk6i6rwgU/pGKia8brRAe06ULDgFQjiyGAk\nanxgTENoUvmwGHnjg0TxYbklpmFi6GAJB1JYKXmkzMorhdJseP1ELEk8jaGGc8i9/ILMQE55eQyj\nLCi/Wmc07OUeSLHSsOwxlZjAHkOMezkCjoAj4Ag4AieMQHjPhGjhfTRShNz05nPjCJwqBE4a4Txq\nAWLNZhzAWGK4iB8AETE9CLYskgib7HaUq0hfRPzkQaMvuXgM57CelB6UbQ/U6DQtVsWBckI8yiXR\nDLPmLXoUX2SRueJuR/xOrIh4dGvF6FPGjctA+UPaUSYqUhuV4eiS3McRcAQcAUfAEXAEHIGpjcDx\nsaeTUkajgi+QLP4V+0jTaLsSmUsgfZpkZOt1MmDQGWqNT4YS6VS4iMAFijd8/YKEXuBAKSSARhRJ\nCkV2gyhpXKn1JPFNqms9kv+C6KMc1I2vsaAKbppOks6QX7q7cQQcAUfAEXAEHAFH4DRC4NQTzphv\nRcStLFImDaS560B6ZteyB02mhWEvelEKQs60E42T7jIeG5kohPEoXGaeQy4VT13uUVd26Isf/5aK\nSWp8pXY24qy+FO2KSjEcJ8q0JM8IMN2ivB9NqHJghFMUkxcinBTB/IqwKnKgnnI0v6MJcndHwBFw\nBBwBR8ARcASmAQKnmHCKbIkcBiI5hJ9ImVia+ZHY2fboIpbaRz1BGlnkjkUMQzJYJtvT7gm2ZCcD\nlkspVJWryd1INrmFZcm6vRlWk5QsAdN7DiV1LIuyoIlK6UQVSgMpdB7qQ1VDArWzq4woinKK/EaC\njyHK9LChTCKYlnfjnLSS0DL/nNZkWtRjCHEvR8ARcAQcAUfAEXAEpgUCQTX4MhZFtDCQTXaLJ9Io\nc3p5thPYuu4Atj11EIM9oqEZJFPskhYTpQZTC8inST7TuVp07c5h87rd2LX5gHV7a3Y6O92lUDxB\nI00rO/GrKIBCDm7rxWf+7Hu4/3sbrCtdLLhIUhv2eh9ftIBNkQin8rVIFLgQqUi08m/mZYc9yoef\nHAFHwBFwBBwBR8AROPkInGINZ9DyqVhB+1hZwBS4TCcJZxkPfe85PHnP80B1Ape+7ly88sazGKGA\nFJc/KhWLqKIGEuxG3/lUB777hfuR6y9ixrxavO3D1yA1W2lQE0nyaNpI06YqtRemWJk6A5sWs1jM\nIVPOYO+2g0AeWNQ61/Supm2ltpJcd1xRlpoCcoul3Vu7qW0tYMEq7vKQLpA0S0tK2KWpFS0eJ1sj\n8ugXjoAj4Ag4Ao6AI+AITEEETrGqTQQrkCweIyMCps5qMi+SvkK+ZGSv2Meu9N4S2nd2oJjlmEd2\nkasbPSUtKNf4zPUW8cidTyN3mJrHrhKq+FdfPYMy1BVPY13fIpG8ksMxf2J9aS5Mz65ujhHVhgz7\ndx0yreSShYvpxwXn2Q0OEUUb4zlanvKu37B7ivntP1LEd267Dz/+7nrk+riEEwuQKOfsHEqsdN04\nAo6AI+AIOAKOgCMwvRE4xYRTYIoAqhtd7IzGCKi6wLmIu7R/1Aq27evBjNlNmDlzJtp2tyM/mCUJ\nZBwtJs/JPElqIJ/8wTbse74NrYvmgRwUC5bONg1oVZFd7aU0f3Xo319G27N9yLdxnGeuDoMHU8h1\nUAbHfSb5y3WRFLZxVOVAA/p2ldGxsR8Fug32pXHkwCDStQk0zauxoaXVg4y/N43+A8wp5Rv5LaRR\nPJTE4WezOLSRezr0VHPxec6aL1Uh31mNrh2D6N03iLpUAxJ9Nci1V9E/jN8UIdafG0fAEXAEHAFH\nwBFwBKY7Aqe8S926kdlJLT2kxlpqGaKE2BuJZ4LjNHsPkiBSGzjv/NnsXudYzqe3YnAwR/1lxrSL\n0m4eOdCHR+/ahJZFc7BgyRzsPbgfLUtmo5TMIUXC130wj/u+8xD2PHsYxcE8iWMVzrxgIcd5HsLy\n81px5ZvOZHJp3Pmlx9C9L4fFZ8zDs49uY3d3Ga9+x4WYNW8W+nsGKLMRqMmh2JvCg3esx/oHd2Hu\nmTPwlvddg97DfXjsnnXY/EgbtbLMKJWVTS01uO4tF2DRmnn4j3/8H/Tv5Lqdgwlse3YPPvN/DqNh\nfgJv+83rOFSAZSWBLrHbPalJRW4cAUfAEXAEHAFHwBGYxgi8DBpO6Thj3V5EtrRdkLqtSTz3Rl3Z\nS5fPxcxFKVsK6dD+bhSsO5tazkHgoe9uQHawgEuuWYNsLsvJOEDrghnIcx2jHLvi7/vmU9j00C4S\nxyZc9urzUN9Yi5/84Hm07exGTW0S6WqOFyUX3L/pMA5v7cST927BvKVzsOxCpjmnmpOWujHQN4jW\nxfNQImG8/1vP4eE7NqGehPLKG9ZgoLuIO774GJ74/m4sPXMOrn/TFVh78Vk4xPGaD39vE4tSwprz\nV6KmnmNNWda1l6/E2quW4PxXrmGPf5rl5Gx7jlWV1lb+bhwBR8ARcAQcAUfAEZjOCJxyDaepAoko\n6ZbhGlFO26KSrBL7drdZkAVLZqC2QM0hzd7t7Vhx6VKUcoPY9tgePPvwLqy57AwsXbEQT/zoWTTN\nTKN2VhFUkGLjY7ux+fGdOGPtPLzpl65GpraES69bhtv+5l60HTiChdSEFjkZaKCnjL79nIBEBC6+\n+gxc8/OvQL5mkBrSEtZ9m9rObAIzalvx7c8/gq3rd2PleQtw460Xo5Yk8r5vPYtdGw7gkqtX4zU/\nf57ld8niFjx279NGhMscBHr5tWdj68N7kKzpxfW3XAzMzHKeE4l2iolLs8kJUAUS7JTGhLpxBBwB\nR8ARcAQcAUdgGiNwygmnaTdtpra61dmVThPWtuSEoYEcu8MHqIEEmhfWoapAOkoS2b6rF+lsCj1H\nOIP9zs3IkCVeddN5nLGeQ/uhTrQumYlMI7vcOX7zqQe2kkSmcfE1q1FuymGwxG725hoUkwUkMym0\nzJ+NNLWle7bvM81oyxkzcdkbzkW2hl35nAmv5Zb272pDmbzw0XueQq7Qi0tfuRw/dcsFSDSRJHaW\nseHh55FJZ9BU04xn7t2FtoOd2PTUbmTq0lh72QorV2dnO/r7B9DUmkGpgeuIkmgmbcklanJJSIvR\nQvI2GDRm3dO4onnRHAFHwBFwBBwBR+D0ReCUE84hqG3Wt3bc4XhGaflIunqPDKC3sw8zF9QDdTnU\ncXJQzYxqTuzJInu4gPX3b0LHvm5cceNaEtIq7Hm6jRN8imia00hNZi0JXh4Hd3Wirr4Gi5YvRj7d\nhxQn+HQcyKK3K4tMcwoNM+qYhTIO7j7CMZ/AmsuXcimlNPJJEkIS0WSihoSz3UhjIdeNdDqJ8y9a\nQ7LJ5edJlDsPD6CH+dFcp3vveoST2xOoaUxg3uJGXHvFhVixdiHjDqD7cA8GBwax7MxW5Ks4DoAa\nTZBAi1uW2PWvbTvDjHfvUh+qE25xBBwBR8ARcAQcgWmJwCknnCJcpJj8Gcc0UKXnLPHQ215CT+cA\n1qxeAXCGeIrbC81dMIsazg48//huPMSJQguXz8LF160iQezH3j37Lf5cThwqp1M40tFGrWcJs1s5\nxYictZ8LtadI8jY+uJMLyGex6pyFSFSVkSUx7d7dj2Qt19lc3cJubi1VRArIGeQDXf3oOpTFvGWz\ncC6J5ve//QDuuftx3LL2lVrJHe2HuSo9e/qvvOEcXHnTWiQauCsRuWMh2UciWSCnHORMdJbjUBFZ\nLufUuqQFiUzeCGqqzLAEoJSg+pSLwGvikjHQcLCy+MERcAQcAUfAEXAEHIHphgB1fKfQiGXGPyVL\nkmdbn4uFUuPXfSjPGd/sTm9tIjFklzvn3Mzj2Mj+3ix+8O3HkGaX+BU3nIvMLMbjWkj7d3SYvNbl\n88gByQLzQVsoAlsm6csUMmjf2oP1P9xCRggsXDoXuUSWmtAcujp60Ty3nhOKUlQ+UrvJ+Kkk1/3c\nc8Bmj7csn4nzrjkLTYvqsXPzYWx4ZA/XnudkI5FD/neSmCbqk+iv7kQ21ccxmVwOiX/sPCcZTuHg\nvk6k2XU+s6kJmWINySwLw270Itf7tPGrzKrNzlfR3TgCjoAj4Ag4Ao6AIzCNETilhFN6zbAsuxim\nGJdOXBqJk2fKnLm9l2Mn05z50zKXBE0zuNnt3ji3WkEwmC1g1YULsPgcTvpJFTl7PMmu8pzWa6dG\ns4nhy2humskoSXaJ9+GZh3fjybt34VufeZSaU67jyXBz5jZQm1lAHwlsZ3sPZs9pRnU1t57kokva\nQ50DPXGYcWVmL+Mi8rO7cNVrV3NdTXD2+UYuRA/mbZa4MXZtOogtj25HnssqdTx9BI/+19PY99QB\nKkFJKrkTUl8uz0lO7Lrf1IUtjx3AYQ4FKLI8JRtKwPU6uVZngmFtwXhL0Q+OgCPgCDgCjoAj4AhM\nTwROeZe6YNSi72WyOI2DtGu5USu4m9rFTF0VFiwk2eMWliVUY86iBpuMU8XljK69+SKkGji5iESt\ns60XA9ksFqxoQnUV18rkBKKG2Q246LplWHf3NvzPv/8YKU4+WrpqIWbOmI32tjZqNGukf0R3Vx+y\nPXnMXtRErWkaOY7f1BqcpVySi87nqF2lZnU5iW16AKsvWcRZ6vvx3Lp9eObxTbjoyrW44nWr8dhd\nm/GNf3kokGZy47pZKdy88lJSV44HpTZ16eoF2MLZ9A/f/RTZKvDTH7gUzUvqbBxokv3qYTSn0e4A\ngh8dAUfAEXAEHIHjQSB6dw69RC0OX0QvcJdWx40jMDkQIPeTam9sU8ptR7HtCVKoLlNGjh1qDFdK\nzKcSeGDfFty1+Sl2MZNgaqFz/kmlKj2nxjKmOHBTiWsZoTIHQh58vpNd7AksOGMGStx3XOpL7ZPe\nvpMTfriv+pwl9RyrWWCcEor9KRzapXU101wAvoETgNgXT61hYYBaxR1d6DrSg+ZZjdR+NqOvL4t8\nNseF3DlhqLqEQW6F2bGrn133tWiYxRnsWqqI+UnmU5yU1I9BbpvZsrwBqRp2gFNmz+EsOg4NYAZn\nnDfPbkRhsITDu7txhKQ3XyygqbEejTNr0LKwkSRV+aaWk8sqHdzWzklGPahnGgtWzkKKyWsHzCQJ\nsw0lUNm9PSAKbhwBR8ARcASOG4H4xcF5A7GxoVrxm5pzBPRSk3KjkT1tt179OixM1/F9FocOZ71/\n/RU0EpPT6crIX1QBCuV6JJvXkvesIJ9KWQ+sKcYmsIKcWsJJLWKs2Rt9U8V7Y+6b0ErupJV25oNV\n5uQa0VVNtJE7mSV/VSSpJLAksrbUEuMrvOizosuunXwCVuq25+NIYhviB9cQ3iC3tC3eUAwGHcMo\nTpHa1xRvSDl66JVv7RxkZ5YxnKOUo3yNIcqdHAFHwBFwBByBE0RA7yy9y2iMcPJdY+8iuXG8l96R\n9q5kKCecQsnNURAw9hOoCnuOTz7hfFm61I9S9ogQ8nExFKT11IPD0Hyoytb/bh50iIijaU0tQPT8\niXyKmOqsYCSdIpq8VnR7JnWWSP5igqvwRlCVcEjcZMhN1wqf1PhLbcHJa23BWbRwgWhKmvwU3A60\nSLaCJI2YSoL8wsmPjoAj4Ag4Ao7Ai0cgeqdoEoMZqXIiEqrr+GUXPP3oCEwKBOLa+rJlJtYGVmob\njZnZ8xQIHz/TmD9jc5ZPW8PStJy65F7sEZGTRlNrWw4RSQsdyGCQSYJok3YUjg+nSCHDKLolJ1sk\ny6JG13IyshkTXYa2/NJfpDKUgfKYkSKXYrI0+KUprWeRWlhdj63ZVaoh5ZCeHx0BR8ARcAQcgWMh\nYG+kKIBe4XqHaDqu1lDRuzJch57AKJifHIFJgMDLTjhNi0ggRpJEISOto0hc3h4kPVBDxlSHXMeS\nBC/B5Y/C80W7gkT8Td3nJS3uGRmRUiOGvNZYUu3wIxJo/DKKY/Y4gs4ksPJSXMunNJyMoa78ON8m\ngenGbspCyAa7/xmZQ1mD0TlKJ3LxkyPgCDgCjoAjcEIImPIyHl6mtZy1JKB14ekFE955miOgeRJu\nHIHJhMDLTjhHgxETOT02InFVHJJSLFSRxHHquHbo4UNkYyf5gOkZS3A5JTPhORsWx2dQm2cOGYXV\nBc9ceXOY/I31UDKMBdY5NkN2ZYCOQ9dxAJ41xJRGIm0IgMLE8scKr8BuHAFHwBFwBByB40SgLH4p\nRkmlSkG716X14pGjvXn08uF1/PMXz3HC6sFOAQKThnDGRFNMztbE5Jdakiu/l/hA1abmc+H2epJL\ndWFHD5KeOmo6g46y8qGSf2xi97HcFObFuEfpH5V1xiS3kgHH+Thami8mH6PLWCl7vPQqw8o+XviJ\nyN9EyKjM93h5rgx7qspYmeZkzJ/fA92h8eu7woyF1VhuRwt7NPeJkFEpezLWs8mev6l+D/iOYc9e\nmSukVKe45nNhK9eZ1uotqgv8sXgl7XQyog7rnrhxBF5eBCYN4ayEIZFUdznHQJJsSrO5Zvl1qE8v\noYKTi6XzL2g4+VSRdNpH3SkhTMoh07aHWA1WZUM/Ivfmp5DDptI+VmM3lptiv1j38dI7UdkvNh9K\nJ87LRMiozHcst9KtMr0TdT8d8nc6lLHyvk/GOuL3QHdouF2Q/UQwOZGwR5M9ETIqZZ/ielbibnV8\n92lL5hI3Mnls42e5PF87NZ7SdNLYGLAoT6agCc5+dARebgQmJeFUA1Tis5OikjBRznA/dO5HXpzH\nXXyYXT5AYUtILouk8SrqVz/lRg0W07V2K04/asTsknYbU3PKM+YJOgKOgCPgCExjBBJaJpBjOEvl\nHArFfmo7+U4cGiWmd6TeTRH5nMY4eNGmHgKTknDauEzOABJ3sy7zkra6rCXhlItIJift0Bq0jQF0\ndWBrLrgZfdWZVeGPZaLwxwryAj/J1E/LJGnJJQWwzPAcy2OXx3hJK5obR8ARcAQcAUfgBBBQ71mZ\nE1htAxG+CJO2bCAn11KzafoXzmsIypgTEOpBHYFTgMAkJJwkbUYs+chwz3SusW40Lkk30bkSHyot\ni6SpQrKV7GELSJX4oOkhjGmf7WMUX4wJZvCMueExgyo+A8Zd5WHPJNFf2Yapr11R0LiyxsyPOzoC\njoAj4Ag4AkdHQLPPNdhMu/JJ21miAia8F+Uu1QuNv4ACDn6cVAhMQsKpZyWidfpc04Njn22VuMUT\nc+hZ5uz1IaN+BfmRAvIL0L7y+AAmXhB/KMKJWZSXIXYqi2WOToFwysWGzFSGO7EUPLQj4Ag4Ao6A\nIzAOAnrJVJjKy4l631WId6sjMBEITErCefwFE7nkkyaN6NBDpi88DqgWM+SXn7nrS9D0kCH4aPmV\nz+pov9HX7MCInEQvZRSb35VD6dPdWKd5+sERcAQcAUfAEXAEHIHTHoEpTjhF+kQsdR9l51IQRvw0\nrqXEsdRyI9W0xXFDCAt2IgxToitM4JLSaIZRo8FLApWWkteZpHSIgJqzHxwBR8ARcAQcAUfAETht\nEZjihFMEU/dOB2kxZUj4xPk4njN2kZ80ntpT3bwspGwvwiiaJi2J3Br71Gx5duHbNkdcpol/GtMZ\njaR5EQl4FEfAEXAEHAFHwBFwBKYXAnH/8NQtlTFIFkNjN63rXERTewxxbGdRk4i4dqcIIvc4L2ut\nJZFBnsOoy0BRT7Tw0mIODvSir/cI5TMDRjxJZk1cRGSj04nK9vCOgCPgCDgCjoAj4AhMNwSmtobT\nJgxxVrppFzU/PIeBgS7s2r4FhXyOpDLFpYvIAkVGqd2cN38B5rS0IpXmDkYvkhCKbA70deKX3/EW\npGpq8MWvfJtklkskDY0bZRUx4mujSKdbffHyOAKOgCPgCDgCjoAjcMIITG3CacWNlbRikEU8+KM7\n8W+f/gSXU8qTY3I9spJmrtfQXoWfffvb8XPvvJV+Be5Fq7XKgoZzaLjleApPJqFO+b27tyOb68G5\nq1chSeaqsaJhkhIDRNpOWk74ZngER8ARcAQcAUfAEXAEpiMCU5pwlmw3BY2WFFMME3me/MlP0Nvd\nixte93osXboS6XSG286G1TLPu+QyBgvbZlbezJhnHg9FlIZz944dyA70YdXK5VyIU1sijZYWS6x0\nd7sj4Ag4Ao6AI+AIOAKnJwJTmnAmqNHUckRl7bsutsgVcTdtfA719bPxs2++FUuWn4miNI7qck+y\ni51ksb+3i2Qxi8bmOUhmqjkGUwvo5tDRcYDEMYUZM9nlTjnlQg5d3W3o6GpHY0MDWubMZ898Dcd/\nFrB96xZqTksknCuQzXdi357nka6qwrwFi5hUtXWvBw6c5+5IRbQfPoD+vl7MapmH2rp6dPV0oKqq\nDnU1zdS8JtDb34FCrh+zZjSzuz6HQ22Hka5JYl7rUtPMUh9rf2mWMVHqR8eRw+ju7ca8lvmoqalF\nF+2JdC1qG5tYlhTSJMEJDi/o7+9F26EO07XOmduCGpajrLGtbhwBR8ARcAQcAUfAETiFCExxwslu\ncZLIIveXFBnr7epG275daJ41H60Ll3IeORcvUtd5iWM2NW+8lMU3bv8SHrz3XvzCe34Fl19zLf0S\nePShH+Iz//R3WH3BJfjgb/4+Du7dh29+5Yt4ev2j6OPkoNraGlx4yZX4xQ/8LhWaeezZsQ2N9Q0k\nntvw5du/iiMklOlUNW5648/h9W96B1LVoZu/q30/5dyGR++/F9lsFg1Nc3Dta1+Nhx++F5ddfi1J\n8Xut6/8zn/47dHXsxauu+Cl89zvfR3vbIcoo4Kab34XXv5FjRWvrOVqgjJ6ONvzXV/4FDz/0YwwO\nDqCpaTZefd2r8dAjD+KiV1yLm99xKwkukO3txN13foO//6HcPlanMmbPa8F7P/SrWHn2RUgmRTpd\nC3sKnzNPyhFwBBwBR8AROK0RiAdATk0QSK6SSXJmckmti7lj6/PI5wZxxvKlqElTc0ltZLo0gKpE\nF/V63A4zXY35ixZgLzWS3/z6bSRnBWzd/AQ++Ym/5IzzHtxwww0Y7DuCP/vj38F3vvOfaJozE1df\ndz3qG2bgv7/xX+hnN3qhkMe2bVvQ19eFL37h89R4prD2/MvR2dGOr331NrS3H+TkJeoXB7rxr5/8\nBG7/wmdQS83iBZddjp7efvzr3/8dnnliHZKpNBWq1cjnB/H44w/i8YcfxCf/4e/JK0s4c81qtB86\niNu//AXKO0StJqck5Xvw6U/9Fb5y27+juqYOF178CvT09OKfP/V/8PSTD5LwAtXq3ufY0s/+2z/g\n05/8WxLxEm786Rtwzvkr8ezTD+Jz//pJDFBj6+NLp2Z191w7Ao6AI+AIOAJTFYEpreHU7HPyM9PV\npbj00fNbniYB7McjD96Fn7nx8uBBRd6Zay7AR/78U6huqMMVV12Nr39pOTY89Qge/MH38J//+Vl0\nsYv6ox/7O6w84zz886f/F3Zs24hbfv7deNcvvR9VJIW53kHs2L6bXdbNOMQJQ4cP7EOqqogbfuZm\nvPcDH0YyXyLhPIjHn3wUuewAEtwA/r67v4MH7r0DF77iEnz0z/8GVXUz0HnwEH7vQ+8hUWwnKV4l\nnsx4h3B43w6kOCzgNTfdhPd/6LeQG+zFX3/sINY/tYHyBpEsJnHfnd/Dj5nfCy99Ff7oY59g13wT\n2g7uxod/81Z0dh3GqqVrUFXM4J4f3I1vff12XHn1T+HXP/z7aG6eja0bn8T6J3+Cvu4BFAv8xrBh\nBlO1ynq+HQFHwBFwBBwBR2CqITC1Cad6hanl5EBHajZz2LVjO7vYM7jsyqswY3ZrmIlO/7NWn4tM\nJkO/FGprZuO1N96Mf/n7v8D//ds/w8DgIH7+ne/DJeySPnBgL7vXv4/F1IK+4ea3IFkzw7rs60hU\nzz53LnJVWWzd8izTLGHl6rPxzlvfg+raGRjMdiNfKKKxsQFV6TTSHFB6zx3fQnVVBm966ztQ3TiL\nM9nTmNs6F3V1NewOr8bixRqfWcbOHVu5QVIeq869AG9/9y8hXdfIbnsuKs/Z7/UMW8WxoSlOjrrr\njm9Qs5nELW99O2oaZ5OscpmnBa2orae8gSosWboMff09uOvO76JUGMT5a8/G1k3PYvee/bjv3jtJ\nqrtw9WtvIWmeIcCmWj31/DoCjoAj4Ag4Ao7AFEZgShNOjt4kaePi7mSd/f192LNnD5pnLsCt7/sN\nLFlxNhV5afqJk3J6EUlpQt3vXJ9z7TlrSeYacYQThV5x1Wvw5rf9Igoc67lz9xYc2r8HN970Zsyd\nuxhZjv1McSRoJpnkTHd2V5MgbhHhJN17zfWvR9PseZRZhd6eHhw6eADz5y9CQ0MjNYmdePapJ7Bw\nyXKcc8FllKBlmco4sH8f9u7by279ZSSfrSiUctj47AbKS+C1N7wBM+csNGLaTnJ4YP9BLFy4iPms\nZ9k6sWHDo1i4eBHOWXspCtTsptJF7N27G/v378bcea3QpKDN257Fjh3PGQH/1P/9B5sEVUdyunjR\nQrzt3R/ELW+/1SZKGUefwpXWs+4IOAKOgCPgCDgCUwuBKU04TVFHIpckg+rv68HOXdvR2roADTNn\nc2fLKpuhrm5rzWRPsBs5xbGQfX2H8fnP/SPD93PJpBTa2trQRxJaVwtqA3cycBW7u1eTqNaQaGoW\nOzexLOZI8DLIcwznjuc3szu9Gpe+4hoS2hrTsHZwclBb2wGsOe8CTjBqwu7tm20y0ILFy5GhBrSo\nWfKFftz3wzuRoyZ21ZpztTEmSewAdmzZiKrqeo7JvJLEOMwgP3LkCA4fbsOac8638aM7t2/iak5F\nLFy0Eg311LRSQVksZvHjH97NsaJFrFlzsRHVzs42HOHwgOuufx3e+e73obZpJmevZ6wcNdV1KFNb\nqj2W3DgCjoAj4Ag4Ao6AI3AqEZjSk4Zi6lRmF3cXyVZXx34uJTQPzTO4/E8ixwlFRf6033qW3dxJ\nZPu78KUv/COeWPdjXPfam3DeeZcYyVz32APUQBZILDWvPYmOtiOB+uU4w7vQx67qbo4N5SxxEsEO\nEssF1FDWNrVYt7bysHfPVo67HMACajTTmVoM5kQnpYWsImHlDkgkh3u3bca3v3m7dZevXHkWEiTE\n7Rz3qUlBCxYuQ+OM2SSvtis79u3aybGbOSw54ywut5Qxkkp1pWlztUVnskx5O7bgO9/6JrvPgZWr\n1nI2PnPOtLRUVJ7kdG7rIsyY1Wpya+tnML1qklYRby2ZFCN3Kquap+UIOAKOgCPgCDgCpysCU1zD\nybnp1Fpqz6BNG56hdrLA2dx5/OTRx0yrWOZkmxS70hevWoL5sxbhh9/7BpcV+iIuvvxVeO+v/jae\nevxhPPPxj+KO//4arr72eiyYz7U2STwfeugenHPheSRrtbj/gR9h/dPP4xOf/HeSzd1csugwLrzo\nVajK1IkDIpEHNj23HilS97POOovxU1xvcwE9Utj07Hqse/AH6O7rw1f+8zYc2reH8aRBXcVhoNSu\ntreh7fB+nHfhFVx3s47kmN3+7GbftOkZzmInkTxrjdXLlpa54q/YzBn1Dz/4XfRyUtFXb/s8Du7a\nTO1oHZadsdwIdl1TE5qaZ+CpJx/Co1x6adXZa9HZ04UtGzdziMBCTmC6nNpeqkfJN30U5+n6yHu5\nHQFHwBFwBByBU4/AlCacZXZ3y4io7duzm7YCZ6jfg3WP3xcxKo7vTGS4fubvYH7LDnz2Xz6FhUvP\nwvs+9BE0zlyECy/7KSxadAaeXbcOe7btxLlrL8Hlr3wtZfwQf/mnnH3OsZtpErrX/szPkT8m0HGg\ng2t99mHpGauQ4T7qRc6M5+JLHNe5hV3XKWoaOfOcZG4etYtXXvcaPPqje/AXf/x7JI/VXIR+BVaf\nvQZ9nT1omcVJP1z/8+ChNi6V1E3CuIqEM0O3IjWrJWzbvAmJahLTVSSwJJotLQtx1U/dhIceuAN/\n9ed/YFrYRVxndAHTKSczHLfaYgR72RkX4Pob34Rvf+0L+Pif/wmqqxs4rCCLJDWt7+Ps9zxJbpnj\nWNNMw40j4Ag4Ao6AI+AIOAKnCoEpTTjFKkU22VOM17zhFpx7/vnETfpOmXDUOp1ncbHz7s4jeO+v\n/S6Wn3kWJ9+sIEFLoH7mHHzgtz5qZLWeXdpNJG6//v/9CR5/9HouNdSBDMc8Lmf395mr11JeGstW\nnY3f+ehfU975nCzE1TYL2ke9iLdwlnu+wN2LZrSSyukvgQ/99kfwqquuRwe1mK3zFzDOOTi4fy8G\nOLlp1oLFRohXrToXH/7Dv8KZ51zEa63eqfXdU5T3Hu5gNMjdkNhtT2JKxogP/PYf4MprXs2dgw5h\nIYcNzJ07B3/8kT/AwgULOdloFtccJRYkx2/n5KBzz7+Y+73v4TjVAcpoxOKlSzlTfy3X6kxz3CgT\nkZbTjSPgCDgCUwEBNuXH02KFFl8hgy0UrTJm5F7pNBXK73l0BKYJAtwanAMHj2JKue0otj1BqtV1\nXA/8kBhKzFMj+MC+Lbhr81PIc1ZPmcTQJu8chyTO8TEdnBSY6WIDLlnya2jEcvUqmwxK46LtSQsz\nnjgVT9tHysguraW64WWO5i6/GBaFrzSKWxkvtsfnOK6uJSNOv/I6lhmnEceNw45wp4zOzoPYuOEp\nnM+lk2Y0NXAsai++/J+fxxc/92941y9/EG9/13uZbK1NCCoxfIpbdFbmM85TXPY4/cpyud0RcAQc\ngUmJAN8Hx8MR9V44roDHF2hSQqFMsZQsQRjvn0/24MFtf4lsOmxhrEmu5sveNxlt09yYK+PWq1+H\nhek6boRizkOHIGvo0i2nGQK6//HjUCjXI9m8FqmaFbZDY4IcS5tqx/4TAc0U13COD0FM5mICVuSE\nGhEukbpKYif3OKzdg1GEMQ5fGSYmbrGfzvLXL5Y9VlpxuDj3cdjKePKzcGxYnuGC8p/4iz/BTO7/\n3jx7Nno7u3Ho0D6sJgG98cbX27CBovreNWOfP8VTupXEOr6O8xyn7WdHwBFwBCYvAmzP1J4eVwYV\nahSjGiMe+4LoOprCjr4eI6I7OQKOwEtCYNoTzpjMxURLpC4mnzFyMUEzgldBFiv9RxNJXcvE8mP/\nSjcLcJRDZbw4TuwWR7E0SB7PW7sWt7zpzdi2dQc6u3uwePky3PCG1+OqV78as+YuYLd+yrriUc6T\n7A7nSXLifEl2jEEs38+OgCPgCLy8CMRE72iUMnaPw42X2/HDBYlUDowQNfJqhJdfOAKOwIQgMO0J\nZ0wMR2v7hF4lwau0x3FihCv95Cb/0W6V1/JXeiJ4lWEr3ePw8TmWq3PsFp+bZy/Gre//MN31Xc6G\nkTPYC1reiMMUuKsmv/6ZH+6brmXupQsYnaZkykiefk48Ax5+dAQcgcmCgAhfTC5H5un4qeDI4U8j\npYQrtoBsRPkbO6mxoribI+AITBAC055wxjgdjWSJnMUmJmS6lnvsF59jAhiHj8PF15X+cXqV2lT5\nx7KOFTf2C+G5yxEnEtni8cqqGkuN1qRW0xrP0JNuRFOtaJyHyvxLXmzifMXXfnYEHAFHYLIgELdf\nyk9oK9noqd1kV7lavtBah6Pav2Cz0JHvsItcFamyzVWHu+SUtGaxTdRkeLNrM42ijX8f3U5LjBtH\nwBF46QicNoRzLKgqGyL5j74eHafSv9IehxvPrdK/0j467dgvPotklm1sEptLtadqRHnSzHTZgpPR\nz6OWYUiWEnPjCDgCjsCkQED0b9ionSK/pNHHc3AXMRRBTPKsTT6MlDJcMvrojts/zoBl+zcsa9g2\nMg25a0tki2dxwnh3fYxLdkx6vc0cRtBtjsBEIHBaE86JAPDkywhf9/rCN2NtJ0mmJglxRqL9Re0p\n17l34wg4Ao7AFEFgNBGMhyqxVTNyqWKIFoaf1jjmgCAbSlTmhZafEynVp7bIqC1IJ4cKI78RxNG8\nh8mmZI8OExPPCjFudQQcgQlAwAnnBIB4skVoH3jNrFTDaKteWCOpRjhimNbNHje0cnfjCDgCjsBU\nQCD6kGabFozIZkQk6RXInzbs5XJv3H6tWGC3t1bgMM0kw3L5PQ6Ypy/bR7aDsbThksft4rALV+mj\n0SFoRKU1ZaK8DNrTEQR1OJrbHAFH4CUi4ITzJQJ48qOrIeZ4TTLNEheZl7H2kl/0aiSHNJ/mqms3\njoAj4AicRAQqOdyIBmeY7qlfJmgmR+dDGsXYxJHjsAnk8gX09vZZd/lgNos+2sskmp2dXRbPxsQz\numQk2f519/SgwO2MtXGGNJ6jW8Dm5uYRGk6lVFudRl1djWVCZHNGUyOqqtJ2VptaNC2pShDnz4Ly\nEEoVroZLoevRIUMYHuNgphQ4ZsihKG5xBKYrAk44p8Cdte/2ihYtWIcb91CEigBToEyeRUfAEZg8\nCKj3JKJxPAXipjGRtq5l1IOiFkdDeUT01KVtBIwaxiJJm42zFCGjn8Zb2lIaCsdg/QNZDPDX3dMH\nKii529oAjnR1k1yWuMxbLzo6Ok1z2dXVxUk7SVRlMiZfG1ikudubhFTxbNpO5UEpSzDlZzLBPc7+\n6FZw996OCjaoPCeYhwIKhYLFL/KsyUIyg9lBFQn1DfWoyVRjRnMD00xhDtc+bmqsQ0N9LWpqqrll\ncBUy3Mp4JsksqARIqPwqu/3pTDSjVUpEaJlNc5NWVjZruaMxpErX8iwvmdEFCK5+dASmBQJOOKfC\nbYwaoWO3Rcf2nQrF9Dw6Ao7Ay4iAmpCI+IgWiSgF8hiIUuBL3M6XfkntZqZeF05cTCSqkCuUSNyS\nGBjMmoayra0Te/ftx5GOIyiQnObzRYYhudPYS/4aGxspQ5rGOixbtsIjfxI6AABAAElEQVQK3SA3\nJpqmtlGEVmSvKk0706vKpJkOM6QxRTrRX5cioloeThxX10ErGbM3kNRS+zlkSAQZv0CCWeIev5In\n8mlL5jFKPgo7MDBAew793NFN4bbv2I2+vh6mUWR+SIL5U4L1NbVomjED87jNcMscbsoxcwZqSEaZ\nfdtGuMyl60SSRTBl1xAoDQdIaAhARJiHsuYWR+A0QMAJ52lwk72IjoAj4AiMi4DxNDE3ESSNF6eD\nsTiNFeePlwUSLRHFQpHULpnGwYPt2L//EH9t1FR2o50EM0dyWVtbQw3gLNQ1tKBxRhPqSSzr2XUt\nAlZbV0v5FGnaPxI/kUjSMuNgJI8ygTialT4ihyKL+oVxnaZJJXEzDSzdUtS0mkyLIuHBVGdIDoeM\n3DUTnZOGmKaIalgvmWcSSxFcyWAqRnLpgCQdpJmkDblcFtlsjuSzD/kcz+zu7+8dwNMbtlN7+ySz\nUsSs2c3UjNZjwYJWzG9tIRltMYKaSOhVy/xSvkh10Igq/+ZkmNuFcHbjCExTBJxwTtMb68VyBBwB\nR+D4EYiJnkgZfyJ4ol2ykgSV2JUu2pUlmdy78yD27t2Pp5/ZQI1lAtU1dRz/OBOz587HyjVrIU0l\nFaDka4E2mgaSelG7Ip8qldh1TdJlxMs0jSJZlM7u9BI1juqej2gYzwrHK5FTXclOYqe8iXTGxJNO\nQ9cMVmFUngqjDTPoJGIp0qlScXA8ZasrnHLpIS2kYhVKGhua4nUgsxl2s2eqaq2sRXWZa2w9hUkz\nWiwVbKhAx5F2dHUfwbonnmO3/dOUUsaiRQux9twzMaOxHnNmNbP8eWiuk8pjmdE5XJnND47AdEXA\nCed0vbNeLkfAEXAEjhMBkTBp/9jfyzPpnIidSBm1mMVSmmMwC3juuY14btM2dHT2oL6+HsuXrybJ\nnIO62jrU8Tp0v4sESoIIYjTrW4wxMFcTKiIq7aZYXSCZ2jWNJE+s0Yw0miJ5ypR+DB8ZUcMgn66R\nJSaeFjQOeKyzxDF9da/LGJk1kqlufF2LvbJb3IhvnI8oLMtkRdElx29qgEEqQ5LKfd4yNU1omtnE\nfK3AIIcWDA4MooeTmg4cOIDv3vFDm6x0xtLFuPyyC4lZht3uFMEyBvKsdFUmy5IfHIFpiYATzml5\nW71QjoAj4AicCALSKhrbMrJlMRMZjsuswu49B3DHHd9HvljGsuUrcPElV3KyToYkUaFI3IykFoys\n6VpyYgIrAsVdd2loEXeUsQ0rZInIFjWbZhgk5lsWtOIiChHC8WhekbzYHosfCjSWJZKZiNOMZUVh\nh9KlJXT1M4LKpwLJyhN1moGQR3FD3lQW+hkoHFJQk+GPwwpmNmPJksUk7APYvWsntm7fjmef3YjX\nXHc1zlm9ijE4ecmIuYFEuxtHYPoi4IRz+t5bL5kj4Ag4AsePgBjTEHuTZi/JrvODuOuuH1GTuQBn\nrVnNST61DCTtpWZ5k2Cxazlo5XShpCqpodiZ3CW00p1aSvOSHnR8oqXYx20sD0cPLe9IsXnMQFqr\nU53swybKZ+Rk2lsDgA7ahIOGiEXFFEEdWa4aks9VZ67GihUr8NyzT+HeHz/MsaD9uPTi84gFU+Iv\nnoU/nKbbHIHphYATzul1P700joAj4Ai8CAREr0SSgtZRzEljNtetW09rGmvPv4An7fIjsslQ6hKn\n0fhMO49mcabiJAGzs0KI6kVsLbqSq5E0WU6hGc7FMRK1QMpzbOQQrkP8+KizJibJL2BDS2Tkpp/G\niqqbnmM+Obj17LXno5ZLL/3k6Q04d+0aNNRwJn5Bs+mFqeQF2bS4cQSmFQJOOKfV7fTCOAKOgCPw\nYhAg4bSu4zAhRxISnLW9/+AhzF+4DCkuP1TmmEWjmTyk6acljmx/84iIDROlmGhFFM34UyCoQzmL\nOFXYonfI9SgWyjsODqZUj8eErvLxQo6VJjWZRrRjjWYkI9qQIyaXwyrU4UyHvZKEAUk7SWd1bS0O\nHDrES1JRjl3lIlNWxDLHgrpxBKYrAlO8do/fxIwfYvjWakD6dDeGh7pwbMB91KiqHYxHqxMCG41k\nXryIIKnE0Zxih9hfI94lxo4600Y3DaoP00wrlyeJAvlpeiOgyqC6pqrB+hVGucU1RG4q/qiKJCc3\nE46AOq8D1nwOqY0Md0HY8xfdB4UJ+s2gd7RdfbgE0jObNyNd34iFC1tRX1vNpYLYEU5xIpu6vzYG\nkTLC/udx2xJmeqsgpvxUuFjbqbDyoIyQL13IxPkKV8NHpTG+OZ5QSpc5rBAWxap0EiY0FpZWy5W1\nl8SF3efWdtLfJh1Z1zkLEvmHqIxp8YLmU2XUjHxRSpH0PXv24Mn1nMHOpZI0UUo7u4eZ+ZasHxyB\naYvAFCecui9RgzEht2hEqzMhEiejEL162LnDI5tAgy98satptUWQOYBJSKihHYGutcDDJdKlZmxq\nNqm0I0NLisSTAuhuCx9XjvMaju62KY3AiJoxXJIK53hxblYM1qOwCHZwC9sQyn2IhFiNGxbjtglE\nQPfEiBEtXIPStGzxM8s7Y7o1+odxifQnObIdheiieeOJTB0ef3ojduzZh7OWLcLyxQstc1zq3e6a\ncS3JIXkKa1mGSqD2wMYlagkhzQIPzsyDiJvaDFnVGlVUA7safVDA0W4vvI5bsRf6jHSJRcVnCR+2\nh7Bq+0LbyLSHiLX8WE4SbiOdLICViV3lWsdTYCgPWjNUdrWJ1p7SnuOEq44jvdi8ZSt2H2zjslE1\nDFKFFGUloyWn7D6E5P3oCExLBKY44eSTrCf7GObYvseIOF299BJg2dRQJthQZrmAsbaHCy+B0GiG\nXT6I3OhWeBQmhq3kRS291qTTwtAa84VympNR5cEfG95k9GIZJcIvpywCL6wcehFryRuRCfmmqB3T\nOo2sFUY4SuU8z9KwGeUh0XmhjCkLxyTOuHSNet7DDGpllETRuoF1b4ImUgQrokvROTz+0uLVNzQj\nVdOInq4jePDxDVxj8lksWrwAC+a3ormpgetu1gUpajIsIYpgWyDiJRJqxFLrX4qg6eNTYx6NtGk2\nuwgwTVQVrE0JLjyGq/D5O3Q55PuiLExHpZYJZZYtTF0avqYf2yuFU371sRTKQHcbsxqRZZY16C2J\nnNwZSLsoCWdpMrVl5iF2m7cf6cSmLTuQ5/KjNfUNaJq1wLTDbX3cdpNG7ab9SQT/3DgC0xWBKU44\np+ttObnlkn6J1MC0k3393Oc4y/2D6+pJEPQiyLExDOlH74AoM0ETES4Cw5SLXlcKru3rEtyGrsiv\n9a6eLDq5L/LGLdu5tV2v7cyR5S4d3pZGUE7508iaMVyc4C7Skamu5t7TdbYP9dKl89Eyq4HkRDvM\nqOapKgR9jviJ14thBE+KzUgdgbaPPpInJSKSI00jr6TjtJtgs611d+L7q5AaXchVJpNc8Ly5DqXG\nHD9S+7Ft9xFs39OG6nQCzTN0b7m7DgloXV2Nbf+oPccVW1pQjfe0xdp1s22hS957ywSvLU27YGiZ\nUC/iPCgn8rUc2UFhXooRxQ5tWSi7ZGlSj4zyozNTZF5FBJNaoZ1Guw2F5ANm0nrq2vLGOp3Lc+H3\nbB69fYNcp7QLh9rauU88dyTi5vGq8ZmaOWjkEkm2bScxyOcGUZRmVEnZX3QPRMbdOALTFIEpTTjt\nazC0Arw9Q5YRt8pcQ+t21DAjIkzzi9BAEhWySu2FnGeLN9DXS/RSaCRBsN03bI06hrG2VggaikMn\nvRSsm0nubGz5z8sqcK1jbNq2G4/95Gns2d+OdKaBkw1qSWSr+d5qoJzQrE9ziE+T4sX1Y7i4ptnk\ni1laoP4cuxAHC9i6ZysepUZsDrf7u+H6V2HpgrkkG5yRq0pj9crrxDCCJ8fGnciJtJ5hYp5kJ7lI\nHnsgyKj44Oo+0l12ez6thYgyEu6xCFMiyWeYbUWiSutLNlBTNxulQha5bB86evtxqH0/ntm4FQ1c\nNkmks5GzsBu4naXWoRTHbGmZbR+0abYt2pUnxRnvYT/zYdoXEtXQHNnsEJyG2u9w+dKOgV4HGUzD\nkrGGLkpLJ9XJiIzTP5BNEUwbbWlbdw5yXc2O9g7ut97PLT07uE98kTsM9XEnJn7Mk2BXV9eTZM5G\nTSMJOHcoEmkn7aSsIj/KpTUNvUpKZ0RZlbwbR2CaIjClCWflg2pdOdZuqAGraEB449SAxQO9w320\nVmaMWzoy3hgBpr5T1L4ZXmpAU1Vc0LnAr/JedgcBM6WFKmWNNIworIgEG/6AczjrPaD3VJld6B1H\ncrjnvsfw3PN7UNMwG7NaV7NnnS8pEtHQeOsF5+RiBKbT7mL42dMr1TRJM+dS/V2w7tgvffUHWLqw\nGT/9+qv4ccO6xwqXkqbIns9pB8YkKZDA5ZegkU49t1mjUyhXsZWUNlHL8fC+GeFUltU26mNAbSbP\n1iTywAkuIkpJauUKJFdJXmvx9NqGGtSi2eIkSKay1NzluMTPvrZ+tiPdKG7Zxa7lgnWtV5Fkzpk1\n0/YWl/a7vr7Wdtup4U5F1dUZElISW1ExDvGRPc0hGVpg3vKjfMTNttoctUchc3SO7JZXhjuWMZKt\n+CofS89tKXM5Lb7OVFgfc4M57hPPM92yuTz9qbnkl3Qv18zs7Ruw8yD3U0+xzUun2baluGMQ27mq\ndA3qZ81GA90TdNPkIibAH/EibvoIIwy8DKQ17KeuDKsUOodSKB9uHIHpisCUJpyhKyJqhfRwW0Op\nhzxumULDopsXCJbZdMVfeMjlMmzCl+3w9fS0hcZOsy1ZXjbAGnOkJreLmgq28yQD3EUk2t5NUMWE\nwGatGs7CTu58ZSWquHRKJ75318M40JZD8+xVSNXWm0y17fGdUAy94CyeHcMhSAr2OKyuxnM/kbCS\nN1748dI7HhkKE8sZL73KsMcjO5Z7PGGPJnsiZFTKHruMQZemtEQDUObLF9VobG5EoXoG9h7aiy98\n6Rt4w01XUds5ny951UHiZt29ku5mYhGgNjHZQ9LXRAJYT8KjTm4SpgSfeN1AtpUpPaikodaeWiXR\nnZOnbozOspFs2mBtfiTEY25IpsLd5j1kvBLDpq0rvYyaOvqwDbH1OjncpkStZpEa0T6SuOIAxzZ2\ndpC45qydSXEojvYvV1pqp0U21UbxwtJUm1TH2fFVXJqpSDIYtsaEkdFqpcewGnduxvIfrDr2dPdY\nSYKLwgSaLc2kiLPSsNLqzF+Jk3v0V+S5wOZKftpDXWOPUySVdTMa0cCPdBFObccpzaWWjwqzzEPi\nFMNva5FnGZZB+dM4UMNPH2Ia1ay2kNIZ1n6WLzqNyr8kuHEEpgsCU5tw8skWZ5Kx/XDtIn5iw9n4\nER/smHAOL90R4g0fFT7+DbtOS5tIZoSJuovU6KrRVAOsfZLLaMCMpjo24tJ+8FWk7nd+/VtDr4ZR\njbvGH/Ervp9dSD/48eM43FVA89zl7E6qtbFJUSsawSd9l/5iU2mL75f8TsT9RMKOL1svlmEzEbIn\nQsZwvicifxMhI+Qoxmq8MgbtZZIvZy3+kqxpRlNNCt2d23HPjx7DG296LWY3NjrZHK54E26TBrOl\nqRc9nWXkShkOoeEYQQ0TTOT42POe8BkOGjaRp1ijSf/41tpZNYeWijGgok3DXc8h2/HnpLWiph0l\nKdNfKpCsKnbFB2IoWfyxHREZy1MDXuIEm2BI+kRU1cawXVK7I+La2UOiWhxkGxTqlDKonphiqTO0\nTWz7R/ZiBWlpkll6DMlWSdiSUStJ8s12Te8D/dKUW+AEp2qSWslRe6gx7WXFVXatvdOZSPDaPtQF\nAa9LCiNjHnJSKlF5FNbIJU8Mp9QlUGdhGmKGKwZQYDeOwLRFYMoSTnuO1fioPbAHXQ2BHlg1IuGs\nhsLC6cE2NzVmwWWsh/u0eNyFid4rHAwfvqwDEnLjgEu+kNi93j1gmDY1VJvmQQ2+8BPRDNBaS8tG\nOY27f/hjbN3diZb5Z1ImNZvEv2QzYNWs00iDJTdpRYYAju+BAgw50n4i7icSVumMF/7F5uNossdL\nT/FOJM0TCXs02RMhI5ZdWb7YTeeRRh2I0m5pVnSZqqpSuZazdM/A/v1bcOfdD+NNP3MdatKBqoyM\n6VcTgUCqXEBLph9zW0vYdXAAfaWZyJer2W7q/ulDUHdIJroHdlvVNogc6Y+11NyG71G48/G1hbDH\ny553PWd0UjugqTgibEPthjU88pFhe01SKtqloTc28sbc5c94JJqhzaYsa3/C02Jpqw23hogRaJeb\nyaRbkG2CImnD9tgWvyf03ggENuSZ85+sHBaO9jB2k/mhXMu1nemrtPUzV8ZVl7lFkrPeQ9GF5Sxc\nmL8NKVJacQCdQ/7NJrF0iWXFUvzsCEwXBKYs4dQN0INtujO2fQX2f4RuDT2wemTDQ20PvxoEayzC\n2bpvzL/y0VaTojgj3ZROMLF7kBvCxXaFiP1lr3TXdWwYxrx4qAwee49wjGSIRA81UKMjHS2dIYEv\nsMSaXqoGiB9LzK96M8qa3iEkkRpz1Nndy263EsdZ1VALQXfiN5S6sk/HbTt2Yceuw2iavYQkky8x\n/hVNs5FnWAqLtCda0097BY+Ny9HKcCLuJxJWpa0ML7tKVummMLGZCPfpJEO4qDzxL8ap8hyXl2dp\nsdLhQ0+fIEkOwSiV6jBj5hLs2rMDm5/fhnNXL6GmTfF5H0bcCjpGlc5e+CFEZUK0x2nJOQo8KsQp\nuRwvG7H/C7IYe8S5fEGA2ONFndV01BQGubRRH9Bai+0H+WQW5qEghqePAAaIP8JDC8h7xL/QFpJC\nGklSnuir9oJW/YS1nSjfnvU4d9aIMIyed2oO7bGPwoeS6oI/ETYjZ2rBA3mN2227ZvsSwisYdzVS\nG27xQvQ4OTnJDIeNHIJz8BjtxEzFrZlSHo4rO//oLww01EjhFMb2O7d3CEOrjJFM4WvNs8ojR12b\nZwhgsmm1MJQZpcCzBbVhCMLB3mMMlDK8zXtKH6zcLEFAQUWJXWKrrvmzAJFdwU5HY9AICFWU6Cy7\nuQdAwgeiQsifJvazy/hCHpG/rJPQTGnCKWjVJHRzaZ82Lr9jN4utYSCeoRELrSNviBoLNSRsyNRV\nYusBVtyQpLWMbOrshgeCFDqZNOtQtjSS9tWtBknigm+KM24LTDPFWZ9qLKyxZEOuteaKbEA0rkm0\nWM2nZiZmCurPKqKQotaHYTikCkW2Q0UKTfNlrK4kVS59/auBT1NDWOLMUi2fYTNLVdPoJ6kykqsk\n1JQpFZnYT/bK6id3Kx5DChcbu8ludOs2Mj/ml4EURkt2tHX287oKTU01lEkSSQ8tVKzEssRCs4/7\n8lVcFmWGjVlSRoSANecimXHqyqDhqxydGmOvCeHEbsMyyxLQCZMDVHYz8ufPalHsZnmOEBQWhqzQ\nHYmrOYw6DGMtG+uNNL2sJ8EIc3bXGS6qHwXipDFwDKvxdDK2fA13H7E86UNA91bxhyUr2OQxR8tX\ncLcjD9q6T2Pa1H2rl7XQTVZz1YKqJjz+5GactWIxJ13oPpCMRvVGz2MpKYy4hifLr2fHsFLdHWHk\nGrkRP9W5cPeUenQfLXzIU4j6Yt2PJoM51AxuplfiXuOV0q3esW7F9zH2jXMZ7nUcQ+1OZRrKbeV1\nHK7SfSw3+Qd3tTHKWXW5Hy1VPcCcPHYeTqCn3MpWSHWN90ZkjsmEGHolyBZyKnStLZKb4avnmyY0\nJFFYOcjIJwgKw28iJ3nRT+3HsDEp5ibXcBUslpeovQiKAiWnWsBwFe6SFaUoq9mH5JgLD5VJWqAo\nxFCi4Z0gufopbaUZ/iST4fmvD2yzKP0oTJwXaxAlVkGGjBwiQ2vUKvKsQNHPTgoXXMMxjjRFz6xL\n9jTyebdy2f1SbVdbyH95ashEmXURAyhlu1DkLwEuE8UxvvZcGCRRu6kII+rNFMVF2Y6wMGtUDCm/\nbBOAzEygeg57CRtZ3lr6hsm2AlN8oMjn1OZeWBsqWeG9EIiosFUli2ttXMeiRCbRacoSzhGQklCU\npTlRhbbXkwiGXuK8CUY2dOYlf3qN23AhaviGjLzV2DB8nmN3FI3Djvjjq4wNTU4vET5IGZFBPiwa\nMC6tgNJTmALHCSUKJIeSzbB6AfKkIzUJetGqSaJsVppiniST9hK1PkUuhK0XQk5EjekmqaVVNdKL\ntkCLEc6iGny68aHTlnIqhnJuFU0X9Ezyl2J+ikFNRMfxjCLS6GGWcEmNHuroMWf55F7F9TT7eS6j\nnqQzybIrXInTLXv7C9i0/QAa555l62+qC0wPlEkTcZJFhmdrtMPVKTsS8pAFK1f4ONA7Q88ld4KO\n8pGLzkGbYVhYi6i4QUJUIoaziFH4sU8hBI/WGDAMExOOBgXtQWbAw8LG2FiAyD/y0IeKOUvM2Mm9\nzK7j50pFEd7SUOlTRLhYHRbJYfTaphbs2rcZ3b151M5iA8t6Iw256pmeF6vpij8kSCTILkaWPa7D\nhrtisi5apMpgijeWORH3o4XVc6k6pPyFPFp5eTVEhi1p4RAZWlQ1+cjQwjN/CbYvoScgPPMh5FCM\nOOawgFEulPICF73+9UEr3DIkna01/MxpSWJrGycJFmfxlnCWNbWdeqYTarSEnWVK9vAiU1lUkrj+\nRjU6pMV8D5sRF+YcNSvDQV5gC3GGYkYRKuMN+THuSNJq0A1JrAw35DjaUiG4UlZMMl8QnA4hSsB2\ndJzR4XUvhwyjhEvWCVmE61C9jAJGt8yoggUaij0FLaplfBb0fuR7y4Djx7S9G+zDu5cfn4dR7NpH\njUU7sr3thCNLRQvrHp95tcr2/okwGQIggmroeqpa4nKpQtkzFgoihVaeeJWquBZ2QyvSjYuQrl3A\nnqB6ksw6qzPD28gqTiwostrzHde1IHOyHuM372TN3zHzpXoYoJctrpWxnWdZrSU/ppgKzyDDXhyM\nZ+OPmIB4XIpaxgS/wAoDXGstO2jLZaRqatFQ24QqLRlCLU6RPItD26Nxj3wFcYB+mhVJpFINeYJa\nTfCn3Tce+fEPsGPn83jtz76da7Xpq4b/fGGJ6NqXtmWdZDally9lkGyqtMqhrq3cOugly194zTGN\nUAR6HKcZM7zkUzj9itSAdnZ1M9GSraunHTRS6Qz27N3D8lTZ0iDDX/oqwyiBoy6PM1cTEIwvSQMp\nZCBgSG2P4SVtWTA2uJ+ax6ip420izklNlhIGav4UX1iEVy4vjmEC4QhxFCVMyggRmAP7YNDLR/cy\nIh+BTdFP91ghWc8s40qTbi8bfiHXL/loZVIh9BzwzH/VLWl3U1xWJs2Zvxs2P49ZV5zD54zPBsmo\njfeUql0aOM5yN/ztA1LETkb3Jb43cgnPQNi1RphFH5sKeopMme2DlctumJWUKStvakd4v1kuI3DK\nD+ugCGrwVT0Q0ZRHMOHprnCIPV7EWbLUrpTYiOmTOMkP7dkZrrs7N4EthwroL7Ywb9opSM8LcyT8\nmU7oQlaCqquUonU7VQbLaIy9/EcZRT7dzNHKXOluoMbADHsM24Ry+MWhptpZNVZD9lVFUnpu2d6V\nqTyxCWoc1lHsfR7Zrmf4GPRab2GGBbZtQPVOYemt/DxITlz7VdOmjVHZWLDK8ckqmybJgWCkuIwY\nl4pBoXc3cnWtyDSvQqJmATHiBF6SiwR5RlA0CavwXBpSencE9ChNKE5eM6UJ54TCKiJiN0sPiKxq\nZEUUud4aNZFpPiTrn3gI99/z39i1czMGB/IchzYX55xzCW64+a1onrUIuTK1gNQYhO3NqKlhQ6/G\nXjN0zVDFxnmVfLGWcWjPc9jy1KO49oaf5fiqGcZzjIQY0WV4aT+ZjySfYFUy/exdZnlSzWWF40uk\npC9IZli/UIKJqXCqwFwZBFX8Wi1y96EjtrxIEk21nIWufYE7+0isuR8wNbOhK0p4MT827ICxLbOh\n2C/bUVAwT+Hh1AOqZoyO4Z92IqZG0Yhg1LTxpaqvdPOL75sWKre4cR3h5bGMCIbJ1j3RvVcro7ii\nFvRTi6ysKCP2kz0YvehFUIYbkNhnupxVZmFBwsUPlgRnBQ9wXUPRr2pOLioYTsJMQxJ4JBZcUMe0\n/upN0LCVym7ncK+EDZ9b6/aUPb7Xsp98E25ldM/i59OSNR/aeDbCqXvPf5VBr1h+kOo+25AWwWL1\nUGHjOvvS864PKrVAHMTDMdn6eOUy8Opez/DjuSWPXW3c+CE/j/7a21t4B62/LUyuPNKHfTzW/vBh\nV24ZSiHdOAKjEVC90LAwnVlntMoJFRWl4mEU2tehdGQfakRA9fETPxokm6pVlUZe9u0Th6n0nAZ2\nG54RlU2vSVvLQU0D36t6PsHVGAq9OzDYuw9Vs1ahetYFbCu5woMC24YNBEHvCNMMqy2N+MUQjmr/\nJqdxwhndFzXx+gWjuy8ipWu6sg/+sQe+hy9/7rOYu2g+3vjmd3PbvmZs3vIcHrrnbhw4sA3v/7U/\nRap+ESVQj8BGW12i1pFrLyDpDyjTup1FRPVKJKEUkWUlSrL/PKmHUMRGgzoZWppOfvrw2ZTmgbEp\nMynNjT2NXHtOchnfHmhq5DQWC6UaRoorH60v0lgSFlekkyUiqdRiyFocnhlBQ0MdjtCe5i5CGgoQ\nV++YeL7IZCc4WqxtDE/20J018ihtE2+xvEQwhZ8IvN09YirCqHtv918hGTAWoMvjMSFZhhSBjeqW\nkXAJ0r1WGhqzSNGyRyiW9RVr91BuinmiCTPaZDYsr+6MGl0NN0hykWzt1JLmR01NgvVL3XEqM7Wd\nZYYpcqiMhouUWf+FhLqW9FwalJQT6pyIfKx9E/AVGutTgEW41bpfbPyNTKq6BNdw75WJUOfIsJl3\ndiEmuLG2kOC9D9Us3GfVSX2mqPWwAivqSzBJjpNTeqpViWiZM9W3FD9kW9lcVM1JcyJRGf2lhST7\nmvhHI/iVB3u5Re0JZdg1PUNOX0KmPOq0RcBGZfBdVta7KZ1HKbcHAwceRNVAJ6rVzhXox+UArO3l\nc22VX9uHqiEUdzodjD1kwwXVqzvFIXlq9m0oFXFJ8z2bYpuX73iO1J3r5jZdzKajNYokoNTmRU2E\nmh49s/YeCUEm6zPqhDPcn6HGXTfKbhy1D9Iq6jnI9XXjvru/g9lz5+AXf+X30DJ3hTW75154JSc7\npHHvHf+FjU8/ivOvaMH2558jgTzCFYYS2LZpK8446xVYesZq5PvbsHXrehzYfwCLFy+j0H4+dNrm\nLGcvg1KuD4cOPYfNmzeQyDVi6bKz0bpwmT2PJe7e8dS6+7HsjIV4ftvTKHAc6GUXvoZd+3ls2PQw\nOvsOYMGSlVh15hUcT6kBxyxOeCOb/SUdVJH1RuSLXuSzq5tjOmVX20Fw1GbEbyClOWHpvqRMR5Gj\nvIvuhwZNWjMRE40Z0p1WFwWpAcvDUrIsWtRaQe11HxVLBdTveEyl9k2tgBoQHaVjDRgqvSQ1eUkR\nXboFcsG0eW1kShpthWdwfaQoZyZCYqakUe5H4qcFsAtqVIU7P1gSHGJSw0kDK+cdJP59VuqgGVbd\n4xqJRMBWU+BXWJHPjKq2BtDb2rD0lYOa32HtsUjSqUQtppe6Qfxc0NtUHzA86Q6KWGtx86IGj+v+\ny5/kT+9Z1YeoE5txVUsUV3l/6R+Oyo22dKhL9VuvSkJDR1j3pQ1OkpRXFbOYleE6lnNT2HH4EHqL\ns+mnsfDKg+qnap82gaiSUorXqtNyc+MIvBCB8MSpjkgBwuExuTbk2taRbB5hXVOrxjqktadkscA8\nsF7ZoaJambe5y2/6Gj76wbDsNoyK19L2Wi8XfdQWaDz7YMcOZPjOr2m9iO9dbhWboLZTwEXx9RYJ\nQBJ7s+oQeYYUJs3RCefoWxFVdN0uEQ8RqJ6uIySPG/DTb/tFzGxdikEuAaTZ2kmuO3npFdfhoe//\nD3aQaF5wxdX40V1fwZ6Nj6O7r497idfgwsM9mDdvDm7/3Cfw6AP3kLQuQi/HadRV57kFZAO5G7Wc\nyUHcedft+N43bsP8xYsx0J/H4bbP4F0f+BAuvPRa9Hbux2c/+VcklXPR1r6P28nNxay6BnztK7ez\nu6IPs+b8P/beA8Cuo7r/P2/f296bpFVvVndvsmxjcLfpmE6AhJKQBMgvkAb8gcCPH4Qk9B5qgh2K\nKQZ3DMi9q1iyJKuset/ey2v/z/fMu1tkYbms7ZW9I729986dO+XMmZnvnDlzps5+/6s/2Ac+Ntca\nZs32EgVD7SNa8ZHlfILniFWH2jw0EACSVFVGmtsxDp/gTGU/pk2tJtdyxhXYpOm6OqYkTgzgwXCz\nBvycVIkGGdeSJmWSJC2LRE1SZi0H8Qbq6CcAoA5UzTuiyhMQTgRzCVcktVKnoE6A+D1entHpDTt4\nc+BCnyh/qiqBFMI7zOQb/ePlEyR4HL1yEOWk8CKJV4Yk4tCmANhVnd9sBfltTGRkiFs7+qGHvgOo\nue4ts4GgI63aEEyDaIQNoD0EVZ2JbM+lc/xIvUuFRtYcvD2owZARNQ3lNqt8yg4pKxjiKelxBRM4\n8J94zfMc+FNfjFUhSBmSiI7ShQ2TaOU3gFo2asR6rbaESU59xra3DFpPejL9nlYuZAidsnhefBjM\nfTNmWSOiCfeCo0DU9lKtlmxabfGew0jr1CbhepqrmrOCePPg6rwoIsCT0adDfvKPPHX/AnDqD450\n3jXmhmoNFyq0RhyNSwpfhIAi07HVLdvE65bTH7LCyTuf/OX61fCV+p9cRPIYh24CcOYqJRJHS77k\ny5y0CAEDtMqQPB6gQaRs+qz5LhvQdF+aUTLhUlk/wwrZONTU3Ep4GhL6F82HDtgr3vxuW/7SC6yg\neDLSz9W2bvX99oo3vsPOv+By27Vzl13zX/8O0zBIISrcv/sx++NNv7bzLrzMXv7KN1sKnbYf//hr\ntvLGn9nCBacCjGCuTB/L2oP2t//4GQDsXGvcvMYO7VtvH/nkZ23ajJM4yxxJRn3dEIuNJfjzATPX\n8uNIpNLQYv4J8+1Q9y4bZIwcry4PMKllRJmuyrJVV0rXrnidAwNqsH7SChWu5puBvpL8aKlbY7Iv\nt7tEyB/055hO8Xjv6Z2GuElxy6gWnQc7Mn31CNCp8+eDxMhhE58AOjI6o7lPuQIj5AZ5R6LHTHbc\nBnBsQ+4CXUQa6MGIEpkhU8YzlDFJmSUFTA7EbeuuFuvpCUvOgXLIomlbUm2pq66wmbOmUJ9aKlZ8\n/An/9cC9wNVQaor+WXcaICUBDOlzo7pkeVpqOWzH5V7qAIXW2txj9zyw2ZafvcRqa1iJQJ9NG6Qc\ngdOXeEH8qgHaI1Skz8gpGtepY1OTdqt7myAN8Z6yKR4v4LSe+mLA5eSs7TyQYCPRJPo3Lf2LjmoT\nkopy7x8oX/yeWxKT5oQ7HijgQAmLB6mOnZbt3APYFI/LkgvtXPwHa+vnbE6Bctzk7PRiZinJFbyt\nQpOh4UkEQTIch2Cy7pHs2GNWOtXiCLI4gJoWzDdqo/R3QTAygrC8G49uAnCqVpzTaSoaDPVIa9DP\nN5PABYPJMPjF4kUABilFCywQRoOgdO7ELUhk1ElLilBTO8UuufIdlmWDjd7t2bHFCgtK7KzzXmFF\nldNs4YnT7Yyzz7dNm9Z6Ort2NFp/PzqRibg9+ugmZx42gtvhQ/usv6fPCjWaMTisuPgKm7voDIBR\nEcbWmy1eWGI33fxru+SKhM2ejbi9kDyQnqQT0eYdFe/pOpeOUM7g1DXIBwkNTN7e3madnR1WVDoM\ncp9uOs/Kd9BB2qV5sT4rQJdIEqbBtOqjGF+ksyoHNaYlC2kN5sV7eJIJDzZFaWClcn1Qdt7wkdnL\n749PkGEHkS7pEo+oeUleCV8BfOdMr7JFc6bYmvU7bH8nvgIbyockreTPOxbr4czoQksN5nMUIfyk\nmaw6lePVDeUdGoiFRAv/+W34A4ul8qXVnESfs8c++Zkf2cYtB5CwZTg6lS13oPQi2lcRdfWqy8+0\nj3/s3dQpH6maNDv0eMWdutUD76IRjafnxolHgo61zo7fvbfLNm54zM45Z4lVV1OP6K499OCj9v73\nf8e++51/tksvOZWcdvANm3RUxw7mNNlRKZT/scl1iIY40RmFy4hXZ9vjuHXVEXhV3UtBtt8mJdot\nW1dou1qbrDdVQTuocpqKR/WN75DlLvQLyueEm6DAaAr40rCxStHeiM5mUH/xpigWhIm8D+Dqup5i\nx9Gf+9PI+eKLgctURv3Usryl0fxFGKeRUKXwBf1oDIFEsm2r5dVNYrxiLCO0xqmYGrDC4IKlDoX3\nx3H350UCOENlPJlaCJWvGQOhNVhS+bU1NXqylpY2W+jLTHoUIB20Js6FHky2sFTOTnO1LABpUQlL\nUsxCkgDQOFKtnvbDls9SWklZkdvf1FJufr6AapA6dHb3AjD6bNO6e2xX40YYSQNP0hafiKJwURG2\nO9GbTOVbTWUD/uLGAps54xR7+3s/ar+/6b/tS5/9R1uwbLm9+b0fs8qaBgApDV3i1qO6iBZ6+SS5\nUp+I4dUKYOyi4kLbu2cPIBkwXHrURMaBpwDOoJ1/9nSbN7sC3dNBW3nXdmvpAcD4jFDNm80UANIT\nZlTZ2Zjl6RtI2d337bF9hwCogEUBQs02tVnC+QFIGkCTngRYteSoe0lEuepWXQaBNCGRlYOYNl4A\nnARqF8+dZG9+1Uw7fHCv7cUChuTkYWkYE0GELabOXvGKs23OjFr76c9W255D7CQmRsXmUSt68h7V\nYPDLPYlXoxcKpwf3Cv4e1v/4y+f4T0hYf/WLshk9S18wnk4iaRu0yVWF9q///DY2qA1ijitln/7c\n/9jMmQ32wb95NfrS/VZfVwX7a+e6lqTpcAFrAuxqqEEfl7ryDlr1E1ygXniKKBkmHAoTchOmBVF3\nqKdIdM/kTcgWp7CqL5UgKke4Y6laZkvYcR/GhwK77fZV9tWv/8yu/vHHrax6MtJbszOWn2Rf+uZf\n2WnL56FZ0cOgoUkuklDnNaVNbK5frDSUgtJWO1bbG5liyFvwUa60LK8QSl08FZ54wPEkYE4aMaRN\nei9JkwZ16Zr6nAeJvuTs+UiN60sOEX7A9jSx4SMFrbG8EfLIp3mAY0cDgR6KfWxcmCaEMhOjWDcq\nbi4BryX85K334dn/4qGyhzhGWzDQK8qoYEQ4HDrEwd/RziPHKwqot5Hf6JATT0ejgNodE5dk1zbY\nrB2S88xkUeQUeAqrADyIpvrxYoi8Qzd4j3yvj18sTrwa0QUiOElydPFNyBBxkL0gmb79yLdqCBv6\nDBpojpCidfhgmNvHF/GiHnZ85WpMcwOna2ByFzpv5/ShXoUK8srVEiadlitRcL6JBgP+CZxU19Ra\naVWDrX/w93b2mcutsGyaSxCzDJDrVt1p/fT/0+ctoNIZwEhLlZ4GkGqTDSvQVlxVgbQqZT1tTVaT\nqNVb6+3lpAWlBRMVVJVaflmVXfzqN9nSZWeTnzKkO+SrEG1CpKSdBzmWTsaZCS/j7jKElMe70857\nmS1autg2rr3b/veH37AH7r3dLrvy9ZgyYle5l5k4yAfBA0OKMRmUXGRPLly3kau/1x9uwz1DlwPW\nICVUuDigSZsdSkvjVlFZlgPMAQ7x1bhzoQ6ydsHp0+yUuTXonrK42dZt1z+IxWsG6DiDrIBEWUnM\n3njZPFu8kGVEwjRua7Y9zTJ2H/TXhO9VzzK8r01ecaTbAvyF+T32rjcsRtJWYd+/5mHrTgJ40NGT\nZDxDR5CB/onMgHeyOnEqyXJxHvq60qSTrmiGwVtmWSVvktamS8wBBTVFMStFIhsvkL6dJh7UtksK\n4EbZdHWzW4GPJbHKZlmCZ+nWuY76jftyveIXKEH9QelJlUBVJdMHquPn0GnMkYOFyCN5UZ79597O\nb+pkfW5PPgsAbcsWTSdM3PYf6reKsmJrqK+xFWcsgta9tm3PYfvuj262k5YstVtuvdOKy/Psfe++\nyjZt3G533rvZdh04bDOn1thll5xppy6d4wPd/as32JbG/TZ/4QK745411tHabhedf4pdcO5CK44n\nMWdmtnrDYbv+5jUc6dqDqbPp9vbXnWlF6Cmve+yw3bpyne3du99qy0vsistOt9NOmUVetOEv3xqZ\nOdxwwyrbQ76mIcF+7auWs6LRaL9b+Ygd6Erad6+502Y3lNh7//zlSA2z9ujmJjv7VEyKVZQwMSyw\n9Y8dsjvu3WDbdu63hoY6z9OZp8yk9Hn26CPb7Z71++zkZafZvXc/iP72YTub5fhLLjrRymBD9lrZ\nuk2H7IZbV9mB5jZbwhGhb7jqNKut0KloBBDSVJVrtHfgGrp7H4wcHIiPcNBfvYrAQQFqJQ1IOotq\n0ra5+RD8i0pA5Jz3Q/jI6xldxRv81OdqsiAeVj7FI5Jc64Qwn5DhrfaXgqfz4W9m7Q6aJbVV36QJ\nuiYIAucJ2p0MfaSl+kPxpKyhSbral58iR1xuVwM/UiE90cZ7+qH7oO/K45A6DfcT7klQgGlgutlS\nnbt9tSarjUJq+OpD+aepkPdcIvsTOO8zxAovMudkyZVbfWJw3PDf6Sbe1aSRnf9ZW8AYU6D5dmBh\nQrialvp9wgXejuIYP9fQA42f/Dw7OVHtifG92qIkRnK03uknF66aUWiHrLZjV1TW2+nnXmwPrLzR\nrv/V9+38l7E0XlRu69c+YHf85nqbPmOBnXjicvQxaVCStvBdPmlKuhVDeWXOzEV2d//Ndv/tN9pl\nL6+0NWseslUP3G6VtTXYxDNbMHsukrZ8e/je+23potOsoDBjPZzws2fHYVu4dBFxiokAKACeBPym\nrvmxLauRTvTakvnsTp+zwBJxTJpgpF1S2TTgUKc9KKQ2pGiAD1xJ/pQvcaTTIyq3aJH74aXOOdBK\nfhqHAigqzM+z6qoyQDQ5UBoaEDy8Bxtff5QxnAAdq7VehnNOm2W3rD6ERJoyajCmmDOnldn8GTWA\nQ0Ec0UsSIAYjwEGxJgQcwVag5XjoOahBivs0OqGlhd02p74A4JEAtNDJchxZL0v26mCxxEqCvYAn\naM3SeJJl/P4cEFSuZAKoJNttRdKb4KjTHtIehKZpOpPrb11tpUUJ29OifJRhD5i4Ygz6pBvTSEra\nWFJkQhLs3eUVUAhmENL9LLBeK2SpOQmQSav8GjDTsqNKyRiA1eU/H05lDuwW8aJ89JMbeaVOmCA5\nyJC9PoEFXodJoO6ztpXTrf7jy7+2grzrCZq22ilVduYZp9hHPvwVq6qtsplzptmPfvSA/fq6e+26\nn37Kpk2ptLvvfti+9b07rYzTshIc3NHXnbLrfnmnfevrf2cvO2+xbd3aZO/9yy9gfaLSZs1psG9/\n9R679Kw5VlFVZ3/115+1wpK4zZsz2+5eud6uu+ku+963PmhnAjq3bm+1v/rQV2zf9iZbungGG/8O\n2PYt223BvFp7bPMWJpVJe+j+tba7Ms/e+aYrbM/eJvv3//yFXXz+UpsypcIeeGiHfehfvmttHKyw\naOlMvr/ffvC96+373/6QnXvWEnt41Vb7zBeut5qKG6nXNHw7aD/5+Ur7yhffZ6+6+EwmRp32jvf9\nu1WXl9mixbPte//1K0B2ndWfOc/51FEZB0d4exchNfnEieJhQJKfe8EbtHV4LfQ1fVaLmtismhar\nKNwP72KjM6ulDPoYjyP3Ufj06f8lSc2LYxwAICsEPngyuYILPC+atAmI0ovS9zEtA8CoLws6wOyi\nV5ZhLEnOQije832atiqLo2orMfopGiNNRFeBUUlz6b8A4QKgPgd3Ma/sHvOGuILz3oDbMSprFO0L\n+QqNs4Ot/Lqdas5jXjdqw0eh41G8RJ4/4f1CptxQ2R5X9pyHX6ClevFUd6vFOaLW2wsM7O07Gs+d\nl+H9IT4einpc3LzgAWcAT6K+qkwdrq4MvOpcRtRKqB8qz3scPXnXBrgCFPDJK1/3TmYUZg/cc5ut\nvOV6XhdbQWmFLTvpLHv9m/7CiovQZYQDsoSP5bN0zhKhxQroKIts6ZLldtaKi+zWm26wm2+4Af3L\nOps2Z6H1dg2wbFVoMxqm2quuervdeN1P7cN/+y5LcILRYH+WTUevtAXzFwMmQjXlMaNJEJ9kAC37\n99ovr/4mQBN4g4h1yvR5diZ6oT4gqCPNlTcMEBRHjrK5JM2LJzrw84s66kABBRO85A+p0NFzLyBU\nVl7sYDOfQV4d8+ijtvTV+HIqlqgmO6f9FGfnzoM2fWq9LZxTZ5u2YAoGaaRZNwM7m7IActuQgM2e\nMxN6SN8uZbMBkxedNdWWLZtmVUgb+1iKv331dlv5wA42iRXYe9/2Ups9uRRaxO3Df3mxtffF7Vs/\nW8M+kAE7H6nq6afPBrAUW3fboN1+52a7c0Mz5NfSutmpJ1TbxeedZFOn1dj+w312692P2QNbWhkE\n03b6WUtt4bRJdvVvH7YDLT32plecY3MnF9ojazfbmSuWWFV5vu0A4Fx/23prPEzng7SnvqzfXn7J\nYjt53mQrBsT2dKeth8nHoe4B+8E1d1sfkx43cgMtxqVz1lN96IfTwAXfqQ91VuVvsJXKO/g/xRGw\nixY22I9/+CmrrIpjxaGb+09gFmw+mCpjN698zN7/wf9nq9ZssGmXnw0XJzg+s8/+5n0vt7/+69fb\n1m1N9mdv/6TddNtGO+fck+2RLXtt5952+5//+YItWVhrqd5eK0LymURE9u2v/Z0tXrqAVYMCW7Oh\nw1731g/ZmnX77IwTF9vPrvm1bVi72+68+T9t8QkzrK1r0NauWWUrVpxhZTVT7Etfvd6++Z8fAwRO\ngk+SthnwI9iTArT1YHv3P77+c+o8z37zqy/bvOmVtm13q/3ZX3zavva16+2kbywChDGJ6e2zP3/L\n+fbRf3gPvNJlb3n7J+zmm9fbFS892zZt329bdrXa9df+K6ojcyzZj041Z9ILq7teude6NiyJ6564\n7kV55S6N5F0T5TyMw08ubba/evOJ9oNbDlozE7A0/OvdBDw/Fs5VIDRq0rF6DrWkj76r7BjrZDcd\nMKE3mix554sEvLkHXV+WKzRhlC69OCQP9aMU7VanKUFo2EfDMr0X+ZWk123buh1Xzdala897Vw8g\njMoiwYL6Rh+0kYSyEqFTr6Qq88RUGwsqvJDioM32tzO8skIjFnE0L87yBh5YMHf7Qir1c1UWtWlR\nM8nEk3NBEUCUe/sQz3tn6RkZ3xyrFvuCd6GaVExVRhjChsEmz3RMeiVJiqSHOptc3a9Lu6TwxABR\nVF5rr3vLe+3Cy650M0naRVxRWWtV1fVIQCo5Dx2pEx3fy9/8NjYbd4RdeUjLvAMsrbRXv/W9dvZF\n2M6ks6yqrrMSjKZ3dfVbed0066PDO+elr7ZFgNfWlt3WO9Bp1bWTOMlotuUTdwLA86H/+0WbMpVB\nRTuo6XjPWvFSm4ldzq7WTo7XrLRJkyexdC+TJionQzcDmTYQaRnYnQ/ieiZLXj515sHpLrrPeXGB\naqIJYLO8tNCqKksBt4E+GXXGOclB2AE7/NW4uVM5IYY2RmgV7n7Ax1UzL7QzT6yzHVuarZsqb6hj\n+Rbwt/dQt23Zvhd6zqTMWsZGcrx4ip1xxhTO+m623R1pWzK92l5/6Xw7jDRqa2OnpTlpCrSnKacN\n9KWtj+VTnUh1yYoZ9uqLFtih9j7bpWXW2jK77KJF9sj2e6wA474J8nXBimXW2tlvLU0ttmhWtU2Z\ntMy2fOlm6xwstKk1SNOmJpAsxe0Qg+TMKQk7dV6RnTB9mR1s67L+rqydd+ok6mKJfe2n65nYpO3N\nV55o5586hQ1nu6yfyc4CQPUgg/TBTWyEot7zqLfQBsTX49GpG+Wngd9daI/UIPnO5ZmZu08G4eeS\n4ri96x2XWF0Z/JjsQwKYb701FXbXg2tY+j5oG7c2e513s9NdY54mXXWTquw1r74QqXW/LZ4/w6qq\nKq3p8GGtItv8efU2eVKpffgfPmvvfOfl9tLzTrUZNUWsNGSxmTvdHlq1xQ7AB9v2trpqTFvnAO2i\nzDaua7RTFs+yxfMamFh2sJSdby976Sk0DUAbusFayM3H5FleHgbtSUcamT428H6wP2aPPLLFXvma\nl9rsWXlI0Xts3oxSO2f5YnvwgXXWAf9o935tbbld9ZqLrbSg36Y1VNikKbXW1Er/Au8tmNZgsyeV\n2yf/9Yv29rdfbFdeeKbNrNOpZdBKgArQrL6LbDwJp3CC+WFSpI9kMmnR3Cl2/lmYdbtbeddCv4CZ\nGtcz5yX1OpoIuT1W+plSliL+z3vOsmKO+ZPOs9qKkkrREcERqLzE7Cc37bJVGw56PjUdFs9oc2cK\nHteETfrxsurhUnF1dpLu8z5OfjMIAFydSG0CIOq6vgTRRF6cpncpypiJq/5kq/dJkG0iyCgKpAc7\nqU94ZSTtdC92CY0xhH/m7DMq3RfDgwQ9chICJTGkn18yOVds9ZdR30mYkbTPhRgvlxcF4IyYP4BM\ndV1qEPyGakZ3/PCTLlBoHd5CXAKhyhJ4S7BDpr5hidVPcR86NWbaADuGH3otvuNSMxmzBTYZjSi+\nR9QwKLM8LEXHE9U2Y3410kICAfikV1dQRUfJrNwlCiz1VNXPtqpJbAzK4wBM5Y9lWpkgQuRpsxae\nRvTStOJZecEG6Iw5y1j30kyeWb2flsOnFNYHBAY1TTLDwKA0+emRC6krq9xIiqRyRswargqjgdjQ\n5yotYrMSy+gyZO+2KvUNcTnze5wKOD6dl5PsCXBuajxkLe2dtnzZdLv5+o3MEvsBn7OsvIQNHr9f\nxWAnuqgK2TCEtYDVj+0HMO60rbvbGJzy7OwF1faB95xvc2dPtjvWdtsXr15tn37v6eiyFtuXrr7H\n2gZKrap40C46ezamfVL29R/ebY2tLPlWAhbZlDSAxDoPyZGqf/PeDvvezx6y9q6MvfctZ9rpJ9Xb\ndJaGN+8GIBBAJ8EMAhikg+aDMt+sRBXg57etRoKZtY9/4Aqrn1phZZUFls8gvGhauTUd6LWvXXM/\nvFJi73/76TZzer394fdbbHAQW68s+8tgumwuj2/nXDkii+Jg8aQyLriGOoEYk4KUlFG7THqy6Dbv\n3d9t7/7AF2z95l12MjrNgzSRJIqZKSRk4nGd0hUDxNAMATHwPn4JnjWhjAPYTzphmn3zyx9GInmt\nfeifvoke9Tz772/+A/rKCfvwR75qd9y51U6YP9MKapAepwZpgZKC5dmhNiaMAFW1T5OuI21ZbSOO\nNFQbzdRnpKWoS3uW7TyZSlKTkY3RvFSJdaA2UV9bQVvmfTIfFYwMKwlxDnQgDQBYGlUK2e6Uv46X\nTLDzSEl5F0L/MKeh1r73lX+yL3znWvv4x79vP/nx7+3rX/kAOrB1pCPQRGKuyBja9QjCPu42K74D\n5iW0SiOKK+88P4bawN33oK6RPQHqAw+PHdXj4v5THlpjCv+QOAL0kvDyo4+1oh+dtkLyfxa6vJXl\nhbbqsb3W3EXPB0hsxQawL5Wrv1UPl0YXlqV+9dt5lCEfusnUWYqyp+gTOZMYf+idEajkUyTNkoha\nppA6VP/NeMB7nbzklklYTYqBPNV3hNWuP5X7Cf/HUwDQnuxGnUztILRchXG6+1V0nXBPlwJOO6cr\nf6RmRf8VTqnzkd4prMY7nmn8ggecgfgBDIbODQPT2PJLsIFCXWtUif4E+JJfZCNQYEzwdFQNamYs\nLx1NR+dF/fILuzzVG/tRlBqQBOQ0S5ZOhZhAklJ1YsQoECEho+sVMfi5riednsaHGKcIMM/mvR5C\nwwVe4qevkVooXu/1dbylola85JJBLYv00zthBX6cI4wyLnDKT3nIMLh09fQiUeWd4uSnOP0MeAa0\nspJCdsZrgxAK+OrIffOBgHCIXKDTs/O4tMaDhyjNIEZZJYg83JqyRza12RUvqbJzTplh96/bbC85\nZQ72U/vs4U3tdv5JmJqgquKSDkGDGPTKdAAAQABJREFU1qYBU02cc0Y9QLLIplUEBe2SQu0qph6Q\nIGpjGEicCQf1g67cpLpyltvjLNF22s5mAYYSa+pMWfP6g5AezcR8JFvQ7sY/brBdbQyAfL9l2wE7\n9+R6qyspsl3o5BaT1wIQcj56mAkGxQQSmxRKpzfcuh4JaDlAdMB272mzuTMrrbwAnkG6qu6mmONG\nS4oqUR+Ad/RLUr/wqmCaH2CgPGoUUOHGnVOecnnz7KmdiB9VF7RMvRJoAFTkA6CkJ+3gADAn8Hjd\ndbdz8MIB+8kPPmfLUUm4/5FN9q73fpJJGKsB3nakhEKVCfhRyQ4k4GVJPtV+gFl24UsWYSP3U/bH\nOzfYe97zefvpr2+2+QuW2a9uXG+/uPr/2iUvWWo7W1rsoiv/mfrB/oTMCCGJfrhxLyf09FuxrE4Q\nV39Xl5VQL/FYkfVl+q2HTV3JeB1dAXlRmmRES75aNp41tZzNToc4y5z+CN5o703bth3NNrmh3krR\nN1V4cC1lFeAkdjV9aKHTsrKUIy+/z1acO9vOOvcj9vCGrfa6N37GfnDtLfbZf8VsFAdCqFy+ZPwk\n6tsPQAi14DRPA5oP9E61b1x9u3VmF2KNgoQBvpq8BZ3JJxHpMYKoXOo5NdGXzrG0kK+5cTNlS1oh\nE+Z6DrSYi1H6n8L7jZz5nuW9pKAnzCiz2ioIA6g/2JSxHYdRTeJtgv52+uQidKoLrYnJQAMTveIi\n1I9aYrZrP5ZA1L+pDvhXV5LxSV5RWZqJaL8dONxjnf1M8GnXcepNvKZ+3Tu4Y5Rj4nVEAbVVBCzR\nowYH/utHbYX76N3E9alTICInnUAyhfm8XF851HdCaJF7PLsXPOB04mvA0sAlCALnlyC1K6C29OyD\nGc1BXbqcNwz16rzQxiF1TyNrMYTX0KKQ2h6i76TzhHyAmbHeuJ8am94xcxdf6Odvw413sNrNHEWv\njRIajTTUDtvKU+oCivJTXNrjzMWzFCIK6ZMXwmjbCzce5vF/FB5H2bx4jGTIXjAFxKALaAkDvP4y\nyDHQFxXFWXYsYVMMVGKApR/mW37KCpfgdDf8FPmOl6vqQicyiCIpToe6f+1Ou3jFLDvtJJacB9pZ\nri6zP9y3i+XvAUAB9IOMcUBIQaoX6Uqtve3VSyyBxKkTUJ4vCRBFlQFeDUZazlOViCAJBiYt5ZUU\nVgAQ0CkUPV3fFpDBpp1snjYTQXdApKiVdj6Bd5CKJaEtNY8/EjESkMQ76NOyaQhQIimN7LtmOPxR\nRrvjxMeKH52NeCXfutGZakPPrwF90n/5yxU2gNRv6pRye+jRgyz/97GiqElI2BA1fusq4iF4XRRS\nRUgwSMl98gNdnOfxzjDRcz/CqX2mAfpZpJU9OuZ12152rRfaf33r59bZimF4bzuBupJg+c5taJYV\neAU5uQ1P6vC3N9+F2aw0KhRLbQCpcwF1NaWqxgpcz1c7wfex5F5vP/rf66ytuYN4Jf3rsQsvOcdu\nvPsa+48v/tquvPRcNgOutR2bdtonP/oOLFsUWDugZ+XKRtpXzM44aQHtLqws6FrM5qWXXbzcrr3+\nXvuv799hLzv3RPv9ynvtnnvX29//3RutsrKEZXrKDUmk5uEnVQkMiQfJs8r2OyTzO/d1YdLrNOvo\ngA9511BVhfUDpKHqd7Cc4CeSiHjHcCK5SK9JpNR2WgYm247WSdaJaaQUFjF0LrZAaR686y53OUa0\nx3it3os+jjRlfUNHgKpPTdPPxVRmOEAlTYtu6O4icLb3vek0mzcrtDMMTFh/X8puuq/RrrtzF316\ngb3ignm2oKGSFYWU1TYwAUSs3dOdtNvubrQb7sKEHX3u/BlF9s5XnmiTKyscgCbp7zY1Ntn//HKz\ndQwA0ym/64+q6Uy4p0gBmGgUu+UeRvk9xSgnggcKjKChTPoFRyOgfeufuxFhcgHG1eUFDzid/qoT\ngTYGYy0tqhfXoKHOLlxD16awhPK/YdkYf8KqMtX1CWFEpjUiiYCkhD4YCsUpJNIHDYSKx0Ep32j2\nrl2RfhymwuX8FGuuSyVhDYzKjXyE7sI8cWhZR0zFdwE+KR0BUb7xAhKW2b6PTniEMii2xzuBGUlw\npasl0CS6COQqbseUSBaKShJWW1fGoKXIGfQBY2HnO3nz9IiZ/wEYucfjE3refVRGKTZIzgl+Qdq3\nl6P7tu5qZsCqs6mY2tHS5H0P7wBQl/h7VvRQgdCkpBe9yzNtMqDhG1c/bOt27mPjUK/9+0ffSnwC\nm7ASg6+IUAht8pFwJdKl1svu5xji1OlaZkXumc6TNBQ6M+vXEqtkaXJZIUb4QVJv1bdqXma4UvgJ\ncOoXNi5o+U8gixVX+MyXAKk7OIyU9WWKQwY4BKC61HbsPYy0mgGYNw/ctdVW3rfHOpByujktdsL7\ngQVI5HMVqGyMI+etK8e/uvBMeWVGKg9JXr42wlBqsL4fgsCWPCRgnNqkCmOC8PIrltuvf3O3feRj\nX7YiwOfpy+ZQv1rF6PW6KmByVSSptNRTqJMYS+1FELZEOsmA/t7+XvvEp6/2VY80upUrzlpsV1x0\nNkETdsXFC+zfPv9t+/IXiPe0+dgJRbeT7Kn2X3/5ebblsT32XZbiv/WVa62kNGb/3z++xQoL89g4\ntNgWL5hsX/jsj+37xTG7/76v046CmksiMcCqQa/9zd++wva3NNtn/99/2eeoqlJsHb3hqvPQT70C\nsJtyCwX51BkQkNRCyxdHwSHe52TS+fa5f7sGqffVqIiYLT9lvr3xygs4Ck/cQdsGMKu7ifqMJ6pw\nrbZIPciwtNA8UG/bWsqwnlBpeWxCE2+KEWWtQV0CXcXYOPoecbFLoTWRy0UsfpcEO5/VBqoeOtA3\nIbUvShSiglBj96xqtO2opWBgAr3qpfaqly20396z3VdsSlE5WDCtxFo6k3b9nRvZ3Z+wS1fMs9de\nusgeWrfd2tChf8crTrIZdaV22+2bOVmp1c5dPsvOOXmabUc3+5YH98EprGZQyYF2Y1PUF0ssI1lj\n5NzEhxIRYWSAFwtRxqicND3aSs4x9ouUof1oBNGb0NJzIcbl5QUPOEV1AQuvCjquUEkAT1UVnZy6\nPEmf3KnT1VSfjs+X0/H3avT3obMVqBOkUzhVs+JRnFFnmeJ9mkFS0gZ6QP7IJIdAgsdExwnJiV/h\nFJPC6J+SHXYKqxkMV9IOIEeqwkpJP3phBYmc3+Pv19wLLgoZuZyvBwllhhYMMK4CwEuHvnTqZWwQ\nqgVoxRnwvJPgnUtXkJiEBJSOYsi5kYlEfuPhSuZlRkj1AKmphrh1ACbufaTZFs6us2I256ze3Grb\n0X3MAAbzKZ+qQMvcGUwLJVjKQ2hpg31Zqy4ts0vOPSm8h2ccyqKbm2E3+9TKmC2bU2AHe2KYt2m2\ndnaGz59aZJefM802YNZqxrQ6O+WUZXb1L+5j4iHgII6AaCz/JdDpAwo6nUVG1UVEW01o8tE7S8Av\nyr5L8vhey/gZJF0C++KqosIiQC+mmcrLrbFxm/WxZj+IDuC0SWXWh63XHhQatVlCy9PS7x2/Tnnj\n5xfANUZRJ9WX209/8gk/NCGeDz8ilTx/xSn2h1u/apWoLnhglthnszR9w7WfRm1i0NU/atng1jfQ\nb6Usm8oO49+89y32rj+7yirKqTnAfx76sT//yccdkIm6b37txdjtvNRa2joNs7dWX8NSLGBXbfB/\nv/cxO3gY4Ar/1NeUW//gADvY1RfEeC6yz3/yXfaJ//MedAu7rQopcwVp5jMY1BYn7ObffN7a9vWi\nJxxjAlfARsB5tmvjj6ymRG06aXNnlNj3v/V3WECIW3tLt9VPLqM+AVdItsWLb3vj5fby115pNaU8\nSZ+T421/8J1/JMeDVgaoveTis23Nwyts3+EmB7tTOaCiAL5SXQuwiz+Ub3Vfx2qm0gWDm6ypb7I1\nttQycWEHLDyYofPMSKqOnqSAbnDeUnL3z+yi6tbGHrTc6RvVw8GvTLxC/+c9HcuHlAFrHC19Zp/5\n2k3MG3pom8W2PdFnpy6ahsWCydR1oQ0wkWAzvXX2Zez7P7/f7mzEuD79azGHZ1x23mxbtnCm7Waj\n3hLUUdav3WcPP7zNuiVJvW+zLUM3e+n8MvvjmhTmzaS+kWunyt+Ee/IU8D6MKoXh9JMb191OyOK4\n/6t24niBFuJthme3qgCmGWrc0YtxXJoXBeDUMmeWzgw04VUh2Kk7l3qq4txfcwU5VaC/zQUnrJ5z\nrSfAzBzIxFtL0B5c0gEBTICLnr2R+Qu4gAcN9q535i+VRPDT7kml5kBXN3L+sbgn4iDCcBvyB9t5\nOP2RbDJygR1DZogi8h55xdPBNZ2CQLgkRnIeL0r6xWygqa7GViHLZyFtxaLySwoReR0Rs14c4aWg\nz7vzDCPxY5otvUlJpmUod9Wjh+y1l59gxdDggXUHsDHIgAO4yFB+H5ghbhpTVWs2ttj0+lL787ed\nainMUBQXFrBcrWEQREKBda799l06pShmb3vbBXaQXepf/vaddsPdm+31l51kb7pymfX0DVgp+p/b\n9xO3pOvwoUjup9IwyKoiU6BaH8bZVKJlefEB+925lvANRslJC81DHFMCyqJ/ScIM8q2krJ1t7Fju\n7bfJ7IY/7cR5gFjsgJZwLCZf3LByj/0KE0FZbbRxMy+q6cBFinG8OGd32k6Y/ukvZfW2k7JKRFZq\nK7K2ID7MSwxymhalSMLvgCq1N3YqoM6QtVmYj1JblUS5sIxyMvHTaVNFiAW181k8LcmfVgfKylWX\nSA+heYK469mEVIM9yyzxZ7BSAMJ38hSw03tWA2IzciUVh3gB8jjlhYoUANMmnmoAaHVNKZMB9Qt6\nIZkdVizYWV49m8QBQgqdT37rKsgXUjbppMYBh8VIPYsxdVWHDm7W5dO8V9mZ/BVyVK3AaZzyyUs0\nKEXJVxMe/jCHAhZS10tmcyIQqgY6BIAMcBWtGJjEI6SsGvfBiSuR5PLIe4WVF//SSPlbB2ussb0C\nqxnQga/82F4kwnksMTN3IzyhlZGxcopPoDia+PMk4Ctpq7rSJMAzSXJpwKbUSmJsAJoxtdQuOfNk\nbKYyGWO5fAabrpQlap42qQlG1rp6s7ZXJyXR3qW+snsfp94Qdzn2bSdVV0N3s4XzJtn7/uICYwWd\njXiGZBxITRvPB4Am+M43Xnr+xqqwL8J4XFgiyk+4saWA2o1c7qqGfJy4Fzzg9KpQvXjnQWfLoCVx\nYtDF02whdLzqdnU/3KH6k3dUeqf/Xr3+vQBMqOE4cQVH586yk7wFHOQitvBNSOHj3Oxdg4Ai5Ee+\nonD+kf9RLPoNv4lAsV7rTfgb7oJHSNtf5V4Pfz3k659JrUCG4UUS/bQ0WwwwqsJuZIE4whk4lGs4\nF9x5XvWRwoRvR8Q8zm4ZRtl087uH9ljF1j0AMMlQ8q2pA9uUN2yy8vKM3b+hCT5AmY5l1XXbO633\n9xuQgPTiV2C/++N2O9zcimH4cuwbZmzL1kOcJDMZE0qABSRBWUQp192+idNk2tnNzNnyDHL9/QW2\n8v4m29/0CANakW+6am7ptTWPdlpXZ4bzwdvs2nif7W/u8jS0KWHT3l776W2bbceBNiQ0Gbt33UHb\nyslSh7r6GGjj9oeHtlnVdsBRv/TntKkhyekzu+2xXUXW2ZFEulPNJKHU7lq1y/735kd9J/MSTrZ5\nzztXcCpWvf32jlVuCF52Y10BcARPjZcKk0Arnzajc5i0/C1wJNNf4jdtmtKyeVrGJUEKkiwKwDnY\npCbEv9Jt9lbEez0OtQTaZmhdim+YkxXIVUvw8jZLY5ZJHU3G4ug9poVICK8JosfoiYQJKXgzOJqH\ngLE2oQsQZ9H7zYuXAJKYsEkVQHqihFU/EVN8hFa78vywTK3VEel+U1h/56o64iv354/b3iUFp4Vk\nlR4s9xZwrvIQ2IGlfyV7waG8fkJVeBnok4vVgaqiIU2fgEPaNABL/NwyUGdb2qZaFxMf2b1U2dUz\nhFUg1mdUhCi/Tks9j4UjYtE3VCRl0i5x4sVLZVeyMV9tQTeZU6X+/u1nWRWnUa3edIB21GTlJ8+z\nAszCSbc5TErgIz5SHXrdqRiclqFLNsVEGrpK9rB1935bu30fJ4BBc21Eg8CHONlqUMdO0T8OK63z\n4YR70hQIkxLqjC/UenR1N3QTeUxcnwoFInrqGzUPUdc7GKfxcD/nDcffj88/L3jAeTSyR7yvK93L\nCBe9GeE18tZf82dEsKEmlfMb8Woo3FAYxXW0cCPTGHU/KrZRb572gzpyBoxQbgYeJF9ajqqsKEcS\nhAQujW4ckhjJmULqoynknniFd/r7LOTxaRdu9IcsItv9q5soq3QoAZa01Bjg5L6HD3KjJWZMqoAI\nsuwo3rynzx7bs5cvXEOQ5feM3fVwm8VXs8Nc3wEO1zUyQCFR80Ea6WIzUs2b79jLRnUWOZGIcfAn\nvyw2NzvtUWxBgheRsmjxVBuHCqzxQJft3N9OXoqCxAZQtZsl/b3okbnLFLGTnk0pLPWnJQHj3yqO\nNpR5nGy6liDUD/W1duMBf6dNMIlCSeVkvwBJGfqFGQy+52PjKU0dtmBKRvVIQXO91BF1OZpcz9uT\npFkDEKupv9L6qIsYqgRSQ3ErDw44BYy8EgAVEV8+s+wGUArQQNQvcCXAWZzXxZGRHHLv8uEcXzu4\nElx5fLqRj+JKxkrZcFIKgJFkGrsFAWnyGfn2PGtQUDTPbR1oiboizmlZiR5yCZCSRB9QlwXECbRn\nOUWopa+BZfRqdDaRxurEMrKs8kY5dUoo3w6O9W5sXIhftI364ZCGJugCxTqyVawLM9Mu82zOrAb0\nlUvsd3c02rU3PYj6RNpmzpqEbVIAJwEVk6hchTrMwpklHPfZglS0ALurDW6poqmt15rZZETRrA+A\nevtde7AwUAVV0NlkQqOJgyY9AuxeVEe+yt2EezoUyLWgp/PpxDdHocBoej7+KbQn+Ye7o0TxvHu9\nKAHn80715zkDGrQFjLTMX8zGiEL0AB0csYyXR8ftx1Z6T/88Z/SZJK92B1hI61i+vBLdSrDtTTGT\nYQcwBU7qKFAJo3mXYRe7204UGGfns5uqYmkxjVRRg7aDEoazjDb3aDMDu78zAo6AvhSSE8UteJdF\nrIWFVQbyQt4xxyd8mqVYzk3hawF68sM/hdOgJiCrFVed3KSz2JXDNPFLwiKwKhuD2rEryYwPgsSe\nQvdTi4f6t2M3Jl0OdtqKk+ejozbbAWcpJyEdbAMM344h+BjLrQqJ3mKQ0SmN8eVk07ITsL4D0KOj\nW2UX0emDpFDAY7gL1d1IN/J5ZCcb+R/NT98HkKl6kBQygFgtq2estFYG27VRh7pzsDkyvaPfa1ta\nZ7rGtjUVWT/HkQqxSXfYDVY54Bz5nfJ27PwNfxGFlc/RynM0v+GwCY47XTyZs9ull+xL1/CoR4kk\nF2Df0j/JGpuqrTdbLQEpSegP0xRvMFE8SsNRKNeR+dH7Z+ICL0Y1LAmsltQ1IRboTIv/va8SjJSx\n626n65J51XbGyZNs7pyptmjeFN6ojeWM/CO+rCjOs7dceZrNnLQX6WaenbZoMhYbUti5bafMcWvc\n22kLF06yN736LNuANYeSsjKbt7jW1m1psntXH3CpNBnJ0WIsy/tMaDXx7QQFjn8KTADO478On2IJ\nQgeqv7J/VwI4kfopXbbHk5UelHRew9NTjHt8Bde2LLeD5QOXBjOGNkZVDaY+fGq8k2RDw5kkUnLa\nsCF6AOqMAVnLiL5Zgm9FI0mE444msMPK0nscxOrp4B9zKQmDIxteUPZz4O6nVbFEl4d5KS3pOegj\nD25LkU1AMnXkZm9IyeP33EgPWIOthmItFytfGvJ9mkD+BGal6xZnKb7f/u1799qSBQ0Ymac5I7Fr\n5zSiRzmq82AnoIKldA3gGerVwbXiGmdO5dYScV+GM+1FdzKqnfgQjGILWuMAQgGYjE3mc7UdwBf1\nIYCD9iL5OOygxicFIjouCvunUwaiIuHsZYf3QAybm/qGepCkTHUmUCsn3dGwVH3sGMMXz/xvfraL\nCVEzbIyEHx1RNxgNQTPZcqR99di3rLSeeC16y5q+kFvyKsmiTrDy5VHPQq4Azzw7R8TglMoRmPpG\nt0I8r590mQdoXwMknYYndPLP1u0ttpYjYk/EsP2733oRuprGcZ/d1jC9lHrTZFB60tgW7h1kI1a/\nXf6yud6rHWLydc1Nj2BXFH1dAOd//2q9vfE1J9l558yxl62Y40C7GXNYj2zQyoFWFogriHmPyO/E\n4wQFJijwTCgwATifCfWO02/DoKIFNoFLBh82JUiyKUmY2+3jTmHAAMe18/xr166UBIWnWcJWWbXM\nqXdBn5crhdUgQ6kptwZXACJAULt0BfwE83xo5JVsIwqUy1eASF8SAz/GdAFbCSf9HbYQIa5OjNKp\nUhlMFMVzeolZNrUI/EomqnxIh8zNXPGch7HyNLuxBWwdIAM4tVNYBqsjwJxFJJonHT/ykkbKuqe3\n2PY+0u2bS5R22PAAUGXQ1rGBWUlaHW2KCEp3fDmZCyuCcDG34KC8Sd1BwEN1ItqK1oKAgc4K8Uyd\nqKBle5miEj9IB9AtScAvaFsEp3ee5rHSpT7gqzgng7lJJyYvql43M6S8S2eANLjJAc5c/M/JRdJC\n9ElJXzlQcSThb+mrs22tdcg/y1xiH5f+q2js/E4wgfDcKod/N4pvjkWPJ1kwVYKct4XAmT4hxEvm\n5n74szuwrZlnTV1qt5zMhFrM1/73YTZ3JawIaxrN7b0chpbHpDmBdQjameyFElcXu9S/+9P7rYd6\nzDDxa+bwhVasTciYfQbAuWVPyr783XvQsU6it15ufX2D1taTtbZueAC1AlVVQialPG/6M+EmKDBB\ngbGgwATgHAsqHmdxyBxSBKI0CAp0hu5+2HcMx/bnjTo+LLqkQmANYMgIogE1KqtADBCSH4CMqyRt\nPvY5OBBIEz30kSQe4er7YX0kUlzE5ABVAzXL3txLmigAq4jCRcCQQV8DuYAj8em7GBJKXZUXvvAw\n8lAYLb173jwzAqz6StI+4hUoQAIdELTyIEPV2mihJXtBJsogCTUgIxe9EnMdyAi+KdXx5aA1y+gu\nxQTtyZat550SQSXKJOmb6ESQURkPoYLXyDeR/9H8FFr+oZ4kQfU6dj7AVzwB7fReS+OBIfTNEznF\nx8//i2+0gUXxKh/KP8/Ui+pMIT1uv4Ynv/Ww4c4jim6P6X+sOAQ4RUfZfiX9bKm19dfb9rbJ1gmI\n0+ZBb/5Clc6QyrN4FMrk+HhUFoby9cxvnJ9FERXB25pqg58mSeSquVerC1BNEm/tzuKpI1WAUX8A\naJtqrdhbbduAeKaQ4zAxSQXYz6BP3Uq4HRwd6qZa0Y1GEQXpJnVC3cgaautAIb8C29OuloXus/oH\n0s1nkpGgLanqpeox4SYoMEGBsaPABOAcO1oeHzFpHH2inGq8eYE4B3IO3sTmPqpxVQEDCFQxpX8X\ndgjriXcuCdTgQ3gHnvLX4MOzAKUDOcWhTRcCFXJ61g9HGB+3la4cD9HA6ljGwwmAMKJpQPf8EYbB\nLtzrO+5JJ5uz25kbhkNw3gYRnMLre4ZoX3oWSBNqkK/ADWVU/iPQim8w7T8eB1EoBJ38tBl0TQXO\nZP2BoT9Hd+TPUkugVAIkFIqfXKB+uB/592j+o/0kvIyzZBuqVfVKGg56WdL3eBVe5sGenNMmnAy6\nvwFSAoZQrxDY0fGZEhSqbmRf0qsjQLwjIh6dv+GXT8X/8WF13rx2bPsGISZFzT11HNHZYB0YddfE\n0/kVCaymXJFgdzjtkXdeCDwi2o989/TvQ9vQ94pfcYefVg6SnNClEgUAymY4UGCwGyx+UDjxNLQG\nRCb5FSH5Tw8m0FuW8gDv4CFfSZAuMKsSCaTYKerDDdlDC9no1CRHU81w7prqR/FOuAkKTFDg2aDA\nBOB8Nqg67uOMBiZ1rgI4en7hdbQajkL5uIxyOTDofpKOCKjlgJ78/DmANw/i9BF40/icAw2AQclY\nNFQN0U7LsQALUTMstWtAyy2xu28uKIOcg8ooTZfuRHXC1YFoFDdB+dbxryRvpBFeE4dsuXh+AJvk\nOQVY1nipffFD5mz0NQN5FNt4rGWVTcBPcM3VGwB/Mi0mia3UPrSZS0dTCjyIcqLHWDgZ4hdllLYD\nDmirE56CNFmUiiCYUn1iynkZCO7yQq8DnVgF1Ylby+yeDjG79HuM8v9kaKCypaBnks1vHbKz2aoN\nQhUO0pyO4kX4Jiy4D9NVd+EXlTu6PplUn3wYtdHhVCV1Dkd0qg4E1sX4Q/zMO9fVdAk4klu+dH1P\nn9TFrJ9NgD+6YQPAP2ntoM68ZCGHBrDzHGA9iCTXpZ2UNEwBiUuzDvGcckDcukqVW6oQytOzU+In\nT5uJkBMUeKFRYAJwvtBq9BjlEfAIQ4m6U3WycsNda+j81RGPHAiib8Jb/8T/RP7DPuPrLsqfrqG8\nIX8RmMSPwUvAJiylBjijQc4dQCeQRqAUH0lMpJ/HoBVoKIlbBEYiMCG6EVjeWpfjRkOjwjOc5b6L\nJKM8kv4wwM0Nv4BHj4NoohjYwk4UkoySN88fLx24EgLgqWFUcMfBTe57lwxpMCacl1FhFGzcOdGG\nfyoSIFzn0LsuZJ5M3osC+Pm/nB5iLv8ji5KrMX8T+R/NTwHkr58DDm4Uu5wmHtooI8Aik1Nhg49e\n8IsiVcCjOL0O9RzkZc5JfKelafnrfajdUBZFoWgjNzL6p+J/rLCeJ+jZNlBmO5oTbIuqA8rpJB1S\nBA1ropJECupcSl5DrkbGGnKovAfpH+9GZjYqwNO4BtuoUZqKQGnAzrQbtRVJuNXWgh1jrUQg9XZ+\nVntVTgklRE0oP3eeu5Y+lsxdpQGD7gqPvrR0q6lR9HV1AlyoW20O9OmL2jh3MZ7DqgIbvbx86hdU\n6jFyyuaQG/Uw5Dv+b0Tx0RQZejpeizT+if6CyuEE4HxBVeeTKQxdhAYW7yD441f5MQBLWuZSBQZN\n6RhG0dG7R2BpCIbqpXr98ew8f5GUShmNpLkCMXIqBMu3nLk9rQR9Nu61q1xneAvg9TLyNPWwkcAl\nbgSVpIooMpz9o2Mnw2Yegjo9GdoJJ3ihE1I0ZPqgDvLIcj59fXnM5kyvtx37DmG/U5I7ThaSfUEG\nfNmYdEETSYRTYpSGZGUyHg7o4r2xOQjZD9cAUHWqjQ+I+pY3nhrBgv6oJJ34KV+ECkdm6nZ81pc2\n7+h0mcBPWkrnFj8vl4A2PgIBAdDxmHMKMexG3PvswD/gtWILEisPC49riVuhtUlJcQhoCozo2FDp\nkjr40HeSKHvAEXF7JI//47VBvFFtiH+kFqDl27QALHmKSR1DaXv+FMdwvKMH8qfi/8RhU9idPdRb\nY12D+djZDPZfpROspPmr0kt1EQppQjMclzhK7/WTG2r34fFZ/Kv6UD5IXzzBVY0jnMQWcjMM4AXu\nWVVw2oYsCTpnOJQhQSVLRUOklp61ZLjSqw2OuB1s6ylXyhw9oveebngY07+iY6ByoK8iD6WKnsPT\nmCY6BpGFXIdezaNzHhJx1eOpjnKJRNcxSTNEMooiUfw5zyMexyDViSieTQpMAM5nk7rjNe5cKw3D\nMN0FjdfNgPggz0t0ocKuZnUw+hcGbe8ZvQdXwRRJ1NzHa0HVKwmwRE7P0S/4qWwnLphm73/FIuFJ\nBzYRXfZxOtCnvvEgQxi6fkKabMYZ1JI24CQGWHQ8x7KsAKh2wGvgc0kLA6VLQQEdolyCb5afPMte\nd+kJ9otbEnbTvbs8nYQ2ERFW9jdTWjZmQIzzrGM2tbM9Kzuh2BEMOpnECXAlM8RJrAyozAlceiOc\n4NYFXFqaKyKeDnhVRyP9Q7HH2d/Ag2AzcssARn5ZUechAvGiYs45GNG96nG0c25UcR2ohHdhMqDv\nCZ+jr+RdcfRedUSl3shOagZTV3GOhoyz4SQme6hSmaAOnOCeqxDf0f5qwE1QZwlMEBXmNXkQTmEk\nH/mY9YE7WM6VzVYOTQyYbqgMI2Pz3I/0yN0/Ff/Hh80Acg8N1JK+3kFPJxv3IofaMlxCafWgv0dx\n1Ii/GvneIzlK2KfmFWIZGS/3ypf+DLXbwBvuRUY8rx4mpOVAWS95F77LGqdUennUnj0evWaK54+K\n1xuuTJ7JRekH82Nucze88CRzt8/wEkDZULUP35C8yioIrZ5IedHzc+0CZQItInocLQ/K28j3IizP\nI72O9tkY+A0lMYI8I27HIIWJKJ4LCkwAzueCyuMqjVzTHXHRYOQ71x2kqD8OIFN/5YZtB9LE3UtX\nNfeoo/Jg4/TP0bqlYT9JnqqKSzhjucQ27Gi1bbsPIh2RhBBzLN1apEbPjzKnAeEZjMWrxL6j2vX9\nGCIkRQFUyIyP0wawE8/0c0SjTLmITGGrzqH9HbZlS4cd3NeJtKuUk18Ao2lOtaEFCmwKYAb9Pslo\n5MmVfKQBtn7MoU7A0UCKQXkNsr6IThoxzMKQIElTpuFihTE1qpER/pHX+LqSQaRYAukjsxruNVjL\nRQNy4MlQ2Cj0iCv1gLxSTMsX0AbQn2Wy4PXmQzpnyXAyVB0S5/JYC+8DuE9SbxUcOyrwqBR8I5km\nFtz7ku6onCk/w07flMQHbA6HQQ3Gm70t5bFJJU2ddqM72dRRZgMgaJ+EwFdRCYZjeBbv4L+0T2yg\nAIUJVhXgFoEuJ1tE35CHKG8BvI3MlwL7ByM9x/4+l6ejRTwq9dyD537Ei6Hbx9/kcs8Lf5cLMPL+\nWSthxFUjSyU/5UVXcffz7Z44F0GdQq0ocpqqycQc7UU01BKNSpEjaxTqmVzVpUZOqyA+eRTJPKua\nII7g3TFMN0pz4jr2FJgAnGNP03EdY2iX6up0FwY/DbEu7WBg8j4QyY+GbYHQ0OglBSHsUAMX8hnZ\n+YzrIj9h5kQHnYsuXHfvo812y707/CQfidhi8bCcXs9RebOmV9u+PXuwAVhpU+sqrLU/a4/ta7YO\nPsxgf7EAgFiW32ezZ1dytF61DXK2+iC4JwWI39PUbzsOttof7mm0Pa391kdatSX5NmdOHXHut9rq\nSptSW2I92AvcvqfN2lJFYEwCIdEsBVo21JVY/VTO36Z6mg8M2h7OfE7llVo/cZfn46kO+Dh24sTo\nN7oY4tMRhZPkUgHdS38CD0ffKGz4gh3+Hogw2hgiSSa8nWBwFz8PYkZnINFnDdUDVlnUhgpsH8LP\nsPM5z42/65siotcudSrRl/WjVI52zWJOp9sml3fTRmgzLNUPcnR9DwbL2zE/lElJtim4GXJ1tBie\nTT8Hl08lAcgKa+UoCFUFTodoLaAx4Z42BSJSDkUgD7nHvQjez9lftacncGo4mriM7PeVZRhFgDNs\nvDxGHE8Q/Z98pSidRKN6ArzcMzBp7vZPxjHxYtxQYAJwjpuqeK4yok4jOO8eeFDjPXi4yarKa6y0\niKUmdS4M7kODtgcXwIw6FL0Jb3NRHbcXLWP7RgKKFkd3M82mgmwegM/FiUgnocMJM0vsL996sjVu\nrbI5U6dYOacz9SG+XLWlxb5/3WrrHcizKeV59udXnWSzZtU6uCgGrGdAiD3JjH3/l49acX6/vfMN\np9l//2aTNa1rtLMWLra3XjHXdm2fZLNnTrYSjhhNDmTsoUd22w9u2gFY4YDMgn678vy5dtGZM60I\nA9gCDsnBpP3ydzvstkcOIUED1XB6kVdoVKnHYU0IwkhHM+IunoZKoWEmFE38pkBDQ00ujL4KX4bJ\nkcLIBinhhzZkgTuRLErinIe9RtnJ7GJCwKGGlqjrsLJ4PyZ3pOvLtwKl6HFKNYIZF7/hXA1l6mg3\nbHCKwTtxnQwF/2SQRO/vqOA8+3JM9mDeh3wHe6pH+/i58QvSTRUxUPTIVEeV9IggeneE15GfTzw/\nIQVEvYiCoqZ+4vNA2cgnCsGL59ANt7ejJ6q2RxhWUrz9uTQToUPQWQhtxMeMo3/9jHxzBNFFI5CT\ni8sQnYZu9HLCjXcKeB2O90xO5G9sKaA2OrKdqrvZveeg3XLbHdaD5A6Ld3QvgTUcVtIbatlYvzAA\nq3uU03O4O17/ahFX+pmaeZ26sMZec9Fce93Fs+3Kl0y3adWy/ZdCJyzN+cxmJ544yx7d02TXXP+A\n9SX77MyltbZsNgAz02unLJpiZy6bzNnMh+x/fvGQNe7eZxWA0E2bd9ieA4etMD9tVaXsnC2QLlkS\naWiSs7tjduKSabZu80H79a2r0QTtt/PPm2P19ej6YY9y6fxqe/WFc6yvv89+9IuH7dqbMflCx/6a\ny+fa1EqW7ZHG+UBwvBJ/KN8s+wIEUwBF8V6KXdV6TqMDmQG8pbjqOSMwx7sMgDADIM9ouTzHpxqJ\nJGWRakMcdYdYhlObYE/ZWswjbFa7zolHOgyqgQEkmId6qmxvyyTrS9VBV4HLoJoQxk54XHq6knRL\nhHMMR0gHclLF6E1X2J62KSylN1gfR13KJI82RYU0jhHR8/X6yE4hl48IpEJ46K2eYsI9HQoEfVl9\nGfFS6EM1fcowEwqbNZ9OzM/BN8oqbUA6zZJ2Z1A1cT1yb3tqU0zwJNl/Eu3kKeUWUjmVuGq/o/Cu\n/5jI5+VW4zxrTynSicDPJwUmJJzPJ/Wfp7S9EWuAJP0AWOKoDObZoxt32P6Dv7BXvOoSm9ZQxWDL\n7lqXhiBbEv5UI/cuQAPPC2PwEYBLSg+TEp2ApLFhymQAScwGWGY/fKjVDhzucT1LkeGP9+yyq2/e\nbsleQhSus7e/aoU1VBVYPpK0WTPLnSS/X7nVdrUMoKOZtfnzGuxwzwC6oGz24QhBdZoOYNgtLNuS\nAja33LHTrr5lMwu32IAsrbI3XzqPJfQyO9DSasuXTsW0S8xuv2uVbWMpPR5rtdnTi+3CFSfY/Onl\ntu+RHpb9y6iWkTvxSeo4c1u377bPff4bfpRieUWFJRLqlgCO6MyWlZdydGGxlWJFoIxrZVmpVShM\nfhxAX2rFRTq2UWAIXVttpIIWeYDN6GjCjAZK0QMGDhvjpCrCpAq6plBYONytowzzbUZdgRUnmqgj\nvgdcaUdz2Dh3bGKqHtmKjhYGxypate1un0S91yPZxLIA6ZMy6Wsih0UEb3Vqec+9+1OSTc+JE4m7\nKGuS8IoGQy+hCQ8KFnoOfzHx5ylQQODdDdFDQW+z9KfiCulq6534U5L48eBkpURuiGcAm2Q1J2bk\nhoMN1LZ8t/8Q0/gnY/bHm5X6AQFZHmIOMrmHEcMmOLxpV2p/Ezw5ZmR/ViOaAJzPKnnHX+RqxPqp\ngfpypQYWjSpI+eIFldadzLff3nKnvfyKl9iMyVUsMcvsCJIfn4XrS4XN/fzheP8jyRNQgKKtvGeL\n3fnQbmbwJTzHrbkDwJ0oZTBAp5X+99D+fuvtw5g0Re7qgCb0u4WFkrLFra2t1xfhX7Zigd21arMt\nmT/JKdzUAigEXCZc0qaZOnSXSSX8dGb6tl1tHLVXjprggLW0drG8b1aRX+C6m5Mqy62QU2JedelZ\n9hJtIoLuNag8qN+NJyrod9Xpq9MNVXK81oRLKxNFlCnfOnolSWdpWmdZAxyb2wehExtxKFw2PYi0\nOQbNsRoAMYoAm0XQo2FyvVVXV1hDwyQrA5AWAVSLCmWCiIkBIDAAcuklQyciEr0kodF1kGMNm7qQ\ncCI6mVbbbxV5HfhT2dEmCL7SN8emMBYMUmW2q6vSDvZXMzZrI9ggEnINjtQ1IFc7pX324qVRnOPL\neTGVJQilMovGbqZLjC46hFcKgRsKHR4n/j4xBRwV0fa5iq/j0Hao/1X/CsHHHUVlAF+ZIr86Olcd\nntpQQHiaRoUNd2orPq1TYA/wxKR40m9zfOh54N6vJKAhK49+IC0Ar+yMP8o96SK+2AJOAM4XW43T\nOKUvJ+Dju299IFSr1fJlsZVXc+xd1yG79tc321VXXmTzZzXQwJHQqcMU6vIr4b1zQY9HV7X649TJ\ndmYCACgw19LJBh/OaO5DsqsNI3mSUKUxHM2ReJI7pNTBQi9J0AZZogUvsgwuo0gpJKKikdmFF0y1\nl5w/FQmb2SOPddr6LQDODGvpLBdLZuCyVGgo25syWp1koJGUVUcQavlXpNQGpjRL6kpxoD9pD61u\ntAO9pKSOljAydbNxZxemfDhBRuDMO3yCH6fOT5SJi/+gMxu1fEShLBL2iB6ipfyyBcVAtoz1QYfM\nQArbkhjvbu9jE1WbZZLa4JMBeFZbTWWVzZzZYFOYME2bVmdFBdrwFmwzakCUxYG4pyMTV0izs8W+\nvF6Y325VFZ2AAgFOHS3KQDsEPon+TziBBfHDgc56O9hRZ0lNCqi7Ihgk6KZq4V8lERfpOj6dcgZ5\n3KmZp+FJSZYH2GQlebDaui/9ahPV+C1GrgTj7QIPUP2ZFNJ3NgT29/RbSYls64qZkXLC5BHtx0PO\n/dhTMiJw7HXNRGywda/FCqqtoHQS/ppQkWPprSiMT9DGHvqp3ajfk9WIjCZA5EmsF1N/yVUrcBOs\nCCGOEzcBOI+TijpWNkd2VqMbIG+GXvJm6GVovIpXr5MsIWfz2DAUL7WyminWiwTv+hv/YJe97Fxb\nvHAWjV5LuoRV4KH4ws714TgV2/HlBHa0NCTwmEUalgRkcB4efprNM0gkkD6gO6gygjuxw9njYDGV\nqHR5VSHvSgAny06osW4GkRvvWs/pk2wY2d9na3e3WyvRJeJI6aCt0tDRiTqesoB7+kriBsSiuxmX\nBBkhgkBpkiW2QZZ5mzo6LTW3yhoP9dk9a5sJqwFf+eKYPs8TYFmRHO+OgcuNfWv+AoAWYYLOG4zG\n/zyN1NxkNKhJj4x/sXxtDBIBw7IvwlEHle1ISFu7mm3b3oNInFNWCNic3jDZ5s6aYXP4TaqtQLoE\n0MTGaZ6f1Y3pcHR0k/ES6xsso5I55Ugk9R3qit/RLs/KQWB8vXYPf2YgpAa7rNb2dKGHWsCqACgz\nn1EyB9N8oNSyYII6FgBVXOPZDZnnol6WLV1kv7t7HfSW+gITJzI/1PyfdiGiGJ4vQoxR+ormmEUI\nabnReXhAE9u+3m7r6WqzM5YutHyAJs3dpXXP1nJ6VNonqrhh3iaD3smL3+mN4IEYfX+yfZP1tW23\notrFdFJ1/FBFoTlmdUKTJmWKXJPCp80Tj//Q+0eiTrNCsL/VbNf+QevCVF1pCepKMwttWg1JIxBw\noupzZX1ENH+yahQoF1ZhQu5DaHH3yO+i+5HxjkjCb4W7qdpwr79R4Jxf9BhChL9RvCP9Xgz3E4Dz\nuKhlBtUhrtXgG5y8AqN710Aj0kAtOAI7cw1DA6EY/PJYZnQdNh4VA1YCvaFm2IQxIP0zRaQlSF3Y\nvVtWOcu6m/barSsf9OXJWbPrPX7tvI4GzTAwDWUsl6vj66IyCEgjbLTTF9a5jUbRKosdxU7OYv79\nXZsgVvFQp5SHJDgBQBHhvUMUJQGnnR0pK1tQZC89Y6EN9CF5WxazFd1J++PqPbZmfSd9UKCT6zxR\nHymkGsQCrTWHlySMZwYe0daP4uPl2k2H7ZyTp7NJaJlVljZaa0uX1U+ptqmzJ9vPf/2IHWqnM5aR\n8uPdQRqK7TwbiiJawb+uzwblqCM9B5fTK9QH+k6V4LeiMNSk7mIsz4vDtS29H4lS464W27b9oFWW\nb7QpU+ptyZIFNqOh3sqxEF4A/SQ5SVIfGYCtjrWUZFntTaoQVK63EwHirKQ5udElbJDQUqjorw1M\nSGg51UeLAOIn1amalNduLv9RCfhg3Lkob05NlZUcapPgSUvmWYrJ6O33PIz+SKklStkkV1yl7sXp\n7XquXr7Q2+iv4gh/VT8hnlBgj9XfK4QQi2S/7hRHri6DR/gb4uI+dHQjXz21e9WbO+VMEEPOS+tl\n8fsofQ+qcLlvvG+MnsI3uU+pb8LlJkTiU58EEbN6ZC2fu86migYtsym2Bfa0WH9vsy1AX/yCc09F\n0i6ZPeFIw1PLRe/ZG4M/yrriDSXJPejJ0wlv/K/Th3x45y6dUvJDmJh1WLpzo6WaH2XCxHs2DPm3\n3gcqvOLSpEyBNVlUbGPjxBK9qTy76f5e++Y1u+0Q5uVY1EAIYLZgVrF98UOz7ITpAcII82rFKOtS\nEdJXU1WWlD+c+omhnOGnuauKquz2p+K28zDnumFiblYdVkto55SMlSOPJvctE0iKqXFCUUoX2zfR\nKm4iQj7gccVoK+Jo0U5/lGqQzPKR8sDLGH2K+nhlyFsa/Y+CvxjcBOA8HmoZLlUDcA71/MKp7gKb\n6p2/pSWovSlc6OhgZxhbgzAqgPgyMBJSuw37B/vtwAGOWWzvtH37W2wv6CXlUj3Jaxg4CVNWO8MG\n2w/Zb2+9wy54yWl20tIFNDSBX+JggPY0QkaO278qbWd/Ghmj2cKFk2zu4kmuR6mO6FB/xm67ezMd\nTbHOErJkuhgK0rMAYjIcTclrP0kmv4AF1KTOZjbbsbfd2tkkNKWqzE6aV28L59fbJ3evAsJwtCAB\nBjD6Lv2+JPQdgI4ZHTeY6kZ/tkDyOAyEkw56tFl2Za/e2GTX/2G7vfQlc+z1rzvZOzThn0cbe9Wf\neh0FfUPvHvE5fp04OHBx4GnnYXF1BAJyb0eV8IjBbSioUB9gxsEQS/V5jBxZBsjugaxt3r7fNm7d\ngQmwYmyrTrHzzz7Vamu14SvjppG0+zWGyCmNFNoPHAqUJlntVqe79LhFfblc9ynRNLVHotQJIQUu\nNOxwrwFKQCQAVg1F49SR19CPiIvlJI3LWiFHJi0/fRETnkJbtXa9bd+3zRLYmS0tLbdEYQkDKCak\n+Ik+kkB7HaoicgBSO7AloY7AmAZY/fP6UTJOKydV+EZ+OE3EIkdsONFYDhqHG/4O3+ViyL05ir/y\ngwtx5YL597m86t7RQAgV8ZJ/5nzG+9zHYQIU8ue8grQ8LjElgdUn6pdF911wJQ1CSiVRuunttL6e\nDtQ9Cu2CC86wpQvnYApNZVJYLlGCSn6snPLrxdZNLvNHxk1e/eCPFGIKx43SC5fUUGpE/ZZu22CD\nLY9aAYdZJJlUeYSeV3FLjmZON9FD78fGKduDpHDH+h77xy81sjHT7LWXT7OZ0wps09YuW7+m1Tr7\nkqxOaGmf0il5NXt1kHyHcQnnGLW/uJAlV8fSAqX+AYBSKw7U2/rtA/bBT2yxUxcX2Rf+aa7llyhC\nvve+IJRHLBBLUmYauM/x4dsYg62Art4lNfaSAJ85MJWf+gLNUR2hkiZenkffBIW/twnM3Ul1Jf5s\n1L/SG2duAnCOswo5WnZC01ZnHmbNIYyzL7eBydWidP6MBk69iTEIpOgIdCZ3Gn2bwy1t7LhusjY2\npuzcfcBa29q9o4vRsWjQyC8st6padjzzdegwGTJ5V1hRz4zc7NY7Vlv/QMyWn7SYBs4gQgNRvpT+\n8eykN7hqywH74L9uc/qmZXcDMsYBfD1IM/vZQPTQY63W+Jk/WFs31EHaqbPJVz/Wbh/5/G3Y4By0\nupoyW4ZZpJ0799l///Ihax/It5JYp/3duy6yBfMmW3VdkT2wcZ9t+NxK6+5l8CXuux/aYY+t32It\nPeiPJootlU7ZQ+v22q5t+/BjaZY6y7CZ6Dd37rHf3b/FqitKrLiwCP3aTutk2bgH6atmztJtOr5r\n4EjuOaI0EZtHwY54HXmPvg7TJOgcwqkMDtJHS7AhK78gn8Gq39ZuoQ62brPZs6bZ0kVzrWY+0qbq\nKgaJDqSk0D8zAFiFGTSg+tK6usugaiEvjSienSPzODozx+GTF47+QdMxjZEDtnjxTJt3wnQmp4dt\nJ4cVNDbusramfZAGPUQkyoVFlWz6krF8jmhNaMIKvaGLxnZdXSKsQVWjsCSb+EkKKIwjN5qETtXw\nAgr7cnTuaSjkyCD+bnQMQ8GPiFn+6inlAgRQL6ZS5iJ09KcwUf/mQYfCihpBbxA+4BNNvMOpYagc\n8S45wNSVAxuSg3020IuedXqAgx0qbDJWJU5edqZNRbIuU8d5gDr153KiVeSG7yKfsbiKNn+KPqHk\nMaTzAk++QTKBSbEs6jxtSDYPrUfDSJPpKI4wvjh9yKx0nB2A62PRUEQZI5eCt37x+712sCtrX/37\n+faWS6uwR4yUOF1ufX3T0YHNWgt94eBg2uorsVuMwERgjsUpa+F0jR5O3phWDbGp034ty7cl+Y4+\ngM2G02riVsykoAU1mDW7E7a1NWPL2b/Q0l+COhMHQpRRz/QZ6STqMkhAD7N5MQMory8psKpK9Pr9\nlCUBzZi1dzBecHJZYaLQDncmra1/wCZVFhAWQtAAuvtjdqB5EPWehE2vYayN9XudE63nzUHpCBYc\nI/KNy2gmAOe4rJYjMsV0Sf9CU1aj1vuhJ26jzkCvAKZIdXoRlfUn07aFgWHXvkO2A5DZ3c95z/kl\nLBuXW1HFLPp9zJtrhsjPhxZme66crYFGYEZTQsBQYSX2BHsT9sd711p/Z7edc9YptGE6ZM/Cn+7I\njijFuHxUN8mQYQe7OS2GqavO1aaXoHzoSaoTyEOSyax5P0vmvnwrQM+AOcBs/0CXzl+KWWUqhsQ4\nbZOn1NrLLzqb5RmWvmsTNqWhxtp6k7br8GHrJr7udmSbbkcSW41EfZCkZCo8xaxb4B5Bq+1r07Iu\nS1rUqUz39KIr1deXsA6kczHO+jbi8QGK8GnE1nHZlqS+Ag8Q6XHpxEhO7NG5H2ItvZNTbR3bRcvs\nUUj/irhE4wwAQU0nzgYk0S2TZLDZ3mF7D6y1fVsz1nFmqZ15Sh3HXHYhr0GK7xvISF8Dq2Yi/BX9\npUvr+QGIxjhRSJvPXjhOIC/Xp0CrPMqKEohbCJg7vRb9uUl21kkLrbW9x7bv2m+79x5gQrvf7Y2m\n2dQWh855SD/zmMgWFBQSATRCfOZRci+7tw5Y+Bu5AN0DBPQKopLCW/UzamWi97Dz3nAEUPM3fOBd\n0nCwx92FUkX8FKWvq+rU/3LPszo3XfinFXPRQ8vnqnMB8WC5Av7BckJPXxcBkWYm++FiLcmmkAgX\n29yTZ2NebpJNqq+x2iqhj5xWrzb6SeqruHLldOkbaT7rXORjBVkh5VwBczTjwAsaipakY7E+y3Rs\nsTTL6PEYBxpI0jdE60Bhp7/ao5bRXYwvmqrvVLxj4IgmzRr2/r2sNBCdzMFxfILFERyXIBQoKWUa\nBJ999bv7bPveHvv0P5xgJ9TBK+S1nz72U1/dZQcP9NjXPrnY2+sPbthvK+9rt4EeLEcw4XzVxdX2\nypfW2me/ttXu25W0Lopyy30HrXFLs52+KN8++oEZbNjM2ur12EC+rsl2NPXQx2dtRn2ZvfNV1Xb5\nCiZY6I/uOJSxj/zbDjv3/FoHn3+4o8naGWfnTC+1f/nLuYDclP302t22Y1cP1jUSdtUl9fa+q2oY\nZ5ncIrZVeTTWvljcBOA8Lmpas2o6XBerqGvWv+DUZemfukr2xVpnT5/t3LXDtgI09xxsRpLDYm5h\nhRWV1FsNnZ4kn2xp4IsAljQI+CqE+gsizSAFkj1DT4COXjqLClvMzkTZFbzr4U3WweaYLpYzBDpH\n6cbk8nQ8XTT0qZPKyy9jpRwJMpt7vPOHHpLkMopQnADEhYlENR1fGDSvGFgBG00tvfabW7fZpRdy\nKtDyaQD6GPUgJfdWu/HOjWxi0SRANSapCL+EBlH0BjE+roFcW4BE4zT0TssGJWEkLUkjKckjPzEA\ncRoJaFi2o/slg1mAUD71pJW7HFscT2QflVdRRr/RTj5iyqfjQnuIvgyDJbTSPwCPwHkYMJHGMfBr\nwtXPxogHtw3ahi2HbOl9zfbnr59lJ02rRG+NitQmIwFOsqR6kpFr1/Ok3lxKxS73o5UgSv/4uYpu\nkROni2OlQgO1ABWioPzULKpYYq8sK7FZM6awYhjjwIh+2kEbEvhu24eqTlNTB5vouq1pbwsSfKk1\nsKtiUKAAAEAASURBVPSOBEi0jsWRMmvTl/oiQIOWpeUve5RBb1cpQWxnCv/D85FOPsGX7AROoU2p\nTzqmU1+K09dKKdSqHkJfoEmJJJc0Ov4nfZKSRY0mjbRSprkytMU0J3yl+JVy7O2kukrUMqq5zmai\nWcOvEvNcSH6ZnMZkVg6CaYoZ5Uy9t5uZc9BJus+Lo9xkyPEnNMsidcuwvJuXZRm9dUMAm2kAtCqc\n96pzSYJjsqShSYXagD5WACFV3vnhCkOlfOaFkl2PBdPy7d6NZl//yW5r+KvZtmQm/SOSzBRpC4oe\nBineu2nANu8ZsPmsJEkP++EtfXbTw9gsrsQ8WsVk++w313Ii3EG74twGO/GsGtuxs9n2bUf6fH61\nlWFHuWNNu1XSZy+eX2aTi9KseJRxpEbcfvjHFvvCd3ZynHG+Xbh8FuoRZj+/eStS/m6bObvYFk2P\n24bGbvvDoz22dg98MZixc06bat17OuyWuzusueuAbd/RAkgttKkzptntD+6xxqv32asvq7KGSsCm\nhhZNZHJgPuKPZ0658RvDBOAcv3UznDPvE9UFa4ALTnNlddiSKKiht7Z3sclks63bsJmlb5ZE8ous\noLje6qqw8chyCR50EJIyiMGJgzjVTaijAOe4vwaWuEsfGFy9g+ETmbwgcBb/fDYLVEyea+sa97Cr\nHePmxZP8nWI5bh0F1aATdK4AI66nJGDIkhf08Mk7hctj3SMN4dTJacad4Ds3RwQdkwCO+ze02Nod\ne1nyYXAlRArJpGbEPeh26rScPAauOFKwFHRUZymgmxawJI2wg5Ur79J5dPL4qXKAQ+SLGyQiefRO\nrvMTwy4ob1ztgaXOmJZ8STH8uByvjiJHCv4gfUAM9eLMrrIFRxAxqj8MS1zCu9F/o1YSfMMX4a8i\n9TYg2kLvDPTGNoHTPoZaSR+TgDW72m3Hlx+xc5bU2ltfucSm1PSiXcsQxGAcBldNCgoBr+QNMJrJ\n9hKH2snx7NQbiEaiXfQTV4lT1T58DcTfaUKmNhM2VzFhgqblxfyms20Yw/cnL0L3G96X+akk65uH\nmlqtA4sLvUh+Olgh6erqwZ5tty+FZliC7+0DyBHnAEuRSjHBkrw6pRgrDm6+SvwNGNVBAL5Jh/TE\nG9I91HufIKsdqSPzvHPJuRRAcbQj35QlzYRNoC8rUAm4lLwxzX2KdlqIPnWCzTGFALAibL4muBaW\ncggBALuIyXtNVSVSyzqrlJ1cTBwlmEDmM1FMaFKp9go6Y82DK2nph5OUNoBhwU3+yTvqiPnM27zT\ndJjf/cNn4Y+SDtnizidPmuyGZfRM5yZLHV5H/4YGJflSP+T9I21SlEzIXFueVlq0ssJ//VQfkvCz\n+qLQY+UKOXL2nVc12MObe+2PqzrsnR/daO9+w1R7/ZXVVg0wLKTeZmPFo//ONtvODvbBs4psEFMg\nv3uwF/WnrL3/DdOstbvDVj7SDrCM2Wc+utim5nej9sBGWcpbVhqzj/zNLLvx3iarRdXm3/9hiU0q\nbEWSn29rtvbbt3+4yyZVFdp3PnWazZ+StTYsWNy3YR8bRPush8M/tDL2SCO6uao/9MQ/8eF5duWK\nWrvtnjp7/+fX2L2PHrS3XlxvH3rXTCupqLa3frDZtiLpbMd0SUMFPK4JCcTSeAAXjxXZxnU8E4Bz\nXFdPLnN0Ct5J0ZHJvp94U8fkpWDYbvRbHt2wzdZv3MpsL2XFFZyHzgksiXyOBpQdHjpkSej8Izo4\n7zxc/0QMPszk6lz0FDojOr3oQQMNwMiHGwboODtVSyvrrKX5gA9CQX+HD49TF/S3BDApOcWWDMcH\nWMrjkix1ppTepbkOUlymhYI4nS8dnroM2c2U9LdvEAkBY6ZrpqnOoDcyGw+rgVvOpaekpbiVTozJ\nQh40Vn2GOgbSSoqgzjuqgzAcKkPewcvf80u95GROHtQTOB7/OIgUYAhgkEI5LUJRBISG3VCYYa+j\n3Ilwkct972Ak+AmYCCBqCJWZKlLmhQCuwBVS5/w6a2dz101rOm3jnvvtz16zyFYsK7VyDcq0KW9F\nbKYJtZ9bLlYDOq6dWn40eREd1BZUpvDzjYLy5VG9UASLohDadRuGzzB4FrCRToBQYKSmfBLvJhOX\nFpxVx3l+XGtfL7qBfNfTyxGt/z977wFm13HdeZ6XOueA0MhoBCKQIEEwiWISRYqSKMmSbcnSyLIt\nBznueMdhvLszY/sbe7/x58/65rNn12uPvU6SRx4vKcmSRUqkEilSzCQIEgCRc2g0Oud+YX//U/e+\nfk0CBEACDXTzFdDv3lu3wqlzz6lz6lTVKWYZxln/KMVsoH+QjXNZ1pn3uBKZ8/TjKKZSdCgmCnAl\n/R8KBEql4Agh9HDxU0tTi9OVMiqroNQETkMj7rFQEGurmKqt0fKKHG6fKrmvQeGs5J1ZRVXKT7nS\nGs0KpmHlD1YbqaR8a5Do7rogL3Gul0+cB9qkNnIpBn+OnkTDERV5jLqeUAKPJXmKmd/WTQnCVDZ4\nBriorxPUIaRYSpTr2265rldx6SWlUt8uJNe6SGXT4QVplM2C4aPIt1GKCsS3uohmoB/weDFCKDNv\nG1cybY6i+Ad/u9OefHHU/tNfHrTjfWP225+cz/HAnA63uEkEaSdOTqJsGkuYUvbQ947Z8nkZu2tz\nE5udtIwJGsPH8te+fcR+9E4GC7U5q6V71VhEh22Msfxs3bKULW4ctxpk6hh/X/tOr51iXef7bl9u\nQ0MJe/jpHvvO1h47eGTEbttYZysXVDGYyti2vZppMk6dW2AfeXc9h3aMQEes54VW1rSl7bc+vdg6\nWydtKDVKOnoX6Lu9QUf1ig7BJwLBNzXJsvEOCGWFc1Z8ZNQKRvhZuF7TUepe+wZHsWbushdefA2n\n43iDrGq21o5m3mudGuIUJksx6nYyjn7UFXsXEz+ro9BciXoWf+MRfq+fEKsculMmOBSL58iA/PEA\nhfcKSjmbgzpN/mieT5t6Z0xn4DjS2lYCzfeppEi0yLqYZBukRFhBCguCS5Pgml73EzmwOk6yOMed\n67MQNE1nrOly9EjHZMaVFqwKWMcUIxGtq+48D2VLaXVLkn8bwSc4pSBF3wELrE/FULY2McXfikSz\nNKhd0R/fIFCcfwRvj9Maj/o85x2mIWV6RqFR6o8GY+wLoD7xgZ41lUwcfJZgGcm+wRH7k3/Yak+x\ng/WzH1lvS5oli0ddyCaxzrl1DGtnGNSdN2RXXMKAKtFYFPwDCGcxEuMrcdC/lMmQGrqFRvU2POtX\n+YJA5cZxK3zLoixLjhTFiuqUNVZh+Zc1G8EtH5Tqt9xaZguUDRzDY9SjfKrNLfzEiyW0+z2NVPed\nxOT1ga/nKYFThRBCPyWYCIJbZfq3F2yK4rvLKhm0nFAWZar+cM45beOdlhuFdoX2MqL3NN5c3sT9\nYaiJkgWolxlgAm08B9pWOR4cAN2FNCHyUv0CAPAITu9O6MPlE5ijD5hCf8EmT71qFX7ghHiBNAJJ\nnxNFTpdKLQ84fQDPJadohix0+jYMLKJdL7gsxuDPEpSLFIS6CvrS61cX7G9+d5N94ZFu+4O/2GP/\n8GCX3btlgd24oYClOc9Rtwk7xDT2yOgyNreeYEnZpP3cj7bahpVofcD82Y+023/968P2nz6/yx78\nWsZ+62c67b1YQ/motnX3sGWxxm9ZV+0zGHSn1o/7kSdf7MLabfbAAzvtXx9gUROOLJpbk/bT72+z\nn/3kImttyHNISB5PL1lrYTr+pz7UYbWV2tBUsN3HBvFMYPbxO1dYZ7v6/0nr7p60Hiybi9pZ889U\nv2ZGRDdOEuBrJr7+Rfosb6uYssL5ttA3E5khSXrrSW1eYAPL0Ai7mV/Ybi9v32sjE+y0a1zMaA1m\n0c5Qjar58x3kULC7u3AQ1cmpg9OVFz71oReh03NrmafTj96L/MUKuqWzV4eujolFLEPdh21eA9ZT\n/nrY+RKsRZ509v64woYYdKfs4AiB5MKB+DwdnoSd70wFJXJ5o1G84qT8uY818KUp9iQaopQ/VxTV\nCVOIBKg2/0zSx2iheAXKZkoKpb4PnbWjGhyrRCmyOtdb1s8cUzQq38t0zPKOa8rr1jfimwBDsCep\nrtmLfic5cK71cmFaNG4LjSq2KwjK+M2bXgPpFvNKkY/p2fP5ozhB65N5AKdSMqU45VjLpvPuk745\nCyUkU81u1w77zotDHHW6037+k1fZ2oWc7840elKWNdYgSlGaVv6bAnelvowVxYBwx4vfvt5iBd6Q\nyuIP9SlKor+AAeXiyf8jWsCLpsXd/6+/4UtI4RMKXMkjCRYopZfVVC/kHkaHLrhSxDtX4nhW0HcM\n9KGpdJWl3cKBLlzhJG8IgiYO3EePujhf6xq/5qVv2iBCG4EUVLam4mWBcnc1lCsPB650wncBEqWc\naq+eihWFh+jXC3Ylt1i5ANJA3/mXi5JEEE3BNa2Qi/QQcOH9vfzkoXAlbQg/m6+wbnM7u6fDBiGQ\n7PXpV2D6E4qULHQ5uSxhPWpw7UP7eYl48CC/lR5C9ujhrV9UnPel0EZjchgFrsm+/3SzfecHPXa8\nF16lwnnNOWtsTtkhDsd47Wi1felfT1sHlsVPfWghm0A1KDf7xAdZ5rGuzf70C3vse08P2H/4o922\n5Qs3WG3dhO0+QM9LG9d3NvOtWZvLN2dFGhvhsrZmVav977+y2Va2sMOcsqrZIb+wgZdY7xWOUGdv\n/4jdtKHe2mrUg7NUg5nFba/1Wk0VHl2uZdDC0owceN51dMAGkd03XYMnGGQBEyTgXsijlRH+vNA5\n/lNWOC/nB44ZtAhDFCGG5dafuPezoBFsx46ftsee3MrO0F6mzjk3upXpBLlvIY22nWj6NmSFIcis\nDjp0F2IQdWl0EOowXctRpdyL4D1ZuKoL9UL0mqBd63qWcBnpP4Wz7Jy97+477Lmtr1jPwSHKC+ni\nPHoWDOHXS+NWdYfYKPUVdgndfRKBo85YsBanEB1vao7sMmDM2xc6Wk2lO9bBp9ZiShkUTn1KPMvU\nLHFh6lzp9I8vIHyDS7dOcidn4mHqURhTvbLmEE/n59YeWVp8+oqyVTdrp3yq35UlvqsAulJDKWh+\nr59AEz4tCR68TfyojXlt2kL5VlxIBdWJ9uJytMFE3bQEpVugRL9qvH4CfYUadK8y+Y3ySplXucUA\nXQf1AkWCyBRWNrfgU70q1U5qH7ApT6rexqtxj3W420789xfssx/qtHuuaWDwgMLJt09o9b8DqTqc\nyyiTb1zks6hiXQKY3FxpQYCVAqc+4wxB7aV1Gup4/8GvBK3oOrRSWJXSqKUGoNKVcdEwePGPQST/\nde9jN6zEQZELFkbHuXBPPs3q5EjkazQp3fnHf1UnT9CB0uvJN+XFH5uYqRCgip/FU2F5TPhOUmhl\nJRVQbtGkPudxV2hRLtRSoYJ0OoJWCq8efaMha3gDVERwN/XnTfQ2i9dVhmhMeAoh9CUhvWJK8R4l\nuRiXqOlFyGL8e3OFzTHLajf6qZfBCWuBBCS0rwG32pvy3aSKIg58qInCmgfS6LspUt9WwR/97s1/\nohKKfK3nmFWEe8bkNs60A1+CtbFgmD5SA5fh8aT19XIkaGXCWutTVokJcl5Dytoaq2zHvmH7wkMH\nbdvufvuFH1/KlDcbLRkkZMlXSRnXr8qzRnODfea3t9krrw3hamnSFuLofT8bgGpZf9y2kD3w8L77\n0oSEx/G5mYavr1o+Zmv8uFuUbRqYJN6ND3zKw8dwq4SrvPUcGAJIhjcpPyXutV0TTKsnrbOTpRnA\nrhmU3YfHbHSStCvw8+ydGhnUWNGZ6E84fnO0zYm3ZYXzsn5GkVjoiujOnJnVgYn7mDHild5pByh+\nH/HZ+MQPt7KGstXqF65gDxB+GMXhEd06t5SSrI+gKYTivFNQWimb6lC4VZcnJg8CnkSqT0qVeldX\nctS1KIHclDON3nvS0pM9dv+H7rTFi5rtha0wnoQCWTUqJKH/C/WRz2FRHRJAU50SD1dWEKjChUOl\nHeEhuCHDu7w4hiv/lZJWe6J4XaZ6TuXPuQDiRoJJF/8RYumM4s6eNzqxQiVpqt1xQ0JPqmjyagSs\nwYOsm4oPNBLqdEXV8a1yqTUk8FRX3g80BC0pSDT7ueSOKdqGcp5yGi3YyqWL7YP33MDmE7M+nNqp\n/5USIKVjYHDQJvB1OjSEX0PcfMkLw8gImwJ6+/04ViQRJYItlFHt4perL1+6wL0r8hKMKCYuQylX\nwiIoPvpiKPVYsPOUMaFNYnxvDRwKWqCrQLymHJNMy7ElxHIsWznUX2l//Ld7rPDxjXbfDa2o/RyY\ngKUlq+lVZeGbhhNJ2OjF+lx9I8GjoPeqdTYEvtCZwXR6ixWmOElI66+iqPCtQ5tD1PSWS9ETf4RY\nvpEeCG7R1JVvprdxvEqaVr4Sk7kYO714vY3CG9sRqgo8qkQp0UeUOhXRq9flCVGWo4rj+v0awRfH\nCZLwF5ekAqO3XBQb9xt6E4IU8UsTVK76j4jz6PpDX6PavPvAkp/rfd7y3a/SD8mdUYj3thYbHKAr\nYpBH4dtLLYks3nop5/fjSr9kjYQQBbgLOpBUgZKbpf4//KtjKIbj9iP3LLLO5Qnr4kjLBx89Yc++\nMsLGnDpbt4JZCfI34iVg2fxKe5wZiP/x5T12zeoK+9xPtFs18/u7DmTtgYdP2123tdnChRl7fmev\nHe7Hb/LCpHW0Ij/xldo7iM9U6t29Z9RqcG+3cjlrdVmv29mRsiNHBu2hR49Z9Z11WD2xhh6C7vFF\nfQ/T8RWcTbyLzYXok7Z2GdPz9BlyKH/kxLj19uVt8eKMdTQxiGU97ATfYdehIXcrtn5JjctTXxoV\niMLz+Tc5P9TN6lRlhfOyfr5IaXAYnI0RhgyTRH1YLnWG7OmeYfv248/YvkOnsWp2WGV1KwKR02kk\nlTWHERXh3YALcAorUi+cTAcqJVLOcN0ao2d1KCiCWvSehZHUqauj1dSX+lHxgZQpWVZZzWbDQ93I\n3T774Ptu53SWduBCCc1AOhLORfzpnk5cSjKwqTX+Flim0hQTX1k3AtZD1HUWn+P4kqt3xiHBtGQ8\nxKP0kHp6q4tpizd8AsdSSURcDVES+FNvzgTX9PLjrFfWVTCKFkSk+hPJ8ATdhLZDG9B7NRs32prr\nPF1zE+d1g2PJIu1AzkG4siopx8RkmCIbxWn7JNaNYdxzHT/ZxQ7oITt+oovdz2wgQfEbwz2JPArI\nQpamjILWzLrAZaqUkVyKXajaoSys6p1LPH9icAQPhQGULMwxjjUYZPoWfszj3DxbmGd/8QBOsfNr\n7J5b2LmcH0X4kRagXdHiXq3VoGEWUD+QXr4wReOXDwbVfGY4Ir67INDOXNL0vuGtlHtBQHif6+sr\nvS+GEumzw1ntGmShbHI2eu7UThQmuV47/7IvIOlZC/VeweUCfBm6CHUMxR5PYqyJHUHb9nXb0//3\nTvoHBqAYYKWs3LOl3n7nF1di1YSzyCxVef4KdqcD2DKsnb/5U51YPZkap4zDJyfsH7/RZX//vVM4\n28f1GVbNhvq0/dpnOn2KPs8I95oltfbEswP2R3+111qo58//cLVtWJG2j//IUvvTvzton//bffb3\nX8alF/0KzpftY3e32b03LqRfStq+3b2cWJawjqV4N8igcILr/QdZv8kmpA1r6xnO5hiQYvUcTtkp\nTkdaxFrTxfMYmArhIhMNCNQXxt0MUXM9lBXOy/mFxVkSpW7pklB1WesjHibr7HTvqP3rwz+wQzgS\nb563zJ2268xmZ01RqYSc8nsbdC9romhZSqaEXdw98JlF2/5Pt2F9UoHjyipQWrUwXmaYAgwgX3k6\nok5niacwX06M99tw3xG7i3N/13QupArc9rChoqmxEabupjbVL5UilO4TTs5Bgiowk+toAqwc3lkY\ncBIINCqFU3c6iUb07nSCsMtxqkd9Y6srauIAX+8HOcpaqVNaitZJaKyS5RxSCCvZLqyjV9taOJ5y\nCXk1cIJe+3C9M4jbna5TA1gntHngEDtQh3kPz+DPVFYeHcPo7nAECv+IRDFUEHQop+Ib6tCShhAn\nqHjH+l5xk296SddbLxvE/ubh3TZ/yXV2I3770ER5K9U1cIIrzTzH3Okvyz9lDMwgBuQfVVpagrXR\nSZ8yg2+SDMoGt9pY13arcXdgMwhQSVUCTX++Xp542U6c3xAWcrP12U8sstXrW2wX/jL7Tw/Z/LZ6\nW95RYTdcU2ttOH3342PJP46m2X0qgW8J1mreN8/u2cxBJex30DT2TdfX2R/8byvt1f3DNokboxXt\nDbjsqkYZrAItyLGKpH3uE602ryNtJ3vGbMWCOlu9DC8FzI3/JOs+VyypspeZfh8cmLDGaqyeC6rt\n3ZSfrECyAvBH3zvf7rsjaZuW4jqL/kfGmtUcvfkfP7fYNl7NkhvksdpYXZlnHSn+apkxWbGUfk39\nSdReXgeFUzfvgFBWOC/rRxbVyXoiUSariEZtCE+o9MjJfvsf//wwHUSjNeH70vCrOYlS6AvcZf7x\nfAJeeXQl+JUfJ2ZZyPhD+fNzz/UaBVIOw2VVGhsa5soanvEhhDLn/XLOVipdZ+0LlpJQ1iHOjRjm\nvNr+g3b3bdfZTdeudWun3D3o3NiGOlyJ4BQ7iz/OJKPHqHJBo+zAoHYpRPCEB48p/7zTMABBOk3Q\nbhQ8hjX8odhhNZhEIZSjbLmZEQc4mTgByQJPeo+AqtwCQgryyFm7rKS+zpJ1WipLSxbaG6ttPpbS\nlYs7zK7bQN6U7TlwyA5z0taxk92278BhrJeSwNTPoElHuhbgiZx25gJiMseaK0SRpsJ9ba1oORoM\nTqKEyiUZ3mi9DHkdODnebp9nTed/+Xd3WVVdI2WgSHs6MSCDMNJ4wfwqiDeiBvlz+aeMgUuNAfm2\n1dpoDaqShWE2B22zLBuEOMAxKKHOa5caijOUH/cHql+8jBCja2BmQsonU+Xpcbtvc9Lu36wTfRqd\nT3UiWwplMMGucvH8OPz2Px/qs4f/9ah94Pp6+/V/g7/LCu1kpQz+atjI86F319jHbqn1nsVVPeRh\nPsXmKAlNlMQl7Br/5R9t8iVsFI+yK2MMFtb0pH1gc8bed10Lokw9lmZGyEt++c3Uvw/f2eCSOwWO\nBZH6KE3Fd36UvRVql5R8rKgVzCZ+6O4GjUbZb0RaXlFEFN5ZfUJZ4Yy/+2W5Yj0Ro/FP1kmJ2zwC\n7WT3oH3zkSdZC1ZvdY0LUOhwMo3wgk65YuWRMgdXQP4OdaBdvZUgl5AjEInqCtGTHktSHsvk2Bg+\nx3C4nGHhSQ0Ojav4+o0L6q1z9Ro7yJT99t0nHAIxzvhov432H7WNa5bYTZvX4fgaBcBHybA6Va1e\nvZyzwL6LwjrMWcLVwCOrk6pVVyC4AiM5zMSUwzsTA6JvJxjIQZThz6J5KZh4PUhDz0tQEEUhWtrh\nS0UkFKA1LfmQgunr/XivzVIMi5y0JEA1hSVbabiqdCiPqXYpfdoSpEnzzmULbcUKHJGzYP/EyVN+\nDOPL27bj83ECv6mcIiNrKfwgK6nzFmVqbbJoWAJIfyqHEZ+/F/ziq4KbZKo53rTJ/uyLL9hnPnm/\nzwwEDlAeB5hyxAchaApQMJZDGQMzgQFZ6jMsA8m4wjRg+aF9Vjj5CnFy6i6GvIy0KLaYYg3nCt9g\nhoImBU8eAjLIIYdRzE5iSbdCVpnE3Ul7eseQ/bcv7rdmptJ/4+dXWHMtVkullFKnK+1jX1DgZxhZ\niqLWU6s4P66TckjKYDU8e12K0HtpnXAxWzS5D6B6qbxWnEBTbJDD4vJQp6bpVbb6Dc+o8li2UwEs\nYTMW0Xrn+QOMFOfhMn6NAMAM/JYVzhlA8tmqcOEr4nWriMzvFdbNNPq3Hn3GunpzWDaXEscqELcK\nQahOqZQGM0gw+1oQHgP98syNSF/WIq3ZmRwbcsUxO4Hri4lBW7pkvi1cs8wWzm9nemIRTo/xG8lO\nvBwbLkZYD5dgd12CnbeT2TGmMQ7adeuX2j3vuZmpASw7LmgRztSnOjL4dVixYqEd4PjGqpomGJB6\nsQ4FthPbK8BCGrZ6cOCi+/LlnYIBKZZSHF3BFC2IDLSeDJc2g4M91jGPE1tqOXJVKho0JsuEArdO\nS+5cO0RRBpFuNVQi0nMf02OULeKJMJXlSqOn51QSpMzyRW22jL/NG1dxDOMArsX22dZtO3WQkyUq\nalyJzGLtVJ1JFF2dj+6CBP6Uj0F8JAFUmNYPz9SPg75ndvXaoqf327Ub1rvS6edsY0VVCwSX/gRn\nOZQxMKMYQCDIxVdGG4QGWHN8+jUsbMxwoQ1pY5sUIG16mWnFU/wghSwWZ3H97jGAl+omtG/PNz1F\nDOQWSdfykD7k5Zhze/q5IfqPlP3kRxfZxtWoo/KT6t0CA1MSade4np0PSa/NYeJbV/wUq7LFnYJF\ntwrc64W6DVc69S76K6bSLiMppCiRgsXd1wnoqEHKR5dBObwkaFYwvle0K6O8Uv2y2qrd8T5FpZ/L\noaxwXsavK1uJGCkLYRbY3NA/NG4PfvURO9VbsFam0bXk2C0vziQQtHNGIGJfAO4co2cUQqdwTltg\nQ0VucsRGh3CdhHxcMr+FdW6LbcP61VZXwy5s0qVgjCQ78lK4otGxfhMwaobdumzRwAmu1myesC2b\nVto9d2zBGqqNQ2I1JhWoyqflqU/1v/umTXbsK9+37Ohpq6hjLZ2XELOuq76ek58oBNjjp/J17mMg\niVsSrZmM7Aooc3SwaHjZkR6oZdTuu/s+jgONaNt7+0hGOGpiWgp4cuoRnYsOPSrQk37DXZRuWrZg\nnwwZwk7ypjqOKaxtZT3VArv9xo04jT6C14U9dqS727KSSBxzx+QXMoK8WCxy8vfIMpRkrpopNR0G\ngNJJkEKbR5pk8RzxyBPbraVtmS1Z0ACPMUWvNmvqnjTBNRb8EME1DTwvqfxTxsClwEDSxtEoa+vH\nbLx3L/QrX870+FjnRcGVrhiVcs6lgOHMZYoHXGRx4zaJEjAwcLqlUmnEM7JK6j4onTyjtaTgr1/9\n9EL7lU92MFMHX7ufatIzYvVNf2RIyRpKXpWhE8Jc0VOZoTPgJlK4o3pYVUYmJB3IURq3lKp+1a0u\niuATG7p3gDyKex4kw1URVz36e6Ujs/L7M+Bo7kW760MgIYn1pKrfCaGscF7GryyBxlgI4sbRN5uB\nfvDU89atBcptKxBknF4CMeY0tRixX7AexgBLcIstIFemSCZZizk62G15lM225nq7867NtnhBO+f+\n1lqGeQXfpIFyCRdFeeJPH8pIUE8ey+YIyubGNR32ntuuZ/G06haXACUcp1Vsmn4U3BKlHW3tdi2K\n7FMv7rCWCpZtc65wQmvkeKf/VMaVPw/xNXosX94RGHAn3hCDBizqbgtsEjJO6hkYOGrXsFyjY34T\nHbCOOJxO3YF+hCInpIArl1DTo8KL1/8qT8inX4qGLsUDuqceRk5ppI/89LW3pq25cYldtbLTtr7y\nrO3a/4IdPzWKkMNqn6plMAi/wIdOxkz/qxRyU5LEhBdsqUzOOF7ZXtx+0BYuWMIrNmloOQDvw2yD\nlE2eAcYFFjnLoYyBS44BrSlmrebIBAaFujbL9nIcsU4oQ9FMYaWT5HFL3SUH5I0VSBo4b3IVn+hB\n6zhl6QsKI9DBsK7DRWmmltagLNOdaC+CjnRXOS5HufFy1SzKUZyCypBFV1Pnqkvtd8uuXnq3pBfU\nLQMpzzrCU8ppKEOJCKEoL7+YnzryMLTcE7qLQKUTzFzUO6g87zh4LxjCG115DwwKyudeY/xp7v/E\nWsfcb+mV2EIEqNie/bf28ra99sqOg9bUupyNvDihlZDjT+6KJCRhA/+L/Xc5c/J+jI09o4OnsYWO\n29VrFtmSjnmsr1xh1RXiSAk+rJiafietcw1XrW5La82likWgJv04TGInx2zV8hX2vrtuYvG1uAmG\n0CgYS80kU+1at+YnDgG3jnqsxEXMu27ejC+zQdt7uMsaWlCSEbQ+1Y+E1j/nPrUxYlhqLId3DAag\nE3Xe/MtjbdCUlqyFvb1HrYMdoXfdeSNkrXVXDGWkDRZDRKs8i0Tj4CQE0dK9e5S/K9JV8Sb08VGm\nOL97YnAzBemipBmsGaxuxmJSafV1afvIXZ2WuStvzz572L70zV3Wn1xkhu/NNFIozz7YPMtPJEh0\nUleAIAze4FLLZ+rt5R377N03XG+NrCnTCTpuzSWH3ntQvTFAIab8W8bAJcIAfS5MlaY/nihwhGjL\n1eiWNZbv28OOaqz1UnigRSlCRZvAJYLk9cWKBVyRVMUIBg3EXJmkj0joNA0xV/Qn2ZdU5xDzjYON\nZJGrM+RaEh4ulucWTbhNyVW0CziKojIVG5iXAgiuAEoWSkyCCymXPt3OriU9K1Uo2ZNTR7BE6oWX\n6wWoHEVEaWhPyElVxAnFQe6qQqWJe66gAKvOFDONPhsflRFKmru/ZYXzEn3bEv54Yw3+UsQZlLJh\njrx6/MmXLFXRwjIxjr5iZBoCFhVt+kHhUxbRthxm6wzf3PiIDbMGrgohuHnDMtuyeb211FehoEox\nhZI1BSgm4168EU7RCHSfYC2a4iQKVTZ86+6R1q5cYPffdwcbimTBpG5ZaWRhRWkgmXcCgYeBnDxy\nr1RdWbD3oqCOfeNxO3xij9U0zreKGnbkYemM16hOZfSGC6hymIMYcBqN2hXuZQ2EjjSoYffn+HCf\nDQ0cs+UcSP6e27ZYfRV7wqEvJ+7SzG+KG2hWdHumcBbyEt/IlOGveZAQk9AQbedZvywXS1n4psJ6\n7aoFnCzyvg67ecsq+6eHXrQXdh+z3lGsnbhCYsTlvKApdT+xi1JVjlTqFDMSYyPj9syLL+NoejO7\naXlJ+Xrn9K+0NLUcyhiYCQyI5LXNNCdH6sxBZzPtlm5vgyQ5fad/P5tpwvISFxUxO0X8449RXClL\nuWLqEfyIj+J81OW3ceKS+LO2VQLkLKGYXdX4P27gHa8fi6Ycwzszu9Icl4MyJ5hgNy1RSws4Xkl5\nVJDl1OWgyon4sChmSULOoPiqTt8qr/ZRewwnV1WrMh0OfvykOaVRBa5d+ms9qUC3lsY4K7bDE4ck\ncbrXR5W8nXO3ZYXzMn1SEbiIMgfVb311t41MVlhD23yIXgIwkKdAk4VIrhgKjPKS8vVFhzF0+jhM\nMcKUeaO9567bmRasYySLgomSOWXJ9MlwSoADqUfqrTOMyvSeIuIcIuW0uqmuEsXxBvcZhjSFvzU9\nTh5ZQpVHXMGN3MYo+LNcSKBMtNRX2I//yN32gx++aNs4432Q9Xk19e0oqWwG0Ro+1a42qIBymLMY\ncBLxjjl0/lLoRCfjYwOsLe7Fz1afT6PfeSvKZjUnA5FWJBE2Ayn3G8P02JiW35jOY0oSv57SnF49\nUSQ4dE+k3JTk8LHp1InQkCeGNOc2XzV/wn7zJ1bY4y8P23//l73WPcxylHSzdEgsKCiqzpPi0wyc\nIjMJpxFxQsneg0dsy5YNuA0jnaSNnMXzHtHPn4Z4UkDLoYyBS4sB0b/3u9C0bBAFrPSFZK2lWjf7\nlPV43yGrwkuE+EKyIfBLkBE+IIvAE4vyuhjCbfj1OrgVmevvfINyi3M04aBssXIYlDZBMxX0XgbO\nYh1E5ABKsyVuuaQEKdbOavxIV5T1VnYZ1aK0KlBnl2sHuWSb9wUeHTYRuTILX6seP8aTnEGZDrAI\nVs9HHs+sR/6kt/r8hSw2CpTtllRudUSmyztehdOyojRKB3yCxwPlqPh3SigrnJfoS78pDeklQzFZ\nModGJ+25F19FQZsPldIpoMS5mZ73mhKRH0CpfNqIMD7Wb33dh20xDmxvu+U2W7tqKe/xKebCDKE2\nrdJpD8VWhlh+X/d67drVbvFJoSDmIlc0xUzRjTPe6yLloFtOt2sqM/aeO262a67ZiEunx+xo12FG\n1ijJLA/QiTG+yelCeqXX1VN+vPIxUFy6AW3pXnQ0yfFxCdZoLl88z7Zcf5d1slEnw+BIU30+7awl\nIyLc19HjTLVW1eovPlVIO8x9vSdr3epxDP3em5pt8dKb7MGHj9u3nuuyfDW+9wpsvpO0Yc0yLg5x\nOyOJCK0zWOzt7bVBfNw21MsiqkRqG8KmTPvgohxmDgMYDeAzp20GRQgXFB0MBKkWq2y6ESsgi7B6\nd/nMlq8jRMa4b1umqKU/aa2xSBbK9me/CYWFJvBOwUk73J73r4qJQ+m9x70uwqukLq2BlhlEIQ2M\nREjzdCfxWnOpzUJ5+DCHMqelkxoUKq/WUgtGHVzb31ew6nrKQdvLSOnTFD55VKqm4dVWKYz+TB6d\nne7T3d5WyleBBMcLcRowJ/AwPz7KO+qr4phNP3GMumQZBSwSR5k859RPMbZ4M/VuLt+VFc7L9HVF\ni9r//dIrOzkrHUezjfU+upKLJNGgNhtISfMdrhPDNjpwAvk1jAP2VXYHp/7UVGqExwGzImwEHYXB\nHFJMz6dBzkElCWFlTDdizpyv74EpxUznUZj8HupkCE3VpOHE9uZq+/GP3mNDNOogu397OBu7rw8L\n14QsPGLpcpirGPARPY0TLVVVsi6yod5aWeaxupM1xbhMqPKV/gg96ET0ImLNQefuRsSp/vJjJrhC\noSUsZdHay0p4bMOCtC3m5JPq2gF76Nk+Gy00oWsixBlopZN4qMVCKrbT0HASIXbs+Clb3IHzZ1c4\noza58Ln87StD8M7BgPt91bwxfbn/0yl1UtRY/pFqvQlFMs8egP2RpRPlCr6U4c17adFrRLMuUmKR\nUbwiHyKeLYqc4s3Fw7GKlOIWjpuNyuVZMw1uOeRGS7/GxpJ25FTOjg/RBiYj5jXmbGVHxqp5n4VX\nH3yi3373vx6w3/rZ+fbpD8zjzHaWxaj/oXBfb6nuiAbjlCUEFFIF7D++vlOKZ45dR9rsU0n5Pu1O\nkmN4lPlffv81a6B/+y//fqUtbFEmAHTA+Qk6spdV/mGwUEbCZcIAtDjGmau79hyxZGUdIzSm3mAA\nySgxUqBZfGLiS3Ok77i11iXsjtvvsGVL29gQBJNxLKUUvTChQN/Afegh3np7pChI0byQICVVHVpY\n6yL3TAl3x1TVlLH2pk7vs3x6HmFcDnMbAzHthFOAoGFII8nxlJpa1m51eWnNcdVASnJPlJbE8Xrs\n3H3msSOeIUg++C03zgOyBEnsEukDqglrrui1z31srVXX7bUHv3/UxnIdVpGsZoDGDnvOUUYakVpr\nPDN29CiDw81ryK8y1EoJ53IoY2BmMSAe83li759VN/QoQtf/bJ1l2q9FWWN5SN8B+m+d4EW8yBVy\n1tXz86gsTsX8BCsfN5FCplJLg/KJ6i9aoDw1IwAGHDK/uowkiutoJmXbT5l98UtH7fkXeuzEAAom\nSmJLU8Ju2thk/8cvLrWGWpat7Zy0rr6ctbfgtUX8GE2Da/od1p0KPKsNFO1tlREldsekqf8CVlW9\nF1DCxcnhgv3w4ITdsZmjLGtVcnRqmUpUunKYhoFSVE97UX64WBiYojrpck6rzsEJznzuhQmGrbqB\nM8qdS1mbgrVSBK61ZBMjp21o6LgtnVdrH/3gPVaLH800QzAdT6kpydgKqalJuS16q8HXagKTrrHS\neT7WTdWHKuH5QstCA9VBBGgEF0xKwzUlUmbAt/qFZk8+pxt6ZKd1OudkZDLQKkZZDVPsPpOrLwjD\nFc8c0sEHLZexiaLXmC8By+FCDCOUgpVT1iG1o8H67ec+uMLWreqwP/l/X2Hd9SLWKOvckWClzWLd\nTLEJaWxUrp80wELyeVDpCm+dR0P+8m8ZAxeCAfp00S6/CdYSS1HSs3iTOWUsh61W0XozVs0KG+/b\niR8GlCktgoQ9tdlGK0UU0EnJq0wwh1+8FOdhV754IwVNr8VLFy2oLoIrviqYZymIOpFIj1k05O9u\nz9pvf36XHT4waTetqrQP39XGYDFlz2w7YV/7Vq998I4m27ShxfYeGLJ5GG0Wz9ep67JuRuVJ1qpt\njhzKJp4qvHxvvjoEtCSt2VYbK9gdLwTKwJNHBu872G9DIwVbvjxjldWUo0Qk0aBbclzZy2EKA2WF\ncwoXl+gucI1bf1C6XAFz7jQ73dtnY/hIa9DRldTuu+ww6WtNTXZ4EOftJ+yGzStwtbLRaitE6Iye\n6D4KCLO83EKIsJ0LxYsRpb8Nlo8V2AtBhBhXAjkEdXDRnV7wNNXJxWmiBOXL3MSAC6bQYYsYwrEB\nPIv2nSJ0L/XT+223iIuCohyXEScSL4KqRCl0gBGuEHGORW06+aQ2O2i3d1ZY8ic32ef/9jU7PdmG\n0snshNaTsZ5ZKwWGhkeDUBfxe4mBQ8oc4Ogo/8wQBsIaQq3jlHIJXdMna8mWT7UDg06xSyRx+9V2\nHQrSqGX7jliG+Wi3XYTkzhH60cybz6KlOBAhWcEUt/iYP3hC+wxUrhxP5lmzHVZFXpxGimckSqT0\nxkFxeSL3njT7vc/vtROHJ+2XPr7EfuHH59mCJtpLgqMDrfbDH/bY1Z0NNjCYsyMnB23xkmqrZ6WL\n2pOlkaPMSAwMJG1okCN22VDUxqxcDT51g1TVIBN8UdYQvpNOjxbYDMvpf7gbbK1nqyDrYSdR4ne8\nNmLVZLmRPiHDND3DZ+vjOPcJMrZxfkSF+8COIS9fywrnpaIBcYUCxB0Hj3LGkeLJmpMjRznUpAoG\n54wfiFaMriP/JkbCNPrtt12Du6MV7oDdF6bAIK4U6upDJ0oUoxcVvrimmb6qkSUN5UkjRY3wNBZl\nLPiG9zMNYbm+i40Bvun0T/6GCgILMEDyhHHiKJ9IN8oR0r0h+8xHCJAYmMg0oSlHX8jFC1k5BHMl\nzuvvuKrddt3abP/46CnLVbTDuCid2nCE1WNyssRqGyngM9+Yco1lDGB5pw9W/xtvLHX6dtd5Ev0o\nn/JOXmi2TMu7WO7ytE324jKJo3ak5EHQ+u8Wf53Ck4XGM81LLF23ErVKG5GQY0qIMpuUHEoOWvbo\n0+zQQeO6mAEYorGbyxWZIIcnkvaPX+ux3fvH7AM31du/+zcL8aXLMjPJGv4vbsnbJ+5jrTUNeHZX\n1o6eyHIyXos1NFZQRtZODqTtf37ntH3/cVy19XCsMxbfNSvr/eSi1YupD20ddrZTw2n7yweO2/Mv\n67CKUauqq7APYDX9yfubsXxW2Kv7WEaG3N60vB7ZnbJXj+Ts8399mKVEk/Yff3mxregoq1ilpFDG\nRik2LuI9NCi6Lwa3bHoMVhDn+gIbavqQU6xsY2tcknPUtb9uYrzHRgaP2V23X283XLeWXYSsUKYg\nKaWSXX46USQMfYjpIjCWksXqZuxGo+czB42m9Sa01m/PnLAcOysxcLbvPr0xQeDx9SUxXGqIVvUc\n63bnV870Ui/WUwRLsTjBEsMTUyxKJIScxpdhPsUMA9YdpUgXuu0T9y63/rG8/cuTxyxRNQ965+AD\nVzAR8Vw1NRfYM64nLrtYYfmmjIFLiIFAgLKvF5KsNcbTiR/NoxpROqWEyt1egRmHZGoels532yRE\nO9q/D3dhOgJZ5CzaJT/32C/Zb9RoicpFxOlwEr0KFk43KiT7mWZ+gciLFFR/XBRwyMiiLkRy9Vh/\n3v7pm4ettTFpv4D7svaqcWYZeIHpFRaN7DCs52R6ffuJYesZL9g1y6utEc25bzBp//b/3GnffnnQ\n1q2otU3r2+3I8SH74rdO2p4TQ/YXv9eJpTRhJ3vS9ht/vMsefX7Qtqxrss7OhbZ1X7f9wwNH7ZZr\naqy+NWMHusesaWHSmlj29tirffY7pD/NRqLPfXIRbgvVV5RqAXFj3rnXssJ5Cb+982Mka8KUepA/\nXiWjwomJSSyc1TzC8PxlJ0ZskA1Ct79rg22+dgXKJpYSWT5ZJ6bdvGI/6Zrap6O1ll6aGD4Sks6c\nqnQmA5XGVU6vX0/xn1LEqWYSuHJdlxUDfH7ZN/UvKGJc3SKvWIUpuriiqEPTjx7grUhe+DQkSqcL\nafEbVqD62tP2mY912q4D/fZa14glKnQG+3QhGZ5CeWrzFdVOh7b8MzcxAKXp3EdfR4xiyZ0riNp2\nLa1NiiTvfUCInJGHk0KiFqWT07+UEOfwOqnO12STVn4vM1qXnGelZ46/FH/R7Jqm1D1PQbLs4lG4\nYFbwsSpXX1OKPJSCvHV3nx3GzdG9W1ps7UpgSXK2rKomsXbiS1kmpTu93/HaoDd39bq0TdCOv/7q\ncXvixUH78B3t9h8/h7LaXLCjPTn7pd8ftl37Rmz/oYJ1YMn8668cs8e2DtqnPjzP/tdPL8bfdNq6\ne1tsz87TtnYBpwPuH7ee/nFbvq7dvvL4hP0/f7vbRtkt/3u/vsTuv7PRKtwBKq2IDUQC6R0eygrn\nJSKAIKfExKrAOSESvuzSleEfphkY5Fi/BiwjMHuCXecj/cetpTZtm65aZjXuGRZixbLpzmtVCoQr\nxbWobEbMHTPmJWrKOYt98/odAaGMgAzuS3OUvCd+6mnq7szpz17GFMDnKkMp43IuJK3ynSt9XO75\npFWac6U/V30zVUZpe84FcwxTlM47XrWjtC1Kc7lCgEPQBQjj9ig+4mCHmXiEq3ahJzhCVqZLraNO\n4ey9rbLffuK9i+xP/3m3nc7WwMssd5Hw9vwomsW1c4qRv4awlvVytbhc7zsJA9FoyclcdKcb9bGi\nc92HGG3T1hnrWrOYyLdYuvUG/Fay33pgn1VpmYjInzXK3n1LBpFTxkQFbY3zCF2jMv32Iv2obq9Y\nCrICYBdYU7n9tVGfXt+4rtIaa+EzmopIDTKSZKjQTEPArZNp27UbB/c8ru+ss2PdeXvwO0etbV7S\nfvHjHbasHRnMy46WlK1Z3mS7dnVxgl+FdfUm7RvfPWUNlUn71U912or2YcqYtAbydc7Hygs+Dp3M\nWTc73yu6s/af/6+trNlM2R/8xhq755YKqwGfYbDqyHfQyz8yQJTDpcFA4GfK1o2YHVEDx4q4AyXC\nuOxIz7HYMQlxjgwfhz+G7Mc+ch9KZw0MxFQ6XJSDat26EtGtb76As8KudCIlEPUu/uN2RkNcb3yN\nKg+P6tCiTk0dgDjQ/3Qf/8Vx6hyJKwbdx3/FyHPEleZXnjj/+cRfSNrzKVtp4nAxyr5SylCbYlji\n9pXG6d1UEO0G2owJRCIqpotAG3oz8yGGh5oFY2mb/FkQBSEbj5Nk3fRYF37MSbBpIAOf3nlDq73v\n1k5LTQ74DnzfFxwLSM+haUtKuzwNdQjKP+80DIjvStXCmO+07lIDJ0Q/dB7TJLqms4C8iSRSTZZp\n22LpxjXmqyJVlAZZ0upQ5TTDFvurDXwj3MY8pPuLE5wNgSvPhr14Ol2mSq0l7Tqp4yvNls6XbNVy\ngagtglV//BO4p7CCHjkxaYvbEtbRVGt790/Y7sN5u/HaFtvUKSyggJM+N5my/v4xq6tJ4MA9bU/t\nHGSdZ9bufledLWme8I28KbxQpMXX7rswaTsO9tokKD6857R19efsM59aafe+q5rFBijvKlP9h+Pl\n4uBjLpRStnBexq/oLDw5zrno+BMbPm3v2rLR5rW2QM/DnKZQyXSAyBXLinYTzeIQmA4OPBfzuUD2\nn1nc2jLoUxiIv6Wu8f3U29lw5/YcpLKLJnam6qhK2XU0zitoWQvSV7MTm69dal/+3l7WbtajiGqJ\nDIGBZEHp4d/AAxLYs5uX1axymEMYiAwa4k/UKS5YNCHuRK7ZKpq3QNuTNj54kOlhKVGaicMAguIl\nS70rgVJq4YtCcRnKxceNFy3xQZCvarjQ2CzvO9cH8LupGQXFexCM3PrRkhhzDh0atoHhCbtufR0Q\n5+xwV7/3RMsXZmgTSwVY86mie9hEtHv/kDW3Jq25bcK2HWBjEus+VyxttBqWz8hzjC8vILUMR+Oc\nNvbqrh5rYRPSx+9dYv/w4D77l28eZJr+KlvG+k8pxkGZj+AK0L3jf8u932UkAfn5S2OmHx/usQUt\nNbbl2qtgBhgbmOT2KMlITkranCBZNUI9wZv9zY2WXkaKutKqVlfuH/5KA+y84XFrCc0IXKi2aNMf\n/Ikg1o50zeslsAM1NyRsfmut5cexhiAh3UG1RJks+ho08utWF67lUMbAlYIBDYSkGLmyKaDonwt5\n1n4m8VeZbGH3+o2WalhmY07TJJTxQ2TNn2yL4QGl0xnlErSKelSfgmoTfAnWoa5YonO9DH+bY9Y1\nqL3pOiqXfRC4eupnY1AvrpAkag50j1vveI5joFtYAzphY8hcuRqtyNBGrXGFfyfI88jTp+3g8Tw+\nOxts8eK0jXAoC3q1DXP0tI7M1BKaCdo+quNs+RtL1tm23ZPWzHGWv/SxxfbudfW2Y8+ofelbp6lD\n6eeI3BbOL2KQRlMOlwkDOnFlZIgRV2LEbr7rTjYhoGTKD5osIsipPEfnOSO7iBO7OctdJmjfTrXA\nfT4jYO9YZmsb3w5+5mpePuic+ZzythDG5wmtz8Ky494lONpSZ1af7BqwwWEsPQjeHMpoGpcpeTz9\niX91TK02Z2hjYDmUMXDlYSD0z77THAWsaJmjz06km6yCjUTykjLWe5LBFBuDtNYzmjAWXQcmv0SM\njuImK2QctCStmt32997QZH/XdNK+9UyPVf23Svv4e9qsKpW3nUcS9qWv77V7bqiz3/y55fbavuPw\nasLWr+JkJcpasajBdCrtg9/ttevWNdhCrJGPcFztn//jPqbdk/ZrP7bcmEG3RW30XejcD/3glN1z\n82L4OWvPvTZmW188bL/7K6vxTsHO9/6C3bo2YwuxiP76z66wz/3+K/ZXXzxs77+h3TYtYTlcBbyv\nVQ0u1+IWvLOvZYXzMn5/nbwyyU71pUvabMWyDoQSi5slobXGBiYJRwAi2CSwtCJ6loTgSiMA69MQ\n0jv0pyEnTK+gkbU/h2Tl3zmKAX1tffLw6cO3n1XfHYEarECSHD73AIdCu3JQqOlINlWMjtXb81u7\nbXCMoy5xw5JA8OVyOKCOZij0aV0ww9fudkIR5VDGwBWAgWB1F1/KYTrqgOjd19lD29CxGFcbiTLN\n17DE6wCDq3roWxyg3e5cJavOx5jwltsqmUFmzSRocy0sqA07qzoq7Fd+Zqn9yT8eQXk8bt9+6oRV\nkXQMZXFpW5XdfkMj6ysTtn//gDHxYEvmo7hS0I2rM/aBm+vsa88O2i/9wXarr0jaABt/Fs6rsN/4\n7CJb1yGXZxP2nmsztnlFjb24a8Q++/svUDYqNi7QPvr+dquvT9pznKcp9GxYWUt/MGrXrk/ZfXe3\nMrXeZV964Iit/1UK0ikQLu8ArBwcA2WF83ISAkScYifdmtWrrLpSPru0UUiCLPCYtDSR6qwS0K/D\nZwy7Rs+xIupxMOKZ1qZKuJfDXMIAggI6l2eFcCydBk+zqAOOQZUDeA384FIXzNxJzmZxD/PMi6P2\n6JMnrLJlvQ0PnGSjn6bbxcYaJIqefRsRv25Dmksft9yWWY8B0XTc50K0KJD8onSiLOkxNn6kW61K\nmhsbXXUAQjDdoWxq3SdKaigjZpaLhxTNGORlUaUObcTxozcpPpPJ2yc/0mprr6qzF7djbTw5wjR5\n2hZymtC7N7bYyvky3kzY/bfNt3tvS9raZWlcJeVtQb3ZH//GKnvXY/126DA7z2njkmW19q5NdbYB\nJTaDAShBnYtaE/Zn/361ffepQTt6fMSqqtK2bnW13fWuGnau5231wgr7nZ9dbHdy+EOavkHO8n/t\nEwtsWXPGVuIySfjJuE4OTi4+Wi4egme4pLLCOcMIn1admAnF68DBg3YjTt4xjkDs8K+UM6jU6dRH\nj7OLaGOFQkpGjiOU9Ex/wZWlAv5PWGDE6KO/aRjxxQPh7VR83B2Wxp8pTu/farzjOqryrZZxpcM3\n023UGi99YgkjpwHdq4efFmJsl0I3LcHlfxBoMZiyAHFQQ4H1m2PThNtbAABAAElEQVTsXDjYk2Hd\n1kHLVXZapnq+VWY5ZSUxQLtRUKWcekbtVxVPl0MZA1ciBkSrGhxFazFd0+RZ0aLayGF8MlVNlCz3\nxMLYgbdJIgXUQ3yNHt/uxbsEBmm6plGMBQ9VSAHV8una/ITdsTphtzNdnsjV84p/6HrJ3JhbaTVG\n/DSnDXkzZI4UeGg87fV5+6UPk368nvWYHIzEdnd50M1MYh7FepulbMnhjYvztvEjdfB7g+U4ojLv\nU+TIMxTJDZzbvm41y2mQcfL5CUi2gtON/u0nW1hHKpQwwPaKeSiHIgbKCmcRFTN/4/oWJw0dOnQc\nlwwj1t6KHz9Rs7OICFXWFFG/GJzHWUK7wZrFPkZ6Bbdqwr1ZHylLAIfOSo0RU78+SNFWOFvXdab4\nM8VdjjLOVueVAt/FgONC2qj+VjtDNXTKs3wkqak4J2SVoiCIYqjOQAye5jL/RFIuOLcO/KjBk1yz\nnB6ts3/46qu2o6vW6uYtoKWi70rfNOQ7etU6DbaIL2jNJ3wtC+msYeTLjPpy9TOBAfEkagB0Guzv\n8mCpyWepBqJXLH6SQfTf4lRf2iUehrm15EvU7a7P/G3MyxcJbq+QelSdQIDvdK9dP3Lu7pzEvab4\npd1JnmTV6XANA1xJExRColz3Qyv0MsiT508rXjjREl1WGYjTlD23buMJxVgeZTSJT+wU77U7vSBr\nq9cBIKRNq27hQ3zOn1ba5FQO6eS2ibflUIKBWaBwOqk4yHw/J2s9RHTl8eFHb0WCZwp6d6YQx19q\nsojrCVf9+h0EqnVeedZ6PPPcS3b/fbdDuwhocVWJYA65zgT/FRQXAwkqZcWCnwmsjxkYtOMntL4t\nx/1wWNsm5tR7R3sp7uNClPdc8ReSVuWdK/256jufMpQmLudc9ZWmPZ+y43LPJ+3Zyr4YZZSW/eZt\n5CtbW0ONteHqa/GSDvdR6WcuF79FaX6VO/PhjRCoZ+FPkikKupMQDoIXfk1OcmJJlT38+H77wUsT\nVr3gek4ZQtEknQ51kHKaFG9j5ZDIliAqmme83Kmy4zrK1zIGLg8GpI6JcrHuSe6o/3IFime/R0WQ\nticS1rF30K82wvhMhVhFeXxnDPdFvtb9xQlyRyTotGPcgaBu1/F4ciUUxU57iqTIJNwU6iC6lwhW\nq5GG9yqANgnU+MhZn2vjvY6S1zt839MmIpQOE6WyePD28UzZBYSaWy8lv2g77vIpkDwETcP73ibQ\nqUOqHSUSggK2HIoYuGIVTn0mpzEHVXfhKY57/WcM8RJxIae/J1Jdvvfx/Mg/nog2pM1BK2IgLHG4\nU/C4YAP3Gi/Oj4ROVKOYGCrOivpToF3PDi0CiqMrt+/aw1mti+yqVct89JWWdZBG+OjRG+M/EVji\ndJU7FXQf2l4aO/X+Ut35DlwxGP+YXQCnTDVyPXT8lD31zMusf+nlC2SI50+dVQSeLF0SytNb4V8h\nArW0HWeKP1Ocsr7V+HPVd6Flv1U4VM+ZYDlT3NnSni3+YpRRWva52ghF5sf55jmrq6qwdVetsA1r\nltvC1gYOORANQw8SCBCOpulmNgh2XKQgUApYJX1/hHjS+wSsOnmtwxI10WcgOHJp3B0xradj/cYK\nTfatZ4fsnx/ttUzjeqvgSMs8MxMJ+pEqbe5DCEkwqbcJ9EhbvRMK/Y9Hl3/KGLgiMCA+EC/CgC6T\nQh8huRX6IZ7DQ0TDilYa8YrSEGKTYDEiRF+U30hgiHMcJO8nghyJwZIl0WGSaI3a4HNpAp1X4m3d\n6V6/Dnb0LmJSnxJXKk8RvVOxXk7cVGCJFaYAFmU5k3uJXobDpPT6KyubjtHSnxh/pXFXxH34hDEo\n+vQERcYvvANXfByhBHEIwszf+ZcnnTOFrtPL8tdOyrEiGpdxca4xyAJXo6sER9/5BlefjhA4mOsz\nFTYxPm7PvbjdVq5cyQJkdQFYOj1hmJaO+C4C6o1tjlp1cYC+gFK8XpAoZVLr2uQs95vffdL2Hepi\n2rHKKus7/Lz4PJZcjSB9FCnwLxfAF9C2ctK3hwF95jzrrAq4+hod7renX9jJ0XF77f5777DFC1hs\nn5KQg1enE/fbq/Q8cwfy05F+cuosQw20SR8he4pbJEXPKMV5d4GE0OJeDq+HC1X22KuT9pcP7Lfx\nzBKrrW2GT2kpTVF7JycnrbWpwdukQZWapp4lDAZdWp4nhOVkZQzMDAaC5BP16m/6b0kEb8P7+OLv\nPDaKn/4ivH47v3GxlKFbfyyJewM38e71MBbzRXCUZA8xUUQxvnhTUqdSEq9X/rokTSgk/Bbf6/Es\naUrTvxPvr1iF87w/RqS4iPgQCfwWSc6L0HfXeg///vzIQl4M8b20zksaQv0OH3WpOm0qkLIpi4rW\n0KTS9XbwULc9/uTzdustm6yCYVkGBc4NQYI+GrnFlBwEmICmMG/cJW3A2QtHcVa7NJ1w/FSPffPb\nP7Tj3RNW37yInfd1WHQR4TjK9SkYb7cglpKh/wK8FPjS73Cu+AtJK/DPlf5c9Z1PGUoTl3Ou+krT\nnk/Zcbnnk/ZsZV+MMkrLfvM2ikYTvhOu0upb6jittcVGBk/ZP33lW3bLDRvsRk7WynBsnRsJinhT\n+WcLpfWVpiltV2n8m90H5VLnloQ/QaE4nPRpHbUXKS2SqUUxIc6kc6kOe/DRY/Z3Dx+2bO16a2jQ\nuk1RFtZ7/mmKTctj6uvZaOD8OgVv1AO9GUDld2UMlDFQxsCcxsCsVjglE+I/faWgN0518uiZiIAp\ntWa6XhlU1KCITOW5VF+7WAM3snrksV5WVmVsZFRrNpnS59SDZEXKnn5umzU119nma9aQZtKnCLQ+\nJNhIBHNo8ZTCKYiLpV8q8M9arivOwK+TGZ549hU71jthje0r2TBUy7obFE2mIXMFLZSJvhSXeIXM\n5YP6rM0pv7jYGBAT+rQ5X5ulJFUNTD9P1EIr26yusd6uXrcU8s260ikKebMgHlCYnm7605vlL32n\n2QNtaIo3I2hdZpYFXe7yJYrUhoNkDkUzUW+nxmrsb76yw77zCoppwzVYNjmCloGiIMoyJSGdtMCp\nYZMTI9bWvgSLv3av6q3+BONbg5OM5VDGQBkDZQzMCQzMGoUzTE+FTjtYDwL+gwiiO8eiUBRIaHRx\nvNLG6zDi7l8WT539KqubrG0q1cu8BFN7RTiiOlSFw4Qwqq+tsd7hIX/WrFzC1zlW2w+feplpuWZb\nvqiNSIQi/9Q2QUtiSkJlC6hQDG8VQk0zbUkRGHlw+YMnX7DdB05hyVqGYlGD3SgoxxG0gtih1I9W\nCihcAnSHgsu/VwwGpHi5dRuCLeSx5GPxTFVz5s74iD32g+ds+ZIF1liLVfECg9jg7dCPeEn6YB6l\nUeuOC27JlPNrAHH6BF60yMl8vR3oqrG//8Zr9u2Xc1bTtsaqqzkmj133hRTH3sGzOZYMqAUpZiwm\nJgatY+F8S4lBvRw1TFwSMawey6GMgTIGyhh4B2JgSgu40hsfd97xFXh93aDUMF97VdK/v74tsrIQ\nkCu+PmsyKyVOZyIzHSzl0yVDUNten/VtP0vuRPJGboIUJCj9iK7KKlfEfGc6wk3/Evg66xvK2Ze/\n+ogdOd5jk8rsijHtVVlFWAWv/oQQ/lxScjvTAfj6eoftmed3WCV+CFOpBtorZR7ssv5NE+6+dhMY\nhWn9qe36i5/L17mKC2gAh8h+FKQrntqmI2t+pdU2zLexyaQ9++yrrPEMk+rnIt1orIXFHC5wbVP8\nJOZ6K0F5M6JGitCgkx6BdSFJloCo6El2A/Tnm+3BJ07af/jz5+z7OyqtrmOTVdY2UjecSrasOhSm\n36thP21cGGK5wIL2emts4Ew8B/atwFXOU8ZAGQNlDMxNDMwaC2fw6YilT/qXFC83kyXxu5VgWnos\nRLrwmS6AZPGTwiNLZlg3mbc+XPUkEnVWU53xsrRBRzfTc77dDy5lUCVOlRoss1IuUJKxkCxfttR2\n7u8y/EcjRAWDNhQBR6rSxrPj9uWvPWr3v/9OW9zRxskKyoNQdMuJfPqp5Kmy3y60bzV/gZMndry2\nk3WatSicTDPK1sMUacLXwQGlPpgmTMsC+K2ieBbngweigRB70WkHfAhJyKJoLCGpqm627Tv2cejB\n1dbceG6lU3ws/3laaymrqXhGPBC8HVwYmrKaOWBAZJyFnsTymoYfUwwI8/hSGS5U2I7DFfYvjxyy\nH+wYtULdaqudP4/0FeyoVzO0rlOqKjvZ8Rztx1bmR21spMfWb+iwxvoqqdXEq78SvHFQZtpeDmUM\nlDFQxsA7EAOzRuEMlj2JLAktKW1yJp6wnr4BG+YAVSk+UtbO1J2H6XJZRGVZS9j4ZM66e/ptXluz\nVVUKBZKCssRdOgqYrm+F1syb124ZNtSo7qTWcUbV+052puoGRkfsK1//tl23aZ29++bNfhKRTnqQ\nYETyutATRvzPFbtL2IAzoEbwDo9mbe/Bo1Zd18hu+wxTkG4zivWMoGc6WDML2xnALUfNMAaCA+lg\nPdSQjxETPIbSKD5llJWqrrfu7uPw8BAKZxPQxRxwZkBhE/IHfs2h+Y2OjlpdLcftvZUgRVCdBeVo\nOU6C9ZsjuDU6PtJq3/juTvvO0/3WNdxmVW3rLcFMhK/JxH2ZO7BH8dRGI/Ggjv6Td4bcBIPe7DAb\noa6mWL1T4RFv+lWPb96+t9KMcp4yBsoYKGNgtmBg1iicQqhbKiUcsPLpyMSevkEbHEXZTMY+Hunk\nvU+XJaEkSJOMOvvYxpBD6J3q7rG2lgbWZIVTcUpyXOLboDA3NtRZa3ODnTw9iJUzEpwOJ8okBp9E\notrQ51jr9gICzOymG651BbmAb0N5lFFQSUFy6jrTAg03SOC/q2fAErXtDADkPFjTiVigdHoFCobA\nE4yuDwvgcnhHYUAbaxKy3mtphSgB/hUtOIeiPFbUNNjuvfutc9lm0rw5/SaZAdBwRq6HnnnmKVuz\nBstjpHCK+qfnDzEefZYfZsaxbjKAo9y+oZRt3T5qjz3xPesebcWd11VW09GOQil3XnL6Ths03U79\nGjwG10msVE6z0Yh3gwOnbdPVa62pAZ+e1OeukoojWDGrWvzm7TsLmOXoMgbKGChjYE5gYNYonLFV\nU1iXstmHsjmKNpZGaGWlhBIvQRAHt6jQwcdR2hWekpYmS6bElgQfwqavt48sdbjwyRSVuLiMi32N\nFWaHBcWyqbHGVqxcZF3dryDQaAGKmrzU5pmSDs6GsKTQvkxVkz313Mu2d/9Bu+3WG2w1efxMWwlx\nGighLgS4IPfbqNUIvKLYjaIuvE2h7DPnK9jEZNaGRsatrQ23N9oYoh3pCOlg51SuSMnwW9rnCrWA\nCcsjFO1tp8UxsKVTkWFzV9S46Gs6qtQw2i08haA1uXFrAz3oSaqOpnMZUpCcNI4TLMo+HQsc8WEA\nTLF6dlmsuNE6PYfdCxceQz2C3L0GSBEhync6U5GmieV02Gv26VqlRPXgoiaHNYfU6+UBi+DxMkWL\nqjqiWMpSTpU81RoqiuGh8cGOD/6Un8KTkTVNa2cVRA6hnZTCg0r2JSU4nBQ+NYUc6mMxScgSweDZ\nVUJovwDwwQM31C+flY7jCGd6HeomvTtQj3lLbfO34dsCo/4pRmXLH7JmxlPQdgH/rBNZaCYKoe3C\nh9pBHiltPnAJrrcgNw4UeMmeYWPdylVXRXhVYlokGP27usqnyFCQU4HKI8qn85Vc9Rasn5mOI0dO\n2QuvHLPuATb/1Gyy2oUtWDw1Y0J68qrMpNocGkCpGlDx7G2esLHRAatLD9r1V99Im0Co5wt1h3Z7\nhAojiI4ipPvzLPvxpvjXifCh9pS2L7Qn8KKo7OwhvHuzFFG5xTrPXtb5v6GwaVVOe5gq5o1NmnpX\nmqU0nfdtcTK90NcPIfBs/K60gDiufC1j4EIxEGhsWq4pgpsWfaU8zBqF05U12Fdrt/K5LOsvq6ym\nSuI+aSMTBRscmfCu3AWKYzcWeeGjJFGG8iiqmsFurKvBzyUnD7kmoO1DKAvyF4hguXhhulBxwUlP\nJ/npMlQVYRHctGm1Pf3D59mMzhqydCXKMxttfHMRygkCVGnzyQqmIBvs6KlB+/o3v+9T7Ddcu8Hq\nqtnxSxopXXJaLY+Cgd5UCe2XhC3p297Y/YfU4Te0vCQ5EfFTSCHRUhq8O0UJySGZCxzoJUGqVutP\nMVLaBJEfCei4liIgJUkKBiqQlFNSS/GQEu7KjX9FpZHizTspsb5bStDrn6+4peygskmLUGz45a3q\n8ZiQWgqDKyI6G42p0DzWqiQ4DqqoYFMbUTK1nk+Q+zN5KUfwF1yZE9SiHb2nGIHK9KuOXUuRT8qd\nWiqF02Ehn9LFSqbiJ8mb0PQt/4KiiFKGwuLTuV4m94CS5UeleBtovys83qZAmy64HEYlIaPaR/mC\nM5XGhY/wGJWhkhzfHqV78UgFscBNeg0QAr40zgmQqWjlQ71TBTQiOtIR6uJgN/4EG4oifKiNMn44\nF/Vp0DfVHqWiFC8LHKo00uufwyQFzu2AuAJjmnpgcJAypwdvhlJTrjaYaUNdnmUnjz37kj37zA54\nuZrKaQt1yGl7nu8hyHT8Xp626Jxn8U+OQaW+pdqiMrVOWpRz7GSvPf3STntt10GbZNNSTcM8q2lv\ng/fYSEReqlMO8EOb+U5JfQvPz4/Ko84Eg9d8fsgGu/fbe65us2Xt1Wzyq6LOMeg2G466oxSvnqqV\nffYHxzLNUGtAkl+nWqa3U088TAt6WxLOnjAkel1yRZZGlWa/0PgiFKWFFCNDG0rLLHk1rX2iZ6Wb\nKkbUrhDHvP56tlI90xz4mevtu/yfKGA46leivshl1yxA/axROEs/cyZdwW7RSu/I87KSjEyicOoI\nPVlWpodgVfTP4cJJm43q6+pQOEnHKSj+ng+V1zrKGf5gUoKl/K6/apXt2HWc7hshyTpIbYUIp8NK\n4aBFPBZIm66qtbHsBM7hX7SDB4/Z9ZvWk7eTE1tQZPDZidbhSpHaKOXFFZu4i3YlRLgRhiQoQgj4\nmmr4FP6kMijod+p96e1UvN5LmShJF2f1AvUjhUT1uiTnERc5bBxJECcFIUhlpZMVEvst3xUVjnYh\n7LV4j/KlXORQKCT8M649qN2oLpSjY0r1JwgUJyVkknIyuK5JsNYuWPzGLJNl44eUZByOCwZ9Ayli\n/kw1cuGdQdfS2CNLXQl3eINyxppaNAkvS8sFUgxY8ulxypIqqt3OEj1B6UnmMpZBURPUOazqsqZW\nagc0m1G0jjjvO7fHIkWEfIJBjfMS9EtpUoqJCyqaSuJRilSkABZccSIlHU5WlkvomWa54ujneONX\nUu+kVOo76fjWyokaG09jsaNdsmwG1Z32gooMVk/HL8nVh6XzUkxpI/m1fhE7u6XZIKPvQFOIDwMi\nKWRJrJQ6QzysoAgwqQ2qQbDoLhBkaGP49Ui94M9TKMKfwjMlKJr8OfA9xrrrx5/bao8/td2a6+bZ\nSP8p4OK4SRsnnTbT6SxzvhOZtB5T+bJCGwptFoAnOWtycHjCXt2x0557YRuDVGZHqlhOU7/Qmmrq\naVYl9QCLps8FiLceCuSISw1JpTanTN8EpR4FNEu705hb8z1H7dbVCfv0x26x7r4x4vE8wfvQBpUU\nSlOJcyKIOPx7BZoM7YvaCf5EMwrRV5/eer6JZ/UU8Y/ST33/ODZcQ1mlG6/OlvL849VLhXKn1zX1\n5GWRJG7h1BvdBcrWnZfiP3Ht6q9fX7ae9V6l6f7174maUyHGxZxq1CxpjHB/ZdPXrFE4w/SavrsY\nXsyrtVVCriwxIUjwuNR0vg6Id6HFaymUSq8swYdesHAWO7PL8a0ArgIl+dZbttiRYw/bEMdbSsXR\nurGYbiQDZW3SFU0BRaeGKfZKLDRDdvKRJ+yVnXtsy/VX2yJ2stdh4ZI80LS2dvMGZUr5FfQ71Vl6\nlKNIFQTxENLxRngqPnjKqZ+zxU+leMOdt4ECsfNFEEgJC8qktAq3WqO4OYg0XF9UQwdX2fRNHRnk\npwwpnJridF+q0IAsWcqYx6IkBVF1oQ5K0/P2u3Nula2apdg6huVQXzEhr0+fq12UFZRdxfMNKFNL\nF0AmiaUcE40yo5Z4iV4uyag0WJODWoIt1BVcaWD6dlAeV+JUd1IDI/IAX5by0I1RYjX6keVNQKAg\nU55c9CihLsKGXqltHk05SqmgKyq73zl82nyGYhxPswt3gcZRurAIJqANLVlwpZwMUvhVnxQFWTAD\n9gWPFHw90dYM76C9JPmEAtGX1jQ6vCAyR76gCMsbJbG0sSArtuhYmvA5g+APbVAjBYl/J5R1eaHI\noag/9dyL/O3mBKtlVpGptOHB0/498ijmGS2Vgd5z+r5YKAVZFtdno2NZ6+3vt127D9mxE73WOzBu\nQxy0UFnVbs3N9Xh+wBopvHqrgJPsapOj3O/CvSzZbu3ESi1a0VcVHscHj1hD/ph95v3XW0v1gPX0\nU0a+2r8vSfz7qenhpDNKDR9IUXMgBCxNNUTPU3HhSUggiHA9OFK4C8/iojCzEL0u5o/TT8WLhy5G\nCKVMyYxQbASnf7S4FvVAGu6VBqUL1FKMleU7cEXIPT2Dpw9p3/CiWMTcuolxObdadSW15swYPnPs\nlQS3YJk1CqeAdWuk2Fq8i6IiywtzcsTQcUmzJF7CXCIrcL9yRXG8kKKiEBQxV+1IRgciQXIZvpe6\nM1QcWzC/2dauXWZPP/8K1kpN6Wl62hvJVVOCyFOUiDTukrTDVx2cW0IL47b74Al2iR+xTtZ13nTN\nBlvSsQCBWkV7UMCkIJBZwjIgRIJT4pyaaW9ocsCVIJkKqgNcRQqVx3u9ii8Nr38ufffG+yB3JKwF\ngaxmUnCGsUTWhcTUJ6WHiVasaez65XvlWUuXkxU70oBT7sIG6HjOajEgQlzWTil9miaW5U71yIpX\nAQ5yKHB5bSqTCxsJfFkDAwaAAyuYlC78QqZQXISdHO814Ss8aSo+wZQsOYmREkO56BQTWJQ1Q5/W\nAEACR+hVi3jna0NlQRRowK/pdG1KGU+M4ci/3ha2NNnOPUdRfNQuMlCO6pJS4/XTXt25biz0+jdQ\nOj0osYS06iN4Xk1t03bqKWDlLiTGAYISUfak5KrMAD2qLMqy4512pMGNW4V5m5P11pUh6iG4oik8\ned1SJqEgGqTlDfOa0rZ26Tzbd6jPjvSOg1sUPdpZwV8SoAWb6tR6WV/n6CWe48fbI8XXK3d614+4\nVd/upW277YfP72RWY4HV1M83w2l8oB54HyV60sm7knor7PTpHtt3+LCd6Oq2rlO9KJqnLVNZb+kK\nZjVqWnBZVM3SA/DCdwuWafAmmtFHBA7xiqhTwXlEZmMGHuElCjDfVgp7tv+EVY3utp/56FLbsCRp\nA4Vh0I47JPAhXDkiVI6Yd04FqX76UKFduovv/dbjhUF9vfA2+qD+pDei7xBE56KWKRz5G/+ZitXb\nlPp6D1Npi+VfQLzXL6t/BEK48EtUacmhj1LBQdUNbSadJ41h0aN4LPCNg3HWn9LSz5po7rwobW5A\n8txp2+VuSYTbIBNFn4rQn4gz9F2XG8Sz1T+rFE5vREy80dUVTRAdpsaJdIkXIV8Z6PCD/026Gu9o\nECgI5/DNVEh49o08TA3OZNAaMNFHLjdqt9x0jXWdPo0g72bHeiPtoBOLaUcKBUqB1qAqJFCQc1Im\ngTfDeeXqwPcc6LZ9e79pCxe025JFC23j+rXWinKD+04EJEqDlAeUAtGklFAPkRVU05AgJcIPpUl5\ncemvyvjjdehww6PyBrzr7tzB10ki1WXJ1PdQWUkbsxs3zbdVnZ32te/uYzqSONWJgid1+953rbDr\nrltqDz70ir16YJSvxMRmYZS/GnQuSvCigBMFQIhKagqYe20EA1uu/GxcVmMfun+TPf3KKXvo+3st\nzXRyMo+y4koBSjk5wwagJAqTO7NB4RTeOVGmMGIrO9K2ftUi65g3j7wZlJcu27evx14+3GcjiVqs\nkKyh5TvIhpmlbFnLKsEdk66ULYXNUWc5pnMzWAnfe9M8u2PLYvvDPzuGy6sYlxJepIc29R2l+As7\nBcHhJat1Mb0SJaDjwL12Wa9aUmO3XL2AqXEUI7KNsIv7wLFe276/z/rGNI3PGmDK8rWzKKKyqgY/\nlI5VLy3LNL/X49Y8cQUWXf8NSxLc+wCDgTXLm+znf3yt/c3/t90O93McJO1N6Ivpin9KX7eKNVhB\n+R3eiNw88k1+IlIjD2oxDxMAun33fvvGI0+6ZbOytk2rGqiD1qBoHjnab6e6+qynp9f27Dls/YNj\n7paLKQDwXY0ltMnmLV0CCCi/yiP6olyVDbjQETBr0CJlhotaHd1EV30T4Q08+RIG+Ah8j/Ues6bJ\nPfbLn1htd22uZiDIxxxnWl7fBmWTvffchrIUJc3Vi9f9LAoxj3vf6t9XuAoYcvyJZnVDZNxfBBoO\nlnFPqSxiBO9zIDxPLbwGdBm06e/9jbERdCwqE//KIyN8Gm1MnHDPBKGMKCGXiYkJ/1OM+jTNYinE\n8FZWVkIHGkxNYV/wVhBXUaF6ua/QfbDM13LyW8ivX/U3GnwIdh5J61ALcMrzZita31mDM+EFGouv\namCoNkob96deFGWVwETU3An61gqOp3BbJH7FlcPbwkARhTEBTpH22yp3pjLPrIb1NloVGBRGldKo\nAMPq7mx8G9Qa3kec7/npQErTK07P+ojxKUAqesYClecxm2mzRwNrUt/7nlvt77/wFd90ULTICRiY\n2P9506VgaWpWL9SZ6QZLWwZFgrWtx3qG7NDxbfbqa3utoa4av4DX2eKF89iFjxiko9VGKXWSGsGr\n7VpeEDZMBcVbcSpXSpSuea1bJNLX4kkwE9Sxp7ES5aQwO/Y8+k1+gB441QaVqdK19u6adfV2642L\n7XvP7GU6UpZGpdH3GLW2eRlrb01ZfT0WXyy5ehcUZRQ80QAIyAgvmu6mvDCNTTS7wvKTUnXSrNVN\n2RLK2NuAFRCFqh5T5Sc+uMV6RvrtXx89xDo+KYaaNlel/CZYB0i51Sib77+hwz5892qrqauwfjak\nScG/4eqVNjm6wh7+wUF74HtHaT8WNlkm2T0ja6F2KBdQOLQLXJauSa2X5L18ThZYUFhLFajLWAxR\ngdQxawBAuiC0WECgqXA1jXRaE5lF4ZIRVDhx+HSRkPdvKAEoup20jasa7SN3LbHJ4XEb5Uz7dDUK\nIAruE9u67G8e3GqjkwhU6sqgIEsZz1JBToMr4VvN5qegJQnAWilrP5pzmgGOFPuEBiXgbkKwkSaJ\ndRff6MAlCpmgNMoWnN4eFRYkjSypTOIDoYT5uYP6T2140qBQinsOq/PxEz323e8/Z9WcSlRV2+Q0\nmUS4yyomhfj7T75q2QkGIUyxy4pZ0TTPqqBxbfxJ0D55eNDGM0EUkChaAUbRvmKlKPg70TjfQW0g\nuBLFfZgKB36itdQiBx3mWLNZP7bfPvdjy+2ua2sZ0BEHDTrfeF0abkCrXhJlOR68FmKou/gmSnAF\nX8T3QaEMONG9+MMPpwAnTvv6cHwzYU48oBaLN6Ek1s6GNJqdGR9jcAKexsbGGRgMugLZiw/lAUZe\nY/hRHhlmIEh/4psHw8dioKbyGBxRh2YUFMIXUi36XmA3UjIDbLzlP2REAtEtA0/aEKBzyCBPDT+g\n4WgmQf1+UoMOgtxu8Wg1tdVYwmvpP6p5Ttq89lb6oXo1E75mLXRlhVXg1US5xBPhoAsNEKmeSMGi\nP0rjQTytpSGhAUHmgB/idT8Xg1rqLSvezMVWznybAgVF9TpuRV+zi4ZmjcI59Xmnob0YLbTHnXyI\nJJ0iz/A9pkdPz1UscAZuZA1KyJm0Wx6zNh+foHffebM98ugTKKF0WnmEHR3jlLImgaUQcBA/qZHq\n3tTbaXNLmh38w2yKGDzZj+P4R60WBWTh/FZbjrVnyaL5WEFbfNOLivH1kHTA6gDVoQbBK9zRLcsU\nRLn0nQhRYNV7KQUSKFJGHAylOVdQlx+USQedLHhT9GlYqSO6TyCg1IZJtBmdKf91nG8/89IhO9jV\nbzmsVQm58gEfEkEJpo21IlLrEN0CJcum+nt+cuMIAo4HzRP3wv4h6/m7H9iJfnZnozDUQu1Xr2yz\n471ZeyTXh421HkWrivolRLGMokRZbszWLqu2H713rVWD+y98eZu9tK8PHGTt+vVN9iN3b7CP3d1p\nW3eesF1HBxFWmtIGSxM6d0abiARRmITPYJGukEJLmeg+CCetz2Q6G9yl2fDibaHcCoQme5uARWo4\nyBFNsDksTWK3TuOVAZTTBpRFLJRSFl2c8V7/kkyVo2Pbl775sr24fRgXVVghP7UOeFvse0/X2bb9\nmhJOAYuWH4yCu2jzC3Doq2T58DrnvAK8y8drAjhThQHpY7yX8pcCFv6gkSosfOo0UigXaSmq2VHo\nYQy9Wd8CH6xOhyof2ClTzTlniJQ74dKVTeA4jkeGrz30OMpxvdXWzwOLQRkQpScZVDS3LWDjH2t4\n5V0CJQDqBT/UBMBOlpSpuDiItkUfrhjrVtiXglQCn98Kbv0jg76V1gL7FWVzpOeQNeT22i/8yDJ7\n/5Ym2i0lCSUc6zeAevniGyD0GkAA5YtL9U6lew1cZ0eQ0hQrnbFyJDoMCrbaKJzrG/NVSFvAsi2r\n5MBgP7MBPTY4OGI9vb0sRclaz+k+GxoeRgll9qCmhv4n7dfa+jpfLtTY2hbiquF1+ECDr7q6YDWu\nYDCd4U9B3yYEBkXgPU28D4aFasFG+covRXEMa6k/q+MiiFdUwgQzAJPyr8WzW0+zGhgxM4DSqwHC\n+NiYdfUO2qFjpzz/4OBLtGvYqlmqVA3ssoS2tbXgJaWa2Y82BvOV1oQ/5aYm+hPoQTMkGsRpYKfB\ntVcL38Y4lJIc33vFc+kHXoQUhGb/Un6vx/izzaW2znBbnB+9r1bFQmjpn/d6MwzRhVcXuPjC813h\nOdTJnz1Mo/2IOcLHO3ueS/HGO2lxIsI6xVXrBTdvXGMFOsTvPPYMsg7FCYuZCz86whDU0StPKURS\nXdTJyX6lLhmFRfcVfF6U2TEsBXsOnmLa/QRp6KixUnV2rmDKvdkWLJhnzQ31uJmqtPparf1UJ4k6\ng+CU/S+NQJdSqvWy2oySQVBoCl5xcogddyil0LzxXl09XTkCPuAe4GlzmrZlvFmqUQovypDKZjfN\nNZ0dtmXTIvvqt16y8WPDdttNa+2WjR32zLM7bfPmq21+W60dONxtj3xvmy1Ztoj3K3zT1PadJ+1f\nHttjpybyNr+1xe5971WuuHZ1DdhP3LvGOqoT1sSRir/903fYkb6U/d3Xn7OhyaDs5ljHWQlubrup\nE4taxv75ay/a158/buOFOnSTajvxZI+lKvfaT923we6+tcP2PviKXdW50D5w21W297X9tvnqZWYo\n91984BU7cGDEbr2ug2MbF1tTa6Xt3HvUFrQiTEGOVCGOEcfiWrAbmQq/ZeNSW9BSYydOD9kTWw9i\nmTxhY8ka27CyyT54+zI7sGe3XY1T8VR1jf3TV1+wbftQwrEAShOShTeBQBcZHR/O2YHBrB0Z7AI3\nbbZx9UJrq+TIURsC95PW3lSwe25ebqtXtHOsZKWdPNVnz7142J7bNmQjWaahU+O2alHB7rx5rS1d\nMg9IC3bsWLc99tR+23lAu8Gpk00xRLsVtwKhva6TQdJ7rrEjXSP21W/uwJqqQQ82Pl9DWguIOnZW\n9PlmAcUXJVr4l6ujbqz0X3/4+zY4lrHG1kVhjamUCSnmGuzwr7Km2gc9smSLFeIpXRI5LgDU8Syl\nyPmlWH008FEeVwbCFwkKIfiMLAauOPAqzTKB3GivjQ8csHXzBu1zH9toVy+XBXUo4gFZsYN1K9JM\nqU5LFWQ9FXGr7XqvegTL7AnqT6QcxbM/6gMmGSCpOTlGBuPjk04f3af67QRrZU929XDM8LhPY1fV\nSBlMWSMnSOk0t2uWrXIFU+vL0/qGoiFZ97W8hQednhaUMH3NENQ/Cm8kceyFWOGRb6d33IqXRA8a\nrPigGdg8jv6pukqzI1KIQ059cZVYzYBGce7RIH7lV/pOxTOo1p8GdYoQDlT2ONbZ3p4ef+461QWd\nDrIh7SCDQ1aKT457ffMZ2Le3N9vixQusobHaDyZgAouSRHei3Zg+p1XMQ2n//vp3s+UZmSOFMwY3\nviniP35Rvr41DCAnIzLRLE6M6IgVSuj8rZU+E7lmn8IZE/G5sOOWBRJ5+pKOXs8eJy4o5YTS+3MV\nfpHe05nl1NlyVQfqe2Do0K/bJIfWSfvW95+lV4awNPWn9qCkSaWcglvUx5+PpEnjFiu1VYJT1gmY\nnw5A6ZNY8oLiqH3QeXt190nSH7UKekNZQCtRTnW2/HyO22xurOc5w07ehjCV5PnVGUsp1BpJBDFW\nJpXnPTTP5wpyGl7AchZ/D1RZYIyFP2WhPOlP4jnNKG5BS9o2djbaEwisA8dGbfH8RttyVZ1tWLqJ\nNYrsFMfqd9f1C23D8jrL1NbbBNaMKhB4310rbYD3X/72LmvFT+k1K+rt+LFK6z5NG1FSUmkWK8i6\n0lRtGRk2VC9wUDlozmPVTNgyliD0DE7Y1v1M9zEJLrsqtkFXyl7Yccw+ddd6a2tAkauosrbmOrtu\nda1dt3KDjWNV7qfumtqc3bSp3n7yI2spP2e9Q+N266YlVocAZBYfiyJtZzr6/Tcts4/cvYad1JN2\n5PSILe2os59escny47i+2tlnLQis61c12Q2rr+c4VspmzVpdlb6tFH2m6aGRsDyhAqupWVsFVvLa\nfmtparRlHS1YeCbsRHcfgjXBkg2zz/3E9baO9Z7dXWM2xA7u9avbbENnOyDusGdf7kJBL9iv/NQt\n1lxTZcdPjiA8zN517SJbt3ae/dFfPIFSyX70SDFI8e1XLKy0z3z0amttrbXt209gyUKcApPozfGK\nJVpDiXMGknhHCr2eZB3zQ9960voG89bAjvQCp215Wdg4g8Ima2ZgYlk0pVyIHv3qN/8/e+8B4Pdx\n3Xe+7Q276L2zgQ3spNibJIoS1RU5suJ6sc9pdpJLnDhxHJ+dRDnHd+fYsZMocVEsneLYpiVZEkWR\nokiRYu+dAEkQRAfR2/Zyn8+b/293AYKdkrDkf4D9/9rUN29mvvPmzRvog+S7+IWfxsFuek7+UwKJ\nL/zUpJc8l2zaZmyL1LdAiAnggQM7o7Hvubjk1Ii//clTY8l0IC56wPJzAyA+A6I/XNqhFgxsE6Zv\nu5SbS/lT6jk+MvB6irikB3l1+VyVhwO9Y/H8+s3x7LPPI7Hsj0O9mBpDjWEWEsqVJx0f3T09qRPZ\nzrKzajdKMisgSVMzpvJXyEI12aNRH8QN4a3I/F7qAarx3TD2dMXVAhKZE/Gc/NJu9WP/ITgepm8Q\n+IwIjjO+/Mng8mMCP9LSrm61JO803eRdvXFSruqGdZjvXW2iDXV0d2LGTn5siONWLuPdGCetFVWA\nQcD3XqS5+/bviwex7/rIY89m+WfNnBHLliyK1ScvQ6WJVQaPUiZ8JmZRkije5EPmcSr/NCF1lmql\nRMn1pTi+qLu3TgHoKPso2edu/K/0Z/Ks745dYh+zgHOig4F+DggSskbsQtCKqNXV7kGy1zqU8S6q\nFna8Eib8G3OpoHL3Q/8lK2oCZUdH4rnrHm7SzMt555zGcs9I3IlR+AGWiVva3Ryks/P1jnJmZ1U6\nxoyEAVtQ6tcEm8Rl3BnOQZAOVmmBL9zxnqM8cfQOjLBreoCNO70sI+0hDF0Gg+7ISAGI7SwZtWOK\nyaU0l5G8VwJ08qoVsXTZcUT42i6bBum647kAvKJfZ0hesUysPNVBngeXdF1E5VaY4b71FsrlBqiH\n1h+ML15/D8upvfGvfvHaWDRrWtxw/674yjfui7nTx+LX/o/rWEqeG9/47tqsWuB6grHnX9wTn//C\nbfHrf+9KdlfvjN/94gOxd4DTkQQd5iHJw9KdksAejJEfPBg79ynNQUvRjd/kfbiVozyR4O3vG0Jq\n0wrAZTkeiZqQfg/SxT/44/tiw6492Jdvjl/6iYuyY/jC9Y/GvY+tibNPWxE/8fHzYlqPm5UaAI6N\n8eErTo6+fYfi9//oO7F+72icumx2/POfvyyuOH9JPPj8XtJkqRqQv79/NH7/j2+L9dv6YrCRZTuk\nNu6GF9yUeibf5O/T15wbH3p/ILGmjhAdP/TIVnZq7ya16XHBGcuRGk+Lu+7fGH92PVLdwYZ4zwXL\nAIxnxlUXzYu1L2yP91+5KubP6Iyv3vAkKg1rkYw2xkfevyo+dO3J8b5LT4ovfuVpSiqICqS1Y3H1\nZefFPMwL/fXNL8RNd69FaovOm4SkDsdY5o8mpJuqhbwO54aLXejzff3bdyPp7Y8Zc+Ar6CsvJsPy\nK/YQWMoXFJerHGN+SBPnE40onwUKDua5Wc4WU4kG0ifyR5lOcMHVTlAdW+saXQImM6hGDOymbgDh\nbS/Fz37mpLjotJ6Y2Ubd0gaSsTIHRgZ8IR3jcXjNaEkvb3hm7aJkdty/YY5FB93oLKSeZRB4WVSl\nfGNIBDds2Rr3P/RoPIeJqZkz58esWbNixfLlMXvuLJaZmchZKfguWEoOx/GTk1IirUCnr3V2R+nB\ni9/pmwRwhq/ep7TMDoyX+dpPNZf5lDX40JjLuIY1Dp/tA8tz5b+6Go7qIhj1Ba94zbzlBz4aAf9T\nqksb8zlTz4zpyUm3Ez4SJ+1pna40eMOEb/YMvjsJbIp9+/bHPnRUtZrwwMNPxV333Mdq0qw4BZvL\nq08/NfuM3MjH9F/+UUfccEeWkwinkKN1tsykv95GSZykS2toBn2knH+F9t7wV3dJgXHa8PSKZMFT\nWguBiqNKAjwKGz63v8u+h4DJqUame8WIyucf1e8xCzgnCDKZct4Xik5+q9+KzhkuOwd8yN32nkl9\neyeeq8fDQ2SwH/ZPZsUscZNdfc7muafzsTO6+PzVMW92T9zyvQc5r/xQtLib0kExW62hK5dNmwcH\nvMnvYcHaY9Inf0yvuiEIHpSCNvjHY3bUNdoIYpQGaPbmELZnhFa9B3m3v59lpAMMNrMBnKb92i71\nVQUjOSLTMSmxZfnaNN2tnxtPeGo2P+g3mhOBdS7D886OS/2+6296Kjbt76TxAY53sBEGvcuv3vxE\nvDTQGQd27UMyxk5xGmIzlv1TalqLybwPpwkdBlIAbD8jxBA6nOqCCguUWLkNRkPmLoFppsjNMWyX\n4R+HDNTiaUQNQLCixLkBjUtNBQlBvn37Y/HkJmJo6o7pTQdj2YLpsY2lxnvX9MWehgVx9zP74pw1\nW1liX5mSs0VzkR4zSO060BAXnbcqzkBiOROCa4lgGh/aAa0toFzJdfOdT8VTm8aif2wOdWWNoAvK\nUq+DoeaHpIs4fStLfBt3D8Us+qLjl7GkfvICgOLq+PbdT8fJK8pBCTfe/kLsGGFQgEaPr9kT27bu\niwUcSzpzZmOsmNcTe3YNx12P74y9jdMxkt8Ytz+0IS67bBXmnNqik+rTFqd1c+0Vq1nGbI7rb3g8\nvnXndnbts1ubetL+px2fm7FyeVpWk3iv4uSNl8j71751W2zdR/lnHYfOK4XIsE45EhZkDLmUCgVK\nlKZUXLmrgQdeZXvKT/KTvmp8SlrG51OZuMnXTG5a0LZlEjUycCgG923ASP6zce25y+NvvvecWDkH\n4B/7YAYyZCdPe0gQAu2NS4pkIVUjAPQUGplH/jJtUkp+qeUh/R9jPxRNyigxFA0yj6KraY0XXtyK\nxO6J2LRtZyxcsiwuu/J9MXv6dL5RThtIAi8rqrRliZHUrfUxBWjWypq0P0q5TViuyevh3w8LP+lT\n0n+S/8n+8tskv5Nvjww3/o24qugm8kF9+TI/lK85wTNQImamGNmnjceSN07GXSWa3jMtli1bxMrH\nqth/aCiefuoRDi54OA8eeO9VV8ZxSD7dWGf/anzycJWHw2OcKk+MIR2LUAVbh2rUoRymJJ7cka52\n42Vql7NWnrf7UhGqRpzq0WTylf09zDnG5uBom0NbLf1g8Y5vx9Tsk97ujL198U0pwClh/asYdnKF\nTFUWzvGoVqaqWgsDUSI67ROOXxEzZs2NG79ze2zYuIVGrKmPLgZHiVENYPnAs1Iv3eSOi9jkxdq7\n/F4lkH71PvGihNRXFRNMnVGUQdqdv24cSqOTWRtVJK98NSaHI0/HMccuczahL+hJQJZf+5kt6AYW\nYAA0TBUC7VQaxuVNOmXA6RARudM1JXsMhn0Y3VR9bjB3szrwNycwNhHHOze5WI4RdpIqQc0hEcAm\nKMglfa5u2OINAyzAEzUBn1xO72xnhzz6ng0DbKoB1ApgtF05o705pgH8n2UTQd/gIeIqu9wPDPSx\n4WmAAcjNOWPR1d4Q29DFPTSIAgPS5BGWxDXGb3/QyBLdNKwSSJXZM1vjyvNWUlbyRJ57qdjN+5Cw\nMQapB6kOb+9BdWhdqsJRMOlZaszacqJAvinz176/Nu55ui86KculZ89GenkWUszlcdsDDyGJyUqM\nA31N0Q/RmF+ww5uTcdg93IpUs7N9JHrYedvPMulB1v0HlFCiztGLXuUACTayHNjSanqZi9S9ZcUU\nvTwl325wYhcv5ZPuydPk113uxZnjV3aaM7rhu3djYaE3uucsJx338ptfh+BS2lJnvKuizOj8XntV\nS0L/xX5o7YOAQBolfa3nDJh8mOfMy2sM+r39vTGArmZT/+ZY1jMQP/fZ96Aq0RbTmg44VSEQCVTt\nhKvlLCkbf+n4j8gceSv/Si5rGTTYMeoSOwKoi/QXqebmbXHjTbewfNcRl1x8SXSxXC59mcrxT73h\nqv1YL6WmrJBsn7WyH6NFfX3Zqur7FX1T5sl+ZBEJwLvUeU+WwWgYKgdzUOG5+OIL48DBPfH4o4/H\nLbd8L8YuvziOXzkf7/IGfGTD4f/UdbSDNqS8baxM9As45RNoQZnUUHCVyOZoi5NOtpy6K1V+WLVD\nJ2kmV+Qv/JS0knbet81HKj6DsVCLHKU1ZutjfCr8mFx4TJJ2CgDOY5Jub0+mxplqcnQ0RkCR5ors\nh5rQ95szqz0+dt1l8bC6QY+uiYOIGTVknbMdOn07uNKMCeBt4VRuykMZ9ibSGP888Wr8rkiQ9MFf\n/i/3dhLelXm43ok10/X+1Z2NQcPkJT4kIxQsOyJBj0H5ZoekLmsCQDrePCmJT56zLsjMslDUBAw2\nPI2cJ/g0uINfM6DHvcwOd9l9Q78ausjOHGAkdMCf6bQLMpCSumlB300gwRFP/8G+5tbtB+K802bH\n6hO6Y93d2+ggAUCQsn3sAEDu1FzNWL8DE0SDLDpzVKa71EdRP2hAkXKEndkak+/FNGMb+lo9mPYb\n4UjFLvLWgakiq0Rz9HsxYdRPmg8/vT3+85e+H73Nc8gH5eVsbkICIDmCk3ilcZqf4uoyp9KQBF9m\nO6koALDE+nWTBCfgIGFZh2rEHnQ4G9rQOWPXe2+/C77BTlooBMAbIT9tHYPRMa0tDqBSsb9vFP3T\nwViI3mh3O1YA+uE/ou1hc1EnQFO1i0PsxNf8lPDqO997NFaja3zVpSfE5t2DcTNqDWowNrhjXcgN\n4HSykJVu5l7FbWPZv3lfOzvPl7DRTdURGMOy+5O5JufcOh77hkv+5UPGKzdVTh/Ue76wxDhGubJB\nRFm28eDHCQYP/bSlvt49MXhgR5y0uC+u+8CsuPqcpTGvtZ965TQjylAkCfA7AVQzyeVWGeKd5OQv\nwabFgl4DTDpuYpLb3tkTF7znQia5SFXwU4oNfakMV2LyWNaKFrbxSTXxTiLPK5WlcF6NF1KKbRuA\nPPZpTm6hke2WGSfvx5B6To9LLr0iHn/40bj9+/ewOe+j6ILTJrOvmto8ZQ/T0MwGy67pMdq/K3lB\njvAv2aL2IM/YBOvuaBSQZ3gvgWSHZCa6K+41BzeC8KJl2omMfdPABvb3tEXYK/dsEDDHMzvKY9TZ\n/9bdj4gCtfaXDdKOq2qDY5r2YNB0uXYMCZMn8kzvbI4rLj43PvLBq2LpwpmglgPYmzyIFAmpWgIO\nQueInFxKXErMqF7+jFvo5V+VxqsXucQh2HnZX7YC42H5VwZ/9YhqX82BALp0NE20Hv9sWDLgknnN\nccKigVi5oJfNLhhI6mAjAhK2JiVmIkw7bKSqCCnpxF0Ex2AzpzG1DiOps9jCTCJrYgm4BYlgviOM\nZoRIkd3wbtLBBAq74Ps5wWgBEr1TFrXF0rkARpDucDZQl/PZicpAe99Dz6M/Oxwfu/bUuOqMWbFq\nzlicNqcxPnXFCXE5u+F3o8N5810vAASnETdqDmRRe5RjmMYZIl/7Aa4vYpB8/tzpcdlZi2J5T8T7\nLlwSZ7LL3iVIDYPv2L4/Bg6xy/sEgO0ZyzD904zuZWucsmJmrJzfk9JC1Q6G6GTGsJk0gtH70rm4\nK9t5opST+pp86uUaMQ+bpStnN8QJCxvjyvMXx5zp7bGbTUNDiCiff36fMC4+fOVxcTrY9oSuxrj6\n/JXolfXEMy/sjF17h7Dfui+mz2iNay5cHqu6G+K0mc3x0YtPjw6Se2rtThQIlBSTJnWxce9I/OnX\nH016/Y0PnhgnUbRmwbAZwTYlomHIAh/lC1++stMmYgtgZ2iQnfCqVxBORYc0tk4d+ly6UBgAHVsl\nkozg3Au+AYTQaeJPnpTna1Mj6lZKpYkpaNWIjdeGkf0x1L8j9u9cFyO9m2LhtJH45NXnxS/9zcvj\nkxcuiAXtWCOIPdi15cAB+NAJIKihgE1VWg5zJWeHvZqKD7ZleLMy2eNkZyunNa044SQmAerS8sL6\ndZJnvyJVubqknPZaWaVwM5snXOUJU9mmpiIh3lieExdAmjwuNpld/i1/Lq1LKbm52Rk1PGv/BSqL\nBUuXoabwEpNp6c4rPus3o/A6JZ29PPr+PSdRoM6kgoWq5iNV4VKvc0qW7webaZuMXFJJze1ZUjLM\nS/lMVNDQNR/V9sV4YrxxDPWf/aBZM4JjvN3VJZxW1I/a5ZglswjM+JdAUxZydgyzcWdnpK7Z8SsW\nxKKF18Y6dorefd/D8dJON/kAnJBieQSkPouU0pgcdHGCFmdCXG38Ra/MDy93h0kt7QnTmYPiStxk\n1UdfT3wqHo7ya6lydzMDUtFvU9YIHEG8qZ7oz37iwty9bWPbg72g//nXD5Bf9CMzr2Np4F2ppLrS\nYyPqLkIL/yDOEO/z6EtepjSQZyACIFJpoF6JBJ3QZsDGMMBr60t74rjVS+J/+8yF8dzOhvj9P7sn\n9h1iacLNLUhNG6IrHkHf8tZ7NwDwl8fP/vh7ME5t+Mbo6QGGHByMv/jWk7ETXVHPRR9qxLYgGGTY\nYw1JWUnu8EBr3M3mnOMWnxKfYsPN1e9ZkUvnTZ47TofQCDAewED7zbetiY9cc0r8/I+dH9t3IrXm\n29zZ0+KOR7bHC197ijKUxVzL43K/5lcc5O1UBFSF9NatA38Amk6Pay+BXqgDdGDsfhf5/tYtz8TQ\nSHc89PimWHPe0li9am4sn38RgAs9s/kdsXX/UHz7zhcxht8ct9yxKS44bVFcfdHJcebx7KoFCHbP\naIs1L+yJBx9+keNG4Sk21AyR2EEGzvuffSmW3rk+PnzNCfE3PnYOG5sejT3sExpByoltr5QaH4Ud\nXvZq6aJ57NBfEHfe/whExbB2N/pJHu9queQTaFqdlOUkR14oGr5GBW2YXAynxNK6LlQp+qNyFEwm\niCU/I4OYfzqEWRvK0MIIf9rKpXHmWZyDPmMaprJa2Mi1PtoAtForcJIDSfmTg6Q76QCKIbaJ1pxp\nVX/Vuyl8pXjSutq8qNrOE0+twboDG6amI1Gp0cF6sa25GkGQmit0zwfbHTwiqfRX9RmH9S1VsCl0\nrcphlqtypWRJXkm+yN4meSY3d4BoXQAAQABJREFUXeUKTOEiOVGecv68d39vPPo4G/CSjyUS/J2E\nLNym16np4AHGoIbmZWyiOi6G9j2VJvhKR1zjDy4ORfbldXc4BZJTHKOzz0m2oO+y6yltaBQ1srae\nlXSRrALR9hCR8E3+cZ2Qe/YB8MCfMR2brg44j5V6OYJHqs45+UkmcvCVGRkQsVrDbsdFcdKqpbnM\n/ii7oD07uqGFQaFZ3UA7Nlt1YVQ7wzJWFqmkNjZfnyusX7oKfs0GP/YVwln/vR5XZmwllHhA0zn9\no53x/cf2sVS7ljZFh42OpiCiDzDmbuWnWJoeu3sT5oIwIASYe+S5l5A2HWJnub6QhrH0/f3Ht8Sz\nOzhVUF1O8tI71h63PLie/q2XYxE5inL/SHz93vWxFjueg3SEQ4Nj8RfffC527+1DgspJNhynOdjH\noOkAqmkb6DsCENkz1Blf+tZzcd9TL8VpmA5y5/YwxqE3bO+PJ5/ZGht3KnF0R/sYkkx2Vt+9MdZt\n9yQaNSopB/m95b7N7FzvjVNPnp9mYtZ+76U4sP9grDppIeCuIfpZ/v6rOzbGM9sOsrlndszDmPQo\nIPDhJ7fHrY9sAtQ1YIaoP75z13okj6Q3zClRSNek0jjZybd6sY8+ty86bn0WgFgk2eLSbXt64/En\ntsSuQ5w1PtaBbuZQ/Js/fCCuRKdzySw6Lrb8v/jIxrjr0XWx7SC0ae6IdbsG4//8gzvjorMXsvsf\npQMiWreln01E6zhtSRkux0nuGIyv3f1iPL99AN3O7rjhjg1xAF3W7hk9TCrkF/hUWjKqJFBLbnl1\nLmlDJ+HKC0+HdkNxJ2ojw3Sm7dPmwfIcy5m8Zh0Zn/QF9oCEXK4UALrT2KUm7TgqHfWdFhbskDWe\nPzBwkNOhDsTgIcxMoUd3/JIZccbp52N2awZWDTpIS70nZKbEP6bKA3koOnVMjkZpaPBDcvnEzxGF\nseEe0XiP8DElHimfLYuxLUGnTOakbu+BPtQn7orjly+JlWyA0cyPfVMavIffRtIEEW1bFFFzfi9q\nQfZX+jXeJGDlZUpeJ5ehKlMpH4M/5ZP/5AQ3YNrnpooLfZ2EVF/9YF9fPPfCC/Hc8xuSbqmjLuky\nlCFfb7987JKvnE42LVpnrka3fh/tbiObOJO5aGdcoUVaNHgHNJm3uxaSJLVmQpeUtHIe44EgbI+N\nlu7jsQO9jA8wjZtGs0eUewoxE4A6KTyGXR1wHguVU/gF9oGR0vmivKxYydlxEaoLt8ow3IAU4Vz0\n6E7B/t1mTsW4675HMOaNLh32EJs57o81QXiSzg+Gzd2nxDC508ykXuPH9N/qUJGbM+yAAWRZHjrk\nocZp8eBzB+Px51m6ZBPLCBI8NM2Zp3HB31YSffj5rUg+uQHAPfDMS/HQWpoWS9guKSOHilsf3Ezx\n+ui8OwnTxJJvV3zttvWQisXfkXaA4TDSyPV09upmtmEeaZhd3I3xp9/chNSUNAFsQ9C1ERCvgSrP\nYneX+ijA7mB0xKMYO39i7ZboZNv6EBtjzOMwyxgN2uZ0Vsn9Mxt7AY3reU83gATTDVB2AEPoMt2/\ntj8e4IhR68mtRdbvI+u2UD+kjd8DozPivmfH4qF12EOlo2gFOPchZR1CZ1IJ7YubD8ZWTjMaZjAf\nBUxbDym/5VtKsolPWqzdPBDPbdlMvskSbxpY0qdkgDSXteisIOooKexlpfuvv7cdc04l9DDqA6MY\nmC/jHJuJiPZFbIJu/u6GtFGoRqwbSFQTaGBH8higd+3mUdLamO8beY/AN268fQs7J/fGIMs8jSzv\ntFJn5kvJL0V/Tad917aW4bjsojOog4a495E18AcnN3GcJfJaeJb8O1Apwafs2k/M9oHqiZMw9Wdh\nAP4Px+AAG66G+gGah3g/iGS5CzuIPXHuWRehC43NVgTZne0AYupzWKP0xk85BZ3O0aRUrm9S7gYJ\nalqU6XBnTegsnH/mZ2q7qppKSXjKF2ycmzEH3m+KZ9dvZ1VlIxYNZsTy5Usx6N6NOaR26k3zZnAd\nxLNe5H3/5YEVVJwn/TgJqADaVKZSJeEsE3jKCs8lxKQPscwep+qzbU6gwHkMcRDzavv3H4g1655n\np7pqMS0clzsHWmGhYts+Q8FfhJHfpjJxyLv5d91FWkTz3GiZcx7l4uCRvm2sHND/0YZz/CnD1xQv\n7Q8i+zY6qOgFGskbbgZVaa51+nHRMuscXvaUjegS20kejCbNM1xiBKWdtXi4O9ac43vd/YgoIJsc\n3dUYyI/c0k5xtlIHcDsmAIVeWJ9xR3c3+p0nn7gYu3gL4/l1L8aatc9z3RSe2ObRcw2YURDsNACk\n7AiNs8zAjeTw+3zhu9pN8VG99ZqBy3Xy61e5zw6IDkdEZHyeJuScTUmSRrpb6IQbPBoQO46MUJSO\nHdkgFQeyFvKf5yunNIVn1AeMw53uBhlFktlqh+9gR6aH8gQG4A4EsoyCV49hRIDKEq87vVuUYdFW\nXa4lDwYirLqJ6gxKZ+WEll9AOspWbM7pITDhlGCQpr5alKLph4jLoONb5G8AWfXcxPpudjIul2gF\nxEHenakq+Wtgmb+NfOc50UjSpFH/MIjQYy3pZVwcEYhzUnhiHtMC8ZFn8iLgJB+5nMIH9sHqGQ/S\nlGd/AKi+aQBEN9nRc98MsB5mmXw/YaRrC0Bam5NNSEBNcWQUAEZYacredZQQsAlLOVt5l0s40sNs\nQDN3KI+f/DOm1QTOzobmLXSP0tAlHnfeO/wQgr9Xdk6GlCy3InW9/KIz2cHfGTeyaz2GF0bHdCSd\n8gf1CmvA92XCpWqF0svhwV6Wytn8Be08lrMD+6jzZ8+MxYtWoJ86h+s8zE9pEkrFBNoLPGdYaTdM\nnTdRJidEQoYm+VPJuudjM6Eg+7j8mXSVkv5V5eJKXFPfWaZSVotjP+NzA22yDRvAre3Tk847MGy+\n/q6HOBCik3pqj1nsXJ8LCJ3DqV6eKJRqL7RbeVCQmVLo5Bfje2e4yQAariltBn5Q2t7P4QyuZGxn\nI9zW7TvjIMvnA6wUNLDTf1rPXDYSdiHhQ3LPqkDpSWwh0qZG/ynOS6nKAhuljdM2jp+d+54Y2nln\njBzcSV9S+lWaWU5Eq5b1zuCKt16KnHLYnRCV0mB70SGEM43dS6J59mrYZRZvVP2C15x8O6Zm90Pv\nRSB6UcJWk2PHo2PP1QHnMVcnsttkV+PAZC47JxXz+Y6CYp6UIUCTORlQu1CIPP2kxbH6lOMwvzEU\na559IZ7fsCXWb9gc/QMuHTK4AjjGGLyLpBMWJyw/JUHjxeVlUjZ8Lp98OfGh5t0gr+4IomkhwYKn\nC1kAFqC5OrBzz0WJmDqXdubDAMS000ms2uGMRgACwRoBUUoNPDu5ycGMUAIRz12mh6Mj54LtJNUK\npMcoEocxzjFXEzIHQPI+2AxQxU5mcPKSqLBB6Z1L8i7pE28jwKk580OmyVtKvki/mSX0VmLCSlJK\nH1PaBkBFUMnGIcAn2bQj7SN+jX2THToINT8BpIgYeUP58IfpJDsGAahHhTYTd2JFvDdSf8N0MIHE\nNne1AhbHyMyQy5Z0NAn6rCuJoZMnpA8xjrmc7uSC+8y3U2PKLa2MPzcrDEOPJmAk4pURj8M0nFJb\n+YK8acS+wXjIg+VqalQXU/ukSpU5ktC4KV/qA0t9wgkqWgFujYBVOz2EvjGUfV3Jh0Fe0xGP4BRC\nRTug9YLVJ2J6CrWEOx+Kvj1IZGcsymjM2xgS4EHOb9+x9cWYO7M9ls7rxlbtvJg3Z3osWjCbZXPO\n38alhQLpDX0axthcl3SjOCraQ/sybcMfoulm8i8wb0igCR8kea1AANdr5Z/y/uhclbkqE6/1bE6P\n9PPydzk5pewSolg+wOoAj02Yzepp68bSwaLox4zUAc5G37N3Z6xFQq+JrZnTewChHRiFB4DO6uGA\niFb6m8bcmZ05tA7SVXmYeBz/UrtJPk5v4y/wXAuXYHhSTHgp9aRf/EyutPRbpTc5rlp0+ar67rua\nn9pnGGXcCQydaPVx1rp8f4jy92IJYg/Gfw8cOsBJQ3tjz4EDlLkNNRomcVy7Zy+M6ajPjKrqRFh5\nb8RGwiQqwWrGnj0ZdyY2KS/jKU+dm1ImykC/ZT/e3LWYfvNS2vZ9Mbp/K3Pu0Rikc2kt3dY4eSeR\n+c0VtiJbLaLqcXJkbyaNKp5xlnozkUzOxNHua4nYPITkjkP80pe2RkvPCdE851z4h9Wq7Gftp+2g\nPP/OfQ7cIzyR54ut39JvHS2ZY+FdHXAeC7VwWB4qFj/s5cs7QgZmZWsp/oJRZUE7LK9jLBdO62pg\nuf2EOP00wWdfbNq4NTZt2pq29QZZcj+AfmHaxdTKOcDJzjSfBWn8K4hJ5hUcmidi5iJozAViAQoN\nonSSXF7VCeQAQ1k04zQUmVbqhxtjUBLEqJdne7a5FVmofvRPWC55FCYSXfMn+GAI5E4pjBFnrmv6\nQZZGU0e8T6mlYAz/dIBKs1pGyLdBbKR6IfImUO+wUjS9kUbqIjHKNpG+oDP9kQcbzKh2Oc1WLsET\nJ/nQHpr6tdaKHUCT8Ewkra4NQGaUd2lcX8DD1yH1E4lHyWvaiCQWAagAu8kNN/gq56RbFvzjVwms\nZ883k6+sezfE8I2KKMXhKW8IoTkffrLuMn7LAZ09JaUhC6lXaWnsFoY7xVM4Y3ODGjnmwXJJ60KH\nRK92chDEXOhbhQR19sxz2lJN/2QLP6lGYfBXdebCmPglTDN5P+3kJTFteicnD3Gm+s6N0QXICaRE\nzuxdumyErte991KO15zJUaUqJDjjl/7u2LeWiC1Z1lihkfWNM6Virsb8Uzb8aM6JGmeSIjPgg2LZ\nFopLRqnde/G5Flk+lpx7+8N15qFW/4AXpdtpHQD92bKLnFfQ0R39TqqkRyl9qevkGyZRWRZomZyS\n8fim/KMCoQn8XfEXNBEwmWoLx+E2d2D+JmeL8CaTosFBJgLoCm/d+1I0rNuY0mQnxZqTmsZpRF1I\nrttbWzlzvDWmTeP4XGzaNjCBaWNHc7Nx2+4gu0bl7YWas03ZJs27Ewe+U+yiqwZww48TBWvP8jTl\nEaNwI5YFrEtDBRspbJfyYgF5cHdWMI+8cxLo5Nf2NIIkchQdcuaq+FE3fCj60LscxJ7ugYOHWBo/\nyBGz5d6MDjPRHHawZ/BvRlejtY0Jz4JFfII/5dHsS0srKbi3xlOkbxPM1keZ0mJHlqL032Z76jrr\nwhalUxjAhtaWFTG2sCsautegS/4cK2/7oBF8hVe7bilovXp9085kqfExwKz1bvpZNUgD7cek/+tK\no2Q8/RuJ9ZT5o3n5yYl96njzUOvmePsGXC3+ySEyXmMnk26u1XRfY/vMaJlxYjS3H49X7d/yzzLq\n2bJQGHrzfF9lJMce+7pj2Nnj1N0xTgEbUHJZlc/y4vB3cqFOZoQrC9TgbHCWKVunt8VcmPfM00/i\n/OMBjm3sZ7lnR2zczKaS3Xti69ZtdLmwL0vvg3TCjYgK3WUpkytHdMAQdJiCJpuwAW73nPfZADLh\nV/4xpAbRM4KatxJbrXGY51r2/WzKE87mXuuK7DFqCXo38X6y//xADL6jhVIOm2ZxNZlCbWD1XQ4E\n+GgChKbM1F4weyaHgzKUuclJmjqApxpARlbynkvyxpPpWU6DO3CYrnmo0Y1nBRtZOgCjw5BQza+m\nU2IgDGmMAlA5QZO4StlzJ6x+eTZOfRtV5p0f30m/jL4iZD7gybe1bwVWTaIug2bxZpn1q6t9z2fS\nr4UVlOvGh8TKv4iN+ypP1eviN4O85s94lo2L8khjTVQtXzI7Pnrd1fGVb94eB/dsia5ZCwETHqvq\n0nhgL7QRfVTqJCFvDf5ad5mZkpMEm5NyMJFW9d2ByXtrorqaDfOSr6ufSbFUtyWOJED16od9TSao\nEnWSYflrdZWFlU99LmWbuBqm+CvtqCr9JK/e1oroteJn46h41r5CvtJMZ3O7BvtNHqk4G7aUjnty\n1rDLzGzO23NgP6+Y2KHr42Yj+xD1nrtZsm+3XuF9Kh5VDyd6IwBTLD/wvhlpYSvSVaXwKbm2jNRP\n57TOnGCOMkEzTw2sgJjmWPIEtnA5nGFoEFCLObQErKxegHBZ9gZE9g/wvpF8eXgDX+n3hjCFNsyJ\nasNYkLAc/UhtyRFpCB6Z7LW25epJI8BS9aQ2JqhN6MoXYAl15KNso2antHDuioOfc7nZvOtMgL/S\nokubznf5cer+WCyZRl6hBXEPBVETamyYy5m7HDDRPS+GD21hNxoqX1iN8FS57Fugj/Vg+BqFxq8Z\njT81V333cdx/hoUTuWbPjCdYAZ6RL8gNeUpeNsCrueq7VyMbr6+Saj/86qqRE//qk9FVwbw/Mn9H\ne5d+/CGgEk1klbAMR8R2zou2aYu5MnFpQV8TfXxXpIoKmBQlUCbmaJUUzufSek1pck58PrZcHXAe\nW/XxNueGYcEZNIBRSYSGYqd1YYx82jSWH7vinDOPoxEyo0Kytg/l9h1sONr+0g52cbN7vBc7ikwR\n+7g6wx+g85WpR+jcBzmNxg0ZbQAjO8yp7HJ5OzsjwRedSeo72nEx8NB2m2sSoxGlQOprZq+ldMVl\nZ+BMSmkZ5Ox9GGwSkPLe5wIQHZCVIUIpOxfuGlAlsKtIAArA9V3pJpXkou5AB6kUZlBASPwatG8m\nXzp9urkphyj8OrBlJ+THKeqUiuZgDCUcqAqVlDqNoIM5Iz7z6Wvj69/4bmx+6TlMOa1I+kgIFsOR\nQBRor9+kg0R+1ziH1kKvigdcfWjwJAKoM4oKRUo3VV3hOSFAbRKTnKT0XZfmVOSjGg3z5dF+jNe/\nGo0rUsuv8nxhUdJE4scSss9N5KWNE7uK/U6nfmXwN56iY007G8LUlgrnxKu+dj+nX40hldyOVDEw\nj+YxtsUMjNxhIqXN6DcPiDAflFE94Kx+dXEdwAGKwea/UDXE9pvWE2gzqLHYJ+qnsdk2hDxVPkJf\nu7mVSQw2aOXHbifeSCnNly6PtK3dCxXLe5EHf1l+/JmB4j0vNZJk+HfND31WgjHpAuC0f7PmGke7\noOdJGC5fzmxxXzQPYVuOv7H+vUxCmBzIu8kXFW2hnnG8hlPnWt3rrANsFlt3BQwycRk+iP7oLmqr\nxFOrmleOsVZhxV/pjbKfpt9PtQh0ylO3nzSKQOCVo3r5F2MtCdgWUzWMFcYGVmg60JFuaFtIO+GK\nTn2RztIOVPUi7wkvHXuqRvbyyKfEmzrgnBLV9OYzWXhUkOOAwFItbTl3OtNZqy85wrWNjnlOTzt/\nizFEvpTEWJi006fjcNY/AiAVlBJJSi6UWthwOjva8wSaN5+7YyEkZaF3UofUzk4pimVTj9FByI7B\nZXU7HbsfDVtLnwIo9W/nqA6igyR39Cdj6mXaoTjo8U+JJT0HUlSAJx5S1wbPqbfoICkZCGvf2kTH\nmcv4poakJnVZ8VAkp0SDvxFAaj6TQi5hG77EkndT7UcaUax0Cdy5k2/d8T6KBGTO9Nb40Psvim/d\nfGds3vZCTJ85j3pwABHMQ9+sPyVIhIPOFT1LjO/UXynGn5eaRLNMWoDh6Ok2cepVv5I/Ri55uIBJ\naVYN5lwllOHTjd9UL45+LSP5Ed8KUMw+ZvyLrYnUaEd5UpiqELxJabIAlW9Z71R0YxvATgtUtfaS\nAywAshOJYSZnti2jzJ/555Y4BJyNxstVgDoK2LA9NCvpRCHcQbuxicG7gY1sWK5ocGXDgxrUY7ax\n4TfPRqfNERMZsF0DkJzQmUd4yaldSdek+U7GLadpF4km/ojKeLJQeLMklSt3tTZevXxHXyGQE280\nN5MgEof+T+AEyaCfkyBUY5QGN3MiBt4aOHa3tarrGm1S7QGC2g+8mpOTPF4YZXT4gLZPx5jWJVgF\naaAfHutnGX/ofrSf+qLVenq1yI78pufaX05s2+dE5+wraFOYBrMZwSdvLELCQIMMJC3ywQScOBoh\n/JXx6om4aTvZfilX6mqOt3M+T1FXB5xTtOJePdulWdlYS8eMlJNlDV3qSsLYSjad9Ll0ru6gDF0a\ngEM4E1B2nPIZAYE6YTYO3tohY2rHvsJ47Xh7t+3X15R1DnpZwJQ0Fvpkw6cDEHy2uuGExj+AFAjj\nPYBBi8oyYS6965MNSvrIZTyGTPoJzQGNOmglAQX7LB3SWaivqWRUne9GFMU09O4ga3gHx1HjhK6e\nsGRttI7uZ7B00w4dM/5yNzXZVafNIUzdr8z/UUHA1KkSS2N501EW78rAzsDF8qwmnhZhEP9jH7wi\n/uJrNyOJ38T7mqKdAz0DTpGAOdDVAOfUKf5byGlSivCOUvIxS79wSyPgqr2zbBgbwObrKObC0k8y\npDyG39RzZYk50ZKTrOo9t6/panWV/ozLP37tQ6i4/Go98jfCX0M2mqpe7TdIFv9OeM2LfUtu2POa\neaMOAZLkCL+UI9uF7URJpGkh0SJe9ZHL/JB3rN5g7wK+4A/Fu0bQRRPldUKdwFAS4Tn1P5XoSgsn\nf343HWlIpuQ7O7giV/K55LdM2AmTnimneTKU/SjX8bITb2aRt+8+B/Hor5K3BJ0Adl2yGP3hGLq1\nHsBQjorFq37UiQQc5kZECZf0NFSRWBZm8vlVXK0OiAxP5sFNsdSRkwsmHyYrC1p1r+lewZP17alj\nTYLA1Ik2Jsoqv7wZV6VjeOmU/ZgTHfpCvrm50YGiWvlpcPJoCaqyvpk0j4EwdcB5DFTCDzoL2bFn\n9w2/yscMyvTXyb/5wo43M1HrIHgqIMBulwD8+evV8SDf5At/3myLywh/5D9lCduZcVm2cBAqy7PA\nS3S//uaHlsWZZ66M//KlJ2PdhgMxi7G7jcFsy356MQa4lMZIBZbfUIAFIDKksWlrbmdT9HShEzs0\nwqlEmPBhfBpy6ceBEv+NKMLO7FRSKaDCmBDR7WfHqwOcddLVPhK/+DNX0X/3xu9+8bE4xOlFIzVd\nRafBteE76zFrIOvjR07Ot5ABB3BdchdXqAQY0GxR4b6xmDejKz710ffHTbfeFeuwwJAjWRa+0Gz8\nltB5b3TvWGcJpZlXaVYboH1G2heN+6O5Exoy0A8cFNRplUEwXrVZ+Tc5kasgwXhK++fmDTnB2Msc\nwCP7mOTn8jVBW94W/+peljvzUUBeqqRQrCbAc07EBA54EhCOOjA7GtcmGDRVmkrxl3mAXyyfmGBE\n6VMiHacgTArZDFTS5+rKjkjVPGY+nfC4nFtyM8aEr8prju9JG9+RTclEO8xs6B/+lI75mrsCXM2Y\n/t5dThrkfAH+SzNkNaLIfUknSS7PJRCVbtJJ3hOkSzDpCY/md6EJ44/Sy3STCVqLuHoPsCzArEi5\nG5Foj6EKUeIsadZir8X1ypdsUXiWd9M0GC+SJ8lb4Q/yJ/85hlqWcfcq+Us/R+S5CpfB5C36ft5l\nnEoklNhm/LwtiL1kqgo3Ra91wDlFK+6o2a54epz3aSITvWQG0UvCyNogYTcPZ9ecjb+40nWXexuc\nnspvzQOXfK7FM/F2at2VRi0BGKSZuTrIOfC6aaGnZYgzyY/n5KPe2LN7p/KV+PRHVnPiyoz4td+7\nk6UapKD2Df5jRqo1zRULmuMjV50YK+Zhu7CtPQc2LQJ49vo9T+7CdmQ3JkEG470XLIz3XrSQgcsc\noDQOMN340qG44banOMWnIfoGOXEJY/4Xn700TljcFY8+j6STrJk7Q5hmDph2RuWJ69R08pFAoXCY\nTxRNvmL0Kp28b3iPtHPezO740DVXxE1IktzNLj0KcMobfoylxFG9eades5TyRHWTgFGKQM3GIfiV\nyUw7EynE50N9SpHc1MOKhQBTCV/yjv4LFV8PnSrKlhAT4SYG91p8lUcj5b56rEKUPPOem0YAg0Al\nVU/waXvwkAaXVd2xq0RMp9H+IcyeNaKX2aTkSpuvuVwOXBRQ8k8+8oAGd+qbVrPqLFjlaAWApF1c\n4syd9+gWjYBMBbF5ghtgdSKfZTUo81plvFaC5FEBkfTLFPBV9YGHBzDL7z5nXUmbpI80tYOEXvwl\nefKX9zJAipN9W+jNC+79E2TWJgAFwfI82ZWYfKPvUuuOaopSipMPJuqlNvmpfXu1S+k7qvgzlvTu\nm7SSAPdlovlTeOzl8VXhJ38xP+ZWV13LUzK/AFMpcKpm8R16qX6Qfdsk/jpazLVYpsSlDjinRDW9\nvkxWzFjacfV0ZFgaUc4qy3eb1GE+D2sLpaOvYtDfYZ+rD1P4qoRS4DbGMmR2jnQoDiUaPD931cqY\nNa017nhoa+zn2EltZi6a2RIr56H8zmDt7vWyZIe2JvYhVwEMf+EnzuHYxKZ4YdOhWPP8tpjJTtqz\nOB5z+adnRves5+Krt65jWWYsFs1viBWLuuPhJzZjKHoMEDsrLjt3UczlfPPP/fEDHIPXHE+tfSmu\numBJnHP6/HicE4uCHbEJOhOIWTcCBycJh9fT1KsOAbRlkPJypB2unEZd1JjTvlqp0ijS457ulvjo\nR97LPhB0FfGnfKrizHcL2KTiKXMhTmnD8oE0k3a8T/KxHN3Uz/K6+sRjMdDHr+zOsFx05PCe1C7D\ndInNd6/lsoYI+WohqqG/xJU+zdMklzZ2ec7DEmhvqddHnEr9F8zANFJrU2zazSZFD0ZAAuRGxWaA\nYxPgspXJx7S2vpg9r4mdz22xc1df7N3FCVNsDOof7aCdlM12M7v6YwammGyvQwzmBwf648ABgCwb\nM8aIV1juMr6m0FRZkZZOOp3UpSml8SJOzrw0Lm2u8FuZpmbRxv1PKui75Lbwg2Dfurct46RHAs68\n4RnaVWA0gdTkvqv6JpNKY8LU6FwiyhiNsLrhKmfjjN6NQyZL2NKfcOu9onA9TQ7G49GcZZjw5p0R\nZ5QlL5XkMfNnm6tc8VeeqhgmlS0/12hSBaldC9/X+jD9JWAn39lYeS8dyEfSt4r6iDimymMdcE6V\nmnqd+azYvoDOKlD1tjzbVia1qspT1bRqz4Qx2BEMfnhM40Gn7E225dJLUVwKSwfnaTRtSIjOOmVa\nmki577H1McCuhsah3pyLt+ItNwID+NK2Js9dLSPxiWtWxLzpTfHN7z0Xf3nLMyyTd3EI5mC8Z9X0\n+MkfOzc+fNVxcefDz2IoGmklFTRA4tfjd92msZjdejD+yc9dFCcsQzG9A+sAfQ3xxHNbYzvL8eet\nXhRf/cbzsQtdJ3dIKmnVTih9LYAse1joP3VrRhYrcq1yVzGd9WExHT4snWXOww6ACZ5OqV6yvJzf\ncvAyfOWmLj2qErz21cEIXxYVkOZScwHqDPgMjB71KQ0bmg5FG2cJSKyBXqgJICsDuVIhnb+1AS+f\nX+0nqZ0eKmofRunMizVWZaxkbyLGKpRXdTw9naosaautjOZl8v+Pf+p0JhaN8V++/ERs2imgY6ii\nzTQBPGd19cX7Lj4lrrl0YZpQcibi8a97Obv1G995Nr736GaK2p2qLb/y96+MBZzE5vKoJ0v1I8l8\n+tld8RdffTC2HTSXHBSg/lwO7hWA8Jn8HVawUoIyQZ0oTXU3UdrqjVciqIp7lLgm+3zn3NtW/VcD\nghZMwJd9rNfyl+Apn+y/5D2dRBKsFr5OHsrBym8VIb2f7ORhwxAKXiq+/K31i0ZpJ191k3p8Faf3\n8S615q/k2NyZTyZrppKeqnzrsaRcC1K7VIka62SXmZr0An8KDgTXWh7JUQaeT2FCLY4E3kfGMymK\nKXJbB5xTpKJeTzaPxvJHhkuTEbUGWr4dnYknx3V0H0dvYkemd8w/Z4dmB8dZ5QV90ujHOB2lJRbO\nmx4v7emNnUggR9B/006pnZdBcvOCHal6Y+zan9nVGKtPnItZqT1x651r2b3fk8t1vfi/+8k9ce7z\n++Pc1bPjjCWL4r6XOFuYZU4ldp7G0Qso7eD872GW1d1Y1EFemjkBpw/QsH5rf5x9Uk/MntUZu3ey\n09ZAYgsImx3feGd9DFJ6MhPlDMgXBTzmZozMcgFFeYufVN9g8LeYWT47Wgju0qs0z+EsdfgcYFiK\nrdJIwKAfw0kfb6QSVURYn31KEJtgwiel2XboRR5ieprC0o//vCt/VSI8pvN9cX4RDpWNXCWu8rXE\nXNJ1UCz/8tv44EFomUmXefXrpHeZNz/63sHN+P0rA2x5x6PvGADdXJUWEPSbR8Dq1w1V6NQ19UZr\nB3CJdIYwSznGcaQlHtMvf5PvBFbjNMi8mS8BWZWXLAnPR3HjnwhjpIeVabL/wguCZU3bZB1n3oei\nq20MCaaSWS0+qM/m5AJt54aD8YkPrY4rzl/GyT598dij62PPvr447vilcebJs+OnPnUGOtMH44nn\n+5gUjsasDgZwTC7dcOMD0do1M047Y1lcfObCOLR7VfzPb6xBrdWSaqbMjFKTFlM+k994M16UydnG\nX3F+PbqPV35/WETvuIdU0RovlbQpfF9euQQtH0vZ8leoJ1/5XnckTV+JvsW3sRSwZhvwgbYBn5aj\nWX1R2varx2K44qrmmE/wwWHZyY+0C+Iv7dpYX0/MlZ+q3Rp75jyTyfZrGxvvF3yNX8accZdREKaK\navzD1LqpA86pVV+vnlt59tV9JMPWuvnX8lm+v544X19Mx6Qv+xBpNgp4aUxld3cDNsU0dufPnjEt\n1r+0OzhuPNqHOY+bZbfcgU4/MMgmoaZBBjPHHqQu8zhm0WMZn9t6MF7ai31DdxugI+aSnVbc16x9\nMS44Y3bMnNvFjnY6YeKy7zjruIWEHYwzj5sWKxfNiefW7cDIeS/5UeepCZCJVPXkGTGTXdoNOw/V\ndqYLlBgoiaDZZSTuExNwd2w5B24HGTt9O0uIhZTWjlXdVwefhujHLqy6dvvzSXux2c/jITWk8O8m\nj7SuwCEGIyylNkk/AHsCA8cZBoA8/0aCunxXDTgSSPDAxMABwvlBGdaQpXnj8hg7T7UWINDX8sA+\nTIS1AXJU/dO2nzuhCwoxIcPwk0uGpVS8Ja7GOMRHjy1txmZkk7YbUbFwqcwgLs0KpJWwpaWBtJ9q\nvTnQ+icHKtmwXn0WPOqkmaoe7CaXj+CmPLkqn/VR4k/pXPo109itbPL4VPnLPMrNahcLOg8g6XT3\nupJOosUuYlKEetG/PDsOvd10UwttDFpqELwWCpbc8fCKznKnyxt/xt/UPvgGeSag0GNlTSNribxp\nDksdas0beWCEJ4mZv0Z4Y9Wy2XHZGYs5+Wck/t8vfD9e3D6Yupjt9+6KT165KD79odPj2itOivWb\nn8SWcNncs3twKL5y74ssyffG8sd3xOd+5apYtWQG5uA6Yw9lV9+1geNn3VXdyCBvfpykFJNnh+e7\nPE1+N/l+vGgTN6/xecLjO+VugjcmaCUX6mr8mrzwSoSp/JYQr+e39CPGV8LKwz4V/q+l80rJHZlA\n5e9l2fADL+HNbKtvqMN9WWRHpFqjWaY9yW+VlyN8T+VHe7G6q1PgXU0Bh+WcmecME1IwCLW2NmCM\ntykOHuxlaRwj+IAI9SftZxiL2PiD1JMNDPZsBpvOOdJOSPcf9Cg+NgvV/OZAybEUh9A31HWgT2aH\nOMLg1knwT1x6UgwCTDn5j+0cLPtxnF7ztEYAAefoYrdz9+690dq0OGbM9KvJ2TkV8GM8ec9vZiSv\nx9KPnad/E+Bncu4EkQKZ977/XPRbt/NJsAnoEWQKFMclkYYCdED8xhr4yO8QOcEphHHXseDQTSDi\nzCbi4C5BH+fa5BCkbUUlH9rUEwgLo8YAl2mfEX0+dzZvQpWhMTfXCACLRNvNCAnIsixVHRjWeJR4\n8A6g2TlT4+NKV6wfN3mRP2w/ekCAqg8jABshZSNAV7CbgxeTnBItzJOgS/AlP/os7fzuErin5GjA\nHACa4JQL+ZInkgGTF6SZoJNB0Q0IgszkFQGbwy+TJMFdO6fr8DSYoJO4E8yWeippkjZ0LOCygNi8\nr07oykRN+K05JZaWuaRJpD6nGgB5hIaFe/xVAplGkeLUE2fQVprjxjueixe32tY4MhGf/Ugx73iQ\nTXbnDsaKubNiTldTbMUklFoGZVWHhfu2kWjvIT2iP0DbHSBeT/YdcUbJ5qTwkAdiE9gqcX1l9zYR\n4JUTeEd9kfN+kO7w+EnL5GSbt8mNR1crRrn4W3vxNqXzboimDjjfDbVcL+NrUgCoRyfFgCOaZJRq\nxj5cC8fsDSMdGQS8jAh0RBZ2Mv5n8B9Dh1B4oASmdwCTHNy3K4wS2PjEBhdFZ6OebNLWnX3gcL++\nGEKR0ilL/aubHokXdg9E1/SWeN97VsUpJy2KKy7pi79EF20U4HqIs5ydr/dgQknYYMzGnVkBKHiy\nTHEO3MeYk1yCxsrVAL2vUmLJd5d7O9AvbGzmmDulgnxr5sSXPNdcQIqUsCjP40cQigoDcigjTtU7\n340KUNl1nGAhKeQSLDbz+BPSeia3S/RuMDKcu541GF42hWiOhH8Yo3ajyag6fVSR0sn0pyg2aVyV\nwxh1whzDmS55I1+NGFsfSVmnagIub1tAgCvgMussASe5FGySuwJilVoan/VXSRJ9LqkUCagZUmru\npMW/Uv7MqF7z2XdFKlxy6q9xmE+lMup0KhUG8Db35/I6LxN0jo6we13AJVD1L4Ef+cl8Eo+oTVDq\nST1ZWvJTZc9I3qSzvkpEJa9pl5F0BBC+Kc2tPCN8Bv6PxPwZ3TGC+bF1L+5DbxMwTxSaKPRUoV1s\nitq2c3+cumJOdDRbB8Ns/huLuW2t8akrT4mO6V1x2kkLLEnc88gGNhiRgyE2F3GkoAmanwTohJOa\nb0MRiaXu6hSoU6CigG2v7uoUeBdTwGHFoYYRLX8ZdBx8kJ4NM852YNqoSRubKPdpE1JElAMhErWU\nwXiEH7tn9+/xGL6I+XMbgyCYOcKvBvURewotTjp+UX7fvmtvSrsMq77gmk3ooW0ERDQeiO2bH4jP\n/cP3xilLO2MGLXMvOp2eFw2eJQ1+xp15FegIIMrLyV/HvR0TN+bMApSMOqS7RJ6ndDCwNyBJkvYe\nZNUE4GuiQJ5yePON34mTT10VK1YuS9CZcBsaNDdgUxHarl+3Pp5b+yx6fHti9ty5seqUU2Px0vng\nIqSMLLePoftwx+33sPlqEDpjDAjJ8qrjV8SyZYsxr6PBIICXKhLE2Yg0sAkwePf3HohD+xD7AUTS\nWjRg8oTTToxlKxeMU5KcF5fS2RrVubh5TFhXFvO0z6osWgkkPKB0kzthp7TwSwE30IQ0kj4Zq/EJ\nPGvx5ju+C5QFgr7PCpeW/KUUstA1vfK9gMsSe4mnitMgUlHeoeyoErS6kYg3RdI5jWSquAzjn88O\nEbXl7nzn+7fLEVcmCQ84CeCxkTwK8FNyTD6LPUfMlfG6kZWCzq42TCMBlCVHywCSbcriigJk0sj8\nEGe021zHmCwO5tI4h1h0tsb7L1+NMXhWLZgQOvlYuKQz2p/aHaP98BP1nRNNojRd68Q+IF1Fktpj\n/VKnQJ0Cb54CdcD55mlXD/lOogADl9IkxxmhwRCgpm8A0ytd7dHKupunVTQCQl2SdTBqRRrXwvKf\ndgGVS+1nA8NLO9i8sHh2nI8Zo9sf2YRksy3akQqdfcKcOPu4OXHo0Eg8/vwWDMBj1iVPkTBFdNVY\nVm3izOiuFsAtA6nApBHTL5q0njm9O6V9vS4PJhhJ6JWUF14c8y4BBPnMrDp6Fyml2N2BX5uIKe1M\nKlIHlH8I3bt//+u/Fz//934iVq5YTlDDK6lsjN59/fHlP/1a/NWfXx/TOqchhW6JQ72HkNo1xU/9\n7c/GRz/1Xu4bMDXVF//6V34b/dCe6Jk5HdWIA+jwjcRP/eyPxyc/+3EwFJQn2gZOqBEANrIO/5v/\n6j8gzGuMWbNm8F3g0RA/9jMfjcUrOEoTMGzdKyVMXJaIpOQrMYlL2WTes8tz6R+dYG4IYzzCTVAQ\nk48sSwaw5riREL7Fr3qeFRj1q8CwOBAWks3EnQlWCZn5SPjOe4iWdNa3SMwYizM+AWOZUE18c+m9\nsdGNROYBcIYqgbvX07/FMu2Mk/BKNi2vy/+6BMnjhSjv3sxv0pD6r4FpS2MhjbmisfnOCQnZGETC\nexCJv/Y4O9ugtzSn/rT4oImlNuq0k2MyDyAB7QNE5pGnRPYSk8Hf+s83pqWJZfOxGPGxM+Lqi1bF\nM+t6477He7Nuk174NUtl8Z77TPvNFKwepk6BOgWORoE64DwaVerv3l0UYPBO3UEHPsc8DFH39o/F\nrgOHADUdLM+NxX4HdQY9B30xxPsvOSV6tePH4DvI5qGnH1sTt9/3XHzq2tPjZ69bHScsnBabt/fH\nPMDLpefMjy6kLF+/dV3s7nP5DiCCFE6pzGmr2TS0aIBz7JvigtXLAKlIPV/oiz6MVTeyVjh7emcM\nId7Zjp1BBW+OiAq5KmiSA2UO0VOnylJIV4NEeQoW5RHcqM/p2dtKuoY4eml02GdkdtDeIwSVcn3n\nxu/G//eFL8XlV10YP/nTPxXd07pj27Yt8du/9bvxX37v83Hq6uVx/CkriQs1h0P98dkf/zFA6Adj\n546d8Vv/7j/GH/63L8QnPnMdGEzJqvoP6mkq74zo6++PKy+/Kn7u7/4tpKJIOtHb7ZkzPetkjLw0\noztp3Q+7sQn82ATI1eD/KJJoJXPNGBzXeHOpDkoz6PI1kk9eDSvt5l8zANnyKJkbRoTe1qZuplJQ\ngI5iXnNOnEOIeTWKIK3cRFMtdUsD9k2RZ+AZfNmYwFguYCICX6nbWkCmRIWZxv/047vyW45iVNI5\nGK3tB/J9gk6PfURyahxpMSDrqYRTClju0vtb/km8Sfzj1gdoh24S0j6moC/BOCC9QVq0QBPos5uJ\nRCv3x2HD9t4n9gNCPZfbSRttCKXohfNmxp6Dfai4EAPEQigafUwKt+8bjV4mgDv27otTH1sX173v\nbI5L7ab8++A6dF1RHzAeeUGauWlPWXPd1SlQp8DbR4E64Hz7aFmPaYpSICUr5p2B3YGWoTZ2sbS6\nYde+OP/kpbGADTtbGegGAD+MZTkQffbDp7OEp9QlAKYRv795c3z9jvUMlp1xJcbaP3T5KgYtwUnE\njp2D8ZW/WhO3PbgRvbPmaEVStI+d60KAT129ioERuRR/m/cPxF9+/8W4/o41oTml2UhxTlzWyYaj\nkXh+yx4ABfpsNdDrwJ8xePO2wgDjeztdZvDwCBNpWPrKCYwc3r0KNAgDPVLXUQkgkk3lyM1I2v7o\n819imX15/Mtf/yfRxvqo9bWIk5/+6a/8g/jlf/jrccNXb4u/f9LxJT7imDGzJxax+3/x4rlx1lmr\n4y///Ouc9gSA0ZAquomlFgo1rZCu7vZYtmIBp9W4pUReQNoNcP3SH/6vmN41IzZt2hJrnlsbP/3z\nn2USMS1uuuGWePaZdQk+zzrn9PhbP/Op6JjRxlL/vviP/+7zcc01H4j77r831jzzXKw6cVX8xE9/\nOu6+58645Zbbor29Iz700Q/E5VdeSpEFTS3xzFPPxl98+euxZeu2mDGjJz7I94suOZdl4pHYtmVn\n/OWffSOeeOzJmIbk9pqPXhHv++CVtdovYHOCF6BnigkhRUomLSOiwZSAFlAlM1n+ppbelA4a0eAh\nrCHgr7QJaYRUM8M7VFg/k+uNx7fiMn9u1ClxWpfK7z0LW3uzNCZycjA6QOMDeRpRczyxpjc+cPFw\nXHrx8XHfU1vjhS39tMEGjLuPxSevPonjT5vjjkc3AEyZwLgRjJIMt6C+0qROLYbjkX53zZht1DGY\n+tQAXB6kxGiWVcZTN7oON99K1dbD1ilwNArUAefRqFJ/966igONeGWDUGSuD8ADSoseePxgXrcZ0\n0cmL4+kNT8QA0pQ/+epT8b++BQBiPXgMKZmbdkaRcO7aO8qu1864/p7n4pbHXohZPR3R1dXB0u6e\n6D3YBIAdRacMAMPANwwS/cZdz8edjz8Pxm1BxuYmmaE4AOrc24v9zcFWQGnE6Su686Sjh5/dE/sH\ngAAsHxYJDIOyg2SCMavKAVtAcQy6BBVV3synf5OdIKbAvvKNAT+BjX6sC53vmtmxvx89151x7XXv\ni3aMgo9A+xJ2NFaesDSWLlkSmzdsQ3Ko//wUu3btjg0vbowdO3bEffc8EGees5pNSkiN3fSVnpBu\nuVyuiQGCjSEZ691zEPCB/UeAbmc3m71499C9j8fap9bEwoULYvlJS9EN7Y1v3fCtOLD7UFx26YWx\nceO2+LP/cX10drbHj//cJ2Kgfyi+9+274oG7H4tlxy9gmX5mfOMrN8Tdd95N3COx+oxT42lA6H/6\nf/4wlixaHqtWrYg1T66NX/vnn4t58+bHRRdfgI3JJ+I3//X/Ff/23/5qXHzJ+fH5//Qn8chDT8Vn\nPvs3Ytv2rfH000/F1ddcAV8A1ZQAI51TIlpoBqjMm4re0lkQNQk0wj/qLHpkXyMb21rbDxKEY1V7\nAWHuTUoKER4/hTiG595E3hZHPOTBiZmmrQSEim/ziEvKMn16W3z8I6cjrdRfU/QPNMVNNz8aTzy5\nI84/c0H845+5NNas2xsHUepctWxmLOWUrg3be+OWO9eh49kS7WTd0vdMa4uf/PCZlLyT+psRy1fO\niHUv9cWzG3bTFgHqjeqIuqkKz/BrWhawfLnF3Zu6q1OgToG3gwJ1wPl2ULEeR1LA/touvoIP2dvn\nm/xwDP+Qc0WHOE2oqN8m/njoiRej95qTMeg+K/76uw54LMntZ7mXMbcJSYnSkyakbi2sg4txRlgd\nHUXsueNAS2zfy7cGNhJBBBeLOdSaOwdUB7Tm2NeHlLMPCR2YyUY4yiA+ypKsO281WO1pR6cePx9D\n103x4GMvRj8DLgf01WgrAJDOFaW9P1adOZuUO8FLQfiF5oLm/G4duKRZPVsel1vVq/UfagXbXvJV\nLFg4n/cstUN3d1ePjfXHtG5OdWK7+759+2OEyhjNdWesAPzlX8dN3/5O7EbiONQ3HFd/4H0AUtIR\n0eusAEEGYEzB613fuyc2b9xIBbNBqb0lfubvfjaWLV9O/bHhpKsrfvU3/kWcetaqGGkZihNOXRFz\n58yNTuw5HsT4+OaNm+KJR9bkLuqsZ8Kcc/bq+Be/+Q9YHm+NX/77vxFPPvF0/Jt//2tx2RWXxHdv\nvT1+89c/Fy+u2xCrTloZN37r5ti1c3f8tz/6g5gxqwPg99H4sY//TNx6y+1xySUXxrNrn4+lS5fG\nZ378kyzVj3JqDnY/4ZlR+IWsJ+gs0sLCH1k+fvwGh9VqQfoKHHEJJP0qB7I834xOJ8vSecgBu7cz\nHHyX9ZVS6SPq0jiOcIaZVNtHfD3ysZZPeN/6zJYCvw+jP401smhrbY5zTluSMlkjPdQ3Et/6Xnd8\n4WuPxJa9p8bl5y6N88+eS6ui7fSNxT2P7Y6v3vxkbN5LTOwckgd2YK9z3oKWuOySU3MloRcLSE8+\nsztu/N76eGF7H81e6bkg141elpkSqN+ahXhjpTmydPXnOgXqFDicAnXAeTg96k9viAL0ygAEx63s\nthEROPRpeiX1wABW44Pa2yYVeUMZfB2ec1jFnyNMATsaeNckzr4DDXHjnRvj9JMXxozZS2L3VgAk\nZcNqDwJGludcksQfQzV7FwCgiIUaAEGM/2Vne45X7IJl9CrLdpwDrSRKGpHaGEqc7qgdFeCqfwfw\nUXuOL5hRao3OuUvj3jV74lGO4mtSV40BNEGmo2ERx+D3WHYQQBCpk08EmmbdXcgO7gm+/SgYEmxa\neqgj/0iGrAfDlF3fLQAQ3w9hqmoU2jVCB0+JUR9xcJANXOhEdgIK1QvN3e/4fd+1V8dVH7g07Zne\n9LXb4k/+6xdjxcnL4ooPXpyStebkVdJLDo6YOX9urD7vVLLCEjcbULrZcNRIJakfecGF58eJq1fG\nEMvtJBwzumbHrd++M9Y/90LsQZK6edOmWHniSvJGniirlgUuvfwilnDdGt0Yi5fPjz0798QZ565i\nF/UQ4HFxtCJdGziEfi7/nnjiGfRWR+Pf/wabl5oBQZDvUO/B2LBxA3qgI/HBa6+JP/pvX4y//VN/\nJz79Yx+LS957YeZRUskXxuG18DGXcWf5JlzhPnnJ95V/EpN/AX5NKj6qz5jOIUKAKjPXXN5OPFut\nxVUToOqpev8KV20dEa+KC260St6GB/rZuPX5P70//hDbpr7TXJUTA3M82DSHENPiy99+Lq6/4dHo\nxt5mI2euH2Rj0CA2OYcaOgGbnYDmQZ6b4td+5wbCYchfM1jMInP3O9eBmI2erhYonHBQFuLP3MOv\nRXfVnFnuuqtToE6Bt4sC9iZ1V6fAm6cAAwXDe4IhxH45LLlr1kFEAOFgNJad+fio9ObT+kGEzGw5\nBJtREmDA9fg/Tx1xMP46kpDbHtgSew6gy2aR9OcXNrfo31Ns3Gmu05S4nxvQOUsrm+kV+kyMzYxt\nE5KmBLYGFLgq5XHcI0wj9j3dIf/Fv34Eug6yCUJzQYJg5C+eYmNGUuJUAYaSJ6M6tpwFF14L5OUS\n8glty+YMypF0kd5FW3KME3hyx3UaViyfGwCbgrcGNoUsXDgH2jTE2rXrAA9tAPxeQD+xAxw2bH4p\ndu5mqfWEc5HUGTegELd0+cI45+JTSb05Tlp+Uqx7an3cf/sDcdkHLiQfxkt+UGXQ2LzZPO2sk+Ln\nf+knyaZ2VV3ubYo+9HcLWAYqs/SsvzHmHr/77/4zy/QPxQWXnRdzZi/goIB1xZ8VWQoH8EF30HgM\nhEqF9TZCHENs1kl46HoyPoTgQyDMrvbOOP/c8/LUI01jnnneGXHcicsx5zkWn/rsJ2LpsqXxleu/\nGb/zHz4f9z7wUPzrz/0z8gpfyA9e+Ve4wd+Ju3JP+nxPtJxXn/GTkwLooHmhgQ50G7Evq4h/3Bmn\nvFa9myhferF++Ff4l/h4zolmSX48lpffmH7JWYmhtB+3cPU1dRMjR1ClM6KqZCVSjmbAT0v0s7kP\nATdtjgkZIYwtT3NSakv0vWOzM6zR5LGwxgMPGV/2W9nO9WqZdBN32RjLy/pvnQJ1CrwNFKBV1l2d\nAm+WAg5ySAiUCuTwqaQC8JX9uUBjqrkymGX++RkBAPQjOetjk0lKO1LSUpUJvzXvE29e/s5vh3mb\n/DD5Xl+O2PwNI83S+PnufZwIo7QswS0DpNekKiMp3hniq6SP8WvJb5YvcyrAdgkbvhFzIP3TRFGz\nYFpzUUod+d+AekIzgCyBNrux27vb4uTVJ8Q9338g1q3dHCesWkyU6GACPm/65m0cA7o73nPxmexi\nbolBpOuyYmMARIb5A1hK7qHRfgAp9AXiNWHCyGNH3X3uKUV+F9Z47Ogwgd1X7nQqJwnSm5lDTqaI\na+PmLWwGejA+9vGPxi/8w59j+XsgXnx2I/FzrKSiSSPjTylsSvCU5tbejQMZZyLGC1BsAowuWDQ/\ntjy7JT756Y9i9wfCUOZyxjj7qMf6oq2nOa74wMVxyeUXxu/89h/EzTffGrux6zpzziwSKxOZ8fbH\nG0tfMlIBRUHjxIRHE0+C1ApMjgx1xUBfKxJkQD6qIiRuJCWK5DXbtBmu+I664rZMLrn6uvYpcXQG\nfrUfCVKLvrorr3iaBPzy2xE/6W+iBVQ50tdhsSbRS9jJfiZiO8w3gcczMOGlflenQJ0CbwsF6oDz\nbSHjuzWSsWhpRRW/i12lHgfnAA9oUF7jxgiHjKN38scyvQowGRUgoNelyW7H7bLk94PLdx7jSPSe\nmuOSsFeJ14gZHSVGecqOFJ1M0GN+bCSDKUES2FQTEQrp6TtZFpZJAdQyTpEo6ke/pWD33/MoS8xK\nzuAzdoBc/b5L4hf+wU/Hb/7q/x2/9Av/OD788Q/GjOnT46EHH417774/rnjfhXHl+y8lbuMRsUY8\ncO+jOXHo7TsYN99wI5OHvrj2w1cXegLssYhKcpxhoxKuuAywmaCNOKS5cNHfnAtklGWDUQ/mmNmv\n2m8AAEAASURBVNrQy7zz+3exiWhJPPTA4/G92++Iiy6/gHSJSLDGZRgJZxo1V1yplDrBGt/MnO0D\nP9qRVEL5kY99OO697f74Z//8X6EKcCXL5c3x8IMPxznvOYOd6hfGP/vHvxxXXX5lzJ41B6P369mI\nNCu6ewSb8iqgXXNNPL3c+ZbMj7fGknaRbMJTSouHOqKfHeojQ5w6JEhn000yfkZWhTWfgjzDS5Xi\nlPEncM06LgyaFhWgXd3VKVCnQJ0CFQXqgLOiRP36pijQyOaKNkDnKDtFW1o8bUWY6cisNISBSClR\nLrG9qeh/JIEcMpUquuvXU4bUn3MXcAWEfhCZElTliUam7aAuwgG4lCMeGfq9Jx/mQRXOBA/HPPgk\noylFM8NmVuCiPqQ84iuUEYYGCrCzzE5alDRismj12adgO3NXfPuG7+qTzSzNsYpTf8694Jz4zd/6\n1fjzL18f373pdmgyGjPm9SBl/Jm47mPXoO6pvA6zR5ybvfrsk2LDhnWxYRNL3bw/4ZSl8Y/+6f/O\nTvUzAX6CP8GT0lV0/ODdZcctiblzZye/SmP5t9LHXbh4PrvHZyMVLZLQ2XPnxM/9nZ+KL3/x+vjv\n//VP2Pm8LK77xAdzAubSeUtbSyxduTimz+YEH3jH+pwzl53Uy5YkOHQq1tLeHEuWLUJP1FN+RsjX\naWxK+qfxhf/+p+hq/mmqZsyZNz2u/dDV0dHSFosWLIw/+59fQZKIGaAZM+Mf/fLfIz34gnyOoL9a\njgS13cEmSbUJYFto78sKBAL00ye2ZAc7Mfwu2NRGrGerAzahzWRXxSleFoRXKZSrX6GWNBPo48Tu\ndVenQJ0CdQpMpkADHaG9xVHd6OALMbLzYZac9o3PZo/q8ciXxDiEnsydW56Nm9Y+xskqdK/04M7u\nJ+bFRwaaeLZTs9sSpzSPTIvzlv0iJxyvzG4uT4+gc/OYQTPu0s0+TnDZsbs374tZG7s7OlQjquni\npdSDWDXQvGT+HE6PccFMNfuSDpc3VkYDvMud9NdA+Ve+fmtseokNGzNWMvQxWKUxaqQtqa+IlBCc\nlKvBh49hxwj1LIUuR8u8CgZ8Lld41k80EyVeP2iX54KTToLOTMzE+c8ybRpJz3fmz7ZUrilx+iHk\n7U2VHV3AXF5FJ9JTd9SNHUPaJ3huRAdz99Y7OBHoupg5Y3+2TRGeE5RGN5zZdvmzz7AuRtngMSo4\nhCSNI83oGjZiIH4opvWwMatpgM1Z+rKOOLmJht00hk6i6Sf6QcOWZWmXr0cAgMP2DeAmBckaaxfE\njQ42YyEAWwBs6Blt1GySZz2xYIzXBnZOu+FrpMXNK/rnGSngECoX/YN9eeqRwHIEKR+qzDgMlQ/2\n5wYzF+1TWq40lw0xDdhXtedpRpI4TP6bSB/1VUKwlI0Xl6j3HzhIHKPR1YMeYw0jmuVDGILVoPz0\nnh46R2iSKi1JneTRon5RpLJ2oFJDP15NQb7xn3ELLIeHpkX//h4kyaSj0XffEe4Q+fit//iFmLtk\nNfntSiDphjgJRjHxa1ze8Ed0eW48r0bUx3UnXLZ9PtcdFGASAG0akhGlPTw41Bc7Nz8W//IXPxPT\nnLD7ljYsRV/LGUOZKqH3y5G4d637XAw0785alY/zawX8aXzdnLz001d8MBY3d+ZO/cnxl7gmv5la\n9+a/1kHTb5T2P4YQxOJLibH+J2No8x3giP5aG3id5cuIbTvGz5X23tA9L5oXfhQ6c3YxNfV6sMzr\nTO1H5q3QryQ/PMaGyxmro6n9ePo/BUW1MkqDt8nVJZxvEyHfrdG0cJLK8SuXxroXH4nu6Wjv5zI0\noKJ0oZCFndcs9008H2uUmtyaSi/jG++yQ8nPdGA/JEA3ASqlk4mX/KkLWHLl+5LP8uvzsewcSkV8\npRxm3R3p7g4+tG9HzJ7RzU5tOnB0K8ewjak3N5lpB7MAGgFnKWluvAF1aQZIsNWC1LOlA0DZCNjM\nbTdlGOYjqqD6AzWRTuE9gZQSdwalREwM7khU0/keYKtR8KYESgKA2pJyxkN+MaMETMg/l93TqgB5\nbcIsURd2Pdk/zR8mfQjvEnnqqLI5Xf1PAuGfNNj5TlMgbmlibIDnjLdAQPVR3RQFpGPyhqSR+PU7\nXs88t2OYXr4sG9WgUQIL88ZHypXpZaHwxavijKE8mGpmiEFzaKCHHfJdSPAFm8XYe+ZEZJup1vzW\nwGWmYf74npNJdVUpV55axL1tpJk/pa25QWciAyUb9d86BeoUeFdToA4439XV/9YLr2WTU08+MR54\n8Ck2HOyO5i43G3B6iIONA3tNF5ER6q0n9kOJoeTT4Xkix4c//VCykYlM5KCkefhz0rgGJAqc+OHl\n7PWlRH4TECm38a8ALVcbBof2wS+74pwzVkYby8V5qpDmjywiYQowcqIikGEjjlF5bzzwliByDKCp\nNFQ8VCjjex0SRe5y53YCUUGfXZ0TIR0zdwASEDOpl1IMAGcjALUpeovsgiCjAksleiQuBHW+r5RW\nUGeauemJt+nwN5wTq7aUhCuRHkvbjuRZyRZ5dsd75l8dVstAvJaoAT3S4go0TokqL7LMtqNarvWT\nkyBv0mUp+VzRN6Pli5v4BIuAbfLqTn9LYGj/lNyqq9nfOx3Q2VnTuwYMU/pMK3Ux8Veja0mzUA7o\nDaAusTRBn5xKjiClZeJZ7IFCpSSJadVdnQJ1CtQpMEGBOuCcoEX97g1SwCGliQGxAz2y888/NW66\n9YHobkUs3zarNkS6DMzolGPV1BiAqlwWKFAIUobaN0ict827gKsWWQ70U4mcZliwZ/5FIQIawRDL\n0L27Y8Hcnrjg7DMw/4h8kOVv1rIBSB51aBCkZBRcv23tWAF3N3oSQtDqn0QB7uQ7YZvvhFU10MST\nfhIsjbVh9B09SaSW+gIpJnBUQKc0UeipKZ32lt3R0zEQLUjtjKlIVPVOLBbFMnAjLHT53tB52hNg\ntg+zPLv6WMJHB9LcuMlsBABquGaX9bkZ4ZSpBJMJEDN7xGC8JY2JvAHrCGdKVr4pVa6wQnljHv0r\ntPULINPlc4B4SytmvJKGhswceQMoJI+1DULDA7TVBtQRtL9JNApjS/z6nEjDp+JQQ0KFpg0pp4cj\npFkvpMQuoXs2vMuO1qH5KYvEVbj6tU6BOgXqFFAUUHd1CrwFCmiMnFXFOOv0E2P7zn3xwBObo2c2\nRzN2sFSnJCgHa0cygcCbcNVoOzESHh5J9b16+0r+qu9HXKvghwUDfDjMK+GZcD4c9mLi0w/sztxV\nOTw8kQI0Dn93bD7VyiAxRVRIAnsP7WZVeiDee9X7YjpLxAP96CX2o5MozTl+UMDC6dn8ATIbMQeE\nTmUjUjeXaQVu4zQRpFonedY3YItPBZRqxsq6kuf4w8RP/4HOjDtBJPGaGTco5SYlgFIzu8m7p+2N\nubNGonW0Dx1QpaHmowbWjJtnHpMvXPbXBqvZGSMfO4Zmx5b9bUC8GSk9FcENA74EmCOIULXaMNwI\nSPNfpZcKGCcX5EU90SLNLVJZ0zL//plkddWvruSpxGUek1t5KyjvZ8MS2qA2SgCu+qwlHunDbnwk\nmgO9PehudvFamA6dBbWUt6Rp9KQ3zvxVmkLzYY5b7YizVk6nPHgBeJIi7bwlHntmW6zfRj0KPBO9\nmmwVVs88v6KrtTXSLCWtefRBMe9hrvLB+4zfuKt3eBwP43cD+r1caxfeWG9FnSLVKzJ8qWm9j2eb\nYFUYY6jcxPeqjyDf6dGslJvcKFkLkPVXeagiqV/rFHgXUqAOOKdApZeFNjPKIHKEyz71iHc/zMdG\nltUUMqnxdsl5qwEPw5xv/EIM98+Ijm5MtnCSSgGbdvx2xmVwPGJo4X2tx6465togMF6W/OxP1clX\nQaRJoYIxV9GMh3uDN5mMBUp0xGV8ECxpvMHo3qL3SfmolTHz5325eYvxv/3BhYzu6C97EZVzWdMg\nTU32DB+MQ3tfis7Wkbjs8vNjyeweOHoQQAaP5K4y/OXGFfIlCOK+AWTT0MB5ovBZBbxKO5A2Lht7\nlRjwQdYbt9z71vf+K3ccLZoAlSdNFJkngV8CPWNSytkSHegfKuH0+5E8WuIsqaWupCJS+EOg1+LS\nOzt/RhpZogZIGm8udfNdeanQMpC06pyElbKYt5J335V7fSQne5OulKB6mvzOCV2hgL/qjXI+agqV\nBeElRkOTNjqbuYyOzuYodkkbxuks+NKn7chc+cc/8p+S3aQxnxK8D8d7zlwYH7t4ZZ5f3k+8Ka8G\nu+3ePxAvbGWzlTsE0YVN8EvSZWIEnT2GNOsFyO+JWUh/R0hXdYVRNnAOK42NNiQgLP8zGUkQzBuP\nmWzKsHARWXRTYgqXkeKqdqF02vyWXVfSA9UAgyG1FeRbTkumjqqTC981Mbm47tJlsWhRZ3z564/F\noX7oRt2MpNoFE2jypF6qqgOSxoMgGo0X6wPq55r/ZnXV0TP2LPYmTZdBA+k/1NCLdJoU4em0LJGq\nFXzzMIO6q1PgXU6BOuCcUgzg4EEPeJib/Oz3H7JT6sSOYweIWT0d8Ynrroz7Hnwibrvzodh1YHd0\n9syN5paOaG1FT88dE3bmZLEMKJPzSt79wPd0OQimT1+Wdw6c6af2mK8nSWYqf7U09FuFrIV42cXo\njlX3anl7tW8/qvKYpwSa8IKDvuak5I3hof4Y7N3LnpneOG7pvLj84nNj3uxupHCMy9jZLBMSYQHQ\nwSX4dMYGEBBEgTCK5MjarL3Xz7hfH/CXzvDGVdW8Yf2gxNK0vK+lxRUcWzYYATAEWGAf35Yl8+Sj\nDGCg4mpRVNJOXwpPi0EwobXACsBlfBmCd0yiXPrPML4bjzKhUvoq7yY9l7f5O+590jtvs4QCccFh\nfrNc6mvqBDjmzHdIXgeRJB9qpz6Q9CbYrHJXuzrRy4D+GJ/tyqyW2Lwz/03WBVH+jz+/M9ZuwzII\ny+mNw02xc7+TDOCi+rYus6dVgiGsAUxDXaIpWpr6WY7n9CffI/kdZof+KJNRjemPBDuIGzFlBThr\nHuaeOEZBl6NNbGYy1WY2lI0cInVBI0dS2g/AYA1YKmgh3w1jTEhIW5WJYVQEhgD/zU5gqM8xAGbj\nKGGRvI6MtnNti7axg3H28TPi+ON64pvf7I0B8qeUNrDH2qAObwPWDOBVTb6NYqN0tLEnyz6CCa4G\nrBe0KKkeBvgCepsxSTCADusIZWlistKOhLmJ08HGGjvgf47OhH9HNJmgq0hZnuq/dQq86yhQB5xT\noMrLkGBG7bEmnkrWj/aufPlh/NIHM0AwOJEtd+I2cM7cuWeuipUrlsZTz6yLF17cErv37op9Q5yW\nMkRHjb+q3xV0Hu6qL9Xbyd/5lo9cK1Cag6kDYfE3EXriroqpfv3BU6BINUs1WUWefd6CVKi7oy2W\nLuqO1aeeHcctX8wmIUAEQGDQ3cxUnYPyBFf84PP5zkkB4gGWKgcUhJ6ATHf4exWEAy6H0NUc7Pfq\nDnelrIIzXAJ2wZDtR2D62s45o3OEbfuGY/0uduWzIQmBNfXZEgtnt8dHrj4rtmzeHCsXz4n5Czri\n23dsiifXbY/zz14Q55y0LDo7G2LvobG484F18cDTLwHWGmP5nO744FWnxQtPvxCL5s2MlcfNij4M\nFtxy26Oxv78hLrninFhGXLu29MY3v/d0PLe3FzDXFjNaGuPCk+fEmaefHrNJe/u2wbj7iY3x8LM7\nyGRTzGrviEsuXhynnTiHI0PHYt36vXHHHc/Eey89P05YND26sb360x+/OHZylOeNtz8Xm3cMxLzp\nw+gVL49TT5wXnXzftXMwbrtrTazdNBiHALBnn7IgrjxzSZ50dfZZy9P26Je+/kRs2bU7Vp8wJy45\n+/RYMKs9dh8cjAcefTEefHJz7AHMqisrleuuToF3MwXqgHNK1L5dFUPE0XAUnxxoyqBR/f5wCmWq\n/rnYWeSMSACQOLQitZg/sz3mXbQ6LjwHA96790UvZ4P3oauncfMCTN5I90sq495drpwAmaWk5WOV\nnx9O6eupHJ0Cgh6WHrE52dmBZBsp0YyebkwHtSHRtN7KArOAtDKd4zJlkadZg3X3+ilQ+F7hZLqc\nwDkDFGzSNgGbI4OAzd6uPK6ysQHppicICfBrfo7eqWTwo/4gKBTGIlkcjC7shw4hWWwCbA6zzDyr\nazguPK0rujC4L5bt492y+Ri8n7E0Pn7NiujdM4Ad0eFYsaQzVq9YHb/3xfvj4Wf2xZKujrjsxOlx\n+QmnYLC/ifPRR6OnqzlOW3hWHGJdu20Ger19o3Hu3K6Y19MYv/1n90cvy+LXXrIsPvXeE2NwYCw2\nbt0VF54+K8477dT4nT++M9ZtOBAfuPCk+OQ1S2PT5kNxEHunF52zIF5ct470u2JaR2O0ISVdtYLT\nmg6QXvtw/P/svQmYXVd157vuULPmWZYty/Igz2BsbAhxCJAESBhNeIQACQlJOnnJ+9IvL0Onu19e\nOhPpDkmn03zdvEBeuqGTkJCB0QyxIWDAYDA2lkdZkjVas1TzeO+t9/+tdfa9t0pVqipZKldJZ1ed\ne87ZZ49rr732f689jbT12M/c9SK79pr11terhVRSEl9/wxJ70dbb7M/+/rv2rV2nbPO6sr3i5hX2\nsmuX2agI3ye5tlpzZrdeu8F+4q6bRM6iHTo8YNddJn9X32x/KWLdvf14o488JVVzy5wCFwcFcsC5\nKMqZhiUal2Y9BLqJ59vUT+CRdhMgqVlSErqkTOBC6hCmcF62URtLSxCzlyRAAwNIdr/x2vQb+WxY\npNY0s6F1dSsG+CYarBuuCafxNtFl/nY+KMC2OEnbjPbaj+kUwGSgueALZiiS6B4xvM3Z8BWpyKfm\ng/ORwgsrzKgp8DhP1AY9qzPmWk/NV61UNNw7qM3ltQUSGjYWBgE26SA2hsrxFyHpYWYjp/h4/Suu\nte/tHxPoalFnsmz33n9AwWiImbovjd6f//3D9o3H92kqY5c0fivswIf77NEn96p3Ombff8tme887\nXmzXbl1nD+7o03C2ZIOSptmP9oH/9Q3bs+ug/dTbXmLfe/Pl1nN0yP70A9+07hM99mvvuM1u3LrW\n1re1W78227/rFdfZyZPd9p8/dJ89K43irdvW2C+98/vsld97pT37yYds85Vddqpv1D74t/fbTi1o\n6ly12vqHqvbVx/7ZfuVdt0uLud7+7Z/cbYeGlosiVXvDnRvtxms22IOPHrH//tGHbVTTQV754svs\nZ99yo73mVVfZd/bcq86UtoASbx/uH7H3feQ+O96jZW4aUv+3b36l9MZF+73/94u2V1MNLl1TsN/+\n16+zV9651f7l8R4bHpsDjWcuhdxFToFFSYEccC6wYkvDks2NAM0D4srP12buUkJtrtFIGZh/cEWa\nPClZejiJxe34AXCgiVTrBEYsShtyGv4TSJ1o8DjJkC3mjXH3wHVXAwVFatKiacTWNWXjLNqAHvqP\n5hQX8oS/3MwLBQK6UO4BPMWqjn980p9SAG9TKvxRUOPSXoVmfF6SdwFGkrqc0DMZjSD42ehFnY2u\neZCVpSJ8q0AmFQU3+NFV91J/SAGc8c7qdI6wXLNhjS0fY/Mqs1PDGtX4ruIojmrRTNEe3b7Xvvb4\nURsYW+lTSff1H7exJUW7ZvMyLSTUFIvlEf0yDa8XtQsBWzlRTe//zk77zs4+pX+lPfZMn730pnH7\nl/t2aloO8yortvNAn1151Vpr6yjb8iUrbIk2KTg1VrMX3SSgKCCtE0Llt2orNZecaRpHdfrcbVdf\nYm9/w4vs819/zB7b1y/w3SFxohX6OmyAZUojmida1YKq1mK/bdm0WlMPRu2rX39aJ6i12aBk1hc0\nLP7WH7rWNi9vsXUSNu2aTzCmfH/2X3bbnqPaDWG809avbbEVyzUvVWm57qrltu2qldpZVSdSacrq\nCh1xurGjxfaN5ouGzshY+ceLggI54FyQxawmWeCpPvTMEKVAVn9/r3V0tGoOnJrpDOTVk0+78TyA\nK0AdACKBvLhnqQJ06rEoxEnyACBnNHVUSZOozOBeF3TgEzpNp4sak+HhMW0cPmjLdVKNn3qCu6bA\n8RrHvDVZ5o/nkAJZ+ZwWokAkwF+GsvdVyvGqAkzlGBb8cjUVu95yMzsKwO1QGAPU5130Vd1gfuPQ\nkLTHYxLvAkR1d/T85IbaGpRPBaPXKAkezmjwAcj8q0980/YcACjqeFFpNLuHKnbt5cwPHbeeQS0S\nG0OTqtXcGrrftnmpvet1N9omzeMd0yIndrQIFpEcY1W9ywetaxdAq9WWih+Y38t2SzrKU8Pl2qhK\noQ7ZoDqwEoNS4NZs6epOB74bLllhr113iy/8Kum7grAjAsCD0uZ+/isH7LLVazV8v0ra1JfbgzuP\n2kc/td2ePR5adzkVryo3WhXfojA5LnRIwLB/UHsK6AjSkuYgV9SpFQbV1BBtNaVVbmyPL1wpN+os\na1EQ2vklAtOcELVEQPiHf+g2Lw5ozFzXY91VzVMlptzkFMgpkAPOBccDzWAzElfVik3mQQKwWtsQ\n6loR6hP+aTiSaW48kt183SMdzSffEHM0h4heNF40Rc3pnS5tKR8NtwASVrb6LjRqVGgAjp/ss1Yt\nPlnCogl5oQFLPuIezep0seT2z5UCidpnCoeypBy480tBJffyP5sgkvP8Pg0Fgsb+EU2htmaqafP5\nmuYsjrPyWjRmlCGIDcG5mvzwyc3sCoN5jeDWbi3qOdIvAKY5nJwSxTnwLQJ5rDQvqk62KG7AZVEr\nwt/y2tvtCs3b/B8ff8S+/dhuW9M6Yu/99R+Tew1CVzllCfmgFeqq2y1sIyQNOQMixONHlHpPVeFJ\n7kXKS9bT0yuNqtmXv/G0/dU/brdhzs+WvrwqOTHKXXTo1XkB/+lDX7Wr1y6xu159rb34pvV25Mjl\n9teffUwrybVRlMLv0BC/ZqHaqO79A/3WeckG69B8YxZftWpIvUtTQbo6CtaneaV9I4LQAvS+64BA\nMYcMsJp+sE/TiYSOdz192N7751/WIQACpcp7WWkoaGX8WFFn0fvIVEbq/JZT4CKlQA44F2DBp2F1\ntHlsxXGqu9cGpdFjzhvbrcSApQSyaywmZ6Deok/+cF7e1UY04YbUaMWddq6RmgQ8zpAMd5zCSIBR\nMEUBVTVkXippvtjgiJ3oHbYRnXjCcXrQA/DtG8yLHvWYJ6TrDHHmn86SAo5iZvTLdjQNo0JpMITK\nrcE7TdYN5/nTzBSAiAI+qiG60D1qBTpaQ8CZ5EcMejeXFTVkMrUbdU4fz2jYT15Yyoeu2buUTiZ1\nUJsjqY6yS4XC0neODvVt0BTViqX60fu+fUfkuc1ueeEN4cZPjxoWiFumdJIm9KXkATBHDmTjMk5h\nehLDDjXn0WPDkgVVu+Gay+yaLSdsxzFtu6XErVy6TEP5RevRnM4btq6xQ0cP2YG+brv/kV12reZ4\ntrd3esd1GA2maHfdppVWOTRix0dabd+zPfY9N2yyV2if0X0nHoaS9rKbrtDpUyV7+GlpWEc9RfT3\nBWwZkFd+NYf1mBZDdfdUbcvmtXb9Nu3OcUhH/Ioua7qWCnS22e6j6F1zk1Mgp8BFCDiRXC69kIsL\nxER6UkNAQyH5LLBp1ts7YH0D2pdOGw3H4goJZn331ro59c/nuCRtRbNRWrCKi8FxgUJ/a3Y03XOi\nBd8Vgv7BLEWBzSGB7u6efpPyBgt+9J39/3BGbM1+9Zqb550ClHwydAfghQkmL7YJ5Jj7C/SEiGEQ\nDTWEB700Ko40dV5PkgOnf7MfyieVUQqHcvKt6l1GUtPciZwhM5nHyb6XZd/zEsCpM4ikktQaJRvV\n0HLF9+pkb01pWzXh8+l9A7Z1TZf90ttfbL2jNbts0yp3OyKAWCtpy3dtxD+kMIYF3tAgstn7mMIQ\nJnToSaQoOTnByOGovp3sq9h939hvr75zi/3su2+x3UdO+TZGl61dZfd8+1n7wld32Y+/fptA4+V2\n8OQp27RWJ0CVy/bkM4eVlVbbefSE3XrLWnvbW2+zl52q2Yc/vcPuf+iotjXaai96wVpbu+EOLbqq\n2Za1Kx1QfuHLuyWTtfBK6RjRpZ1GpcUd1/OYtnyr2Be+uc9+4vXX2r965x22//BJG5PfSzUn9Mn9\nA/a+v/yuCAbNm02iebNd/pxT4MKmwAUPOL1a68d7+xIQzA+i1xwbMSOqU98aKTr/JhrkED6IecAk\nmgP6+Cd7Bqx3QMNkEuyIKx9q0nc6/XUIB9qS8ePkmhoet5yHH6daJKEpthCuWDegZ9PnaR8DmqbP\n+GfhEfPSBkcqdqK7T6s9Wf4gtpXGMyZ0uSs1SMQUCfH5n0z2yt5TePl9ZgqAVWAwNFXwGADDOzjO\ngdQXLigcT3w+k2kcc3i6q6l553R3uc1UFIB64nEnYrYgRYAIC/bKbPC+w0W3zxzzUSbqSjw36qvE\ni+QM8jFdqmpe2uIGYddH9moou3Ov9Wgvzao2OLcCx4sqJfJzqq9oX3z4kO3Z3y0gpikw2mOTofa/\n/czDdvjEFrt801IbHa3Y3fc9ZVdecYnt2ifNX3WJHevtti8+ssd27NcYuLZtYjh739FT9rmH9tkB\nn2+poXvJgB17T9rnvqOFQhorH1P4//PuJ227/NxwXacWDAm0qmP++K5+++I3D2ofzHH7yKcfs5fc\nrC25tA/sU88csw8/vNMe3nFCQ/MdGorfo83hx7V3aJemBxTkXlrOU+P2+x+63+684zJtf6R5o8rT\nw0/12L33aS9NrXYH9O45eMq+8tBBO3ZCpyM5vZVHfbjn/l22/+Bx+547NtmSTi2gkt8dDx6wb33n\nmDSpohXEU8FUJJfQ+nO0Zgk7AfeaiJ5kux6iSPLfnAIXIAUueMCJPKYKU4+LSGIBGARozBqi8kfT\nieBuVHV8JdOwbQjsqexwP1d7yVd5iaFxPfg/W8Voc+S+ftdsamaTrCXsXVsRmk/ygpDKBpg8oX40\nnic75Tilf6a8zCbd8xdGDKZlaUedoFwOj1bt1Ck0m6g6pDXRcHqZsvTWEbKJPgyr0wA7DeUPDQ8v\nddP8PFV+prLD89nazxTfXMM+23QQT0rLbMJQjr3RA2pG3Ui+CemsTHO0ZxVA7mlqCsDjzSaBy2QH\n4WdHfMAVcgigVBCY4/Siku74Z19VWKKoPXQfePioffuRQ77/Zk1Aq+rTWZCnVTt0omp/98knXMNa\nLSyVjGJxozrO2nvzU/futVZpNNlNYkz1+Bvf1bnrmvM5pnmOh48O2kf/8TEBWh25KTDInPXd+3p1\nYadFTwK1Nc2FfPjRY/aYtixCRozVOjWE3mrf2n7MHn5swLrKWhVeabWhquZjKl6WBT34xBF74qkj\n1q7RkOExLTKUBnVMnXeQY19/u939+f1+clJF7ocVJqvvj/eYffpze6yjrDmwGjZneN7KS6Vt5ejT\nNntkR79tf/pxLVxijqqmFEgOFbULAFMZntxTsaf3PmVtrSzyrGnhkxbp6xsyvKBJo9jhDrnlf3Sm\nddFR9hO2XN7JiWxyk1PgQqTABQ84U6FxhFrMjdQwjoQgx6DRoCJkkth2bY5sm6t7c2Ob7KeyI56z\nsxdsRJoTuC5O7unWhPiBIWkPmCOVpca1DAg47PyunjX+MBJgaV4nr2eXDnxOnfeUb76fbdizDkNC\nN8qDmLTqVFL75MkBrR7VnCkdI1fTeB44k4bRN7CW4EeOV72J0QenBSFwQZUwM6U7ucP1TG5xM5P7\ncxFGc1pmiq/Z7XTpm00Y5L6mownLbNCv3hALJDy8Zs9uk/9cWBRQJVJZezGr3MvISN2LAn9sP1ak\n7jGkXdOCJEbpNXTOQhgHqg66JIPklo3gWbxD549z1YtoOvV9TAuJ2E6ooEU9CtpGsw6jRJmmDnVJ\nGaCwFDmnqvsCQa1WL1KXZefnsiu+guLWSLXcUPXRFpJeNWHVpTrbHZ+SBTp9qMLxmnor6KjKIY3z\nD+nUITrs1TJSQrwt4FnRWec1bYdUVgRjOnd9XEP7AG7aBTSgWkPkYVTL41pQRP4VtvY2rVV0WhPp\nkna3yjmoGlKXslJ3wgNgt0lmSe5oEVOFRVxyS1oLAtqM1MihMqDvyix05Lx1tJ2kFyCMxjM3OQUu\nVApcFNztPXeNFSLA6FEeOXpSlbwBCaKPSRE37OavwANeEbdS5+kDJjGUjHAiRUq2P4+OjnragVdA\nZuz57po9nPibPyzinyZ6IJQdWDJsBbBUhp0YyrUah8GhEdu3/5AE+FhYU76ec8JwoukeNouYIPOa\ndLaYamst2fo1Kx100kR6GcxrKvLI5p0Cqju+fypVywVLzbpapbnTZpKlVrSM4CQ0mQGgnCfUW081\njSc0oaAohuOZGFTW3Y0Aloct0AgsBHAi3gCrOvxUzyw4EobVdwXg/IaGtar4SoA1nbPOyVWc1c6p\nRg52OSte80arAsUFtIhoZQXYqgJvhMJxuzVNgo/V4XIDoE1tAOlRXAxzs3URqSItJYHhCmCQJeyK\nS2cRyV4bvauVBIo6UAQ0KhxAZNGHxAHVSiOgEcAoX4gpwLbHCV0VJouq+MqUrpKmDFR0Zny7tlFi\n0yfs4qMc5CanwAVMgYsCcAJOAJVxZyuL7L1esIgCzPyCE48N4Z5F7YLeBZSLN+/wItAR1mgAXMvg\nQBm4KZMll9yEwPInviw6M4EWpF4tklOmTh5chF1oGIImNEoMAbqX8OHueI9ORRYAFrmZmQIis3d2\n2IYKIA/EEE9mrDaz/9zF4qQACBAwh6yUjGmRZm/zZWttz+GjtqxVmj34QDyBuKoKyJVQTcqd84bq\nIMKImsavizRAlAwaPIbIAXN+OINc+EgTI06ZD/MV3/BdBlodqKLpDLAG9wE8PRp9gy993ySlKU4v\nA7ChVRX4Q1ZqqJ6gfRoS+FD2yBO2XWILJ0aLxgVWa1puT9hKiu4KX+pT4qlI41lkqoDmoErRrxVC\nAqASxG4nzSZ5qgkhMwog5EqmZQCqhIa6E0xM2rkr3y6ziUt+AJuj/TbQd1ir2tdrb085llvXCnsZ\nuPf8J6fABUmBiwNwqugczFGELswQDIjHzFDRMf4tHufjlzSkhrw+j5NkSJhHiuIrmlkMIiueA5Bi\nF2HEd58HhOUiNRPpIWntJssbFFH5eM7VMLFFFBRhjxYvSxfqUa5J4+uwfJ7LNEv04rzBbiKz0xNt\nFVoZNeCJRxdnpvJUz4oCrqWjxkjKCBSVBYRe+Yrb7bP//DUd1bjDlq28zMrty1BDOtgcF/DiNB+x\nR8gq+EZhIKP8j3qpj16L5Q5A50duKjFeo1VfiYs6XZeCfHBZLDuvt9wBdNK0CsdR4/UiGejjOxqm\n9lf9ADYJhz/JBp6FTgU9BfpkB6ATE8diHeJkjiVuI8SQrMSl0N29hscVeLHEYiyFJ/cuaxSur/r3\ndMqta3gFwskL2lZpOZkp4DlU/GUH5ZFITkAqiSZDA6es9+R+27C6w17zQ3cqDjS6xOJqVKcHIeQm\np8CFSIGLBnBG4bmoy8oxnhEHLiD0O/8n0yD8aM5DgDZSwhP2CNFm49JM1q53SsmWg8wVY1WL2iDQ\nIwOR8+b8N5cUtEmUye7eWvCsKwPoHlLqTESw+e8ZKQD9oR+OoHHQ/DQviabu7rSvucVipICXKfJD\n4MllUsXWrem0N73ulfb1B7bb40/ts+7jB6xzxWpr7eiy1lbNcxRIYg4lHTyAJvMV4R2HmQojOsfA\nSen92HOTIXPYSj9eRXnm4AZfdIN9YijseM6uTK5NOOxC3x3E4o1AmS/Jny9i0qusCJ7hbYAwEnMc\njSZOlV58s6ApHJLvJF8iVM9TI5H6LvcAXbSb+A9kKVAtja+jbmS4FnzqOUJXvAKgVR2fxHD76GCP\njQ712/Klrfbq73+x3XDtFt9QHsr5YlaN7RMdechNToELlQIXGeCkGBFiDeNvSTo1rOfnSZHXU+Ny\nJgHGTOg0f5+QIsSlDNIuHiZ8XcwvdXo0KDMhO64NobxcMEOvzIeTjGcakmYvE16aP+TPp1Eg6NqA\n+fEetMZx4s9GKZ0WRG6xKCkQWka02Vl9URGj3V6+pMVe/YqX2E3XXWNPPr3b9h58VmeU65x0zb0s\ntyzVyWfLrMwcT2nzyho2Z1EOpqBFOc49klGuYayzDjEASQOQFgCcqQ67z/STPHhgbjkuzWtSawIW\nY8pMgMxGpReEA2A6WMQbUDjrOAEYs7iw0+C5h0v8AMz4hAvNq9S7LwLK/LLIh3mlmJgSoDCZ7yk7\nAGMskFKO0axWxnSs6KgAZo/u/X5sJpvH33H7tdoYfqutWt6lOFiVFNMGmGrgaDMSkKUpv+UUuPAo\ncHEATu8tU3gSCPW20sVh49Xtm4TcPJR1AKOUIO7pIm0SQtziJ0s3gjPSGMCL79hlxoVsCi9ZLp67\n0wOVRDL1RgMLJ0b2Rc/urEGP+EBj0eSuKajMY36bFQWga8ZXFIromHVxZA9Rm2g8q/ByRwueAj4+\nTZkD4JpkjBbraJcfu3zDErtsw83SEt5qe/cetmc1t/OZ/dqfsve4HX22V6Czy0ot7ZqTGFeh2Ko5\njjqsgt0OxEPMeUQDGsPTTSAzKr3Hm2Rb0Crjvzq/6d0BY0obrhIfiidd44mNM2vc5cIBIUPeuIZ1\nuetCTmSs7ZxObISME+xj5xDAqsArUcs+HFSEKWOuKAukyNfwiPYPFYAcG9VWT6M6OWlkxPf+3HbF\npbZqxTrbqj1HL9m4VvtuCuQKkBZ1djsgVaEIqEbamJfKWe3oEHKTU+BCpcAFBDhVU723rKKaUGl5\nCSHlH+rfMulTL9nJ7/UP5/Gh0Yy72EkS0ZMyKT0uiVI+siTV83IekzjPQadce9b8RzbJMmsqPElT\n0WNyWi9A+kzO4nl5hw/RJjFUmJVBsCbNOU11Imy9YM5LMvJA55sCUa6OAb3SaZajbycXcxN9WLpS\ntasvW6dN3FfZi2/Zpv0wa3bk+CkdNdmjc8pP2XFtZDk0NKC9hM16BofEKRp0b9XelwKfLS0dAqDa\n2L3UqqmgWp2uP/qXrL1h1XfsSQkY9S+6w19NMk9M6JrLTE46HzoLyo2SHppMWbhs4NjJsOdLdETx\nQR51ERZhyy3zRVk57ouM9M7qdmItsOJeq+BZ3U5dGNPKcrZDGhWwrFW1bZ3y3q7ppV1dLb6zw5pL\nVtqGdVcIXG7QEZtLrK1NZ8prjmZBw/2mnT4xbPmEFpSFV0LfioQ8aXdOgU1Ws3ua3WX+k1PgwqPA\nBQA4EQ30RBErGN4RKvGGTbxzb7ab6h27+TSks9nQz25OY3pO92a3mV0C2enTVE7TtwV/Fz2itYuU\nTiaP25LBRZ3JBVwKoqs3xBAetQ6AM4kI7LIrJ/8CLsOzTVrUq8lFG/MyGV7GSLbqn2FzQGJRczIF\nHe2KS9boWqvv2jd3ZNT6+wdttFq23r4BbV02bAeePeRH0vbpTPM+HdXru0poLqSwnD8TR1Vgi4WA\ngFHmR6IZBZyBLXEfALWROp7QEvpcSthSFswZRWPoK9dlNVbRvE4HcQ1/DjjRTGpvTYDkGHMs+SxA\nWalqT01949VnaupZUehd7YvC6dCJRctXLBGgvFxgskXay+XSZBZ1X+LaybY2tlHK5mQ6xYDbmKwO\n6UVRklC35cfjxpXTgrYrNzkFLlwKpNZkkeaQ2huX910FVnxeD6DF63Sq2LhZgGZyslzeJEvu6Zl8\npLxMysc01pNcLY7XlN2U2rOhR/Kb38+KAsFO+uXByyMx2OTCOavgc0+LkgKJB5T4KR/REgpMCaB1\ndrRae5uaFb1v2NAuFirYzTdt1ruG1mEhaR37BgalCdVeyLIb1nGU3TqydmRk2N329Q8IlI3b8PCI\nrn4HZ0Maoh4ZQkvYiJxwh0flRt+YUxmaT7kAdAr0ASY7OzutpVUa1TrNg4fbpU1sb9eRnGonVqxb\nLv9oQ82WL1snf9p/tEv+tIH8suVLbMmSTi2QKtvSpbIDZQtuA069cujOvFW0oMSLptSzmMHziLYR\nu79Peg03+W9OgYuDAhcA4KTyMz+Iqs5UdAzDJdEnl8Rym4X6EyIqUt2cRmziG7a8NSQVT403vi/s\nPJLC2ZrZ0CPyfzrNGnFcOPRo5Om5PgVHzRRKUDWn30x0yr9PpEAMB0tTyVAxhnmOLpPR6gHGQjYz\njM0CmpVbNjgoxAnSGqBXKpcdaKLZZAN2ACxIEO2n86X/JMlX0ObpaCrRKEras3O8PgFqfdW3HriX\ndJxmw4R0YY/MEqpLhco99vusCmQGOCU9bHGEdtXBrMJnqygOmMAbczjZ1B2Nqi/4IWJCI/Ls7gDY\n3/KfnAI5BRIFFjXgjOqtrFDfuWL8JctbmrWzgOFYyKks8ZFsRGImtlzYZpmZcAu5hli8wEw9Q/WH\neheCxoHGB4NgR8PRMM3PDdvmpqZhe3E+wVMTaTY1HXKaTU2X3HZ6CiRwFfeQXuwr6Sf7CKG5JHbQ\nGXXX9+9kbiR/AnagOI4eRkPI4UbMHy6pjvuCHeo8cn2y8fCY9xkf0tzH0HZGPHUvGfD1d7mPE4Hw\nSNhMx9JfmThYEIQDDYIrTbig9WCFOhH5cL00tIKxrtXkK24AtglsYpebnAI5BaamwKIGnGTJBQ61\n3oUSQiOEg99xsOANaW42vKOxJR/N9sqR28mynteJ3y+MtynokRqMOj0abmgsWGyQm5koEDSCh87U\nOEZ7G4Suu+M1J/FMBL5ov4eWMUBXAp/IKOZXAuCwC/ZBdnGh/fTBaaG1OPmHzqQrNPUOCAwPgD78\n6zVYsk5j52MfxVLYHn44CHueI966h/SgsALquuSQ30i3tyPuRvVDiQMsownFJO2tL/iRwyRviMtT\nmHnGeaKFe8x/cgrkFJhAgUUNOOl5Mg9I4gGMBiLjp57BiW916wX9EKlnvlPW886EXtWlMf1tiTjP\nK1lt5HVBZ+o5JI6GxtsosqpnhrK4+013TPBBPOe/01BARHQSagiShtLriW48TTAiuMMDaCs/aG8A\nDRmpJzjNXy5GCiROaHCOD2GLFMFXQZPYpB1L2KheUfUez/wC10LKZXp1gnS3cCAecaO7+LWa+cNJ\nMuJOHLhp2Ge8ygp1GW8bMkceJ2HSbkzw1fDt1riRFQP5+CeoCFVvfKjngRCJo2ESLRo2+VNOgZwC\niQKLG3BS+WkMMwHiIE12bFicJnCT0RAISSyEkAgCJDve5mI/F7dnDttDIhnKQ92lnksuMGNuEoJc\nB8plSZQAxKkLvRCI7rH+44HV31Luw+Js0x1pm48wpqIHDRN/FCSaBwzDW5hoV2gOoulKjYF/qwfG\nW7jnaWqanIs8Noc9U3zNbnmeyf1zTR+NpXLuwVBnNPfNW9LmePWd1yYrX4DBe3P0JDc3FykFmpjj\njEzR5E68E4ANkgUETMRDQ8hX+G4qFsOO/rX793CSz4l3JGGYpnhl0Yi34b400YmnoPG1EQrgcpJT\nApw6oZMDyN9zCuQUOI0CixpwxpYXzBECbKCNkSxgQreeQ1BJOkQLK0FxmugQMaayg0ZzsZ+L29PD\nTsmrSzKlE4AJiEYzEBq9DBx4ulJ86a4wHSVkAve0fDa5I/q6mYv9XNwSwVTup7Kb5FZZSM1Gokdq\nkBIgd3oIbAbdFKYHG+XdiBfLLLDT6DEpTl7dzCJ9yemc3Z+LsJ9rGIlGSryC8q1jtDVMmvuWsuY8\nx4pi/VEWTr5GoSRn+f2ipwD8eCbGCA3j9GRy7lIIyO5GUC7KJnniu4c2oQpMeGkEEAJhUghNr+5t\nol9PiScic+efibHZMr65DZXi9E9NkeSPOQVyCkxFgUUNONFiYrRnsG+8K1jmDahrOL3JbMwFakiI\nZmHTLDWmsuc79ulbcp/eiT3Z8Xz29uETeBVaTVZ7xh5z+qIo0EihxWMlJUcSE1cddHm8KR3czz4d\nU+cnhR3x8hsm2ac4U7zJHldnsuP7VG6TrxD6aDMrogf3OPUDnSduxrUfXptWsWpjaoLxoIgPnQgu\nMCn8lI5mO56TfXLXbDed29nYp3Bn43Y2cT7H9Pm50Yon68iMjo4GL9FLm2C0UXeLnKlRZS9EqtiU\nmH2Cn/zl4qRAfW7P6dlnlCmz9a5Lo2c9wS21pFFTNDqVPCVXqOVl4hhLf/L3+Ek+mz2FZGg4av4W\n7hspw1XoQFNI7k9ewleyTXd99Q/kG8NLutwi/8kpkFPgDBRY1ICTyo646OpssY7OlcpmiIlGfpsF\nIt9CcDSJj8wH3+QWQMOcNQ1fV7WKckSNcrsAjWtO0/5rLhEjntPDacQ82yfCIBfJcF7vWKVmJ0/2\n2OCIztvNhtZJaEupbGtWLbMOnWChWU3JS3aPNE2yPPvXlKQs2HiN3wRR1KYAgZVewGBVe9sJqcgG\nrQXGvWYAp47/wta/N35SZCH+0wwrvkMPdjzp6em3Hu3RV9Dm0ComL8qO9jZbu3alwCZxsk9fI5yI\n/RzThAQtNKMsk0sup7oeggrezENANeJoy/mu7WbENt3d3SqzcQFObU+jzatbdYef2PQ6zdkUF4rO\nousEtJliWmhEyNMzfxSAB2Zh4LlpnEUIAfQyKZG5nMKPHIf7qeKdyo6gzmyfYdh6nNnDGW/ThXhG\nT/nHnAI5BSZQYFEDzlpM4FOG0G0GyJmQu/oL4mI68Zc5YuWkVIfVaiyS6O7u1xFtw9r4t8OWa9Pf\nYlGna0jd42CnHm89gkkPs4gvSxEu48rSL0HNFh1r1yyzo8e7tbmxtupg/ibufbqAhkFlA0CI4WZy\nBgScXZwe0Cx+CA2TmoV4jl/tYOJzKVm1OTJSUzpP6dzkcVuujZK7OrWvSbYKFaQYQ7akmPyl3BJO\nc3oj75QQustwJTvAKm+iyYrlnbrXrFenmBR1TjPHz/FeKspdbdhp4XvlKYygR4LFxHVhm1T6DYqK\nkv6SgU4nb0l7GxbUkRFPDY+KtEUb0uba43qnE9NS0j6EKsNxHdnnmnUnmUImHDepGzBDPUrO8/sF\nSoE6Q8yQv5ndhYuZ3XlEs3Q2Q6JmHdTE6Ca+NUZPCG7yt9mkIHeTU+DipMCiBpxeZKrvM1f51Eim\n++TCFrDRvDW0P0DXU929NjCoRlmAp69vSAhGJ1IAeAB8GruN+KYPC8gzF0N4KQ9sqxHHuJmtWrlc\n2qheaTqlfZILho2JPblPfuYS16zdeuDE2shLxCyYi5WGWwcGR+xEz6CADEC85sfXFYvL/LSRqo6J\nK0sj6xsjuwaSmFNYBJ6esQcyBejkLeXP3QhsAnCh+4rlS33bkX6VDZo4NHL6cff4Sw1BDLhPjiNc\nXHC/ymZDCZnKS8DR1Tip9IrO2yfFS0NDoTUHlEOhEWnRT3X3SNO51ElTUiciaagnllGiHL5yk1Pg\nwqLA3Lh6bq4vLErluckpcPYUuHjUQE4jQM7kK9mgMyzZKQEoAE2NoVw1vegRe/uHrbt3SM8t8t0s\nbCaHxfvZG0L2vd+kveMs37ZWhjyXagidSXWg4dBunn0MZ+cTJnHQpzuKxfFimw0O1+xE96CNaGgW\nbRkp1gwEO36yX0BUcypLbaIh36DvdHQhx4meHsuEBAprKl72w4Pq0jDLyQppUZfquDne/fQS6EL4\nKZgJIVwkL8p74EtgZAY6oT1k1znUlVpRZUW5qID0Hmc7MzWBcQFpOnVs4CkdLzgmd7w3SounyddF\nQtM8mzkFcgrkFMgpcE4pcMEDzkZzmTRoNMjpOaOlWutKpaBGd9B6+4a90QXaWbHFKvoG6OyRptNB\nZzW17uEXnBPhRTPNwp54mmM5BTKTX4EGSkWaO80ktTad6btm1XLrFOiMRVIACcUg9ylvc4xpVs4n\nhE105FJDsgVpwNBsHj/VZ8OAT4FN35tONIJOY5pKebJ7QOclC7RrziBkzH6miJdYzmygJ9pP1k0X\nxsdMGNxWLuu0ZQKdAURJmYwHFaVxXglz5uQ+L1+jrOI3SopkQAt1AqTQPHay1/ql2fSyAlBSZk6v\nmIgCSw/q+/ETvXIvesOLTSb4O9UZ+Ju/3OQUyCmQUyCnQE6B2VPgggecToqJ7ac3qIA2FqSkRSnd\nmWaToXW0PAArPwOYu97RBvVJ09kjTafW9MifAJ9fanonNdCBgGZfCOGSRDJoHWDAC0ZDxsxL1BHD\nGl5fqqHqNmEv9K4JXISf8H+uIUACFYIbUjWG5qtkwxreP9UzYCMCJuMClNAIvVpNdAOgYwdoYVoC\nbmvjgE7yFGkNoAP9UnrTfVIh4T59csqQAigU8zaZV7u0q11u0HAmk+gxKaz0+QK9s4MBy8mZX8wE\ng8TXTBE5wfSQEZ0LrcVW+qQLt+F+XH44DxpDGQ9LRY2mk/KjTFWgUU4qi5hO4U7zn5wCOQVyCuQU\nyCkwZwpcHIDTyZJASMC1goYWaVQZbjzVOyiNnDSbAByBI58DqGfXKHKn8VWDrYXrvmilW9rOSg2d\nG404d0wjfH+dxY+3/5k7wgAscPmfPhYZShaYQHfYKk3nCmn22tuYY4dJvuPNrc7xT0YpaTaVtvGS\nQOa4nTipoVch7qCfUuGAJdISwDMA6BhgRyvtR0YBL6xex02AabBm+GtOcGgqU254gxZyyL8b9HEB\nfbU2XgtcWBVfcnVwczh6Ft38mmR9ob6ypyadHhZoxTB5Weu1inZSWugBDZej6aRj4EUAbURHQKfz\nt4PPoDOaz4HhMTupHQG0Vs3DSOWc6ZHlNyuMC5WYeb5yCuQUyCmQU+C8UODiAJy0sU0NJQ0rIGqM\nVbtqXHsHRgLc0Ci7Szw0rgBeWGl4XZrOHg27d/dpsYzeXcOn8BpGYXh8DZvpn1IcckHUWVuebD05\nsgvoVdEinAS+1PxnbqcP+7l9ScmBTgWtCh8crtix4xpyFeoODW+mcRRK961zHEUq5fJYE6hhmB0t\nKMO0g4AeAL7clrTYKLTL9VwqoTxPMp6AoLy71Dv0wBr3xAnYdO3eJK8X3asKCbqE0rhkYxojP6qO\nAcPorl3WV46ATdSDPriv8zUWbgLOD2quBOWm4vMtqdD6N0z4bLznTzkFcgrkFMgpkFNgZgqwguCC\nNgFQvDlWPjNgKATDwHS3wCYLhACRJg2ea9281WaoMQNByaujPwEchaKBYmk6hxz0sHqdXQwTApwY\n32xIS4jhC0TFG0OdCRK43lOAgqMu0RUmA2g7XUuYvj73OyliETj7Xg4IuDCMPoyKVzQE7DrGBo3W\nDVMUQKfAwQA/Rfkdk1b0lDRtxfEObZnUrkVHY1m63VXdtz949gi8YY3W1K2Tlb6xkMjjcnrJxQQt\nZ7PrpoCS/wvw7rkE+AvUAzZPiN6DDKPrHe083/1IUO+lZDRJpHG+DqIE5ca9bgyOakauhtdXLe+y\nVvenOjLJ6wVIyjxLOQVyCuQUyClwnijQrJo7T1EshGCbQYiAlDSbPQJQ/QKNaCirNLoZaIk2FfcT\nr0b7zLAlmk1pOrUnZI80nTUNX56tSeE2/MumCQS4vQNQAQA0hxnQPJ9gkzjB3YDNYYZYNQ9wRBMC\nGbKNGZMCxgI4QEHSX788nQkIy43TVSBIqjK2dxoYHFIepqNV0Pt0ejgFJv3gVhpWBcXq+dNNhHW6\n/QVo05T/KgcGnOpWB2E4m1FAh0AO6Ai4gfZTUThKkNL0S16wGRoc9rm4FSaD1k24rb+es4dUZk0Z\nOuuwTw+jYZPiIXDVpywOnuIts0kf5pQGeYr/LOwIM1HfIzurcOeUiNzxRUGBOqNdFLlNmUw1KhaT\nRmWirfKKpzaBdiGuporGY9Or3uZsGkE0pERzkOl7s92cI7kIPCw8DaeXnIYAvfET83gJqsELrhIW\ng6FCE+hN5wwl7Awqh+htMLVa2VejxxYxCkeLcth7MM5lT3zpIbv79OPROKASyNEIo+8BqXT09Y44\nf69coQUsBWnvBMPkQg0+OZiNaXYXTb7HpZ/wz28Wkux8cNNvAIaGAABAAElEQVQdzCbss3ND8MBJ\nhlZZuT82onRpOL82zub3SosWAgEmqxnay1LnGl+QTlVlVRKRxrVlEiEBjocFhk5IYzZe0OKn9piH\nygBu3TjIjpAyMvsnbJqzm7lQmBSCaDfZQd1XPeQL/0GdH7hIsxW0o0GnrfG6Is4XuGf+7LOHTmr6\nB8/OPWKn0JVD11g0RD2Lr5s2rtGyLw3Fq2woB+Z5ltLes14QzaVxrkir+L1ekw7KVReRExV8oQ6i\nEsHLHCOEOcgzCwFlFAdUcLYVLwfLlbzG1nSwQ7WgbaPkkhGGguZoe3wEQbx+5/l0gzQidSVtW1Yq\nas9c9zoiL9qSqqRDCuS3rAMlSrrGOaggNzkFzpICDnfq9TeYkj084KpoSeDEC4HHvPJH/fcKpbqj\ntnpc9atm2ocZxUVNawPUnvs6ioJGHE1tMYtUJcvYMSVqtmqnKqC8hig4Qz32IkkiRu7SI/31cYSG\nP+ggFj37ygRPIj9qyXCvNCHGKAHGQYkwdpZxi/xHFFh4gNOLhWYhQCX3kPa8xx/viRlmLkU4ISoh\n+0L2ahh9iH0iC1pRrUYHMMQMuArjx7TYboizyTS9jrNHplRrnMZC28FpO4NacERDs2xZm6dLTb88\nNw+AN4U14bEp4An28RJ5nORm0usU3s6J1WhlTNrbAd8uqqQN3Gvsg9nC9lEKXmlwbadWiPvUAweh\nsgQAqrr5GhYfbg9aUVHdvejV3TuguajLtAiKjMyd/Zpp4s+OOJuzPE8Eao7y+X4WDQLyiIX1zEZe\nUIG641tTiR9d2+y0mrrm4J5Q2HYqal+iY9yn9nWuMq76SR110Bni2htNf88A4JyiityH0CflzCFW\nFDr5qqaWpzCuI2tLI9YyzlQP5VatRVUgs6swqGhFBdhS3wtq0KBhosT0SSCOfusoSg6UqPeSJZI1\nFXWuBqo6rKDQqlfVAVUMXJ5fWk6fyvzLhUIBCVK4yEeLADYXJleFckk10Duf0TZ7+6JOtNdJtT9e\nO8m+W+BGO7morrGQclzTt1isUZU1ug/pTGZRlxWEjFMUkQR5QZOASRQtCrIkIenT6HAkCWsF4Qnv\nKOOOBlIywHe5kSyVmyTRcH2xm7m3+OebYmIuiWpd+lP5oW1Iz3pVgWYJ8MJues+sJ98k9517YMCS\ntCQrtJH6yhXalBwmoqeiW7/2lTypRRYAUnH35CAa7/reVi7ZqlVLrauj1ZUw6u6IuWlkaGwUHvMc\nSfii5jIBD3HGxg3LtNe8KhkAmkorLSUbuw9pAREZ9EU7XjqpWMg0DThzTsets0tnnYtWZVn76mnR\nj3PPfSsjkSiGRSCvXjKyZzcsczMrCgSDU0ec9q62C+YDhFJ2AUYTQ+IuM/4IxbloxGSejwKYkGYE\nuKS8X4BNtnTipC26hbMBf2SAvGYNsYNWZVT/JW3bxde2cqttWFWw9R0nrU0gtCw5UKnpZCxpTkpq\nxOBVFr2NF8eiIXFf0xMGGLm0bdCu29Ipvz1KO7sCtNvR3nY72FOyIbyqPtQU1zi9sdzkFHhOFNAI\nkfg5akPwU/xSobMLCx4Xs/F6iFxCHqj+A+o0ShAKHRZEchrgKVV3uoXICH0X4NRu0BJntMkiQLFb\nFV/f9IiCcsbalxyku3yAHVg3UCDcoup3Tcf/6ru3XwBMCgNNK6ELDxSkUKlpLj3HYQOQZxGr3Fwc\nZuEBTujutYkGg1KPptSfHYHCOU2FqNeZDX4Q+OqJoFWThgO5jyaipLDK6rZ4b0gN0kympoZJS2Gc\nAeFgmJ/kUiEIn2E0Z0RvRGcKbeF+R/M7riFC6ERHuqqGmKFVn8Wp+ZsUTXQEkg4IIqhGK9+Ujj6K\npvRA0SgBQBGSvOuDBIRrfJxwOHYf8pDdCTw3s6eACz2RTwLZh3XqPimTENbBpAjkZgO9uXCXmea6\nlezm5U59J62UvdJT5w3ypTSiYWhO57RpgisJK+NLH45TvhWkaqhyW9SJWFU7cWzYulZXrXNJv9qj\nbmthOF0yYtzncuNeHUrsAL6u8pw2Qn1gULPPOjSsp11zbbTWZaf6S9pwv2KjyIFsZwZPv8uFnL/P\nRM3825koAO/ocr4WU+uOlh7wCedfKIba67KJYXPXGlIPZeX5Jq+jVhnrtp5jT1lx7LC1SuHjcoLv\n6qCWVM8QISUb1HQWTXUDbGakmxWNMlL6Al7JBdrB6tBJG937Zaswp47EOIKVTKFOqwwqhS5rWXqV\nda5e7WUC6HXtLJ5z4xRYeIAzCWRvZMR2XqGkmXBG450ej1e5rHpRmBl31L/4g9tTEWOBTQz11ljN\nK/cwAsPEsCSqduay0UzNZFClw27uWo2Rz4FzhhNTMhcURlfjOLHyT5++iK+ZIWdyi4+Z3J+DMJzu\n5FTaGmltqazQEpUwkJJ8eiygUTdULgCnw+0AqjTvaqw1EC9rDcnLndMlSx7zXEP7lgXht5S3dMdy\nqvxMZTed29nYzxTfbMLATQpnHtPnUVEu+tMzVM2slB7qDJxNOaW0pTJLdnLvHvwnc8dzcq/H81oG\ndNykkVAiqF2uqRD/eezMWxnnlC3ywBX1nxRNbfjOhW/khtd29z+OhsKzVbahsaW294ToVRqzNdrP\nta06IC9oNqPTGR2laMjDz9SxYesNm+Kpif+raoyOD3fYvlOrbMC6rFZSeIBX6F8YlutEc3wmevM8\nE62nc38uwmgO+2zT0RwGzymchZi+lLbp0jxX+/nNY3A0nXdkbnO9UL48KeKxqNBkZBEbyon6giFj\nuqhsKHpk3dLWZktWrrTRo3ustdIjl5IfDFnK8OukEB0c70Gm2aAdgsdvBKMnGT3T/hVRulRPMoDh\nySBl0p34d6mwrHXJMmtbtV5ygNmj+sBYPo1ebuoUmE0R1B3P10NBYNP1GYBLB4ipYtGQMLEfbqB5\nxfBLqScTtvFGw8t2QgF12LsxfCkcMQ/Du76wgmDlbnZGrESaFI2D36wxJ9aa0htqfaYsA2aTmT59\n4WJimpOvyFt6mykM3KVw5uIWf1O5b4QFOPf5NNRCkQkaQkl/8Y4ABNTlaEdhQV46DHIcANwhqvyE\nrxRfvBF3igvggWTAJDuep0rfVHbTuZ2N/UzxzSYM3KRw5jN9Ko8kIZ3HKQsAHJ0shnfRvGs+Ih2C\nSXxOB6xIj8uTC1+rDDQCQAk38kK+psrPVHbTuZ3Z3imnBsPrkFoUb1DlrajFeFHvCSPRl+e5GPhK\nYQMqRZcxLeYZHV9mzxzTsNzaiq1tr4pCLPSRO313WeE8rPjg6zMauam1KswWO9y/yvaeXGeDGlYb\n04iJpnRK+gwpDOQB/I1JeWgON9nxfS72c3E7m7DPNh3Thb0Q07d48wg1feEZgNIbIfgK3kaaxp9T\nnJ8Z+VZuFqiJEhKozuqN1wlvh6Iegx4LttTau7Za6/qajRz+tsig9QGA0aydovo6vRCHIJ3mYp8u\n381uIDFX5hYUQXcYBQzhhrJJkknlUFx+qbWuu0UdzOX6JnnL0Lpkb6PNmy7Ci8t+YQLODHSw0blp\nSxbXTCQA5w2Gil7cRGGmdnb6YssaMHeg8MQcMEsselElZbhMYdZq0j7MojfC/K5CoV1NRzQgzGP0\nU1wCiWUNP6sGiWUxm6BVVFmACuUAu6DBFN2kyakxZCCiQUtMDGVK6FFGvomnKhzz7xzo4CBoj2gM\nk+688cyg54JkyUjuAvytU7AOjoLzvAyoI2iqC23a/1QuKaa6BzKjksjeoXxJ5cQ85tAyzndmkexc\nDV6K4SzqmS5AsLcap2ViUkLJEBf1T/4Iz7UeWtCneZWATTqh8CrgcqiyxA4e3WCFta22uuOItY2z\nWIiGGz7Ef0rTBMLpW8Mw37MimXCiZ50d7F7nms2K0uoNFI2dtFCkRjOjdU0fTiPE/CmnwPQU8M68\nGIoFnOOtLFSFr0MzD5/5aAF862aR85tnQ3XYQaTyIhmFFfWoMN6mS6MY7dusbb3Z8JHvqK5riozy\n7m7ww3PcMnqc+RZhQ8NJJgKUJcowGclJwqWeF7sutfKal+p9tWbgxNp4PlY1elIEeIaPSQFenK8L\nsnX3RgFGoZBpGOA3tJ0qOCCOz1MTowXAmQjtmhnFvRNENuzAO8XP0B2bK8AHxIU+MhpZek8zGDXg\nMZghP/Jf0TzHGC4jXcQQKYgKEWHV01H/OtF+yjRP4xafM7mfKb7ZhQFr0GRCcdHYI6VMlDM9u9ZM\ni6TilKEsUa7tDB8ldRZqzIErDWQatlYPj3AiLL26WFB+lOAIPum05iePjRTMHF+zW549vTzITEXv\nqeymczsb++nic+HnBAQyRg2hS0CtoIdd0SR7A+g4jeWCetQUmO/SoHnJdAVck+1+w0GTs/OfRyIj\nHz7RPqWF4bAAiL6rhFLZnCbodrrBTbMrGoagTY0Kq08RFQ2WZIi0kP2i0d6TgphraraufEjfo/S8\nPjNm5rQ7PaZko9RqNXqXHTjVbgPjq7VuoKTV/qIpiEAamqrG9Dgalo1alJrkrekp0pQ+NFzMbD8X\nt4Q/k/uJlEspmjkd04U9U3z4m0ucc3E7XdjnIozmsOc7j/SfkMGuyRNPay2aylXtERlDPrskQBrM\nQoNCRhaoISfRYmcJRIilvPGJi5/xTit0XG1t60Zt5OjDggwcyqJWOSsYd4bTOZjJfqLNwlaBEq7q\n9Ji2Tisu2SC58WJZrFPSpHyB7MhYlxvI3skhyeoiNgsTcIIEVXvYF6+lpSqB3Svw0q1ylvAWyIEJ\nWRXmswW9hKcvwUZxwwnyJs7xFajOuETTZdWR6I0UtH3JGRlEDWK1PGDVFmlbWlnNOih2UngCrTRS\nATiJhUZysRvRWJnwKsawhj+rYqshrdGb9q1eQlfkE6PdpfLsGmNE3qgaXp1PX9TeaGwx42A0NeUe\nXPJRv0OxEDKLnXbzl/4GnwWUSXWCaSnVmrYIKS5RkQyKR5kTObG644M/b6hUYt6BE3/XVN9CsM9X\nPrJa6h1DxZk1mrEtERpy7X2n1E8EkjOljToZmkwTH1r5pPJ3UvRwKK6GQeBPvDqqOAulUQFFs87+\ncVu7gkZCw+xyx1wsGm1szmQog0HJof6WHhspHJa8Utw1HXLQwtC9qKoAilUNAUpmVckjwDo3OQXO\nigLiHXhYw7rFokYuSgJXZU3ZKLLoDX6NukT9zx7PKpbn3xN1RNLJhVOq+7p7HpFl0Z4zrY0Ontky\nibobrE0dyKFDD1pLddC7d1Q1wCIgfaZ6fFqeUzXNxJOUlW4Ic1SazNKKzVbWMLoVpV7VhJyid+aV\nLtVxjgMu+cp13lNAp8Vw0VlMbIEWSvZVsFU2vNIkfxhlx/77rFT5rgoOEIM9K55hMpguGE8PUxpv\npMRxDnVU8PEH5yhgVUqGIloKm6yzfL2CAnCe2dQKvfbMoSe0r9dhNdSsYoWZoleZ4uItwOdiZTSl\n2/OlZp7aSoVjT8LqcutouV77aG4W3cgt+3MyFzaRgTJRY0tPVD28/qEj9tiup6QJ7hGFqISEG/SF\nMv5Yt4pypHxyM3sKBDmpxlAO4QYdRUOXsuogjK+1zrYrBarWqFJRPpigcWhEmjoBWmW9/elvao3O\nQbmZhbbfwzoXP8E3HJxQL38lsSSeK6lj8z3rbvRVp7FaPGOgM0QbnRbVcckItJtVgb9Hdn1awI/6\nqnwhO+gYiVdr6gyxN6cPd69cajetXK9n4sgu6JjRa/oox22wetQe2v2IDZa1UEjhljQ3tqbhNt8k\nWvkqqbUqV7Udm6aYuMwiHakyeMDEk2RZ1rLJhpLiS92cZlH/kj9cFBRQDRGwKQnQ0CGr0Rkr9IhH\nGGnDUJcyjoFX0sUnnpvMpNemLwvlUSms1xPqM/WCjqTyV6+X5EK5V5tTKizT8Pq11rFe2sejD1pt\nTMPrWVbAESn/Tc3QBFLhdCqa0JxFdKK9hGZF7V5x+SYrr3+R3lcrSRpBUgfA49IITYRBO0h651OO\nkoOFbRYe4ISXWNDjmky4RMdQVneIzZRU2RfYzFVzNwCdPlA2Cw2nYyexAVoLb5QVGitfaXjY0qBd\nV0fLDbDxjAZmGhw7YCPFJ1TR9UKFAHgqZJ/PGJbxbcbQFrID5Yf8ee2hgabWLdfWSOtFvc2qRxJw\nrgHCARe9zqhgPhdQFa1SOG691UelEdVeaNCeMKYwzXSf2sUUnnIrp4DTDl5WGYyzuCZJUy+vdiuO\nXGrt7ZcK8NCZonwC1DAZH4HISUTQnIMMauU+Hdf6pEDRgeBr2c+PIV1caBYR2DxH09la6VRablTj\nSkMzW0Mth3/Ri2quse7dtUelXcy2R1G+Y66wcq6OLe7QAA9VL1X8akCcnsBGGotsysyZYhfNxzVz\ns39sr7Z/V3OoiiO8qXgBBaSazlZsyVSQppMwfQ9ByTDPq77JQv90pnVnaE5PNHI0cN6B0zsmZFk2\ngiJ/8R7f8t+LjQIwiBhNPf6iwCdADJ6B6zG8U5N4i4tfanvD8BauG3YL4ylSH6AtS5EnNHXKMjuy\nr8rWoopQYRhbI5blJdsk1SpWOfyIqho1UhRRPYI2bjISpNfMNr4TBA6pWLrhFFmC9OF5VHKivOQS\nLRC6TTJzo+yibSyk8XvV6QgX/9Tn3DRTYOEBTlJHibm0JXkM36oR8lJk9iTDuxSmOwq3+JnGuFN5\npknwxgxG8rA1oRc/NAhVDfvOVnLTOLC9SanP44YJmXfmrKlwY9hSYWYMy+fFajxv/qP8iJAFbaob\nc2lDw8z+bzUvH9EkCkhZRSC4p3iGNtIssYm2l+sUxEiup/iUW81AAWhXGGe7HQygE97jGaAkwFTs\n1zvf4VGAqbjed1ZAUOpVZVifEgFvowUsMkRHOc6XUUIS/6ABRANJJlSHqLPeAERtnWWCqNlkLgNw\n8CRTO1xbQtD65vKD4HDHq2RLIZMDWtXvxj85MeP9jL/yg39veCRvvKWH1pkn0dZlkdNfdp4WPgI6\nAbbIOr0rbYU67ZEnstKFmOIiWAxTgzjlKwUftvnvxUuBrBPSRAD4DX5Cfx9SebFxC5w/g6Eue/1h\nCFs1yEGehrc7r7eWjR02fPibGlno1yV3UQFVNwVBCVrvAEkPwmklcnmUQS+G0Avs4iHHiKeKOpAt\ny7ZYee2LVEfXKgDA5eQ0Tn6fIf0X2ecFCThhgCj3rOERW8RcDgCO7LxRhYFgmiSCpys5+jf4odop\nVPcDm/BGRMQkMngjN10YTfY+FNcgm4fkGpEIGj1G6GZoOIlskRqvhY20h7ZINBOd4hivACreh3aw\nQD9QeQa0iLIskhjXkCJVmh43mqZFTY8GKRbcU4hHkiXeg48z8NbQbyTtM27gd9y4dNYb/MpzAi/p\nO27nyXiFV13RfpU62ko8Rv0SzzCi4XlR+slXvWU4c7rwU3RAh39vUtxvDHqRPzqw1P+QAtCL5pov\n+PW9DWFjbFho5F94P5NR+gToAzwSEnWfGOIpZIJKyvOQZESETcct4iAVqkfIJHl3hQ1k0as/Z2Hx\n7MuPdE/zyvQpNzkFJlCAjSngHcCVWCVYzB8azvRp8Ru1Oa4JZRREbU4oqDo0KXurFhIN2+iRR9Qs\n9XnXDkJAAuhC248YxMJfoQTf9eL1Cod80T9HxhS6LrEWLRAqMIzunXe+52YuFGggp7n4Ou9uafwS\nC1DqLv1lh2DWvBV/AtzMIiF4d1AZDSph8McCJIbk4zk1ALMIz0U97mVcayESqjFk2I6eFvMZ6wmb\nTfpwvhANDXxGYCA7eYsSoQFP31jdDAvpOw18lt8oJ15wF8YreHrJ7+eYAtA6k5ROc5eUYScOp0PF\nXOWsePSOXXqL+uWdr8xbCGM5mTfTFDF1NUvauHcSpR3XlBUfWvb0pHRPn7iAbfru4E7uNUQdBr8x\nnM62ZrHBCa7hYOQBRrRxDbC/zPonUdE7VzRk8omcCqN49Ui9CClEByDygRY67HhPl2ygg/xQUpm+\n1YOibAgVJahradw2/8kpMAUF4H+YJbHVJCdY83mxm5ANQgxCiePUdTVJlbLa4vEOKy+9XludFW1E\nWyYVtWVS6sT5IEJGF+oUj1mVdHJAunQxFlpYqq2P1t+uufBajY6MvRAI5zmd358FCTiTsI1hJ2k8\nHNjBFQhsiWA90jjAJCHaz0Q0OAORzTwqPOJLxm+CUnRlZB8gNrMOF6f9ElI0IrgnZgXijRpOeeZO\nXDwAwnhfnMY1LcoAFTSorZueea1r1Hz4k++ydTqQYdGGGuw1WiBUdKfH2aAT7nNzTingZSSaO79R\nM9LFSmxiAnDC57L3dwAYMAcOxhNcDUfLFg9J0up9fgzpzUzWcfPMMLQNeHNtJQkHfs1sQqOj3KjT\nlHSXzrPIkdRByoJBI+kyhHiJy9+iE+X2M0cnP4pKNIs55ZJR0LC+kq4eUTyQLOgrNy51pGJh/ldM\nVYn4ExjFAzWoomx7Q6mYeHbvsqe8QgOsh9zkFJhEgRaxNIvXyrA2k4qzfhf8inH+icfF/asREfJE\nfYrRStot1UfVLfbeLSzRlknlko0cekC7RQxai77RPFFPEYsMsVPPvK2jaoowvrZP9zHmYS+7TFsf\n3S536/zbOEdk+8p4KJibuVBg4QFOCjzrcvhEXG34nDRmqXGUC+VRb7o1C+cpM054uBc3+Zmn9Fbk\nME5mYcsT5rXhBqA4kyHeaAR1kJUYmlW1GJo1hUrA3KjcgNv46C4W008AERJPPriRF2omFVsVGQBJ\nI5hlyssAN26TGlIacjS+SDsqP45xk5tzTQEHkxmzBUiKcvNhprSwy0EbMSOUKYcoF8rMJ9TLLrZO\n0ruX5XyWVeIk0hdGbYHSwVzh2DkCGRB13RkpOZvyjosYPs80iQ5iyY/DN8995A5aEBH2aC2whcPD\nXzqwYGZKEKNGSxwJ4lpAX4ciqFVScAnUA0SRWciPAPcpNqBqdMqYUiANrBAz8zg9p3IEgC6rAmkn\nNvmPWkbjSKM5W1CsROXmYqKA+IM5jQAvHaKlE3ho/+AouC4zvPIOUy1ak/KjmlBSRpmGI1lRqrZq\n1XqqV+0aXr/GWjaM2vCx72qqteZ0qj759BlV0UoKIiNDkIPjZiQHlmy0lrW3i5jrFK4cyk2cIBTS\naNGS7XlK+MIDnCIEvQc2Zh/XfK6StJrFWpdkdFqFhyAGHDJhN/SMZ6JdqlNaoup+2HuPVZ/YAxr9\nBBZYi0UttDszmCKLKpQutgkq6Si7cT9qU2GiHfHISJPexeuL2ZAVH6rQg5/mwHCFTmUpt7aqfKQX\n0m7DrPQt+vxVaJvVWiZtq1HFjQOeitxbpxrjWRB3MRPseUx7aO3Q4Iv3ADWUnYOe4HXToqKitv/R\nJrIqJl0UroMjlaM2fQcXFbR/HRvEF7R1T1nCOkDYfGUqVRalgTbD06Z0ahuncQAnnTfZUb8yLpsh\nYYyEpAaBsNXYVpfqpikgCoRFUtRSIKyDPb0xf7TIvqXwqU8nyYbNSMsMhjS58kggk9XCsUm9guGd\n+PTnHTbFyLG4LGxgTipTU5BEvs9oqV9lwWiOQpL8I10MvgAsAQ7XrNpgW7tWezDE5yQiXXKTm5wC\np1FAfMOxtQVp7wBXXarfKEKQ6XD07OrRaaEuUAvlSLKPuhTzr8krHVXZ0VZpx5uatldj9XqH6DFy\n+GFtEiHQ6Q22aqfogoyBLm5EHDSbxaXSbK6/Q2Gsd3pFm67dcZBH7pq6nZu5UGBBAk62RYqGAbDZ\nbstbt4k5VsgO6cpqZzWevjXSbIsbd4BU/Eugs+rdF02ogRFfFscvkRVbLZ3Z0HAUah22vOMqsTIN\nmIJiU2mB2AgbUAXb8l5nXz0vPuOpd5CoSuVaJlVI5b2s86cBBXQEWBh0uqEpFzW06W1rYY2t7NSW\nNl5e0CY354cC8LXoq/JCEwj/1Tg9SEKz1LYywJaAkBxMYEs6SaUSwEdCmeohobx62XV60J6d89pj\nirrCYjRvNpzvlK6WsrUwND2mSgqQ9gU8CZwqmdMawosw0bKzn+fKtheooRAMdw0IDRDUIOeZW8mT\nrhbFqeBZrMTXWRvJpVJlja3rfKH1l3XMYIUt2wQa1diV62FBZ6e0gkZ+BcAHWFYFNnuGH5N7HZTg\noEB1KIue1LFD3JUr1tn3bbyGxOk/8kYeAKW5ySkwFQXG1XEZhf/1sW0UcERHTAbeqj9gsdiNMkN7\n7qBRba/yV/DV6qocdOx0zHKB3VLGu6ykfTrb1pdt8PC3fHP4FtU3VqHjhwEOTgWrUJ+XaM7m2pco\nHHXyqIwudjK4NItO6GKn6PlKf0bB8xX8WYSrskWVgYaMc0iL2ofvyi0vs87S5WpP1XNBWLPNi46z\nmlWdkaNY4coWKSGdx6WlJJpqtUOMWLOB/pIdP0ZjIFv9e7j1pDfe4LOyNj/fuPbF1rFETZUYtcQp\nLkqnawFJOr4ZeqbRJJJFa0g8tUz5cEDt/Uc7erRggwOio1pmJmk3toTKwCf2XnNbbYnOmF2zYaM0\nPonyi5YYCzfhiT0dpAGUmK8pTaUmIfnxozpn+cC+Med180MTaHQQxEA7XdLCAUxd36f5Ttdc/lJ1\nwCSg51OoKg+hb1SyJOzZQisW9CldBTQR2kvP98vU91mUBJwbeyMAzqSHL3TadZf/oMJuC40jeQe4\n1RtgYmuxTe0H5F4XfK9AAowSGteZTEny6VK79rJNApxLVC8EOn1xoxp7xYVvaB4dZuWgOCgLQHRs\nBF8pHrMHnthrpRZ1EiT3xtVAMv2HakQ6ybPLHqojjarbq6RJ5gSCpJeI0VOMY0x2ixcPMR7n5Xe+\n4kv5J1MTMjwvuZy/SFI+m8s5y3NiCGcScTVNnpwxeWy8KvmguYzORrIOiZ3Cmr/Un+uYol4RqvLi\nPTVkCO2QRgxkyy4QnPQFITgWu9il1evrR7R6/VF1RrU5PHVUXllvpDFVfdcw+rpbNdCgOZs+ihcd\neQeeHofwg+jLCMVC5rMoWUc1IUOCQvU0Px81ZOEBTpHDGUegEyHNEFSpipZmld5YDS5hLLYouIYT\ngUz1wYfIJ66BDaJGyb8aTt8w3hkDzWgI73EOn5XRGUNyo/AVR0HnrzJ5mJC8rroLfkKqO4hSaIDX\nkrR8Jc6rU4mOj6kRU5xo9IoCZqjbfe27p4six79uXvpZ7c9eM4gbn2Tnpu42WaQ7H+bTpISg+qJi\nAU9kNOlaRAOv+FAsVtDVgWm86Ff5pGGstguga9K228x3+j0xF8kPJeOloztlJe1aRbwv8FKjs1DR\nxvsyIXp4CrdeV1xrqC+UobRupeoK8XGwLC7nxXh84g/qDICToWg9U68KOsWjYDomUiA0au/s+Ii6\nBZPSwNCSlCsrRQvxoip37F8ZsoJ5lT6Er1iKVegE/Whgmmg0IxHkF5Bf6VCaJafQXiofhMBxd6QY\nmZIaxvGaRkfUkMVUFcWlzoGqlLshKlGAm0zKqxMowkCGyRoXLj/cX7jj3RdCMCwvdz5KpLRE/vCR\nOMBTRgT1mHhOsfF8Lgwxhon4nN/covEluTgX99PLLCPU5MBdXjUsE1UaNvHkPDTZcvI7hZFM02NY\nnZ98Rv2lLcFAW+KBbzHUEsqejqc6W/rEOwxWKDmHIJ2zsj5f6fOEzONPEB7+8gV7HrPy5p1w3dBg\neq0iv9SRDmvpulYnUrZqc/gHtT+7Vq/ri4PNpZusbc1t8hT7bEJDH1lREE5H/dKJVY33sPSz8AzF\nLbnn6XUaZJyMPX9MF/ChEWg0v8lfkIAzihLgJjCos7s5N71Iz0WTghHMPg9NczLBh+gmwpAVvkdl\n80ZFzxmG9KEn1zpKQDB0B8NwLGNUVPmR0PdKqjhDMDYqcAzHy5bIaY3lt1BB+zJmlRINh5KkBib8\nqXBpLHEmE8KeNOKfnGEXFZ+y9kYBO7fXT5NxXvBw9OQvTR/P82NULqIlX4hypV1CrGDDolUDhEbC\nmiuf6ApDazCn6I29uglpyd95TvPFHTxlEPxFrQj+51186qxMhws+gqGyOpPxbIgjzU3mXQBFB8Pp\nmkfjkXkNyNKX1RXn/air/jinJCnvyi8yBEzg9ZFAPP/UcX+RvaYTyA7xHHYQC1/UZhI2G0ooLI28\nFDRXmbndBI2v0OwTcpJRYS+ppq8YNJnc5aZUcWnkmhN5dh9KF8n1C2dZUhhid2+EjJ3y6bKJBUlo\nuFXvfNsnOSrR4fDhRX1yT/ykC7+6+BGRovOeRYJ1Fos/pshnZS/aIyvlP/AY+fDIZZfCj7THW7Ij\n8OSO56nsp7ILty6zkK0KotlV85trr5ujwGuKE3mVjAIJ2Y1Fc2jNnpN9uvOt+Tt+m9+b3fENk+x4\nTm6nsmt2K3f6D/4kCLhF5RzE1rt/9KBTiESTnvmMaeSPt5nidB/8yMzF7XTuz0UYWdgpQ7y6gR7U\n5gzeUKxpagu0kkAc59S8zqusuLFqw4cetppOMCwv3WCta29Vpdkg96zPEN+qEjtfeYjgBdLNN+4Q\nsk5VPSeT7HhP+ZzKju9na5/CnRSGeABADC/EbnCSbcgA2gbWnnjbzbOs6u0BYcyPyUpkfiKbbSwU\nJoXsFUJ0pWLFQhwBSjEXWk+IyGR7tkzyhtILTt/1V9DclRpakpIEr5agxSpcipZC4krgEE7kgvo8\nBWDFTcxP5JuGvrzFFiCVO9iDOBDpfPV9v2BgNSKRbjGl0hin8OBa8QFw9eju8aNnUsEvfnjGpZv0\nkO7J/nm8k74og0i1Myqp9jRmdvUc6B1GxvDof035iy/57zmkANRu8HYwTtgRSVYWkx55nWCysprg\nfoKD+XuJFKdf7umaaxrw16BHhDgxjBRyo7plruDhhuVET1O9ubcIjVqNiZDCbmJgyV0KSO/umJ8s\n0nBSxw7JZbq7yzpAclitTwKuGs6vaFSBARgHr1oAFkAyOCQBwZQ6j055ZSsz5Nq5MXTqCU1/nlDi\n1lAmwatT4/Z6QQ6eW6PI6uTjgchDttfjkTXSP7lzoZxekkJAjvHd0JbVfU/xQByZ8QxlCUh23HHi\nhGi2fC7PDAHTaaFFoXOoOL2jqHdNPYGwTBHxjg4NT3OSmp+fSxIWpV/KnU6ZKOb0WmXldo02rFll\nowN91rpmqw1Lszmu6XoT6sciy2tZOAYdbDXj5yILqFQNWgu9Gs3tFj5SfYQvngezIAGnF7YTBKLo\nynrozJXkqDuG2wraDLqqO8IL4QZYBBCGnYbftPS2IvdFX31LzwRBRwUFAgkcuqoZYYRdyfe3cziZ\ngSW0PEQdjBf+8F9kboj8njr+rH3pnz9lV15/rd34opcqPgl2hIq+RYODn3QRThhixxaGSDZR9Fle\niVT/uKub54k56vHnDzkFcgosOAog7xAW/qfdByrSsJ4aXW07j5RtuLRUix+0Yl5Tj/jeMM3P7j0a\nH5c5SKZzY4iFeBm545k5qYgxpJp/0wN3tLVNkk42z83E3FePQQGleyNOgJ+3AykhHnlDGofLLE3e\nYM+UuvBLTCkj/pxlIz3HaVKZ5XO4eY5ESO8+0OFAcwdhldZoM+AJrlByzJT655CUReXV6aYUoyiC\nJozCwQfMaC2N3yo7YYeDbcIPAvLCE4ubbjFqwyJJUE+LWKTVRmzbhqKt6NT8U2Gnsms76bTMb04X\nKOAUHejBcflWJQz3aYi22A5biIRj9tSOh+0Ln/2MVUdlDyN5D5JhKcFGmKZYtjVrNtmb3vSj1rVy\nmfwQJtnVxSp1QKzC1FJY2VE5VUlF+4LroQUs5cMFupgvKjmfMzsNF29/6Ov2F//tv9g7fu4XAnDK\nLz3KIlpX9xEg1wPFRoUPoEU2UMRcJNmFn555CkGROfBcYY9Lcsg9NzkFcgrkFMgo4Fou5BYyB3mj\nhkZNy6iA53BptZ/9rDk/kngB9qYUIS5uQva5CD0HxE2SConG5c28LNP0IWQZUwEckGYu3Nlz+SEf\nogdTr5hm5VpdHz7NAnWgScqAZZFCx2luQyqhQchnnmMOHHJ37iby3OTP8970fi4evbAIWIGRz3jQ\nvaHwyBRc5yK2RR8GLTcXWnZAJ+0uOyUG/FxWb21hR5RKi9aQfhU8e2UDOBmHdTFRGVGHtFsjGVXN\n6436DrAW+pzXrC5MwAkhmIRf0xY8lRU+bF3QZnQ1rbIraJgclnn0u9+2L3z87+ROYktD7OzrgqAp\nahVeQfMqGVPadsMt9qbXvcFBHhWyJOJCa+mUVSgBSsfoIBIitVNaAuLAux7cHgb0k46IA4fqspc0\nXLHz6V2aX1qwK7dsjQJm71CloSyNqk/cV1qcyQmXjQ41xA97RyErKLknbP3LkAfFy6QvBKPy5J1X\nxZSGdeaZLzxV+U9OgZwCC5cCyBcEBWIJTRdbPpUlP1o0hFYc1ZZS/k1yRg5qkimcvMI2WDVkm8tC\nheCCRY0SMtcDeu75RaYB5sIQKBBPFizOZCidiCQX3fYcxUnDSZSEzj32SkTG8qaYRIvUziKH6wtB\nkMsZDeRQBveoG3iaReIc+OGvYTxGsqgHFp0UJf8jVQ03z+VJuVK4lGE2jYs2pm4az6QjN4kC8Ad/\n8LpTUPSjYyEqqXyq4k207d4Ez6bcU7AL7k4+4Vv4APVb8ENNCjjWqfh+2D7skPh+fjOwIAEnuLyk\neRRLtYfjsuLV1lrUJF5pHtnCCMFZkFbyTT/6Tnv9m94qwVa1Y/v22m/921+3EaHHD/6PD1upQ3Mw\nBB7LInJLSUNKWp2KNrMmVbLEsYAsCyNkx16RKo/R8SHZidu0dUJJy/p8Y3nnPAlnxUeRseIXQeXD\n9wKlj23/rrW2ttnVV12j7xq+HxuzdracUBoQMIpQAJcN7HVpHilgEt4GL/seoAK6Na2WL2mvQQe5\nAqDjFYSTemElrZ4DCSscH4D3Hizx5yanQE6BnAKJAqAanpETdJDRctLtpsFBtIfM4tR4OrDsnRtA\nJTq+AYTkjI4tvs6RiCF2kkBwLj0dEEkLiwylkZesHZdsTqDTHeLnORuF65ETEA9cai8ULRRS5hGp\nkumMkjFfXy+ATYCbgKFnHy9ZgmZHD4WR4sErxtuMADgOChWwhx1fn+OvSso7DFUbHNYxjS0darOY\nzgV4IiWKV5ERu5sGQZ5jvIvdO2UvvqesNNLpW7/xKp50eopDylJ5wg4V74AszvzCvkwX8HySVf3R\n+XLMARqBH3wUF/5wRpnXjC48wCkauHBgZXpltcTgJeq8L5OwQhGuCb8QUT2UsuYoFVq6BBJH7UR/\nn/UMDNg1179AG7ausUKbNnEXij954og98uCX7Oqt19nJ7kP26GMP2eVXXmMvveMVQvrtdvTYIXvi\niUds5+6nbcXKTXbZFdvs2cN7FVeLXXPti1UQnRLSYwpnn+1/5lHbt+8ZW792tb30thvt6OE9tmTZ\nclu3/nLNFWWT84r1nXrWdu18wnbv3m39A4N21VXX2U0v1J6dHdqbTwLhwL7dtnvHE3btdddJQ7rb\n9uzZZ7fefrtdd9026zl5yp56/DHbtWuX4i/btutusBtvvtVKArWwBoyUfv0x/8kpkFPgIqdAQEuA\nmzctajyralS5xqSuQXvjOjAQiKQnq1RHkJU9J2x4eNha2zpt5eq11tK+RB1hQNdECYOvZEL+xNvM\n9rjQ5WAHn6RTnWnJyL17dlvvqVOS1Tfr1LIOfXNpHwGHr/rzVHFOZYcHB1jKb9JKogQgDQzju75S\n7UStMmbdfd3W23dSGFN7p3Yus5Ur1yiZDHPFnEenqCLBt+dBv9PHiRuAKi74DcoQny9azYALX2sg\n/szMRL8zx4cio2b3fP5T9rGPfcx+/Cd/1l525yuVYFIun2ob/e5rFPwpS1VEfuaww81zS9/8hUFM\nKT8zpZkyobMFxqxm5RLgS2F4IAy3q4wUEKOpzWamsHGb0sHzVO6nspvO7Wzsp4sPv2g2MeipWBzk\nGFN1L7aJipQgG5rDcA/z8LPwACeE0sWm6n6WsqGhFNhEJkiDGHMvUYHznX580Y4c3K/N20/YVVdu\nERBtkVutaRfvbP/2N+yP3/vvbdv1N9meZ560wb4eu/Wl32e33fICO3joqL339/4fe2bXjozMNdt0\n+UY7deKkXX/Ty+3Gq2/xLZl2P/OQ/c2H32f7n35YidJqLxXmPZvXSWgfFlh8lQSJ5oAK3J46ddT+\n6L2/Ydu/c78AqEpagq5c7rBXveaN9sv/16+p8Gt298c/Yl+4+1N2xVVX2ZPbH/V0jgz/hG3etNze\n9we/aw996xtqO0JLWmrttD//Xx+1jZdtEeBWZtR05CanQE6BnAJ1CjjAklz0NkTygQbTWxE1ngyb\n6UPM15Lw1Orl3Tsetw9/6AO2T6Cvu7vHOpcsl6x7ob3nF3/Z1q7bKNACPJ3azMUetzTwwJ3kj5Gh\nwb5e+4+//Vu2d9du+9M//wt16m+UCiEaxKliTX6bv01lx3e3hxDxUPcC0B0fH7He7qP2d3/1EXv8\nkYftyNHDsivZunUb7OWverW98a63Ovj1/QkJixZa/z7cmsKuh9h4iLRAcACKQKauBHyR2GNjHFCi\nTXRa2CnAC6bhOXuKMCZaT2WHi7AXgBZwevThb9vR/c9Yh3Z2ZyoF7U0gp0y7pVe2+3LM3xT8mcNu\ncpg9TuV+Kjucz8V+Lm7PRdiUkJ9kpoLxfW9VP9ifMxYQR9nRy3CejQpFtKeZc5Hu8x0Go77FbC0K\nWxhWGdkQ+oSfqwLT5L+kdTAlKcqoqfNpFiTgjLqjyqvzn4taXVXQyUIFFgxRUlQi9ub0miRC6v7k\nk09KE1iS9vJKMb0gqFaMVzVJ9onHH5UCdMCe2P6gvfpH3mR3vvwHvVc71D9k7/vD37GD+3faO3/6\nPfbSl7zCvnTvPfaZT/6NDUkzecllV/tc0L6Bw/aXf/771n10p73+LT9hL77l9fb4ow/ax//pAxry\nHpewvlZpGZB2ssd+77f/jT2zY7u9892/aG/WUP/RI3s0zP9/2+c/dbf9/M//snr3Ndu7c68N9Q7Z\nM089Y+/6iZ+zK67eZlu2Xmmf+vjd9tAD37Ef/bGftPf89M/Yyd5+++rXH7BVqzYJbDInNJMl88kZ\nzXFNriEkaDrjn/gJN42n6Tzk9s+ZAkFqrxspLC8iGk1ZpCt9m+6egpnu+/m1z1KZElFvn1XfFXH9\n9fwmYpGFTjMqI6CJOAzSyU7aDLaMC9AXjemTj2+33/k3v25jkoe33X6rXbrpEtu5c589/sQTdvzE\nMY3UaCRpMpUVYKJ7/ZQVj69h76/uiB95cD/AroBf4VLvavTG1Om+/IorbN3GDXbp5stDniuGFEcW\nNLe6bR0wiaGb3bmjzKV/ASTgJmsXklvA2CMCZ3/4H37Ldxa54uor7bbbXiQwWLUdT+60D37g/XbH\nS19ml15+ZSNInrJ8kBLnQM9eFqri8ie5cbBJ3O4uciv1pv4r9pu/8n/aiaOH7Pf/6D/bhsu2Og08\nZ6ly4p2I+PUAI3wHrRGkf8NJ/TtD57qeeGy7LVnSaZdettk7ClU0WJ4qAITCcYBLeBFmBHTx/noR\nqZ4AxqoaCfD5mw62oI8IrFsNLXHGP4uXUqp5Di6lovL8sk84G73rwBphifIYi6RlfNpfPM7n74IE\nnF5ZxADe01QvhOrNnBvmILEYyHue+u6rHsU8Tz690zrblwkobo4VWarwZak4dzz1hHwW7M5XvMbe\n87//qo5aXKM5m2P2yX/8nxrS3m4vf81d9qYff4+0pa32ytetsl17d9tDX7/HLt96jWBuze6///N2\n/MAeu+NlP2iveuO7Ff9a+55LN9n+Q4/bN7/yedt8uU4r0N/Xv/hZe2r7t+wH3vBGe+2b3yxhM259\n3f0Kd8SWa4V8S0ubD+McOPCM5myW7G0//tP21nf9lD/XKhU7LG2rMmMbN2wSMO3StmAr7XWvv0IL\nTCWk1UOhzwotqBrzZ0IQUhNp1niLBUwSv3phiIg0xakt+kjZkMJwqGcEI3a4U0Wf38Qr3ovHMGjo\nxhtlCoA92LBBY6VvFJiEkPdmXaDCUVjDU9QtwAGGSoV7f5m/H49PcXNAAHvGITDR2ihdzLdjsDAG\nXrOGfv5StmBjqpeXUpgOkqD43HDnUvn6/C0V6D2f+7R1Hz9o7/6FX7a73vbjmn+u86SHRu3w4W7b\nIJmG/GFrOTr1o5qjPjzIcaJV69Kwe4tGWygDtpxjHlxtZMxG1DEX7rG2Tp1O1daheNpUx6UvkuXY\nQMWGRk/oWMUW6+jSCU8sWNBI0PLOlfZz/+qXrNzRZm1dyxWmeE1Dv0WdilVRmIMjA3JbsK7OpfLL\nPHyO3IgFTpWRYXGz1uBrutSwnkcGNCWgtdVaO5coXUuUWe1HSfrEv4wmw9Gw8q6d2+2//el7rfvE\nCXvrO35e8vlN0uYKXGt4/dChA3bvl75kS1evUb7hMNUFAcVR5W1sbERTDdqtXdMNUATBjZwmx+LO\n0THt2dhW1HYzbTYy0qu1A6PKj07oUtqod8XKuBQX3bbr6e/ahjVrbHmH5uTLnbUvZ8qgVYd0cpb+\nykr/QN8JeSha61ItjhW9KINSZdCGh3RpXUCbaNvWriOYtRaBhS0tovHoUJ8d3LfLLtu6zdau3wyF\nHJD6vqsq89h3lfrO8HpuGhSAx1lUJxuVA6LQsb/sHJDiUHaLmWYhIalXMZebvLiWXznnW+SNLpTc\n+EWm588sPMApisAIIBQqJYt8nDDRRXE7SIk0YZL8yGif7d+9y1avWK7e3mXwkRN1oL/Xnj2w1zq7\nltnbpU3sXLZOQFALdDS0dK+0mWwk/7o3vF1CZY0qsE4MUi+g51Sfh3vplqtsdHTInnj4mxJ8Jfve\nO1+rY4+X2YhOIyjWRq27p1tAco2tXbtRgqti937+k9JEDtuBPbvtz/7oP9lAd596z0+qvlfsZ37p\nV6wsQdS9/6SdOHnUrrrmOnvNG9+ieaZLbFSAtFwuS9N5tY1/ftg+9ME/tSNHDmsx1Nts7SWX25g2\nqS1JGHEGbmKU+WKNiC9iS89RLGJVykJSnc3x2ZMU1qUORzlBf3qK3kT5F9h7vtPvyblYfhzNQ2Ho\nTEnQEWDBGza8qyxofbNvfKeSATV9MrnK03dwANxpp4X53xaEtGGovVGD46QkmnnliVM/tAgwFsXw\nPTdOAco0kcPn8HnJ6xNlG39BqapGX57SY8Vuv+NWbXbdIWBUsnbNLd9y5QYbk1yDTwq1Mdv51Hb7\n3Cf+yQ6qo80o0ZYt2+wt/9t7NN1om0bcx+344QP2D3/9Yduz6ykbEijauHmTveXHfta2XvkCgbFh\ne/DbX7J//szdduz4YV/QcvMtL7G73v4OAbc2e+Q7X9dozj/aD9/1FnvhHesl15DxVfvKFz9j37jv\nX+zQ0YPW0lb2Dv+PvuWnbOPGq8SLcKlk4wf+2NoU/52vfJV95uNK375nbemyLrvrHe+w61/wCoke\neEXkgOllSuqs1yRf/+kf/sqe2f2UvfyVb7R3aL5jsavT897W1m6btlxt7/jpbRpaFHiUkuLYsb32\n8Y//re15+mm1Bd22cu0Ge8lL7rQfeN1rtV6gU3XKpKh4wj72dx+0H/zhH7Bndx5Sfu+3QbU1N7zw\nNnvzO95py5avto9/7CMa8r7fhvsO24nakP3XP/4DW7p2nb3lp37B1kuZ8Cfv+y0pFy4R7S63e+/+\npJU15P4Lv/qbtv6SzdYnYPyZv/+wPfXUo5pvOmgrV6y2l7zs++xVr369AG2XSmnM9u/dpRG2EWmL\nt6pM2tR2OXQXmI0yR956G+poCrmQG1UH0STaodBiiiaQRldzTVnslPIsqW7HvGTJfu9IqW4gKhCz\n9HgCZT8vWV14gHMOZKAXu/+ZXTY20m/rLrnBli1dqRXnkHxcAmmngGSvz1HacuU2iVrBV0mMY8eO\n2eFnj1rX0nV27dUSkmrMSlrBfmz/bjv57H5bteFSa1+yzE6dPG7P7t9vGy7ZYss3XCGxyMKgMes9\necB2PP6QXbF5q62XRvKIwjt29Ih1dS21FctWSni02OatV9nLvv9VdstLb7e1mzar4S/b0089pWlU\nYxIedzpYpaFHgNUU/2vfcJdOBzlon/vMP9pHP/p++8pXP2///j/8gV1xzdU2pn302FvOMcUcaHPO\nnIo/I24lVs+lYo8qKKdbQE/S5v1r3vQ59aIEQmVfEpjGL3R3z+csUXlAEykAfTGASxpcykqXiC89\nisqhV0AyA51eP/iOezXmApmMrvhd23yxYC20oXyfZ6PjIbWfj1JF2tFYkQVNn1Htc7t5Ts7Cjy4r\nay/3LLVYednyDtX0ogZm4yWbNG+8Zn/z139hP/9Lv2GrV23RN83kUvmXNBpUE4C593P/ZO8XOFot\n0HTttds0L33A/vmzf2+Pbn/SPvSXH7MTp47Y7/7mr9lBAZ6bXnSzrVxziT3++BO2Z+fTdsNVN9vn\nvvAJe/+f/Y4WIq20q69/kUZ5eu1rX/+SvebNPyKN4DJ74Jv32AMP/Iu94odfqyKtWGVw0P6///5f\n7O5P/I3A1lq79IqtWqB52D77iY9oxOgR+49/9BFrXb7UenuP2H33ftw7sZ/+1D/Yes037RBg/ObX\nv2bHu/fb7//JNbZ0yUqX5a7WFD9Xpal8dv8O+8o9n7OVq9bZT77nZ7R7SasoVXEtYaWqMSztf0en\nuaxRtD07HrN/9+9+3rp7D9uV16gtWbVCw9bftgfu+7zm5A/aD7/p7S6HH3v4q/a1ez9tjzzyVRsb\nKthmDZUfe/ag6PCQrVC+73rbu+2Q3p/ZobUBAtQrVq61U719WsyqtkFap2MHdykvn5BmuMOGhsds\nk0blOjuWq22p2e7HHtRc/t+3Q1qcevX12wQ2V9iuJ5+wB7/xNevpPmlv+8lf8LLdvn27yraoxac3\nqH6osyhwfRqGqPMAfJCbnAILgwKLF3DSZqpS7dSwORrAa7Zdr56rJA2tlD7s273Te4E333KrD//6\nMJ0arl6tUqyo575GwrJNI4uj0kwWx4ftgfvvsV4tBLr5+u9TT0B6Irnp7z1l61evs7YObQzLqR0j\nQ/bAl++xscEeW71+vbVrGGS/VroPa/rMZq2E/43f/kMraAimqF62Bl9sVMKgVtSiJw0ZPe0aBrMb\nX0B6YiUhII2hnDb1XN/57nfZD776DvvTP3q/3f/Vb2r+6X12023SAkjDm/SDaiHm34jGRBvyqyBB\nyeSG0DjFllEBNV235lo06K/8OZCpWntbv3CQwPXzNGdk/gk2/zGGTjPKKZUU5eGNkIbpyu0MoQP+\nKcl0RTobe9KpV6xOUFeHysu10/OdDxhN4FJAgTz4ML8zHbwzKDuWl2Ampt+tLtYfL2CVNCSR4TWo\nk2gEHcP+R7Qf8cMPCix9+Ut27HCPvfNdv2i33vo9Gt7WfHfV1ace+4595IPvl+Zto/3qb/6uXaOR\nGHbn+Nf/x8/YoYM7BEx7ddjGQ5qm9JC99rVvsP+fvfeAs7O47v7P7r3be5NWvRfUOxJFCExvMsaF\nGEPcYsfEjU8c+/3beRPHcZy84WPndRK3OK4v2LFxxcZg0QUIIZBAAqHepVWXdrV97927/+/vzH12\nr2ClVbMx9o6093meqWfOnDlz5syZmQ/f+Wk0bqW2i0l6eVkZU9BWexjTIi3v/9XHPmNz5i60Zpam\nDx49YmWsLnUlW2371m0oBUqsdmAtGvUuW/rIffbbX//ApnIax4c+8nEbPHw8mzb32hc+/wlbz4ki\nu7GxH1s+E16+jSXpdmtpbrVFV96ApvL98O6YffzDt8Gj662x4Rg3qMAr4asywJCuE/2+vbjiGevg\n6KDzF1xmtUNQGrhgxvoYPDebiZWwoyly/dFD9s1v/Ac2ngcxOfiwLbrqJpb1S205ZlN3/eNn7Nmn\nnrBFV1wL7KWsXK124TzZ2ml3fOxTbBy92B594Ff27a9/CdOoOqg0y973wY9aW3OT7fvtQXvfh+5E\nA3s+5lOFVlCYbytfehHFFIp+sgAAQABJREFUQ9La2Edw1Zvfbje/9R2Wy9FGFSWV9sUvfN7qWCp/\n1+0fsmsWL7aC4hJgeMS+fNdn7XG0wO941x2w0pjt2LELuTobrfJYtS7/5NJE4F+aZoTvyNej9P/0\nY+B1xsAbV+AEcWIuW7XLnAFy/ISJTPqojjQ4MNDtCJzqiMPHjMNfy8DoQ9HyZKPFQSJkl/kBxrZ6\nnx2uWrHUHvrNz2FECRs2cgwMHK0QQqeWs3WERmvrARhCrq1a/rg99tvfeJONQ8BFNWA5Bfmuydu5\nYzdMlSAt6cB0ZeujnfTq91p2385xRzGWToaPGuM2TRLCslie37dvrw2prbWC3BwbQ9nTpk+1Z55e\nznIXdqg5TQinyk+CtIr1Hy//d/0TmJhKTL/x0GBSlK8KBV2awJGG3sEC72LfqrLqpkPxhQP5RgPi\n7xP+3zV+/lDyj9AvtDuuo/biWzaaGnhKZJVCIyhOcOHNhyplgOtuo65G3jPjhvDf/a/EZmkzOdLM\n66DvwJ66sjgnl69wf3SwP+XzT9oJH2q/0JLhVxQQXPQMmOxi2XXSjHn2vz93FxrIL9n6l9faP3z6\nr+39d3zUrr3pZkx+Yr66Un9kr73lbXdaaUWlvbThFXvqiV+jXTxqYydNxo4zZu1o5NS3d+/ehenP\nAZaEK2zwkNEORQpb82Zm3sn2pG1Yt96mTb2IVZ8yVouw4eR0D51TvHHDFt8ZXssqUlPDUZbX/8c6\nko32V3d+EntE+KkVIfCWc9rIdGBcZYcP77GxNsV2bNuOTWSbjRlznv3VRz/JcXQDsSNtsgLs9lMx\n7Do5DSScIMKDlRWYvXW2d9gWlqW7gHcWy/qWXQCc4k+EMyEWB5PZRgozgpfWLLdVK5fZ/IsutWsX\n34qCoYbgLLS8s6yirBqBtpFVtISluBZw86a1mArk2K3v+oAtuvxGHwMquYtb5zQXFDBpY4+A7GaP\ncsxdMdrZYaNHWUF5DSVjk4pyY8vGLZSZbVM4Lu/9H7wTk68yDV/23FNL7ZXVL9jkadPtHbe+31LY\nj8p/xsw5bA4qsEMH96haCPGtbHbdZVUs0VdWc+83dfCLQijB21/fTgSigR46cK/+n34MvM4YeEML\nnM2cvbl7924YUJUNGjISRqJOZ5wx12o7d+z0petqll+8Q9J5Yxz4XouRdTVG41vWv2R/+7d/Tews\nW716re9eb2+LMxOegHmi7JtKsVsayXLRi3bv9++CR+XbljXrGfQ0KHJO53lTPe2w2oE2bPhQW7ty\nlf3Tp+9Egzkdu9KEvUCeg8ZOtI//9f+yA3v3uNH6iJHjEVBLYEjScBo72evsX9jdXlycZ/MXTLSj\n9Qft1798gJ3rw2z23FmARn2y252BaHH09XOwMBhZYF8IMbyk2VsPT/NA3cKEI1AbukKatETzusL/\n+mHud12y0B7aRSVB5BkuCJHSQNM10sNQRjDp+Ee7Bpd+ptsxM97v5x3YXRhgWb3bCSZgxCRFsEYU\n2B3c/xIwEDVhGh/id26TCQEIZ7JPt1ihTZh2gf3r/51iv/rZD+1/vvc1u+f7X7HzZkz0nc7Pr3jK\nNzn+v2990+753veZ7HbBE/PtAuzXb3vvB9CC5tt5k+bbtDkX20srn7A77/hzu/za6+zWd38ATdwg\nhCjZxN9k/71jnd39rS/b00sfsz9793ts/sLLgaELAXWPNdXX27RZ86wETeGLq1bY/r27berMC2ww\nm18ScQgPG1DpKFvZsCShsLiYDTssfe/cttHp9Ka3vcOKOPu4E0VAa1uT7anbYedNnYbgxXI6CgVR\nuehZNyol4cENCIpUHFvQoQSxGU3KAARDTYq1jJ4lBQSTnGefeZyeI+3pVQibJeTCqhfKiwQXcbQh\naObll6AsyMcG/5CbTw0YONSuufZmhN1woUgdS+CpZIeNHjnMN00dO4LZ1t5dLLcPQejW5ifB1G6d\niQbOXt6EgiLGiSTYtSJsygK+kzHl+ZVLKa/Jrrz+BoRNbr7WZlHgbGltxtyqCxMwXc/caY3H6qyu\nbrsNHjwMzXEVfmSvDt7d9V9FDAT1u34M/KFg4A0tcCaYxSZZYhiLzY3sdLSkImYjI/g2mMlIZsSV\nLJ13EUe2bOwFZPm6wm5/3x32na//O8tI61nmKLZrr3+LNTQ2MzPfyK6/8XTifPwrmf2/C8YMk+CQ\n9myWPS67+gY/S27d2jXYG42lp7OlSQbff/Vx++7X/8uXzTdoZzxwyBb0wvnno0zttGYYaA6G+pq9\nxuM63ils0ihlSX769Dn22MP32TbO+5QtzuQZ4+29778Nw/lh8BB0uMQNS6EiGTjQ78n1VlLffmJ2\nPQxP8YOgEAHdWw5RWP/zTDEgsSK4TPyG98gneh5fRqYIF2JEOf0+aa0b9vSEsadsQSO4MOHQLCct\nVBxfhz/lL3DS3WDqaTi+YUvcsIaQiXZP/FCbfdwfAamgKMduuvlmW7fmCXv2uedtP/aGcVZq2tkA\nNHLsOLvtXR9n404BmrdcNsBUW+2gMf6tYqrgpZ/6zD8isN7NxqD77ac//LYVlxTaLbd/BCEu1xZe\ndpXVYsd47z3ftVWcR/yf//bPVjt8pI0cORHN3jpBZ2NGj/Z2PMCO+Zb2Fs7inObnBPrCELA2NB2y\nXdjTx/PRdg5m82ZLB8qDrWj0amwcy/x+TjHxtu3YyIpVwkawwVNnLyPSUUfxSmRKSZQ4aTTl2jta\nEDsxyUCI1dmLOv5G2k2ZPiG72s5dW+DvZiPGTiBNIfIhFv8cxbdzxwZrYkNQzaDBaC9L4O9r0NQm\nWYWaxSbUEmtjo1JXRwohcgcCYTlHS41Ci5lvDSzzHzy4zyZOvgz8oIUUaNgmt7QedSG5dsgwTLDG\nURYaUeBrw372wME6Kywp4MioIa4hlS11Fjvpt2/fjilBu02fNoMagp+jB/2opRmz5wftKFXVBCNM\nK1Vv5djv+jHwh4mBN67ASb+q4ZaMf/yXL3HIO/q/eLEvyGmDSgnM4PP/+kW3c8mOF7ltpzSE2KnD\nWHJs9vxFNuf8+dZw4IiVYpjdlsyxusMNCK9aRmcJCIGRbeQ2adoi+9t/mskuxHorKStl6YajL7D3\nLK/Kg+Ew+4bFd3Fj0ciJs+1zX/w6jOYoTLTJd39WEV87QbNg/GKU//HN73JwPXZDcY630LwW3lBS\nUm1/ecff2O3vfTdlopEtj1lRMbNmHc7KNZx+/JPTjZhI9CcPMZbIZTKYyL83P8U/U/+Qb2bqMKWW\nBVSm05em2hEcPaLQ8WVnporiKp/T8T+duKeS95nCcaK8f3/wZWLZ1csCyV0Eg56Z7aJA+b26/eQv\npzD99YUTxYlcX3EVr/f4Qfsqgcn14xnxBHMaDlfjKI9+14OBNM67mypQgoRM3XSjw97jTGJTnRxx\nhIDVxYQ7C8ajDWJJzHxi8Rz4WKm1csxQF6s6VWVD7ZJLr7d2BDgOB0LDhtDD2X1Km8WGri60kJU1\nQ+09f/E3NnnKbPu7z9zBZpnVpOeYoIIKJsz53Kx2iU2fNM8+/7k7bdnyZxDSttkodrqvX7cWsLPQ\nkk6B+6GrQ1jUNcC5XMsoe8sYsEmjt3z5MgS9bWhCL+IWoEEIfAdtx9btXI4xzGpqOQJIEiX/13N+\nqLSgE8ZPJTcu+3AtIiY8bNTR8Xk6TqgGTaQkypXPLeWkkUXUW/UnOYJmEttOKdRzC3I42qjDUZqH\n1KlVMOGiI3HYHvjNT4ncZQsuvoRznuO2ectm4O6y86bNpGjRKooNNK3btm+z8uoB2PWPpG55tgOB\nuSPZbENGjXLNqMu/5NN07Jjt2bPXJnPYflnlQMYANi2pneiHHZgixBAydSpKHAFSG45k67nkN79B\ngZIChitcoSI72BSa19GYikl50SFp1vsGSAET3e5Vn93+/S/9GHgdMfDGFTiFNDqmbuSRZtNns3Qy\nLTs4T8JIW5rGlHo7nKwLWx0Jckm0KF3cVS5mVTlAZ8Fp3yK/HDKfxUw/IS0LzFXHdSRgPLH8Ws74\nrOZ+VbFJNmCQZzZlwg8QPungxGffowuTBRWcO5fFMgd5Iy7C6yhLM2qlyWMpXQM8DEK7FXXzkIzX\nOwmLc4ZbcTnLSbmNfGsmzuBAvMBbxdjESDKYiere7Xrz781PCU7f/7Upgk/Qp2gQE2fTnxin3l6b\nwgM8ThQWPUNIz+/p+J9OXJVwOvFPJ+6J8j4XeZxq3gH3Pe0QtYmegiNqp8z8MnXPiicXPaP8gm/4\n7a0+vfkp9pn4R5TTA3M44kl5RX0ggi9A1P8bYSDCWfhWy+pMSrl7vv8d24em7gqWi6sGDUNb1szO\n7fvtxRdetolT59iwEWM4cxLBFOFl47oNtpaLLQaNGmUtna12cP9BdnTn2USEmyefephzI3PsvAkc\nI4RQtXfvfnhUttXWshLT1m6//OXdmAHNsaqqgVxuUW/12DDGuaxjxJARaE+bEbR2cA5yKcvbHF2X\nlUM8dpqjNXx66RK7/Kpr3IRJRy1986tftFxWg97y1tspL47t4k6uwjxkA+ZfzG5uTiGhTl0I0tKY\nxhAQJ4ybAo+H/8fRJiBcx2R+gYAWg5fPvnChPXD/D+zhJb/wjUrT5lzkh37v2bGNo4t+ysrWzXbN\nDW+xIYNG2e5NL9tjD/4CM4F8F/B+9qNv2prnn7cZcy60OfMv4KzNFtu5aT277fNt2DCuM5Zgy+pW\nR9NRdsNvthmzzmeVrQb5lKuQ0VYaCoOW1no2Pm1lR32RVQ0YwHXI+1kha2Jz1CiW7hmf2Oyq48hy\nsbGVXevqZ59ik9BjXJ88EHvYhD225AE2LD1iM+ctQEFyMQNOFptJJbgj9CK4a/yQ1l/jBUhVc+PU\n7vj7M5rAyb/f9WPg9cfAG1vghLF2MSv05TYX0sKQqf6nDUSusaQf6vBonRepO1LDTkbN2AnXQMaM\nEtGyR1xiuYW968Tjyc502BjhnPlJWp2BhvTo6XIoRGdN+lKG+rtiqYx0R9ecPaklG/EBweLCppbS\nYQYIv7Jx1CYceCXwA1ecRSEJtfjr8OJsmHKXyhNTEQNJDyB84FRBuYjJyEfv+g1h4U16XX0HJuRC\neXecnrTKqcdl+keMK/gppyg0giD4yDcKCctE4UvQ6E264Ci8p6Twdvr+oexIYBKMr3YR0+0NUwpT\nmVG5yi2zZq/OK4p36v7KLU0ZlELbqv1wUdv0lHc6eZ88bsCGygnxemqkb/nLJ+3kxQAV+USYzHyG\nmCcvM8ru5HGj9olKU+zIL8ohCov8NVCq//GXBiHE6KHvkDITvigP+UV/6fwV1D0gp/3+aB4RDtSe\n4IfPyKcTgebIoUP2KIe+L1v6uJVjdiSbwEbuUh9YO9Le/5cfZaka+0vizV5woT31yK/snz77CcIG\nWjs2ifv3H7Err32zTRg9wh576EFb/cIyGzp0ALwrj+sx93MO8TC7afEtnDvcZN/92r/br39ajglT\nNad9NGCfWWeXXnG9jR07ipuM9tuh/btt8NDBrOCU0RYIrpNm24SJM2z1qqfsM5/4GKs9JRwnxNI2\nI9Kf3X4Hu78XQAEdtnGzluK7bMy48RheQh8IdNrItH//frStg1j2r0TOxBITHq+Ki5fyw2Q/xk75\nBbbo6uvtySW/Yhf6V6z83p+jZ+iyZgTiSs5RHjGKnevYnl591WJbCxw/uOe/7AnwlODe9bpdO23s\nuGn2kY/9jZ+T2cgq1x7MD6o5m7O8spaiUDKg7dy1DY0jy+ojsc/PQ8jVUn0tcbIRfB/4xS9sxdJV\nnD5yAxd93MYh9JtVFRs9biztpLYSv0fLysrXwksu47ilB+1nP/6hrWR3fVsH+MfGdez4MfYXH7zD\nj1dKJtvQprKPABvQkaPHOmdRn5XQqnxF5vr15x8tvXslz+gn4CckBf29ODy980Q9qJco/V5nhYHY\nZ3EnyqGrs966WvYxPKD9O1GkE/jrxohd7PDeArORklGcUH1AHeRUXFaqyArjIyy3C6bCLj0tm0dp\nw01DYrCecWCy5N/9L73EIGFPMXREvF9lpW8EON2p6mZN6vD4YSoDI9bhx8T0NMqJ5XDPB2EJQ3PP\nG3qUbXsxt2vk6cVTMDiKeiUQ8uf5k9brKpg8P4IUV0lwbpMJk9QMNdhnJrDHaSS8w+NEmh1FFwzS\n1IYcKIf/urpO+fUM3JStZSWHWmdkorVFoHbtKUzb79olMwm4yiCkVfroj9duF/kFYB3LEqRdAEeM\nktbW01Ga91rie/2Vr+CQk+AgoVl1lJ/iyj/KW++Ri/z0zHQn9heTFjyCLQhzIW3YWBWlCzgLOQqG\nqO56F3yMbO6n7yhN9MSr20V+ema63vyBhvpqU0OSAU8HDIMB8A6cIECieAz7sDB5CPjtKbvvvHuP\nG+BXbqH+kdAm/KTrFjoe31Hbe2SvvjAYQeJ04ZOzCJbe6qiwyD+Kl+l3PJ4Ekzb9hDrT0RwKxVH7\naIKmf/IVTetJfP8HnXn/E3xAxopA1JeIlnYRHMeXqbwDLkLezOasvaUSqAWHkoaS/I2kQo9cWU69\nDSisB4LAF0KsdGCI0uuvaLC1s9QONBVwFFoRX3Kk8/9KTyHpd//yfhNqqZid2a226+iT2BbqDNIA\nncOkNCGpjasYaCNLqrrDlU5O8SNPqMuak2V2sFlwlNBX4zYN27/BQ7XBpJJd0xV+f/kiBKA7PvYJ\n17Qxk/al3Flz5lo1h5HrVqFcjnarGTjEFiy6Ai3gYja+lLuAk8vmmWxMiCqrB9qChZfZhxDGBg4e\nyeaXUuwfJwIGB6jnFxJ3nF3PrT6L3/ZOjkXjYPIObi7iVqO55y/gfMlpCEx52L6zUfKCBQhS1Wgy\nC9ngWWPT511gH/zIp2zeBZeiB9BCO0cWHWvG1GigXXrldcTB3AntZWeCLT7siJ+GVnHS9JloZx0L\nwgS4ENL0QqvQD8+ff4mNnTCd5e6hVokJ1qgx4+2ya25C2L6TE0Ow1wfBQ4YNtWmz5wJHMZrWIjb7\njLKr3/xOe+8HP26VXPmpvDoxQWjkIPapM863ybPmIBij6CDtscYG11Zeipa2GjtTHTE1kA09pbRX\nHudrjhw9yhZefhlL6JV25GiDC6w6xL2QvQOB7gK9D0ITPGXGTGxsixBwC2zYqHF26dWL7YMf/QT7\nAQZTLXgKNxo1c5zSVDZuTcOGMxsB1238Q4WJE+quuFE/Eib6XRoD3WQScZjAJ5zvqY+KX3rfjHjo\nGxVzooPA150D8i6eVlt8xIpj2kiHc1JJIyT9SMnkJh9Tjzi80mkozaPS4SHh2f2iaNPw3btLdWyz\nzkMvwKZ12PdpOHJMsIz9dN0mW4KhdQKBzc+2dCScWk5ZCWxi8hZaUWqWDa2ttHzykzZRA6TuCnW7\nlTQzPilkxFG6FGf8uaAqIU9CJ4lkSymNZ3NLlx04zBFE3gongQ9UAYYNrEYY9uOBWBIBO2KMrhGV\nEMSRF9qr7ccZnRQw2Jjgh0lg3WOlVfuxqcLWSkQPXEEoUgaCVDDpHEISUJ4OXncvBGcfzDx+LqEx\ny0EDoI6TYClM0MlYXiUoTHlLAPJ6egZEPZGDGzomhGtSqOQw9Aedqb5cm+CEqUwiwUWpgm1SOPAd\nHKdTBqAV91y4HnwEfCl34UqQCmZpiCXQSMADA2lcZenOYdo/CJyKF3B6bmATDoQrYVnipSCSfjy0\np7CvckIb83pOXGiZkJXy159qFdpNEOiqVR+EvC092Gvd85Zua2Ulp4HrnDhhQPSotmAx1HGvd8EI\n7XLhgooKtKnCVa5oWk5UI7zJCb6AuxAn+Pb+q3xUJk/vW+STKrTGA6MYnLGP9qr11E8LGrpbWamG\nF2yzyVXbgaAD4Q/YlUVI0HtRaV9dCnGkY4i9vLfSmrIHMKlQbsBLMZp0qC3Ee8R3VLLzL950M4xc\ne+ywPb31C1xlq/4fsBMm6eQCfFoNuXb0NLtk0HgPj6BXKYJRTlmlwPOBNg5431dhzcDhWPYCO7A9\nZB1HS+csi2djoyngwkYsIqgR2NyoFZpEh07FCJlq+bgLfiaIBJdsP3X7kN7jCIwOqewQiR4DX8lE\nG9kkEQBVd9lVMoCpJ9DuosEuvn1GLWWkZt++OYfLK4FLsMRyNCEA4wjKHp/UeBCPkgRjhlN9JFCK\nf4rPhPgqQ7AqTegJYcUInsf5rrIb9fiqP3xf5k5a4eIQJXJmgyYrT9rdnstSuW620tK/4BFedMSb\nMlU/QrHJUytrgg7+KvikmnUHvvgWtG1sitJxSXkI3c4Fie9pKNtrQ14y4QymV5haiQ7RYuqedy3X\nC0a1VZKyVa5a2Hkub0EJ4QX6jwubPZ/9b71gwPudtxkUwrPHiW7koT9RT8SDemK8od7gMzoeTP1U\nPEeyCYea2fSBm6w2F3MPiND5Ef3a0eDEiITRxWS5fCpmhGPgf9Co8ysC0+HnAgdRLzkXefXncQYY\ncHlfTJbBQoOtlv+lGQytHHoFrJxwERAMygcpxZUTJSiOhAs9tatdzIkQZ+YSLSVgSgASAw5xfaDg\n++ROcXvyDiw0+Ihp6k9MFtL1WCEk5JiCQeocU8EThD7FUZ30d/ZOg2AW9lviEcrXtdeU1eMC3Kq7\nNGtulw9uhT/XcnnECMcR7npSn/lbwEZMgzfwdFKE8KbdsDKX0Pimrq8pSRBCz7yknpShruFb7/rT\nL20PIWiAyoJ5BD9/pN9DvG4fGjQMaj0t2h12xi8qg0pDs5rY+TNN53r3W4QkBIuYwEugZxUmGETv\nGmBFR6IzsarQnopxYpcuUxHEbMklTDbcgx/l98ftgpAlYUZ0mMY99o25aBa14iAB31tfqxWSFnES\noMQnYrkQLR1LS9JJNInq4+ohwppi5qD9lLYvBY9y3Ip30deToh/sQHM5TkkdTm0n63YJgF1omLvQ\nxiHuIowjFuuoN4S5JNpSDtR0+3dxQJUj3seemEC7go38AZM8oGiVQZwY2k/Fd0e4jg0S7Ypq3N9p\nJvC84Kc6IUa4IKwMwAmdsYtvwPG0/FIPaWjzhQVBTnnqp8qBX4pTiYJBuFF+KUmeoksCZNMprSyR\ngwOHeeBK/EemVaE/ilcJPcpDkCpHQsCXhM5O4S07l1MBwmRVuEt43QK/d1iJF2BKtwrweKbCjbLL\ndI6HV3tmRvjTeQ/NEjUO9c5Ai3y1AiWbZCil3/0OMdAvcP4OkdtX1t2zUs22O2kKBoXgp96gP3WF\n6F1dAY1BZxFPzbyjMJ5+/Z/CdZAS3wrC6UimMLTw6wwepgvXCqw4xDnZr9ikc2SPBBzAqavgJByo\niMDoYZsOi2LLN3RZMeYutArZ0m7gH2aNqss5cNhdqSgvUUXKPkKCy3HOAzyedoN2pXQ4dIQ3YIS5\neKC0bg73cYnP8CPoSqTxiLum2YcZBi+0QQJHgzDlSvCVxvpcOT/sWpkxwIS8Q1u4CQXlafgXDiKK\nisoNMJFMdOK7mUUbveEySnEGT6+32Iy/8FTbUQp0mOVLoW0ZmQpCtWP0R7MCNwcTEh/hRP4I7id3\n5OE0GujQxZiUtHGi2z9+J8FFkoewHSauTPvAh+9Ox08TVvl720dNIj8tYYNeCXPawa7d2DF2kEcC\nmbSb2lTpfR6hiEzISz0blyYk7bJOasOO+pvaCo2h6ClF/CQRXcjyuNApgqaOqvMziX2wl8BE/1GB\nSuXxoV1eIjBdE8i3ytB72Ikf0rkwSnoqL4jSTrxJeWryQvsLN4IHenctpAtkiqq6SAgUnyBc/voP\nT/H0aFPVPwSU45dAFw6JE2lXs0XLlCNOJ4ilke3EFtSvnSQf50FUROXGtRzPU4Jn4PfyFy7BOelU\nX+GBHIOQTFzHv5O06J/aUBdpeZVeaQNcBB3nIswd5/kn+SFcRhNPvTsv8Sc/HghfdnoAZyFCFNr/\nPIcY0EjQ714nDIjpOHeBeXMyiUm5wKlNvTgJeDDFJLZozSzPdOo2FhizC00wHBealJXihYEVdocc\n5luVwvDNIBBYufrTqfUoxVc5gQWK+cfsSH0z9ljSUGgowZFVCNeHfPCgXtpQlZ+fw33AHJdCvUKJ\np1aucjq5E2Ri0MDGmmNeIaYEQp6XEqAO6YULDUyy4cthuVBLfCFexHwkuJ8qPkKeJ/4NmFL+kcAJ\njD6AAIdworJgagxHEUJOnNlphEiH7VUn76guWjKRAKHJwcFDRxmkJdgF2vB2685f9Q8+grx2QBWD\nHoPaOWkq1VftFOobtJVpSmAQzitoQ6MkkweVL2D1jP549bbLsY5WtZ1wKKFTUJ7E+aAR6hnykq5K\nAzxCUkASz6iMPvI6STF/qEE9QojaNbggTEKd0KL+ie14mL9IAJVwJG0oQgzX8LqgKoHSCQgcSfgJ\nCdICFl5K2+1UVhDawpPI8IdsBE0JToEC0Bum2IzpKzC0feIY5aSsJbcGYTTAFaDShCzdUtL46V2F\n86cyg4AWYIsEUK+XA5huVyWi3JCL0vIpP88Df5XH5NlhVRkOK/zM30M52vzjtItQmCM7Klwn5Qs3\nElgDHAiCegdXSqs6yym2C53SpEpT7+WGEL13IjALXAmrii2BUU7fLhyztC5jqE7lTZk+OSOq+J1K\nkFCsDbGCW4Ky9xOvoHLJbBflL9eb39n4R/m+3nmo/AiWk9dRfF+T/dA4oc2dhpVc9OA0IRoWthQe\n5ct3N92pvH53NhjoFzjPBntnmdZJOv3TmQxLvoE5iK1EBB+YefhFaEywLOZaTrE0BEzvRBJs1DHU\nnNJkqfPpuCfFkVYzyeardsvl1o4ulqI7snSUVGCOJ6qCln+Vi+fleasz5nALSJxz91gelvAiEImk\nx3FOjJkYWvou4TaNFAckw0NfG++4RKf6oYykoRAj55UDmq1ANnua+etP9QoMPCpRO/5TSbRcCQz1\nhTP/J1xJiFGs19SAsNN3ZIXGRvlq05ayZomOQTYvFe6TT3HbizZzCfAgfJ1+Gb2lUFs50/R6aOAL\nsVS+6tvSzPI+6/sueEWMVMC6EwJEJUrG8JjkAgVuWklnkY5zpg/lrTYRxjPoTZojaLIrj7MQOYIs\ntJsA6gaKd0GjPpELvXNZQgdXE3ahpVabnRJ0oV4yRwkuKj8q49zUMJ35H8wjCDGvAgeBxmvfPXCm\n29tRAD7wjztdCDfCu0REtZgipPFFHG8nJp1BQ0oeUXovTvmQujsfciFcKysqTRNgtTlbGS2PG3Uq\nO/aoWGvT2cbYm4csFDMNmzzS+SueJm6REKAigvlHVF5I3g1rZtrud70ESHzSmRbQ/NQQ8OP8hHBp\nIt3+XkKg8Aaf7Iw0kYRhZYALfEOCus/5gS8SNgPMiiMnhCiQBzXXp7/LT7gJn7yLpoVdfNCy+rsC\niaPiNFHVPzkX391ERpjCOY4y8pJfv+sFAwGZ6geOWohK/0RLfuW16AuaEJcMy+s8neaF/zSSe8m1\n3+v0MBCo+/TS9Mc+VxjopmNRPRoYFxgDY+kpQt9iO2HgDsxO2hqie0eIGBmDuHMxDdOysWK7hmsY\nsq2Yw+qLk0ctv+2IJXJK7Qh2Sn64vedyoh91PfGzAI8LDM4YtVwGRLzLL+rAIVaa/zmThX3CoPXn\nvggZ6uBn74QrBjHNSD0zfbdkZCvfaKiUsCG8SW0sSHmCZw2oQXARowcuYDs3DoZFFbXEraFVZeRx\nnmFV+w7s13KtKW+otWNHxxBGHBcBzkGxAQvOOb39AwwuhFKShEzVW20V2lxTAZyXL5rRbB6Y9cRb\ntnk+IONz9k51VPmR8xIoLBPfes/8jihKcdU+GtxF71oW55kewPE4iRPsSq+89JSLytC3/s5F/ZTv\nG8NFtQ71pu4RWtKvQcwLODk+bhQxYDAIhAGXrl3rCSbLINBFZYRY5ElfFT/SwF3W3mBVbVutDIGz\nOa47xhFEQWHIRuWnARJaAzhRoHxOw0WJe0siCo8AD4KF9wHIJaWJUJruNDmX/WWcc5vZaM/m0jau\nH27gsPd26+DYog4OjHfb5O68eisr8hM8EQ1GfplPcUcwmOaXQSYlflSNCFzy8HhK2u2XmU//e28Y\nkLY6NyfGWa4F3CIoW+OYlZcVY08MXerGKOjQuQWEoLO7RauR2Ua/wNkbRs/ML3M0OLMc+lOdAwzA\nOVzYFAeJOIyy1Tt/Eoi6vRVHQoOEqUgczEgnTsXMTEsxMWwo87AlLG/bbxUddZbNDtNmDPsVFgQU\nsjiJU65esKIDgE/4uuFQmGJETDQdWwKLlpcUpgReVpQoxFHKs3dheBPzdRgdDpWj76gcwSGBM+BP\n2mAXnFUX/kWwR6IrHmfttJStCwHyEPJzsFOraK+zyrY6a+KoiaZcMTbYWiRwaZQ7l45yA0WobmKf\nPXiP3uSn+nr9KV7+nsbbKROYcwlbGCRD7qH8oBrKLC/zXbCn280hDFCGGH3Bpbhqdz19COFJGp9U\npNOmtVvy/tNwwn8PNWT2/QgFx4WfECn0HxIEzQ8vLqU5drvzD/2K3OgHvkxJO0rLLDPrgmSjlbfv\nhBfts4KuJm4vL3Wojis7o090w6YI0ccJYVNAZiQlSruM1+AjPaF6Qej5oXze4Q9hZRxbVDS5zW0p\nbjpqtc1bN9ju3ZwlevSY36+eZFdTEhMELW27cPqayaPo79UulPJq38zv0Hs1iaceRNdkMHJeBS9H\nbz3+UXj/sy8MCKFM+7GflZ2yTCbKS4psyqQJNmRwtZUV53GEV9yPX+xi0qE4MjPpd+cWA/0C57nF\n5xnk5qyEdNHgmJmFwtLh6UEyzL6J67xVnSgdridx9E/L3frNTbVZCVe01bRut9LkIWvJLrZjrpmU\n5iiTOWeWGd4Vqiw9Fj9hkBGbTpenMC87sEmV6yGeRn5BYxY9+yrvtRCcxMeXacUMVA+evmlKmgmV\nIpMC4VJOjBm4vBLyE7kLd7IbVHq+fU3MI/B9dk7lq9bCfh43k5Qn6qymfbsVY6/WGisDXwF/4aiK\nCMazK/PVqb0VvDoBG6q//vQbNLv6SpetKI61CC7hK0qnsHPhNCgL3ypT2krlmX73p97lqfbQu2oQ\nfQOP2qm7veSvOCdzGW3pZYlGcN2bw/pKH6L/cf2CkzRaHCXRB5X0vhE8u+N0193bLAOfHkDfdq/I\nP93vFZbmB+EVnZF4DXnE2TxUnKhHs7nDyhE2c31FQoWmC+6GLcpTOfTA5uUR9fjQECfzV1ynJ5Ji\nu09PFC8O6kpPPjQB9Z7BBpxgvwnvAN5OTIFWrVlvG7futq07dhE/DzvxMnaPV1gOGrIctJCy2/Td\n6l5Kuh7dJb36WwGZdJcZ/qpaOR8P1Yh6QlQpT9Ut5Gfm0V1w/8tJMKB2T3JdqOgihYb6AGe8Pvg4\nxz5i1jOwutwmTRxls6ZNsHwO1tfGud43Yp2kgP6gPjHQL3D2iaLjI/hgKEZFf/fz9hgQw7KlWIqY\nGSKWhBhsDPtyYdlTAkqCvDj7jxy09KpFa9k8BQFBxs4aeCXMaPab8OsvJVR2akAADr/HiPR+p7CE\nKGCQsFmcPGwDEDarEnv4TlpbjPvfg/Tl6U4GX1iuV221dC4H+2PW7yKJNGnOtOXfI6QEQYZ6uwaP\nVM5LwQrfGcOSEp2dA/nCT1xnO1LvJHZiWnKVKUE8XZYvWVPXlO/Oxw4QnGj5Tvv23aLNwePHYc0c\nDM4cNFVXG260jF6eOGDVaHNKOg6CPwnBnLOHHW1YTk93u4DYMy8wnbJnyHLqdF8pLEUHrs0WLYIL\nURo/wCP8KVUAQO9hQqEBWDTVE5Yu4oweykUmBrr/2lssLcCIvuO+U1jlSgxF4+Rhgkj0pGVW0b9O\nORRMiPDQnjZ6RLugvXeBax22I01aJ0JBgrxyJVjS/1QFlZ/ulf706YCqzB8yBeGCC9oEP263peju\nlFIw6SmddOhnUn/pEgg59T+9xdBis92ed1knigME0UJ9Vtfoqo/q4gWFCOaQY/hVrxAPSGYzWdKV\nY9xmBjCeh0xGRKuavOSIpr1BgYkCZFcoelJuAZrwq5UFlaWj0ZISiMhbRxP5pEr0L42cwrETDDmE\n3h1yITJlCm+qsW84pNxO6ia7Y8XRyQuhHkTy8rXSIv7Et+M9wCTTDWFHsQR2YeKo1bZttBK0/QXw\nJRUiW2c/54/wsKgOLMCme9/Uqs7bSO8leLbgUnjAfjHsSBc2ZRuuvqvS1LOgFOLoTbjVBQx+jikx\nhGeBKbi1WSkLsxyV5u0JLeo85daObFu/dZctffJZq8fuObeo0koHjufMUUyQKEdHFnkzUIL6ltOg\nfsWPHA4qo4Gh2wlr4dsvP+j2P9GL0osG1AY9WXkO3ofwVBTaP00l1A/7dNEPQYH6FBLaJNT3RGWl\n/UPmoVx5eVuqV5CLTvWAfrrzAbbQqooY+oSjlK9Aj/KXT+Sr7z8Mp2r6ddSo2rVEHouzcY39BVmd\n3LQFTR5pbrJHnt5gy5972ebOnGTTJ4+30iLdHAVv0Pmt/BNeIQJy0l9opz+M2r1xoOgXOE+nrdKM\nQPxPHc87nxgMjDgHos3rbPFlbGWpzt/T7bxXp0vq8Q2sEQs7ZvyxeLsv43TA+HS1pg+ComsxOhVB\nZ9eB1MUJ7jDG6D6FXVsSxi4GlYMgE9cgw6CsAUKax7zkMStD6Kno2OsbhiSUiaGXYMvZ2aFDmcVM\nIvda+CLoFeIdjdpIWO3swoYJJgtfd+armb5rOp0raWiGKTnTRwRkd3hFe7MzeC1h9LjM9x58qKQe\ndzJ/4R7RHLyL2bZ1svsc5hiGnHBjlAYcDV7CmnCZn6q3/PYm8MNAA8MROII1OJV1svIiqPqCj0Ed\nmAqTDdio7UXgP0LpKWtn4IuzxC7hM5tlmk4OwBZsx7u+8+6JnxlXRBK5UB9vCn7UbrJH6ug6zLBB\n/aCPwCxDfBcyGUG7bZSIUs4dznFuvApt0RdOMuF4bVwJrxrwO/nRRowgpAU/TYiSbKjSxEzt5rDS\nLlGLxL0StCKwFCSbLY+bZbq6uAVIUhRhOgIxlYNwn2LjETJVIovJFMf2xNigFa6FFQVQN+8TATaJ\nr8pXZXWIbmkFmZ2o/ASCTGHsGObAHP6tQTYtWKqGEtJcYgE6XWAh4U/9w4VUhBXJirltLBez1Fqg\nMtK7i3UQu+4OypONGEfkpLAFVAVduGYg07FAOjQsmdpvIzi2K6lD18GVWknt1OF9VnbA7VbeSp1b\nJNwgXGmpT4gljlwQ0p0VWG57o1UkpVVksBRu4Q0S1DvhIxGzT0KPMSTuFIiLS4IVSlUf8gstytSV\niZz6RweCfJI/iVd5OjOTCBJmXVAnR02QZSEcrgOW8C/KzqXcYN+oFpVZSUX7HsoKfEo1V6GiAfWT\nXCbC+vYygVm8Kgi5mqQh5NJm4j2QB3nLLlx8r5VvREwhX378qcXjlKuJZxLBoBPBOgdcCIYk7Smh\nU/VwcY2jtiQ8dHC7E9v6bOfepD2+gqXzg9BAvMxKB1STLfXgiA3hxquo9ALCHXUWHUhAdCd6UljQ\noMlESvjs1qwLVhdK09FP+Mjoz+miohKjJGoD9SfxXS8TGFR32SmqgfTu5KGEr04cZRI9wav6kx8E\nrmbE+SUpeuEKU1+NAWc0gnxweqr9w3fIXu+hv/kKXF9lKpvXw4EnbfQSX0o6goBWlxHEcyyfm7jy\nimustbHOHlu+3tas3WpzZ4y3WVPGWF4ObQJufVzTBEzoEB9K4+D1qMobtcyIB71R4f+9w+031XBw\ncWd2HsxYM6CUFbFcpKWiMhhrUaLBBTwfDNSZT+KcXdABJBa1c6tHY6LGjuUVeoqoe4tZShugEzYL\nUo1W1nrIilpb2XEeZpvOeEih+M40yE8M2s/gZDCNw4B89zr+ealjVtXRjE3htj75kJYynYGJuZBW\nTEmMJsVglcUtS8FYPl1wmv2oK4caU6a0QergTSof6LrzIM1ZOg0uAkestY2bOBpgFkmug9PtMJrF\nSuh2QZPyOVDHCtE4FrUetYpjCCcMaI6ns4Sht+RqT/3X8S+5/GkSIDzFqX8JNFLMJCDwKOlizpXL\nzEkDTihCckSYjcdsrPbaMLhK+O3NqdU0gKidso8pYe/xekt7Mj/hQ/QSbPgkwAAbg65rIxFijpaV\nY8enExNEH+EmMF2vpoFa/6Q9LEwdwe6vyXJaWi0fQSubm2x0zqOGtyQawWzwrKmGNITSSHYxAeHH\n66CeJe2X6IFMiaWVBPUnRBL+RNdxHdgNMXVgsxWvb7X2uhbehQ31oSD6SRwWlvMRfto4rFzwSruu\nAch0xiQ3heWmdtkYTgIQX5DWTVpE3Xjk5RBDoqVoQZquLLRSaiiJCTHqnZXdZtPKh5LXWOBUuQEX\nHeQRoyFjOuWhniOEGg4xgUiypCv41f91iYB6gWcXhK+uozYcYdPbG/h0JFkyvRFGsGThkaWzMknS\nKaEXKUz4Sahfg69IQyga6GRQ7QS7mqiIdoQ/CSC6xEAhwoJuNMuhr+v6Yz+NgvzUdjBG+CPHIhEn\nh/IwX6YfSFgljDpLmC2iLUc1PgskAR/CugTFoP1UPmplJpX8k042rqZVOHmIH2qy7ad0EEv41K8m\n1XJJ0QJx8zgAVBMKTXrEG0RqugWpUxNU+MUxJimPbm2zHyw7DA8eaaXlgyw7r4hNQsGezzPzH0/Y\n86lsKEMCtsoOkzkVoPIVV20NTj0ZLURcB5CQM3XkkM6DTKm7f4gGwYZPRBROIaqx82z59+EkhIuX\nqvX16wKyrlilXlkI735nPDmKN3h9iNWNCZKoNPkE7CuPP1QnGg2QCytOR9CFeqHzPxoqm5uuSipr\nLNFWbI3HjtpjS1/k1I4sO3/uJO+jmu506iYtJqei1X53+hjoFzhPB2fQmIzf82FieWgzJHDG6fil\nbQ1W2X4IoaKRQRFhEOarsNDpT1yANpeIdXbAGBMMROoIGprUjWN0AD9GRDN+sXsYlpbppAUoSGnw\nTe9Ed8aqDkBnJ66WwrSsrDzEptG9Mv4yAJFLPgKr+BRv3uFODBmpnXGLeSpfxRRridiLvnjHP7CY\niNFET1iXeBd10OHvEnx0RFIUW7mdsXNYGNwEFnWXsB5nMNH1qZ2UJ38xBpWZYFARY2CPKTihXdB+\n5YAfaTgldGgwVOU0mPZAfsaQkVNAlTQvmojIA5B8oM1Gw6mPLIQWBUiw8kHozItLpxQiHCnhWzj3\ngsF3AIG2gLakddNgmBmXeBo0HW78Y1quRMDRUH8unBfloNFGmvyotYAvgYDVlY2Gib8ER3Rl0xZx\n6EODnmjG28e/NGCz9EW7FXceRWgRbOQCSUmbqSVikMgYidBJe8vp2BqPReFOHaAn5hvy0sIIOFC7\nx1wwQpurJwIarYETFaDVIg/hSWG6Deeu79xrK9aut3u/9EkXUEXKnRz0rcO/JcUUoUUUvXGit9dT\nGrYOIrn23/GaZa2oRVa9ss2KCnNt6uhh3MGdsBWrN9ic6UO4v139vQjYc9RbqRx4QjrLzkYwI5+E\ndkvjrwlMDFzFKNPpXFonaFzioGrtwg72aAUxrpiUFOH9jnTgNAfhuRWhugUNdhm4E24SgJybxpf3\ndGD1fNUfJLTzp5UEn6CRvYqTVlhlueaUd3oTuETwI9DP0xTe6GNa9ZGpkGtzSadbt3K8D0i4lLCv\n9hBcao80n6HNC4AHbKW1qHrH0ZfIgrpDI9ADLBCaAVP4d0iQhNn4ZIRY6skSWsUbsoFLPTzis4Jf\nZUtozyWTAy359sMVO21pHW1VPsbKi4dRH6YGgsELVvzgejSbkQ9P4UMV4UUli5OIo1Oi4y8IM+pz\nQdQJlclIf0avqkT6z9sYfgIeA7gqW44+jcerqhCCXv1Lf+iSDbz6Eufc6vpRDR+OK/ES8taIIS7h\nt+HxGXCj+CEzlRVB8Ors/1C+HReidV5EIwHegCG/bSvGeAot6pSA7LwSK60pt+b6w/bIM+ts39F6\nW3jRdCstZhIMvXl89fdTRvIfChZefzj6Bc7TaAPvehBkWO4RW0GYYbApbT9opR2HYaitELOspnTt\nIiTtnfUkBdChXdShB2uglfCp2XhgFSpNPhr+YZww8Wwxcu8wiiUxyUULykHAiTqRC1KhIwXBilhk\n5WcXMzhFzKkv2Jx5vorrSmsioVYuMG/1uXSnFWBpR1VwdGp/QmK8ePy+8BFl0MdTA41YqzMOuKN4\npTAgDQbI4EflwV1wPkD5K4EMTJ2MqBpUJcjIPieCrQd6T3ZGPyqmE0RrUNagF8wIwiDgqBCbC8gB\nLnwCiGdUVu+JRA9ipRoo0plTTLgLGmw5KKGmAR7RVhCcBLUYadRO5wIfARp+oRHpmpOaAEgQx6mc\nHATIXIQChYn6BacmBA6L0iAoa3KUZKG6EyFEwo/arq4lZd+6b4nVt0qIz7LSvFxbMP08mzdplBXn\npAdgIqq6EoRCC1AmwlsX2kLRhwYLCY7SZuryAGmowvghLR5pnVaz7DCa1YeWvWzTZ08Ap1225WCj\n/eLx5fbSlh1WUFRgF8+eYX924QTEFARl8CdttiaKEgxVbhZwZFFQS2vcvnb3wzZq1EAb/f5BtuNQ\ng33qX79nX/nCh6160hDqTC9n0FOxLuypPUj/5NrNdt8TK+3iOdPsunmTHZcu2KGpzEYDLA4SNLiU\npgRMGmQSQHI+w8RV+E8Azy+XrrQlS1+wT73nLTZySBmRxUH4JVw9KIiP0hcHP7WalqgVSViUXlQ9\nL0CGmAsvkKZXsIM1wrxYz1P8Ra2nuqtfaqIhgVgL+8pHuFTpan1pmwSHXDYmFLnUS0v+YWmdMtVP\nySPJ8nhzK2ZFBZikQEfSxgoG0bprXZ3mEaa9/QSpSlA+qqnqKMcEHu3r1sZy+9GyI/bs/mrLHTDG\ncgs4IxfttLSr2QikWdjtST0suCJh07V8jivPKP0T8hWuVccuP1eWL+JpTSEIpIoT1TAz7Zm9BzpV\nL6ElwI1ydzta/6ac0ypKqflTvxBvd222/GTaAO92PGuSoXYCJ1H9KUPlB+eUnn4/rcLTaX4PD4R+\njQXiHw62wKQufu0vfEg70wN/xNyMCJp05JfWcDlFob28cYcdrj9qV142z4ZUFzkKnL96gggHv4c6\n/BEUIUrpd6eKAQhUs+kEDAvex4ClTqjZcgPH4NTDSlvpuLIrpONKFdrXnwsfIm8GWpbRxGhlJyUm\nHzoHZTHjVMd3psygoX7iXRo6FxvQQCdtqIYCX2iEG8nOTIOp7qqOoW2N89QgkUCj1CHNkhKeDDYG\nafEVaU4y/2kQ0YYF2CjMNMznA9PnnbDwpzA6MkAqvndHASwJ42RlnmoYOJfwnQu+JJyIPUg4UKle\nMRiLmLzKlY/ehDss0Bj+tHmIyQD/pG3LA89aEPWNA6da/kniZRMm7bJwIsEyJToB17IfU5s4NDD0\ncOPFOcKHNpoIt+k/1cX/NBBpKZY/CWox/NUuMXDT8wdIJFeVtGQpf7V71knq6JFPMVz5OG5DpmSs\n+a0EAihIghjlaWNCJxq8NoI6gFEDqAQfFRGW32k773PqC+jMyGM/u0u/fu8j9uKGHXbgUL0tfW61\nffgfvmLf/NkSlkpFCeBfy17E1xWA6VKpG+XS/nJ+1Se0oq6gptEkQRiSQKx+JPtHLSk/ufplO9bW\nZpfPm4Omo90++oVv2I/vX2o1xaXWdLjR7vr3e6xuH3Z/EtahKbVzh+okDRwmAFpUVl1SqPgSwNbK\n4CY6kVCInpdvwalyZTeKdTTLmV0u6KjP59lXf/yIfW/JavuvHz9kzR30a/LS8nfED7SUGrSZ1EH1\ngPVoI4kv2yPpKV6oYBdnR3ZyS5jsv6Xthu7VL1hGlPZRfUX9Jwvb8LjTKHgE1zIByMYPgxziCH8I\nuCCtHRgTaHlTfCd5JvDrkokDQrOus/VlcvKRdlS8SMvgCQS6JGULT67R1uivdPQXlS/B1DXH0EEn\nWlhNmIU8bw/wtn3/IfvIP33TNuw64OVqMiHel4dmyuCT4pUpTGyUTBrMXIRXLZuDdkeBeKQKbM0p\nsXtWHLBlhyotPmiqxQq5Hx48djBZTKFA0CqRHwhPrSNhk4SvdeQrm9tOtZ0LfoGziJbcAVsSTa/C\nyOocuXRG4JbCPV+VGsp0ZIIv2b1SJvTVl/OJgQQxxReetBLBEoJ6kQT+hCZjINTvnqcN1bLBdEVt\nr790Xb2svsvrC57fZTjVxMEFoRd6qNOcpiSuSFGQ6JXVDvUn8W5FyMrNt4qaEbZnf4stWfKstXco\nnLi0rd8wxdjjSdNP/+j/OSEGxIv73SljACKDsCRsafDWUB6O1hDDjDq4Oiid0gkwzRy8M0aFRH76\nJhc+RbISO8ndO3cUs+cZwugGlC/9gAZEMTmlE6tODyyeQDAG6OgTHup6BB/U1OP4Cn2kJ/ve4BMo\nitHdkZTIISRA9e7JRHBEjqGCaGkPAYDTZ0Z0+bh/+MlIfKr+MDnXXKZrp1pGraI89SUXBN4As4QQ\nF0w9SGUSC1wGHOIZwewpzww+Z/ueFPyQj/DuddWg7/mrBUOXC3jywtI/fZV5Ijyp/SMX5aG4/EVI\n96fwIDrKdOk8RRsZ3oF25XGiMiP/zFSRn9L1+KvMkI/KD3QrIUXLcxJGXCDnmUBQyWKw8/EtjStB\nFWNw0EYsP5EgPbgpHw1z77n+IrvmolnWgArrjr/7sv3yoeftw2+/nHG4C6G03drbW21gVQkTLuoN\nDvYdabZYXpYVcvDzEZbJKkvLrII1ZZl9SNjZy4aRwqIcKy9A0GLAaWTb+8PPvWKDhtTY9HFDbeVL\na20b14R++gM32y0XTHGTjU376m0QR6pokG8HvmPJHM5rbLEmDgUfXJ5rlZzvh5jmgok4RjaDmrR3\nok3nGF5h9RkJ2aIXUap2uKds174mluF32/Sp4+yllzfZ+l2H7Pyx5U63WZj1MD21PZR1tJlTEUoK\nbER5DhuU0KYicO7GHrW+6Rg3feXZ8MoiTA66bNFFc2zieeNsyMASmiRprQyuR5oSaG+OWB6w5zC4\ndrEcP6IETWIH9o1tnVZeXm4Hjxy1tvZ2G84yY5nW4LHJVF33HG21yhKWHltTdgANUFlZodWUFiCw\npGyHhHCEUpVVIIaD8Ip+jPaOWd2hI9bY1E78chtQFmPTEEIbJgeHmzqEISsvKrXtpOd0GhtaWWzF\nCD6t1PeVvfX2yNpddkNdgxVxhuLAsnzLRwsu05ntnJPZjra8prLKqrnqlmZ2kweZ3Eh4ktZZFwg0\nZ5XafasbbM2BAssbMBJhUwK7tNoI2rIRdpEqTAIy6RjQexE+xUs1YSUmdZZ5VUoETYvrSJ1OtKQx\nNpxoCT9MezL7qnI8C+d9xLkamcBvKD+GsC1ajmFTnC0BXFfBpvvMiUrSWEF3oa2kMOi0ITXScKds\n91FNSMRnoU3++41L9DOZ3TjvpE2DMB54nWY6gbuov5/Dep4I8DPwz6JNxIVy2YORhembJkA62UXc\nyidmwO2TYA3K0IEmgXqzWL6VlY+w/Yd229PPvGSXXDiVjUSgBXoRDnQFrM7t1POkExTl9Sfu+gXO\n0yAAEZ8LFfBcCS85/EmrF47mkOZEA4YYWFDfO7GeJH8ndMKlSdHsvIMNCO085S8B1gcoT09OzhTF\nPBUqFgNbwDvseA0dyWdrBMMugUudhz8Hgm4G45DmKAg+avYTQ+dwed7qnqE0f5BvJJfJP0CSzrL7\nqyd+FFdBGmhPXGLI/VR+VabXBC6p/FJu+6r6h7+AHw0cgk9xVHdtUhDTFzPWcA5mYabh2j3h6lRK\nPsU45CWBSKVriVh0EoRLykvDqhhi8ucCH6E0ZSbKJMeoLp6/PiIPoFKBfRVKo0G9p1jZvqOFJXQG\nZsoVXkTTjhue6LxoGzRKCkOD5e0AuL7EC97UdoJEAloOmziyGVS1qiCNlzCci0RSgIYuxc1ZpQhG\nDUca/Xy9FAc4/+TRp2zNSxvt7z/+boQ+doCT3+e+ca+NHzvILphznn3lv++za6+63N62aJHTIOMA\nAEAASURBVIoPOfuamuyT/+c7duPlC+zN15zvcB5s7rBnVq61xVcvsAEIYYg9gJ6y/fVN1kafLQTg\nScPL0KbJnhoht6XLvvzde23jlp3WkcBWEuHr1pvfZJeyHG8MclQ21KdTGkhpDeVEH+qrCKbkrXxS\nMewr6bsPLVtpFWWl9oGbrrQvbNtuv1n6rM0de7Vr91PJuP3Pbx63nz61xto4M7A0P9f+9vbL7Lzx\nY+3HTyyzHz64whKJditiIPzku6+yudMnU5dV9iu0NH97x9ts+MAye3r1ZvvOTx+2ww2NftyUcQtW\nQW7MvvSJW2z5C+vs6TW7bODAKnsOW9NEe5vNGD/cPvWuK21IRTECSbN99t/usamTptraTdtt+959\nVl5RYre+9TrbumaDPf78ardNf9Os8+wjt1xpRblZ2I922rd/+ZA9uWqdNXIOYmF+gb3zqjm2eNFM\n0Npl3/nZw3awESMKNKbPr9+GprXTLps70T5065tJ86J9+QcPsmnL7Mt332dDSnLtb95ztQ0fPNq+\n+qMl9sLLL/ukohJt5Zf+/i+tIl+2rXLwOvKBuqy5q9CWb2+3X65utXjZFLRXbPyUsIAeOu5XC9NT\nEZJlDiNqDVzVMznhjwQMyWTiqDkcZk+xLnR0QM/aNJVASHM+GBr7hPmcXgD0kqYe8X+NFchSaNE7\nraY832ZPG2H79tTbS9vq6ScBCyfMX8AhsMuVc1jA7bfMtEaOCPreT9dYJxstOYEU0+RQXgfrzLoi\nV4YUQbNJWg0w0CwIxqV5zTmtq/I9N06TTvGjMUPLbPTgEfbc2j12sEk8hZYW/wR8gS6eTQflXU98\nGLviuUVWUj7YVq7eaBUVRTaD3es5Sic+JRpQmn7XJwb6Bc4+UZQZITCPpDZhQJAxlnUjQUZikJab\nJOxpSBTh9tXvwo5KuJUImkFLDC64zJRhQJKGQOUmxcSipVLK804OsQc2AAwMWi4uMGCJyUbDtvqN\nZm16RKXIpzenOBJeXxuRnNOJj8vjuA8SO1zH199h6q2w0/QTbM4IhG9nfRKw00ehONtXhkFc19We\nEryz2bmsc/2EC3c89C6WEny6Q0L4Gf6GHLXoJCcoA7PX5pbQtsya/Z/CzxVGghBDtt21Ue7uGEyc\nXyqQP9X11U0VIvb8Cqo0lno8z/hNfYFBCulaGgQ/2saXZ8EUg5y0SlqmzfFBT6XiD9d3SPkULauF\ntKybQiDLQaenpTCJDzq4ac3mOjQLebZ+z0EEjs32zhsWITBpERvtIJrIjWjEkqRVfsLS5j2HrLCs\nxAYOHmkHm9vs3kcft+svm0ROKXv4+Rdt/e599qEhtVCRJnM59tTK1ZZobrfFC+cZMqzNnDLRBtXW\n2pe+/2vbve+wvfOKeTZjdC39EVE00WFfufuX9gga0Q/92TU2bPAA+++fPGqf/+pPbOz/+bgV51MH\n4dGXX6VZVz1pD+op2FyzgmCtZWmdN3mkKWmPrdxssyeMtgsnDLN5UyfYky9tsa31CRtTVmCrd+y2\n//rVE3bJwvPtmgUzbPPmHSxtF9i6g832jR/91i6cO81uvGS6bd0KjhgYhYXWo0dt+06ulOSIqTaE\n3n/8yg9t6OCh9om/uIajYFbZPb9dbrdfd4kNQPO7H83pr556wc4bXGm33PgmlhQP248feNImjxxo\n771hobUgfKwHn8+88qAtvmKBzb9otv0HwvbH/vkbNmlgtb2VOGs21dm3f/EYsEy1OVNG2fd/+yh5\nPGW3LL7Mpp430n7x8LP26f+81yZPHGmDq8pt++Fj2Jm+YBdPHWvvuOkKW7HiRfvOr5fZFITlcWy0\nunbRbFv7vftt0fzZNndUlY0ZNASheL19+9eP2Ofv/HMXol9hkoGUx340Gc0I3/yn/6ViheAm2+5+\naqulSiZbXq5sWKE9wjSJCLuxRXMk8K7ZV09JZ85mrDg8prwgQT1rbfLYGq5PzLdDx9psxQuHbOXa\nBniPDH+89ZXoHLjAtwIvAVhMB2TCI3ORAWizb3jTSFu1aqut23HE+8lJC4RXq5dqHBszrNAmjSqw\nh588ZEmOB7tywXCbxC08gaN1WUtzi+0+0GYvrN0LfSKcy+ZVPFd8hkJOTUQ/KTS/u0BJk0xWdSzS\nnGlVdvUFI23XnjoETmky1d9lUqQ6RO2kjin+LV5FzehD2fmFllNcbY/QLwbVDrBBVfmu2RTQ0nb2\nHxTfd/P1C5x94ygjBp3aZzJp9gFNsugQtJN0dmkQRbCaRakbqxOezCmGrAn1DOwtCA9hKNJApD/F\n4RemqOVFN9TnKBhpQxRPBvFh/kk8OogsntyA35mmuov+lIs88vhQrupUmdCF0vEM/nyq1ChGd6i/\nRL4h9vG/nrCbAWWGSUAPNcr0Pf13gRAEOdVINYuwF54BK6F+Okxfe9HDjvvQLm7sTR6qRRACBe6r\nW6u7xumYPNz17e/3x3vuoYTIBENCkFwQMPQmgTkTl33lfeK4UcqeGOk3tbUHRu1/fKsLitc40rx2\ncFR+USmvSdHt0VN+2ovKylZPOBCOQ7joNNjnCSppM7WcHZx6T2gV/YrmtSU5hV2jbBtln5lEVep6\nasj5YRj/qtWbWIpt5NDudqtvZBkZLVpFLoMDEzPvncTPZTBg1HBKkUBRUVRsF8+fZt/6ySNWt7/R\nBlQUoJHbasNGDrVxI6uBOWFHOhL20JMrbfbksTayoool3y60akX2b3e+175574P26JMv2BNPv2g3\nXjzT7vzzN6P1bLGlz79k58+aahdwcHQMO7irF86xr353q728bpfNnzEFzYlqxeCG2NzTG3hL9w2f\nrArN4GT7nn22asMW+8LHbrXqkrhdMn+qLf3aj231pj02Zu54jgMzawA/7QiwWraePeFihKcWexkh\nuxmtXTPa4AFVA2zm+AngkUEVeNqxCW1i0toaL7H9R1rsQEOr3f722bZw+jgOuc6xR59ZY4Nryqyo\nQIJEzCoKc+wf7nyPzR0/2PY1J+2h59ba6u0H0e5qN3MLf1m2+E3z7X+9bzGa5i7buO4V+w0brD73\nsdswQRhsm2e32MMPL7N127dxi8tw+9nSVTZ0zFC7aOF8DsNK2OWXXGBLENCffeEVe/PVl/nAPm30\nUPvHv3q7jR42EGGnzFat32SbWS6/fPoou2zyKPs30LNw9iS7fNJQ6kudNF/Hrwtt9/gRg2zB+OFW\nAA1LgAoUC70hTLQwYXlo7T7blzOSo2/GUT8JSmimmQwlc2gPcJbHn5aQdYKBL7FSv5M6ClAXy80+\nZm+9cZpdPGuwHTvYYs3NzTZ+eKkNrK2y7XXLbP9hiFU2gYrMQ3Qv5/TtZeibMP6788+ej6B9S8fx\nSOFd/CPQk/qWeg7/ONM5j5UVHQfnqwnqQ16e0uhV5QQI9Ct+JDtrbUC7aB40inT5/Iptlscy8oLJ\ng2z68ErbfeQYuXdZaWGVxQvzWFIeY/f++kV7ecthxr5iskND6sKZcgwc2IvUjwoQzE7jDoCX7+H0\nB8GmNIoT4EnDqag41dCT8x4wEmD2MI3F6ez1HcY15Rl4SE9Oigeh0FcETw7v4g5xffuf0gQtr7if\n+p9fNR0KdPrRNFdNmIcJSQdXRK9Y9ZLddPUF1N2nNS54umhATsGlE/tHDyRR6J/qs1/gPM2WT/MM\nUmkQhfghYHUKJ3HRlTRq+LkGw/tDRGyBAN1+jTieivQSUWW0z7jseSpfKD79pxmWlqLFTMJQHLqS\nYkXdVN0szM7kJxfZkQguOXXlSKPiOafL98DefoiklCF1RoSQfYaHXqOYmbFV8vGRu2ERAhWaZjYh\nM4cQwBUGZpxJRelDmLyEV4V5CD8qQ0vjQXupnMQsyMNBCdtANGv15XwvjxhKzJ/ykR5UrE44dk9/\nP7sfZa/iBa/eAt5VX5WQiRWVefbOy1KJlCdMBeeFZ2QeRPMMjxO+KofQVoJdeYow9eIEylsQHL0k\nFROKDi9efgQD8Qn3emswgtaDeUFEt9JTRgNN0LGEEoSj0MbKPrypEMolQpekUzqLznS89YZLbeG8\nqdba1mz3PvAMNpxP24IJQ+1tC2f6+CZbVC+BAVUCFNtx6K8pK+R4nkvPn2b33PeELcEm66IFs+1F\nbCTfeu0iq2b5XTVYt7sOQWcP2rzLrJJzZ3UepLRIk4dU2hc++i5b/sp6++aP77fv3/+0TUT7NnbC\neDvAJqJnX3zF9m7fSXzDjpOtQWgC29rYTS4POZ4+SGlQwwVOgLeEAGAT1com8YlnX7ZGpCnEB3tw\n9VY2uhQiXGXZ48+uthvmTLBx2JVejdD8mweW2da1W+zyi2babdddaEMHlNsNC2eBj+W2bfMuu3TB\nTHvvTYssn46gMzBlyCBBpKy4CE1czB5auswmjh1mz7y4xVrbEzZ2APebM7GVkFbJLvyhleUMxdk2\nsDQf7XHcmllaV8s5jwFRQweUWDGjt/SJAytKrZBDtEcPrXGRuiqfw/Rz2CjFZiW1Rd3eA+CowT57\n1zdovy5rRojPRRvZXn+MuktcysKmsxjb10KWuLHxLCm2As4nTrIzPZu46BKFPis2LgLobIZ35tu0\nSeNs4bTR9qWv/dB+O3aIve2ahXb1nMmWD72wOo6coXbLsy2H4/bynmwrrBhEDhjW0JaBT9K2yAwS\n9gPPVgmB6njpw4EA2qyK43JmT6yyY2jLvvidZ5kAtVpVdaFrwZrQFGYx+SngkoIEQj8r7SgJmPiD\nr+IcLj/guzWhMz85EgpTAtGLNp3m60gs+k0rdpgt7FDVSgDzKBcmE0ygcmjPXEwvOKfB2rTRhdWc\nTiYZWmVTDdSXctkxHyPPFNrsdi77UDcUj8hjpaCAGE3gvB2Ti2zoalhNCRrjEqs72GY797GSwMqB\nNkAeOtZq3/3JSmx0O6y6usDmM9m5FMH6nddNtn//3jO2sxFyQejSWkURZiPZMgcBhladi9tZoAKh\nBaaIOWov4SuHCQGTSOi/paPU5b3C3DbL4ZDVBPU8xsRAG+8k8Ks/KG0RmknxcR0l14KMqHMRlDGk\nxXI/6RJsaePmoDiToAS1ayNfsQmZ6cRZfWAbn+XntjJB6aAMaAj6lqLGORBwaAIcSwvMOinCNzV6\nGRTjmKRwyhMvzGKylVNQaevoWzP3HGWFgCtP/Vgp8XMpj3AiKHIXlCF9ePPPP/GffoHzNAlArMjJ\nhx8JemKUsgvUn0hSvVqd3QUbxfWBWkM4jISO6KIpnUnaDtd+wfi0A5Ax0QfkMNiTAZ1YAq2Yn9LI\nZiYa/EMemrGJaZKOmbliBmJXmTKFllNah4pfxZCPuoCG4fAtv14dmXmMjGie/4ki9+ofPCWUi4EE\nSIKQodyjQUa4kQbMa+DlgkGlUXLhx3EL43S8CX6AIlxHmKiTR0sfSi+hRuEB1ghitQhp9J9wxQ+C\naxDiw+w7o6Iq90wd+YsCvI2681Bd+aMegkwl+c5Vf+uOdEYvAWrVKUouH+Ey00WBfdSRaIJSv6Gt\nBCef6eR6FSbl9O5XuKbfQ6Qw2w+h6XzQIES0F7IJJQScE1N4IWqYSEUxVYLKTuMR2leeKTSPWQg0\n2sCAQg3BJN8mYI8VT3FZwrUX2mOrXrG12w/Y9YuAHok0iQZMB6SH3bTYRlOQdhVrJ/LoodU2c9J4\ne+z5ddYADTUxsN6IoCabbPW1p17casn2Lrv0wll8o/XiL8UAmM0gXs4gftn0EVY54Db7y7/7T3uW\njSwjxk3ApjTbFi2YhU3iXF9iy9dSJxs5Rg3mMGnK1PRGmzu0rKf+L+oVlpBAHQcqR9g60JxlD3Pr\nTXlJvn3nez/wODK4KSnIsWXPv2J12E8Oqyy0//3+m+06NgL9+MHl9h/3PMhNRUn7i7de4ccevWne\nDPs5m6i++qMHrKOtxe5855XOo3LgKzloszoYeGMIc1v3HrE7/79/tpLSEnvb1RfZojlTgU/tqBIZ\nNGlw3VYl/paDICDKEA2I5+ipXeLSBiW1+uKDu+oT6EBp5HQ0v3hALumnThpj73/7dcCgK3pzXKiZ\nMKiI/GhbJeWfBBgXOkgf7OMQvjA1SEl4Ik6Cc1dRKTkMoxHsvv73H7Qly1+2Hz+0wj7xhW9b9ec/\nZIumjSSmbm7KtobOYlvy0hE7kjMYLV2lSgDxoSeqFSKaduru+SB9X46+joClm2hyyU8H6rc0J+xY\notCO7M2xrQgkcXhUdUWefezd59uyF3bZksfXUnqRVZXl2sf+/CJ7Yd1Ou+/R9VaWl2cfeddFVrdr\nr8WK4zYVrbLyW7Vhv/3s0VcQYrvQvg+wt105CU3yRhszZogNGVTB5q52e/r57Wj7d1srZ8ZGpz/I\npnDowAp7y80X2PbN+7Dd3WBNqTw2UrXbNQvH2IJZI+3bP19jr+w+4jieNKrcqjHV+MmSl9GQs0lG\nwil1aqbdDjbHbHcDdrsNSduw+0Wn/7lTB3MTzyjb/dguK8pP2vzJNXbFBfOwOc6zY0wQnluzwx55\ncpfVt2fb8CHF9o4bz7etWzbbwJoaOw+zDG2qe+iJjXaMjXzXXDHbqtkc1oDG/X84kWHthkZoI9fG\nj8y3q9GmThhWg8DIEWUIvU+u3GYPrNhCf4rbtDGD7S1XTbBVaMjHjx1qo4ZUcL5thy15Yiuby+qg\nuQIrZXJy8YKhtmDebKspKbSduw9aUY6uGIZuoQ2Nu7KnFufxSyPU5N4po2egYZG5n+oA3efml1hL\nY6k9uXy13bx4IcKyU47nEWIrrd7IiHT9rgcD4nn97pxjQIzRxaI07cJG6bxick7LGmD4005r/8O/\ng3cnaig0olFfciGFxFkJVuHGodAbwrFJSi+yDv+C2CkmrRwgeAY1558IWOFGDn3BuFme7MsJeoc2\nAibk6H3o+LTEopyMaKTrcerI+o6Mqj0e9dUziqd37/COs5BXd5jwpjyI5MKiv6dT4Ck/HwQ9nHhE\nlxCjYdHtAYnDOMU72aRLUeFBAKQsnwR0swlFOisn3Ktt0uoE8hLOQ30lMAeY0DMB97lyoQRy8yy9\nxU+QtSPhBGFpb29L8lBU0Wd37ABvRJ1pDBMaJkIhVHQauVCWliyhODwliIvGRQ/p9kzTdRA6JaQo\nruhdMUL/Ec68LRUXYBLgDSWHu1YEnAYEJ9019/Km7dba1MymCZb4GEgqy8vswIEG23WwCSFjIEvg\naJ6OaBPFCOz54vjl2uXzz7N//e4vbcPeOq6wG2mj2IWt3eltiU57+PFnbdak4Ta4uoL+kkDIoYyN\nO1DXlNiIYUPYX5PLsvQebiVJWBUauRp2aA/FdvHQ/gM2rLaawbvYYu31drThGJqsLjuK8OrLtMAm\nAVjaNGFFU1BpYzr4zuXgeGnrV6zdbPsa6u3Dt91oN5zPkjjmAJ1oVpaseMW+/O177WE29Fx/wWRL\ntDRx9uhYlm6H28a6Q7at7hgbgNqAv9Fms6lhyNChtgHby027j3L8EnhisNSeXAlxW7DlrGen+Dve\nstimDq/CxjSGDWSVw6Nr+6QpE61KW6OlR1mri2R1RWY4Gk2tQoPQBjQp3Ik6ie6VPxF9AxRPtapP\nFMH5xNHD7ShtUovWdHgFmkuEsf3c/pWPwKJzELXjW8fRpLQ9nZQS8p2XSTuL8B7juiLlt4nd8dO5\nCrQIaA8fOmg5+Xl23cVzrbp2kH1885dtw7ZdtnDyCLqgjmIqsI31hbZsG7fAjZzp336pBnCerVO9\nO6njgcZ2qzvcahM52/TOd82xXzyxwV7a3YoGD/MmNo8VIUxOGJplm3aofTXZjVtxLGHja3PsIJpX\nHcNUGM+3MbVZNn3EMMw5krYNwUia3uvmD4EuuuxbP19ppTktNm4wwuiI8+wQwteeun02hDLfecVY\nS2FK8uBT++ArxbSx6AvNcmsS4VBHetXasuUvsakum81lKbtkbrVVVZjVH9nHxI2d7AhlF82dgSlA\nGxvA9oe9AjSter/oUw0sLiu75hboeNnq/djg1tqIIQOsoHOzvWnqCLvlpom250A7KwX7bNigQrv5\nsjFoMuP2g4c3oRkvtAlMDGYNnWSHj6E1xf55NJvt3rN4Ev0qZccw1zhGO44aXmG3Xz/V/m8dGlV2\nyV/CZruJQxESd+5Fa9luUybU2i3Xj7ONu/fy147JR8wmDorb2FryBR9HoPVR4OPdi8fbpgN1tmdv\no104fZDdevVYTo9ox+56vw2pLbFBFWV+QUQMgV4nE3Q6rYsaxLtpHw0iJ3EKLy+vtO27Ntje/Ue8\nTKd79ZeIrnzJUtyTvE6e3UlK+uML6hc4fydtKgrTX2BqGkJdjEozZBcSCVI3ltNA5IMPhKxhWaxc\nsynv5jD4MOiG3NTxwzClhITBoDUSazDQIAY3I30YyjTLUqgbxcPkvD8QqjL6cipTgkDknPH0fKa9\nBY3KVHk9LtQq/a1gXrU848IW79J2BF/FpFOqg6c7uQelM9DSX4C5RwwJ36qrylQ66s7AJO2Qzl1U\nUjHbICppSFR9g8bFAwkXHBrIPK7n0jc+iHZKTrAoZ9mQeft52/CmNgZcaW4kjAqfvJ0TFwQ04SIj\nO8fhazyIkEZuRtTjXj2J2ILsm/Sh+Dx56E04VqNokFT9VFP3J8zVYdGXPHGyKVZbqEVCDsFXeYco\n5K328L5BmMpUGISgf3woAY6yGCBiCC6ie7XYD+572pZyE0iS44/WbNhswzme6KoF07DZzLIFk6bY\nt1K/sX/inMxhgwagNVlvTSxtx2mHOOtp2gH9pvlz7HP//VNrZLPKxYuvdBu+TvrJC5t2cKf2EXvf\n269i0RYtJYJaJ0uWv35qk/326RdszNgxVoqd49pNWxmw2u2my+dabWmuXXfZAvv6PffbZ+76to0c\nXmPtDU12rL7ePvmht7JZqdKFMPWDTnAn/ArLjkPRMIJoG4Of7lt/lnNFSxFSLp0xzkZUFIIbBCfw\ncSFLx/9TWeG7vLXr+yc/f9SmTBxr9QgK+w4ctJsvnWWvbN1hX/7ez2zqlNHW0tLBJqHddvmbL/Xd\n58Kk91iYxejhI9xG8/v3/AjhuMSK0M5WFOXbzezSfyvHTenGKT9OirpruVxnpWp/jZpHxwhxHADh\nUDGaSc4C8rNFlXvoSaJu8TBffwCvCStGaFp89SK7i6XvT//LN23GuFpr4m74HZgu/P0Hr7Nhw4Z5\nWqcU6ityEd619C6JPJtl6MqSKo6yQut772Nsitlmt1OvpUufsNUb99kETh/Yzs7sDrRr4yZO5vgl\niV1QHUu4a/ZyZWleNUvx4jXH8ypQchZOp4vErbGz0H50/3p7381T7bwJlTZi5Dx7YesBu+f+zRy1\nJZMo8T+Kpu/7uZbS4Gq5W/II7drJ8rO035qctULj//3TVbZ6/T4/Euqzd1xu0xDEBnPsVR4NoKXl\ngw0p+9K3nmP5u8VmTq2xj79zps2bNcgeXL6JXfYsY5MX0RDummwHGv9x54+0CaMG2HaOvxo7rBKz\nhULopJ7d6HBflp0HVJfY2KHlCMT1tqOunrauIJ+AJ038dMaptP7isXFsgA9z5FiKCVhFcbGVYCpw\n+YIRaJvNvn73Mo4ea7HKwlZsf99ss6cPYBPYJqdzTXQamrLsP3640nbt2mO33TAZM5DzrO5Iq32N\npfmm+mZ7761zbcqUQfQVViba0FQuX4cddcL20T9Fb9ddPNzesXiejWR1YsuOfUxIwmRnz/42++rd\nz3EcWZO9/cbpds2FI2wYKx8H9u3ALhVhnDrefd86e+al/TZheKF9CLvlGo4s00qUjNq06cobSXUV\nFQp5arS0k7IkUpj4xiD6cIyrlLO0SW/jNgTvOfQFtSDjjIiexFoTiLhelE//M/CDfjz8TjAgquUv\nTbj68jPReHF2zCCrLi3BLsG5MGEHuu7mCMOQhvFAujB+H9w5yFiHGbM0p2UxbVDSQK4jKuQk0Gj4\nFwPrGfgpiXLCgC6bEkokXDu3+3LKJhP2MPgHJqS0oT560VuPv8KOc8qHKC5sAbWLwGm/oIlVbCJQ\noM8suwWPkItjQYIkMLtoQ1rVJNj5wKz9gG9P7mV4KrLTQC4cxX3Q4pB4Bj23JRJTwAnqaBlfzDLU\nz4PO+Ed5ylw/aK/EfALOVV21uQ/HqiflaWCW/7lw3lZeo4zcujPvfiEwLXhnRHv1q8hFi6CC2IVm\n2le4Ef7l01OW2lxYFi75dUarr4zJgcJIJwjUdhK2lCJgAoYs2yes0GQz1eMCvOGXskkjjbE2y+Wh\nme9MtbGcnG0f4LihYx1BIMmPFdmlF2Bbdv5cG1oCpukjM8fU2Bc/825sHl+h7Gz7h7+5zdZv2muD\nObuRY+Y59N9sODaKsydNtK0sY146cwLHHGVbPQPp/U+/ZIPRVs5kJ7V2xUugykPges8tV1v1gArb\nzM5vLRtfc+EMu+6K+WhYSrEua7N333SpDWWjyPMvbLSjR5rYuVxgV1w82gbVVJJFwq46fxKapXIr\nYRl2MALqO666wEZwTmgOtn0SJLTUnqCvj6kqtsk3XGgTa8spG+GEMJ2jeR7f77/2ItvT0GBzJ462\nA+cftS11h1mqjtmnb73Gbrl8NsuZrZxNOs9e3nkYu02ODrrlMnsX5ehayckjBtjNi2axCSnfnn15\nI8JZwv7hL9+CFmgCeTbad37ykN11969s/gVz2Sg02orYOFXFgZZ0Ido9YTdy/mA1GuRcYKosLGDD\n1FzTJp98YMsGb7PGDreOS81KWFJQm0nIfDs72OfRFsXJenvzxVM557HUd/lqk1U+Jwpcs2AyJgeD\nGLC77JJZUzjTsx38sPyOcFVD+1x30VybiWY0jiZsCEuid334NnbUr3ZhuBYt9Y2XXYANI8LGoWbf\n6f6fn3mvXTR+IP2dI6zAZzPN9xLazZySiQgo4Bj8iueKIs+Fc8rltIU1WxL291951i6fO8jeNGuE\nXTBxMEfvDLAv/deTtD0TJOSaOPak8aw2tMUYvYoJAE8OxzHlYrcoHq1et2nnEVu57pClEuW242DC\nXkIInDis2EpLEKBpTy0DP8lGqy0HtZmuFHMOzh/lHNQaluFzmYToPFtd86u6y77zMTYALZo3yubP\nHm5PcQzQtImD0BRn29Ordlp7F+Yo2U128cyRPLnkYMVuzjKtYD4sAbMV8KQRp49mNSNQ0m/op506\nno9Jn7p7Apquqoxx/JewkLCrFw3hiclLJ9cvY+8rMKrY8CZxWlOsp7FFXofWsYvd7Vt2ddoiPB9l\nwrj5EBNAzB621CVsxlRy4BzcDtpp7/4mBOEiu3DsQPIpstFoxZVnAUeA6ZjALoRQ8aYlz2yxTYcR\n7LNLbM3Wwy5wVnJDUA64HjykCG1wPZvP6rGBLrf12zjbluX2qy4cT+3gdDq7lPFF18X6plvq1duk\nRGNTj+ZTkwTsRrHl1CkQHRfStmpSz1F0pUx4Aqvatd/1YCBIKz3f/W/nCANhyVTE5sMxvxqwNUuU\nyl6DeDSYpunbBS7epSFzIS7cYRzsH2WMHAZ8Gf1L3HTxUr2+2ykG5XmRyl2ET/M6wQc4op3igdVm\npu3OpOeFJL6khk8QMnjSQ7Xcpg0AntoDxGwEn8rIcHx6XI1WOGlwXGBxuIIwKHjVSUP3BGbhgLih\n46oqegt4k0AdGcQ7TgSMyvQdkmJuYSOJpyIsCEe6ClGinjSgiFuev7Sgghe4HeQAn2A8WyfofcMY\nGQUMSbsHJJStZlCYjwUKP67tzq5kn4ELce6iF68cPuEpkspCG5MZ6u8hgLalHWijIISDE/CmcNXJ\nTSZ8QkO42tzThJy9nqqL04QEbFEohfk37YcWwrUk/AYKpT1Bhnaaa/lYf0EACGV5FQRrFJu21M5h\n/an91H+q0XLd+ec3MOBKEKUoyhe0cQQhY5tNku9cBl6d4Xjx7PPwBwKWtf9/9t48yNLjOPDL7tev\n756j574xmBkMDpI4CRAEQIqHRJGUKIqUxF0du/IqJNnyH4pwhCMcjrDDf9je8D+73nXYDm1od0Pa\nlUSToiiRIiUegsQDIEDcNzCYGWDuu3um7+73Xrd/v6z3zTS4OIkRMEN0zfR736uvjqysyqysrKys\nO3B67i04apfQz8U5DuXs44DPTddsRQu6Cp+MTbSFc3H/w0/F+27cHlcgDHawUHFxo+P5jZyO+a1P\n3YF9JpMw7avVPfzh6KfdwNeLzeDPIkD97G3X4BuTyRo7wy6E2Hp7kv7nv/hB8nFfOgLxxlXL43/4\nvV9JzUiN31K1W9WOkd/63CcotbTbW81cqKaHCmD5p5+4jZTYPtLG//pzH+X0NS1h7A/y25uzVg0M\nxO9+5mMcrmCUI9j20d682hSc3/zuq+KG63ahvephm/2h6GIreh1unvrR1q7H5cuyYQ5AIAR6L/2d\nN14bH7jxKuqxqZTFePjtX/pg2mx20Z5l9MHv/eYn0wdpB8Ks4+Wu9+7iUMm1HNvgeBbt6Ecw+P3f\n/mziJ2hjL0LJHVdvjvfv3si2Px2HYONhpnKdacSnP3orzeZQF/5O3V0ZRvP63/zTT2SdHdgeKnR/\nHNx+iENTdGIK7p1opLf/i5+LqQa+NAG2n7p7WZSQAMGrK14cW6BP6T8ONHl9QHWDkP32ZoMw1jzx\nSf80aMcpnOV/8d5DCDRn4jc/dVVcf83auGH36njouZGUP2rQX5f2gi1ubWK8gDKgdMxqUsA3zKFQ\nELwJs4KFrgatKZcSMLzT7Eo+2wQPKEWTt4DFXBAk76HfktZkdv4xqg6MNGPv0cn0ZLB7S0+8l0XU\ncRZDzx04w2J9IFb2sY1/1TK2sGfiuYOanPS5vkKpqX0tYxdhuYNDR+mrkr7xsNK6NUhX9VocGD0S\nnQMcF6LeBQDatG5t0qF2yHtPnMUmE28PMwsxxD0Dwqt7NK867Wj0cIBKXmJ74cXQSEuvBwiZ3TSo\n3mJcQMsfv2tL/Pxd2zCxmI+zmF4so39pegp7qoc7GN/yCaZMfsrnsQtuwBPAaxcw29ddkKi+Vmc4\nnOauoRpvdfYGcSfvEKcVjyqx+fr8xwVBk7fAnIH6a5gKTJw7FQcPnYird6zN+vOd2/Lncy89LMbA\nksC5GBsX9VmxToIoAk2SPwTsxOKgzgnGSd46GcNdMORaawaeowGzeYmUsUAQCk/8gCjRt7i1xRaA\nWjR1pDkxQwRZrsJqlkf61Bo58EkhUVGRtJLfSj2Z0MSGxT8qUkmRLN8kjWUDgIJ6024yibcUWOXI\novjh73k1M/nk77bQIHM5X5dvCy6SG2daRZWSq+BNNiCTEb6CS/MU90/WDfNlEk5/qCmYlMoL/sgB\njJY9DwdKJ+9MQN7E4aSeyEgcAkMyHaGuwsvhw3evHZ+4EbQs037wDyjARzqCb4+HvKc4+/X11vnK\n8FUjpGC+DaP1K4DxbV94t3fBc1VfSV0tHlwckIGxRQ9YFfkcQxmdvVDaYe5SA79NR+KyuCpj0fFY\nNNWOPPtPjVLJlSko061ZhUzdqXi8zR7PJCbjl6Mg/4BHMPIl8GcefnTRrDpTsYdLFDjTU4EwAov+\nFPOqVyYvxDQEJYSMnOgQ0xLwMkl5Yvv7jz7MjUOT8bG7bsZOjm087mycxn5ux4YV8ZmP3BK9aqTQ\n5jCNKQtQru3hoe0IO8eQINMW37hNj26IJqCN5XBTS1zaBuDKCyIULFLrrbjbnpTEH88G72IXF92M\n60Q/bZdiWrTVBZs07w6Hp2ydqHsQJutMuvZT2XYvRwW7cY3TixZLm0hLnmPcq9lz/6SDZ7CPNnFX\nfOkvvxH/27/6j9icrsLcQJvAM/E7aHE3DyoEcoJam1Jg6bEcT/zaZ+S1Hx1LxXmSdC5s+lM1DcI4\n77PbEExtm2jPk9k89AC/AmsfOGyJC+JsswtKzheTWpjBOCYMSmS9xKX5i30ILZm/E8EjhQb7H0z0\nIejXEJBSMG6PGd97NeOTxxtxmnvs+1bLO8WAY9oxcDECwg0DsKPJzT60IzHcMRAHOen9yNNH4z0I\nnL1o5nBARYu4m5s74F3AdC9wM5SO6SGhFvmb6VeZvmboLFPgpz06ja+jZVzDQZ65GQQm1J/LsFkU\nl0OU46JlDqFtpTc1YYY5hm1kg7Fb01zEBZZjFXxNYkf6IIfa/snHro6PvfcKTqMPxLcePBoj+AmV\nEDeu5nAO2+zffexgHEfrPN/ZnwJbGev0MnVkHwJrjXlnJSfRP8ZJdZ3j72G8jLFAa+KFYJwT+v/H\nv/km7rr6uJ5U+hQhXGRSw6aaOhjmlMO4YIEwz7gt14HSGAaA85v1deCTyfGgg8E6Y/jjH97JTdFT\n8e/+6Iexn63+d23pjv/2d1mAoNG3B+fa852Cse6cWowptcmGeZDbhL9MTaERZWwMowE+MS5dTLHA\nYszmAKBe+qNcQQwU4MMx9mqhEj6ZuqPe08dFBD1x6vS52IXJQk0VuiEL59kVxVJ4CQaWBM6XoOMi\n/pAzQET5BUk5MboGM+TqNhmzAiVMROYgQUrUpFKT4yTmxCz5SfK6dkihMsvhXbJMphMYbP6C2c9B\nZGbJyRFmfOH0uikMwJP588erfiTZybwpPasinwzdySHLkThlEtZoVNZs7T6RzwbzwvdCVQ7SmF+G\nY3vM3xaQM0UpNwvIMswH1ODCFirIYXxAkeShbdai5qkT/OWERCqnH4NCunWLB5kIHAU0UVp71W8a\nS2UDirRllW3Mmw1ZAv3arp36hUdMEgOs5bR0qTnrKuh6U9WWuvj8kbK8daQIkeCtjW/7ZXHI3iwF\ngBv7A0hJW5YaJWUZZ5bvSLAnxL1bZKSDOWf2HCcC0B7PxDqubXcdbWLG85xlk8eUlqYQ1g3nbjCB\nqJUs02m7IdTnk8Ikeo+8gUsaSJs+3kgz/hOnTlJqGQsSFO6Mc3wQZZuZ4PIdaR2HTnpUH1fi4ujf\n/o+/HnfdwMTWZGZistyIW6B//T//Lpo38Md2p9oRqil0k2igssQFdYAzoVOT5gSfqiFg085LbY16\n0Ry7gkGFucBifIh3R11Z+GDoAazlznm+TWpfGEcar4lsAIvCE1HUpljD1Ze8TUEWGCkJPFgDsXzr\nakde46lyr8gUP+LaYM1qXG9DI/M3f/i/sNX4QhzHSf4wBymu2X5FbF/RF70KmxTUkDYoz+3DtEEU\naf5ZCTgoPSzHSkTzqfBrDQrTLgaEzDKE2B63TdnyzCNE5ineJqTvoo01l/UaOhW2SZ226km/lqAY\nXsaGPS+dd6uWI7bc1kRJwKgmeO/p8ejoX4HGu4vtYngkoJZRW/BhHW8mNLDhvePGTZzu3xYPPfxc\nHMAP6vCyHm6z2hIo92LPoTNo57AhBTVX7ejHAf4yzDN64xd/5j3J74XTMdwJf+ol/bu2LOdmqVvj\nuw88j93lVg4JDXDBwVm0ktwiNFRw/sm7rsLmcZaDRWfi/WqUe2rxxJ5RNO89UAo7CvSb48Q+mmcR\n9egTp+KTt7Otjk0wbCHuv/8I32w5x1jcycGcOv6F7n0E/6rySAX69viddaHRp71vLYam5mPz8uXx\nM7fcEldsWhbff/pEPPrYqeia7eLAVCN2714eH/vITfHgk4dyp2DT+mHK6eY2n9M5GjrpO4W5lvTE\nePb6WSlWe2AXQ51ofufpdLs4xw7CLfIc2/b8YvysWTsYH/nQu/EGwHsWgpkb5UuOT3krf+7S1BHy\naEZwOJ6/3tjz4pm47erh+MU7t2D7/Fxcf90mDiPtzPGTO2rgXLgKb3x9YyJTUX56l6GNJ0+iwU5+\n5xgsA7fQeFVeezC/mYH2E5J3SeD8R+rIHGIyXx4QNyEkiQO3FNrvONyZBOF/xDPNsh0wyzbXLG4t\ntDSsDgxlZsk1GbfCmQTJ5h4ToxoDGZVajxqEJtOd5Z2/tVdkikYRozZPIUPNldO5jNiVv9oDKOZ8\nqAjDiDZxAPtLycRJFMj5U9A8/51tqQoq5eSUQvm6kcnAN7uLZOZDbUxiBMaWk1ZZ1UryiZf85ok6\n3ALTvxwWdMSSF2FTYccJTuHXSXoOpqMjam8VkvkoFJVy0HGANydl67TNc2whyYgtS3xlH1hfMovX\nwAfJS7n5wMdi7JR2+17tjC5enPjKOlwhrOC9wRiYBV7zas9YJr6qvKoMf79c2S8X105rE7KYC2lk\n6EZ6ZWCuymXIi6vI9OVDXHbi2iUXFDzbQ2m2YH76QcGKGLRX6ofQsIlTfvMGPPJtfmJyezwxgPhH\nf1iSfVfZtRJBB5lSjbOnQx0JZRmmcGawnurTsW4tszihDk716n5HASXtlR2/wJFFZv3nl3PAI3RO\nPOZ3seeWdekD+0VJtEmfb72Cm4O2uQ3byKsdFVA7Ebb6oRvnxRm2NaWxHEdoFW3PAifWLV0No8pF\nRcAGg3veW5OgX+ERG6CbIHz+gVvabWtz/ObgE2/inDIViGs6UldrC80ynu0soIw+fAzOoTmx7i76\nwfJSdCXOYvzL7iDeAxAtylOr1IA/mEutGbXkKXghK/CZlq3rnmbcecvNtIVWgE/xNo8ANYE7GXeK\n1aMyeKKBpnNagSHzF55mWeU3n44RfwOz8fKa6lk0VO/Uyik8CK+5pYsZ7jbPBSh5FTT4yPymMBin\nACE+uuk788vD2sObsvroX9rJ2LBXmvBS+9ZFzCzvZjEz6OAEuBcGaDJSS62T/ZAdZBVvIlAmZgFz\neDXYtLEPgf1G7Clnopvt5hqayu89eiSe3T8auC6N7z9yIN570xYOot1Bf7B0xj52jv4sgjVcyy1m\nYGpiz/u+69fHzdes5jakWoyOcQHBD14IrplPehHFMxwG+8WfuY777bGVHOxB+zeFrfJh+p5+p4xp\n+kNtrrfTuY1/5NS52HtsEm8KPQip5/jNOEN3vG75DJ4Dlsdptr6ffuEMKMatEjjMXmQcTYHTXg6t\nfe7TNxFXIwf0jG/Zf3joQHzpW3uxAebWu/l6fPkf9sVvrL42Pv2xXfHh922GdnCiP9gfTx0YjYee\nPOygAi77lTFLHzQZAC18h2qzukA/5XySvSd/pH0MRGewhx89Hre/Z238V792C2/nYwiaw1SYAe08\nApTs07dAoIK9tFunWyVvSIbXvGv2xt337ucU+UB8BPdKt9yArTB77JMgsxvzmCY05BkAF6COBvla\nGdP8eI3g7op2r924Fjt24hR1wc8La8+hVcpJYCnpYoy11wDoMnm9JHC+kY6SEiXH/HaAnn9sl+IL\n1/I5xSSRuFpXOzHTibvi7lUQwyAkrSBlOhd7DHImnAYzgysyGb8ngFNwyYpyyoTZYDDtxIsx9jRl\nyoXTkbNEDPGl41oIUO2p2xHdrXG2n8ZY+U8m83eSVuCdwdB8ons1UC7u+naDshWFOMqWPREVreTk\niLNgrjdrwHSciKT5DoW1Kk3m94NJCcqvw3gHBvqLjOGkCkELqwJxT2ss+pojub2kDZd6khIUQdDq\n5MTSHdO1lUz8K/mNQCmjMV0yCFkgggKdMMM2kCWweSgrTxw4tbI4RtaEScEJmuB+oXc5IDiNK/SX\nsmSwOf+/pBH/JT4KbK8eb7/XW2zbsMXWOz9OLdPUooCsEDAUk7UVbDktp34nAgXtxYh79bJfiuSX\nprWYUtSF8uwbYumHDrbbJlKD+V8K1qQwIX+eoPV7eMUyJm+mAvBq3kobraCnsNlF+3oYW72MK296\nUXApQj7Z6bMcZwibs4z3OQSfJuPWsZsXHlCGECqGMtoZh/30LYsJmXdBcL5P0Okn5mMmEsriZpz5\nOsdysMx3oeXE7OJDJbr9rMAhLTaYzNR+Cb9CkMKuePPGqQ6219xt852pFP5FmrZp4w1MWSCt3u4J\nHL+fJQ1bi5STXg8c3oxXyJWx7oxSbKvVrNUUxBm7vZxWnWmu4pQsQkHXAJNwETilRXEjvI4Btzi1\n4WxRmPgskztCR20Sn433YjdX7mpH7045RdDtw2F3k77ZtZbt0BWrKIMxxWTdw7i2bgXeTmc6Dp6k\nwNnsibNn4SOMNSf+GjjwdHEJZdGaArH103/z4L/B5Fw0S02csc9SIsgAL95zT6I2LhzdZXwlvskv\nbsuyqeJ35b2UrGbasaOW03GiyVDv3AjjBgfc9H2dfpjt6I/TvdtJZ1ssXQEMisl8ZadAuOWf4r8P\nzZaHKzVVqKEZk0fNAac4TmFDeBw3xlCOi9Gj46cZcAPgk0U6dXiLVC62GR9vOlCEAs7De07F+J8+\nErs3d8U6TpPrY/LAyRbXWnJQpYkBY+dM/Ckn1p97cSS24W9y9OxEPP3iidi+bVMcHBvLbfV5HeQz\n1vYeOh3fe3AvLrbWx/TUbDyBwPr8kQnww6ilbQqo33lgb5zENnPNMNu52DY++OxkHDnNPMFhnKNn\np7ky9Ok4fAQ+neONRQgLqOdxJXQTmr5H95+IEbwaSKs7duAeiaswv3vfPmwcHT+MI/CiydLsbEd8\n4zuH4umnD8DHcOSOpnQUX6Bn0azuOTxOXSxK5BGM04e4s/30f7iP26Xw5YkrJ/F8HFOVpzig02BB\ncXRkBp+wT3ACnt/CBA3vP3IuvvS3T8We/dhcY28pE378+ZPR/Nq5OIzbq1nK/8JfkWfvqliJK6PT\n3N50dP84bqBWxDOHuCiAsbP/hbH44jee4NDfGP3AnArcL55qxZ/9zWOxdw8L4yZXwO6diP+Tk/HX\nXTnIAqsv9h+cjHEOEe3YuQKt8TS4wfZXOoFm096fNjFLvebQcEfDmRnP+mxsMP5zboImCiNu578I\nY+w1Ibm8ElR8/vKC+m2E1snQYdT2GpEMoBpk6dolGbQMWNFI5sfEwYBucRruyMBOJgFOnjINtNSU\nMBGUbVce0XB0d55LYTGFqlILaSGAXJUrTvXGRC8nXhEak8ULCATCD75g2nJ6yjR9T2MsljUOx/Ds\nYU6ITpAElxus+Md7NsWJ3s3ArdBVBVtVhTaR8GVxqcHi2/rmqeP45Fluv9DIXYHNCdR8pqzKkBBT\nB4PmiBtK+oZSuHKCdRLPLU1SLJ87FluarKpl/NZF9jKh2V7LUjidj/H6mjjZtzN/Oxk5qcosncbV\n2YqVrto4JWPrlbEKmrYBmGQeWW4tJuqrY7wfvOU74aYMJ2Jw1alFu0CcD5ZbhTcST1rKXDF9NFbP\nHGKmXGCbDAGBdrjgONV/ZYzXhqmptO+N1fnycLgmF3OJu2xbgdux6AQlzg+ePZ3bomVU+r5qH3mZ\nABIeBAPxt2VgPb2GQICQoGDhGFZbqsGC+YYaZ2PzxDPYdpVTwOqhzwslCQspKXO8vi7OIEyokVKT\nqZClUANQQAQk6VZnjDooJ4PlKwqaqLSpMwc0J8cpq9k3SLwaRN+VvuVHO22lmSCiZPUF4aUTR8G6\nY5UgroAlc/aqNeXqyr6j0bcK7TQTbzVvJErTV2HJJgznAwWK+W4Eotm5FXFqdjCmOldnrWXSgvpp\ntGXbdgXO3FZEALBehSjfzXaOxoMLX2biQrBwwBIWOIBiW7IFSBkrB1bF1WvWE8UOSElCXmwCEwl+\nWk8Pty6ti5NzQ2iJVpudPwWVLJKPdtuzVCu3/pKqXWucddyAgRJMLxylgKpvKuEw8yQey5tSRilT\nLaQLZz1sIA5m+4fmTsea6f2xEtoX4hkWJKf6rkAoUbNmKONYmByLVUh+IhgZ5xjJ1gJ7e9yTUD5V\n8rt0Bc+MHW3eT5z7YfRy+EuxubRFjJ5HCM9vJlAOPKmJsPLsi+Ox5wUXZSmGUJtPClLW25mHZ+5+\nYAR8eDBHTWwnd7ufzEVdF7zUQ2HOFR6m+f5DaEWB35Z0IJi7YHb8iAev3Zya7uQK0eNMIwjoNKfh\nAZm8Oyhi5FxnfPOeM1mWC5UaAroHs27avRmbzxZXMp5k8aumdyJue/fVMYs68P6nJoARODX3YLDk\nmCTmsb3j/JUFmtBcGEhopZMfGOWeEe6tOGl++NQoeB8BVrFt3yBeMEZPcRvRN+4p8cqbjscjZ/Bd\n+r0TmaYzdyI4aHR4KvYd9hpNxZLOODHdEX/9oI7pMRcQPmB49CA4451lH0JgPPw9hF+eVXyoQT2J\ny6a/upuT8PB31JnU1RV7Ds7FXvKVQ0P2/3w8fQxPAOTTLtmQNJLDIj8y7pU+TFHomv6t98ZpfMt6\nlzompLwwl7BSpjjj87VLzOTviA97dim8TgzICF19q4XU6LkIT2aWMUgEbiPKGSU3hpqDPoUkBRtX\n42p7NLVH68ZWjMKTLoockJ6yq8MELjBaR69/CgKW5if6O5hzs8bNKvmOLwc3BZS6eIBpuF3LR4x3\nrKMmXQKxgcI9y7LCWVZ0c65O+X61YI0G4RGKZH+0Zw7XNLPa4Bgr9xA2Z0DrNKSQozaHtsFUrasT\n4Vq8eKmaDNNSBzpHKMGtzuqdYognmg26u0CrRK1OGtP4hXMKaqYqUs2HqYAIPDv5scgmRnat0FLB\nQaqET6KnJMppgLdkLMbTlmyjZeT8Wmom8Y8dnPMcExP1teAdHM3S0w21nAgVML45BP4G2lhhzon+\nzVeZsNr2wtba/UGsuLPnvA5uTg0wbU4foJmjjaPEQ1vgVONKKY5Rsem2VAqcyZApn/FpPT1sueZI\nzP62HTkCeGMrxTmfjIfUMNHesrVuTseMGLeXpB/HqdfNTaYwmmDxrpRWvkt/uoVq36ElZas339Nn\npZ9NZ1/6zZ/jMOvgK8vy+9WD8oCnZ83ZVEuRY4zffGdxWe4rl2HNYj8PHqBhcryLt4RLkOAZ0p2T\nuOMx0zMWhC/tPnlq1KZjjhNNTQSDQuWUaUL+hM/FQ/pFbMPGmwvBqoSe8v1nG5r0odpDR4N/WZbJ\nXkdQZ/pmQ4HElqitlM4cG+yO1LHrQ8MoPKsROm2tOxeaCL35UHCnXbFP2ocr6CqYuPAy0BWgime/\nL1agUP+JaXlMm7IWlW4H8T7nDPHgn+YkwoegSbz+RqUubW/V9rlwyIWamjdSZ8s4WKRApYaz4VYy\ntJU0RR7Ex4TAucLUmrBklSwiKTE2bl4XK3C8/uLROZyfo92EjryCcmyqN36Ifec+ttkdiy3Gmdpy\nF/8iy3kq28VTmkJJd7kwtfiyQNT21K370tdFwEqhNVtNq8gjHyrtJpInT6rbptIhllVwlH2VdZqO\nlOLMtiwKpqxC7kDQ9pcGeR48zC7J4IP90i6nHV+VszhZ6cd2ttf4KmnJDZ5yvJHeuMXlZhtfo5x3\n2uslgfON9riMXSElt0QhJUaYpx5zAmS7zIGetk/ES2Rl+9fJnNUvnHYe7UYGZ7OcmN4oAK+Qnno9\nAFMIyJUvtiq11TAmTjGy+qrB4BUCFOAqQn+Fkv4Romm3jFUhhW/bLT7U/IrHwmjKpODEpA5MQVTh\npQj1snHfy04Lg5Edng/n8VjKyLQIIakNzXTttHIDGER2EiWV4HeVrx31Y35ZijA3utg2q21knLhV\n7BY7QhVMudi2uU5HMGj31I9Z1T9atlwo0Dcy+wZ/agjcGuYhIRbvbmequVKIcnyJP/tQvBaNuNOn\nfezWtaA6IfIvJ0Dpp0yMuYWV9KCFlxNCGb1JSz77M/tsUV9JN0bmO5/bY4qnC/nzx9LH24QB+13D\nlUrDnnazLCLnsFMf6d2Yi23jFETeGX1GO6tFEa126ObawdYr2EFD49wU9OVvPhtnzxVbfJUWHvrr\n4DpKzZMUNvdza9Z/Zht6/6HJUgplpiVM4lGaKLsNCnz4A0C4l465UWh6Ib787Wfj6MGJvHfdXZgm\n0u0f//k9lKOA6s4BfBc4PDz4o32SLJOUzmzmle6FzwVUJ1vZyT/5XVyq8aq9sMo0pLWn5QYuRN1N\ncjfOBWwhbr6WwjsGA0sC5xvoaunMmxnKSVkzlsMnHo5RgHKlqTjpCl6SL4d9fFYg9duVoEyWMkpU\nSZhv3+yHJC1hyxbcclaLhFE3LiFGcLcEMGz98E4h4s1W9WPk94BQnkaU/cjBFD6BWOaTK2kZGJAX\nMQW8gsSyXiQdjFRBTvcXbtWlo3CFTFHJhynFd0GmkQ7aYdpvAABAAElEQVRrW1kxNd8qGLkA8FX2\nDnl8f/FCloZW1+8GZg/z3ZtFO/f5HgfvaqWpF4Zdtb3Ae/HqvxgliRlHSA5PJ708RWqLxCuishoi\n3qt78C71IjS4mLA1BedlJDr+6FH8YbrQqOErJXuJhP7TFYwOqtVpe/a29GDJ3+5YqqEu+yr7i2oz\nlL67EGce/6ovcdz+XWKXPt8WDNjLuTxhrBQhy99q28ewIa/hHNwx5ELsJz2UUe0Id+xWvxymZZzq\nsm1ythF/d+9RzsF4SNLFl9gScy7QzMkW82gj/vYHp3gnHfI+hzrUCq9MEoGmPAiJoT+sT5qEutB4\nHz0xy0nq47AeaZI4ea220bzPuQry1pPFPIUodGbZCSdFGRJM+1OhsvotRMCQEeRJgdPElI2JgBDn\nIUTi1fxahK3xjZxiKbwzMfCTT+0XtV+ZaCF0hSG3z7UV1AVLumSAmBSoZAKu7CQv2a1CZzmRK7n1\nwEzYSue92j0JL0XEzPTmAC1kLAOXjauVAkbqUNM51r2OuZvVKIcjPL0qK3trQ4FOrBXu5VM5vCNr\nNYgnT0EXYbkI5k5T5aRyO03i07IUHpMVt1uioGrqFPf5LuzNdG22SExVtwITULRtUHMxcLHQQTkK\nWanJpWYPCp3t2YL9JFtkeWqUBAhDeZgJGC5Wtdmgi/ShplINvVoJeyjFTFDn1nw50OEhLMYy7cyF\nQ6awWfYoCWmflmzFG4KeMPWTWmzPcgJl8mkxMeYOQebQtETx1r+qj2xMlpb9VyZY4+w38Vb6sMRA\nR4lIvxfHl7dLn28HBqRAaZRvxklTG0V+aacoq/PdeM96YtSQv0OmoBybcKFUToCZFNTkB9AR80ke\nCJWHqdAQL4zxpDGGtDOH9szyc2khZw7sMJPPUE6hB4VB8S6CS5w8z9/a3idfIo9b8m6Dd3AIaIGD\nCC08I3R7205qNqG5FGRJRM7yZy+WIEco5dmf9FsqDaqU8uPS5yW1+Q3msUWOhQqiC2WXNEuf7xQM\nvEOo/eJ1J5vSKRglM02iw2jarQiIPfWKELvCnhOw20aQO7Z7MF0mWglUwchvNUemmYfYL2aQ5ciQ\nZGSyJkl7urYMzUKd07TT/F7MFC5mza9clgKFNpnlRhEhFAqFYXXCsCIYrlpjT5oqjDdgst4rLNNV\nfO7hAEthX3zC/VpuzaZQ3xZU8tlWZ8sXfVuTcaYD39ifypzdwuKDfuCv4qakuFhB0UssW/gM1601\n+7FdZUvR0SBEajDK+4tV48UspwiO9lliD4Gg0rA4lvWLaN8VtJXPssRxTGuTR5z+YPnyUVcrLSdL\nvDDYTfa8fVCjT3Vz09Okbz38kFrJUl5pjZl9amtLqv6mYCF0dDNksv+EqyRuR5htKbyNGLAfq75k\n6YKWTc241ySyXMl3s9hTO5rkp1XKtxHgf/yqcyxDUTS2Ejbzhh4GsR4iXGhrctVFghaCuW7kHNVu\nvXcgJdbRSGJhDdsilkhpIHGcBTr6+Y1gmcKnGsbcTtdeFnyblJD2rZmXVAjAxZ6evpFQ24u1xX1R\nPSfovM+ysx7pX9EB+3tMYoQmDxaaRnjJaFyBRR6uwGmAc+glIhvVBirjlz7eKRhYEjjfYE+n0Tbs\noRuhxyvcvPHErb9yx0ibrPhSCyrZ+ZWGzzCB7oVJ2C1aPAizCUcph1reIACvlJw6NaJOTWFug8pI\nJHuhQM+kaxq0bLKNwqxeqaCLH1/Dt1w/LpD6OLiU98KDu/7GKFpeJx8ZD3ACaxFxrN/GwJ5gxkMN\nTzfu5736Ns6icyhjqmsVhyJ0EwVy3QLSjghme0EILWUWCYdnJRP6yOvXSGRtWUcHmjYF0sI87a83\nH8p2ksy2aFs9RVodXrC/q02lbOCbr+6ilyB8HuRQRyKmFArUeXJnB2Oa8Y55hhgrtpuletOpbXFc\npf0Yz952M4Drq1nuSFbgaHJIocn2ur3sQTmvJLRP7I8ytTq9Lu6D8qzLJK+idPJypyDHCz4vS9oq\nPz8zWIbBvIvLysilj7cMA4wQxoB9UPrN0e6YQfBAQEoORL9KDcb7/yc55GiU5bQfio2lnFi8OKZ9\nUPRmPMsv8DnroVTpwhPsXZjj6KfTaxkXOAzq7kNBmePfMoghwl0baas43J+G7typQJCV76vRVDgF\n75avLWVey0lcUk0pMOvMQtsfgi3gdmfZap/FJRc1cv2kQfd+1XZ6mfMoKM2G7GXnI4Xo4rxfHt/h\nDVMIwwXy8pkFLX28IzCwJHC+gW6WNFdyknxj42Qsa+LjEifRriSXN89C5AiWLO2cdAurZYKkbF3v\neGp8Oe5kFiYfT22opzJncUI+WVsV090rkzDfABivktT6k8XzKRQydIQsBC61Umr2FH4U3c7zl1cp\n7WK9gtWhyZqJXoTHvtYIGkueYUI9nFjNLTVW7SmfA5TMFIBhVPpTBLeNE9i7jqVoP41LoQXcJM1x\nDeEseC08HMatxjJDYjzz+9MVvcKkWzpqxEqoWu63DM88FyeUEoVdrY1tMsYecZNaeymcgxPvhJCh\nAuXiVH9xSkmYGCX0TTf95KniARYHg/RbjhvMRlwoqJ1Pzcj5WmkxiwLVG+YbpK/duvPQ0TRLs5le\nrugbYFJlwswbOjyNW+cgCfdFl7FaRNxSnOPYohy/jN2uGbyPOK0ygbG17+TbQg2eC4XzOKwe/K6e\nzwO39PBWYkDBhj6wh4qQIlUX3uRCxYOAxdrdni+LwLcSvLejrhyRSm0qAZJfGSNWyu5N4gr6USOp\n8/oFdqQWPOxJXKuBVtAdMnhep0InxOEBnKKwsDwXZeJa+rAK6A9XXy0WZhNoSBpsxcvprINspC3z\nUuf5A6zusiWEfF8IFWeUg5lbryA9NbfgJ/GnuZybmzARo155dx4UEjApV7q1jQi1mpn1kr5OFdP4\n9myQx1YvpvYLNS49/aRjYEngfIM93IQJ1LnfdeX88RhCa1fD5Y8Tq66SdDytQKGA4WTowQjvyvUe\n4oGmzrLxwYYN5RiC5mjvFjR1F3eFp1ZBoVINqkKPulV/u7WveyQd4+bW/hts85tNrv3iVO8qppbZ\n6AcHAws6Rnd7WyFCHChYwFgV0JJpiUPfwKzm1W41Y6xzecx1DyCgL8cDNAJrN87d4HRm6ezime3y\nsnVemKMMMjVoMuZOGF4/5Xk1DNHzLVxLNXDZ05TRwhgt5KIEWXPbaTpPlQZbgddWalaQHF+unzEX\npdKLXIh4d2KzLzy8RZ+xuBqeO5RaywYud3qIc5y7NBB/trrlBEms2bp5X5s7FX0dujyCEjo2IVzi\nDkphc3AKdJO/ye+OGfpcP40IJ2o8M1R9YekItky+9d4u7i12KsNoBVqawx+5315XWvDot+nN63M1\nVfK4FN56DEBPxUY6N1HhO4wQeJJBuk63WfYlv+yxqsczwU/oh+qH8g/cgJ9KWHTclvaDI3j2Aou5\nGjQzz4LKawsG8c+8aesg438hzuDkHdedIGyAssSnHJR/zEFSrRcTLDTnYjn3hv/6L9/B9ZdT8Sdf\nQcmBD+AFFo6dON9XJKUGKJwnBEPnqopekg0qL1Yx9iPP+QFhO4/svGI4Pv1zt8c3vnk4HnnmDORW\n+KftKe7A5AtCpXhLHLW994Z1ceft18ZXvv5sPLEPX51cNlB4tYUvhXcSBiouf9m2+cLU4rRnqGIk\nan/L4iQA3jobtkOZLCWoNlFVL6rs1e9F3xLRRMeKONM9FEOU1cPWdd88zstJ47aCht2lBhgHq0cJ\nuNjraAdnohZbwstiDEfWo71rYrYHQaQ25guYjIKK7mFMqPYuM+S7hNDtR1aytTp3A/OdghKr3Sbb\nyQtcL6ZwICM3yHzUrCZLKsvhnI+Fr9zjnsle14cliqtsY+YAGn7ogkl48iuFxCqNcItx/8pTCuM4\neW71bQJGdFRT4GZ+BL+jaLeAyfaaq42kkisFETecOMGJsHkOAf1sz+aYqg9i33Q2+vrxWaqwD2IV\nXAqUlqJGU62JDNiy9fOJI/MetoN7eFboZqU9z40sYrnkK5MhPwgJSXl8w5/2mgzX8mTtlmXvqWH1\nAI6w5KZy6Zs3XP4rZMjBVcHtt+1q192eNNzOKrD4vSgQ36aOHMO+devNw1qzTAyeKu5inPej4ezG\np2gPDt9tXSmPEkGx2owyRsjdXtwUl1edcap7AwurdWzIYwYxRX+xndfHgqGjYxoIteutpjxHjDCL\nt9Ifxc2KQrq+Ay0aS1h8m87OsN2YY85aq3a7yOJntn1RtFFL4S3HQMW/HP3Jv85/lz6TO/mmjJy3\nHLy3vEJp7HzIsetv/lIQbz+3MdKJUqMfwfOmnYPxy5+8JZatZJpGOTE914yHnjgbn/+rx7nOcags\n0KHvdHqfZC/nURPZjC0bUGz0sdDugR9x92P6g3UbDprr8CID5pOyYwC9QbNl7lEcEFJ5mHyV3mFR\nnwdlKd90w0M9ce0Vy+KhZZTBYlCTIUs1jTs3RWlAj5/nK81Yvawzdm6oh1nkDwZNZdIOO2mdOFFB\nHaKmuBlscyUihaWoJqjMA0/+hvFYhwcSE7eFZWTZSx+XLgYue4Hzpah16BdWdyG+DH6GcxnU+UIC\n8zd/CAMSYZnAGLVGlQ8fCJZZBTU53KKCn8UTZJtHYFoNMfdwjaE3wzrh5eoRLWYTgdNDMD1SJMSh\nQ+apzlUIq9tjpG9DzPRwpdbAFAxhPB2jZ7XAI8FJYglfEqPkVJh2TtaDbkF6tRdX0s0OxNQkTr21\nYYQ5CGuhOyZkCizb51kyjAChlEdJ93WFxA0pwZPEnoAZp+BLEVkKDc7S/ZHpfG8M6ReFfM2HB2jO\n9myAT7GNhNA30Bxlg9T28BJG5HeW2GbC2hJOcdL7TPfmGOneEpNdXE0pjmaXsaXOnS8D4IFr49J+\nMyGq8GaN2kAJiwJMuePa3/NzmDNMcs3dnHdfK5yaTgFezJneUH1bzhsJMr/KeT1lZDG2yIWEOLOP\nXlpTRrzJj9IL1l1ChccSX37lm0wAjoUrPwTRyCqn0cbYx6gRGYwNNJfjPdzrvLA1J5RlzdO893Qr\niyX6rMKgCxA1HI5d+2gO7wgTXEt6on8XfcXtWDqqxvykNWkeJo0eBFdtaB3wIkrJNeESR9IkcFKO\nmjEF0AUWV41pbj2fwb1U7uGpUU0E85kZyVPaU1pT3mXk0sdbjoFc/NIRufjKrqjGYeFQZbRWPfiW\ng/fWV5g4cCyXYX5Bw+dYL5FJC97vzfgeXr4Qn/3Zq2Lbur749g+fi2MjY3HDNdvjys09HP7kliaE\nT+ciMdjXUviC8ti+9jyONag/lqfOs5PUy0n0Bd7Nkl56qqVz3GInqolKmsew7d2BAzcKhMUXCnJB\n7s5DByYyCqTSa50yUbZmO9LOFLo3eQ13TpoAuKMmf/DaVaeKFvb70mcPdK6GNUlbbauTpTslHigD\nKdK4wm2Hh2hz98t8hORTLDS53aNFGcyaOc9aRy563eWg7Qm46ZfCJY2B9mi/pGF8VeByUGYKnwpT\nK+RSpiFfVZNqYXXGKBA5NZJGQac9UZayLpRoypcLEpj3RJ/o3ZluhtbN7IPoEX74Z/BmHx1Zq51z\nJeY262RXL+m3x9nunQifnNxtMPlOeHgCrWU/2wwIPhI/nAHQZCNA7TajjETikq4kWJnMwiDC5lBM\njyPYttieSILTMNtcFfw/0poU4hK81/UhzhJD4Ed4UggBjhQyEkRSsPUjLqyzBWPgZwk+8+S7wmLZ\nwgHuOozELf2Rnk0pAK+aeSGWoyHuxiVHORRk6iLEKOio2TzdvQ1t8Ka8acYtnbwDG2iaCB9zrT6E\nTmwF62NZn8KlkBYwfIbpwfhsugJLc2YwZiaWwwRhjnm6UnAVSAH0ooUKCS9X4I/0ycslecNxdoDj\nTaH5QrjQIt+VkHZWtjZftmFxYNl+JxS0v+XAAVt09JfxnkpXG3W2dx3CJ0YR013cEnOcRZb1UQb5\nCqbRGFNMNxMOess4hxb/BFd5zmA+Ug4YuXVOSg4RTTHuFb57eqzDIW2fUZ5jLfuiDX3SJmkxgWhM\ncUvTDAsHtfnZ2wimORkJg6UQcpyat4rI2KWPtwsD2Q2L++LC84Wntwu4t6Pedqtf0vgLP5LnI0B1\nMbSXreyIdeuXx1PPHosvfHVvjEB7X7sHM64+dgtYdEmeNWw1V6/sjavWr4mu3g7uaG/EXu41n6eM\nXmijF0LfhinRhrWD7CzU49lj03FgDPMjTIvqlFfD+fvy/oXYsWl59C/vjdFzHXHgwDjlQ0MwcwXK\nnvoM2knuSF/dHWfOjEY/ttTSecsyoOitwytzC//ZIyPuI0GC87F5uDtWDy+LvdwdP6f/XReNkjp/\n8p4ueEQfETu3dnMDEvbdc/NxgPvij7vZl+yqMwZozzVXrI1l8IiJmQ6utDwe401NAagFACxOm/GZ\nbi+hcLu/LObfjl5dqvP1Y+AyFjidWIpw4VOZhPxWZGkHaFlyrkg6Bcyc0Iy5IJqUZ4WeKuMrfbtK\nM5+bowgwnDQ/07s1oVg9e5CDFaOo+BEgESKFLN1YQBlj2B6e6dmYt2zM4N5CH2gKpAsQ4wzX2Lpa\n6+ll2zBXkYLWFoHYsnYDVrgTWuDrwBC8wZ3NsxPLsEHsg5jtQgQr0rqtfqG1PL7JUPBqIeJGCATN\nT1mj3ENcVwJermvb79ppE256BqEmmSmfOqNvdXXHWXDnqrxryjaCN8tRcCbtLM/TgWazZxs425J3\nLltngceSCnOZU825sCx6l0/BRKttdeoGrurASRaJsDk3g1Z0cgU4GxCJ5c8yGSwpr/P80lBqe2nc\npfurYBz42kJX6R/hBRcpVIKqpAxSvqSpZaSWfOqj/Luw7e992KZQ+J/t7kks1RnfK+a4FxphNG8i\nYsFQA4l6cNCSdpRt9ON9u7lPfeV5jUdBrzil9PnemEHTqZzcg8JygSsuy8Ev3/vnlrn9wsKAQwYz\n0wNoN3Gkz1Zg6XunG8efiXjMPOarKP8lDTTBUljCwCWPAU1/UjPMMJ5nMT3TmI9Nm1bHjm2rY5Tb\nhea4L/301EzUuhHiWpPxvt0r45984t0xvAKbdIc/c8Xd9x6O79yz193u2LZ2Rfz+b94ZQ8tXcK14\nR5w+NxP/9osPxb6DUOl0M67b3hu/8unrYiPCobsPHWhXDx6eiD/8/ANxYhyBFTOk3/zstfG+qzZE\nN/eSz6IwmG/ijwUeo8mWO4MfuWVD3HTd2vi///SxePaQdBzxgZs3xYduvyL+1R98n/vRMSNKBYx5\nmDmpY3lvI37t53bHzbtWJ8XWiBvhDvQ/+/pz8dgzI7EBE4J/9tkbY8fWYeY6BNt6V3zj/u744jf3\n0U6uQU17bxQF7OxpI5ozgzx9KVzyGLiMBU5x255mFTAWoTqf86OIQznB8uF9sQojZYIq05M/U5ji\nW8GuzGB+V2HxQGa6g9A8fWw+x/hsDWGyb0tm65qai8HgbjInVWZTtYFT+MB0G/1M9xa2NDjwwuSv\nz8HO1FRqg9mPppOCOP3exfa6dpHh9ncG69ZOUpLSOoft81m2hCdwcdTELRBm5ek2RrhzS30x3O0i\nfsyvRJPtIL/aR/Hms0xDX3DV6VKFmLTn5FsYS0eYkkDakr8IMZYjlhVgvOt5rIe73jmtvno6YmCe\n+3wV1Ekz2zEITrem4DKD+YKHnWTEasUUiAKhOw8iUNoMvHN+cnX09p/h8JBbP7zOGtrLC4SU5gwC\nyyQn3JuDvEa0VcosDSzgCuuiJ0u4fIINLnrkAnViPNtTNJpF2LdJZbFgf1WtK4K57TXKP7HmZQY5\nnhToKLuId1512R+T9fUsskpfDDVPscXmjTHkQehsMamd6V4TJ3uvjHG+9U/rAaQLQdiogQVAB5rO\nWYVOtCjameG3hTqFoL14clMQ2phDkz07NUSfDZCvjAPhEbQCtA9LYQkDlz8GivIAUsDk6NSp6Xju\nhTNx53tWx+/86i1x3VOn4m/+7qk4NcEuD/xrzVBH/NJHd8fW1b3xpW+w5X5mLG6+YWtsXD/A3MKC\nHB49ONQXo1Md8Z+//mBctX11/NTN2+Mjt26Iw0eeZ4oh/yffHVvWDcQ3/+H5eG7/yXj/jZvjgwiK\nP3XX9vjzv90Tt7x7Y9xxwyY0m434xrcfje1bV8Vdt22DMqE5t8Opph/aHUILiUwIX2GuYl7rZ6Nu\nZQ/7dewiqYrVz6iUnewEbyV33rI7PnjL+njooWPx/Qf2x+YNQ/ELP3NN/Oondsfje74RV++8Iq7f\nvYqbl/bE/U8cj42r18Wpc/j/XeCEO9pb+VgKvHzj3teSCy+4/IfAT3wLLmOB0yFchKDyVPoqt8h5\nzBsdGOiKNzmdpoDBwHTfN2erzJ5zXLE3cuIqGkLLq4KxVSi6UydJ/6gdAckJVTdHZ3o2p/DUMX0w\nt9etbrqOsFm/AvvDnQiby/JazDJJqrW0ZMpxOxF7zKlJbG0QKHt6oSAIt4gQCkZARVldCE5zbKXM\nTkJ0Tf0kmk/bHKHjl9kWA14B/Tq+03XJosxpS8TvvFc3G0r5lF0Ec1asKRRTsQJAqZ2WWL+fQkNo\nSwRFc4V4Cq4UO21X0cai/8XO7yyaX+PmZw5FH+6l5hREe7HZBJ8Km5bjdmumoZG2u8W2qkxVG1rv\nDJ5jm1c89Qycw4UOZgcJDXkQTOemh9CmoWlDs6mwKYxuCVlOylXC6i/roa3Vd0ZfDh+2JTWYYN0+\nAs8+2AcK5U48jlt4M+1VVPuRkHRhnGktwJzqR/ltToTGGhqKbgRL389hi6lWfw7NuoevhudOEM22\nO2WP1Dei2dyBsLmacooNlu6trF8AyiIBGBg3OV7xqTo3QX30Z70P7Yj+/dCa4OY6hc2ZqYGYm2LB\ngeCZJ3AZd26qFQ126T8hL6HADYFXEUvfSxi4rDCQbtxc4KFwODs9E//hiz+MqfFr4/YbtsTP37Ex\nPnTTxvgSd6n/zQ8OxK7N6xAWh+K+xw/El7+zB3rsiXufPsnh077or8vLIo6cmox//cc/iD2jEVsO\nTMfuretj48rB6Ob98MruuHbXsnhuz4m455Fn0J7W44cPPxu3XLcpdm1lCx0XZrftWhF1VKf/6Ws/\niPueRZny+GhwUD7+yUe3c4YJSoSI3f/Q5jP9S2t+yTZHnsIH8x3cZJTKCXkSfKXGQnN4sB63v4ud\npqmF+LvvPRYnJ+px7OSJeO91G+KKbati7RoOKbJdrwOXbrxTHB87F0/vHWfxuhIYu+HvlsUf87gK\niMLYpHnpfylc6hi4jAVOUVsmF6fGtlhZvqG2nDJT6OEl2pe81cEsPDs0Mz0Zi6H1Am52ct1milcJ\nakkVShFacl63HkqjHm/OONW9lVdOwocpfx5BU8FpG4LVEMKPxSIsmdzZn0k6uULGE8k2Y3NK1xIS\nmlsTbq+oSaSN82g12VZU2+NWiwwptXQ2RAgSJp9/vKCg1aJBuaVNEX5XJx/znXJkamTZTqmr7URc\nFrdKDQpx4sAAvBlgLuK/B67RwWo9hRdXurZfJpE5FLmxEMRlxwhbsN7LPcABoVl/92xAiOcUJmvp\nDLQP6EjtJ4wLZMriEqdyVt5on2k9nYPlIJHa31k0m7NsxTYxPTBNOfVsXsYAmdVAy7SK1ttyisBp\n+9RAXy7BNVRuiYELR7cocfwRyz8OFyAsduCaKg+pEmvLctj5iTDpdnjBhDgmtPsxNduWR2TpAfOx\nkc7kdg6bTm+N6eYAQC+mJJP14Tjefy3b6GvIAf5yfNPXCJelRgnG4IQKbLloUYuJqcM0Cyj6o6sP\nrQl9vcAYb0xyQxNjnuMGWb+HEKxd2nGBmMK0TRS4SpBOwds4/pbCEgYuNwxIE4pu+rdEiTEyi7D3\nlSfjez/cFx98/y62qjfHZz56VRw6Nh7LsQyqd3eyZX0yhcUWNOklCzUWgj21cegKQ6WxyTg9hkIE\nVcY0W+GTc3PRj+eJbkxV1qzsS+3gji0r4/d/+8PUypEh6KYPIQ9XKNhXd8fyvoEYOzsdZ85yaQNn\nECzj0NGp3L5PJ/5wgNSmQof6C/XQkHe36/fTdavGUvNoOTsVDpkXNa0a7KaePm+964hf/40PcJ97\nOe2wZogDgVJ7fXk8g+ukZ18cj/fduB0hdG088eRIfO27B+I4O2GaW7n/14HHCo9GLbBDotDpvLAU\nLn0MXOYCZ3uQMeCcQJ1unW3nGe3lGi8nPAY6c5J++xQ6fd9CCMqZlWcnuL6+WvT2sW3nFsBrjFtv\ntVFTpujkFYsSqhOs+SbrnobGXhCn7m4zj6PxmerCsTv1mKb4yCxVmN9yOueZbNsTcKPBwQoOAS0s\njEdP3xniPSnMNnpzVUxwGt1txXmNsDHQKXdRCyyE7gEOmJXlvWYDXmFMppCZQqWYLKWoLcaBYpZb\nnKhjZL5iFXWXenPLlnYq2ORfhTsnfhiecbq48EknxAorxeJGvCvENGOabw/0nMEx+GhsScGzhZ3r\nDO54LE6BsK3flL0QJ1O2rZZuew3UQD/PzpoWpjmAnWsTY/MpNJscOJnnAFIxOSAPuOqickWxDjSk\nOWbIpG3RPIyyCJpCfLkERG9sq2r0nXZcxak0+Ldljnfil63k9CnjvAsmnbhq00sK2g7dHJ9FkBMf\npTfBdApy5TtPq7bfmGIaTfRoD1p9Tsj2NpfFqb5tuPvaxBsOPTBuW+l6hYNzdlSOF1hNTqi6TrGX\nCCmMFpvm+Ql8rtIfXdiJznFAaJa+W0jNJn4fSJ9aaeARNk+4pvLWiYw2W0UJ1dPl1H8V7Evf73QM\npNkQc8o8vKiJdtAwwa7XE0cX4vm/eAIH6s244+YdsWkDyommlMbYh6Zr8/jZxL6zDjEsNOFp0Efl\nrkjPEf0QTzdzywLfyZYhD8U1FYUHDozEPY8fTiHRu4NaCIwvnmvGyNxMNNkWh42iG2EHgt2MOu7J\narXpjGtKeFIeBVpWNwKlLt3n5yeiR//TgO+8Z0jlhA+WlbyHA07j4/H39z4VU3PO0SXtPIeCjp5q\nYibVHf/mPz8aP3XLKoTsXfELH9gWy/q74g+/+myM4s5ORYfXcgrBgr6VExNWsBQudQxc3gInI85h\n55//nNwkwybbeKdOj8WLh4/H88+/EONjEzE5ORVzEJ00knQAweQWH5OYgprPqQErNPKK/aYfx3Kw\nQt2RE7rCJ4H8nsJO571UoICm29vQ7gQia3JDQ5IIglfCSxpt0jwZvOC96vjhVCxFFOPk9XR84lNX\nxw03bokX943Hn3/xq5wgtN5abN60kRODK+Ld1+7GXmeY04asJllZdtcQ5pyV30SQMShw6dxe9xTT\nM/Px/L5D8czzL8bJ06MxPjUVM7P6RFQwA26IXngVWMQ/jeXPthMPXtU4KhxmPI8pwNhuGZPpWI13\nc1WiDKQF/DoP7/TQlXgkd6MmcwEmMJ3bJ37KJS03tVmUZH/ZpzC4FG7R5m2/clOcGzsZp84oRJX3\n9pslWdaGdatj5Yrl8a5rr4pdV2wqOIRxZjuApcIDOS/9QPs60RY05fBdOLNnQTTLqc8jR4/i3B4b\n4lmEP1ptKBpOcMd4TBzaB4lHygDPJU5B3P60f6CnttBp/tIX2XP0pXc/458TrcdCxyoWWbNMRC+S\nqEyULiLs/3nGNaWTV1bDckMzCDuL/ihxdpD1AyU+Zjv17ddkocUYLLBx4xGanBUsdJYPDcTAgHZc\nUrllWFcFt9+lnZa3FJYwcNlhQJpgDNdYsK1dUYthlCAHDpxhOxmXYtDyHP5nmcFihpXlOAu0SeKu\nu25LfPOBh6H1mVjFEYFVK4dibFR+Kb8t1FgWi9IpealiFrOjQyPQKyTZwvjy3keOxDju4jzck9f9\nsuDvZR4YH52Id7HNvXPDyjh6/HQMYuF07a41SeE1tKTuXk3OcONe/6ZYNQzkByc5oV6PqzZxSEmS\nhzxtkQx6nh0M7hXDVGAuxianOYG/MvY+Mxb7TzViDibezaKyG6DncM3Uj8JgZGYqvnD3gfjeQwfj\nv/8Xd8SVHCBagd/qSQTf8q8948BnioYzK1r6uMQxcPkKnAxOp8WyrVqERYWSmcZCPPzE0/GD+x+F\nMGvcUrKcHYI1UcdVQz2lj7Zg4aSXQZJw4pVESyhTYHm+EFvqy0mXBNbtu3TioxAFdRXtm4SOAMsq\nr4vT6F1c4edzC3+RxfeYQhRoh8DKFq+1ySDUtnEKl631BW5ouffhySBHPPfUGLvtV8VAT09OsifG\np+PwyWOxZ+/RPL34Ux+4GdcWXve3GNIC++v9LAKWOAASJnqZ3jP7Dsb3fvBYnBrFaAeTgK6e4ehe\nsQEckog/DzIpFKrtEhduyebkr4CCYTecqwh7sidfJY7Ub/pTQbGwjfbLjONVeUv5CrP97Y4ouK3w\nzXo648vvkqfkSxEXPJxBbTqPZnnFetIiUMloq21z++gcTPL0gdHY98K9sX3z6rjzvVfHlk3rWXTQ\nFtKW8qvxcSkIMSKwCm2kJFIL9hSmdWN08tREPPTI03H0yOk4Nz6ZPTOL+63qykn7TFHf/svAVy6M\n7DPaThG5PFCY91pRS08s2FfOHgTL8k57oVBDbRo11wqAZbFloQj3pHPCq8wgSiekXoU8lH2e/iyJ\n3I4n+z0hED5h4pO6/etlq28QFcwdt90YV1yxJQYZ8wseWMpyTE/I5wo/1Xd5tfS5hIFLHQOVCUsd\nv5e3XrUifuHD18XefafihaMTsY499DtuWQs/nokDnCQfYx44enIs3r1jTfzWp66Nk2dGWEBviZHJ\nZvz5X3IAk7knORc0obZTkyVPruvKTj5waqQRz+0bi91sqf/2L98c9z19hO3sely9e2PsOzwe37r7\nhXji+bF43/ULHOi5MlasnGe+4UT6VdwYJ7m1vAGuI06c8nshfv4jV8fwqv64/poNsXvzMO6QoF14\neJ4FlAtA27pSmmS9+ciTY3HdFWvin//KLfH9h16Iiel57EbXctq+P/7ln94bn/zQrbF9bU88/PBz\nsYoT+KvwGL/36FhMs8DVsXyZdyr+L5+Qp13qvbsEnxi4fAXO9gArE6KDz+3UPgyR740HHn82+pav\nw1ZkGAUkroPQMhryzlmpzmmUSbFsjecbPhaP2PZzfkld/mVN+eiAzyjeF2FGIdYJ2mgnY0gQ25Iu\nCU7hi/8dXEfm4Yui0UQIgvjyVJ/1whQU2BbcXmdLHle+rOQG4t77uTqMGyVqvRhLO4kjBHT3royO\nFax2p8bjkacPcT9tK34aAh1eXs9tBs0GmjjbVXgo4fxDQqewlwC126Pskc3kt5rYBYzOn3z6xfjm\ndx/kNovuGBjezgnEZcCPvR1wJg5Np3RKXSlooFXLgSQOgNNteNPZvsRP1sjKXdcb1gvTKAIgSbXZ\nI1amVe7nJrFAkbETfGXazF9E2nI/OdGZw34x+G3/EOsJfx6FrGjBbF1poeX6VO/ltDqwzU3PxvOH\njsXZc/fHxz/2odi0jpW5rkngkmUBYl8YStnny8m4t+BD5DkuxGsGcAVOuuhczUKcoOyXBvh88dDJ\n+NrffC/OjrOFNrQ6+letz7wDuDMp45y223/0qejSJEN8eD1kbqln3yJgMmZNX26k4n27ZvvHoA2u\nmukOBHsFxHlmn7Sn5Z3be+IIfStx/APODssT/hy/mlXQprTvpH4nQRZfmdZ4tOYASBm8U3MvnODA\n77m5qZhojMeXv/mD2LJ+X3zq4z8VKzmpm9v0mcdy+OnYsaylsISBywwDDmP/1OAfOHwGZ+9Tcf2N\nG+I9N0MOxB88NhV/+a2nY/9RvJnw+4ucJP/cp94d73/fVlq6NVhjxr1ffyIvApmED07CHxruqEFn\nLXgy+wcxBx3OcyaghZ30H33pkfjcp6+Pd1+/Oa7jhDpsJNgMjEefPQPv7soT4ju3r4rbcHP0i5+6\nPs6dneOQ0otx86070LrCXYHzoadOxDVXnYhb3rMhfv7j18XxEzPxwJ6R2LlrOHesvAVupl33LAoV\nPY783f3PxzAKoDveuyl+9bOYaAH9LILog4+fkiXFxLmJuOa2jXHzte8HjojTI6342j88H2c5WKvr\n9yKYO3/7T45iCWRcCpc8Bpjjk1u/LKDzcy9E6/QjTMLnsktfNtHLRdL3DexQ7jn6fHxzz+M4jHby\nUatVDY6Xy/TSuI7G2ljV8wHc5dwUm9k67qU8RQgnJwUfRYrUijAFOsk0Gl3xwKP74u/ueyL6h9dx\n6hW7FomNSVBj5eJKoUxk/GBgOxErjFUD1UFrKL+tpxrHCkJSgsRQJsxKiCvxF7QylJETKO+BMf1x\nkle7GLdzdQivaY5lpOkKeOliK5dSCLbNP58Mag5tp0HYKviMcwJXLIg4c+yFWLtyIT73Sx/GvxmC\nNXXAC2i7eFGYMFUVqrqMK0JMuTObeCb4Jg185vlT8bffeiia9RXRxzZmHlBiiaxrizwBTbsk8mI/\nKbwKkQpmxrN9qvbWbQ7sYU2VbaNc20wpAuZnOxR2kT/kdpma9iWi6W1xZRbemLLgqOCkMBljswbe\n0Z4s2Ay2z9p8V73nO4c6sTl+KIE6W82pmBw5hmuPiF/6uZ+KDTgiztuLxBvtSDtVBaiELyugzLcm\nWLfjYD6FQ5HBNrVj2Zs6AM+tcm1jnz98Ir7ytbuBcXkMrdiMFgA7YEG1nX6DD/tnQUFvntMGjjCv\nA7Vc+ytHUht/oKngluhXCymYQoMI5/Nsp2cubC5zPGl7nDiDxkS/9bAo0aOA/TXfifU/sHVyOKIT\n+nScZMLzYyCL44NQdR8FzWN60mrMxvToSPR3TsU//ewH81RrucMZgVPYS4U+lPyv8ekYa8mfSLe1\n74W4btWLYAN/g8RZ1OvBhYfbRuY2xZPHhmOicy10b2nimP90RNIBhZWxBCra4y9xRcrZ2pm4Z///\njtkN17WSOns9+49SEjUL8Ykr3xMf3HBVvq9aZi3ZXL7tb28WOzW7KZ45PoywsTbLOV8WaX6yg3yF\nfpQ+eHKRIz7+5b/+0+hdvZPLNVb4lr6xUy5NTDifCZzjp4Zv2r7uVgz2zWNG0h3j09NsRcNh80pe\n+S00rUlL93SsXIYf5y4uWzi3EJPTeDMhfmA5aZkDZqcQ0LjooqtrjnMBHLlhPM2SZgElTAse0Ns1\nGUNcibeK0+vTMzPU0YqxOS/V6MUWu5Otbg4o9bHRPdjHwhyTKnbA6r24R5tFscEOogvKXk6OD/Y3\n+UNzOjqZPKkb39OTaFtVpXAJEYoSbtrjattWOoJnTsa0Zhl+PocHUFbUOvER2ogJXP3NuivJlcXL\ne7wSs5/rPGeBaS7GqW+eg7nF3TyjmsFPDPgq/f566f1i9bwk7JzQYBE8evjJ+J9+/9fREMtjSw1+\nFUoWvrc4pLzjvEzd+exsPRvXr3s+1ncfBTAoQX4EBuUhFYBNzoh0rng3Cq4d8D/6lrzJ/y5iA1Ix\n9Raj46JVJx4kPGXmUyMT8d17H0S7swHXQpxwZT+vOiSkHVmKV0piTA5OAmUiEd2J8vxO5s1IcoJI\nHLdfpWAKYSXz5u0FjROpqLv0R4GmKs6Vmd1cOh04Kbz8M4bAYC3MscpvZOn7Up61lSdLKn/8Tvh8\ng7BEG1cOr2cFuD/uve/x+Om7bo06xFsmXb+r/JZcQgrPlJcn5RM64knnTUGY/sXffedeVsX9sWwI\n1xW8Sns5y9HGEcgTb6nRdLCq7SowJXxqBq0ycaI2V8bge3Fm/bbch/xhRPuJePL4PoXBUggTc5W2\nyicBWGaVL4tof5jWdIvwlhO7cQYztcsTrgycc+TU5rKVa2Ps1JH47vd/GL/0qQ/B8PQCYBbSmzbH\nTVVOO+tb9iXMhHb1jnV93emSRO3widPn4u6776PZy2Jw2fpkyJ4UldFnc8lI71KA5SiIG+hocFME\nn1Kwi7l2J/HudbRV1GRZ9Mh5fDpOHRfiTI0j7KUNvjgsCzfGhfkyDyPq/Hse8rmKyMIv5M+fNew5\nB6IXO7Fzpw/E17/1/fiFT34wVnDCtQYuHPdu/VeHFdolLH0tYeDywIC8HaJSu7jAwnCKddvMLD45\nR9l1wT67maZKXJwhHUtPfE+hDJg8w7WUEiNO0XFglvb3o5xZyKt7ITCvpPTkeGO60Li7XwsenIVm\nZlt4P8EMbRQzJAXTBS7lmKOeeQRYoIHtD+ItYi6QI/m9HEEEHo9vT+e2BXeTUER4gGl6vBGnx728\nxMOetZhuwAcwD4M5YF/KQSDsTV1wduIKr8VcMgcQjRncP+EeSZ4+r4s0G4FNaQOhc24SmBBYNadp\noRkN7Eq98lOmVnm5KHzNcuVrl7UoA/zvjJC8/3JtagFeIaU77n/w8ejoHsIXIw6+0frk3a5F6qN5\nkACE14WAVmcwV9dgOeGWbV+JT/+SfDuNkjaF0tSUMXlDmGop5/hrQWBJ3YXCKdnSC/FLaApkxpXV\nl0+VJhWdj4KAxA4RekhDsSC3BFMwSn0V+bRzTFGvwJKlZSVWdD64XenU3VkfxOn5mnj8yf2sUIVf\nQZZPCFNh8eVCwmwaX/Jhqg4I+vEn9nEwqBVDK9fDi7xrnBbZXnHil1pOvhdkOvwFK1G3T8uBE+Dx\ngBRaBQVF7fdEv61MTWGpjUIMWVh++0SpoJQ6fE5tnLGVQITglP1oOSV1ye/zhacK69ZYWmSrKjwW\nPCQzFycIO/aVdqJu8bCki8Hla+PgkVMcNDuBMCcDc4VX7KCyVostIPLw1obSamEuKNBcV4Yvrh99\n/LkY5eKAPhZagXcExxv8PHFqqoKl8pthx28F0TLGimioeCj+L+DytVtH4vToYPnApSY4/ynsiWtL\nLAJnuajUyYCBzzvHdqbNPMY5jqQ/ynzVQC5cO7nIWuAw2Yo1W9hiPBv3P/wMv5mQGHOlDYmlVy1p\n6eUSBi5JDHirF7sDeeMQigPnnIaCFpeLNNgN6FBYSzp1zkN7iFmWninmsbFvseBs8d4T7q1Ui8sH\nUC1SRuHV0CCKhAWE0iY80F0TeV8Tnt2A1+vqrNWlBhG+4slvtO0t3Cs1UdY0WYDPIMg2OEjaggYb\n8P4WvF8NqWm9bbbBzXFe2TzPH87SKAf6J30KpR4wEjZ2U5LfyjPkA7xX0GxaN2Rb0zh03t0Pd02g\n9a4B6vU9f+7EEKedvXw4aT1xUXG4S7JHl4D6EQwUme1HIi+Xn3mQgdn1JHe8HjhyAmFzJYOSAzQM\naOWK4uKGAenWOsTVhbSn4Kkw6bToTSjFnxjEDaE1IQzuPWE8Q0EQDCWQt4iOTVdq/PSkdWr2SO/a\nrPxVU7e/CRBMmVitReJQ8IFAyd8UHP4VwbLadkaAAwJt4RSSyjPfbYHYlWbKY0Xas0hWe9SBfZ5w\nd/evZKVaj4OHUJcz8QqDftheyZdkCh2kyTm+AMxWzFw89cy+6MbudQG7V1eTKZBRTlWtTCH/bAdM\nrYmgMA/jSdtP4MitdBMLgUyD6ybna5ySVkDNf8ImPgv+L+DPPGYT35VgKWCmlnWJl+wxvhVOxI1/\nMmgXCtViATyDs8Qf7/mRZZYT6kWItE+LFUliiSTAxEq91jPIFlSdGzcO0B6FJXNbZ/XH41seCoYA\nkJptS/lWw6l2wi2uhx97OgbQbAaTUmrUuYhZ7XXRYJPNhmQgj31ERMEs4xub4dJnZSyXdOczlJ+v\n8FnKADf5YJ8JkzgnLsegsJZUF/rZd+axLebxf4HnFap5SbRuXdKWmBHRQFsysGJj7Nl3JA8Kpl/O\nHNDS3lJYwsDlhwHHrS7bNAdosaXeQtyax7tJC5MV/VmmEz4S5XJOV2i8d15JOnLCk/4WpuDrekuR\n6uBjfCdH5H3x+sBCGo8gxjk/uGAkY5Yhc0cdk+6VapwD8Arb9ArCHFDXFtwyodkuaF0XTJ4qrzMP\nderVRLKmpFa7PlmNgq4KHf9a6YkFQRd49UThgdoODjTq0k8+q4xcm6NMYOi2fMBqtjRtsRzLtgI4\nXGp2qQj+oWKp2KQ7Vy+FywEDjrbLNDi8IQBa4IncWZd+bI+qJyyHIJjIdBYNhSjuJNFBzJ5yy0MO\nEHGN1VQeZMGljAdUPCihQKMGKG+xgcgVZnQ6rRjaRR1qR+chlspWSOEpxaIUzBReqEntHyHhgMCG\nuhsxiPBVo9zc7kZYKIJPtoD8EDxwUUQ+27ILv30uVFcxjOrADWScxMi+MNcD9seR4ydS0C7tZ+qn\nTTKInIdLoVluVmex1kOw/iZL5THus613c7MLAtiChjd853Vrwka6dCNFOWrKPADVBXMRAO1EOwC+\nE9uhLv9gGnVW5DWZigwNQU4c+WxdgmJ5Mid/e1ClOqFZ2gp+jeNdNyvoIe7e7eKUfx50Ida+VKBS\n0FVLrcbYMt1KVdBOgcsaklPJ+dp/MitW/OKWyDYkPgEjq21tkw4dPpbvk7nKRS3ff4lIsr2lobQL\nIKi13VvgzP4VX0dOnKKJbFOxDZYyHHhIxg7MBcslX9rxiAvbmZOS4wImTb+IG5tmny7ojso2097X\nFRKNZQGXtopONmogqF168q71rgWcufNsuHBhQMmTQLeblQle88O2M3b4UztSx23MFMdhjxw52q5X\nuqtw9pqFLSVYwsAlhgFoEj/MHSpIXFQihTlXOadI89Jv2tVByx4+TbqF1vKfO1HQnk7W5bMduZiU\nx5mLf84hhJx/5NX8LsIeMyb06duswzkO5UUXNpw17EVr3urGu3kE0OTIkHINO8xO0nRiWuWhWG2R\nndGsyzJksXpdqeZSlS+CJ29xDsx6tenWdInCtaHOtmkO4AFf2tZUEEXjmdvnCr7igO/0WOK8Q5Bv\nl52U0raMXPq4pDFwWS8NHIxq+MbG8bM5OxcD3b0ObQahE65E6qhknce32wyNJFQcVddnY/0KUjZx\nq6KUh6peFysSi07Cp1BDnhmfYxvD7XCISoEKgayLspzsFFoVmWpsX+iOSEGshtA3nyeHXWUa59Y9\nxtxskfzur90Z05Pj8f/8+YO84rYgDkpolGu5koonjxW0PJiT10kSl0wkJ2/AkzlkHGnaT6bNR4nV\n+ZzyJqfcjpD0mXQLTbbTGyPMi4O//TMhNjVzzTjLzRSrsIcTNoVBYSu5ZCowmvzjijN4woohcIP7\njk40as0Wq+rEB6tZYJmdG4rT3E6RDtfZGrIGy9FRvgeZjCi4s4+KACh8igoeTLIFXTA68Xz7zRvi\n9ps2x//31Qdi/5H2PRP0ow7cddae7eS30OLMh4ospTBHAMg+lZkpfPumM21cBUikAUjiuDCsru5u\nnMWPGpvQ8JVBYe7tCMKRoV2/X7bB/wp2R46dQE7mFiW2s7KDGP32QxUcQ6Ul4odFBYO3i8uHtd9y\n4vKfmnLnNpdl4iQXFdpmvc7wo6jRAf2CE5qajNaEoDJ+WcQwkTiJJm1St6G0Jx9f10fWBYwuuNxp\n6NBPK9roKfz6iQIXCdW4f10FLiVawsAlhIE0J5I2kjCgS+ejpCBpXt7vnwF+lgtGeawUZuA7eZ9U\nz3zlNnYKfe1X5nSxyXfyBZka/5OfEJ+HTCm3ElHhwBaawi6cm293mUreTKPAyz/zS9NlEvKNheZn\nAclCKLeEAmte3EBU7owpNROKkoC8lmc7Mp4Wl2ZZajtYF798bzp/vV0MugJp6ft1Y+CyFjgVzjoQ\nOpwxneJy8Nl0x2KigE+eO1Xdc5KuCQFLxDs3D8Tv/bMbY0ChisFqPoeuZDLH++Oc9vt3f/JQHDqJ\n1barsCzNKwIVmBSIFCjZSkYIVWCqMUHP63jb8inLjQMNtt3aqC3oc6w7zo1w6g+Naks7GoUuiYUy\nPHDhvzyhmIRTmIqsJskZgSAJMAlcoRaY6wqEChdALvAEBVAFTmE1Z640gScT/AhBVj4pS2a1rsBK\nWmUv25OaKJkQJYnXTu3tEFbc9qjDeD7+kZvwXbksuuEGXdSvIOoq1TWtLO0x3GL8yRee4wRftiD5\nTQftEG+udsX1ggI8/WaKZGW5jW0PuEWkp0dMI6hz/XBXXHtFfwz0sjjI3DI6ivTDlbmMi0o9NJKy\nI7CIC6+/VCOq8EME6cv4KInIm6WUT+HJGATtaRYuKbQk6nxzQYQzyr+3KizutjKybCcYpKH209QU\nWl/GZxHkaAOA55hIAAu09l8CTZ46fdqPtr3OIsiToB1oomX+9nKONp7Hcaw8h2a6LFxevqUFW450\nQ/mVsAobfeYp26Hejvjgne/Jqv/++/tjAlLibEFZJACawmEVhPH14LWkAVrqyfFN21sceJia4kQD\n9FAm59cvLFf1L30vYeCSwEDy3At0IeNU4DIUrq62E57U5pXlwKYUWOWBwHg+T/nnaazQahHiSAux\nXqA382QUizgYaQq2vFXwzZLhDdTpYjTPNSSt8t4CLN93UF5JA8Qu8uU552HisQ1RiRNa8zF/ZfYC\nW3JZ62+H5AjAeeFAovXY9nZ+6jQkv3M+TTVFRi19XMIYuKwFTrdHmwgUyhQKFTnQfTZIMxJsrtTU\nJM4gHCHgobU8O9aM7/7gIJeGzcRwfz1uuWEH7hca8fDjB2ICzzFnJjo4dTfObQvd3PXqTSkUpj8j\nhRgEJrfYa0hfxW6sCEZYUhZhEI2fElAN7V8XV5EpZCrqEFsYBYJjQrqAsKDPR4r10FNnallN2RYa\nrAPhSwEXNoMG1XvUhYSSklATqnzO5jIJK0yTgbg2EpIh8HtRyNKNSqZjQb4UCGpOGhZrTt1txmW8\nbbYVtL9JvueeP0zaAVxmTMVWHPO+98bdOCcewX/bCVrUGwdPjqJN1oQA7ZtaZCVS+kHmIP4UKA22\npYm2t4bgoMZOq4g6aTvQjLGZjuCCbSX2QQq16ekzb9MRPvLreNgtJzrf/MKe2mhgVPTVdZAaZsCF\nGYpX6gfHGUEJ5ZuXogAmJgtWEM8t9/PvfS0rpMdImrw1IX/rPsRcdtH5Ko0popUwlS1xbU7LJHJ+\nKqEtYkrA3ZLrBOeDjPff+uXrY3jILTPHpKmdLOhx0nld3de+czx++MwZ0fIqofSkCSxbCMsooUbK\nctegjwXejddwpSbh/vvmsRGmRFYH88Dh+JJeE/nC+EYCfaqwbV9485YT0jy3HZVAP/suYXr1FryR\nKpfSLmHgrcCAvMrDqyX4A75TIjNKGssJI9M4ixAqAbGi2Ix0ooAHZi4+knG1+YG5UrDjrXmZjxbU\nlpImBdJ2WlOXIH3CJ+ANWXRFr6XydtmWU9IVmm5nlc6drwiltFKCv+U8lumsYMgZMhma7/hj4Vqe\nhMv37d/53BZ+s9A2nL5eCpc8Bi5jgROSSGGIwc5ATYHIQVr+M0ALAbTUfvLXiVan+L2rx7FTEV/4\n1iGEwqnYtqoeO67eEUdOT8Z/+vZhtCWz+DQLrtLaEI3ZqVy5rdu4CrdLM7H3Bfw1IqBeuWVdDLKl\n3GLSfuHFs7imKVu9QhFcy7d53VDsXMchDuzXDh7Ehyk010Dr1uBEnnex11vTsXltLW+36WQr/viJ\nyThw7AwWb9qwLMTWLWu4PQjfZMePxK6dG2OWPPc9Crwd+k9DRNBIm/L8JxMqtJZrxBSCu8QH5aQA\nCj22SfJHBqO5Sk5fJK2TJ2P4xnScMsjr5J2nEhHyqFu3F48+f45bKM4iCE7Frbv74oabd8cDT5+N\nv/r6YfyL9kXv0GRctWNDjJ08F+vXDHMCvDv2cO3ZCNdjbsC5+qaNyxASI0ZHue3ihSO49lCEwu4T\nRA30dMZVV6zD91sf15NyRhH/cQqcHbjZ4NgXQuk0kM3EjivWx/AaDnkB1iGcJJ/CNciCbjf43LFj\nLb7mziHwR2zYOBwn0FQ/94L2rbSPRURBiOPDkOJkWSygOfBd9iOf4q3CbsmTGX78Dwu8gPJXLafU\nbf1Vn5Tk+TvLQOgUVgW3/FZ8lMEbTFBVZA7+8VMTkbEpXY1ok9yKXVvWx4plPfH8i9xMNDmDmN7F\nARwXAy4KxEIR5HIyv5TcuwAAQABJREFUyBKFyjrEDchtTxCmd7LKnywKtLfqQoPq3oChi4VBwWbB\neTklr9AsjE5IfNOYLMORULIJNa/Ui7fzWRjPTrWZxNf5S1iJ43flEqlqfWZZ+ljCwD8SBnIcUnYZ\ns47oQoNlbMuNfJn/XwcEjlrGeg5eynEVnfRhVulEWnDxr9LD9y68jC8UIf24V1HyW7P1W7lppZlF\ndNQmsg5tMEkjNV7I2zZJIj7pK4VJUxks1z+F2mwdP3mX0ZZPfJ5hEHYDL9r5SwElvuDH1/IbQtK5\nPCULIsJ0PAO7aYW98CGhkNfZbvmKOFkKlwsGLmOBExQzwyVJJWEy+NxndeLiX/pwdLyq7TLalGjT\nMgcrPKZFXnrzAUPWP957JWUTu7BBjt/9yid2xGqu0+tBwKz193AadjT+ujkav/rp23C0PoCwyfYk\nEs1Zrkj4o794iuu6ECxxrH37TWvjcz97XazkHj6FtdGRK2MZR+7OULOr1d7aVHzovaviMz9zTZ78\nmwN275397n2H4i/v3htT+Cv7+B3b49Zr8MM2ti3WrFoWZ8n32JMIc15TSJs6MViryLkwAZst2dG6\nMuvzBBbIJ5GKDwm5pK1yQMhJ3DIL4/jtndyJ05y+zZVh8Spbra73qLMjTo4+sOvhIlgAjE/8NThV\nuX3d8vidz74n6tMzKWx2IMF/5duPx6aV2+K6a1fTTzA56lSbee8TQ/Hvv7aXK89qsX5gLjVw12xb\nnk6HJ9E6ayguS5K91GFO/b3z8asfvTHef8M6xE6YG7iYog/+4mtPxX1PnGIrtzt+8+e2R283DpMH\nMXzvwd3TcxPx4oFjaKt1q1GYbmFoCXppJ21INNg+/gpGqDTb5hfjyzSJ5qqMRE9GiunMRJRs0B+Z\nlE+fjDGU/P7yrV/iuqStErXfUIRP5KWz8zH7lv4n2pFsXGHLppMxV4GnzFB+l7rR8dNvf/JXexD2\n6bWFyfjtz30wbrm2Hn/218/E/uOchKUEHeBvGe6NEQTT9ausu8ZVql6y2sX1etyxjHPjeYT2Y+M4\niJ5w4pOFSG8zsaKvJ9YMooumnzrYP+9ibCuycgQPjwbaii7EAN9rlndGbx82xxyyPYmt9OxcTmvR\niXPo1dzXPDs+hTNrnMhT9JERbh7h9Krb/0Wi5dmOgob90utDKkPaTfZEbBubFTKWvpcw8I+CgaQ6\nhyJbT17jqLC1ZW1vLOcayqf3HIdO2OGBNrAsgnoqge21QCFxErIcj2CxyTT84QsjfOeAd6RDD9Sb\nWXLXwPdVMA3PyfPkF4UzFUZWeFjZoSBNZstSyo82r8lqsvTqXZXWDFVdfCe/kdpNZ/wiHpnJFuXP\nR987B/FF8CvplraWf74vbZP3yvPKQd12udSngJzwK/Aurs8Cl8IliYHLV+B0hOZk7dB2YC4K/Eg7\nL2KTGSQxEJkSg1N0sYWR/Eo6SJiB7q65PgUtb7DewVWHg/HisbH4+7uf5iQ82ke0d2dn63H3N55E\na8b1WxtXxmd+/uq48/ZN8ejTh2MNs/VnPrwLgbQef/o3j3IrRMTHb7+KiVeArHcirtywMj7909dy\nlddC/PFXHmXHeS4+87Gd8cE7tsbj+0+iOTyTk/5aNJxnGkPx1W88G+c8ZISE58ltt96Z8oGwEJ4C\nZpEtxADP+Qk+fEhq5iETVIzAF+LMUgzmKWUkmDKtktlX5ZHUJVdmyHgjUsOMNOQ7hVdvl9IOqE7c\nyu6OWI7Q8OCzx/k7HEdOIohu2R73PH4oHn/2SAxxuvqXP3ZV3HHDFfHV7x+Mw2h5P3zrlfGeXSu5\nJ/543HPfC/Ged2+L227YmA7pW3ROs2M67rx+Y9z1vg3xzN4j8fV79sYwdfzqJ2+JT39od+w7OBFz\nHh7DjG/z2sHYe+hsfO+BfXFmsoeDTUQqwWSjaHky3/MN5F0RU2zp+ba2HyrWt4hlLkJEwWt+8mEp\nBR/tJO3SqrzVSK3qSJxXL6ss1XeWV8GTbLddfnvioK5iO5UJq1wv+bYeS8h6mPYcd3ofwAIZnLih\nhe0vY3oCoU/tyYe5Uu4jt26jv87G1TvXxPhEI77wpfvj1vfuQuu/KgYHi/nC4VPj8YWvPhXPHtLt\n1UJcsaorfuPn3sX45qo6xsIZ7nbewO0lx0anGX5MkNjbrh9qxqc/sjPeddWm6B+ocRVfM5585nT8\n5defjLPMytdtWxe//sldMYo/1Cu5W7mTBd+//9Kjce/TZxhXZWGT/ZfN9QOYXWRKA/4RlR9+L4Ul\nDLwVGFCjz0LO27RqKBw+wBWRV16xil2XI3FuFjqF8MoC6bWBKTyh4gyL0mdUGdTn0ySPJg2D/qXD\n/aX5pW8SZWFVlvK7nQ7arfJfyNkW6sx1ITLLuPDxcmkWl/TSjBd+VU/OYZbf/s2P6o3R8isF5QXP\nDzCfFCjlnqUOhc2Sd3Eucy6FSxkDl6/A+Tqxmiuf3Gpn9aVWhH/F4btaNrR/DPg8xMNQrrHl6BZD\nDmZ+T3JP+R98/p7Yc0RvkMsQ9mbj2T+4Gy0Zhy7QrJ3FLc1H79rOiW02oPvRDDEhrxnujx88uj++\n8t0jTOh98dyz34n/9b/7GcgDC0Mm3et3roh1TNp/+e2n4tDRsTwp/9DDe+MXP3lr7LpyTTyz5yQ7\nEggX/P2nrz4R33mUyRaI8o5wNEtqLFPLmHTmh38wvNxaKSLk60TNRUnmClPWk87eYbwy1y5gcfLf\ne+Rc/F+ff4Br/zgohZ3l0//xbpizvtW6Yhkjb/ezy+NnP7wi1nAl5wjXp77vxh3cozuLIP5c7Dve\niB889RgiUsQN79qUzAc1QtyFgNqB+55v3vNYHD9Tj9GTU7HnyT0Ip7tj09r+eOHIBL7ruOIN7dn/\n+0ePoCFroLX25P0ytLJq2cSRmjkxB+74X1jYa6FDVmf6SmQ0fZv52QVgwV/ajSY+jDI9sVUOmWh5\nVqPeZrimaDPdZKaZpcqRL0spLASMLWnbzN60avQWTRrEvELIygvI5suQSCCOkjOuI1Yhre9c2xNb\n1qyJh544ij1zk+syV8XGzcPxxIsncI5/Lq7dOBB33LQNIX9n/JvPP4nmdD5+55duiWu2LI8nnz7C\nzSgN7KI3QRfd4H8KYXE++pmQf+PjN7GAWAN9nIg9Lx6Md+1eH5+8c2s06azPf/u5WNbdGVu5RWjH\nqo2x/8BoHNozlgej1Di7oBFjS5qMqu+Wvt9uDEgy+oVUMNIGfc2Kzvjp23cGiv64+T2b4ts/PAmv\n6mUnq5pT3m6IL5H6F/OfRSBd4JQkgOYLT9UwRz7l/C3vxpsMn2kzqua2bX+6qJilx0sYAz/xAidD\nM/85fFOFn3O1AxpdlhOtA5s/xQRdreQ1mC5YGeSnR5tx+HSDCXUFEbXcGn/vNVvjA7duipVDupjp\nxI1QPQ8ZcfQFu05sNNk+f/bQSbbc0YiypXL45JmYYh9cgawL35RrVw2nm6YP37krbr7tyjxg0c9W\nou5kuknfqeaNvwYwPbr/REwj2LodU8vtYAAj1NxSZxtHnZdEKbTlj6+3OKhplQ8IN4gsW0sI+ApX\nTx04HiNendYxkPayOxBaPvi+nbFta390o3BchamCgk6PWipsX1eu6Iiz2FseRRva4Cq3cfB1dGQy\nbuZ1LV0c1WIdh5R6e+rxG5+9C7ERQZ9qh8BfEwXYvD/awuQxbHIPjXKlW20FWELzKnzuy6DdTvsn\nhXRgt/uJ4OONBNNXeQDufLBHikhUiiwiUnbP+TT2lHBeCMlQ+ZklJUCOS0sSxv+fvfcO0uvKEvtO\n99cR3Q10o5FzIMAE5kwOOeTOLEebd1arDVayglUu2eWy/1C5yvYfLv8hlcuWbNWW5dqq3ZKqVquV\nVhvGs5NndjKH5DDOMBMEkYnUSJ1z+/c793vdDZIgCYKDQH4X6O+9d9+N551z7rnnnntuVY9LyLQ3\nE/FTbsp7kyxuBo/vCNlk9bfWZv1E0MgkB1tNvXon0IRBV19PvHAg/vA/vxbD+AVsYQPQC3tPcKLR\nIHiNzeyylrgO7f7KZX3pqaC/rxN70N549cBA/P6fPstO9/Z4bs/x+Me/fTvLiXiLZRl9eU/E7bv6\n48ixofiLr/04xsfnOKjgWFy3bX1s39kTnY/RHNw1uXFs7/Gh+Jf//skY5Og7z4MOTiLxhUvy891+\nRwcbEQ0IXHoIKGhqXQ4Likfgbb09IDBxn71vO3b3b2HuozAKr65PKi99C6/cGgsvKu1L867kURoL\nOQHWkAr+ybjnUrp7MRxo8p3pTAMz4LfwwhRGS1mN3ysXAp8AgbMazsHSJPoyxBY/hDCLHNh9x4Ii\nmpq5PDmnPgyj8fRILR3Qzs0Mxq3X9sY/+M3rOVt2Kn743OsxNDYUv/6p26Kd8buTIzPbGBvLjmqY\njP4O3YWOuOhSeKmaXd4MwKMIi08890q8weCrDZzHZTaz0/b1w2VZ2mPHpCk3yNTUauLEd8bZXJKb\nggHEqERjorrI4LtLH6xTBqFAqDyn5rBo4lQqOxeVHWt7uQK72H/0G7fE5nXd8cSLB9BEHolrt66L\nu3dtJg2io8vdwM98SBb86NYe+81cSucJhq4mbYZle202v/HXz7FhCCkTIEwDvxG0x7sPjbNH3o1D\n1ovQjoAyRR4HBTcpYZdQ4Mat3yNlu2x9ApLY9wuyN9OWP5vqXbmWX5/98gUK5T4F20xnfnGOh/p3\nU4Aq2GZcKd9PW4TNEmcpBnGpXGmF+EDmnOln7Hv/2LqFYAsLXVhy+sXz2wFjhVHMN+MHPx2I09Nq\nFwQ8Bxcsa41PPXJdrGDpfAW2y71L8eCA29fWVmwyUVe7QeulfcMxwHnN00ycXj00GEdPnM2NYK4c\nrOhrZ5LRxLn1rfE7v3N7tHPsVgcTpzZML9rYINbegSN3Gmnff/zcvjg+zCQCh/ZNHL7QjH1visqJ\n9wu9aNw1IHD5ISDNTsX65c3x0E2roJZC4zvXdcYdO1bEj17SF63UB4E0wiIIJBNMyBT+6MSS1646\n8so1xXKSEnsD4OkurStu1jNwqQubltgQNhfB9cq+/WQInCBk2SDjUq+uVBDwwF1PytEJdg64oHUT\nriSacDjuRgkxG3foxCGsKBWgGbtp5wq0cRH/7os/iR+8OBCrlzbFL97DGetonsYQhE6ww32azRL3\nXLc1vvqjxylxKm7buSqWMZKepi63XhwZGEVjxIaJydZ47OnTpOcsW4SxNtwI6aOzhYFYd0BuDpqN\nHo77UqtFW7QTkswckWlZRWQ67E1JrS6U8HAJA6w0hUyqpF0eN6lbHNspc7CprToZ54iytez0X7N6\nSbz4xkD8v3/8FFrajljVvVrTvpjCefkES+4DZ6bQGHfGzvXunB6JXjaXXL+xP+Utvk4ynqPHpqN3\ncxs2gtPxPP4+3QvdogaMzUr4F4j2Tr5VCr/a/lA33gnKslcR7IpNleIgkwLguohtvQfcHDLojJxQ\nLTj4kLfca0ObTujFMStGasshR80HABAGCt1ZAnG5c5s4Cszysn5A5g76NJXgBB21fClIkkZfre76\nNkMz8LXuFKi52pciiNXL43K+UMRLv4/tpTXMjIr7IHLkJ8sWouHXVpapkrSB4N+Cl5Yb1rXHP/un\n9wP/2Xhl3wl8lY4HaEmTaCCN7WpbQhuVSzm+1Y1ndo7v6cEGNSdb/LUiWJp8cGgcU4gh8JzNeKTb\ne3QwDp2aZPc8LsuooZW658a5UvwcNOEAlD5UyZsFcGmEBgSuFAhIfy1NI/HgPddiN+5Kji1rznHi\ncw9vZxL2Y2zIOyTfRqhDQPZXBXlYBRw5qCfW6WtYl4AP3b0CZUXgYWQ09h1nnNNkC76iBjknwmTV\n7lzYyskb4cqHwMde4ExBkwE7NWdohWpqhhxwFRBAXH0zegKRKOuRjC157roiXIoYmW+G+FbiT2Nf\n2IKq7OE7t8TS7ua466ZNLCu2o9UZQaM5E29wtOTRU8Oxa/Oq+Ce/eluMTI+xm3pL7kIXFWoMpq++\nfiwG7t0Wn7l/B4q8ljhy5Gz09PXGju3L44t//Wbs3n8iB3zJp431S3cHK1KmFDVPVJJs/S+FEYW9\n5HSZ8lL+KFq6+DGJBK+mNh2tpwQDRFkGdylcnnIGO71JduBfs2FZ/OK9m2P1qhXx4G0bk6HM4tto\nFvg+98Ke+NWfuzb+3q/eFM/jBuqaG9fEtdi1CoA2hMdWBNdnnnkD+O6Kv/3r98XaH7+JT9Wx2LJp\nTXTwPf7Dl15lOZhlWbWatEmhLTesADcFv/Isd/IBCIoDybqE9nsHtcq6IHLePUWHzg7iMgtpeQkG\nW92daKHzpCqd2NeXqVMitQ5dObXGGY5fnWKDmJ4NerqXgHcwVVERe9Q5tNu1Ft1QoR2kr+Ns4Bkd\nY/LC9+/GprIbG1fPTE58peEu4RlksmLBBwuKwzkNQNikHxDENN8GUqAe4VEEZ5vtH0BMyCh8Xnfz\nuuhgzfAv/+r5+OqPj0Rn50ys/rs90d6OSQRlnDmLSyoAvn29vlkROvGB6m71vq72OI2A6alSZ6Ed\nBd3BoYn4z194jsnBsjTLauN76Yd2EkF7EniK6zPCjL/iUQAtZ+KT/gn9qg1NEUBohCsAApKJ55Kv\nW9oaD0IjLGolX5EoZTHXr++LW6/pj2+/wPhQf3UFNPsKaYIMGCAJRCjc/7kfAKbY5LgHH/n7v3VP\nntf+R3/2LJtKTwPDaoUHxpmCKlnh9SU0+EIdEFf05WMvcC5AX+2mgiW4DZIqQOQRjs0dMQLyTjJw\nIlYyyCn84I+QAdmBcJb3M3APT7J5ms0ON25fHzdcvzqu4+/4qYl4Hd+Sow7WEM/Jwdn4T9/YE3/v\nl3bFQ/dvinEG8ldeOxWTa3tjAqmslXL3Hh2KP/rSi/Fbv3xb/OJnrk2Ck3beODDORheWkJnFjSLY\njBEXLBAjQaEsUrtXD0mg9X7UBeUiQEl4Jb5K+rO/4roebfAYtD5KJ3Tp3YIgNU37J2jnjM7wEbZs\n35GTY/HNpw/EL95/Tfzub9zNsvhsvHZwMHbu7E0fpdNoKb/x+NFYuXpN3HN9b2zc0BsHge+PXz4S\n11+zBiEGbSJf6PGXjmMruzd+/jPXxK//yq5kQcix8eQLJ/EziZDkBIL6xxFgY64DfrQIxSsmlYAR\nkLlIwzWB/d7g8hv7XSZn47EnX4ifslFpEgFy3drV8eD9t8dmBpc0KaD/MlG/xBzaOT1evvj6gfj+\nD55EMBvE3U9PXHvN5njgvjvYXECf+N6yyhl2u4pvx5mwfPM7T8QRnOcrCK5ZuSzuuGVn3HTjVlqp\n7pDOUr5fu/TAyVO5f88OkMamKbxRFfdoIDXXsBCFO4Q93W3lGc5EusGthtBpJs0YZrmuWdnP0uFI\n3MlO9vVorAdOIa5z1v3B06fjBIcp3LKtHxOTbXEUv7SPPrQjVrOB7kwK5hN8//E4eGIstm9eEb/5\n8/fE488eCFbM44ZrVkM3c/HdZ96CQND20xnAjCDr4QospSUkwX4mHInntrcRGhC4QiDgJGm6vSf+\n4tuvRQ+u2P6LX7kNemyKHzyzJ14/iO9hTEw0LSoOzhvIu/DZnC5L0smUGCuADeNEuveTV7myw3u5\nt6seuVpEmvlJM/EFmv5aUiNcDRBYNBpfDc298DYmQiOYVUu9KbrhvmLOpUMEggMMgv/8X38zZtCy\nDU0tSZtBj+H7P3+fXQwMuGMs/Xksplqo/Sda43//w8fwN1hsDk9i26aOrI2l3NFxB+e2eOKVE/h9\n/EaswaH20PhoDOJDsA1BolUhDOFnrtaGm5fxePrlr0d/9zTaoiW5zDg8qW3nUkprj3/7J9+NP0EL\ndWp8OZ58XGr1xCE+VZ04qQhASGSKYJeP4FzWePHNsfhv/qcvpo/L2lwX7USLu38g/sd/8WUEQPx0\nIrCn9g9Y/tk398bXvvMKpzuhLUbTNUT71fgNAm+8ZsaRweb4f/7DT+I/cNZ9R+dsDIxMxRTa5Y5W\nzstmDXeak4sGsOf7s+8fir96/PXo7davaTPas7EYYif8WEsXO/wn45//398nn4vXuA7gt9JkFsN9\n4FY4VUKxun8/niX8p8CXfWx0efzpl2Pp8o3pY/TQ8WPYkz4Wf+s3HsX3qpMahDUGnFygBu8GECC/\n8JXv4Ax/dfSt38xRjBPx5E/3ovWcjc88ci8MtXxDzSbGkJy/96OnYt+xs7Fk2bpox+zg1OjZ+Mq3\nHo/evmWxbk0f6Z380AG6oUskz1I+R44+H4mQpbRJtMEuCqNYBU//1C5rNjvL6ViTfCcUrMBabT/a\nR/rwFMLhvTetjbvv2RK33bGZTUFTnDk/k998Bpw+jdHnv//GS/G38T/7G79wE5MrNtydGoujp4eh\nJ5zzM5kYZsL2B3/xHNrrO+KXHtoWv/DQ1lw9GMeR5l99dz9twoCFjqjH9C+X/hPfeVLYtMP178ZD\nIzQgcEVAQAMaj0A+hB/bXo7f/d1fg8rA02d3D8a3nh2CNhkrmNw1kPftnwt6zuAV4ZOLY2mO06x6\ntKAuzhTyLVaiNOfRx673Mry0YTeTPCETltIav1c2BD72Amciswid3wFkhRukixWuZYmUJUG0bYzc\nZUkYLc80g+6ZMY5WVPODZqcMg1iLMGiOzHTEKA6vNWKealrKoMzAi1BUw3hHd0lNOJMfwX/mvgHc\n/9SWIXBBPAgXzWgCp1uxW0PLmUuZTX1x2HM0OUYzqUbiyd24ag0jxhi0p9EsKbrkebIQVmq3UkxC\nk1dUU9mr7JuD8yUO7pqeQMicngKN1IhlH5yDtsZZTgbiUMoUKrTv05RhfBJbzemuOM2mK5wsFuEC\nEJiNDnEzi2a5OY6yOxmgEo87JZ7HZ1hORiKahXEr0E2RbnKqLcYGWXrFZrbZIzQR+xXiPCzt7AT3\nimaUmwxKRsW/AiGF9SKwVxrv9wcbkE94t2ASsRdF3DLcYPVTIp4JcIVy+szeGBoaQ+BcRoXigd/K\nb9QSL7+6J2qtPQiQa1Kwq3UujfaeyXh195545JH7ExdntHMFt0ZxlL9vPz5Kl2/FYX0fZbABSkF3\n/CymFydiwzrMC9B4UzFBjajXfPDm/YPwILlmv25G+/I3no6nn0SwP6WrLXQw1PWjpw/Hq2+ciDeO\njyQMa8Du0Inp+Nf/9sexeXUrwn9bHDw6ghYSvEYgHhtrx2xiSTz2/EAcfuux2Liik7gpNLQjLMMz\n0Wtph5a0yOqIlw9QDq6qNqxgE1k/EwQE1+NnphmwPYavKV7G/+rv/dsz+AAd5RvLmvhODjC+Rdgv\ndtjv3823pyi0UvAsPyMJtO9tplXSo4B0o14wEU24WoBgXUhszHsEW883hP5rmFaI+WWiCw5Svnax\nTg6k5nLa2XsU1Xh1RUAgP39BAr5aPcDDyr1vy5ii5lI7eg/ZyMkjr9wU6P4Abf9zNcFNp5ZlZsoo\noSqpjEcgjP8L3tRrKUjIry/eFkrt8y1729sr/HG+O8Auu2CEHBM6dE8Fqk139qvhdFUGlgr0Sl9V\naKgssv+a82jtae5Sgv0mMQNKSWGp9bVBK3LppBEuKwQ+AQKnBCvi1dEy8RbW4GMiJ+8ddFxOFH0r\n/MSmTL2KKNqMg2xtxyrCV9AqAwjzrkRiBxQFJOLJMYfWzQFcDtKaDEamQtk2BEJy80cJdfAjpGaA\nSelTsbJVUZi15dadlyQjhSc7RMtyQExqpCrLqsrN0n7mPwlK2qSQl4CyxhQUaFqyD9mB7ctftGil\nP3MIjvYSPpxwSbhSWA7GwCg3SSGAJRMBHumMHzgK3fxs9L0Z4TwFO2yoEgwIDHoIUMuMrE47bJ2Q\n8uou5zoIuZZ33hCywHL7Xr9ZDt9zeGQEARKBVqNg2tHEuvA0o8oUGnKrVIvqKVQt4A+8M4bHcHxO\nG32Z/XIg4nmKDWYyUxtv2QpFxtfMR3keRKCwVSPRHOW1MVmxw/P9AE5m/6BB3Mz0FJBnzPO07+Rc\n/mULclBrwoxhjk08HNWqVkY8zgpqcfAkgudJVNFYXzq4lu/KpIrTppyUBW7A9rw1FfsOjZBGPSx2\np3h8KFat7GJ3oCCXJxQd5a9p72Ad9NALExTD2ZG5eO6N8UynAF4+rG+En9eLDX7s8sHb2WTW1Xom\n2qZpP7EzreNsfhqkHq1whT0V+uID1Ovw1hpjsRwBuwOTDu3K2LzPd1zJCgfaGWnak5aYyH6gAknV\nCFcGBAou8NUSbUQGqaXgEBoFvivflmhNiZxqS5XlKzt6yBPYBEhm81QU60S3XkIdHerjBznSF3S9\n6w4XBV/FxwpzbUu+qKe6+i4LrQcO0IVQa2b8SgWAkzMZZ/J7ObfCOxAlk2kAJmZnrCziz7mN51bM\npqaEL0nm2LQoq52GTyb/gn/OC5zykEa47BD4+H+FCrura4K8PvhW4HdwATXPSVJ/KBd+TZNcgsGx\n0H8SQ3UPORAv4VioGyFkMAZ/y938Y94s/pFFEeaTLeQ0MovMBAt356RPBjSfOV9dqp8E3ULDz6m2\nalHFbpNrK0jJYlLQqievusW15Ek2zz1P9K18GeNMUEkBdZhZRL2icuG3/lwvPRO8I2rh5Qe4o16m\n2Wq8167qjzf2vxJdPX3kw3515GSs5ESdlcvZBJOaDjBBoLAspG/VjetW4UD9tZgaQ7PZ3s2S+nRM\nDp+M67dvTwHSGbvymjP6JRwBuuv67fHjn7we7V2jzPTbYuTsiVjV2x7btqyjXw5iNCXhRSbB8WEG\nnwSGeFUEvSw0f3xRBP35x/mXCpFVUIwkqHXmn5MH2+ZQO8su0tRGMii4Y9/76guW7+Jg6wEG7x4U\nSudDvZ3l+Xw55lOf9+ZcH4hgI9/Hs+Q39kzF8vYBUGqcdjNhwdrOOdEsMxdXMIrdmPj23nULye62\nodixknJqZ9RvojvtjbcGutC2d6cdcRsa/5Y5TryqIYRnv87b3MaLyw0BPjdkWfDZiRemJR4+IB7I\ny8rpXioYUC8gDOmFQYlHwVK/s4o5enGYlj7Af8cO04j5CkDurC6CEPEiA5q6GkqNNJFJIalCkML5\nlGiNqT+RX3rLBl5uSF1E/RUfL0VUPSw8voorlDeJFshT7JBFgZXKA3iUAj7EOjbLISxCBCF1joMj\nJnnfCn8p2gi4kxNA7fjh3U2sPiQgL6LVjawXB4GPv8B5cfA5J7cspwSYTD4UJlTIorx1nlulqd80\nLnBLWW3+ylATbAV2at4K5AST7KaCo5kKmy0AlMnCrIkqsC+xl+o3zQUYeG7ZdU0cOPRWHD+xh4Yw\nqrDL/PaH7oql7MhuxgVTWU7nFY2c492N126LN/YdjDcPHInxs4gmmAesWNYd99x+a865i8N1hU50\ngvgAeeCeW2IVO/hfeHl3jIwOx+Ydq+Pu229go5Runpz9qyEvZCuTdsASHgmqSwWMnGWgiaX+8l3p\nL3U7pPqbmtz6fRlILlXD3q8eWuM3o9UT03iIONkSHSuxvaudTYFizt36jGpFj0w6dsoW4WAxHr6z\njnzLzvyOGoMeAutk07IYOFuLgdHJmGQjibvtdXfGLOSdmRsxVyAE5EFid4Xf4IQrWGnWgYbNZTDM\nL9CvIUiqvPDbii+m51+dQZlfL5KJFIXaeUvIdIuH3sVUIjZlKlNmSA6ZE3TLJ5jkag5V+10rz/FS\n+BmSkvjlmnRaojw4osBEIZ+3rG7VpnGh1z4a12xfxmEgnCLHKtArHLhy+AQbb6HjFOhzJVE4O/G1\nTMs/F7ZENMIlhMBirL+E1V6lVSUjQXPjrDY1N9yC1DNoR4ogBOvhnSyogdbn+8YSvaylXIVTGeCN\nlFFUA36VqhJVTVngWuU1xaUJsC8EvSZm2SvQZv72r38u9h88EiMsl69asTzWr13BN1fDV/pV+FoR\nIlk8j1//pZ/jGNNj8RbO7pfg4mjHtq3R06l2Ec2Jg49CI1CQSfZ0NsftN26LW2+4Joc8VW7NzOL1\nxSlk1CpWGk7rS1xL5Ls0kFiohTZTr2YQDqu23x6UwZZ2Qgccu5XfdCHPlXBX8G4SU4Bjsyti5sxo\n7Ohrie7mYY4g1GRAgUG4emgDtM6dJiHvF/gsmHJgWlHr53SsFXHobF+MYG6hvfEcE5G5Nk0v+JbA\nqhGufAjkhBFc0P5WnGhWsEQLmeZSKSSxnA5+J60n5iOgoonMU+HIoXkPe02Thp2AldPNeAYfQCxy\nEAoqnvOQ9vr5whTSFL+kSzd+xCd9Of7MZ7agqy2Uvs0DQDgkQOyHDxVgFqLVMOfYAJ3NYr6ydmkt\n/vvfvjeu3eHKknDFJIdllz/4T8/F4z8ZZFKghwt3ukNzuqwDZh553AiXFwINgfNC4e8A4qwVBC/o\nyy+zMWOLtg6KcJBKpnShhX9M0wsi4ZPdK78yUeFU3GAUvXBhomVAFrbCueg/ZfpGIMwryAj5UkxG\nX4ofbQrVXir4deFc/vqd67Nam6Gg6MCTp0Fx1d9kLqtz73GM2j1uYof55nXLc+Cxt3l0G0w0j9y0\nEEpxGHGD1QzHfLZgjJTDDZuxktFaOcvVwssSEwbJhH0qMRl9SX5oA8KV30sBSmfNAiC1QLnYnj0h\nTX1YzP5dkoZ9wEqAqHDG7dLJ8TXRPHAyNq9oiWUtpxEU7JdO6Iv5QqHl9y9WZc1Ejc2CZ7uxUe3B\nrygHFvCdHexEWT1NtOq0uo7K719iI8XlhYBIC88Stf2HwImFNasW0Cjaam0zaxwSojA5i7ZTm2ZH\ngZyE8Y318IDBDWVoay7PqoeUHrmnYHFGf7ySbzH7kI5zWmmCkoGLhz2keYp1QfNmMdQv5eFj8Cuc\nndgnSORzgEOIyELd/yCEZ4isNY/Fp+66Lm5E2Hz86f3x9cdejs6epXHHnUzS0Xzq9hCODEyBe9If\ntOzkly/SCJcXAg2B8wLgL/PRxmwOotDYOSlCVoOLGf0XygBkIBLKx40ZXACY3pFUYVOgCBM3XFXQ\nqRiKrCTZCe9c1pxDuJOxJL9IliPjh4Ek4za/AL60I7cDix2gdTBEf0vIPmWLaJcDAveJA/naVPa7\nbP5xMFFzYlwOK5aXz/bJ4Yq8vE5/mJkvk5K/LnarZannT8mFe//ZtKo95PiZh6wL/J9JzwRl81YC\nB9yfydO5/FZof1hyzPhL2roP1v3UQOGSKXDHpRu0g2ygaloxGX2t2HEqCbAJCrCX5r9PkQrW07gA\nOzHcG0cHe/FLi2N/BQ5cczVzLO0cKyBqgmtseHADEbeNcCVDQHpiEiXN53pV4rmTQyaCadMpWogc\nLvWK5woy2gemmMO3n4u7bt8RE62n4tixk3hsOIunDXwiuOFFraen2TmOYBpjPQqbHr9rKByEK2Xk\nhI13mvOkuQf0rns90xJl4o9NcCyQizW7VJCwABLAxs1YrWxb15TIscPDUuZmhmPtauyh6f8PXz4Q\nLxwdj+kj+Mne/RowKflqNVYohKG80+/JmNJQcF5+dGkInBf0DcBcmIUCx2yTs1dntO6UA4xwkmZ3\nz4nkSkr+fZw4wgXB6Z2JFToVvQxeFbmamYlqyK179Jq7g9npXWMpZNKlKY67xOMRTFeGXMrLC9yj\nspEqsZfy1xYU0a4aGMo3rjcwh6d3in72tspn+kxRF5grmBjpDN//9SryUgY2+1gGP+Em3gnNhavv\nL22oNkPMMCh4StI030VXVHxA2s93ZHJQ7KbKQHppW/f+tWkSU+Os+JmW8Zho6kbT2RJLBhE4l2PP\nqeZq1uMIXVJXqKi+77uXK72PzvaxlN7FEa3r2DzCufG5JO93QnAFNjU0nAq5VyY03r1fn9TYpEkE\nm9QmprCJsIg9dq3diYWbfsBxVzxYM2/Co4SY7wRYwcbg9PA2jkHedd3K/OZDw9Ox99ipOH56KE4O\nDMbhoxGnOCjhzQPHmIC4fc0SKAeN+7STlBSwxBR5nwKYWj8KVyqTthDKrm48ElD+ycNKsHvyP4Xp\nFBMRNE9wfPGGla0cILESX9ZnEel5R8dxRhcnTpCXPJ998Lo4eeaF2HNY94Tk5NPUgKmbsPweae6W\n9TgeW99CnfWqG5dLCIGGwHmBwJYonJWibIJBsGOOMaltdjiWdkxwAtHa6FvRH8/89BAuZNxw0ADv\nYvBWDDkFdYVMli6726dj24buuGbLGvxYdsRxTm56Zc+J2HtkCPaCyxo1CoQUtFK7J3O/nExDRmlQ\nq1EF49TqLQwDqRH1db3TipneOuMuTK/0IYVM02UocTLcHG6yKoUVX1pCqTtFVmBgHUVjXHJfsl86\nYpt02TQ7x8QLYao5vxWDNJ/H5cUUsJhElPNCFsPqkrXyPSuaQRs128KpYmz0UesYs+34v0U4zOCK\nBROg/HYSevku5yvQt54ONllNMhFS0n+vWuD8bpTnBgZcMb3TM8D5Sm3EXzYIOMmlcqxuo5MJSUdH\ndwxPtqGl1MYXbTW7nnV7N83JWNpWun2u0HShT9s9yoaxofHJWLWskyNem6N3+yq+PX8knWImPT7F\nDuuJiXjz8FgKoh4ocej4eJydxI8wR8VOuvsMqsrldCjKpyZcpmmCo0Bb1tOs6WoOwou/OnmlnTxD\n5hxCvDxQIVvSnIOe9L2psG0OYf/4s/vj7ht64+atK2PbP/gUh2kci68//mYcGBhjcsAJd6aDHoWU\nZWR2WW8jXFYINCSiCwK/jAh7EAcPkBm6YOY1xyysFp//3M1xy00bUyt38ujxOMTxfvOUJJks0FXW\nKOF8UoLMoxq01U76r+ZMPcbjMw9cH7/w8NbohCu08tdUWw8Dnoz/6w+eiH06BJdpyHQAmA61k+Pw\ns6D5uzRQ9Hu5RFPCuV+vPJV3aj5zuMo2+6YaHOi1nXAqn8hQpa8XSbp5fKkLOCZXqCwuVWShBq8l\nb8miMHdpOakCb40BsZfToFau6InTpwfjxBk2Ns30sAQmgx+LbeuWxtTEWOw/7VB5OQTOAnsqP0/g\neyhLOnP0W6EaKWKGtptuAjQ/gmIC+TxFzEeTylWOdNmiQ3wKZKOQdmPaHIvz6YuTshz4GuHKhoBf\nvo3l81t3LI2fu+/aWNLdFU/9dDC+8cNn4vO/dlu8/srRePHVMyzz9pJS/uSEwj6pp+SXh698/6fx\n5MsnY1X/Ug45WB7bN66INSuWxLpVbXk6Wg+npy3rQnvX3w1WrKSMbZy+NhNDo1McxHA6zmLn8ebB\noTh2eiz2HTgRo5htTWCf6KlFyF/gl3iblVpxPdSf3x5dvV58rbOQElV/qOdbeHXudDjTLrycL600\nY/EL8p37mDQ2nyFvqkZ65Y/0zF+5QIVcXUpv10yNeElrUs0OgmQNYR0yi0NnJuP3/vjZ+JWf2xZ3\n71oVj3xqY1xz7cr4oy88Fc/vHoPemBTkdE8NtIQuD1rcqHNb03i6NBBoCJwXCGcO4CMHCKzdJoLn\nmv62+G//8W3R24XPxNNnondZby6zOLfSDjFDElI1dPEgUYn8vi6cqqT7uP7SZTdpqAHUDrPJZUaZ\nAQylhr3bE88MxHPPvx4TU6fit37lvpy1fuaBTfHv/vIVmKtO8RHjEEbn+JPXciHmygz1L56Nq774\nQlz59rb+nPZXCc6JLP2T4YpL797jd8lwsWCp2kI5eUsVC4MHMTw7KLS3TsU//M074q5dy3EvNBn/\ny7/6UoxwktQc2p+Nyzviv/svb2WwnI1/8fs/ym92sc268PyLOvKumbMjCM6wQG+hQ4ckbvkBy+bp\n0piM9c15g6JpE4cO+LWamUyV2tXQgAXwCRdic8Xj/Ys6bx2NF5cGAprxXLOhM/6r37kF3HUiHHF0\nXTfO/CdjS39v7Hx4Wbz02o/YNDTO53ZzHwnqQbSZQVg6MjgTu4+3xuvHxYXjCIr7WV6fjGXd7bF5\nTT+CaGesW9mCENoXy5d2xqaN3dHHzuvVSyN2rF3L2EE5d4uXaEsZcg4cHo3TgyPx5r7DnPQ1HCfP\nckrXyTMsI3PaGv5dZ8G9nDTZgES+FN0SdXODX+JdeafNaKW9V0Prgr5ZCs46pfVOHmV6fjO9Y1YW\nwrvS35LeeuTK5Erp0FRQQ0qhpic+E3pfxr1ih+6T5bBKmO/R3M6Nc9TxZNx1w4b4+XvviTWc4mZT\nNm9YxWrKbpQ5PCuxsKIyg9nL3pOz8W/+/Pn44nfb4rd/8ea47fqV8Xd+9c546f/4Lif8lbbmARq0\nYQYBXdCUWMpohMsCgYbA+aHALkE5xIDI05Px/IvH4qcv7Ynbr+9HY9cbUy1F1Ci/EqSE5ZPXclen\nwnzKyI/1jwyOfqfAWaDijHMSZvnVb++BkbTEGIO0pwStePxg3L5pJbN/GFcrjIzoPP0JZiaTSWHg\nMsBqnme+X92qzQwVZ+Oaeee/vS9LjHcZMq0/VaZ6jrzIpBfnqPCowiWZ/dvKy0I/ip+q3LqmIQcV\nyuVbtLdNx44NvdHFN123rDl27lgbz7zIAMxAs66/K/o62uKlY3t+hm27uP45+GTI0bDqJ1cFUPvJ\nvzLAVgnr6c9zKVZ4Ov7OIZQiEF9zhFNj6mDKi7fjxnnKakRfegik9wdwIYUrqt++aVnMckjD7/3x\nD+PRB3bwBdVm1uKtA8fjzjvXYz7BSVpqGXkj3SaVgj7KXE5c0jk5i+3yfG3+OdaBvy42ljXHwBuY\noeweJt1UdLXvj862Oa4zsWXjqljT1xnbtqzkMIkluFBrjR4UGUtAyRu2uBGtIz61qz9GOPZ3bHIm\nRlmSPzMcsWf/cBzBNnTfoRMxNDLFe/7GNWfBNRD4XOw/xWMbSFvrG5VkyvZbBsNvdsX+11xVyhhN\nZuwZvTAuk1qOgXgjeJubc+ijb4SGCc2T0rL18UKFg/YEWp8qABvXjLa2Ccm6hg/bvu7ZuGF7X3zu\n4Z2xfW139hmrgzhwbCSefPZIKnjKpE2jFCdymMJQ9jTHH6MAjv/4Zz+KG/6HR2PF0pZow9XHmJsX\n7bvmD7RFcvS7NMLlhUBD4LwQ+MtNkuq0JdFSrTWOM9P8j196NbVvN1zXwaYBbHRw4D0LMRQUlyhL\nSCKsHj5JV7hLus/hN0+kafL4QplSK0tFDNUY5CtV1vBTuKq3L+1jz3Kg/Digm0ULmhuHyJsbPWRp\ngvZKDR9J24qoI+aU4hZwKLttJFEfSVXvCkdKFtetJH+ryvh+ecty+pJa9C5tZoDjpA8Gxdt2rowX\n2TE6zUablau6OVWpFofeOk1yB+WrKQhV+8+ldP8DNd7kGebz1WMWvfB2/nU9eeNy+SGQQpffJnl2\naU+rx7pOzcbACU6PmoR/Yevb5HGoS9nY49m5rrjMdPI9WerVY4n5S9b6b/W1vapFZOJRHxMKTWAD\nCl8cRHhUgDw1FJgQDSAYQS+z+1Pg7EXSXMcKWl/XXGzfsAataE9s2bCMyR4HFnAqWf/SDsy5ZmPX\nth7KXhtj49fG8VOjLMePcPDAcPx090QcOTYEHbJLSYtUpC5HLlvj6tKMdqJoaKcQymruR1AKdBNU\n0jj946alxXTYOvNOLl6IokyiSn/5ZSLVrFcK8ilSurGBs5ayv+7ET1tKbEk8Tc1xc063UkoeuH/r\nRqnwuQe2xZ3X98aOjf1sFgVSjLGvHB6K7z1zMJ5CmXNsaAy/mkty9aSGsNzZNhV33rIu9h84hQeA\nQVYpZhE0WW6nrcMTTvoYX/Qvh0Z0DiWF7c6+5Teg3ka4bBBoCJwXAHrHn7ncwYHQgyGNZ7zqe3Gm\nCWSHSPQVhllbtE4JVmdWEBZ3SeIScQ7i9Qpzdly//5hfCmOqz3pzFHfmjGAuZGBEzkjZmh7repvi\nrlv7Yxye9eQL7OBEi1AYGDwsmYbzZ+e5ltiYr14StMmPB9wdjBKfHXimYhvamBY+wXeeeS2uu35T\n3LVjY3y1+wAanNHYuGFzjI3NcLRj4ztdkm/UqOSiIVAtG1tQM8ux+w8PRkdXR/yT37k9+nDaP45g\n9hufuzY+hXbzsWcOIGOxax2BKTV3H6D2wvodBNS3KbYhDKVdoeME0UQ2uzKG8MSrGBicxS56kt3X\nekNhbIkzaB65QnPbN66MFSw3b9/cFRvX9iGQdsbqlWpEm2IL2sFgF3fMro6fvyOz5gT+MBuSjpwc\nir2Hj2MbOhIn8chw/NRgDI6NI3wizGV7aAdEne6Ysk0InXgO0S7bNqtEUVGQ7FfeLXn7p+Ye/pB8\nmQmn410lgCrMu+VJIbNVn6TIqm0I6/0dM/HQnWvjcw/uiJU9HleJ/Mmg8Az2sV//0YF4dvdRxgjt\nW/Em0dLFZkTGUoTHZnYlLumciM/etya2/c3r4uD+k2hzx2LrttVsAmyJb33vdYaSHtrmCqRNU4C2\ndzwZ0QiXFQINgfOCwA9RYVMo+rZAbUkk/HiCgc4tapyf3QK/aHXCC5JXCF7Q3UHbypK9QJ8F+z8Z\nNOCcWlZlb0E5tJrNMF53CvvchCF4D0zt849cH1tXd8dzz7FLffcIsGS3IbuF61wtYZcg5K4RftYQ\nEE8JDoBeEtth24nW07GFTUFOGp595Ui0dC+NR27ujWs2Lo2zrw0wCK7kWM5xBjRO2Ml8WUTjpwGB\nKxoCuZKiUAVev7p/JL78g8PxyP2rom+JLrKaYjU4/+regfjmD99E+EHT2TwOH3MIfZ/Jr9rBdDyO\n4CY91Zn+nPaTKB70N6nG0YqLJg7hC9ZYq8H/TMvfFNpBeZ/098J+XHe9ORnff46F/p4l2JhOR8+S\nJmxA2zEFWBcr+5dAn525r6CjrTU6O2uxdmVHrF3VEbffsBJNKKtHk9PspB+PwaFx+nQmBjhxa9+h\n43GaJfkx3IUNYTiqhxDnmbZhFu2gtsil8fIBJ568sFG8TQ0ud3MIn9oqG62G1PaqnFEz2Y7D9r7e\nubj71o3xufs2sWGqE+E+aMsM8D7Jpqw346d7hmJoQh+lfVm2ppgzrK3rEWMOrxezaE/PjE/FX31r\nT3zmnq2xto/+scJyGE3ud598K558/jitWYpgiuZZV3u2hDJUVKQkXx6MaITLAIGGwHlBQIfEQN5m\nlwYgPpmDR5VNE+lyxQzLIdMQkL6/PKlEI27WYhizC2PRhrEQgFT4Pkzqgtp1hSdOgrfnMlVns96j\nIeZONzodaMw+dduqePiBrbH/yGj8yVdfirEpmLzLWE2jCSuX07X71NEymbn3pxE+agiUSUHh0YLY\niZE47oShDDKcncIS4oa1nQxcEfuPT0bby6fi0Vu3xF23rImX3ngJp8zdcejQqTjJUphftxEaELga\nIKDTcYVO/8bhS1/9zk8wE+nI42vbsC8fGT0bbx46FGcneuDp/Zj7luNm358VyfHkXcm48prjh4Ka\n0i38MVkkz7kBB5pLd7a+JzXDBwIXvJK0qjnS13NzB3TZFMdG1N2hzRuGux6bimdee4NxaTq6OtiA\nhIDZ3d3Kcbytcf0OhDOW5Letxya0oxl3dG1MFNnktmpp3IzLJsUx5v1x9OQEQucYLprOxOt7hxBE\nRzjG9xj82Pe6qFNkKH8Klx5y4QEQbuh0BW++fTRcLq/T+haO5l3agUeS+7bEI3dvi/WrOynBcTPi\n5b2D8eXv7o7nXx+IUQbPOVYLhVUTy/wKrIiYaeuJipQCcSuGcmd8rhMPAGfj+VeeolzEW4T5YUx7\nxnEbNcU7BWMhrmrX9ijYq1lOAZnfRrh8EGgInBcA+9TSMXNqkrgghFxKYKbbIoPA/nAGoRO6TO1d\njR13zgzVfSpoySRkOaK7PKYEnt6fW1WJr+prdlmhkVCOQASGOes9E4/ctzH+7i/dGIePj8S/+Y9P\nxN4zQJekuasXIR3omYt/Qs8/nxvhZwKBc/ARWKvigIF70aVPE2tiy/va2FnbFQePjMTQVE+88Nqx\nXP7bvnlp3H3zpmC/ELtq8YfnKJbf62fS0kahDQh8ZBBQyFwcNiIU/eO/fV/85IWj8ZffeANe3hnX\nbVka//M/+6348jf34PPxJDxfsQaiYB793gHeldpBU1VjRxEmXUFQKCpv+GUsKTRDOm9pllo+B41M\nRfrUOBqXgRuI0+bLKbWZdEyZxP701FsoRhDUOJYgvv3Es5h8TTIajaPpXMYO+d7Ysml5bFq/NJZ3\nL8F1U2d04th+w6r22MDS966ty+Jz7JJ3FUPYDDLv33doNHa/uY/d8WNx7OQoceN4ohihnSyVow2d\noR2a2/hrW1XMrGDn/adYOn/0U9dEf3cLJaOKwQb26T0n48vffi1e3Dsco3PLUNT0UU45BMSxVRM1\nFsIIlEYbnOwKaIVv905MoXyYQRwdZzk9jztu7kLRI7jMS5vUKLNpSLMFRw55WDmpzjIb4XJBoCFw\nXijkxXsRGaqXEPUX1scOww58D/a0qbWbix6Ml/u7mzEIZ9lCqtF3GrNBdytWWiKrhQw+IQEY1IVN\n5skwAeGAoToM5u7bVsevPXodcJyNL//VD2JsuImNQx0wpVk2pHCCzewSYKTDbBgNDMWTicpMtQiv\nnxAAXpJuVvionkU27TcT3YV3Dsjgu2x+OTtBly3rjjcOHmb5je0BuGV5dvfBuO+urfHIPTfmYLPv\n8ElwntxMziyhERoQuFogoD3ntWzEWcnGuNdfPciGlYgxVq3ePIJ2c2BrPIgd5zeePAxDgjLcXJSq\nhPfoXaJ/zr5IVJ8sJ7GpgSsUpvIitYP8pmwF/ZVd4JZbz5NVKLwq6JKahP4pkKnBU9hyNcJjl12Z\naOHUIqU2NXwTSGrNaADHOKBhkF3dr50YjLmXzkQPO+F72SXfvWQulmFLef3OjdB3U2xdv5x37dGO\nqrWLDYBr8Bqycmd33HndLvyBKoBOsEFnGp/JY5yYhPP6k2pCj+LQnrpYru8k3903bYlP37cu1qNp\nVXgex07/1QMD8fXvvREv7B3F9tLRoIt2I0xr3JkCY+Hr2van4EmfUnxN6VstBP2lLLc/uYpocJnd\njUmOywlP3pcJhIAR7hUPEtaNcDkh0BA4LxD6EroCZCHucfxutsQ//PUbYmV3U6xY1hGtbU3xS4/u\nirseiPjmYwfjiZ8egCBYJoBnmPeTifLCjP/MeJ3pq7n0aMROsO9vPLwrepn5ToxOx6/94v3xC07f\nYeKj2Bn94Z++wJFwMBOn+HAZmUpDzLxAhL3A5DkO8oW8FqflXIG7J5wwdPHlZqKfCYHLctpNTWgT\nwaD21GunmDxsiR2bV+Ry5IEjA4ntyKj57S+wGY3kDQhcFggoqLi03sFu6RkEp9OntexBM+fuaXZA\nnxgYjh07sS+EDmaxsfzAIaVCUiNYFmZoTikK3qaE6YUY/Q1XYX60qE/8qnh3ZVfFeXWTj/rPOZfd\nETZ1F5Q7zMkn7c56EhhKkFmW2hWQrSG1kRylOYQ3EDf4zbGq5Jj29O4DKfy11l5lCb47J5fr17TH\nTlw1LcdedBOu0Dram7EX7YgVdGXryu64Z2eZV842Xc/ueGDGDvllMPc1bGSqqdmlvZ4e95XvvxY/\nfm0whmc7qb9MZgFrCofu2PcEoVk61KTArEBZB0XVJ8XMorCx/6Rj2R0DnxSwhYNH0vJLstEEqUfT\nCkNEbwCrG6sKgo3r5YLA1StwJjLWMVKCrd/OA5Jno+aj52/mU3yIG2eUGHLLNGRMMiEEybNnTkfz\nZI1lhrMQTCE+Hc1Ojo+mkDXLoKwLoBLE+kWNWXR73gYloVQJfeDPx/l4Hxb9VUmzwMUPVZp8cU4Z\nC6nK3eLfi6bU+cK5kbFoT8MMdgozhJdfOhpnjrrcAYvJbvnTzEx5LkaYlc9wIkexXao3F9+dJE6+\nUu/FR3Opt9FL9VfBuP7qo6nnQkuh8vn25F29gQuX9y+x6kDiy/snX0jht0D4ZF2t6GK8Z8PQ5vV8\nn2kEzsGSlLOLX3jzNGcaj0b3qi60HVNoQAqrXyjrI7qr+pJXfrxWcQs3RF5wZz+iBjaKuSogMI8z\npbVqNT3H2008buw5isauCyftf/83dsb3nzgQI3pmWLcu7r91dTz/BlIoFNHserMC3XxMhAwAAEAA\nSURBVAdBtRSOFiCT1Ute4qy2KlzlgbXFysx8b+EKkPW8VpmpyQEfnZfK6gW2MPlrQZgzpOZP+Tap\n1zh4qQMWQdtKg4LpBJKd57fbER3O12qd2EK2xR4cq78BHOaws2z98Ql2lxfzpt4lrbFt0yo2CXbG\nhhVtsaqvK1b2dkfXknauNXbQ9wJDN+wg/Npe+PkyTmu659atsXzFqTh6lg2Fp5vzNKVjJ4epFmEQ\nHqJCws248nc1tHKdee2x4y3fJY3WuBp0mZcCqnDzPXkFwwwu9lziTw5EfC7HZ38XAzeLaPxcYghc\nvQKnqMUMxuPj8mgxiKycuap/zAXqFAF9UkYx1C95X6VaiC8x74zP5KUcyxDhJWSYyNDZyfijv9jN\nTFIDcoKZ4Q7+m7ZNGHcnA6gaUJVimvm0Zizhne2TJVGy+ZkBVkvTxjXBnbIt3KfRdkWIMhwLJ4vt\nIlFpF1XAVinKlpblB5mCRKsM3YSAl39oFF2gyfOfq/bWr4vbV2/y+1+yn9btTfm1cVP050+/9koy\nEZmnTLW45NAwAaPyZmbC+I9LOJGzmX7YTsv4MOGcXNkRGBSw0NShApELMMIjYVcuyZStr2r5h6n7\nw+QpzVBAz+bw3WS6fBdg4+CYTBmg2RXbX7Uwl5PIXFDAXlc9z0QmPH+wDnHEOtNDAEnJZgmesuUR\njTMt3bF3YCQOouHU/ckstg7DI7X41tNvxSP3boifvHkqzo6AV2nA/wHqzNZUbTxP06SX7GRiMA1i\nyxnmFWpP3FA2p80Wu4Z9WzY1vE9556mmEf0JgIAoKbOZv8gF0YaJ9HjG0I/kS3sG4vvPHsvNjDuv\nWZP01dwyF4exX/za99+AFpiCKfBkKe//U6i0wslFNFFFSWF1vrNAr96Rcz4N9Zxz74NlmbekTVrN\nNC5ESxemKDzEZXcGozrNmAihGdrx3wyTfx6ynBxTpDfea0QjP5zgvbKq48MEWtGB00fj2Z+MxOre\n6filR26I1XewMxxt5jR06a7zMxh9trW2RlsLzpBY9VvBEUor+bvrls3AGrdPI9NxEh+af/ql5+Kl\nwzPBwbjUZ0sVfIErm4PkbwVu2SzuSwo1ndzmX77n3nFjdl5ad3psKL8FRsKpES43BK5qgVP+4Ayq\nva0zWnUKmLMqBClmnWBt/Y9oEbLCvUUQPwcFC63lW9F+ISykmo+XMdRDEzND3NfWn+rxmX1xGQts\nSSIqwbT88Thfbr5YKLuKdxAtAkVJb7IkPYuqislsJV262mAgTlcS57BEMzBAW0ApIe8g7Uxlmeph\np6jMdtZSuiupZU7vHhba9O7v67EUU/WnlERtMOwpDcAXZ6yqq8fxVedDvnrb+/mX73qzAPd8XaTV\nRSlL200li5pV4EbYTlBma0tlCnxGXlDVi2r5sLdZLT8Vzoh2Cpw2xGW/Emy9X3ABuunvNScaJOU7\nOjh+0NaXUqWl0tssVdsoy3O5jknWn37heZ6nOSnKSRYDtHZsLGd96Xtv4tqEI+jQskwjbKZdVWJW\ngWi9wee5fLA0phIefsoa38uNCblsaPPw57qINM9TTyP6kw2BxCDIAQRKHBfPFbzKBDx5LXg9Mr4k\n/viLL8WTzx2KjWjt22GMZ8dm44U3jsYATtr1XanfyOSxHwR1k5wWEhbqqqiyeoLuCgGe84mqt2+P\nLPGLOWR2BXo3uJQt9ZrKv3rBOZCUnJUAl+lzcw0p0+SJ5WyX3mF8/tP1dFMNYxp8NbXoKYQNsZvX\nLYk7b9rEhqCtaXNv6Sgu44mXDsRPX0UYfW43dt4ron95b6xiI9JWTm5as6I31q9cFn2cTLYKO/D1\n3ctj7T96MP7r//VbwNMl7zp8+C62XJ6myFtaW+9GJWzWH+ff+TmJMyi0zsc73lbllteN38sIgatY\n4HQ5m4EO5GzDqNkBMHC/0Iy9pChXQkFBRaxq0F5AS1NU6bxfhNg+XkHBmWZqJevMIPsCISkgOSlN\n90wcZ7Z2zWp6lL0FHvRZhjPfRW/KX+ZP0PCeNJpItiGw97BbcQpnxoFm0ZMnUlbgnbkWiLkCjLEG\n3iQTK09X368dVBNQzuBoBRj6oZ/vkx2vOn+JO7dQrXcF3iUuGxjLe5cxrgzQVEwScExd+CopFA4z\neC3DSg44CwXW37/bxbLL33xylw2hs7JTdAZBU1rR/Qi2nbiGmZkdwwk8Q8P0EnCSvOIecJxGA6nr\nqw/G763zvYL90raLwHIZDSr9pq9q+qmemtDUpBBhmvcqq/HukwqBgj8ih7ShYOLGE2Kx06wxoVrR\njx5/glhO/+nC5c6pk6fjzKkz4Bo0AF43ocHrWYJGfxicS+ErS7wiwTnnCmAGeQN9loa1Z6yPEV7T\n9RCDiMveKVjCy4XMlDwERtjMGFMjjw7ge1qnY+OaLo5vvi3uvmEFdppoP+EFZwen4ie4Nfryd1+I\ng6cm0Ya6KrUpBgex8x6eirkDQ9HyzGE2IKFghU7XsSP+b36G04Vu3JITR/1lJsGmQsBvI3/xWu65\naYSPCQSuXoFT4ocUmiH6vr5uzpxlR/PkcLRyBJaMRJRNfM0hCsZSDURX24eb52fcOOAyetu3GYTt\nFApZ55id5uivyZHo79vBGzPASDJV1dmKeAspV0U6K/WNwoT3rTCYqamJ6OhEkGCWryBqjsKCqlzG\nCXtzlLf+XonB5i9uWd5nn8qLevcyjcvqk+PDsWX9Wly+IcApJaWkpAahynSJe1mv1m9a2k4E38on\nl71aOZkkD6tD4Cw95V3VyWy/XUhK4DV5PlA3LMBv65UM0Jk2yGoR855CpCXPaE5/gE56GKlmwMNm\n3aMgcHp2s8eVNhGv66sPFt6/caX9pTxTz4CrLQi8q1ZwHGpOGKypavsHq7WR6hMGAXCzYFCdLlLg\nnE6bxr6e1vjd37wujh2djkO734hf+exOJnLgUxKVky54Jc/7j03Ev/uTl/Lo3aSRy8Uf3ufTlZUN\nWpg0a2Jpt1xzUqpQSYQUA8fnj5fQtcdgukTd3IKms4mjazlVaSNHbP6Nh26Im6/rx2YTbSRZR8am\n48cvHMfh+p7Y89aZGGeVY9Zd51yb2BEPa4CXKtSysYdxesaJKlW8eWA49h8aiDt3baEu6yzHYNoK\nm5dKE4TPBSURkY3wsYDA1StwCn4HPgilD6Pl3mVt4c7Yvk4Gn1wYVuhEWICochkyUfn9vpmkdwUG\nR1q1cNkHlzHtF58OoVPfZTPTI7GUXfLbtm6g8Wq7FAgkZPuyuE8O04sH9kyQMV2dHbF168Z4Ef9o\nbV19CBE47EaoScZEjQq6BkXNLJd3hVXZtsV1ZLIr4qfYt9o+4WGT3qWdvqZ/s9PjCJxDCC/rhG7C\nxI4WmJPosoUyMJZhsvRF+AvyG2/YGX/9vafQyKBhxIVJGv7TVydcpa8KonU0yC6Q6T27QsEJKPJl\nOn/4I5s+7+Y1vyRj+E1oqiOawk1KK/jZpNDHgKwg2qzqnf+lnHeBuw27gGBR/oGYOdjZw8nJceqd\nTfrXN64JtEnLzRSmbYQGBN4OAXAzScR4NWo+KHiBz9No8XR0fubsGE7eJ1IoUu2nKCb/VRyahu8d\nPjWHm6SJmGxtw8oEneHbNgS9vcrL8kzfmtjBXYJcWz7ipDN/HTrpU1IU7pOgJrShmYJ+St2tSIst\n2GpuX98dn753dTx8x5ZYiorSfENjc/Gj5w/Gt588EK8fPovdpnav3dC94xHmCSw3pFcLGQZVGOdY\nnHCk9FqNoyrRmirAZzNQpJiujCOsWPA9is9M39YTcdcIVz8Erl6BUzx0YBMnGQzvuO1GnE1/DxcP\nZ6JtSVvadjo4KTCI6mXArj5YZqoeFl3F+isv6IxGXZa7hWV8LouoYfREiekJjgI7czgevv8mdgka\nTxrhwp3/3y3IXtXgKZBVAqRLHbfuuj5efPXrMTl2OtqX9Ofg7kxXRpBpZVaCqHArbmQkhvNUlO8u\n14/sU2jYNq5cbLt/JbYwYN/OoY2bGj/DmcQtcfONaDXqjFg4yZQTRt5frqDg7xdN2At+Wz2D77yO\nWLeqP44MnmSixeknDH7p+Bm8z539JCuinh3nwWzvE96RRHhRX4UnZneq4VJk4gI11LTjzCpsJ0In\nhSTcU/grLXifas/7utTh63rLchcuj9Ms3Q2einvuuDF9ggaW1KW3F1efNTXCxxcC0r4ud9xsWnDK\nTafEsrnz7Ohc/PkXXmXyORM3bVkeYxMt8b2nX42DA2yAw2ZTxYXcdwYcbMYRfG1KLitRXolBIlR4\nlH7LjvFk28mznWK7ubbwFMcTN/65ochJ4xKcw2/f0BOP4Ff33ptdOkdjiaRwnA2yz3N07dd+8Hoc\nODESE27YBQ65Sc+zzqVReJVujOQZFf8U4r4qFIz3TOqag0c5aTZOczEnqo5aiveFe/hU5eGmET4W\nELh6BU7A31zX5CkMbd20gcHnpnjsqVdjWUt71NqXgtigvEKnI7U4D1I7i61p8PWu4Xzx75r4kkVW\ngmEevabQCeNz97kmBOPDx2LrxuVx865tiqR1os7untO+QuzkJVT3lRDmDFRGsW5tf2zZuCL24sC3\ntRUG29pVUru8IXMCfsIxSyhFcetN9eC7KyiUji40iGd7Uo5JU2yqs7ip4Rg+fSQevPsG7CK7gI+z\nfZKdk9+HS9vPqjZrLuyb1iuB+Rm42s4H778jvvDNx2Pk7HFscFfCyMFxd9KlUEpOM0MG9jWjeHzv\nQAY1P4RCNm8Xtx1YLJJCvSk/BTJUUNq8uOXVvWkvINA/u2rxKe7aLJ4d9JvxKzg6SH+xpbvxuu05\nSBatvmk/ZH0X0LRG0qsbAom2dRyXiuQCWgdrBz02qQCFreKm3rjntvXx5E/YkT7XEZPTrPigRddB\nec2VJVJh9Zn4qaP1Ky/YM0U+/xCwaaMxKi7yQAeYm31wZ/ks3idcGWjl8JL1fTPsOr8x7ti5Jlb2\nsVGI7EPj05xRfjK+xdL5PjSaI5NYZtfUaCJYplArvyg8w0LdyGeoxyRFpuIiY2kFY1eSNmnlSS5O\nGGypTKd69rERPl4QuKoFzrQtUwgCU/FFi5bvTuWmePr5V6K5vS+WLFsJ/nYgOHjslogMuWFbct5w\nhY5VzTI0GF1x12YjOU92ZDBGh45xFNkSHM0/xKkQzBirETqpmWRyFAPPKXBwW6L8TfImHjFV5gOh\nt+IO5JcffTC++JXvxb639kd33/po62T5A7ueVuA2o7ALGymDuuzLZ8rJer2vQlWxz1X8u8X5/sPG\nV+W+RxkUnfJwnfVV7c0TnxhinFlPcD7y8Km34rZdW+P+u3eRspxq5IYYtYhZAF3MVi5uqtVewlCq\npgfiuwOIjJn27dy+MR4aHI7vPvZ0TDB4tHctZwOPO8QdWEjPt5Gp1+pa6oUmnwd+ORCRget8eJek\nfv3cH8T3N6WtMa5AyjtXFcq7hbLepaCshPhF1ZV6S7ttfJZvP8SzmQlckR2LpsnB+Pzf+uVYtby7\nfDMbU1LWr6WUxm8DAm+HgMu1BRPlgeAotKTLLwUodRFuwjt8+Fh0Nm+Kz392V3zha2/EMOe0ztQm\nEEo5TnGsNU6xIUbPDMkf3l7BlfCc9KTgxx8Dh/2VKpUF5SB6dGlH8JudGce/5nRsQ6P56Xuuiwdv\n28jSubTHZiCWzn/w/GGWzt+M3UdHOeQBF3/BH4L5HE6Tm1F76qi+GknKhqQyUUxeW5gvIJKGrdVY\nW+JeBNrEbUmCGGK0cUbQdh8b4eMHgata4HRp0c0tNVw2iKD65Xvw3puil1MQnn72tTh9bB+Gzz3R\n0tkNo3BhvQTV/VdPkABhDJ7hTl/1gzY2cgYBYiJuvGZ9/NxDd7EUjP0eDKQQbbmcQ7ASsdRt4CLp\n5+Yj4FBmnsJDwXEKrVFL/PLnHo6vf+fJ2H3gYHRO90YNTWcLGs8mnfNWglhdmCjFXnnwzBbZbfuM\nILbQQpgay7Gz02jJhrE/GhuMO2/ZGQ/df3N0sAFFEw1PQUomyn0lRlnM5Qn2wI4s9ECYp7IZXFDL\nedctO3Cs3BFf+foPYhi7xvZuzCHU8vOXm31SK/7BWl/EuyqtTwnBKqIMEtkUpirZtDKU5K1Qrr8z\noXhWBqP57Oe/caZYMpc09aLsq94Y/JuaGI0xhM1l7B7+7KP3YU7QSR1sODCosRKfwfWrirxL6xu/\nlwwCToMY9uCpBd0UxhQ6mUjDJ9JbBZO1Jd2d0cFG1Ft3rY9tnJ41xaqBwpWHU+w5Mha/9/s/Zhbn\niXOJ8Jes9RdWkdQLXcA7kop1/cSNE9XWJu2fR2LzhqXx6btuhIesiKVdbWmnfYLTgn7y2rH4+g9f\ni30nZjmeGeGas8qdtOZpcZIqDKjyCOHpcaUGzWyciGp4IGQyYYHzPP9C5CR9CsL1zqh1TSimsOl7\nXmSD6wkal48NBK5ugRM0rbVofwJy5wA3x3GJs3H7Tdtjx7Yt8dIrB+PAwRPx1vEjMTw2AkOBDEjr\nQFgnQZBb7DbG4PyrwvSybFLmb6bxrwyKklIhqIW05l4ICgKU5mhpnVm+af2rylp40l9tps+feine\n14PyggqcZbjBaUciuvPu62Pt2r7YsHZ5dLap2cRujgQSalVLZvXh3Jtznxc3pw4/tWe9Szvj0c/e\nF2tf2x3PPPNCDGMj2NyCGxwEzuRYMKyqqUVbgFjmQG/pqT3gLh985lZGkv9LrvJKmFdMyYbU//Il\nPz4SfNQdiW5JhKWdTEaW6XhZz9bOhCKXYurxVmlbSzF+A78eeoxZnMUBL3c4r+rvi/s//XBcs2UN\nRzXSJ9z7pPsTa1Wwts56CQtXoi5DKP3ITiUcxC1NSrARQTPdHDfs2BRdnb8QT/3k5Xjpjb0InGWC\noBYCCZoWO6DaH4N4bBAuvKt/nxQ2KbLSWZQ05iUk3M1RkLXCbb9/WVYjF+Wkv07xnvq0Pa7KzjLy\nx/oojvIUWF11KG2oaKukJImJ8ptNo6GZxXOAS+lb1y2Phx64PTauXcowxU54ytBudWHiRGnku7om\nlaXPjd9LAQExy7+KBqxTGpG/gDfiLveH3hqKP//6s2l+NcNkp4aw5k5rFp+xZUQwhRdW3KWUZzkX\nGai6KqvQhM9G5ovyLunQ9pe41Aj6hvgqzKeeX+6uv+GFJ/W0c27S6r5aPPrwbewUXxtrlzrBbooR\nNgk99TKC5g/eZNf5OBpNdquTtUbZLWwMdPlwDvd5MxAuBl3kkOdiw62JQbo28kxzRQpWV8yZfEDh\nkrblOCvM6VmlybTwjCn9kbcIf6NLb8pkIHlXpjWvJRSeITeb73fJYHGNcAVDQOy4akNNo+W0V4MA\nHHxBdDU+NXbc9fZE3HfX9rj/zhtT3JviFBKXSd1V2I6/Tqx1cBjfGmPTOh/XDUQBhaerJG6zXOLS\n6wyEpANrz2mV3Uw5aPPXopCHcXVxGCzBVmSixkc7HwZJjbWbOnCkrnE1ZOLSeDIBLpZti/05Pgzh\nQKQQZRFlC2HZI4mrhsAw1wHD69U5rmXwx0sFVcbi7JeyUbLQOuHVL5RgkDTrgRfZPx5TwMgnYhTq\ngEryBSDS0zML/K6JB265LvbveStOnB6Kk0ODpOFc3qzJJVu2iyDAtbRPxxQNmZ4yztOBEDbye1CD\nWln6arAvahbsazNHVLa3zvENFBrIIRPKpWL6AdMySy21tnxR3GoMTcDoUngyP/2348mgGC54vHn1\n5uhhuadUZX8KDAEO6Qo0hnFYvGf4OBrvWqxbvwa/pWuzLzJLe+/xaqYt83NueZs5S3YjLmko1ZZv\ntzCepKk/7RDWXhTsED5xXbRtXW9s2XB/PHD62jg+MBjH3zqJ0ppEZBZanigynTjJN1RQ5Hs7zs7W\n0I4zYWnxAYbvWcwl8E28Bd/n2MmqgNjahNdr0jkJmGYXqpMpB6QUDUmbGyv4PtNswpicUysylnF1\nFMi2qkZWoVk7Mx61Me59xpzDkCvjVmnFDnB852XXb4yWNuw1r9kWS5xg+d0pyR5l+3iSLAtAqrb7\n3AgNCLwdAuCNyJg8SLwT58QnaACc8ojE1ctnYtO67jh1aiYOnTobrxweBofd8S3eV3jHhM7b5Ftv\nr+NDPoPM/rNYGTEjCFcfbJ/NFsmh21lo37fQhrSfSczLX04EpScibV+2kWweT9LGmLd9Y1c8cs/1\nce9tm6NbOzTCifE5HNzvje8+vj/ePDQMXS+DB+hnV+ouddiQVM5YJm2TCuUVyVmyEktizDQ+gw2o\n7utRvC006mqjHnpLSDB63jvfpIlxdhrNQRPCvat2Mw5y8PcangCKU3pMG1AuqG2uwfNaZ3SDSBtz\nIK0X2LhcsRC4qgVOBboUwFJFLyGy5JukAAozWCUxzOEMHmbiGa0zCHzaQ7bNDcXqrkFOSJiMU0PN\n8dZQd4w0seGCuZ/CHZhO2lHoyUFY0mIwZAnCU1VaMKxuobweiHVJp3v9IHmFz4qyk9KhEM9c57OP\nknUQ57fuGk5B0Uuig9aTVDU5HUPHBxAYID+XInzJj2Qu8zFoq9qysic6e/U3aKkagEuuhTllFhNe\nUKjIvcpkXXUGQVtbaU8yPwTw7desi63WBwOxe7gF5om+wwSWtR6NtSs8eqOJ84c74+zESqCDP0b6\nYNqsBXA0IRjZ8jyOlFhnxV1tc7G8T3B7RGGKqcBSIaKgpU6HZW5j07U4PjSFQ2HN9BF+sh2UnAwP\nNkfBI68didYxhVcbSASXvM0fW4E4zW7LB2+6I8/aFcZz1JtLzvUvUm+trawgkfeX/yeh+LZm1OP4\n+AqPyXRxzKyt8urlK2NN3+po3rkDu1t2b4NXDqTLameicyluXziGcnxqJVBiIwR9FxPFJO0uc0DN\nb+yPtlaCme/ApKkdAXP5Us6GcrCmPLXh5hKG2r0phKplrs1Mxxjf9/gw3iJc+ydeDbwYkMNXvfyx\nvTitZ+erNJubgSiN1TuCg2f5GjMMqj03bEihWE1+OrnP/Karw8DbRmhA4ANCwDPQ5UUKVBxb4B1/\nuDeHz16/rjX+/m/fFutXI8iA42c5UOPPv/oam1GPxvh0F7guV5wCT8nruAMuFkHwA1b+XslyMkjL\nkmc5FoDfNi3pM6vKZ+stE7MK/xkHVWZA5+V0IDL5PxUcrPoxody6ujUevmdX3L1rDcoYFCDUcRra\ne+6VE9ho7osDaHRHURjMtizH9ZNledRnKUc6S5v2Ot3Nk1/eZANpY9WW9+qg70ruMmms0jpWlBjh\nm5NPTMgUYJtmVPw4NpAT/i+vaUJxoIbVsnKkpMj5NlVFNq5XJASuXoFTgSEFTYQ9CCQHLQjTmav3\nOciBjpW9SJOzL2i5DbuV/u5TsalvCKPw49GJD8/Z5uVxaLATjUwBh8g8hzqniUFTjajCkQOfs6oW\nmIAatTYG0iU8uyMPGqnXo6siibMM/Lo38/xZYvj4to0LhC6jc7x2+aCN51bKqyloEZeElcQnEUlQ\nDusOwN4Vwip39QcuPstAP6pgvfbZQj2fdhp70VoKgWplfauwORa9LUOxse9YrFpyGk3aLJu0VnHG\ndlOcxe5zGrtauaJihoxrzvIMOBG2L2rLtLxtYTNUE7PbGmlMraa5aJvrsOVZDXFbMmOXvRGLgFVu\niiklJgBycmGD/RiGeQ4kdPjL8qkj4+txCTnvr+6gkKeWxoGiGZ94zWiVc17CZKFFaXxmOLpqZ2NL\n/1k01+NxksnSgeOTMTa3PmHZkrgF60bLqBZBDUp+i7rAqeCp3Zcah0600jVoorWa+ABQB0bpTNxQ\nIdHKp24C+VvTt5/f00GilKjiG2zI79iSAj9CrUfm5aBKG7ItmShpwalVy4xeA6S/RMmr+2M1Wn8F\nQAD8Ss2kPEfiET/hKSgnHrp/Z2xa0xlf+fYrcXZkLD7N8688sDV27z4T+08VfpwcHT6m4MdoAELX\nedtF9sxxoZy641hhm3hMKvAKYdlWqEzNpgKnRw87kVR4nEXrh1ojV9Fy5Qu+WWOsW9/fHp/71I64\n/5Z10dddxsWRibl4hqXzb3xvT+w/zObTGcbAJk4tgzjdOKUP3eT0VgfvDRQ52ZjkrxfZSbLLC1wV\nsX+GHA95SCGayYBKH01tnOTSOTo6kbAOVgtVOKgJbWZl08nsTMs46eRXH803KC1q/P6sIHD1CpzJ\nKiBKBTlCQTrvJJaF4FNZhGSmF8OxputUbOlD09N0krxssGBQ3twLAqOtOzw0k+eiq6lR0SlyZ36e\nPWVcDZD1zLFbLwUpbcggFHc7p1BZrziZAw7ZbUiLMzJutJ+TvS0OmRym4ewu30Bxkk32gDgFCdO0\neI4thJentlSEmenqpZ1bbD3y4i5z2i1B0AoA6UWKGae9lCnVmGEuazkV16w8Gb1tAwgE2D7ybkXn\n6aitqMXuU1NxdqYXJrE0+YXCUGEJNlSoYrZPxxTWa/QZ0ST7VuDG4lYdHrAW0pMn4Q8snBDkI4w2\nuZUAAk6ZXkidL5jO90CWOvOakDXab/MzAOD5mvIziJ8DUfhc4HA7uOgnYjDUttmvwrdZ2nIiNq8Y\njBVtJ6J1Yjo62tuidXkt9p1sxZpLV0rCh3x+4Rw8Wb7iqzjoJJgoVK1QKwNsC/BzoiDIEp8Tdn4U\nvlbiuhMCNTAMBkTzpbLHwrjCbW2uxQIHxhoDR+URoHwhk0sB1Flz6Q1NBkW4OlFfN/8ZQLBR5CcH\nAnIZMD1NOJzMgOtMsET72dpUrN+wJJ5/+UT8p6/vBvPa4sjRifhn/+AeNlPipeOMK2rSGbQlfzeI\ntB9ZkN+6LC2lOG4sCGXW60hG5d5Ch1AfE8Ai7HrClwYzjhWaiw3hR7M7Pnv/rnjg1o3B3id4dsTp\nkdn44Qtvxdcee53TkoYoj820/M20ZuHJC+X5eiPxEIkyKslzC+V+VNO9hd4VwNmvOsXDV+AM06Os\nWrmOyHjLngzfqVHW3M2NSpr+NGHiUOM40qaaXkXonEBphCseAlexwAmOIfwlYUqkEj4DY9G0FCEi\nNxIkscxG5+xZltERNpedjo65M+Cny4VqPWvRPjcYG5eCseR/a1CkXg7xIQTBhXRiWxYCqQlk538x\nmobwpxmAHRLzZASZQZKo5MSdI3+SAm3KfOY1rh6LEGaYYZlyCuYhe0GKgqYdxlP0KoO6eXNJswhW\niktZAkmrYEllWK9iLvYKC6BA9gUnDJrUOvJPwa6Gk/Se1tOxqf90ETadV8u0YVQxN4qT4KOxZcV0\n7EMbcHa6NSbRuClQK7CU3jtHh6nUYaEYorLM2boMxSWuAjk7qKguSISHNpwy4MKEi8Y3RZ4Sl6nt\nt/nIZcY6jOoxJb4eB1CBr20m+ioO4pQY2lwrp55oj+qQlOYjLKd3107HtuVo9VvOMhCpXW6ONtTu\nqzuGY66/he8UMdzUDzhYrlKgFx7ApgRxGYD5yHKjg0GTm3eM4DtQWCYrZgwKmkb5fUjhnyqLotIs\n6Utq8lMug4aaivz4fNdSyeJk0oXaHIVNtPzUZb1X++QgQdD4uYwQEEmdSFX4K7f1UbOo2XSvd2Zi\nGB+UydlZuRlEYz8X61BKHBt1HwA8abwWJ8ec0JW8H1lnoAfO+LE1lGzpMEYal1QG/hdRU15ahFHb\n7MTO4J6CDlJsXdkRD2F7f8+t/bGCpXP7dBpFyrMvnYy/xr3R60dGULPQt9pSipb+HAc1a4H+dXck\nL3Y1Sx6QdTvRswW2yavQuthgOQiS832zTPdIEFBm3LClK9Yu72fFxknrBOfWj8bxM2MxMDwR45O0\nAj7kSqbQauLc9rRlrfMii2iEKxcCV7fAKREgGZUZocMuBOhyQ8JbpCxLDi0sLazqOR5blp6MrmY3\nvvhK9DZ/oaeOpsFYv4w3MKNDp9nw0IQrDF7OcoRZajEtdAaSpvxmZsKSXzhLlkJ5KuJUlsyzlAQj\ncFaWV3VHEpV/piZYMc/JtNBgMp7zJDGZxnbUBTQeMyc/8zuDYTJZhuUQfP+RBuuqQzEZbNaAoMLS\n97JmzRFOxvLO4wiJRYvrbDiXQ+hTjU0lK9nx3bx8Fk3nJJpObAUxQC+2gWrNsiNZuprPGf44Ejzf\nF8N9BJvsvz3ynu9L+W5Wyo1IaoGBaQro2S7TCYHqTwZcvkNlhmCKKgjeBHE978dCgFHYR9pLwY/O\naW7QzGauruaB2LbibKxoOYkmBzxOli7u4FcVxr5qCTaUaAjeONGED8/laDbQGlDUHHr+1G7Ow1S8\nFB/fPvgAVUDdDA04USgaTp6NJFTyfIWr+e15VzY2lK+U3yMTOPUgeO83ylt/k4xycEnhNWMaPw0I\nfFgIOHFRM4hWECzLHdPyGTXobCBtnWqKu2/cHJs2rUpG0SZPw0bkdz5/czw6KT9qioOHRuL3/v1z\n4DEHY4i0jAkfRUjxVSnMljEOFQWFbYT/IWTS6hQKpQ7lK1cG9AvaMjscm1d2x8/fuy0+dRtL512O\nezhsn5yNJ188ikZzf7x6cIhJG8M9f5olacLiRlurk5+WiSLlOsMjrhwPq0aXcZXKygriRyUuUC+V\nOGYkT+BbNKPgUfBtbj4Tv/v5h2PHxqX2NsVvYXsazwBP/PRAfPFbb8SJ0dbcvDvDODzL7vkaTESo\nNcKVD4GPCoMuQ08dmWQZErsCiap3B77kACAwsz6ElDaIcXXPqdjQfwJHt6hz4Cv4duDPwZM8amBm\nWIqkjM4YZHldf5csrw8yvDax1Z1AMggYoRDits5mKCUJFLV/2nBmKsqzXGpN7FfQTE2RbSNAFHUR\nMt/balsq47Ct/kk0LjPKaPIdz1lERU0WVN1zW4V3iapeXfQ1FzRoT4vGBi0Dcc3y49HXdgbmzBnu\ndpdZdotLMFV/cbSvbmBl27Fo6Z+M3QMY3k+vAZatKm/pG8yNv7Ls6nOZJBSGZzn1JiPEKLjkvJ6M\nwoXVlYSXMDCZf8KpAGUxFLyvCsoECz/niV5IcLXdgYsIm81oAubQUKgJZksQy+jHY2v/yViNNro5\nDfBFLHDakYhBRLxt55utaYdmVs/E/hMTMda0gSRMxHgvTIuAWCCcg4Nw948iBGMZMLznX77ja/mC\newcp3xc6qw8wtC7HUwrI6V7aeEpTCMj5gqyZxyLKTUUfTgwsuhEaELhYCBS8BseSZ4GiCndoCNUW\nfve5o7GRjUPa7ItwNSa7L+8ZSLycJM7x5vgxrsTXHEMYP6SWjyZQJ+XLU7NNPEmPTq9tcxt1TmPP\n3ISg5UpDBzSxZVVL/MJD18ZdbKxbhvcNSxgYmo5nXjoWX/rBfo7lHEM4g/fW2FWPVsM2W1qOk4xX\nZWXD1TxprgoVpRmj4sb4wgeqFBd3tUDHaov2HkEenuBKYA1PIW28wFQ8/rd/+Zccsbw2Nqzriwfu\nWhU//8AW7Gt741/94VNxatqTBKcwIbAoS/oo20dxjfAzgYAiw1UdHKcqlxZJMRCog1Urm1ra4wya\nzaHYtOwM9mcsIYLYqY5nVEwEz118RUCcdfMKVNdGvg19aH4QLt8aXBFj2CIqbhGRs0kZjnmtQxTP\nqzSTA6LkU8hoAai+NBAvVdeZHKVxX8qQAbhL1yFVgStLyX4ZU+Uvpbz9N4tcHCmPuOhg24SLWis2\nDEHYS9vPxua+U7G0FdtXBRiYs9rdol2mQpqZJg70bxZJo7llKgXTrX3tLNsOIXR2McM2jz0vTNTt\nUDKKhJnwy3d2ILkIad0uZfqSJzV0goNyKrgUVuNTBSdjqmA/yj3FZ6hfuPfdOyJLoqvo1x6IA6U7\nfCsmWT3NI7ENk4eVKWx6xjhBQTPTuUwN3IF1sxt72CixmpNF5vqb4/BAjQ0Eq6AS2QLfgMKFZtaR\nP8LdAUjML0uQ1l2EQ3HBO3CYP+/KkxkXB+oiE+heL7g0q6onhVRyFgG0fNW0qa4nX1xS474BgQuF\ngGjnpFf80348sTN5N9NkNJn/32N7xT4ETk52E7mlkTJTZoLNpC7d98jLEOBUWCQey68+omCZhahs\nKHW4+qUWj/KnRmJJC+1C6Ny6vjseuHNz3HvT2ljZgzkNNHd6ZDJ+/PLJ+PZT+2L3W2fx7qFrI5y5\n00a9rcjTFWSnmejlDnvoXOVJCmtqVZLaVTEY5NUu73tnGt/zJgFm7EUE+YfjtGXxpzY3x5LcM9DC\nSUZwEdr15uHxGMGzxtNvDsRzL++Nf/p37o6d25bHLbtWxXeeO0MRCNLuNaiY+0U0qZH10kDAkeXq\nDBDjLAp3mULaGkoQ2pixJOCGlHaWDFcuPcPZsIOxJE4xaSQy/SzSXZcgGThz+SB7j2AFJBBHGeiK\nTecGNu1JaocH8dg5hzuitPeUPqxHQakMrSXGgdF4SRXi4X2hSwnUZRhD/dcoibkeJO/8J+HxrpKB\nLGmewEvWqoh6zoXXRliiLOGjC1QKY2iFwS5tZRl92TCbToZgUSxtq26k1cV426UQHunnHIxMxsG5\norQF+LBE0tcJ7Ptb47XTczHKEk8N/4wyk1wWFxYKPqkZLjCx5OIHMgulFjQI3Kox86qmy4lBgY+1\nWI9PBY7mWgilDN+dG+oDTUKt5D33/dX1pP2spyN5+pQ2m1v62ArUwmlUuPBKZo595zQfKZeeuGpP\nW+DkdKElOmcmY23HYNT622LviRb0oyv4fn6PKl0FI7+Ofwbe18Ga0Mx77xieGIWrZToHFs0yFr4L\nCXOUdgD0z4wMl3xDKSoTEjefJzP67LcuOJLVN34aEPhQEKhwUXwT49C0peZSwQ7VBcfCNnGdQ0BD\n5+koUTAen82z0JM7uKdx1WPkDO6GtGuv6OBDNecdmaSrpASKpYWMZwqT2l/Xms7G+hVd8dl7r48H\n79gS3V2kg2SGmFM+/eJb8a0fvRmvHRhEo7kEXolbJ9qWA7waQMYk+WrOFZNGnYOq2bX/1gd9pdAp\nPZY+SXrCyE2xhce+o7EfMqKUPJ+ZdgjGlOsZo/XsotZ4hrFiphnvMbTrIIuT3/7hG/GPfvfuuHFH\nf/zw+QHSwRHSjxqZG6xhHpxX8s3VK3AmVGUGzr8kTWdJ6sA4eQXBp6drLvqXd0FkY7i7XYXrIXe+\njaIcw4daEpcbjZzBqa2D6NJ2pTXGPXu9eQmlNEff8hb8c+LUfJhTF7DvsXSq4Cp2++fgWx8kpRje\nVGH+SfolLLzxqRB1eVWEp7wnkVf/nHg6M10cqndVXJZpZD1fFX+x16yWMpvRfi1BcNmMtmxF61D6\nV4zmNooHjlk5bbVu72VWCn4yj2mM611q0r4WG8GlSw7HJtZIDg0Mc5qFGmM3qRSYFHaeoj7lWFB9\nhk15RYS07AKP0q4yUBQo8e5DhixLPPiQ+a+kbDMslTUjdLay5Nbbw2YAbA/OTPWzx7YNR+mcsMV3\nTO0znVXAdxNYBj7cFC5Phmcw0Oe7Ntfao4djYSd0q8rnTNstAZRCoV9DeNU/XH50BUb/V1CUJjID\ncRWN+Fy9N3F177WkcVDLgkxZPsx8jurZlI3QgMDFQiCxr0LhxGX5jHjdinAGPk/rAk48VchyyRl+\nhjZihhUwkVJbe1hbLvC4BJ98TGYNgtapg/IskT+IKPEXnM+NdMZWNkMZVyhDkki0N842ZEkWCWWh\nOOmsjcfmVR3x2QfvjNvxo7mM44fdFeBmoOdePRZf+e4rcWBgFrd+CJrNvflO8yODbdEHsk7LFBql\n1XQrJB9Qwku+LXX5zmAbaFAGYyzIPztdxefLD/1Th06Wag3y/codmr61XQizOs2uEr7cT6OpfXM/\nJ97xqq+7lWOIWcthYuDENoVhCprnQwXob2vfQq/e9qLxeAkhcBULnKJq0TQmduZcTvsU3CQwwE7g\ni/AQmyHm5jQ+juhvm4oNSxmC0/efhAMRSWxJXM4imd1ivHxiqDNOTnazQ7Zg/TTLujomb9YOpm7C\nLEswv2Q8T4xZDuXSLOnSGgwFzflNwrDOOmGbgLTWUv2ZPkOVOUuxnwtB5mTwUlpIC+bT56uP5gdi\nb0U471uGQ2ROd5mYdA+kjvFH8/hQl7sFUboZwjShCXvWsgQjAOgnL6ews5mY7eQZpgkj6V/aHZOn\nsTDUnQVcu5oopBZORovGyzg3oRhSuLH7dDo1osb5nP31h7/6ex5KdP3X5wyZoXp42/VnAbe3VXEp\nHl1uc6lvjm+A9QInQqme72JpvS2uYam8p6W47xKOZTOAcLTzczhor8VLAyvAZE/sUIvMJKwu+Ffg\nLXhmXoXVBLgFZPpMm9+k/kwm7wyJ6QnjEpO35uP7msrJRplwWIMhU5Tb+m/Bd1NLoY3QgMDFQgAs\nqgSnFP5UH5RhUH6ubWZyn0Q2cFWhMvHUesFBURUaEC/N7p94LjYb518RYeuxvM9MvqDeEis2k8N2\nwLw9brkJAcp30oY2iTVOzmmHb25e2xafvndX3HHjKg4qQRlC3YNjU/HkS8fjh0/s5mSgsRiZYqc2\nNppOEK3d9ikV2ybbnE2Av57DCnlwld33pvJqTww5ZmVkec7JY0mU7y/+x8IUMkuNVflNCvV+A+L9\nIgkP+tFKQzWHU+Os9jNPJsumuVtAs6+qcdXVFpogv6QZuOfd4tcmaYRLDoGrV+AEeRQuE7FSY2NX\n9FcpcrXF0Din4eAvs4WpXtPcMBuCXPp1xiTimaYKIqVLkmXqOjndEWeH6z4kZQbMbqe0fclpbYXA\nVd6Cw4n79SiJ9Vy89qn6q98uZD/33Tnx5cGci8uvkpxbRxX70VyrsqeBxVs4cj9yvJOd5BA3MOju\nPBzXsQN9aW2U5Qy1ysA9l2zUCHikoWyuLSameuL1k/1xmhNtdNLbwg7/CZj5BEsmuoJyh3thsH43\nmEwK/6X9Vf0+eb8AUe8yV0k4//tOqM+/eseNJb4bRN+R8KqJcIDTnGQaDq6mPhcCwfE2NxGhGfCd\nfS4aDidJwksmnmdwYafczmSrjTgCmlKX2TQ5mQ/ngOud8HtHTBZE7ne8mC+xcdOAwOWHQOLpAnfJ\nBs0LL+c2r6B0HbEXXTQjKl4XnCyD8tCK6gut1POMcWJdkp5iuUp68417DuSVruu46yW1dLmHAFaK\n7WgrvHDbqk5OBtrJ0vlmNJrSJEvnoyyd//RgfPeJQ/HafrytYLo0hR/NuTzFjY1ElFnaIO3W6Xe+\nrfWbqluLHhduF+6qZHk9T/Q5aS7gwdUW/Z9q+uZfHlUs4PC7i8+SVCNpAcfByayW+X08enoo+jDN\n4tC5ODXKBkdVzfS+Kc27rLyaDC8e26t7r/7Vx2+TN8JlgcDVK3AmohYil5CVIx3h3BSUjsOZFTVh\nmzNd35k3i8DjTFU7TnckOttcoCNnm4g9/HDmDXkYgDnVQAFWQmetsS4QOQP7ZARho23gNK4/5jhb\ndwrhe4ql9DZnxhyN5LGQxZl9JZhIzPzBLNN3aStWR2jLxnCJJMzbWO6ZYiPRNG6mtLmtKcDLfD2O\nVODLcGTAjXBBEEioqYURnwGj+JpzLuf+qZXnnXQB1IV4hfTFN6wMHnrwKFLMSHKJjfxSRqEOC7ug\n5vz/7L13lGbHddh5O3dP92TEQRgkIhAgwACC2SRFUSRFSpYoHdmybGsddGx5ZdnHxzry7vH6HO/x\nsb1/rHe9K6fd9UoOK9qyJEomKTGKOZoEAQYQGAAkwiAMJvVMz3QO+/vd+97X3wwnYnow6avu7716\nFW7dunWr6tat1Avco8AlRQGrhwM561bnh2PdlKOLA3HbxdTj5UkbOPDP0T72W25OYkbO5TBDtKk3\nX7Mu3vbaG+KNr7iKpUil6NjNtb4PPLI7PvbpR+O5PZxFiSJgacClScAHrsKrNTzrqgoAjxhSR+ia\n+nTkdV4Z8CXf2ewnXvYdYEofo761o7F0o+/A+lxTu2FoId56751Jv51PuUufzVAOjqFiqiCy/yg4\nq1mW/pr2XV+957mjwIUrcMJErrvJqUErnT0tFa9dd+aVjDldBxMPKPT0jcCHs0VpOb1jqkMurSej\nIPxW0MAtWYnR1uXd5Wo4nfYAQh5wvsrRHSgXm8Wqa45zkw60zQPyofcQh7kPOsx0BA25pBMqzGYj\nCqN1I0Jz1xB5tIVHJtkgzg/M0EgiqBIgN5Qo85tGNpgK8paXLq0Ai7VnTo0CSUiIbXPtlLgFY7k4\nYJKmKYkeH5TaBLWi2dBTHqlxIJ51yjKpcmka8+OD6fn0KHBpUcAqp2DXGr/5WR3LUGdS2LQdpBZl\nnTSIG5BUltCDsbxolM1HV26ai/e87Y54ze1XxVWbRpz0QaO5GF/73r745Ncejx3P7o/ZBfs7+jG1\npXlYO7MUtK/q7hTVsv7zrjpfGKziUt/nw1PlkEohSJK/WrsP9njY+ucxb9Bx8/AKRz8djuGxqXjv\nW++Me+/YFnv3zMfXv/kDyKMCyfjSBBrkrEybO3Pd/lq3XvvVUuJcvrtqy7lE48WlLcPBbTCetTzZ\nrxZ845ydJTznWkGP6akpcQVIxpW410mAMqXxFCXL6uyw9jw6A1uNTumOaSBcr0P9vgQMTRaZXWGa\nJ0fvSTCmNfjLaygZQadWkskP366voRWkCJgwyrBqLmkCs1FlfRHufQibaRBW8xDhnBJxHagTwgj3\nwqHBsUR65tQpIPUdWKX+suHNXICvkFn/AKtOqRtqS2fprqCZ67+IX9PpxrgkGL2bJD17jwKnSQFq\nEW1cVrSsh9QZ+yNeKUwx1V1DNtpNdrY78M72jhmJ/qVF5hXQaF41Hm+/77Z482uuiQ3j1uKVYI9q\nfOmBZ+JTX3kqHtk5yTQyMxB9TJ3TtgpbzWW2mbTDnltZA0PxIO0UcNVs+rOWtzUd63liFDilj6Za\nmaJhXafMSk0+XfX2N3/pPTE2viHWbxoK9gnF88/OxG9/6Jvc+ITAOuQeCgIhsJvFWpuuUKmQnw7Y\nm0TsX0ys850fvcc5oMCFK3DKUzBSMawVtcZ5yck5UpLFrL6+2DXtypDcAadwhFunoTBew4q410ES\njjD5qbmT8y9FQcgWgbyrwfTMtkGmw23t1By7y99GQ+rm+iM0wCnvS1bLJAWYWs85n40kTQPwXDVo\nLEeyLtDPshFQ+iDEYsuhfb57j1OmgCN8Ahe308XZ4NrxQOE8o87Rf/ZUhip+148RBT953PpTy0WK\n34V1tPBfpXPKOPUC9ihwCVCgaolipfXHPsahmkNAvmjzvC5SDZzLXPK+c6rd8MBU3MQ5mm+/9654\nw91bmDpn1gj3PZPsOn90b3z0Czs4nowFTAsO1Mdy9sjFL06Tu47edfMqQPLECetpNsaNsJlts2HV\nGDrbcX7WW2lin1H9hi0X3wjo86xBv//h/Wh3Z2nH+uPAvv1x/2Pzseupmfj2I8/Ec4eWY2bQvoJD\n3zkjVTo4O5O9Ce1eKS7MvXZeWoWebVx3+6d7z7zUFLhwBc6kVNN51qI1XKpyVSOAnwytK4/2bvNi\nwNWQOSJq4gkyw/MsSH7bdPjFaLWp2Ia7uI35ZZi5zIYT1tooeLu+po8RucdPuS1FmtgAZn2WGERR\npnEUr6zKYVI0jvOsDfTgcRoIGpJ2qjapaxIJSeHTxkfXDjQ9e+YUKZCkbHg9mT7LR1paTsW9aUn+\n7XZRULV7bH8Z6jgPCzhTOo5/z7lHgUuMAlYHBmx5ADzKjNRg4lTLsFBdeI0k1bA/9wzMcbpHxJUb\nh+O9b7sr7r39MnadMz2O/9TcUnz9oefjE196Go3mNEIXQhQbgtR1uHPbOqqxlipsOnQvga3c9fGv\nGmG1hfhnBJ5p8eP8Mc4UiqXLrhS0B9ydzrKCWk++Pn7/Y4+jxPV4JPZSkK/loUU0xLVpdZ57kO2T\nB7It0u6Vvd74Zyum0I8f+zicqeyZ848CF7jAKUEbobNTsxzNWPlqtGmNyxA68Uvtj/aO0RG2Vmi1\ncuaUhIzrUUjC4FYGqgd3EOFdXXMn6kVsSUEbWngWqFd9DjMVbkMxNMjomsrc71Q49FG41PRzJI+U\nXoZO2eAxuvZA90HcuVAN+lkuGTTLI4VTC0SgLJpHHM3ycU0ULj1zyhSQqHRJjqoawiX/l+ogodhR\nZS1I+rfU5aMTRQv833odN+0EcFzfnkePApceBaw0NeOzpIRIXcwnDdwgZ2gO0LZ5AcPN12yNt7z+\n2njDq66PjaO0pYQ8NLscX3zgyfjkV56Jh58/yLpGZ9W8V5wW0zMmvWWINnTRAT+D99yV7cCfulry\nllKVP3GotK3nLmdKN/uyk1dqwry0pijWtCV8uBdDPO1P7HBmmHkZHMXNVVhsDArWaarBzHlMaZMn\ncYzGLNreAegyxMUVCrDsFIBm9NFqf4WphjeT6bVbL20JHz+1C1jglG2bdYHZ2cpUNRas9RxmGhaF\n+XL6AZbMTpeamoJjCphtheVN5azDZ4lGmJoqLjZWRVrau4Z//ShObqqz3/4ag/VIl8av21F081tR\nTGtpmZqQHXDdUZqAbSr52X4Ybm2rldWbnc4pWCJIcqajWk5XzqxqNpmypfInIpISIcfF2wo8TpkP\nYG9PrTBY2ygKo891SCkUFeb9qQF1Cr/NSFGinsbVHPmVnzrxa17HpUEnZkm6Ce24gcv3gnmm5oPM\n1OL7yqn85F91OfqZHVyaN1b/qzxxLF2nYr/QjHW0MbQQK2x1dBWmUjzSXm6GP8pk3dGt/PQXYmEj\ncm2MVdd0q4D4r6Fpk2rfgO5g3OV28hQJ3OZLKxGSzCeP2Atx1ilwWgV5etgImv4l6062KyoqquwH\nl+djfPBwbLt8JP7U614Tf+ruK7mMhDYUxtg7Nc+u873xyS8+Hk/smmXqnB6JNZr2NtnCsmPdUyeX\nHNDj6MyaA/rcVIPAtURfxWlBtKK20TV9Lv+1ddd35fpIThTFDl+mpUJ1uSb+J3q0MQxzBIgmUvrT\nrlc6TWpN3WjbJw/EX2AmjGzTr1hfmnDi7dp+1qwuodEcBM4y1yjnsUjkMzdaoboc5Jg94yzSN9nH\nZF9Df/Led98dOx7ZGU+z7nWWiTV9SoEhfOw5IGgQPcNXNx2qxp8hwEsk+gUscFpCKTpWUSXPtmzQ\nMjBv/t1SUVXQDjUlH+K0blip0J7/pSbTCN5rXoxqWIVahSjeKbwSgkqvv3HUhNbuwzZtGyDXlSjE\n1sit1veQtiNTEeLpT+j+Fq2gju5yukQcxaHJAy9HdyoCa6cjGkN2KIqi4kQJbYatDrqJxfeZGfEs\nXEtk8TkIDUzPdZnDID4kju0Pd+lU1KbB4FxH8zbodAmnBCw7rdRAtIFIDTRPtaGORI0JESo/OTIX\nErRsGok+GhqF12XT4+1SJt9OrbgT3tJ03DEAYURF3A2iLaPgaPNsWGm1ZCAQTL6ogBn6gnyYJXLS\nGrMzCL37+HlbSl4VyjpmNc51y5B0tuMqOg3TOUoT77qvTXVSRToWYXINmvC4CMAzbunukrZVl7Lk\ncDd9aAxdS3h1MDJPp2KdgtTwvtBKmGVzGZofrxQ0vDUJa8ZztsGCEUbiB/4DnPdqx5vTZFaEtTCA\nSQ4BKUE6cBJHj/SiD0seymSKBCdM0ZriDWfJtoMcwM3dZp692MmxfJ05kgOtFT1ztilQ3Egq3eWX\n7bo+a1gG1pGGl/rZwGIS1pe7rp+IV75iW9x047bYvIHNPQQ6wF3nf/KFx7kdaDIee2Yqpr0Uo3+c\nCG5qgV+Ia23yfvA+Tkpxal705Z7caKkfdi69QxGAu3xKXowjw6YAyked2EKApv7qba6L+4TIV8I2\npj7So5tQfP6QkYMLRja0fGQsHdPWpiEkhUQ97B95W6HShiv1y7ZpmD5gniTn+Nn3ec3mUtNWkHEc\nabdcTkCb5Gaifhr8pANwVOC6uXFQOvHnrFrfynS87uWb4/1v2hbf+s4z8fHPPRqPPTvFhPsEsFyC\nYAkIN1E580cLx+xlqVRZWf5FdsuMD00btr4u6ecFLHBaitWZdZegrqvl2/hXzWyC6dswXlqrsnkg\nr+PKoZX9HMVAFVo+TDDjs3g5O24YH8HIKeZ+OtKLGSw8AABAAElEQVRhjqfwgrFBj6dg57vCTnJa\nCpXAtDPFvgT3jdm5ZkfdkFt8rPD4c3M7VoWy6p6yxQKU61KqehOW8MN9UzHOKG4+tYyGNxCCWyOo\nWumrkuO+FgbhJEVdpoWcbu3jPM7+pQVysZ907FBJm046BUJayqpcq7sBEUsRSic5vIKJoOX1nvQI\nrlZAGx9FyQXoQu5pdL2ffYB82JjabKaAox2i1iBgkHtwZujSEWSli/nOlp3mBtzqUro5wlNeWWZS\no0xT5fGj0YJ+o7GP1BTELAP5gF8bqIlzwb2yhTMvZqXo1790mGtJudYSbUDySqqapZtLHmz4WSpi\nRwdvTsSemEWo49QV3CgL4SnoS0vsarr74HNXVHme6mDydnWQyYYSkH83k+U1gJQRHBHjXLgwzzSh\nwlcfdSeLP/mVegTPT8NDeayJbEw5Wn45rZZlKAQ4gjCjK3vAiTJLPluj0gHfpEM+6bygy8jKgRQ4\n5evC9dTSst4N0OHZfigUezKGG7GKC6UQJl/yXMuZ5dx7ngUKFFMeAbgphSPc1uKjjw5hsY91hApE\n8CesjFmJd7z5LlUR6eYU+Qv7D8YXv/pQTNOt3HD1aFx/7dUpOJWw6YCljFx/MqOCQpMKCMI7eHaP\ngoqSgewfqDmOyPkWmtXZGPWVLrQSfpWrbcbJTMVStBKe7W7D3bxtIVp3xS7umEv49s+mUAocB7S2\n6azU5EKWO7Ztpq9diXEE63e94SrywNmiCJn2ANWXOiNpqkI+sbHtGKQ/3jY2EFtHluMd7Ph/w93X\nxTd37I6PfPbReJT75Wc4u1TBs6mIJwZ4Kr6ihsmXxMAivlJSHqierOhuuJ4pCjQS0KVNDplGAWqY\nDvLKieHYtJFDeengvPLMKeLs+AiTRyXB/3bAQ3TmCklq63Jkac3CVk0HVZmOmw82uw/GFeuEgI7f\nTlzm1IO4rjWZP7w+9u3ge5H1OwpAdOpWDG0kxD8aIK7p3Do2Q0OFoDdYGqAUAu28DZ/CYQmoBd/I\nZ2iy06fDz00/4I2AIOpDpDkODp5ytOJ8CA2uf5pqIGy+XCC/EldvWYzLuI8+7yNuqqF5yvxDb9Z/\nI5QeQDYHno0Rf8vZ2hikoEr9FWi4dRQ/pvW92iyDqA4wXfDsXxyPqSdnYm6G0axOWSJJPsKQCGWo\nvDXCWaA3X0NZUHYptIox4dUmJ1qEviCNPQq59uXoX02dPKk2d5zyG5Sv8qwR3vILuZWvbd4nhmbj\nhitn0ZgciCVPIsC/dsDaONt8Qn/i22nakYwMIcQmDLywqQU1lOk7YHDN1SLpL3Mdqo3/yiB8n/EN\nxTf4rayg2QGfpV2LMcW5rl6LageZXWDeeERY8OtnIDfKPdI3X0Nw0jVPdl7m9kyNlJBWCt3iPsCp\nFGPSysFfK5Sbn8Tq5CkODy/H+DrusJ9D+KDuK2SvCpfG91eUwtIzFwkFGKZRzM78YKPMD87Mxcgo\ngzoGeshPlLitEW3hpnXxM++6DwcvFmlYwwFf0kGNfytyngJh5C350v4kbU2dyMpFPbItZVB4NNfa\nDLRKmuLIdEgO1+dExtpZ4qahiO3Aib6gNuPySfsQK6oXgIlQafji91UsatMt3omIeeBWJabK/vLP\nvAV7a6q/Fb4ti1k6mak74YVmXAeL3IgHOd94x+Xxshs2xee/sy8++LEdsZ8d7m3+Twaz5392KNAT\nOKVr1js7z6WYGJmh40EHhtsAgpwHniuU+MjpQiqAG4+yAtI52ZhUnfCJqw1HSkRUQCueoyoqVVbB\nBCQwv2yk+mJ6cWsMLUzE0iI3KiAVKcMJp37aqbY4jjElumlskv6cQ8i4ZUENqFqnHE+5ODrTpUJl\nGhntzB42KOQtBWfRcce6eUEjpoZXgUBtVi41yDTxy3E6ggl+g+C7nt2FCtoK4S4yEF7mKxvGpjmi\n8RGGdKz2shrR1XwYB/rhvExjbbMnHMPX9C1uAxvQcnLN28oGYFRTZxhDmp5/CmFD6Bw2jBwkG/wc\nKJBg7Y5v0szwF9iDTDrCN79OUZMxrNAAgvWjPXBAlBSRuJZTDlIMJF+5FW4+No9M0b67aWEmw6t3\nr0GMcAlrNKPLt9SRPKuzQKS/qWjaDsmSDo+DQeOQOh9wMW4OsrClNgOhc4QNEtPoVy1X60oNAkxb\ngZmBFvym0LlpdC+stZf6SF0TF+GvgZFDvEpQwROJlhqlsN0F/5QTAtcBhPERNDvz1A2F2G44ibHA\ncOyZi4oCA7SLA/Cry5yWGQR//qs74ife+graGDmeARJlntwPr1v6+cvqYpunj/xma3Q6puLJv9lm\nCkVLVcNMo6C2T99HmuLEcj3a78iQ7ZehSJeItpmZDwTetpX1u1oL/ewLNBk4w4qfEIyvUkHK+DOM\nbipk078DpdIQykkNEU0/GymTxOq3NH36hcPx0KNPs05WhY9L3HrmXFKgJ3B2qA/z54jRTnWWjljG\nlTxUsuxtcacjcUrATwdpbQUrzSWO1pysWNiVkDSORvm0emU1wJ7CWn6hVaUhGsh1JjQ6NFi2G4IB\nAD+rjBHaw32nwWimcXPCRo2UxjRMr0kj3c7wYSYxKURkZtVU4UAyuaM/7701XdOssPWGZgqjCCZq\nLQdY9J2HEOeUrgAM66+hHlanonLdDu8UTHgWRJ/CL6Ep9b4SRwlBUD7AU83z4DJXM9LwK4Smad98\n2ih6CP0gZTLCdWl9y9DQPDV4NFmteBfaUxKldkTaSWtpwiv5QUGu8u9T5ywvBVTiNPd68JYelFcb\nP2kOrIpgpPzlJ37J/wq3pkUs10clfGAIP/1xScExI3lunrzM2mO++/Nc3Glib2WNZ5V5HWkCj4W3\nggmXYmaxmmcfDC9PkZ2pYgWE0JY7MtCLfZCEGhfXjiF5A8VBFHUdbatGPOX9Hz7VIr1/+MG1hCGv\nAy8HMZUFITVh2/cPR+25XLgUcLnhEm3h0sBoHGAXzL//o53xgQ8/BefAO0O0R7AELU9e6yun20b5\nt8wSlgFHN4ST7wyVwpuDlapoxyRKxVDw40eIrD1pYV82oByUybeLKi+yDTDNtrWtOls1rgRDn85c\nnChNgvAvfmpimS2icrjUaYkZAdeHuwq7n6VnA0yV91G3bVuWUQC4PKYfQbzUCba/DiA5xznXggsU\nH2ZEHOC6IVW8NLVu3zZFfJlxOAE9iES+l0htMv7+33k/s4DrY2FhJb796PPxyS8/Ed/YsQsIG+he\n6B+EdyJYpNYzZ5cCPYFT+sKEyh9qx+yovT4s2d9eR7+GUatCUDkIm30iYbGWaTrJ2sSDk23I0SZ7\n4mp0skfLdTY0PgT22sflrIiknI0JqYmTMMBBGSunUDyQnh18eTZmNigklG8DHitR3V+MMWV/dvBa\nWQLgN7jZ8NTUtzbzw68JbdDEI+lYX/lUACdDq41KZkuIwCS2U6pJQ9PIFHkbpvKUUzc2pgqzKczj\nbmSCJl6ES9g22E2c8jeQk73C4WcPgQCfSbXJNG88LkyT+PtwU5CNNPn0k6xKm6QPvFcbhvTXnQCS\nAiOvF83VlJdLxuej4naCElg6ZgzexuM7ywt48reCpUKnYDDJIcwUGCQFU3mbgQUKoIwnNrn+zJQo\nFPE3rCVmWrmGN8PKacV7LQ8J/0xMsZv4g4VCp3lJPjFtf3zyOpkxWNUD37YP1odyTfolEGkiMNPo\nmZeMAtmwVyFWmRT/r1X6yZEIPFW2Qwg342zqUbThz7YK7b2DXfcC4ISdI34Y1IjLktNo8Fm2e75F\nqlA9Lnp6u0PdVnkYi+kussTJjUTz1LP2Ck32gbOemHS8ichNN1n/HVyhNOkkU/ye9eC4KVbgXE6G\ntZaQiaa9IvWTmYrcPY6gyfY+qv541nPXY7ummwU25K82KLqVyJzbBKtjsCPN6XAJk/knN8SrqXdx\nI5DldyKD/xKCq7TeO78S+5+YjE/+yfdixxP7Y3KGtPo2AoIj+oRmlVwj4zKj3JwFPN85ME3KrmEi\na4Tr+QSmJ3C2pUGlyBGQ6yGR7lLQUXuXAgzdHxybnSfCTjZcP1QPsgbJftVRWYk0Lf9ZcQpIl6P7\nc10sTTXOqT3Hgo4WjWZlzTYqcXBdmJVUDUwJT478bG5szDSVnsJf4pBuPloEtItja07inq0Q4VO4\nM86qkJaVN8EoINDImKbhpZVg/eFWI9XyquBNms0rm9gML171yzJohBqbt04roTdpZBPbIJA7D02H\nRt3ce9yG9yDVDn/okrRwylS8FF50slGstFaXA+DxYulk1GPS1TRa02Q4P1+s+/FhuF5Lk+Ug/2oy\n79KL9CybzDyEk2eyrIDHuwZTeBresszIPomjm3Ug82dHJeE1+mmnNJKnmwIxEehbfhmw0ugIoO4m\nBSYdoIssvCa1cxpDdkXAzTTEC8wA60DLwY2hW7/CR/ji2RrjtqZ1P5abYXQnTKJtWGim0Gmqfrrs\nAH7JvPF5akbOtC7QYYKv5ujUy/XUoPVCrQ0FWqFAaEtoFQe5zWZNjfWDtl3NXy1XsdxJg8KuuudA\ng2+FTtzsBvqor5624UCrlmk5yMIDt7wOuJtxjoksfRWw8sxJ1vcj0iLEqi0EjnGz/XSzHktEcM+r\nhYFte2z7mrMNAsjAthHid8yE0rHby/pR656tPvK7eafeNPmzaR3OZVSEw9aHcsRNtapfbRqWF6gf\n6CPFIlHgq/BRoaGxHkk52uz8OjFuhlduXx5YH//pQ4/Frud2xxw3NC2wGXUlz/AkhEjbTmVblmDX\n5OEO+mw1yL8nC9Qoek1AX7RAegJnU7Qt0xfXSxbZX3ZSWKF6wLTVufruRGosRzrUouhmKiC9YHgr\ndcIzCt/ZKGi3ImTT5AfJGpZKKBMTzgbJYyPqlh/xIX2+FZasqolfhseaOFd8v8r43ZoWT79P5k5Y\ncKhQPBMv303MBGUDdrQRR4ytSSe/Opj/7lSbcAknH/ianu58Z3rajdf6Y/XLAmgbjwyHZs7vLBjC\n07i0gkyK7dIav9yZLsbSzm9vUcJktCNy0p1edw5Px/10worFycIfH4/axKO/Qhm/BNXA45UNbid/\nLRw9zH8jZGXcEsyLfyt+cmYKYwTNupBReEjn6nCKB/VvylRrlktaeDSbC1I4NX3rkJuFFOr89CHe\nLmTRVzzEz4Fe4djyz2paFTIj5KPwre+uPHYCtG46CBtsdQKnrNtZB4UhH5k3/axr3XBxO66RUv7U\neQml4plEpiw9OjQ5LpCexxpTwN3jGgXP/rUWNoFb4/FqX6quZHKwLmKcg1vSlZmW5W14KZdlwd+2\nRckSwmiYpFjN8IVzQTryqc8gEQ01Q4TBIYZinEhx82VjccXGwZjYMBr7DyzHc3umY/IQx5JxftIg\nx9I50M4j5Yotq3rB+61wfGQqR34lelQLo5pfB1ReVezZoB5qn3e7943ngpzRdXPxmls3x6Gp6fju\nEy4xGeaUCbaREpG75ygDFCbE9SzRqtPWscpx9bG2YZKK1E6h7onT0pAHw/fHjicO0oqMQT1nVBTm\n7SdJB01NrtfHpyiXSZzRIwcyDX4DCLa1gash7hlBvrgj9wROyrfYxJokOWTKMinY+EUFr1GgPlYE\n/emoO3a/daywflWnJeTGnXem08Rd7Tjd/a1Y5GhJLaJpKWo6xqORoMLIzI5jje+OSMPwz7fArEzA\nYNNDfiZ8nM/UpHBgXs2ljUM1BJUqKYsAOGeaTVpV/1qBB/9sVEp4aHE1qHlbpbJwWqRJz3Qbk3Fa\nr9bxiLdrj5rwmZaeNPL5J85CFoC4m5dyK0FLO346Y7twTdGs6NvyGG5mLH/mTBqZ0SrHKjv9W2PH\nIxX4EabKu/lOGjkR1sTPDtR4q1SreqI/Zd0xNW2oUKnQWMI+5ZAsA7S2nEkveRx4xfv6VQlWow7M\nFlWTEP5q0p3UTtdSIAGUOEMX675wpVGnHZBehkyPkyRhWH5JP15Zb3hnQmRaDVCCqpRPAqznvUYU\nWM7ZDEgP3+YYeI3gCsYmsAYWDnDtO+QBTcMzljmtuO7Zf6S/PGdtq7oqz9cxZMTNNtX4JzIImeQl\nT4IYRIPIevQfee22+Ik33czB8iy3GmL4Cah9k4fit//ou/HQ45M5he9MmFi0bWFyNHXQd9XfE6UJ\nyniLsTnM3DBgdPr+1hu3xPU3bIyvfuPZ2DfNSQ3jA/HeH7snntu5J3Y89XBsvWwk3nzPy+ILD/wg\nntk/U3Wb/m6AtZ2u/bRByJ40aYId0lX7RIpZeRpaHge9wos82GYwuDhyxsV2T2zF2nJY+7qXvSPa\nzbzONKl0HER7zkkBS/ySMx2261hkRRsHBD6ZMvtnPe0d7Sxgaz/5ZeVspjBXGxj9W9aXnMKSyRtj\nvIRVQKqCWxEclzlVrm8mytNKU3GdAllKAbOm3l0EXpVHLJ1a11iRTF9c02FtHq1AkPkAn8yeKdrE\nKgQo2LX51L3BIWmVgYsmnXiNGyErdDW4RyBto5EJtVloaGjUxlSeiQWN3JgktNU4q2nUYfxGksUL\nQPpmGg0U8pjlYrCLxXRIIO2acsm8abfc5DMC+aaTzE64LWudLe8U4gtQNf7SSZoLSH/9qmyOYPsK\ngJ9pCMs48gjNcuNXGsMSavPg9+ZIGXHrQxtSHbOxTYOyzTT9bs3aNVkF2zQqEfMqNxWNcPvhxFsk\njvGWLtLEeLwTZktrgzf2rK/HiN5zOmMKdFiltVCUE+PjMb+0GKNOr2a7Wnzb8uiZJlqtmG0Rickv\nsq08QHkXGrrJDzWbUjxVPNYqMVo+9JxZwVS842NWB8K7SWiB8yzH4q/+zF2xb99sfOCjD8bzk0tx\n8/Xj8f4fuTV+7qfuid/4ra/Ern0cpwffeVKHh6unQgN8VP56OJo4O+1d52UqQoE7O7rtb9zkI+55\ntBownFnz2khxHyBft90wHn/qLdvisR1PxIFDC3F4cjQ+8vsPxuGDh4k2HddeOxrve9eWeGznt+L5\n/Vw+4XZc2m0HnK79zGVNnnUt7NxoZNqiBL2SFrYfJzAQa2hBhQ0bpVyqwF/Ft3cCd/6zz6Q+ts3c\nCaCdspebnRzMLC2yand8Ijc/Sbkjy47EzUT+Thn0RR1w7Vrv85VMMFpWaDnB8s8pFi0YRmhWHDtD\n2aICpKUex6v9ustaRw+ZW27jXR2sDi3DNWkaz0qs0Jq4teKSYW2UbBg9vgH3rNza8GumP9NuzVEz\n43l/+jG1kUYQa2YafBOmnSUm7eanbQR0aPNngNbonoF5EzY7X8IZNN2tmKUb0KXc04Z3C7v95p2g\neAhCuw8bEOiQQq+LgwhU4+9q6G2CMlhGaP2MbFjppfAFqArE94VtJK25S5P82U1Hff3ZUfhuTVOu\nfGb8BCBRHAzJh4ZLH94d6NjlDcPxSncs/JfGAmtXPI8dSponO8nzhjWiHRf1Ds18H52bHYY8vtzw\nvtCy46OsqKbcmQwczxKthHifmRGDMvJCAxdbCdhNXrOHWg3ZRDjGq8LnNa9JFL/R4rDBL4XYgbqm\nsIaVDexjQOk5nQkFsiVM6tsC5MYTwA1zb/nCIryGcGCbXALTKt+fWYrGbvgjX0fzSjI9YbrTa8P4\nhhd4ZXXVfiqGYAqOC+xCHxyejx990w3syl6O//d3vx7f/ME0CorBePSJPVyUEfG+d7887rhpa+zd\n/2T8yn/3+niGDTX/5TM7qHr98affenNsv2Fr/Ic/+DI1bCje/5474s5rL4vxUaamn56KD3784djx\n3GzcddN4/PR77opnv/903Hrr9uiHng88+Fx86FOPxBvfdGP82Buv4Zzp0fjln39j/ODZQ/HhT34n\n3vDqG2Pv7v0x9sKh+Lkfvy3WU9//8k/dF/sPD8Sj39oZt9+5Lf7NB78RT++iRnBO7y/8xF2xaWQ4\nfvODO+LgfAm1A8RZbi+taEl2HPosexa05Mt+gFeSNh1wrJnBplE6DoQu5yyG7gRx6P4kqEHsvRS4\n+xZnYtO6MQTOcrN9bQfZgRbXNveHAOByqZqLXuCEFanQsgcCRo5KZMjqTIuPHJ1S/Dm11t0wHINN\nOoynxUj+6uUIrfOdHXbb2BA2a0AnckbJXpSGqDpVG0u6XEefKXCVJjPXbtK4eHvEII2EJwulNoba\n5RmKJZwSJzviBpcWp0plTZ5HYd4Fs02zy0nrMSKUZrYJRwtb06ZtvCaCBdEI2R0gOFmG3UZamfd2\n2lJvp27dAWrDoyzfilvZEBFawTR3twsvCxwYlEtBbgcc3alcePZjkL3JROWyPhr7MQKXE0/KpyOY\nNlOS3bxdrTvQoKNxqjyk8arJ+uDAynqV69esD9aRbp4Hl6yb7GW1Tlr2OiUYhV7gA6NKCVc9G/RX\nU1orWzfg49lPnJY5dKBTsw/CkE/F3zWsWFOY5t0zZ4kCxTltrTaRTRs3xAHuK3eDjUOfHEitYeqd\nepJJt+lnQjzkAd34lVfr0fXdzWtdzoQ8tjFFBmhsgJoY7I8rN4/F089Pxg9emI2F/lGq5lJM04/c\n/4N98eMLi3H91g3sm1/icgc0i4eZcodJ3eBz+Zah2H7luljHYO/GG7bH9ddtifsf3o0mcybe/qab\nWKt5W/xvv/nV2LxuIF5xw0TcfvWt8d3HdnN72Ei89+03cHvP3ti390BMHtwaW9evi10HDsXjz03l\ncUxXXM1AC7hTO2Zi755DcePm0Xhqz2Q8u2coDk4fihuuG4tX3bE5nt31AmfsLsVbXnVl7NixJ+Y9\nw1YBjTq/ZMNNm9Ch77GJ0SFYbdTrCp30tk05XsQTubeRrNHHN2qavSVw/UauJ82OB5xFOxOt55Gl\ne3xYl4rPRS9w2kl597NnXC4hifiVnVirFYO37DCLxU4seMg8GSJ7DzsTfxg6zeK0CpFuNWwtd7kw\nw1YqNRVt3WcHIfjlDnXC5FESxPP8ysQK+yCNQ+2WJa0UqJq0MhGFVHC3XqXA3DJ7ep43jyMFxpYG\n9U4kO7Qii+S969OMJc1boab80R51SO0kird9VDfjonZNkRAxl3LXTSHU22w8esoboiyP1XS6cMnY\nl+6jmsm2WYDIEjKJKU3aBtgSldAyXnJ+h2BHUhLezAJx6qzKtcqHzh/itzt0vZrPsiiIYMCHO2Dd\nze7h9XmcU7tGroNLJ8nzwCKNqIs5JWqdFmeOheGddZm8WYfzeBzqqazbM2ebAlJ5Ja688vJ47OnH\nsbl8CW25691tMC9QY65YuY7uZChvfNuAdm3X5EGupnVA7QknVYsOc1H5AkLQhg1M95Ln1OHDe/3Z\nHnJPOfXI0G7e+9b3nomHH3kwpqfnYh03j924bQJhdAO8ywwa7eUyO+H/+LM74vc/9e244rJ18U/+\n7vti+3WXx3/92LcRaIdj66bx+MOPfid2PHuYe+M5CB9+l/efeOZAfOELP4hX3bg1PkH8B7+/FFdv\nXIm37DsQ991xNYfk72SD0TVcaNIfX33gcdaeutbbIwlZLsZSm7ZlObJNeSkKrquGHjdxKA0fLXCg\n/OhITak7YM5153BbRbtw+exsUbntWc4W/PMALkVvZ8XB4EuOSKhkHkgrQ8gWTtfZjWY17Whzjo+2\nHXKxY7FU9ritW3aGTadMhcuA6VaNX/XOTbyUdmwGFHapXFTuxCRvqGCa0el+19jxp3se+QOSLtS2\nd858pKBlB2YoOmfP5zxPTWa3wS01k9CloQSuTQVvadKp7wo2hmpC8jKnanlz6tMpT+mb/9CBclWb\nLYnyXjnca6eiJLOEpZWGAMSVZk3kdO09pEDD3w3Jk76tvVOIOjS8BkFb76RfETjJmt8Jz2mtWvBQ\nbnC99Ade/xJ10ykxysaULdvaUaqmEBfCWZ55N7nazhZ+C+h8eUsbtRzq0UBSfsu732GxPDImO3fb\nnqZ9OF/wvijxsBykMwLOVZezHOORWJibiYFhBYPSoB3JtBcQEWAzV1AtclHB/NJCzMzPcc2yorT1\nB9bDU4XGOIfOD9IHTc9yODvu7UB9AQ1of/+8ojehrE8zudP9Z9/1+th+8+ZYN4yQPjYS+w5wkLv9\nzxL3s7Ek4SGm4w/FZTGMULrAZXfjQ8PwNVC4aUn+Xl5cl7xtq5p1G36fR5hd9lITimKASX7b4uem\nDsd3vj8VP/n66xBqB+PeO7bG7hfm4xE0psvspnfpg5t/nBewEfH50hv45yQJi+cS9F9amI8bb7wO\nFKUyGbWfoZ1KANAl3yeBRaBLxlz0AmdqFjiTa25mQ7zwPOr8yzYwwj2QBZzCi6ov14c1ws7J+zME\nOwMp7HS4sipHfqZgg3encxa0DNhC9m3KGiona9i8PrM/K5nh+NFhLfe5gcJQauMQpLjVwWsmFaq4\nXJwgds522gpZBGmYuzRPfJ9nphFjwKqlVSuCSA/zrbGCuuYFg3MKIPgZQmpLc+35lfktocR4rp/z\n7vA6bgPKZUCpAWz+XZbgblCFeBtKG+VsGFIzvIpdgr+kH8V1lo6FUPRuCWJ3QvnhaCmmMUC3FHhE\nBAUvG+LS0ud90RkXd/h3iDJxAFVn+FHC8rcNdzNoMPV++d0CdEBm2OSRTuqJwnnxgKfyKlz4ckUt\nWkfwVLtrJyQtHFT2zNmiQA5C4ZD8a1hk65YNMTE2hCbqMALnOAISba7KAEJdkAa02frDYylm2bCy\n79Bs3Hzt1ti2dTAO7pxCyz7GtpyFePktV8Xo4GA8+/y+PBSeXoJ7y+lrWOM5wDmZI8R3pm9wYDbe\n9frb4m33Xh0f/8pD8chjO+Mn3/Kq2LiR65YRSxfpn5YgV2oc4WOUnY4PSb6G664lzbOPm/qpGCtt\ns51AebLgCRVKGVz9qvsSfddX7n863veG6+Ldb3tFbL96A1P5O2P/dLXl/QD3SEvry2ojw/dLZY7T\ntJTmEpTgnba3WFqcjXVc33vddduy/06WStaSA7Vkr/VSYX5BpHPRC5yO7BYHV2KakdqemYHYNriO\nc8G4Szu1mQoe3p9cHcEKlfHEXG4j5c/KaifSMlT7Ll9hqKHM6WA7Y8O1nXJ+W5mySuLnDkCm3xaY\nimCkutLvjkI62dxVKdN6fASGyu4IcZnRaX8feKoZUnCyL+bPW7Gzc8/G1AjnkaFha0XFqojkC+Gv\ntI7VQShW1rEWlZ92xJiV21Yu6WFDK02kbdFQYWSFsqw1PGqGFVilmOEMZtmUXfEzD0lvhRiFgITV\nwMwYl/IDaiSz2eJjGp4tatqRYCw3vXNNA3qIDJORcJSO2JMHsWcZOUDCqvY9y8IytLSZquN2koWB\nOWKNMSDA3ykp+LtusZLHhed6Na/McwDmdLV1SffzyZBBeknr4gDtjGvslunUcyALukkdPSWc61l7\n5uxRANaQ3jkI571l00RcedlEfP/ZyRhZtwU/+Op8Y5/ToIao93PDkGd8zrCp5v7vvBCvYXr6599z\nN1Pcj7IxZy5uufFKhMbbYxdncT78+P6YhzfnuIXn1m1b4w7occ216+PVd1wTh+foOYaHWXYwGlNT\nc3H/N3ayjpI1iRNjrMXkbqSRlZjnwHa1jWoyuS+IKjkQ8/D0YlZPNmTRzw2vG4zt2/tiatYlC6yj\nd4Dp4JG6PLdc19nefu3m2LNnKp5Bc/rMngPx6NOTce+dVyE0L8VXvjMJjtxQZMGggVVOy2Yey/lY\nVCV8QtPpybj+io3wmLcrNVdOy3wizbv6uqz9p1HCF3dQ2ObiNhZ3VhLWhsz1D1NZuI98Zc7DGbJV\nyj6RaYhki1JdnpAgqTNrhEUrSFWI7mqBPUdnCDN0MCV4Np2NtQi/DM3bGxtWWDOzSMfr2psVOlYF\nq2Xucpdr212/gwhRAwvj4H0YbyrykndOkzYdeE3XCZMcUFFTG+TrvDJSVxqAtcKCQkuDfzriK+op\nemJJ+mS4zBCCqOKqHsRSaEGbadgYYJF5Bva7YCraepTUSl/d/d7HURsKM47HDZNCj4Vuw7bUaJFd\nL6Rbz0AByim7GOhJOVkMykrp7ofrKhH6ZHF7hdr8lQFKM9EOCCgHw/SvwNPZ+yuwEk6eV3DsH8kr\nAJfQwnhLx2BT92rgQLPEYCqv4zMZ47NJQkE1a1zCw+O8MdRlZyAGrbfkcXGE/IyQS46GyRGhfGee\nejx2Voss63BDY3hEcWWFm3buuev22PH9T8HHcwzqueP7rCJx9oGXbtE2b1N86YHJuOWG5+K+u66I\nX/0rr+Wg96UYHmbfOdVtEZ572Y1jcWh6ffy3r+2M97/z1vj1v0EYUHQmaIZ6fJj737/Lgen33r49\nfuUvv431mhHMqHMt5AKzD/NeEJQzeh6LNIiA6zQ8lwWR9jx96GzsYd0m1vj5990bj911KH7vI9/O\nBmNOhQ5l8Awbmqam5uPH33573HffcvzD3/h07J9ZiW8+9ETcft0m1p8uxHeeZDodgVPYNcNh3GY2\nRXJSfc6lUcCsqyutwuLmLCdLGqb3x/ZrX5EKq0KRMkmL7Zzt3zlG/FwS7ThpX/QCp/3YAJoSNZr9\ng1ZVK6qdgUIJVKHiOaWdgiEd34mYG1DEd0qWSijgVPUYh1qqZ0YuJlvxSAQawLobVm+FpxKaBCAD\nLzJanEO7s4DA452z4XoYNZdocxKU08Yg6VloAwpIy1yDqTAK7D7uXi8ZqbDKiioGiYe4nD8mMVQz\nJQnITyt0NpQCZ+lhKIV0XP2XyFjym6srfee6OOlBKTBZQ3AbKDRKNE7cZQGMOd6uKbKcLOkEyRta\nQbMM0wjped2aNFfjYVI9kxRQKC/NM28LIssDm2WU2gdprlGAtARzmKADPFlx8lB3+DvLjniIYwhk\ndnPEVeB02pkrXV0G4tV7AM7BV931XtPvBXCewYNaUNed1XYIp01N9Xwy1u156uwct6gsMQiq0xBY\nj8b0oR0zzJd5JZPUAXmux3BnpfySrDzgJ9uE4pTluOaqrbF5/XDMHNobYxs4MMjZkgvZMBOn4OPG\nu4PMHPy7D30rvnT/hriaDT0jo8PcNHQwhrl952ffdlf84k++Ni6/7Ek27XyPXeEH45orLoun9+3l\nFqIZdldvicnpkfjMV56Pmb3zsfXysdh7eDEOTB6OUeAsL26IJ59cid/63UfiB7vgW/qr6fmh+M3f\nfyBe2D1Hizsc32U95r/8wANx1bZR3GZj54HF+B2OVDoM/OXlkdh9cC5+4z8+GDdcszlm0GaqVfV4\ns927ZzjDcjm+8e3n0JLalpSx7XGTb7o0bU/rdy7fpdUUA2o7g+rF6enYyHT6bbdsZ5OU/bOKIttO\nmbD5nW8N1bkkYJP2RS9wms9+OjXFj4WV9RyMOxRjvBUwFwaZ1qND60PQG3AltoLjiZiESp6aSATT\nsRFEG6bq+xjp9KnFgMmyvUsAdrz8UUGX0MYdnlW49cgeGjrgZ+ctYjDo3CwHE9NJDaIh6VOLgzbT\nXZQKkClU0VnN0VlProzHzskNsdB3FbOMjSZFgfQohE+EvkmeC1PVrzCzPpq7foSXYQSUsREFE4XB\nomF3fqS2hwO7lmh6ziFCLnMnCwqtrvfDZtkxkp9mWYQaY9e0DvKd0zpmNgvFcxA5LmRlIp6e2oz9\n8pzuEaM6hkqLgXum6CUd5GGI4j+CvdPZrqd0PZzLQNQ6VrdtuTVkzs6CQZ1CPTEOoyXpR+jyFIZF\nAusmmR3gDaJdnka7YhkLO89Uxd2GOw11apFB3ULfunhminLv24Cz5e4Ar4KcN094bAU+nJrbTF2m\n8xmYRvlOvuDd0oy4M5rcZ70+b7C+6BCRLazu2S4kX9Xh5OOjg3Hfa+9B6Pou078MSocZ7Jx3THRq\nxWHe8mQThTI3mFJfDs2NsgN8Mb7x/b0oVeihUFoMLTGd/p3PxuvfcGMc4Ei9vWzq2fswM2nf28Us\nH4O/AQZ9T+4Dmm3laHz5u1O8p3LQpEA+sOStQMPxHNsdnn5wP3U4a1+u6fzcA/uI5X3oE3GY6vi1\nx6ajH8HTlU/LfaPx1e8dSiXJCku/aOXjgScX4ltPPpvlskxao/R3L79tGxualuK7Dz2Tk01egGLP\nsOxysRyU2la0JXpqtDlboazDy8gQvp21nF9ciIP7d8dbXnNbTqf3owRagUB94G7/5gbf4sISpM+3\n5ups0elU4F4SAqdHQCwzKpxe2RA796O6Z4HKCh3hIoKbGhSPG+pcTXYi7oCZFHkGuLli0/qh4Pgx\nqogbBOgwszpJ8uowl2A+QsYMI7o9jPLUxJXfagIDCK4sYYNBD8DQ7ApUYEKbV8xawq8NzAINwO6F\njTF3YILURhkgCo90U+AkL4AsqIb2d36ZFFzsAFJIUZBG/EdoWTfcF1s3IGTUJA/+CDZ2yqkRMh+K\noYMc+TEUew/O5H28jrIXpa0dOIOGQQYKQyyROLQylfRLETxJgI23VuEtooGaXxmL7+9fjxDjdK4b\nsQqGZdozDQXowJKDFRpbVmVw0A9vDqGJv5Kz/uT5wRRCq2E1mHR2Fs1GV/F0DoXIbm4W4bK7FDg9\nJqW0ojxh7WEa8CmOFHEjTYqd8HLtXG/4Xlh0eEvUzaf2jlN+bF6gHjioaJi9QfjcvxxADpP5eWiT\nmWMARSMDUZIq2flCHfIqd/rrmbNBAaldphm0wJVqpvppK+6849Z46JGdsfvAbo7+2ZYDWZqa5Nti\ndPlTTkzGw26rVSJPA7Req4msOrf1ZNXl7NlIy6G3K1ectfNedXnKvsYD0O2Jhqkzatuepm95+nNP\n0ffxjSZ00P4F/HPuB0WIVDK6uV4QIGFYvIXgyOBdOjCfzlGexPdLgcqlSX4zZ0Q19Mt+SJIsu3YZ\nQStnpqynyfumh0BLouJs2+2sh8sahie2xNd37IonOLuzjz4tmwfjZH6a8hPZtTQtODOR9tZhNRFL\nvQbB9kW0bwRJToBZEkcoM3N4fwyzPvW+e+9K7aZopzAqXIxc88OQy+9Sf178Aqdcwi9ZgM6QrpIF\nz1ltKHu1NZIA3+KmE/MD0bIqMnVWGxuc1hUy2ssc1ZBO1mDOeszel9rKSJBVMYShQbCzJP00cKR7\nIoaoVApfHlpeOiO5lylIKmfuyyDwkNwLngtUTEeaeYA0HXCytumbnfPYlDBOtU0hRvEcA/7ulDSv\neYOM0+bZzJEfwumutpLMJu0WFRKJk2SmQWvbImnYhyDZz0awAWp+0VBBqKFLCkaeZ+oUlPspx6C7\nBC1Yks6vnikK5PEp0MphgJ1Xsha8aIeRFxE0wqZUc4bYtVaUBl+UJeVi46t+0/XHcwMclQJfa6/y\nysKiHCmfAejfz5rkLONGCEODmvuEDAZohxsDlNl8PwMt4+hIHT7fjDSbbfrIQbS0dqDiWjv0bXus\nq8VnZqFnzg4Fqh2stqHo7MIaeBhhYQTN332vell85I8/H8tjm6N/BN4EjeRwhQnbHAsNPuu0V34f\n0+je+tnWvHTGVBtRMROtKVxdrSvkwUxZj7Jbc1aNT6pm1jAFPjqVTj+DkFn10rpFiFxypqBl+5sg\nTaxJT7j8eORpE0Rs+Tt3/adfM1g1lDDEhfZC46a/xsLMxnL837/7XTbKLsbsPMtOTLvpf3NpGWkk\nOAGUpeKe8VNgiVQXpKO/bb3gh+xoLFtpRF4SJ1pBrus8fOj5eOurXxYTw/AXywSknfsMEjZJSN81\nRbsL2wvdKpUuCSMDFBsnR/jBTxeZ43TYoyC1RFuN2boXtGLRpsNu4a8GbtJvcVjFrTvIqmsFb58J\nLnE/PcxbnM/Vu5M3G5f8HY1JJ0R5GKwTRFuVlbbuX9skdsJ2LE3ArvDGbJNuYeDdM8eiwBEEKqIq\nWvrrmJbWzbuilH+uBU2Hcm2CZOHVOtGqH3Jx+lWwDmgtFafRtnYAHBHkPPgAsYani0O7m9U2U+37\nPED3okdhtV305IsVZpLUct5w/bVxy83bY/+ep2ORY5JUyee64RzLIGgxpPeEEO8Ur2HUMQiVPNjF\n/8cIcradipN4djVkWuU9f/6X8F01q3224RtWLTQJa/jWL+08jv1n0OavE+/I3LbOCZSEVtMqH59q\nOacOzXGGKDQfUBBtYrXh/c6IR8I+4y+la5PSZJKmQ13t+jn7xj58+IKBB4Kmg2U5Q8l8ZWk29j//\nZLzsqi3xulfeBV8x68NaWXHNQUoLu1LoPY9Bge6W8RjePaceBXoU6FGgR4EeBS5QCiAUuMFTAWKM\nI4De+qb74rpt62Pm4AtMMSlgahQgXeqjsEl4BE5lkxMbtV8nDXRiEJeor7KkmwCR1FhKBq0V2FTF\nvtTGJBF+c0MquIAJDu3Aw/J1CQ94gqtL3qanXojrLx+PH/+RN7EJjaVFuC+xtuCc4f9S02sN0usJ\nnGtAxB6IHgV6FOhRoEeB84cCigsahQFPs1BXFaxN3Iig8LY3vyqW5/aza30PM+huWFT0ydCEqinV\n1iWdj34YOCOcAyHpaFwuwG9lyxI0z5H40QqaHSGzLVA8OiONcqsNvnMxO7M35qaej/f86Fti2xZu\nrGIfh0ZB2WPdeubUKHCOSvzUkOuF6lGgR4EeBXoU6FHgtCjQERoUBJxOVz7krAs0nYNsgLnh6i3x\n3ne+KRZmXoiZqeeQMdw4Skg2rrl62Q1uuZITYUKB4rjat1ZOOS3keoGlQG4OVfLEKLDlwCC/zuYj\nJU0SaAouk3cy3DIuodFd/8u55pp1mbkpay6m9z8T8wefiXe+43Vx9ZXric5GKdZ4JhR4ZYAd/2o7\nj8snZzNLFxjs828F/gVGwB66PQr0KNCjQI8C5wMFSoApgUIZsgQLBYrc2IJQoSyqWHnXrduZKu2L\nj3/6azE1OR9j6y6PodGNHJ3nhk3CqIphulUImlaYeGkEo0rzYn0eTcOjvzPfRxblGpFCoG2JFkhL\n2M2qKsIdnDi97oZdz7720vjDaDUHlg/EW99yb9z98psJy4Ysw7skQP4gXo83ipan8uwJnKdCpV6Y\nHgV6FOhRoEeB85oCig4dgUJBoBE4y13US4BU+FRDdddt2+OyKy6Pj33si7GTzSAbL7s+Bke5bz2l\njxJMFEAyZkkkae89XgoKFN0txbNp1Gy6ocwNQJ6Vm2tLSXph5mDM7HsmtkwMxLvf+Q40m5u5ix5h\nlKl0T7Rx13+PJU6/ZHpT6qdPs16MHgV6FOhRoEeBC4ICiptOkddP+bEOS3Jd51JctXkifvq9b4vb\ntm+K/c89FIf2PBHLswc5zccj79R8EZs4arFaTdYFke0LGcmzJGMKtht0cQYuCJucoQ+HUMbzh2Nm\ncie/J+Om69bHn3n/O+K6qzZxWD3+nGw/yPF9hoUZLmQKnzPcexrOl5D0x2JR3ZzyqarQFeIIa9dH\nEzJ5vsW921uPUzRtNNpUa1uZjiOfjXu3UxPq3L6OwqsoWFQ8VjZEdtV3ldoZtoG1phnqwu94+Bwz\nvfOO0MfE8rQc2ywZSbvUb93adzq0hOqCXuHbmBXfYJ142I8RrQvCmVnbdDKN9sP0uxLNutMm0+Xe\nOvXe54YCpd1s0270KpRPq/WUiVa4G3yQs4A3jw/E+37sDXHTjU/H1+9/JPbteTZGN12FtpPNIQoa\nyWV54BCRiNhTbbWEXdt31rG2EvnuqnRrmJItkMKlQw8WkGLnfM0Frt2cPhhz3I++beu6eMNb3hA3\nb78ixofrbFFkUqbZEZecaj+iw1xDxC4BUD2B86wUctPAydP8quq4DJ2xdY6a8cdRvnX6xsOuPY3M\ng97tzKwOrG/HVKBlj+qwcmT4qi6q9IVo25fnr+GXh2Yb7RSM4IGY6QCiRu8A66cCcglFIuIh9obr\n7mD5/CFj/JfSiN4SDf8yQ021DkMs7s51V7izJyDt7bSYB4dLSw/Mz4PKCbMI4V03NECHYxlIb//X\nxACv6Fl0S/g4WO79HrGBsc8qm2mX6U5e/5bm3e5N0AvqJY+ZB/lUjsv1T3x4rrLl4cJ8NQY2+xku\nCcKDjyUCLTSL8429ZFjPVcSe9IFQ0rG9Xz2jCuSUTEIhJBEAZjTrp8ZbUuSJOsxZd7eSFM7t2drJ\nS4CwHlpvjdmWmTB65lxQoLvwkxsaJFbdi3v45n+QwlvJW7QiJtYNxWvveVm84uW3x3ceejS+fP/3\nYvfzO2N4ZCxGx9YznToeg0Ne4mGXySUeFji8aP3umbWkwGpZJdQ1rFR5SmnWbY+/oi1amI2F+UMx\nP8tv7hA8MBJveuOdcc/Lb4kN3JMenMe5wsUUtk058qAdsPHODU9rmeVLCFZP4DxLhX1EOySfwrMK\nRwodNlIpUDbv7N6oAEhRCHs0iSxItgu06mUX2HwP0tt5kaZrToIbDux87ckBm51kRqjH8XNFWE3i\nYwJ+A6CEMUb8aa9mWRyb4J13p4E1bhsf60tqEDzM+jL0UuDglsrMwyqu4F8SDlkrJNOPvHnjoBsC\npLn5OxtZ6IapXZP4iIQ/HNswzWe6Z1geul00hszIMxaHg4Q+iJ9XnFJ2lBztON9ewpzCuJlPUS/L\nJwsXaXRgkbIaoj7I65S9b4XCpoiTli29WtolLVvH03gnXxgZfPJ8PuwO7Ox+NB4iLvNZnhmsnHvP\n84ICq6W+ajsxYslTyUs2Ctz0PbiE4Hlj3H7bdfH4Uy/Ezp3PxbPP7o4XXngWlhjjoquRPKx8cFjh\ns1ppYfTM+U8B70NfYQ3m0tIcJ2RxlTQH/Y8zV379tZvizpffE9ddc2WsGxlkAGwrYqdim9PWfPNn\nO2X73CtvqfFiTE/gfDFUO4U4Coop1DVvBaO8BhAPBce+FBi5xYBrvvymH8WDN40X7V8KVLK2zt4Z\nPuBduN5/u7DAndZqIhll00CqAVqm0/ZadetJpmNaJzCGMY1OtbHBbKQChd08I832NysXXo23yLTw\nE98TpHE2vez0+xBYRqHbAGpdrvxlJyE04A43NZnSzMObNeZBza/vfsIMc78w13dzbTCXMUo7rznl\nWIu1MqZaKQuxsdlOgZcC7gCILFFuObAwhLjx5pW09X3RmMxv5WsZoXIIOqNQouwoM+7ekxappc8M\na6NeQIC8Hpby44ty5RLSvM+VzgIa9jHHOZ/zW0UlaedPIy1Px8jLgO+UgaWQd0HDXwNeVweui7CG\nGv9l6ocDvMLNcjRipX06Mwung18v7EtDgRzA2KbAfB5xs7i4GOvHhuNVt22Pu2/ZzlTrfDy3a088\n+dQzMT07F1PTMzE5uYtrGRFcaAjr1qyXBtdeKi+WAisxMjIc42PrYv2GdWwCui6uuGxLXLZlQ2ze\niPba++K9ZpefmlDbgvq92PR68Y5FgZ7AeSyqnLEbPRH8mh1g2wkqZNLbLhyaiRkasGE8B+xI6a0U\nlvrpzLy7Wi0QYhSRWxiOsuiYZxZievdBOsAlxl5UDqSXYSoO12fQGdptnl71aDtb4ynWpsaGNBdn\n5zkehHvLsZfW0xBljOPPvrbNVvm8tM/5+fk48Pze6D+8GGPLCi4edCJGbAOAVnU1XeEkZWp6Hcy5\nSm3v488SZjFmoOPyusHYcOWWGB6HjmtlREMCtUZB3rIGv8XDMylwjawfD7qqVR5pwjZF3sa8KN7u\nAu1T0GSN1MH9Bxk0MXBi8OTIYHkOiQ7jfdB5bI18CK2Sv3izvC417rN7pmLBDgHpcx7uH6G8hteP\nxXxKr2obu0h1NP27vE5kzSJr4g4wOlg8PBuLM4sxftlG6l/VyyXe1kWLtNX+K2xaT/y2bvTMhUsB\ndyh7JqRvbxzqW+EmIsp1EzuVN45fGbfffHW2z7aXVnIHQJZ63bV94eb7UsDcEsvRa1p48I+Kgsrr\nDVNcZenMCW1V8sClQJBzlMeewHlWCF8MnTKQnRjGTmmgfzAFy4XZwzF3eA7NnNo5xEulIhov2L06\nT77twNIQz2nEpbmFmN97AP/lWKTzHb18YywPD2FHbgHOsNPxpnUKvZ6gu8DzQYdPry2cAy9MxuYt\nHAExPkwnT0Bb3E7osikSt/HxzGz6fqnMGKPU4cv6Y2ZxEqGTA52hoQKnmqgSPrqxE6vSTA2QvxRn\n0JKtQ1Afu2xT9LNup8SeM8f+WJSSUBaLmrFFtNmH9h5E+ERIXl9lZ6qWqYGM78HDF40hT2qSa50c\n+UKzPLN/KoYXoIVlob+ZZdrcxj41C/Cb/KWRLAMEOrh3EvkUbT4S6PBG1tQpGBDeQVIrbErjNEcX\nfet+9DvDyQ8NT2PJlHn3M4BZXODAZ8pqCE3n0KYxJF9u2m7qgtg5gNDoZD565sKlQLv73GsKNQ4q\nLNTkQ8dGjgSTUz06B6+qqbgppMgJF1GdlQAXq1EbbTvDgNKydhbDkmV4wfpw19gP8qZ8Uf70zNmh\nQK+mnB26ZhPUymoytR3jIkw9PDYamy7bGv0Ii26CcC49tSW8azpP0alEOkddea8v8QfUkFobiLJx\n8+aY2LQhFukEnZ61egySQHWfJ86QeCiYZkfN27gcc5sC5xDqmr7DCzGz60D0zzGtmIgTvpIlHkIt\n4dv8iOm5MAobw+tGY4IpkZVRBLfs9ckLyA2KHHRr/7Trpp/oGtY4xhWGawHX0nRDO4I6pOMU8srs\nEkevTEbMLcYQ39K45ZPEw0j8mtdaonZOYNl4y299Q4OxiYHMxMYN2Wn3Uy4OtAabX9LgGBhm0VoP\noMjIujHOStwSg2MMEoApD3fojSW1jC09jwHrSCen84vKPluTQiwwOPyE5SsrMb1rfywdmIkh1/4S\nPusAraazERmbymG5Hg//Fm7vfX5TILWaajb5aRQyl+DdRXnMXwohWeIwM+9c24c0ChfIg+3PuK1p\n3Xx3m5O5n07YU4F9svROBYY4tXDWAr+1gNGNd4tbt9vROKu4Ubi0ExvoH6JcKTuFT2syyiAPfM+y\n7UauZ19TCvQ0nGtKzlVg2TTB/b6rZ6LCosmZV1hC4Nm87YqYfHZPLBzmLl8bORo4ww5SCUp70nSH\nwLBZW/TA2bH+GNk6ESOsO1lAilJAdSqyow01rVMwNqBl2upJh0klVMs5woh9+eBcHOzfH+NXb45B\njoVYGULjs0yPS6PbxhBZ7eeko4VeC6Q+uGEk1vVvjoPP7cvpWTXGahJTyME/hRVwzvtw+ViAUEsj\nA7GBfPVPqME102SkQ4+WLmf+FqT0SRpRRokXgwa1rMsI9YdYHrHpii0cT6BWO1tAAvO+mAyZd8Dk\nNHlOR7Mgf+wyBU7qwd6pGELjOUyW5UcFyO5ykG72DbXOlrW346MxduWmQL2ZmneiZviMRuDu6BlX\n/xMY09S0/KuYkR2PfINdYbif8hpE6Jx57kCMOL3PcSkrw1x9SDkZn6wVyosGFlgDFGvPXDgUSI1m\nF7r1TeE6rW7BZrFa2BY6AVvmwSP5oCtuz3p+UsAitGfNFpZK75+Vv2p7Fmoi3qvBZ7f8egLnWaHv\nUWwrP8PcbBXJzSNLCD6uE9vAnb6HX9gfs2hQhhldKRhZARiIpSFItnU5fTjG9PtlE/zWx5yCEvDU\n3DkdZGqOxjvtYEU/7rNBJ2GYQiZnh6lMybdTDXOH5sBtMtZftpk00OIMMY1pdTUxIpD8OTNJD/Lr\nNOvgBhaC921EKJhk/elirhFMJO0cGlwRD1h6gE6KHYnjV2+MWD9cNDQH0m8NBQWTTJNErr6pSFU+\njrAtvrnJ2ZgbnI6RzeMpdOZ0rQXemJa8HXitxwX0Fnc18zbyDJeYkmaH6AhC3OUTrBFejMV90zHo\nJi4Yt+5Wrsy5plMGU0hdcpf6xAiCKuXGUSXSKXeoO1DAONjK4NilWUs3/U7FGD4hCachdrnVQK4f\nSdhaOcMAwZNSBjeM5Y55NeW1kYgyI0J2YKeSYC/MBUEBWSFnnHIqXZTTpXn7XUbePtV2t43Te58b\nCliC1u0yDhq7a21VftuSGj02wXqvNaVAT+BcU3K2wEqIaxuiZGUFm+ybWINJMDf6DLBpZR1XZs0w\nVb40NYt2czA7rtzk0tQOO9flIXbqbh3nNxHzQ07N1lS7nW1OjwMvNUSZfFWcFpNjvY3XVfMShnAG\n0Bw6pic5NE9ozPX7aQAAQABJREFUoSbZ4MR6w/GrNuYmJaeVnIYwrqkoHmX9PHmShFxDY3q0DLnJ\nClXU6PqRGF/ZEDPPH4iFWeiqjrghvo3KIrTuU9gkH/0Im7PuRsG9NMlriJdoCa6hTwtZ2qZQha+0\nV8uJ2BJTrMlFukdjPUEZsIlJghI2adpGvoDf5mPRaSx4xobGW4jVWK5wRdy6yzcERRVz+6djgLWt\nrV6/zXuWLfTo43zECTSby5TfMpK6Hbw0Ks2p9QCaNpJid+ef5WDYExkDkWB2MjxKwDBC8blDOXfF\nu6ZkAJXqLPw1jtsI60j7OKbJM0UVPF1nKt6nlKbge+b8p0Ay4pEthE4porQFnW2MPHj+Z6eHIYVG\nOXWKDoJYa6nhXaSpMEc4dfn2rGdOgZ7AeeY0PCaE7Bgbzm0ZOzdGwNM5xYvw5i7bgYmhWH/VVjrf\nfbHI7nU1PRWX+qGQynT2KJrNgSvQCuXuomr2GrmvM7q2ITyy8hwTrU5dspq16RhSuwKPwpHCq1q4\nMTSdswcOoUlcyk5/hU62jdM2stnWCuwUzGq7XHmwARDrVffVBiFbB2CWXxui3iaXtpQU3ETFkRcI\nnYN9W+Pgs/tjkfWnrmnVKBD0jUDjqzdF33o0twqbjXasQ7wMeeaPxAvEfNevSqWQXR1Nq0V2hcII\nhTjlek60x0MsDxDlytfxcKn8d1OpYpiaRv/Wng7pVq5Hu7f+x3sTy3+iHR1TeB1zxEe5rjoxkBpg\nEIXANgSU4ZyKVlDjx9T02BWbYmZwMObQHjplrTGulPIAh+GJsRgnzOIo2tFcUKygKT7EZ5DmUpPO\nQfBNXPm35U2cTtlUHW2DF0+qwRSt1EqzycD6O/38vhhd3hiDaKatDykYg+/RNGohHf0u2rTPNhbf\n6dS6N7Ea52TzowF1viuOn1WXhKmbvxY+1p45TQq4EdGflKTVg5RF6XrmN2652bJxMoFuinc5n5b7\nWsDoxuXF4tENQ3sLZy3wWwsY3fi1uHW7deNsm1umDenGQ11WfSy91Va6Cd57rSkFegLnmpKzBVYN\nlLtdraUpdjC1qIrTTmwId2/08TzNZSSPlXUDMXzV5ljYfSCW2L1uI6YmcQlV49jm9azb5Bid7EXR\nAwHPDrcV/JwSLmOFOrpS6dO6aW/Dasc0uGkdomcd5pebaBpQgnYH+ILTv/2HYnzrxlikk1VjWEfE\nFMTSJnbDPipN8i7OFcJE6RqZUrWDXOpT0GIdK11327y7ms4wNvjLOc2sVtdNNmi5OFbHLHn4vVPh\nhtFhEfvQmNqwzXGIpQBLHIHkcSV9I30ILWhox91cpG4ZbRswUpTIntz4YtbgbzmZQMLVTUpXV44F\nm/5tmPLTvTUezY8kQp7Mp+IKO9KhoTdJLdDCiafH7HgMlrLvMMFmdnFc0MBWTgYYYm0uGkAEm5Za\ndfYK6UHzpIt4rghNHHDz1HvSK6NdMcxqbRzUcmhOzYXulkHlLR18NKYtL/OJvRHW5bXVXfOma4qk\nTXmZNwclmoyd/C3W8CZ2F+gPc8Ay88+AcxBFYN55EL8RhIW2fngrO8A5mmRpz2zWCT08hWFw/Sgn\nMWxgGYRcQfkDT9gUqqzRbO6oesBnAeTZj8RpValupHzSu7Asa0IkpQaW1EZ0FXRqn908t0D9dG3t\nEvSTjywv6dKXG4kOxgSbDEY4mmkRIikcZ3knnOOn2VCKtORDy8wZDcsMDBYYMPVPw+/QSfyT79PK\nMVI4eO4uuXKT1aoxoA7FZ4uxDtpzMDn0HGZwusKRYVKiU1AZ8UT4GUBatOZkYQ13svBrAcN0Wjgn\nS687rPZjhT+W29Fh/e4iNslXLJ985Hc3HMOvYllfq88W+1WXsh3L/Vhuhn4x7mIoX3faEwHhmLCs\nSLRHmtOBfTphjwd7LWCcDmxqg8GPMrah3Xk/VpijovQ+z4gCPYHzjMh3sshU5qrPvKqWO/3nbTce\nh2SDlget06GMsHt95LKhOLCwO1Y4c9NzNjdu2hRjW9YjpKzEmDvU0wiwAZrf3ZWkdT+Wm4Fx578T\nClwyJA2PHfUgN7qoieqjs1XoNUX7M5lkZt8hOkG0rZu84o3jI1IAUvwyZ0I8QZpNKEUGTaXKVPgC\nk99zw7lmlG1JCUWhRIHMkP7ULi3Row8jBPRxWPiynWi2oHayhGkzA56GHV83zNKDgTj4wr4MthEh\nuX98LBbIYx9wvDmib5lbQhCaanlApiCkTK/AKUEouBEnHVra66IApF/RsaZhXc9HQP+Nx527HkJf\nQghHYSEQ9i8tkA/KXqEryx0AgB1CmJlDsz2952CsG9jIGahVJb0NyTM6EJ+w6EbgpDlWhbsut5UU\nOAlLmkUQ/UnH8hWvzItnEXSbY5eXVCi6EzaDIOhIb4SaIdIcmB9FkwyXgEtRRfqVWfJAdjSa4ue1\noSPylKMmTApSlqug8ldco2C1buOmmONqucMHDop1HtC8ceuWWEYzrWgmXybFk8ZtWVgw/lqTyEKi\n/jiwh7JXSKsMNAGODlv+akQVZKWT2XS5g6fj9DPNP8oW5YyVYUgNXJ3K9xD7g5wDu35la4xumMgj\nVRK1TKnwqESPSjPLDR/gKOAWn3jq7lBsnL+Ga/bGKCrjwKdJKOnm8hZCz13LxsFrUrZMMAq/Ygey\nDtbky2XWMq8sbqBt4RsBPW9JAc5KtjWF0YlpYpijcG6jvWj3E9DjRcFeC/zOFEYbv32fj3msskwM\nk5ewgWYeAaW1PpPf5h2hZcBOgfQsPQqcFQpU73ZWQF+6QG1+sglqKnV1kYgONPwKaJNclbZ0cAat\nXWmf7CwGWL/J9tcYofux/nuDxcF9+2Pv/r3V0a0ROdt2xQ4tR304+M7OFhy8RtCNHmUyF4mPGtDD\nuydZdzjZCCDkCm/FmBKKToBgNngFi8wQEDtaHfVe/QgufcsL5F+do12nmjjDluCTsRBwFu1gB0bQ\n4NA2IhAPEE8/O2SNmlkFYmno2Yn9jcCxl5MA5qB3MHWb50GSN/RExGAFHmmqnVWISJxa4lSigsVk\ngtkeVygFuXJWOJJutta+xEUv6WHDvqwKk/yIc15ohJZqgDMoU1uGT0pf+LlhbP7QbOyfmUmcCk9h\nyxngar4yBcUvIiDQpsCiXS0mWi0R6VuZ4S0GCiwKIIT3M4V0YVSZYcEQt2MMVEYh2Xy4Q1vX4hLy\nobCLILPb3Jk3wqVAbbk0IfNF+ksIuB4xsoyAP4gmMNMl7RJRia/QbfKizyOve4U2cp20W2Lz166n\nnqsNQqRjiaVmP1FexZXgP2T0VSN4Uq074QxrmpZngYZmWNRuUhMQ3NiRbj6tkF0mp1EhydQze+Lg\nrr2yHIIq8JL2XQGPYU3sV9AQp4YT7S9UWemfjZsHXwNHMijBvZ91o/3QWtw8lsfi3/NoxKe/N5UQ\n1QBbOg5uHCjCPbirPZ+OeU6XmFtB87oylfUB9DNs5TSj9x6XIAXkO2dONLasnuQhV/e7/v36y6pN\nS9/eo0eBs0cBe4OeOdsUsEOjdtsJcsENZ/qpsaPLoP7bcdgMpHhl551Cht0uDYLTemzkUetSg9Ds\nrs4MW1sZOyubGztT/lyziYNI8LPT5aPxM6QHwntyk51Xtll0rHmUE4g7RerfiQ0BmyA0dQQtAUp4\nfvU5DZgSSMHthiZKBso73pUGSC8FEJ585M+norubmvRlpwpr+0gDmGqjxpjWXeYU+6S//olzvohl\nnMInBaPuxIFbLTH+SQ8+GyfpJzzzIIJY0y/d0oVvNLLq5lZUE1uuGZ6wENFoKaBgsWyHHYzkAASt\nYA4+imZ1bZ5Imd9Eo7G3+bcK6y9Ep9pbragu4m1+zV/Fx9IYw7emMq2LQqRvaVx5X3Wz3PoRdvOn\nwC8Da5KexCtiJF1WkJKypBjASOGCbWAJpUv98ZF+uUQjYVVuBtVoK8Dxn+dcKkRm9KPzoePRptI8\n2vXob+H5S9bzzVexmAsjSB/Sev9ym2JLsSxHi4e6oqzXlqM5OjVDOTUCfYbvG+YmpstTSF9hiYl0\nc8lJDjwbJCWta1/Fwdtw0lCu6cZ3Dn7wdeafVS+UFHxEHViB/9Wh9sylR4HkV/kHi5zj0M0qlVpO\n2vQcpDOoaoJcegTq5fglp0BP4DwLJLcC20llhRc+DnZkdkxOqbqmrw+h06qugMJ/CX0ZywgYhJIU\nE2gYhGO4BKTfGRtgAtSOU6FBPFJgE26OfKvrVLhsUzVsGlssMuPSQV2cSq6rI8v72E/zWfSwMy3R\nofKVLSAY2BzmTCOJth23HXpNAakJNXz5pZDbaItbOqsprW5YQuNqp4yGLzeVgG/R0o4cDEHcxtcP\n4+R0ajrikQEMVIJQCccZId2cSm6FY4MCsdI12YxuXsHXb/6EIh2LfOBBpKJ15TIFZJMjzGARIOF7\nXSdZRHumpwbgmYvEGLtvje5qUjViY051k8oAMGHLmb8isOGObbJEDJbRfQjP/ODjWkbw4zAufqRB\nOPOYaWlPkAqh2a1RbuInjDJl89n84GsTSr7GqrY76QksMa/yaiKnsI4dAcp6dHLTpnPykIYwtPnJ\nfJs66XgTiQOY9oxcg6QxEEhYLJaXlzNo7yxfaMMd720mFVSTYvIzZYcTxyggWLNkhGs/i1eKxnmR\nAd5NMea7ruEDDWBl3WNNb/EbNIc/815ogPbnqeVqmzOHx8Oo536xUsBilyWa/PnOVqdpU7K+dXwv\nViL08nU+UaAncJ6l0sh+pYFtf6Kx00CxmQKHHVkruNj3duo9nYNChk5299mZGZdf9si+z8AIp0QR\nxYtWaCnQbYebiRIuGyffICOOTTuFC4KHCGJKkGlzWG4/9Czk03k1XoVKIVJY/BTmsoXMzriFXQm5\nrlOb4tqSqif6Y6dZFUgrRIsvjoTNvDHdrICUa+/UFhufntlfCi6qbXFd4c5krzUzk6mtypCEB7Z/\neexO5oHQJgZBElVSVmQwLbWYuckkC8yNYUwno+VLbaZxibeCALyYglgJHGLuWljpW7mo9AWR9JaJ\n8GlN697HVP0KmjfzZVmojzM/fTFKergZRQSz4IAs7bCbl0REewrkDeWMmzhUSkUD8pZEEqfSgvjd\nx9xx3m4FMgqF7e0d4iYMk+inYBJXt+IrpJoP8MkhgUIaOPa7VhHnMq2A3n4TXB7j0zJwqUTiLhwd\nG7RXQ3fZGphtbluf7ihNkKSx8KSi/ommEXLtbKVtRvSz3DOejwQGfXDMv/zGQwAnMOahtMDS07CZ\nq4RXa39xYiRnWlXfSLsRGIofMkrSPSniml3xYsmJiwDSZCaIB/jCXOROjltF7j0vFgq0nCgvyQGt\nSa6jota67OSM1qv37lHgrFOgJ3CeJRLbiVm5m+YeC190XrqlxqFpCHJTh2E7rYLdhGGrS6pntz+B\nz9TYi2lIRqG206niZHqiaggFjxYt8S4tDhbCiGXGFchJTAkwFajgJbTGoYmvhKIRnbLVR2KwioX6\nM5cAZMcNkobVtzrXNiZfOC6zIrbPQ0zTH7EQoXCJk0X3Te3O/JnkEJtcxkc5CWCQ3dJq7+jZLTtV\nRtLC9ZbLTufyzrKwDDMZkRAwfgphy3PxwENfj49/7sMxv7AQP/3u98fVl1/DbvmVuOqqmxHeFD5d\noepaUsAjNIqNAw+n/c1E4YxnCmkN7Ew1E8QB3CiclWU2PLHeb/LAFHd+i+cCm6XWx7pRd5CLvfBw\nF1fh4lIUIgcIvAqJAwNovpp0M4TJNlRU1FtyUhlQrq80/541mflXiMyMC1WRE1f8iiNMTGFefAlP\n3kpglGbcIz81GQenpuKaq68lDOtZc30neJrxFMR5JbIKc8mBiUMK9KYprTN/gD+OaWmYy0SOE0Zn\noUu5TDrzLsb1Mz19V00ilf66thrDpMdqoJPaKrza6DYl3/Angwd5T6zIefKfwqxpSdu2HBU6jaF7\nlQL86QeO0j+NlVd7BpQPKKN0K+/e89KgQMsnvotjKt9VU23eOhxTHr1njwIvAQV6AudZInJ1Gym6\nZAdil9J2BKlloyWg68fNTqb6h/zsfFQzUVPpNBBGXwOT3XjTHwnT5MRCk0nrpwMfhj3C4FYCgd42\nWHbZJ8ftCPiEb+Nlyo1nTtumX4sVkAXeJFjBqhO2U864+mcg3+DLd9IpA+vpWjkFTrWP7vxfjh/s\nfCx+49/+r+yKritFx0fH4rabbo+feu+fjS0brywBNdOlBOnslxESFYiUf/oVQvFTiBKv3KSkgIXA\nd/DQnvit3/433HE/Hq94+atyp/P/+W/+j5idno3/5R/9azSbC/Hsrh/kJpptV94ATghxCGSLChtO\n3SZcEjEt/swhulny41cZ32lHWP38Fz4Vn/jMp+JZNq6McV3nDTfcEn/jl34t1o2Nx9ShvbF713Nx\n3XXbY3RkAticKsDVqAq3Cn6ma4ZKo2ySSbXMl+Ww7M5ntkY/v+e5mNx/IG656TbCDyFeInyC7wB0\nlL9dS9jvsUeWEf/ycwmZYi+uhiI/KTyuxB986Pfj69/+evyPv/r3Y9u264jQ5ExtK6FTuDL/0iJB\nFBz9JJC44ZU/4R/LCNEwKfMeK0DjZhgD5ruxN9g0IVZfR7u38lvGlZSC4lFUXI13tC1rS66nNV3K\nXd7M6XUhlGDoZi0HPcIuL2zJb0KDHgblt0QZ1sauhj8okzTp32AiEAaXKZSWb+95CVFAVmiN/Jk8\nhUO2Nbz9PhnPtvF77x4F1oICPYFzLah4DBjZ7rfufGSjb6Wn88i+oelE2grfdmLGa432ri63dT6j\nt41MazqwU8gpV6w1Vd0G6n4n/jZShWXBKvGvO9iJ7G3+2rjV7DUxhJ/NYPWr+ezSxrbNo6Nz6dnS\nLGMDMOPi18IoPZApKe4jcCL0HTp0IHbv2RXvfddPxA3X3hzfe+Rb8cWvfS6mZqbjr/3Fvx0jHMIu\n0W2Ulwlfaj5FP7FpV7oShsQXEZScrvZAzam5/bFr/zPx5/7M/xCvvfuthGc9HsdQLrFzfAk480sz\n8Xsf+bcxDPxf+oW/GyMDE4RBHDMhYQjLVJIv8EmhMHPWyhid9+M/eCz+y4f+U2y//qb4cz//izG5\nd2/8t/u/GTNzMzE6Phjf+PaX4sMf/WD87V/+tbju6pdxyDxrItmlv8RpAO6lUojuiLFmjAybtm4D\n/cMpBy6vzMVnv/Gp+OKXPx//4Nf+UWwau5JsuuvckwIWwJs4TJ17jmzexmN5wDz6qRVOoTxpRh5x\nX0SIfcPr3hg3XH9jbL3ssuRreY1YpCod05rLSVIDC6LWldy0wxlR4pwaaJ4kdVxTYFY54LgBBdIA\n8mX6rVOiZVng0gRJMAW7ME4HQ3RgmI8TmxKY1QoTMs+EpRwyMWuiyw/kUZdIFP5wX2LhU1rILwMO\nFEwoEwOWb4/gyjLE7icwCvvybut5+faelx4FksmKZch8h32wJRtdegTp5fgcUaAncJ4lwqeWJtU0\ntV7Lql3VvjqRlFNIO92o9QqebeVv36JWHZK2Nrb2F2+68RCkWNmVt+CzMzOpFjGs4tOm3vHCbdW0\nvqsuR9pWG7YKWZ1zt1hQ1FnNeYXgO/9Xu8w2fddvShzjJY34PhILfTx4HA0l5eAB27WWEXEZ6eLW\nG18Rr77r9fHKV7w6dk/uju8+/O04ODvJ2YuL8eCDD6LVuz4e3vFd4gzGffe+JYYGB+KpZx6PJ55+\nIg4fPhy3ohXdfv1tMTw8FLv27owvf+NLdPmL8cQTj3GI/vq4B7jrPP8TQXKZMzi/hWbv+ReeiiG2\nEH/l/s/GNVtvjJtvuSP2Tb4QDz30YBxkevy6q6+Pu15+Dyc4ccxR4lzCvPi2xnWiz+95AeFyLu57\n3evilttuz6NxXnnPG4A9Fk/ufDQe+f63EKD3xDe+++XY+fwLce89bwbvJ2Ny6rmYmJiIHQ8/HNuv\nuyFedsut8czzz8QjOx6OhcWFuPrqq+Pu219NXtfFI0/siMeeeiQmD+2Orz34ubhi/U1x5y2vjmXy\nu2ffs/Ho498D5/1x/XU3hdrVdaObEZaWY3bxUDz6/Ydj51NPgw+3CY1v5BDygbgdPEfGh/PnWZNy\nxCLHAz23a2c8+ugjscAyhBtuuiG2b78ZoXeUsluOvXuejUceeSgmD+6L67ZfG3fc+qoYHuTu+aNK\nuqWNb9nC5yrF0uE4jwrls+IZbJVXT5ROC7BNp+K3X63vkW/DKDQq4Gur9ZyGafmbd4LogkOFVKjV\npa60xc53DfoKYmGv3XAKp2XTYrxTpYaxeubipED1I8UjbQ5rmNl+9d49Cpx9CvQEzrNE42zoq+XP\nFKzwueu6ccuOoe3m8Gu7HAMb94fNkY3FD/u/OBe7fnUuLSpau+QbPztmFa9Vm57COFVzZMyueF20\nWoVVoauT1nU1ttPn9VkwVn3a2FIYwU2tJOrQ2l2dojXlgIaUtW1LrKscHpqITeObyf/jCADzCJ9P\nxW/8u38c1227MvYg2G3ZdEXcePPN8e2HH4gP/O5vxsYtm9GCDsR//q//Ot5y33vjL/zCX4nvI5h9\n7gt/QhoL8ZkvfSwe3vpY3HLHDfEHn/xgHJ6eil/772+NP/n8hxGuniaNpfiDP/pA3H3HvbGeQ+n/\nxf/zT2Pfwefimquuj09/6ePxF/r+Wtx9z6vB0elqhBOyp7a0hAa0YGgUt27ejKZyOD7z2S/EjTfe\nGdds2RbrBtGYMjX+7Qfvjwe/9eU4eHh3fOzTH2KK/cq47Y6Xx+e/9rH43Nc/FEucAzoM3HvueA3C\n6PfjP3/wt+PKa65OLezTH34sfuJHfzb+9I/9Qnz1m1+Nhx75TswguH7ko78TN17xiti+7YbYgwD7\nL37rn+QU/ZVbr4zf+aN/H/fc/dr4lV/8n9D4LsTvffy3448+8YdxE9rXZ597IiYPH4jbr395/NXL\n/3p88zv3x6c//xmE9ZdD16H4wv2fjH/3O/9XbN7CLUsI2f/hD3fEz/zkz8d73vkLseuFvfHP/9X/\njMA+E5dfviU+8ZUX4pf+3K/GXbe+njJd5Ztc/9kW+em8k2G64JxO3C4+bKMV/63Ca92PeGcgV19q\naiNW1XyFUD2rOS7hICfZDVhT62lDDK+EGgxkDjmDvzZSJ/wqLqdTPzOZ3uOioEDLAQ3LdPLUust1\n/or3Wtd06D16FDgrFOgJnGeFrCcDahdx/piXApczS+PFxm4a0TwovTSb1bguxAEEsn0HnonvP/Gd\neOjhb8bLbr4zxoe3xqH+KcSZaW5TGol/8Pf+WWzasCV2730+/uuH/3O8/U1vjz/9vl9EOBqKj3z8\nP8affOZj8cq774nX3P22GGXj0T/5578ef+kv/HLcedO9MbqOTUgkv8zayYmx9fHX/9Lfin/1H/5Z\nDHBL01/9+b8ZE6T1vcfvB/az8Wd/7hfRQr49pg/OxrrhsVwrKeYlRGDjX6Ehf0xX34Im8B1vfmN8\n8rMfjX/8T3813nTfO+Mdb/kpNifdGD/2oz/F+svF+ONPfij+xl/59dh+zR0IndwRhDB9YHJPvPut\nfyZ+6l3vj9HhEZYW7I2/97f+YWy79mUxvzyNQP0v4oEHvhE/+sb3I/j9WdYJHoyvfvXz8Wt/8x/F\nlrHtbDRajA/9lw/EuomNmZ8t6zfFl/7bp4j3/8X3Hv16bNq0Ib72lS/ET77zp+Mn3v4z8dTTP4i/\n/7//erztze9mKv021m9+g1wgSA8MxQt7n4sPf/x34040un/+534ZzeVA/N4f/Mv44uc/Gfe+4o3x\n9FPfj9nDu+Iv/vlfijtvf0PMTC/G2PAE8Z32r2OLXrSweV5UvW6ebu3tuxA88utESJ96yBNB6fld\nGhRoucU2pmd6FHgpKdATOF9KavfSeokp4Bq35udYHmtpb2fjDz/ygfjEyEdj/4HnY/3EhvjZ9/wc\nAqdXA9ZquB/7kffG9qvvZDpzIe7/1lcRUA/Ej+N2GVPEg9x49LpXvjW+9JVPxzPPPBH3vfLt7HQv\nYWjd8IZYv24T+ZxHYJxnzaO3KAUC3gRC30hq8ibGtsa6ofHYQLghNJV/8ulPxKb1V8XN192CcDrB\ncj7X8YGvWl+m0GtiXV0E1ZVbg4aY5n//e/9S3HrzXQhtH4xPfPaP436m7P/Or/zDuPqKm2JkeBNC\n2VisY8PQ/8/eewdbmlyHff3uvS/HyXFnZnc2YneRQYBBJE3QIljMtEklV9FJtuXAYukPy7QlS+WS\nSi5ZrpJs+Q/RlFSkTYoyKYoQAyAQBEEQILAIi7RYbA6T45uZl8O97/n3O/31vfe9eROwmJnd2b09\n8+73fR1Pnz59+vTp091jg0MsuWNzyQadicmd6UN//qfS7u33ejZ+2n94J0vgl9L0hZPpkjv3KWKB\n247S+hI2ptvSKOkbbAoaH55kyXyMjVGn04svPJ32Hj6Qnn31GerTQIN5KbS2p06/nCYnHkzLK/Mh\nVNcHh9OO3fuoBXfncGwPa/FhnqDZQatvMZ0+f5wl83Pp0JHD6amnn+SA8mzyMDs/l6anZ9PE1K6w\n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\n" } }, "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "![BSE_system_diagram.png](attachment:8626ec3c-5c9c-4ae1-821a-31ee98123508.png)" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "BSE simulates a *market*, composed of an *exchange* and some number *N* of *traders* which each interact with the exchange. Any one simulation of a market session proceeds according to BSE's *system clock*, which provides a unified time signal to all elements of the simulation.\n", "\n", "Separate from the simulation of the market is BSE's *session control* logic, which determines the (potentially time-dependent) market's *supply and demand schedule* (SDS): this is used to issue *assignments* to the traders, i.e. allocations of cash and limit-prices to buyers, and allocations of stock and limit-prices to sellers -- this is the simulation's correlate of real-world market *customer orders* coming from customers to sales-traders who are responsible for working each customer order. The session-control logic is also responsible for recording whole-market data, such as the profits and strategy-values of each trader in the market, as were visualised in the graphs and plots earlier in this paper.\n", "\n", "The exchange receives orders from the traders: *bids* from buyers; *asks* from sellers. When each order arrives at the exchange, it is processed by the *matching engine*, attempting to find one or more matching bids for a newly-arrived ask, or one or more matching asks for a newly-arrived bid. It does this by comparing the new order to those earlier orders, as yet unfulfilled, that are \"resting\" at the exchange and which are summarised in aggregated and anonymized form on the exchange's *limit order book* (LOB). If a new order can be matched with one or more existing orders on the LOB then the matching orders are removed from the LOB, and the new order plus its counterparty orders from the LOB are recorded as fulfilled, resulting in a transaction taking place. When a transaction occurs, its details are written to the exchange's public record of transactions which is commonly referred to as the exchange's *tape* -- the tape records transactions and also other notable market events, such as cancellations of existing orders. When a transaction occurs, the exchange also notifies the traders concerned, adjusting their cash balances appropriately. The BSE exchange also can be configured to write the state of the LOB at any one instance (referred to as a *LOB frame*) to an external record, the *LOB framestore*, for subsequent analysis.\n", "\n", "Each of the *N* traders in the market receives occasional fresh assignments from the session control, all have the same view of the LOB data published by the exchange, and when a trader is involved in a transaction it receives notification of the relevant details from the exchange's matching engine. Each trader is able to send orders to the exchange, and to send cancellations of existing orders, and each maintains its own local private record of assignments received, orders sent to the exchange, and transaction details received from the exchange: this is conventionally referred to as the trader's *blotter*.\n" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## Using BSE: a first walk-through\n", "\n", "Let's start by using BSE to replicate the experiment that Vernon Smith showed results from in Chart 1 of his landmark 1962 JPE paper *\"An Experimental Study of Competitive Market Behavior\"* -- this was Smith's first reported CDA experiment.\n", "\n", "For a PDF version of Smith's 1962 paper, see here: https://www.journals.uchicago.edu/doi/abs/10.1086/258609\n", "\n", "First of all let's import what we'll need." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "pycharm": { "is_executing": true, "name": "#%%\n" }, "tags": [] }, "outputs": [], "source": [ "# un-comment these lines if you need to install the packages\n", "# !{sys.executable} pip3 install numpy\n", "# !{sys.executable} pip3 install matplotlib\n", "\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import csv\n", "import random\n", "\n", "from BSE import market_session" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "Let's say all our experiments are to last for 10 simulated minutes..." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "pycharm": { "name": "#%%\n" }, "tags": [] }, "outputs": [], "source": [ "start_time = 0\n", "end_time = 60 * 10" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "The supply and demand curves in Smith's Chart 1 are symmetric: they each involve 11 traders, each given an assignment to trade (buy or sell) a single unit.\n", "\n", "On both curves the minimum price was 80 and the max was 320,\n", "and the step-size between successive prices was always 20" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "pycharm": { "name": "#%%\n" }, "tags": [] }, "outputs": [], "source": [ "chart1_range=(80, 320)\n", "\n", "supply_schedule = [{'from': start_time, 'to': end_time, 'ranges': [chart1_range], 'stepmode': 'fixed'}]\n", "demand_schedule = [{'from': start_time, 'to': end_time, 'ranges': [chart1_range], 'stepmode': 'fixed'}]" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "Smith used periodic updating -- at the start of each \"day\" all traders are issued with fresh assignments.\n", "\n", "Let's do that once every 60 seconds" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "pycharm": { "name": "#%%\n" }, "tags": [] }, "outputs": [], "source": [ "order_interval = 60\n", "order_sched = {'sup': supply_schedule, 'dem': demand_schedule,\n", " 'interval': order_interval, 'timemode': 'periodic'}" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "And finally let's use 11 ZIP traders on each side, buyers and sellers, because ZIP gives reasonably human-like market dynamics." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "pycharm": { "name": "#%%\n" }, "tags": [] }, "outputs": [], "source": [ "sellers_spec = [('ZIP', 11)]\n", "buyers_spec = sellers_spec\n", "prop_traders_spec = []\n", "traders_spec = {'sellers': sellers_spec, 'buyers': buyers_spec, 'proptraders': prop_traders_spec}" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "When we run a BSE market session we can alter how verbose it is, how much it tells us about what is going on, but this can generate a *lot* of text, so let's switch that off for the time being." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "pycharm": { "name": "#%%\n" }, "tags": [] }, "outputs": [], "source": [ "verbose = False" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "Finally BSE (which was originally created before the days of Jupyter notebooks) writes output data-files\n", "in csv format for later anaylsis, so we need to give it a session-identifier string which will be used at the start of all this session's data-files, and what data to dump into files.\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "pycharm": { "name": "#%%\n" }, "tags": [] }, "outputs": [], "source": [ "\n", "trial_id = 'smith_chart_1'\n", "dump_flags = {'dump_blotters': True, 'dump_lobs': True, 'dump_strats': True,\n", " 'dump_avgbals': True, 'dump_tape': True}" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "And now we're ready to go... we'll run a market session, which dumps data to a file, and then we'll immediately read the file back and plot a graph of the transaction-price time-series.\n" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "pycharm": { "name": "#%%\n" }, "tags": [] }, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "random.seed(100) # changing the seed value will give us different seqences of random numbers\n", "\n", "market_session(trial_id, start_time, end_time, traders_spec, order_sched, dump_flags, verbose)\n", "\n", "prices_fname = trial_id + '_tape.csv'\n", "x = np.empty(0)\n", "y = np.empty(0)\n", "with open(prices_fname, newline='') as csvfile:\n", " reader = csv.reader(csvfile)\n", " for row in reader:\n", " time = float(row[1])\n", " price = float(row[2])\n", " x = np.append(x,time)\n", " y = np.append(y,price)\n", "\n", "plt.plot(x, y, 'x', color='black');" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "But that's just one run from a stochastic system: if we run it again with a different random seed we'll get a different sequence of events.\n", "\n", "Note that while ZIP is pretty good at doing what it does, there are a couple of known issues when a market is populated entirely by ZIP traders (i.e., when it is a homogeneous population of ZIPs): (a) sometimes a homogenous ZIP market will fail to equilibrate at all, or maybe it will converge to a steady-state range of transaction prices that is clearly off-equilibrium; or (b) sometimes a homogeneous ZIP market will grind to a halt, with a total cessation of trading.\n", "\n", "The behavior of each ZIP trader is determined by eight numeric parameters such as its\n", "learning rate, its momentum parameter, and the two constants used for relative and absolute perturbation of the target price -- these parameters can be fine-tuned to particular market scenarios, and if the parameters are not tuned to the market situation, sometimes problem (a) occurs.\n", "\n", "Also ZIP, as implemented here, was originally intended for operating in simulation models of open-outcry trading pits, where there is a lot of (noisy) information to act upon. In a Limit Order Book (LOB) trading scenario, ZIP does not always do so well and sometimes a homogeneous population of ZIPs \"locks up\", causing problem (b), because every ZIP in the market is waiting for some other ZIP to issue a quote that changes the LOB, and because they are all waiting, nobody does anything. This problem disappears once there are one or more other traders present, even if they're just noise traders like Gode & Sunder's ZI-U or ZI-C, because the other traders put enough life into the LOB to prevent the ZIPs from locking up.\n", "\n", "\n", "\n", "Analysis of individual runs is important, but it's also important to characterise the general behavior of the system -- for example, we could do 10 or 50 or 100 (or more) independent and identically distributed (iid) runs, and compute summary statistics. In the original ZIP paper (HP Labs Technical Report, 1997) multiple transaction-price time series were summarised by showing the mean price plus and minus one standard deviation. That's a very straightforward way of doing things.\n", "\n", "Let's take the code in the previous cell and wrap a loop around it, so that instead of doing a single session, it does n_sessions." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "pycharm": { "name": "#%%\n" }, "tags": [] }, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "n_sessions = 10\n", "\n", "x = np.empty(0)\n", "y = np.empty(0)\n", "\n", "for sess in range(n_sessions):\n", " trial_id = 'smith_chart_' + str(sess)\n", "\n", " market_session(trial_id, start_time, end_time, traders_spec, order_sched, dump_flags, verbose)\n", "\n", " prices_fname = trial_id + '_tape.csv'\n", " with open(prices_fname, newline='') as csvfile:\n", " reader = csv.reader(csvfile)\n", " for row in reader:\n", " time = float(row[1])\n", " price = float(row[2])\n", " x = np.append(x,time)\n", " y = np.append(y,price)\n", "\n", "plt.plot(x, y, 'x', color='black');" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "As you can see, ZIP is not perfect (ZIP was originally written to operate in a market\n", "that is a model of an open-outcry trading pit, rather than a LOB), but the results we're getting here from ZIP in BSE are illustrative of machine trading on an automated exchange: no-one is saying ZIP is optimal!\n", "\n", "To make things a bit more realistic, let's change the re-supply of buyer and seller assignments from periodic to stochastic, feeding them into the market via a Poisson process, and make the schedule re-supply be on a 10-second cycle..." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "pycharm": { "name": "#%%\n" }, "tags": [] }, "outputs": [ { "data": { "image/png": "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10+9GFEEACiFfHJYaowxJtdgp6amRmvWrNHChQtd15900kk666yzdM8996Tdz+bNmzV9+nRt3rxZp556ar/1sVhMsVgs+by3t1cTJkxQT0+PRo4cmWvxPTkvn9ifOy+fzJo1S5K0YcMGdXV16emnn072sEgfzG47e/bs5PJCzF47a9Ysbdy4UVJqDovzfZ2sHhXrNYUqDwAAmfT29qquri639jufSElpelieeuopI8ls3bo14376+vrMkCFDzKpVq3y9b7lnurVz66Vwzm5b6J4Mt9lzvWbUdcP9igAA5VDSUUJ+Pfjgg5o2bZqmTJmScduXXnpJR44cUWNjY7GKUxRWD0a6UT+33XZbMhfGKxnWa7RQNBp17SXp7u5Ovq+1Tysnxr7ea5/OxGEAAIIu64Dl0KFD2rp1q7Zu3SpJ2rlzp7Zu3apdu3Ylt+nt7dVPf/pTffnLX+73+tdee02RSES//e1v9frrr+vxxx/X5z//eU2dOlVnnHFG7jUpAyuRVfogaKmtrU0uC4VCaYdA2/eRTTJsS0uL583kIpGIWlpaXN/LmUic7/BqAABKJtsumQ0bNrgmwC5evDi5zfe//30zdOhQc+DAgX6v37Vrl5k5c6apr6834XDYfOQjHzHXXHONeeutt3yXIaiXhOyTt/mdZM65D7fnhS6nn+UAABQaU/OXmduInWwDkELOhOuGmyUCAMotn/Y764njkF4oFOo3t4qfieLa29t12223ZbyElKt0k8IxSggAEHQELHmy8kKkD4YXt7W1qbW1Nbk8kUgoFAq5ThRnDZO257u4zaILAMBAVtSZbgcC+4gdK5HVvswKRqzgxd7TYQU7Tz/9NMmwAACkU4RLVEUXlByWbBJZ/c6em2nfAABUKnJYyiSRSHgOL7bWO5d1dHQkc1WsHpjW1lZf+wAAYKDKa2r+cslrat8yq62tVTweVygU0tGjR/utd7tzMndaBgBUg3zab3JYisRtBltrltmamholEgnNnj2733q3yeK40zIAYMAr+AWqEghKDks6mXJWmpubU+7snClnhTstAwAqHRPHFUEhJlpzBinOBFvreSgU8hV8FHtyOQAAiomApQgKNZW9tb1XUGIt93vnZO60DACoVIG8W3Ols24kaM8dsd880O+kbu3t7QqHw0okEv1msLUSZv3eOZk7LQMABqwiBFBFV8oclnwv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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "order_interval = 10\n", "order_sched = {'sup': supply_schedule, 'dem': demand_schedule,\n", " 'interval': order_interval, 'timemode': 'drip-poisson'}\n", "\n", "n_sessions = 10\n", "\n", "x = np.empty(0)\n", "y = np.empty(0)\n", "\n", "for sess in range(n_sessions):\n", " trial_id = 'smith_chart_' + str(sess)\n", "\n", " market_session(trial_id, start_time, end_time, traders_spec, order_sched, dump_flags, verbose)\n", "\n", " prices_fname = trial_id + '_tape.csv'\n", " with open(prices_fname, newline='') as csvfile:\n", " reader = csv.reader(csvfile)\n", " for row in reader:\n", " time = float(row[1])\n", " price = float(row[2])\n", " x = np.append(x,time)\n", " y = np.append(y,price)\n", "\n", "plt.plot(x, y, 'x', color='black');" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "And let's increase the number of traders from 11 on each side to\n", "something bigger, say 40..." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "pycharm": { "name": "#%%\n" }, "tags": [] }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGdCAYAAAA44ojeAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAABUYklEQVR4nO3dfXQb1Zk/8K88tuSQJlJMmtgmjqWwlJ4lNISEUl6MZRlCQwO00OW12/DSbF+StDkJp7+mRbKQsiRlD7Q9ZduyuyRACRtOT4Gypcs2WJFxlnIOJEAT2NJAZBMgbnZpbCchkeLx/f0R7mVmNCNLtmTL8fdzjk6imdFopJE0j+997nNdQggBIiIiojJSMdYHQERERGTFAIWIiIjKDgMUIiIiKjsMUIiIiKjsMEAhIiKissMAhYiIiMoOAxQiIiIqOwxQiIiIqOxUjvUBDMfg4CDef/99TJkyBS6Xa6wPh4iIiPIghMChQ4dQX1+PiorcbSTjMkB5//330dDQMNaHQURERMOwb98+zJo1K+c24zJAmTJlCoATL3Dq1KljfDRERESUj/7+fjQ0NKjreC7jMkCR3TpTp05lgEJERDTO5JOewSRZIiIiKjsMUIiIiKjsMEAhIiKissMAhYiIiMoOAxQiIiIqOwxQiIiIqOwwQCEiIqKywwCFiIiIyg4DFCIiIio7DFAsotEo4vG47bp4PI5oNDq6B0RERDQBMUCx0DQNkUgkK0iJx+OIRCLQNG2MjoyIiGjiGJdz8ZRSOBwGAEQiEXVfBiexWEytJyIiotJxCSHEWB9Eofr7++H1etHX11eyyQJlUOJ2u5HJZBicEBERjVAh1++Cunh+/vOf4zOf+YyaRfiCCy7Af/7nf6r1QghEo1HU19dj0qRJCAaDeP311037SKfTWLlyJaZPn47JkyfjqquuwrvvvlvIYYyKcDisghO3283ghIiIaBQVFKDMmjULGzZswMsvv4yXX34ZoVAIV199tQpC7rnnHtx33324//778dJLL6G2thaXXXYZDh06pPaxatUqPPnkk9iyZQu2b9+Ow4cPY8mSJdB1vbivbITi8bgKTjKZjGPiLBEREZWAGKFp06aJf/u3fxODg4OitrZWbNiwQa07duyY8Hq94he/+IUQQoje3l5RVVUltmzZorZ57733REVFhXj22Wfzfs6+vj4BQPT19Y308G3FYjEBQMRiMdv7REREVLhCrt/DHsWj6zq2bNmCI0eO4IILLkAqlUJPTw8WLVqktvF4PGhubsYLL7wAANixYweOHz9u2qa+vh5z585V29hJp9Po7+833UrFLiE2HA4jFovZju4hIiKi4it4FM+uXbtwwQUX4NixY/jEJz6BJ598En/7t3+rAoyZM2eatp85cya6u7sBAD09PXC73Zg2bVrWNj09PY7PuX79etx1112FHuqw6LpumxAr75dbVxQREdHJqOAA5cwzz8Srr76K3t5e/PrXv8bSpUvR0dGh1rtcLtP2QoisZVZDbbN27VqsXr1a3e/v70dDQ0Ohh56XXIXYmChLREQ0Ogru4nG73fibv/kbLFy4EOvXr8e8efPwk5/8BLW1tQCQ1RJy4MAB1apSW1uLTCaDgwcPOm5jx+PxqJFD8kZEREQnrxFXkhVCIJ1OIxAIoLa2Flu3blXrMpkMOjo6cOGFFwIAFixYgKqqKtM2+/fvx+7du9U2RERERAV18Xz/+9/H4sWL0dDQgEOHDmHLli1IJpN49tln4XK5sGrVKtx9990444wzcMYZZ+Duu+/GKaecgptuugkA4PV6cfvtt2PNmjU49dRTUVNTgzvuuANnn302Lr300pK8QCIiIhp/CgpQ/vKXv+Dv//7vsX//fni9XnzmM5/Bs88+i8suuwwA8N3vfhdHjx7Ft771LRw8eBDnn38+fv/732PKlClqHz/60Y9QWVmJ6667DkePHkVrayseeughznFDRERECkvdG0SjUWiaZpsMG4/Hoes6ZzMmIiIappKVuj/ZcSZjIiKi8sDZjA04kzEREVF5YBePDc5kTEREVHyFXL8ZoDjweDxqssB0Ol2S5yAiIppImIMyQpzJmIiIaGwxQLEw5pyk02lOEkhERDQGmCRr4DSTMWBOnCUiIqLSYoBiwJmMiYiIygOTZImIiGhUMEmWiIiIxjUGKERERFR2GKAQERFR2WGAQkRERGWHAQoRERGVHQYoREREVHYYoBAREVHZYYBCREREZYcBChEREZUdBihERERUdhigEBERUdlhgEJERERlhwGKRTQaRTwet10Xj8cRjUZH94CIiIgmIAYoFpqmIRKJZAUp8XgckUgEmqaN0ZERERFNHJVjfQDlJhwOAwAikYi6L4OTWCym1hMREVHpuIQQYqwPolD9/f3wer3o6+vD1KlTS/IcMihxu93IZDIMToiIiEaokOs3A5QcPB4PMpkM3G430ul0yZ6HiIhoIijk+s0cFAfxeFwFJ5lMxjFxloiIiIqPAYoNY85JOp1GLBazTZwlIiKi0mCSrIVdQqxd4iwRERGVDgMUC13XbRNi5X1d18fisIiIiCYUJskSERHRqChZkuz69etx3nnnYcqUKZgxYwa++MUv4s033zRt43K5bG//9E//pLYJBoNZ62+44YZCDoWIiIhOYgUFKB0dHVi+fDlefPFFbN26FQMDA1i0aBGOHDmittm/f7/ptnHjRrhcLlx77bWmfS1btsy03QMPPFCcV0RERETjXkE5KM8++6zp/qZNmzBjxgzs2LEDl1xyCQCgtrbWtM1vfvMbtLS0YM6cOablp5xySta2RERERMAIhxn39fUBAGpqamzX/+Uvf8EzzzyD22+/PWvd5s2bMX36dJx11lm44447cOjQIcfnSafT6O/vN92IiIjo5DXsUTxCCKxevRoXX3wx5s6da7vNww8/jClTpuCaa64xLb/55psRCARQW1uL3bt3Y+3atXjttdewdetW2/2sX78ed91113APlYiIiMaZYY/iWb58OZ555hls374ds2bNst3m05/+NC677DL89Kc/zbmvHTt2YOHChdixYwfOPffcrPXpdNpUar6/vx8NDQ0cxUNERDSOFDKKZ1gtKCtXrsTTTz+N559/3jE46ezsxJtvvonHH398yP2de+65qKqqwp49e2wDFI/HA4/HM5xDJSIionGooABFCIGVK1fiySefRDKZRCAQcNz2wQcfxIIFCzBv3rwh9/v666/j+PHjqKurK+RwiIiI6CRVUICyfPlyPPbYY/jNb36DKVOmoKenBwDg9XoxadIktV1/fz9+9atf4d57783ax9tvv43NmzfjiiuuwPTp0/HGG29gzZo1mD9/Pi666KIRvhwiIiI6GRQ0iufnP/85+vr6EAwGUVdXp27WbpwtW7ZACIEbb7wxax9utxvt7e24/PLLceaZZ+Lb3/42Fi1ahOeeew6apo3s1RAREdFJgaXuiYiIaFSUrNQ9ERER0WhggEJERERlhwEKERERlR0GKHmIRqOIx+O26+LxOKLR6OgeEBER0UmOAUoeNE1DJBLJClLi8TgikQhHHxERERXZsOfimUjC4TAAIBKJqPsyOInFYmo9ERERFQeHGRdABiVutxuZTIbBCRERUQEKuX4zQCmQx+NBJpOB2+02TWBIREREubEOSonE43EVnGQyGcfEWSIiIhoZBih5MuacpNNpxGIx28RZIiIiGjkmyebBLiHWLnGWiIiIioMBSh50XbdNiJX3dV0fi8MiIiI6aTFJloiIiEYFk2RHiJVjiYiIxhYDFBuycmxLS4spUDFWjmWgQkREVDrMQbFhTIBNJpNquUyUtf6fiIiIios5KDnIFhPJGpxw5A4REVH+WEm2iGTlWAAscU9ERDQCTJItEmPlWADq/wxOiIiISosBigNjcbY777xTLWeJeyIiotJjgGLDGJwAH+ecGO8zSCEiIiodjuKxISvHAvYJsYlEgiXuiYiISohJsjlEo1FommYbhMTjcei6zlooREREeeIoHiIiIio7HMVDRERE4xoDFAecj4eIiGjsMEnWQUdHhypzb8xBkSN8gsHg2BwYERHRBMAWFAehUAiAeUixsfS9XE9ERETFxyTZHIwBiSxzD4Cl7omIiIaBSbJFEg6HVT0UBidERESjhwEKERERlZ2CApT169fjvPPOw5QpUzBjxgx88YtfxJtvvmna5pZbboHL5TLdPve5z5m2SafTWLlyJaZPn47JkyfjqquuwrvvvjvyV1Nk1i4egGXuiYiIRkNBAUpHRweWL1+OF198EVu3bsXAwAAWLVqEI0eOmLb7/Oc/j/3796vb7373O9P6VatW4cknn8SWLVuwfft2HD58GEuWLIGu6yN/RUViDE5isRjS6TTn4iEiIhotYgQOHDggAIiOjg61bOnSpeLqq692fExvb6+oqqoSW7ZsUcvee+89UVFRIZ599tm8nrevr08AEH19fcM+9qEEg0EBQMRiMdPyWCwmAIhgMFiy5yYiIjoZFXL9HlEdlL6+PgBATU2NaXkymcSMGTPg8/nQ3NyMf/zHf8SMGTMAADt27MDx48exaNEitX19fT3mzp2LF154AZdffnnW86TTaaTTaXW/v79/JIedl+bmZoRCoayEWHm/nFp7iIiITjbDHmYshMDVV1+NgwcPorOzUy1//PHH8YlPfAKNjY1IpVIIh8MYGBjAjh074PF48Nhjj+HWW281BRwAsGjRIgQCATzwwANZzxWNRnHXXXdlLedcPERERONHIcOMh92CsmLFCvzxj3/E9u3bTcuvv/569f+5c+di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EYvFEIlE4PF4GJwQERGNoYIDlCNHjmDevHm4//77s9Z9+OGH2LlzJ8LhMHbu3IknnngCf/7zn3HVVVdlbbts2TLs379f3R544IHhvYIiCofDcLvdyGQycLvdDE6IiIjGSMFdPIsXL8bixYtt13m9XmzdutW07Kc//Sk++9nP4p133sHs2bPV8lNOOQW1tbWFPn1JxeNxFZxkMhnE43EGKURERGOg5EmyfX19cLlc8Pl8puWbN2/G9OnTcdZZZ+GOO+7AoUOHHPeRTqfR399vuhWbMecknU6r7h6n0T1ERERUOiVNkj127Bi+973v4aabbsLUqVPV8ptvvhmBQAC1tbXYvXs31q5di9deey2r9UVav3497rrrrpIdp11CrF3iLBEREY2OkgUox48fxw033IDBwUH87Gc/M61btmyZ+v/cuXNxxhlnYOHChdi5cyfOPffcrH2tXbsWq1evVvf7+/vR0NBQtGPVdd02IVbe13W9aM9FREREQytJgHL8+HFcd911SKVSSCQSptYTO+eeey6qqqqwZ88e2wDF4/HA4/GU4lABIGchNracEBERjb6iBygyONmzZw+2bduGU089dcjHvP766zh+/Djq6uqKfThEREQ0DhUcoBw+fBhvvfWWup9KpfDqq6+ipqYG9fX1+PKXv4ydO3fit7/9LXRdR09PDwCgpqYGbrcbb7/9NjZv3owrrrgC06dPxxtvvIE1a9Zg/vz5uOiii4r3yoiIiGjccgkhRCEPSCaTaGlpyVq+dOlSRKNRBAIB28dt27YNwWAQ+/btw1e+8hXs3r0bhw8fRkNDA77whS+gra0NNTU1eR1Df38/vF4v+vr6huw+IiIiovJQyPW74AClHDBAISIiGn8KuX5zskAiIiIqOwxQPhKNRh2LssXj8ZwjfYiIiKi4GKB8RNM028qxsoibpmljdGREREQTT0kryY4ndpVj7SrMEhERUekxSdZCBiVywkAGJ0RERMXBUTzDEI1GoWkawuEwPB6PmtU4nU4jHo9D13XmoRAREY0AR/EMg8xBaW1tVcFJJpNBa2src1CIiIhGGVtQDFpbW5FIJBAKhdDe3p51n4iIiIaPLSjDEI/HVTCSSCTg8XhM952GIBMREVHxMUD5iK7riMViaG9vV907brcb7e3tiMVi0HV9rA+RiIhowuAw44/IBNh4PG7KQYnH4xzFQ0RENMrYgmJgrHuSTqcRi8Vsi7cRERFRabEF5SN2RdnsircRERFR6TFA+YjMQbEGIfI+c1CIiIhGD4cZExER0ajgMGMiIiIa1xigEBERUdlhgEJERERlhwGKQTQadRxSHI/HOVkgERHRKGGAYiAnDLQGKXIIMicMJCIiGh0cZmxgV/fErj4KERERlRaHGduQQYksd8/ghIiIaOQKuX4zQHHg8XjUnDzpdLokz0FERDSRsA7KCNlNGEhERESjhwGKBScMJCIiGntMksWJ4cVyhI5dQmwwGOSEgURERKOIOSj4uNUkGAwiFApB1/WsgAWAWq7rOmuiEBERFaiQ6zdbUGAeXhwKhVQ9FACm1hRj9w8RERGVDgOUjxiDFLfbnbWe9VCIiIhGD7t4LIzDi++8807WQyEiIioSDjMeJuvwYgDq/263m8EJERHRKCk4QHn++edx5ZVXor6+Hi6XC0899ZRpvRAC0WgU9fX1mDRpEoLBIF5//XXTNul0GitXrsT06dMxefJkXHXVVXj33XdH9EJGyml4MeuhEBERjb6CA5QjR45g3rx5uP/++23X33PPPbjvvvtw//3346WXXkJtbS0uu+wyHDp0SG2zatUqPPnkk9iyZQu2b9+Ow4cPY8mSJdB1ffivZASGyi+58847WQ+FiIhoFBWcJLt48WIsXrzYdp0QAj/+8Y/xgx/8ANdccw0A4OGHH8bMmTPx2GOP4etf/zr6+vrw4IMP4pe//CUuvfRSAMCjjz6KhoYGPPfcc7j88stH8HKGR9f1nKN1jMOKWQ+FiIio9Io6iieVSqGnpweLFi1SyzweD5qbm/HCCy/g61//Onbs2IHjx4+btqmvr8fcuXPxwgsvjEmAYq1pYg1YJHl/rFp6iIiIJoqiBig9PT0AgJkzZ5qWz5w5E93d3Wobt9uNadOmZW0jH2+VTqdNE/b19/cX87Cz5CrCxpYTIiKi0ivJKB6Xy2W6L4TIWmaVa5v169fD6/WqW0NDQ9GOlYiIiMpPUQOU2tpaAMhqCTlw4IBqVamtrUUmk8HBgwcdt7Fau3Yt+vr61G3fvn3FPGwiIiIqM0UNUAKBAGpra7F161a1LJPJoKOjAxdeeCEAYMGCBaiqqjJts3//fuzevVttY+XxeDB16lTTjYiIiE5eBeegHD58GG+99Za6n0ql8Oqrr6KmpgazZ8/GqlWrcPfdd+OMM87AGWecgbvvvhunnHIKbrrpJgCA1+vF7bffjjVr1uDUU09FTU0N7rjjDpx99tlqVA8RERFNbAUHKC+//DJaWlrU/dWrVwMAli5dioceegjf/e53cfToUXzrW9/CwYMHcf755+P3v/89pkyZoh7zox/9CJWVlbjuuutw9OhRtLa24qGHHlIzCBMREdHExrl4iIiIaFRwLh4iIiIa1xigEBERUdlhgEJERERlhwEKERERlZ2ilrofLTKvt9Ql74mIiKh45HU7n/E54zJAOXToEACw5D0REdE4dOjQIXi93pzbjMthxoODg3j//fcxZcqUIef4KUR/fz8aGhqwb98+Dl8eJ3jOxieet/GH52z8KcdzJoTAoUOHUF9fj4qK3Fkm47IFpaKiArNmzSrZ/llOf/zhORufeN7GH56z8afcztlQLScSk2SJiIio7DBAISIiorLDAMXA4/Ggra0NHo9nrA+F8sRzNj7xvI0/PGfjz3g/Z+MySZaIiIhObmxBISIiorLDAIWIiIjKDgMUIiIiKjsMUIiIiKjsMED5yM9+9jMEAgFUV1djwYIF6OzsHOtDIoNoNAqXy2W61dbWqvVCCESjUdTX12PSpEkIBoN4/fXXx/CIJ57nn38eV155Jerr6+FyufDUU0+Z1udzjtLpNFauXInp06dj8uTJuOqqq/Duu++O4quYWIY6Z7fcckvW9+5zn/ucaRues9G1fv16nHfeeZgyZQpmzJiBL37xi3jzzTdN25ws3zUGKAAef/xxrFq1Cj/4wQ/wyiuvoKmpCYsXL8Y777wz1odGBmeddRb279+vbrt27VLr7rnnHtx33324//778dJLL6G2thaXXXaZmreJSu/IkSOYN28e7r//ftv1+ZyjVatW4cknn8SWLVuwfft2HD58GEuWLIGu66P1MiaUoc4ZAHz+8583fe9+97vfmdbznI2ujo4OLF++HC+++CK2bt2KgYEBLFq0CEeOHFHbnDTfNUHis5/9rPjGN75hWvbpT39afO973xujIyKrtrY2MW/ePNt1g4ODora2VmzYsEEtO3bsmPB6veIXv/jFKB0hGQEQTz75pLqfzznq7e0VVVVVYsuWLWqb9957T1RUVIhnn3121I59orKeMyGEWLp0qbj66qsdH8NzNvYOHDggAIiOjg4hxMn1XZvwLSiZTAY7duzAokWLTMsXLVqEF154YYyOiuzs2bMH9fX1CAQCuOGGG7B3714AQCqVQk9Pj+kcejweNDc38xyWiXzO0Y4dO3D8+HHTNvX19Zg7dy7P4xhKJpOYMWMGPvWpT2HZsmU4cOCAWsdzNvb6+voAADU1NQBOru/ahA9Q/u///g+6rmPmzJmm5TNnzkRPT88YHRVZnX/++XjkkUfwX//1X/jXf/1X9PT04MILL8QHH3ygzhPPYfnK5xz19PTA7XZj2rRpjtvQ6Fq8eDE2b96MRCKBe++9Fy+99BJCoRDS6TQAnrOxJoTA6tWrcfHFF2Pu3LkATq7v2riczbgUXC6X6b4QImsZjZ3Fixer/5999tm44IILcPrpp+Phhx9WSXs8h+VvOOeI53HsXH/99er/c+fOxcKFC9HY2IhnnnkG11xzjePjeM5Gx4oVK/DHP/4R27dvz1p3MnzXJnwLyvTp06FpWlbUeODAgawIlMrH5MmTcfbZZ2PPnj1qNA/PYfnK5xzV1tYik8ng4MGDjtvQ2Kqrq0NjYyP27NkDgOdsLK1cuRJPP/00tm3bhlmzZqnlJ9N3bcIHKG63GwsWLMDWrVtNy7du3YoLL7xwjI6KhpJOp/E///M/qKurQyAQQG1trekcZjIZdHR08ByWiXzO0YIFC1BVVWXaZv/+/di9ezfPY5n44IMPsG/fPtTV1QHgORsLQgisWLECTzzxBBKJBAKBgGn9SfVdG7P03DKyZcsWUVVVJR588EHxxhtviFWrVonJkyeLrq6usT40+siaNWtEMpkUe/fuFS+++KJYsmSJmDJlijpHGzZsEF6vVzzxxBNi165d4sYbbxR1dXWiv79/jI984jh06JB45ZVXxCuvvCIAiPvuu0+88sororu7WwiR3zn6xje+IWbNmiWee+45sXPnThEKhcS8efPEwMDAWL2sk1quc3bo0CGxZs0a8cILL4hUKiW2bdsmLrjgAnHaaafxnI2hb37zm8Lr9YpkMin279+vbh9++KHa5mT5rjFA+cg///M/i8bGRuF2u8W5556rhmxRebj++utFXV2dqKqqEvX19eKaa64Rr7/+ulo/ODgo2traRG1trfB4POKSSy4Ru3btGsMjnni2bdsmAGTdli5dKoTI7xwdPXpUrFixQtTU1IhJkyaJJUuWiHfeeWcMXs3EkOucffjhh2LRokXik5/8pKiqqhKzZ88WS5cuzTofPGejy+58ARCbNm1S25ws3zWXEEKMdqsNERERUS4TPgeFiIiIyg8DFCIiIio7DFCIiIio7DBAISIiorLDAIWIiIjKDgMUIiIiKjsMUIiIiKjsMEAhIiKissMAhYiIiMoOAxQiIiIqOwxQiIiIqOwwQCEiIqKy8/8BgC5cVmtgBlkAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sellers_spec = [('ZIP', 40)]\n", "buyers_spec = sellers_spec\n", "traders_spec = {'sellers':sellers_spec, 'buyers':buyers_spec}\n", "\n", "order_interval = 10\n", "order_sched = {'sup': supply_schedule, 'dem': demand_schedule,\n", " 'interval': order_interval, 'timemode': 'drip-poisson'}\n", "\n", "n_sessions = 10\n", "\n", "x = np.empty(0)\n", "y = np.empty(0)\n", "\n", "for sess in range(n_sessions):\n", " trial_id = 'smith_chart_' + str(sess)\n", "\n", " market_session(trial_id, start_time, end_time, traders_spec, order_sched, dump_flags, verbose)\n", "\n", " prices_fname = trial_id + '_tape.csv'\n", " with open(prices_fname, newline='') as csvfile:\n", " reader = csv.reader(csvfile)\n", " for row in reader:\n", " time = float(row[1])\n", " price = float(row[2])\n", " x = np.append(x,time)\n", " y = np.append(y,price)\n", "\n", "plt.plot(x, y, 'x', color='black');" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "And let's introduce some variety into the population of traders, have a mix of different strategies." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "pycharm": { "name": "#%%\n" }, "tags": [] }, "outputs": [ { "data": { "image/png": "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SqXSSbEAEdIBAABAUsBJNgJYfOgESAcAAABpAEM8vwOLD50A6QAAACANYIhHgMWHToB0AAAAEDdYqK1KsPjQCZAOAAAA4gQ+KFWAxYdOgHQAAABQTyCgMLD40AmQDgAAAOoNnGR/BxYfOgHSAQAAQBqAgPI7sPjQCZAOAAAA0gCcZAEAAABQE+AkG4GhoSGrj8Xw8DBWTwUAAABqCASU34EVVAEAAID0AB+U34EVVAEAAID0AB8UAVZQBQAAAJIhUR+U5557jlatWkXd3d2UyWToqaeest574403UiaToW9961tl5ycmJujWW2+l0047jWbPnk2rV6+mPXv2hA1KIvT393vCSWtrK4QTAAAAoA6EFlDeffddWrx4MT300EO+9z311FP0s5/9jLq7uyuu3XbbbbRhwwZav349vfDCC3TkyBFauXJlKqawYgVVAAAAIAWoKiAitWHDhorze/bsUWeddZbauXOn6unpUQ8++KB37dChQ6qlpUWtX7/eO7d3717V1NSkNm7c6PTd8fFxRURqfHy8muBXUCgUFBGpQqFg/A0AAACA6ITpv2N3kp2amqIvfvGLdNddd9H5559fcX3r1q10/PhxuuKKK7xz3d3dtGjRInrxxRdpxYoVFc9MTEyUbVZ3+PDhuIONFVQBAACAFBG7gHL//ffTjBkz6M/+7M+M1w8cOECtra00b968svMLFiygAwcOGJ9Zu3YtffOb34w7qGVgBVUAAAAgPcQqoGzdupW+/e1v07Zt2yiTyYR6Villfebuu++m22+/3ft9+PBhOvvss6sKq8RvITZYTgAAAIDaEutCbc8//zwdPHiQzjnnHJoxYwbNmDGD3nzzTbrjjjuot7eXiIi6urpocnKS3n777bJnDx48SAsWLDC+t62tjebOnVt2AAAAAGD6EquA8sUvfpFefvll2r59u3d0d3fTXXfdRT/5yU+IiGjJkiXU0tJCmzZt8p7bv38/7dy5ky699NI4gwMAAACABiX0EM+RI0fo9ddf936PjY3R9u3bqbOzk8455xyaP39+2f0tLS3U1dVF5513HhERtbe30/XXX0933HEHzZ8/nzo7O+nOO++kCy64gJYvX15ldAAAAAAwHQgtoPzjP/4jLV261PutfUOuvfZaevTRR53e8eCDD9KMGTPos5/9LB09epSWLVtGjz76KPa7AQAAAAARYal7AAAAANSIRJe6n84MDQ1ZV44dHh72nekDAAAAgPiAgMJobm6mgYGBCiFFL+KGISgAAACgNsS+UFsjY1o51rTCLAAAAACSBT4oBrRQojcMhHACAAAAVE+Y/hsCioW2tjZvV2O+DxAAAAAAogEn2SoZHh72hJPJyUmr4ywAAAAAkgE+KIKlS5fS6OioN6yjh3s0pVIJs3kAAACAhIGAwhgeHqbR0dGyc9JxtlAo1DpYAAAAwEkHBBRGqVTyBBA+k0eTy+XgLAsAAADUADjJWsBMHgAAACBe4CQbEb6SbH9/vyectLa2etcBAAAAkDwY4mHolWQ1fCaPXqwNAAAAAMkDAYVhc4jlQgsAAAAAkgcCig/33HOP539CZHacBQAAAED8QEAR6Jk8WjhpbW0tE0hKpVIdQwcAAACcHMBJVqAdYU0ryfb398NRFgAAAKgBEFAEfPfiiYkJKhQKNDAwgOXuAQAAgBqCIR4GF070sI50nIX/CQAAAJA8EFAY2v9ECiH6N/xPAAAAgNqAlWQBAAAAUBOwkiwAAAAAGhoIKAAAAABIHRBQAAAAAJA6IKAAAAAAIHVAQAEAAABA6oCAAgAAAIDUAQEFAAAAAKkDAgoAAAAAUgcEFAAAAACkDggoAAAAAEgdEFAAAAAAkDpCCyjPPfccrVq1irq7uymTydBTTz3lXTt+/Dh97WtfowsuuIBmz55N3d3ddM0119C+ffvK3jExMUG33nornXbaaTR79mxavXo17dmzp+rIAAAAAGB6EFpAeffdd2nx4sX00EMPVVx77733aNu2bdTf30/btm2jJ598kl599VVavXp12X233XYbbdiwgdavX08vvPACHTlyhFauXIndggEAAABARFXuZpzJZGjDhg101VVXWe/5h3/4B/rEJz5Bb775Jp1zzjk0Pj5Op59+Ov3t3/4tXX311UREtG/fPjr77LPpmWeeoRUrVgR+F7sZAwAAAI1HqnYzHh8fp0wmQx0dHUREtHXrVjp+/DhdccUV3j3d3d20aNEievHFF43vmJiYoMOHD5cdAAAAAJi+JCqgHDt2jP7iL/6C/v2///eepHTgwAFqbW2lefPmld27YMECOnDggPE9a9eupfb2du84++yzkww2AAAAAOpMYgLK8ePH6XOf+xxNTU3Rww8/HHi/UooymYzx2t13303j4+PesXv37riDCwAAAIAUkYiAcvz4cfrsZz9LY2NjtGnTprJxpq6uLpqcnKS333677JmDBw/SggULjO9ra2ujuXPnlh0AAAAAmL7ELqBo4eS1116jZ599lubPn192fcmSJdTS0kKbNm3yzu3fv5927txJl156adzBAQAAAEADMiPsA0eOHKHXX3/d+z02Nkbbt2+nzs5O6u7upn/7b/8tbdu2jX70ox9RqVTy/Eo6OzuptbWV2tvb6frrr6c77riD5s+fT52dnXTnnXfSBRdcQMuXL48vZgAAAABoWEJPMx4dHaWlS5dWnL/22mtpaGiIstms8bnNmzdTLpcjohPOs3fddRf94Ac/oKNHj9KyZcvo4YcfdnZ+xTRjAAAAoPEI039XtQ5KvYCAAgAAADQeqVoHBQAAAAAgLBBQAAAAAJA6IKAAAAAAIHVAQAEAAABA6oCAAgAAAIDUAQEFAAAAAKkDAgoAAAAAUgcEFAAAAACkDggoAAAAAEgdEFAAAAAAkDogoAAAAAAgdUBAAQAAAEDqgIACAAAAgNQBAQUAAAAAqQMCCgAAAABSBwQUAAAAAKQOCCgAAAAASB0QUAAAAACQOiCgAAAAACB1QEABAAAAQOqAgAIAAACA1AEBBQAAAACpAwIKAAAAAFIHBBQAAAAApA4IKAAAAABIHRBQAAAAAJA6IKAAAAAAIHVAQAEAAABA6oCAAgAAAIDUAQEFAAAAAKkDAgoAAAAAUkdoAeW5556jVatWUXd3N2UyGXrqqafKriulaGhoiLq7u2nWrFmUy+XoF7/4Rdk9ExMTdOutt9Jpp51Gs2fPptWrV9OePXuqiggAAAAApg+hBZR3332XFi9eTA899JDx+n/6T/+JHnjgAXrooYfoH/7hH6irq4suv/xyeuedd7x7brvtNtqwYQOtX7+eXnjhBTpy5AitXLmSSqVS9JgAAAAAYNqQUUqpyA9nMrRhwwa66qqriOiE9aS7u5tuu+02+trXvkZEJ6wlCxYsoPvvv59uvPFGGh8fp9NPP53+9m//lq6++moiItq3bx+dffbZ9Mwzz9CKFSsCv3v48GFqb2+n8fFxmjt3btTgAwAAAKCGhOm/Y/VBGRsbowMHDtAVV1zhnWtra6O+vj568cUXiYho69atdPz48bJ7uru7adGiRd49komJCTp8+HDZAQAAAIDpS6wCyoEDB4iIaMGCBWXnFyxY4F07cOAAtba20rx586z3SNauXUvt7e3ecfbZZ8cZbBoaGqLh4WHjteHhYRoaGor1ewAAAADwJ5FZPJlMpuy3UqrinMTvnrvvvpvGx8e9Y/fu3bGFlYioubmZBgYGKoSU4eFhGhgYoObm5li/BwAAAAB/ZsT5sq6uLiI6YSU588wzvfMHDx70rCpdXV00OTlJb7/9dpkV5eDBg3TppZca39vW1kZtbW1xBrWM/v5+IiIaGBjwfmvhpFAoeNcBAAAAUBtitaBks1nq6uqiTZs2eecmJydpy5YtnvCxZMkSamlpKbtn//79tHPnTquAUgv6+/upUCjQwMAAtbW1QTgBAAAA6kjoWTxHjhyh119/nYiIPv7xj9MDDzxAS5cupc7OTjrnnHPo/vvvp7Vr19K6devoQx/6EN177700OjpKr7zyCs2ZM4eIiG6++Wb60Y9+RI8++ih1dnbSnXfeSf/yL/9CW7dudRpOSXIWT1tbG01OTlJraytNTEzE+m4AAADgZCZM/x16iOcf//EfaenSpd7v22+/nYiIrr32Wnr00Ufpq1/9Kh09epS+/OUv09tvv02XXHIJ/fSnP/WEEyKiBx98kGbMmEGf/exn6ejRo7Rs2TJ69NFH6+7rMTw87Aknk5OTNDw8DAsKAAAAUAeqWgelXiRhQZE+J/BBAQAAAOIlUQvKdMQkjJgcZwEAAABQGyCgEFGpVDJaSvRvLMEPAAAA1BYM8QAAAACgJtRtqXsAAAAAgDiAgAIAAACA1AEBhYE9eQAAAIB0AAGFgT15AAAAgHSAWTwM7MkDAAAApAPM4jGghRK9oiyEEwAAAKB6wvTfEFAsYE8eAAAAIF4wzbhKTHvyAAAAAKB2QEARcJ+TiYkJKhQKRsdZAAAAACQHnGQZ2JMHAAAASAcQUBjYkwcAAABIB3CSBQAAAEBNgJMsAAAAABoaCCgAAAAASB0QUAAAAACQOiCgAAAAACB1QEABAAAAQOqAgAIAAACA1AEBBQAAAACpAwIKAAAAAFIHBBQAAAAApA4IKAAAAABIHQ25F49enf/w4cN1DgkAAAAAXNH9tssuOw0poLzzzjtERHT22WfXOSQAAAAACMs777xD7e3tvvc05GaBU1NTtG/fPpozZw5lMpnY3nv48GE6++yzaffu3diEMACkVTiQXu4grdxBWoUD6eVOUmmllKJ33nmHuru7qanJ38ukIS0oTU1NtHDhwsTeP3fuXBReR5BW4UB6uYO0cgdpFQ6klztJpFWQ5UQDJ1kAAAAApA4IKAAAAABIHRBQGG1tbTQ4OEhtbW31DkrqQVqFA+nlDtLKHaRVOJBe7qQhrRrSSRYAAAAA0xtYUAAAAACQOiCgAAAAACB1QEABAAAAQOqAgAIAAACA1AEBBQAAAACpAwLK73j44Ycpm83SzJkzacmSJfT888/XO0g157nnnqNVq1ZRd3c3ZTIZeuqpp8quK6VoaGiIuru7adasWZTL5egXv/hF2T0TExN066230mmnnUazZ8+m1atX0549e2oYi9qwdu1auvjii2nOnDl0xhln0FVXXUWvvPJK2T1Irw/4zne+QxdeeKG3KuUnP/lJ+j//5/9415FWdtauXUuZTIZuu+027xzS6wOGhoYok8mUHV1dXd51pFU5e/fupS984Qs0f/58OuWUU+hjH/sYbd261bueqvRSQK1fv161tLSo733ve+qXv/yl+spXvqJmz56t3nzzzXoHraY888wz6utf/7p64oknFBGpDRs2lF2/77771Jw5c9QTTzyhduzYoa6++mp15plnqsOHD3v33HTTTeqss85SmzZtUtu2bVNLly5VixcvVu+//36NY5MsK1asUOvWrVM7d+5U27dvV5/5zGfUOeeco44cOeLdg/T6gKefflr9+Mc/Vq+88op65ZVX1F/+5V+qlpYWtXPnTqUU0srGz3/+c9Xb26suvPBC9ZWvfMU7j/T6gMHBQXX++eer/fv3e8fBgwe960irD3jrrbdUT0+P+tKXvqR+9rOfqbGxMfXss8+q119/3bsnTekFAUUp9YlPfELddNNNZec+/OEPq7/4i7+oU4jqjxRQpqamVFdXl7rvvvu8c8eOHVPt7e3qu9/9rlJKqUOHDqmWlha1fv167569e/eqpqYmtXHjxpqFvR4cPHhQEZHasmWLUgrp5cK8efPU//gf/wNpZeGdd95RH/rQh9SmTZtUX1+fJ6AgvcoZHBxUixcvNl5DWpXzta99TV122WXW62lLr5N+iGdycpK2bt1KV1xxRdn5K664gl588cU6hSp9jI2N0YEDB8rSqa2tjfr6+rx02rp1Kx0/frzsnu7ublq0aNG0T8vx8XEiIurs7CQipJcfpVKJ1q9fT++++y598pOfRFpZuOWWW+gzn/kMLV++vOw80quS1157jbq7uymbzdLnPvc5euONN4gIaSV5+umn6aKLLqJ/9+/+HZ1xxhn08Y9/nL73ve9519OWXie9gPLP//zPVCqVaMGCBWXnFyxYQAcOHKhTqNKHTgu/dDpw4AC1trbSvHnzrPdMR5RSdPvtt9Nll11GixYtIiKkl4kdO3bQqaeeSm1tbXTTTTfRhg0b6KMf/SjSysD69etp27ZttHbt2oprSK9yLrnkEvr+979PP/nJT+h73/seHThwgC699FL6l3/5F6SV4I033qDvfOc79KEPfYh+8pOf0E033UR/9md/Rt///veJKH1la0asb2tgMplM2W+lVMU5EC2dpntarlmzhl5++WV64YUXKq4hvT7gvPPOo+3bt9OhQ4foiSeeoGuvvZa2bNniXUdanWD37t30la98hX7605/SzJkzrfchvU7wb/7Nv/H+v+CCC+iTn/wk/d7v/R79z//5P+mP/uiPiAhppZmamqKLLrqI7r33XiIi+vjHP06/+MUv6Dvf+Q5dc8013n1pSa+T3oJy2mmnUXNzc4Xkd/DgwQop8mRGe8X7pVNXVxdNTk7S22+/bb1nunHrrbfS008/TZs3b6aFCxd655FelbS2ttLv//7v00UXXURr166lxYsX07e//W2klWDr1q108OBBWrJkCc2YMYNmzJhBW7Zsob/6q7+iGTNmePFFepmZPXs2XXDBBfTaa6+hbAnOPPNM+uhHP1p27iMf+Qjt2rWLiNLXbp30AkpraystWbKENm3aVHZ+06ZNdOmll9YpVOkjm81SV1dXWTpNTk7Sli1bvHRasmQJtbS0lN2zf/9+2rlz57RLS6UUrVmzhp588kkqFouUzWbLriO9glFK0cTEBNJKsGzZMtqxYwdt377dOy666CL6kz/5E9q+fTude+65SC8fJiYm6Fe/+hWdeeaZKFuCf/Wv/lXFcgivvvoq9fT0EFEK261YXW4bFD3N+JFHHlG//OUv1W233aZmz56tfvvb39Y7aDXlnXfeUS+99JJ66aWXFBGpBx54QL300kvedOv77rtPtbe3qyeffFLt2LFDff7znzdOP1u4cKF69tln1bZt21Q+n5+W0/Vuvvlm1d7erkZHR8umN7733nvePUivD7j77rvVc889p8bGxtTLL7+s/vIv/1I1NTWpn/70p0oppFUQfBaPUkgvzh133KFGR0fVG2+8of7+7/9erVy5Us2ZM8drv5FWH/Dzn/9czZgxQ/3H//gf1WuvvaYee+wxdcopp6j/9b/+l3dPmtILAsrv+K//9b+qnp4e1draqv7wD//Qmy56MrF582ZFRBXHtddeq5Q6MQVtcHBQdXV1qba2NvXpT39a7dixo+wdR48eVWvWrFGdnZ1q1qxZauXKlWrXrl11iE2ymNKJiNS6deu8e5BeH3Ddddd59ev0009Xy5Yt84QTpZBWQUgBBen1AXqdjpaWFtXd3a3++I//WP3iF7/wriOtyvnhD3+oFi1apNra2tSHP/xh9d//+38vu56m9MoopVS8NhkAAAAAgOo46X1QAAAAAJA+IKAAAAAAIHVAQAEAAABA6oCAAgAAAIDUAQEFAAAAAKkDAgoAAAAAUgcEFAAAAACkDggoAAAAAEgdEFAAAAAAkDogoAAAAAAgdUBAAQAAAEDq+P/QogXRltvOxwAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "sellers_spec = [('ZIP', 10), ('ZIC', 10), ('SHVR', 10), ('GVWY', 10)]\n", "buyers_spec = sellers_spec\n", "traders_spec = {'sellers':sellers_spec, 'buyers':buyers_spec}\n", "\n", "order_interval = 10\n", "order_sched = {'sup': supply_schedule, 'dem': demand_schedule,\n", " 'interval': order_interval, 'timemode': 'drip-poisson'}\n", "\n", "n_sessions = 1\n", "\n", "x = np.empty(0)\n", "y = np.empty(0)\n", "\n", "for sess in range(n_sessions):\n", " trial_id = 'smith_chart_' + str(sess)\n", "\n", " market_session(trial_id, start_time, end_time, traders_spec, order_sched, dump_flags, verbose)\n", "\n", " prices_fname = trial_id + '_tape.csv'\n", " with open(prices_fname, newline='') as csvfile:\n", " reader = csv.reader(csvfile)\n", " for row in reader:\n", " time = float(row[1])\n", " price = float(row[2])\n", " x = np.append(x,time)\n", " y = np.append(y,price)\n", "\n", "plt.plot(x, y, 'x', color='black');" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "Everything we've done so far has had a single static supply and demand schedule. But in real markets, the supply and demand schedules are constantly varying -- often by only small amounts, but occasionally there might be large \"shock\" changes.\n", "\n", "Let's introduce a shock, part-way through the session." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "pycharm": { "name": "#%%\n" }, "tags": [] }, "outputs": [ { "data": { "image/png": "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eFoN5/W2Yk4eAAAAALQG7rf9jM+ZlwLl2LFjREQodw8AAADMQ44dO0aJRMJ1m3k5zHhubo5eeOEFWrhwoXH+nnrgOX4OHTqE4csewFbBgL38A1v5B7YKBuzln1bZSghBx44do5UrV1Jfn3uWybz0oPT19dGqVatadnzM9+Mf2CoYsJd/YCv/wFbBgL380wpbeXlOGCTJAgAAACB0QKAAAAAAIHRAoEjE43EaGxujeDze6aaEHtgqGLCXf2Ar/8BWwYC9/BMGW83LJFkAAAAAdDfwoAAAAAAgdECgAAAAACB0QKAAAAAAIHRAoAAAAAAgdECg/J5vfvOblMlkaHBwkNauXUu7d+/udJM6wsMPP0yXX345rVy5kiKRCP3oRz9yfC+EoHw+TytXrqTTTjuNstksPfnkk45tyuUyXXPNNbR06VJasGABXXHFFfTcc8+18Spaz7Zt2+id73wnLVy4kJYtW0Yf/OAH6amnnnJsA1u9zq233koXXHCBXfRp/fr19OMf/9j+HrYys23bNopEInTttdfa62CvU+TzeYpEIo5laGjI/h52quX555+nj3/843TGGWfQ6aefThdeeCHt2bPH/j5UNhNA7NixQwwMDIhvf/vb4pe//KX44he/KBYsWCCeeeaZTjet7dx///3i7/7u78Rdd90liEjcc889ju+3b98uFi5cKO666y6xb98+ceWVV4oVK1aI2dlZe5urrrpKnHXWWWLnzp1i7969YuPGjWLNmjXi5MmTbb6a1nHppZeK22+/Xezfv188/vjj4v3vf78455xzxMsvv2xvA1u9zr333ivuu+8+8dRTT4mnnnpK3HjjjWJgYEDs379fCAFbmfjZz34m0um0uOCCC8QXv/hFez3sdYqxsTHx9re/XRw+fNhejh49an8POzn53e9+J1KplPjkJz8p/vu//1tMTU2Jhx56SPz617+2twmTzSBQhBB/9Ed/JK666irHure+9a3iy1/+codaFA5UgTI3NyeGhobE9u3b7XUnTpwQiURCfOtb3xJCCDE9PS0GBgbEjh077G2ef/550dfXJx544IG2tb3dHD16VBCRmJycFELAVn5YvHix+M53vgNbGTh27Jg477zzxM6dO8Xw8LAtUGCv1xkbGxNr1qzRfgc71fK3f/u34uKLLzZ+Hzab9XyIp1Kp0J49e2jTpk2O9Zs2baJHH320Q60KJ1NTU3TkyBGHreLxOA0PD9u22rNnD7322muObVauXEmrV6/uanvOzMwQEdGSJUuICLZyo1qt0o4dO+iVV16h9evXw1YGrr76anr/+99Pl1xyiWM97OXk6aefppUrV1Imk6E//dM/pQMHDhAR7KTj3nvvpXXr1tFHPvIRWrZsGb3jHe+gb3/72/b3YbNZzwuU3/72t1StVmn58uWO9cuXL6cjR450qFXhhO3hZqsjR45QLBajxYsXG7fpNoQQdN1119HFF19Mq1evJiLYSse+ffvoDW94A8Xjcbrqqqvonnvuobe97W2wlYYdO3bQ3r17adu2bTXfwV6v8653vYvuuOMO+slPfkLf/va36ciRI3TRRRfRiy++CDtpOHDgAN1666103nnn0U9+8hO66qqr6K//+q/pjjvuIKLwPVvzcjbjVhCJRByfhRA168Ap6rFVN9tz69at9MQTT9AjjzxS8x1s9Tpvectb6PHHH6fp6Wm66667aMuWLTQ5OWl/D1ud4tChQ/TFL36RHnzwQRocHDRuB3sRXXbZZfa/zz//fFq/fj298Y1vpO9///v07ne/m4hgJ5m5uTlat24d3XLLLURE9I53vIOefPJJuvXWW+kTn/iEvV1YbNbzHpSlS5dSNBqtUX5Hjx6tUZG9DmfHu9lqaGiIKpUKvfTSS8ZtuolrrrmG7r33Xtq1axetWrXKXg9b1RKLxehNb3oTrVu3jrZt20Zr1qyhr3/967CVwp49e+jo0aO0du1a6u/vp/7+fpqcnKR/+qd/ov7+fvt6Ya9aFixYQOeffz49/fTTeK40rFixgt72trc51v3BH/wBPfvss0QUvt+tnhcosViM1q5dSzt37nSs37lzJ1100UUdalU4yWQyNDQ05LBVpVKhyclJ21Zr166lgYEBxzaHDx+m/fv3d5U9hRC0detWuvvuu6lUKlEmk3F8D1t5I4SgcrkMWymMjIzQvn376PHHH7eXdevW0Z//+Z/T448/Tueeey7sZaBcLtP//M//0IoVK/BcafjjP/7jmnIIv/rVryiVShFRCH+3mppyO0/hYcbf/e53xS9/+Utx7bXXigULFoiDBw92umlt59ixY+Kxxx4Tjz32mCAi8dWvflU89thj9pDr7du3i0QiIe6++26xb98+8bGPfUw7BG3VqlXioYceEnv37hWWZXXdsL3Pf/7zIpFIiImJCccQx1dffdXeBrZ6nRtuuEE8/PDDYmpqSjzxxBPixhtvFH19feLBBx8UQsBWXsijeISAvZjrr79eTExMiAMHDoif/vSnYvPmzWLhwoX2bzfs5ORnP/uZ6O/vF1/5ylfE008/LX7wgx+I008/Xdx55532NmGyGQTK7/nnf/5nkUqlRCwWE3/4h39oDxftNXbt2iWIqGbZsmWLEOLUMLSxsTExNDQk4vG4eM973iP27dvnOMbx48fF1q1bxZIlS8Rpp50mNm/eLJ599tkOXE3r0NmIiMTtt99ubwNbvc6nP/1p+//XmWeeKUZGRmxxIgRs5YUqUGCvU3CNjoGBAbFy5Urx4Q9/WDz55JP297BTLf/+7/8uVq9eLeLxuHjrW98q/uVf/sXxfZhsFhFCiOb6ZAAAAAAAGqPnc1AAAAAAED4gUAAAAAAQOiBQAAAAABA6IFAAAAAAEDogUAAAAAAQOiBQAAAAABA6IFAAAAAAEDogUAAAAAAQOiBQAAAAABA6IFAAAAAAEDogUAAAAAAQOiBQAAAAABA6/j8D+EPSL84oiAAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "shocked_range = (300, 400)\n", "shock_time = int(end_time / 2)\n", "\n", "supply_schedule = [ {'from':0, 'to':shock_time, 'ranges':[chart1_range], 'stepmode':'fixed'},\n", " {'from':shock_time, 'to':end_time, 'ranges':[shocked_range], 'stepmode':'fixed'},\n", " ]\n", "demand_schedule = supply_schedule\n", "\n", "sellers_spec = [('ZIP', 10), ('ZIC', 10), ('SHVR', 10), ('GVWY', 10)]\n", "buyers_spec = sellers_spec\n", "traders_spec = {'sellers':sellers_spec, 'buyers':buyers_spec}\n", "\n", "order_interval = 10\n", "order_sched = {'sup': supply_schedule, 'dem': demand_schedule,\n", " 'interval': order_interval, 'timemode': 'drip-poisson'}\n", "\n", "n_sessions = 1\n", "\n", "x = np.empty(0)\n", "y = np.empty(0)\n", "\n", "for sess in range(n_sessions):\n", " trial_id = 'smith_chart_' + str(sess)\n", "\n", " market_session(trial_id, start_time, end_time, traders_spec, order_sched, dump_flags, verbose)\n", "\n", " prices_fname = trial_id + '_tape.csv'\n", " with open(prices_fname, newline='') as csvfile:\n", " reader = csv.reader(csvfile)\n", " for row in reader:\n", " time = float(row[1])\n", " price = float(row[2])\n", " x = np.append(x,time)\n", " y = np.append(y,price)\n", "\n", "plt.plot(x, y, 'x', color='black');" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "That's it for now: this notebook has shown you how to set up BSE to run market sessions like Vernon Smith's first experiment in his landmark 1962 JPE paper, and then has shown you how to alter and extend the setup to take it closer to real-world market scenarios: a heterogeneous mix of trader-types; supply/demand assignments arriving randomly; and the supply and demand schedules altering during the session.\n", "\n", "## Further Reading\n", "\n", "Here are links to some recent research papers that use BSE...\n", "\n", "https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4153519 \n", "\n", "https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4154426 \n", "\n", "https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4070885\n", "\n", "https://arxiv.org/abs/2012.08840\n", "\n", "https://arxiv.org/abs/2012.00821\n", "\n", "https://arxiv.org/abs/2011.14346" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.5" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: LICENSE.md ================================================ Copyright (c) 2012 Dave Cliff Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (copied from http://opensource.org/licenses/mit-license.php) Last update: Dave Cliff, October 25th 2012. ================================================ FILE: Offsets_BTC_USD/empty_file ================================================ ================================================ FILE: README.md ================================================ NB: in Q3 of 2025, 13 years after BSE was first launched, we'll be making BSE2 available in a separate repo. BSE2 is a major refactoring and extension of the original BSE. This, the original BSE repo, will be retained for legacy and reference, and because the code is old and stable and simple; but for advanced usage BSE2 should be the preferred choice once it is available. BSE, The Bristol Stock Exchange, is a simple minimal simulation of a limit-order-book financial exchange, developed for teaching. The aim is to let students explore writing automated trading strategies that deal with "Level 2" market data. It is written in Python, is single-threaded and all in one file for ease of use by novices. The file BSEguide.pdf explains much of what is going on and includes an example programming assignment. The Wiki here on the BSE GitHub site holds a copy of the BSEguide text: it may be that the Wiki text is more up to date than the PDF file. The code in BSE is based on a large number of simplifying assumptions, chief of which is absolute-zero latency: if a trader issues a new quote, that gets processed by the exchange and all other traders can react to it, in zero time (i.e., before any other quote is issued). Nevertheless, because the BSE system is stochastic it can also be used to introduce issues in the design of experiments and analysis of empirical data. Real exchanges are much much more complicated than this. If you use BSE in your work, please link back to this GitHub page for BSE so that people know where to find the original Python source-code: https://github.com/davecliff/BristolStockExchange, and please also cite the peer-reviewed paper that describes BSE: Cliff, D. (2018). BSE: A Minimal Simulation of a Limit-Order-Book Stock Exchange. In M. Affenzeller, et al. (Eds.), Proceedings 30th European Modeling and Simulation Symposium (EMSS 2018), pp. 194-203. DIME University of Genoa. Which you can download from here: https://research-information.bris.ac.uk/ws/portalfiles/portal/167944812/Cliff_i3M_CRC_formatted_repository.pdf or here: https://arxiv.org/abs/1809.06027 The code is open-sourced via the MIT Licence: see the LICENSE file for full text. (copied from http://opensource.org/licenses/mit-license.php) Last update: Dave Cliff, March 27th 2024. ================================================ FILE: Trader_AA.py ================================================ ''' Created on 1 Dec 2012 @author: Ash Booth AA order execution strategy as described in: "Perukrishnen, Cliff and Jennings (2008) 'Strategic Bidding in Continuous Double Auctions'. Artificial Intelligence Journal, 172, (14), 1700-1729". With notable... Amendments: - slightly modified equilibrium price updating - spin up period instead of rounds Additions: - Includes functions for using Newton-Rhapson method for finding complementary theta values. ''' import math import random class Trader_AA(object): def __init__(self): # External parameters (you must choose [optimise] values yourselves) self.spin_up_time = 20 self.eta = 3.0 self.theta_max = 2.0 self.theta_min = -8.0 self.lambda_a = 0.01 self.lambda_r = 0.02 self.beta_1 = 0.4 self.beta_2 = 0.4 self.gamma = 2.0 self.nLastTrades = 5 # N in AIJ08 self.ema_param = 2 / float(self.nLastTrades + 1) self.maxNewtonItter = 10 self.maxNewtonError = 0.0001 # The order we're trying to trade self.orders = [] self.limit = None self.active = False self.job = None # Parameters describing what the market looks like and it's contstraints self.marketMax = bse_sys_maxprice self.prev_best_bid_p = None self.prev_best_bid_q = None self.prev_best_ask_p = None self.prev_best_ask_q = None # Internal parameters (spin up time need to get values for some of these) self.eqlbm = None self.theta = -1.0 * (5.0 * random.random()) self.smithsAlpha = None self.lastTrades = [] self.smithsAlphaMin = None self.smithsAlphaMax = None self.aggressiveness_buy = -1.0 * (0.3 * random.random()) self.aggressiveness_sell = -1.0 * (0.3 * random.random()) self.target_buy = None self.target_sell = None def updateEq(self, price): # Updates the equilibrium price estimate using EMA if self.eqlbm == None: self.eqlbm = price else: self.eqlbm = self.ema_param * price + (1 - self.ema_param) * self.eqlbm def newton4Buying(self): # runs Newton-Raphson to find theta_est (the value of theta that makes the 1st # derivative of eqn(3) continuous) theta_est = self.theta rightHside = ((self.theta * (self.limit - self.eqlbm)) / float(math.exp(self.theta) - 1)); i = 0 while i <= self.maxNewtonItter: eX = math.exp(theta_est) eXminOne = eX - 1 fofX = (((theta_est * self.eqlbm) / float(eXminOne)) - rightHside) if abs(fofX) <= self.maxNewtonError: break dfofX = ((self.eqlbm / eXminOne) - ((eX * self.eqlbm * theta_est) / float(eXminOne * eXminOne))) theta_est = (theta_est - (fofX / float(dfofX))); i += 1 if theta_est == 0.0: theta_est += 0.000001 return theta_est def newton4Selling(self): # runs Newton-Raphson to find theta_est (the value of theta that makes the 1st # derivative of eqn(4) continuous) theta_est = self.theta rightHside = ((self.theta * (self.eqlbm - self.limit)) / float(math.exp(self.theta) - 1)) i = 0 while i <= self.maxNewtonItter: eX = math.exp(theta_est) eXminOne = eX - 1 fofX = (((theta_est * (self.marketMax - self.eqlbm)) / float(eXminOne)) - rightHside) if abs(fofX) <= self.maxNewtonError: break dfofX = (((self.marketMax - self.eqlbm) / eXminOne) - ((eX * (self.marketMax - self.eqlbm) * theta_est) / float(eXminOne * eXminOne))) theta_est = (theta_est - (fofX / float(dfofX))) i += 1 if theta_est == 0.0: theta_est += 0.000001 return theta_est def updateTarget(self): # relates to eqns (3),(4),(5) and (6) # For buying if self.limit < self.eqlbm: # Extra-marginal buyer if self.aggressiveness_buy >= 0: target = self.limit else: target = self.limit * (1 - (math.exp(-self.aggressiveness_buy * self.theta) - 1) / float(math.exp(self.theta) - 1)) self.target_buy = target else: # Intra-marginal buyer if self.aggressiveness_buy >= 0: target = (self.eqlbm + (self.limit - self.eqlbm) * ((math.exp(self.aggressiveness_buy * self.theta) - 1) / float(math.exp(self.theta) - 1))) else: theta_est = self.newton4Buying() target = self.eqlbm * (1 - (math.exp(-self.aggressiveness_buy * theta_est) - 1) / float(math.exp(theta_est) - 1)) self.target_buy = target # For selling if self.limit > self.eqlbm: # Extra-marginal seller if self.aggressiveness_sell >= 0: target = self.limit else: target = self.limit + (self.marketMax - self.limit) * ((math.exp(-self.aggressiveness_sell * self.theta) - 1) / float(math.exp(self.theta) - 1)) self.target_sell = target else: # Intra-marginal seller if self.aggressiveness_sell >= 0: target = self.limit + (self.eqlbm - self.limit) * (1 - (math.exp(self.aggressiveness_sell * self.theta) - 1) / float(math.exp(self.theta) - 1)) else: theta_est = self.newton4Selling() target = self.eqlbm + (self.marketMax - self.eqlbm) * ((math.exp(-self.aggressiveness_sell * theta_est) - 1) / (math.exp(theta_est) - 1)) self.target_sell = target def calcRshout(self, target, buying): if buying: # Are we extramarginal? if self.eqlbm >= self.limit: r_shout = 0.0 else: # Intra-marginal if target > self.eqlbm: if target > self.limit: target = self.limit r_shout = math.log((((target - self.eqlbm) * (math.exp(self.theta) - 1)) / (self.limit - self.eqlbm)) + 1) / self.theta else: # other formula for intra buyer r_shout = math.log((1 - (target / self.eqlbm)) * (math.exp(self.newton4Buying()) - 1) + 1) / -self.newton4Buying() else: # Selling # Are we extra-marginal? if self.limit >= self.eqlbm: r_shout = 0.0 else: # Intra-marginal if target > self.eqlbm: r_shout = math.log(((target - self.eqlbm) * (math.exp(self.newton4Selling()) - 1)) / (self.marketMax - self.eqlbm) + 1) / -self.newton4Selling() else: # other intra seller formula if target < self.limit: target = self.limit r_shout = math.log((1 - (target - self.limit) / (self.eqlbm - self.limit)) * (math.exp(self.theta) - 1) + 1) / self.theta return r_shout def updateAgg(self, up, buying, target): if buying: old_agg = self.aggressiveness_buy else: old_agg = self.aggressiveness_sell if up: delta = (1 + self.lambda_r) * self.calcRshout(target, buying) + self.lambda_a else: delta = (1 - self.lambda_r) * self.calcRshout(target, buying) - self.lambda_a new_agg = old_agg + self.beta_1 * (delta - old_agg) if new_agg > 1.0: new_agg = 1.0 elif new_agg < 0.0: new_agg = 0.000001 return new_agg def updateSmithsAlpha(self, price): self.lastTrades.append(price) if not (len(self.lastTrades) <= self.nLastTrades): self.lastTrades.pop(0) self.smithsAlpha = math.sqrt(sum(((p - self.eqlbm) ** 2) for p in self.lastTrades) * (1 / float(len(self.lastTrades)))) / self.eqlbm if self.smithsAlphaMin == None: self.smithsAlphaMin = self.smithsAlpha self.smithsAlphaMax = self.smithsAlphaMax else: if self.smithsAlpha < self.smithsAlphaMin: self.smithsAlphaMin = self.smithsAlpha if self.smithsAlpha > self.smithsAlphaMax: self.smithsAlphaMax = self.smithsAlpha def updateTheta(self): alphaBar = (self.smithsAlpha - self.smithsAlphaMin) / (self.smithsAlphaMax - self.smithsAlphaMin) desiredTheta = (self.theta_max - self.theta_min) * (1 - (alphaBar * math.exp(self.gamma * (alphaBar - 1)))) + self.theta_min theta = self.theta + self.beta_2 * (desiredTheta - self.theta) if theta == 0: theta += 0.0000001 self.theta = theta def getorder(self, time, countdown, lob): if len(self.orders) < 1: self.active = False order = None else: self.active = True self.limit = self.orders[0].price self.job = self.orders[0].otype self.updateTarget() if self.job == 'Bid': # currently a buyer (working a bid order) if self.spin_up_time > 0: ask_plus = (1 + self.lambda_r) * self.prev_best_ask_p + self.lambda_a quoteprice = self.prev_best_bid_p + (min(self.limit, ask_plus) - self.prev_best_bid_p) / self.eta else: quoteprice = self.prev_best_bid_p + (self.target - self.prev_best_bid_p) / self.eta else: # currently a seller (working a sell order) if self.spin_up_time > 0: bid_minus = (1 - self.lambda_r) * self.prev_best_bid_p - self.lambda_a quoteprice = self.prev_best_ask_p - (self.prev_best_ask_p - max(self.limit, bid_minus)) / self.eta else: quoteprice = (self.prev_best_ask_p - (self.prev_best_ask_p - self.target) / self.eta) order = Order(self.tid, self.job, quoteprice, self.orders[0].qty, time) return order def respond(self, time, lob, trade, verbose): # what, if anything, has happened on the bid LOB? bid_improved = False bid_hit = False lob_best_bid_p = lob['bids']['best'] lob_best_bid_q = None if lob_best_bid_p != None: # non-empty bid LOB lob_best_bid_q = lob['bids']['lob'][-1][1] if self.prev_best_bid_p < lob_best_bid_p : # best bid has improved # NB doesn't check if the improvement was by self bid_improved = True elif trade != None and ((self.prev_best_bid_p > lob_best_bid_p) or ((self.prev_best_bid_p == lob_best_bid_p) and (self.prev_best_bid_q > lob_best_bid_q))): # previous best bid was hit bid_hit = True elif self.prev_best_bid_p != None: # the bid LOB has been emptied by a hit bid_hit = True # what, if anything, has happened on the ask LOB? ask_improved = False ask_lifted = False lob_best_ask_p = lob['asks']['best'] lob_best_ask_q = None if lob_best_ask_p != None: # non-empty ask LOB lob_best_ask_q = lob['asks']['lob'][0][1] if self.prev_best_ask_p > lob_best_ask_p : # best ask has improved -- NB doesn't check if the improvement was by self ask_improved = True elif trade != None and ((self.prev_best_ask_p < lob_best_ask_p) or ((self.prev_best_ask_p == lob_best_ask_p) and (self.prev_best_ask_q > lob_best_ask_q))): # trade happened and best ask price has got worse, or stayed same but quantity reduced -- assume previous best ask was lifted ask_lifted = True elif self.prev_best_ask_p != None: # the bid LOB is empty now but was not previously, so must have been hit ask_lifted = True if verbose and (bid_improved or bid_hit or ask_improved or ask_lifted): print ('B_improved', bid_improved, 'B_hit', bid_hit, 'A_improved', ask_improved, 'A_lifted', ask_lifted) deal = bid_hit or ask_lifted self.prev_best_bid_p = lob_best_bid_p self.prev_best_ask_p = lob_best_ask_p if self.spin_up_time > 0: self.spin_up_time -= 1 if deal: price = trade['price'] self.updateEq(price) self.updateSmithsAlpha(price) self.updateTheta() # The lines below represent the rules in fig(7) in AIJ08. The if statements have not # been merged for the sake of clarity. # For buying if deal: if self.target >= price: self.aggressiveness_buy = self.updateAgg(False, True, price) else: self.aggressiveness_buy = self.updateAgg(True, True, price) elif bid_improved and (self.target <= price): self.aggressiveness_buy = self.updateAgg(True, True, self.prev_best_bid_p) # For selling if deal: if self.target <= price: self.aggressiveness_sell = self.updateAgg(False, False, price) else: self.aggressiveness_sell = self.updateAgg(True, False, price) elif ask_improved and (self.target >= price): self.aggressiveness_sell = self.updateAgg(True, False, self.prev_best_ask_p) self.updateTarget() ================================================ FILE: ZhenZhang/README.md ================================================ This folder holds the sourcecode developed by Zhen Zhang as part of his MSc project, supervised by Dave Cliff, at the University of Bristol, submitted in September 2020. The main contribution is to adapt the multi-level order flow imbalance (MLOFI) to IAA. It includes two modules: + "impact-sensitive" module + "evaluation" module In [IAA_MLOFI.py](./source/IAA_MLOFI.py), it is the IAA with "impact-sensitive" module. In [IAA_NEW.py](./source/IAA_NEW.py), it is the IAA with two modules. In [IZIP_MLOFI.py](./source/IZIP_MLOFI.py), it is the ZZIZIP. In [ZZISHV.py](./source/ZZISHV.py), it is the ZZISHV. ================================================ FILE: ZhenZhang/source/BSE2.py ================================================ # -*- coding: utf-8 -*- # # BSE: The Bristol Stock Exchange # # Version 2.0Beta: Nov 20th, 2018. # Version 1.4: August 30th, 2018. # Version 1.3: July 21st, 2018. # Version 1.2: November 17th, 2012. # # Copyright (c) 2012-2019, Dave Cliff # # # ------------------------ # # MIT Open-Source License: # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and # associated documentation files (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all copies or substantial # portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT # LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # ------------------------ # # # # BSE is a very simple simulation of automated execution traders # operating on a very simple model of a limit order book (LOB) exchange # # major simplifications in this version: # (a) only one financial instrument being traded # (b) each trader can have max of one order per single orderbook. # (c) simply processes each order in sequence and republishes LOB to all traders # => no issues with exchange processing latency/delays or simultaneously issued orders. # # NB this code has been written to be readable/intelligible, not efficient! # could import pylab here for graphing etc import sys import math import random import csv from datetime import datetime from BSE2_msg_classes import Assignment, Order, Exch_msg from BSE_trader_agents import Trader_ISHV, Trader_Shaver,Trader_Giveaway,Trader_AA, Trader_Sniper, Trader_ZIC,Trader_ZIP,Trader_OAA #, Trader_IAAB from IZIP_MLOFI import Trader_IZIP_MLOFI from IAA_MLOFI import Trader_IAA_MLOFI from Simple_MLOFI import Trader_Simple_MLOFI from GDX import Trader_GDX from IGDX_MLOFI import Trader_IGDX_MLOFI from IAA_NEW import Trader_IAA_NEW from ZZISHV import Trader_ZZISHV # from BSE2_unittests import test_all # from BSE2_dev import proc_OXO proc_ICE bse_sys_minprice = 1 # minimum price in the system, in cents/pennies bse_sys_maxprice = 200 # maximum price in the system, in cents/pennies: Todo -- eliminate reliance on this ticksize = 1 # minimum change in price, in cents/pennies # Orderbook_half is one side of the book: # The internal records of the exchange include the ID of the trader who issued the order, arrival time, etc. # The externally published LOB aggregates and anonymizes these details. class Orderbook_half: def __init__(self, booktype, worstprice): self.booktype = booktype def bid_equaltoorbetterthan(p1, p2, verbose): if verbose: print("bid_equaltoorbetterthan: %d >= %d ?" % (p1, p2)) if p1 >= p2: return(True) else: return(False) def ask_equaltoorbetterthan(p1, p2, verbose): if verbose: print("ask_equaltoorbetterthan: %d <= %d ?" % (p1, p2)) if p1 <= p2: return(True) else: return(False) # function for deciding whether price A is equal to or better than price B if self.booktype == 'Bid': self.equaltoorbetterthan = bid_equaltoorbetterthan elif self.booktype == 'Ask': self.equaltoorbetterthan = ask_equaltoorbetterthan else: sys.exit('Fail: Orderbook_half __init__ passed booktype=%s', str(booktype)) # dictionary of live orders received, indexed by Order ID self.orders = {} # limit order book, exchange's internal list, ordered by price, with associated order info self.lob = [] # anonymized LOB, aggregated list with only price/qty info: as published to market observers self.lob_anon = [] # list of orders "resting" at the exchange, i.e. orders that persist for some time (e.g. AON, ICE) self.resting = [] # On-Close & On-Open hold LIM & MKT orders that execute at market open and close (MOO, MOC, LOO, LOC) self.on_close = [] self.on_open = [] # OXO stores details of "other" for OSO and OCO orders self.oxo = [] # summary stats # self.best_price = None self.worst_price = worstprice # self.n_orders = 0 # how many orders? # self.lob_depth = 0 # how many different prices on lob? def __str__(self): v = 'OB_H> ' s = '\n' + v + self.booktype + '\n' s = s + v + 'Orders: ' for oid in self.orders: s = s + str(oid) + '=' + str(self.orders[oid]) + ' ' s = s + '\n' s = s + v + 'LOB:\n' for row in self.lob: s = s + '[P=%d,[' % row[0] # price for order in row[1]: s = s + '[T=%5.2f Q=%d %s OID:%d]' % (order[0], order[1], order[2], order[3]) s = s + ']]\n' s = s + v + 'LOB_anon' + str(self.lob_anon) + '\n' s = s + v + 'MOB:' s = s + '\n' return s def anonymize_lob(self, verbose): # anonymize a lob, strip out order details, format as a sorted list # sorting is best prices at the front (LHS) of the list self.lob_anon = [] if self.booktype == 'Bid': for price in sorted(self.lob, reverse=True): qty = self.lob[price][0] self.lob_anon.append([price, qty]) elif self.booktype == 'Ask': for price in sorted(self.lob): qty = self.lob[price][0] self.lob_anon.append([price, qty]) else: sys.exit('Fail: Orderbook_half __init__ passed booktype=%s', str(booktype)) if verbose: print self.lob_anon def build_lob(self, verbose): # take a list of orders and build a limit-order-book (lob) from it # NB the exchange needs to know arrival times and trader-id associated with each order # returns lob as a list, sorted by price best to worst, orders at same price sorted by arrival time # also builds aggregated & anonymized version (just price/quantity, sorted, as a list) for publishing to traders # First builds lob as a dictionary indexed by price lob = {} for oid in self.orders: order = self.orders.get(oid) price = int(order.price) if price in lob: # update existing entry qty = lob[price][0] orderlist = lob[price][1] orderlist.append([order.time, order.qty, order.tid, order.orderid]) lob[price] = [qty + order.qty, orderlist] else: # create a new dictionary entry lob[price] = [order.qty, [[order.time, order.qty, order.tid, order.orderid]]] self.lob = [] for price in lob: orderlist = lob[price][1] orderlist.sort() #orders are sorted by arrival time self.lob.append([price, orderlist]) #appends only the price and the order-list # now sort by price: order depends on book type if self.booktype == 'Bid': self.lob.sort(reverse=True) elif self.booktype == 'Ask': self.lob.sort() else: sys.exit('Fail: Orderbook_half __init__ passed booktype=%s', str(booktype)) # create anonymized version of LOB for publication self.lob_anon = [] if self.booktype == 'Bid': for price in sorted(lob, reverse=True): qty = lob[price][0] self.lob_anon.append([price, qty]) else: for price in sorted(lob): qty = lob[price][0] self.lob_anon.append([price, qty]) if verbose: print self.lob_anon # record best price and associated trader-id if len(self.lob) > 0 : if self.booktype == 'Bid': self.best_price = self.lob_anon[-1][0] #assumes reverse order COME BACK HERE else : self.best_price = self.lob_anon[0][0] else : self.best_price = None if verbose: print self.lob def book_add(self, order, verbose): # add an order to the master list holding the orders if verbose: print('>book_add %s' % (order)) self.orders[order.orderid] = order self.n_orders = len(self.orders) # reconstruct the LOB -- from scratch (inefficient) self.build_lob(verbose) return None #null response def book_CAN(self, time, order, pool_id, verbose): # delete (CANcel) an order from the dictionary holding the orders def add_tapeitem(eventlist, pool_id, time, oid, otype, qty, verbose): # add_tapeitem(): add an event to list of events that will be written to tape tape_event = {'pool_id':pool_id, 'type':'CAN', 'time':time, 'oid':oid, 'otype':otype, 'o_qty':qty} eventlist.append(tape_event) if verbose: print('book_CAN.add_tapeitem() trans_event=%s' % tape_event) tape_events=[] if verbose: print('>OrderbookHalf.book_CAN %s' % order) for ord in self.orders: print("{%s: %s}" % (ord,str(self.orders[ord]))) oid = order.orderid if len(self.orders)>0 and (self.orders.get(oid) != None) : if verbose: print('Deleting order %s' % oid) o_qty = self.orders[oid].qty o_type = self.booktype del(self.orders[oid]) self.n_orders = len(self.orders) # reconstruct the LOB -- from scratch (inefficient) self.build_lob(verbose) if verbose: print('book_take(): order=%s, lob=%s' % (order, self.lob)) # initial checks, return FAIL if there is simply no hope of executing this order if len(self.lob) == 0: # no point going any further; LOB is empty add_msg(msg_list, order.tid, order.orderid, "FAIL", [], None, fee, verbose) return {"TraderMsgs": msg_list, "TapeEvents": tape_events} # how deep is the book? (i.e. what is cumulative qty available) at this order's indicated price level? depth = 0 for level in self.lob_anon: if self.equaltoorbetterthan(level[0], order.price, verbose): depth += level[1] else: # we're past the level in the LOB where the prices are good for this order break if order.ostyle == "FOK" or order.ostyle == "AON": # FOK and AON require a complete fill # so we first check that this order can in principle be filled: is there enough liquidity available? if depth < order.qty: # there is not enough depth at prices that allow this order to completely fill add_msg(msg_list, order.tid, order.oid, "FAIL", [], None, fee, verbose) # NB here book_take() sends a msg back that an AON order is FAIL, that needs to be picked up by the # exchange logic and not passed back to the trader concerned, unless the AON has actually timed out return {"TraderMsgs": msg_list, "TapeEvents": tape_events} if order.ostyle == "IOC" and depth < 1 : # IOC order is a FAIL because there is no depth at all for the indicated price add_msg(msg_list, order.tid, order.orderid, "FAIL", [], None, fee, verbose) return {"TraderMsgs": msg_list, "TapeEvents": tape_events} # we only get this far if... # LOB is not empty # order is FOK or AON (complete fill only) -- we know there's enough depth to complete # order is MKT (allows partial fill, ignores prices, stops when indicated quantity is reached or LOB is empty) # order is IOC (allows partial fill, aims for indicated quantity but stops when price-limit is reached or LOB is empty) and LOB depth at price > 0 if order.otype == "Bid": tid_to = order.tid oid_to = order.orderid elif order.otype == "Ask": tid_from = order.tid oid_from = order.orderid else: # this shouldn't happen sys.exit('>book_take: order.otype=%s in book_take' % order.otype) # make a copy of the order-list and lobs as it initially stands # used for reconciling fills and when order is abandoned because it can't complete (e.g. FOK, AON) # initial_orders = self.orders # work this order by "walking the book" qty_remaining = order.qty best_lob_price = self.lob[0][0] good_price = True if order.ostyle != "MKT": good_price = self.equaltoorbetterthan(best_lob_price, order.price, verbose) # this while loop consumes the top of the LOB while trying to fill the order while good_price and (qty_remaining > 0) and (len(self.orders)>0): good_price = self.equaltoorbetterthan(self.lob[0][0], order.price, verbose) if verbose: print('BK_TAKE: qty_rem=%d; lob=%s; good_price=%s' % (qty_remaining, str(self.lob), good_price)) sys.stdout.flush() if order.ostyle == "IOC" and (not good_price): # current LOB best price is unacceptable for IOC if verbose: print( 'BK_TAKE: IOC breaks out of while loop (otype=%s best LOB price = %d; order price = %d)' % (order.otype, self.lob[0][0], order.price)) break # out of the while loop best_lob_price = self.lob[0][0] best_lob_orders = self.lob[0][1] best_lob_order = best_lob_orders[0] best_lob_order_qty = best_lob_order[1] best_lob_order_tid = best_lob_order[2] best_lob_order_oid = best_lob_order[3] if order.otype == "Bid": tid_from = best_lob_order_tid oid_from = best_lob_order_oid elif order.otype == "Ask": tid_to = best_lob_order_tid oid_to = best_lob_order_oid if verbose: print('BK_TAKE: best_lob _price=%d _order=%s qty=%d oid_from=%d oid_to=%d tid_from=%s tid_to=%s\n' % (best_lob_price, best_lob_order, best_lob_order_qty, oid_from, oid_to, tid_from, tid_to)) # walk the book: does this order consume current best order on book? if best_lob_order_qty >= qty_remaining: # incoming liquidity-taking order is completely filled by consuming some/all of best order on LOB qty = qty_remaining price = best_lob_price qty_filled = qty_filled + qty best_lob_order_qty = best_lob_order_qty - qty # the incoming order is a complete fill transaction = {"Price":price, "Qty":qty} trnsctns.append(transaction) # add a message to the list of outgoing messages from exch to traders add_msg(msg_list, order.tid, order.orderid, "FILL", trnsctns, None, fee, verbose) # add a record of this to the tape (NB this identifies both parties to the trade, so only do it once) add_tapeitem(tape_events, pool_id, 'Trade', time, price, qty, tid_from, tid_to, verbose) # so far have dealt with effect of match on incoming order # now need to deal with effect of match on best order on LOB (the other side of the deal) if best_lob_order_qty > 0: # the best LOB order is only partially consumed best_lob_order[1] = best_lob_order_qty best_lob_orders[0] = best_lob_order self.lob[0][1] = best_lob_orders self.orders[best_lob_order_oid].qty = best_lob_order_qty # The LOB order it matched against is only a partial fill add_msg(msg_list, best_lob_order_tid, best_lob_order_oid, "PART", [transaction], self.orders[best_lob_order_oid], fee, verbose) # add_tapeitem(tape_events, 'Trade', time, price, qty, tid_from, tid_to, verbose) else: # the best LOB order is fully consumed: delete it from LOB del(best_lob_orders[0]) del(self.orders[best_lob_order_oid]) # The LOB order it matched against also complete add_msg(msg_list, best_lob_order_tid, best_lob_order_oid, "FILL", [transaction], None, fee, verbose) # add_tapeitem(tape_events, 'Trade', time, price, qty, tid_from, tid_to, verbose) # check: are there other remaining orders at this price? if len(best_lob_orders) > 0: # yes self.lob[0][1] = best_lob_orders else: # no del (self.lob[0]) # consumed the last order on the LOB at this price qty_remaining = 0 # liquidity-taking all done else: # order is only partially filled by current best order, but current best LOB order is fully filled # consume all the current best and repeat qty = best_lob_order_qty price = best_lob_price qty_filled = qty_filled + qty transaction = {"Price": price, "Qty": qty} trnsctns.append(transaction) # add a message to the list of outgoing messages from exch to traders add_msg(msg_list, best_lob_order_tid, best_lob_order_oid, "FILL", [transaction], None, fee, verbose) # add a record of this to the tape (NB this identifies both parties to the trade, so only do it once) add_tapeitem(tape_events, pool_id, 'Trade', time, price, qty, tid_from, tid_to, verbose) # the best LOB order is fully consumed: delete it from LOB and from order-list del(self.orders[best_lob_order_oid]) del(best_lob_orders[0]) # check: are there other remaining orders at this price? if len(best_lob_orders) > 0: # yes self.lob[0][1] = best_lob_orders else: # no del (self.lob[0]) # consumed the last order on the LOB at this price qty_remaining = qty_remaining - qty if verbose: print('New LOB=%s orders=%s' % (str(self.lob), str(self.orders))) # main while loop ends here # when we get to here either... # the order completely filled by consuming the front of the book (which may have emptied the whole book) # or the whole book was consumed (and is now empty) without completely filling the order # or IOC consumed as much of the book's availability at the order's indicated price (good_price = False) if qty_remaining > 0 : if qty_remaining == order.qty: # this order is wholly unfilled: that's a FAIL (how did this get past the initial checks?) add_msg(msg_list, order.tid, order.orderid, "FAIL", [], None, fee, verbose) else: # this liquidity-taking order only partially filled but ran out of usable LOB order.qty = qty_remaining #revise the order quantity add_msg(msg_list, order.tid, order.orderid, "PART", trnsctns, order, fee, verbose) # add_tapeitem(tape_events, 'Trade', time, price, qty, tid_from, tid_to, verbose) if verbose: print(' 0 and ask_q == None : mprice = bid_p elif ask_q > 0 and bid_q == None : mprice = ask_p elif bid_q>0 and ask_q >0 : mprice = ( bid_p + ask_p ) / 2.0 return mprice def microprice(self, bid_p, bid_q, ask_p, ask_q): mprice = None if bid_q>0 and ask_q >0 : tot_q = bid_q + ask_q mprice = ( (bid_p * ask_q) + (ask_p * bid_q) ) / tot_q return mprice def add_lim_order(self, order, verbose): # add a LIM order to the LOB and update records if verbose: print('>add_lim_order: order.orderid=%d' % (order.orderid)) if order.otype == 'Bid': response=self.bids.book_add(order, verbose) best_price = self.bids.lob_anon[0][0] self.bids.best_price = best_price else: response=self.asks.book_add(order, verbose) best_price = self.asks.lob_anon[0][0] self.asks.best_price = best_price return response def process_order_CAN(self, time, order, verbose): # cancel an existing order if verbose: print('>Orderbook.process_order_CAN order.orderid=%d' % order.orderid) if order.otype == 'Bid': # cancel order from the bid book response = self.bids.book_CAN(time, order, self.idstr, verbose) elif order.otype == 'Ask': # cancel order from the ask book response = self.asks.book_CAN(time, order, self.idstr, verbose) else: # we should never get here sys.exit('process_order_CAN() given neither Bid nor Ask') # response should be a message for the trader, and an event to write to the tape if verbose: print('PO_CAN %s' % response) return response def process_order_XXX(self, time, order, verbose): # cancel all orders on this orderbook that were issued by the trader that issued this order if verbose: print('>Orderbook.process_order_XXX order.orderid=%d' % order.orderid) tid = order.tid # need to sweep through all bids and and all asks and delete all orders from this trader responselist = [] for bid_order in self.bids.orders: if bid_order.tid == tid : responselist.append(self.bids.book_CAN(time, order, verbose)) for ask_order in self.asks.orders: if ask_order.tid == tid: responselist.append(self.asks.book_CAN(time, order, verbose)) # responselist is handed back to caller level for them to unpack if verbose: print('PO_CAN %s' % responselist) return responselist def process_order_take(self, time, order, verbose): if verbose: print('> Orderbook.process_order_take order.orderid=%d' % order.orderid) if order.otype == 'Bid': # this bid consumes from the top of the ask book response = self.asks.book_take(time, order, self.idstr, verbose) elif order.otype == 'Ask': # this ask consumes from the top of the bid book response = self.bids.book_take(time, order, self.idstr, verbose) else: # we should never get here sys.exit('process_order_take() given neither Bid nor Ask') if verbose: print('OB.PO_take %s' % response) return response def process_order_LIM(self, time, order, verbose): # adds LIM and GFD orders -- GFD is just a time-limited LIM def process_LIM(order, verbose): response = self.add_lim_order(order, verbose) if verbose: print('>process_order_LIM order.orderid=%d' % order.orderid) print('Response: %s' % response) return response oprice = order.price # does the LIM price cross the spread? if order.otype == 'Bid': if len(self.asks.lob) > 0 and oprice >= self.asks.lob[0][0]: # crosses: this LIM bid lifts the best ask, so treat as IOC if verbose: print("Bid LIM $%s lifts best ask ($%s) =>IOC" % (oprice, self.asks.lob[0][0])) order.ostyle = 'IOC' response = self.process_order_take(time, order, verbose) else: response = process_LIM(order, verbose) elif order.otype == 'Ask': if len(self.bids.lob) > 0 and oprice <= self.bids.lob[0][0]: # crosses: this LIM ask hits the best bid, so treat as IOC if verbose: print("Ask LIM $%s hits best bid ($%s) =>IOC" % (oprice, self.bids.lob[0][0])) order.ostyle = 'IOC' response = self.process_order_take(time, order, verbose) else: response = process_LIM(order, verbose) else: # we should never get here sys.exit('process_order_LIM() given neither Bid nor Ask') return response def process_order_pending(self, time, order, verbose): # this returns a null response because it just places the order on the relevant pending-execution list # order styles LOO and MOO are subsequently processed/executed in the market_open() method # order styles LOC and MOC are subsequently processed/executed in the market_close() method if order.ostyle == 'LOO' or order.ostyle == 'MOO': if order.otype == 'Bid': self.bids.on_open.append(order) elif order.otype == 'Ask': self.asks.on_open.append(order) else: # we should never get here sys.exit('process_order_pending() LOO/MOO given neither Bid nor Ask') elif order.ostyle == 'LOC' or order.ostyle == 'MOC': if order.otype == 'Bid': self.bids.on_close.append(order) elif order.otype == 'Ask': self.asks.on_close.append(order) else: # we should never get here sys.exit('process_order_pending() LOC/MOC given neither Bid nor Ask') else: sys.exit('process_order_pending() given something other than LOO MOO LOC MOC') return {'TraderMsgs':None, 'TapeEvents':None} # Exchange's internal orderbooks class Exchange(Orderbook): def __init__(self, eid): self.eid = eid # exchange ID string self.lit = Orderbook(eid + "Lit") # traditional lit exchange self.drk = Orderbook(eid + "Drk") # NB just a placeholder -- in this version of BSE the dark pool is undefined self.tape = [] # tape: consolidated record of trading events on the exchange self.trader_recs = {} # trader records (balances from fees, reputations, etc), indexed by traderID self.order_id = 0 # unique ID code for each order received by the exchange, starts at zero self.open = False # is the exchange open (for business) or closed? def __str__(self): s = '\nExchID: %s ' % (self.eid) if self.open: s = s + '(Open)\n' else: s = s + '(Closed)\n' s = s + 'Lit ' + str(self.lit) s = s + 'Dark ' + str(self.drk) s = s + 'OID: %d; ' % self.order_id s = s + 'TraderRecs: %s' % self.trader_recs s = s + 'Tape[-4:]: %s' % self.tape[-4:] s = s + '\n' return s class trader_record: # exchange's records for an individual trader def __init__(self, time, tid): self.tid = tid # this trader's ID self.regtime = time # time when first registered self.balance = 0 # balance at the exchange (from exchange fees and rebates) self.reputation = None # reputation -- FOR GEORGE CHURCH todo -- integrate with George's work self.orders = [] # list of orders received from this trader self.msgs = [] # list of messages sent to this trader def __str__(self): s = '[%s bal=%d rep=%s orders=%s msgs=%s]' % (self.tid, self.balance, self.reputation, self.orders, self.msgs) return s def consolidate_responses(self, responses): consolidated = {'TraderMsgs':[], 'TapeEvents':[]} if len(responses) > 1: # only need to do this if been given more than one response for resp in responses: consolidated['TraderMsgs'].append(resp['TraderMsgs']) consolidated['TapeEvents'].append(resp['TapeEvents']) # could sort into time-order here, but its not essential -- todo else: consolidated = responses[0] return consolidated def mkt_open(self, time, verbose): # exchange opens for business # need to process any LOO and MOO orders: # processes LOO and MOO orders in sequence wrt where they are in the relevant on_open list def open_pool(time, pool, verbose): responses = [] # LOO and MOO for order in pool.on_open: if order.ostyle == 'LIM': responses.append(pool.process_order_LIM(time, order, verbose)) elif order.ostyle == 'MKT': responses.append(pool.process_order_take(time, order, verbose)) else: sys.exit('FAIL in open_pool(): neither LIM nor MKT in on_open list ') return responses print('Exchange %s opening for business', self.eid) response_l = open_pool(self.lit) response_d = open_pool(self.drk) self.open = True return consolidate_responses([response_l, response_d]) def mkt_close(self): # exchange closes for business # need to process any LOC, MOC, and GFD orders # NB GFD orders assumes that exchange closing is the same as end of day def close_pool(time, pool, verbose): responses = [] # LOC and MOC for order in pool.on_close: if order.ostyle == 'LIM': responses.append(pool.process_order_LIM(time, order, verbose)) elif order.ostyle == 'MKT': responses.append(pool.process_order_take(time, order, verbose)) else: sys.exit('FAIL in open_pool(): neither LIM nor MKT in on_close list ') # GFD -- cancel any orders still on the books for order in pool.orders: if order.ostyle == 'GFD': responses.append(pool.process_order_CAN(time, order, verbose)) return responses print('Exchange %s closing for business', self.eid) response_l = close_pool(self.lit) response_d = close_pool(self.drk) self.open = False return consolidate_responses([response_l, response_d]) def tape_update(self, tr, verbose): # updates the tape if verbose: print("Tape update: tr=%s; len(tape)=%d tape[-3:]=%s" % (tr, len(self.tape), self.tape[-3:])) self.tape.append(tr) if tr['type'] == 'Trade': # process the trade if verbose: print('>>>>>>>>TRADE t=%5.3f $%d Q%d %s %s\n' % (tr['time'], tr['price'], tr['qty'], tr['party1'], tr['party2'])) self.last_trans_t = tr['time'] # time of last transaction self.last_trans_p = tr['price'] # price of last transaction self.last_trans_q = tr['qty'] # quantity of last transaction return tr def dump_tape(self, session_id, dumpfile, tmode,traders): # print('Dumping tape s.tape=') # for ti in self.tape: # print('%s' % ti) for tapeitem in self.tape: # print('tape_dump: tapitem=%s' % tapeitem) if tapeitem['type'] == 'Trade': dumpfile.write('%s, %s, %s,%s,%s,%s,%s, %s\n' % (session_id, tapeitem['pool_id'], tapeitem['time'], tapeitem['price'],tapeitem['qty'],traders[tapeitem['party2']].ttype, traders[tapeitem['party1']].ttype,str(tapeitem))) if tmode == 'wipe': self.tape = [] aaFile = open('myFile_AA.csv','a'); for tapeitem in self.tape: # print('tape_dump: tapitem=%s' % tapeitem) if tapeitem['type'] == 'Trade': if(traders[tapeitem['party2']].ttype == 'SHVR' and traders[tapeitem['party1']].ttype == 'AA'): aaFile.write('%s\n' % (tapeitem['price'])) aaFile.close() iaaFile = open('myFile_IAA.csv','a') for tapeitem in self.tape: # print('tape_dump: tapitem=%s' % tapeitem) if tapeitem['type'] == 'Trade': if (traders[tapeitem['party2']].ttype == 'SHVR' and traders[tapeitem['party1']].ttype == 'IAA'): iaaFile.write('%s\n' % (tapeitem['price'])) iaaFile.close() def process_order(self, time, order, verbose): # process the order passed in as a parameter # number of allowable order-types is significantly expanded in BSE2 (previously just had LIM/MKT functionality) # BSE2 added order types such as FOK, ICE, etc # also added stub logic for larger orders to be routed to dark pool # currently treats dark pool as another instance of Orderbook, same as lit pool # incoming order has order ID assigned by exchange # return is {'tape_summary':... ,'trader_msgs':...}, explained further below if verbose: print('>Exchange.process_order()\n') trader_id = order.tid if not trader_id in self.trader_recs: # we've not seen this trader before, so create a record for it if verbose: print('t=%f: Exchange %s registering Trader %s:' % (time, self.eid, trader_id)) trader_rec = self.trader_record(time, trader_id) self.trader_recs[trader_id] = trader_rec if verbose: print('record= %s' % str(trader_rec)) # what quantity qualifies as a block trade (route to DARK)? block_size = 300 ostyle = order.ostyle ack_response = Exch_msg(trader_id, order.orderid, 'ACK', [[order.price, order.qty]], None, 0, 0) if verbose: print ack_response # which pool does it get sent to: Lit or Dark? if order.qty < block_size: if verbose: print('Process_order: qty=%d routes to LIT pool' % order.qty) pool = self.lit else: if verbose: print('Process_order: qty=%d routes to DARK pool' % order.qty) pool = self.drk # Cancellations don't generate new order-ids if ostyle == 'CAN': # deleting a single existing order # NB this trusts the order.qty -- sends CANcel only to the pool that the QTY indicates response = pool.process_order_CAN(time, order, verbose) elif ostyle == 'XXX': # delete all orders from the trader that issued the XXX order # need to sweep through both pools response_l = self.lit.process_order_XXX(time, order, verbose) response_d = self.drk.process_order_XXX(time, order, verbose) # the response from either lit and/or dark might be a string of responses from multiple individual CAN orders # here we just glue those together for later processing self.consolidate_responses([response_l, response_d]) else: # give each new order a unique ID order.orderid = self.order_id self.order_id = order.orderid + 1 ack_msg = Exch_msg(trader_id, order.orderid, 'ACK', [[order.price, order.qty]], None, 0, 0) if verbose: print('OrderID:%d, ack:%s\n' % (order.orderid, ack_msg)) if ostyle == 'LIM' or ostyle == 'GFD': # GFD is just a LIM order with an expiry time response = pool.process_order_LIM(time, order, verbose) elif ostyle == 'MKT' or ostyle == 'AON' or ostyle == 'FOK' or ostyle == 'IOC': if ostyle == 'AON': pool.resting.append(order) # put it on the list of resting orders response = pool.process_order_take(time, order, verbose) # AON is a special case: if current response is that it FAILed, but has not timed out # then ignore the failure # and if it didn't fail, check to remove it from the MOB if ostyle == 'AON': if response['TraderMsgs'].event == 'FAIL': # it failed, but has it timed out yet? if time < order.styleparams['ExpiryTime']: # it hasn't expired yet # nothing to say back to the trader, nothing to write to tape response['TraderMsgs'] = None response['TapeEvents'] = None else: # AON order executed successfully, remove it from the MOB pool.resting.remove(order) elif ostyle == 'LOC' or ostyle == 'MOC' or ostyle == 'LOO' or ostyle == 'MOO': # these are just placed on the relevant wait-list at the exchange # and then processed by mkt_open() or mkt_close() response = pool.process_order_pending(time, order, verbose) elif ostyle == 'OCO' or ostyle == 'OSO': # processing of OSO and OCO orders is a recursive call of this method # that is, call process_order() on the first order in the OXO pair # then call or ignore the second order depending on outcome of the first # OCO and OSO are both defined via the following syntax... # ostyle=OSO or OCO; styleparams=[[order1], [order2]] # currently only defined for [order1] and [order2] both LIM type if len(order.styleparams) == 2: order1 = order.styleparams[0] order2 = order.styleparams[1] if order1.ostyle == 'LIM' and order2.ostyle == 'LIM': sys.exit('Give up') response = pool.process_order_OXO(time, order, verbose) elif ostyle == 'ICE': # this boils down to a chain of successively refreshed OSO orders, until its all used up # so underneath it's LIM functionality only response = pool.process_order_ICE(time, order, verbose) else: sys.exit('FAIL: process_order given order style %s', ostyle) if verbose: print (' 0: for event in tape_events: self.tape_update(event, verbose) if verbose: print('1 can result in multiple separate transactions, # so we need to aggregate those into one order. Do this by computing total cost C of # execution for quantity Q and then declaring that the price for each unit was C/Q # As there may now be more then one counterparty to a single order, party1 & party2 returned as None tape_summary = None if len(tape_events) > 0: total_cost = 0 total_qty = 0 if verbose: print('tape_summary:') for event in tape_events: if event['type'] == 'Trade': total_cost += event['price'] total_qty += event['qty'] if verbose: print('total_cost=%d; total_qty=%d' % (total_cost, total_qty)) if total_qty > 0 : avg_cost = total_cost / total_qty if verbose: print('avg_cost=%d' % avg_cost) tape_summary = {'type': 'Trade', 'time': time, 'price': avg_cost, 'party1': None, 'party2': None, 'qty': total_qty} return {'tape_summary':tape_summary, 'trader_msgs':trader_msgs} else: return {'tape_summary':None, 'trader_msgs':None} # this returns the LOB data "published" by the exchange, # only applies to the lit book -- dark pools aren't published def publish_lob(self, time, tape_depth, verbose): n_bids = len(self.lit.bids.orders) if n_bids > 0 : best_bid_p = self.lit.bids.lob_anon[0][0] else: best_bid_p = None n_asks = len(self.lit.asks.orders) if n_asks > 0: best_ask_p = self.lit.asks.lob_anon[0][0] else: best_ask_p = None public_data = {} public_data['time'] = time public_data['bids'] = {'bestp':best_bid_p, 'worstp':self.lit.bids.worst_price, 'n': n_bids, 'lob':self.lit.bids.lob_anon} public_data['asks'] = {'bestp':best_ask_p, 'worstp':self.lit.asks.worst_price, 'n': n_asks, 'lob':self.lit.asks.lob_anon} public_data['last_t'] = self.lit.last_trans_t public_data['last_p'] = self.lit.last_trans_p public_data['last_q'] = self.lit.last_trans_q if tape_depth == None : public_data['tape'] = self.tape # the full thing else: public_data['tape'] = self.tape[-tape_depth:] # depth-limited public_data['midprice'] = None public_data['microprice'] = None if n_bids>0 and n_asks>0 : # neither side of the LOB is empty best_bid_q= self.lit.bids.lob_anon[0][1] best_ask_q = self.lit.asks.lob_anon[0][1] public_data['midprice'] = self.lit.midprice(best_bid_p, best_bid_q, best_ask_p, best_ask_q) public_data['microprice'] = self.lit.microprice(best_bid_p, best_bid_q, best_ask_p, best_ask_q) if verbose: print('Exchange.publish_lob: t=%s' % time) print('BID_lob=%s' % public_data['bids']['lob']) print('best=%s; worst=%s; n=%s ' % (best_bid_p, self.lit.bids.worst_price, n_bids)) print(str(self.lit.bids)) print('ASK_lob=%s' % public_data['asks']['lob']) print('best=%s; worst=%s; n=%s ' % (best_ask_p, self.lit.asks.worst_price, n_asks)) print(str(self.lit.asks)) print('Midprice=%s; Microprice=%s' % (public_data['midprice'], public_data['microprice'])) print('Last transaction: time=%s; price=%s; qty=%s' % (public_data['last_t'],public_data['last_p'],public_data['last_q'])) print('tape[-3:]=%s'% public_data['tape'][-3:]) sys.stdout.flush() return public_data ##########################---Below lies the experiment/test-rig---################## # trade_stats() # dump CSV statistics on exchange data and trader population to file for later analysis # this makes no assumptions about the number of types of traders, or # the number of traders of any one type -- allows either/both to change # between successive calls, but that does make it inefficient as it has to # re-analyse the entire set of traders on each call def trade_stats(expid, traders, dumpfile, time, lob): trader_types = {} n_traders = len(traders) for t in traders: ttype = traders[t].ttype if ttype in trader_types.keys(): t_balance = trader_types[ttype]['balance_sum'] + traders[t].balance n = trader_types[ttype]['n'] + 1 else: t_balance = traders[t].balance n = 1 trader_types[ttype] = {'n':n, 'balance_sum':t_balance} dumpfile.write('%s, %06d, ' % (expid, time)) for ttype in sorted(list(trader_types.keys())): n = trader_types[ttype]['n'] s = trader_types[ttype]['balance_sum'] dumpfile.write('%s, %d, %d, %f, ' % (ttype, s, n, s / float(n))) if lob['bids']['bestp'] != None : dumpfile.write('%d, ' % (lob['bids']['bestp'])) else: dumpfile.write('N, ') if lob['asks']['bestp'] != None : dumpfile.write('%d, ' % (lob['asks']['bestp'])) else: dumpfile.write('N, ') dumpfile.write('\n'); # create a bunch of traders from traders_spec # returns tuple (n_buyers, n_sellers) # optionally shuffles the pack of buyers and the pack of sellers def populate_market(traders_spec, traders, shuffle, verbose): def trader_type(robottype, name): if robottype == 'GVWY': return Trader_Giveaway('GVWY', name, 0.00, 0) elif robottype == 'ZIC': return Trader_ZIC('ZIC', name, 0.00, 0) elif robottype == 'SHVR': return Trader_Shaver('SHVR', name, 0.00, 0) elif robottype == 'ISHV': return Trader_ISHV('ISHV', name, 0.00, 0) elif robottype == 'SNPR': return Trader_Sniper('SNPR', name, 0.00, 0) elif robottype == 'ZIP': return Trader_ZIP('ZIP', name, 0.00, 0) elif robottype == 'AA': return Trader_AA('AA', name, 0.00, 0) elif robottype == 'AAA': return Trader_AA('AAA', name, 0.00, 0) elif robottype == 'SIMPLE': return Trader_Simple_MLOFI('SIMPLE',name,0.00,0) elif robottype == 'IAA_MLOFI_ASK': return Trader_IAA_MLOFI('MLOFI_ASK',name,0.00,0) elif robottype == 'IAA_MLOFI_BID': return Trader_IAA_MLOFI('MLOFI_BID',name,0.00,0) elif robottype == 'AAAA': return Trader_AA('AAAA', name, 0.00, 0) elif robottype == 'ISHV_ASK': return Trader_ISHV('ISHV_ASK', name, 0.00, 0) elif robottype == 'GDX': return Trader_GDX('GDX', name, 0.00, 0) elif robottype == 'GDXB': return Trader_GDX('GDXB', name, 0.00, 0) elif robottype == 'ZIPP': return Trader_ZIP('ZIPP', name, 0.00, 0) elif robottype == 'IAA_MLOFI': return Trader_IAA_MLOFI('MLOFI',name,0.00,0,3) elif robottype == 'IAA_MLOFI1': return Trader_IAA_MLOFI('MLOFI1',name,0.00,0, 1) elif robottype == 'IAA_MLOFI2': return Trader_IAA_MLOFI('MLOFI2', name, 0.00,0, 2) elif robottype == 'IAA_MLOFI3': return Trader_IAA_MLOFI('MLOFI3', name, 0.00,0, 3) elif robottype == 'IAA_MLOFI4': return Trader_IAA_MLOFI('MLOFI4', name, 0.00, 0,4) elif robottype == 'IAA_MLOFI5': return Trader_IAA_MLOFI('MLOFI5', name, 0.00,0, 5) elif robottype == 'IAA_MLOFI6': return Trader_IAA_MLOFI('MLOFI6', name, 0.00,0, 6) elif robottype == 'IAA_MLOFI7': return Trader_IAA_MLOFI('MLOFI7', name, 0.00,0, 7) elif robottype == 'IAA_MLOFI8': return Trader_IAA_MLOFI('MLOFI8', name, 0.00,0, 8) elif robottype == 'IAA_MLOFI9': return Trader_IAA_MLOFI('MLOFI9', name, 0.00,0, 9) elif robottype == 'IAA_MLOFI10': return Trader_IAA_MLOFI('MLOFI10', name, 0.00, 0, 10) elif robottype == 'IAA_MLOFI11': return Trader_IAA_MLOFI('MLOFI11', name, 0.00, 0, 11) elif robottype == 'IAA_MLOFI12': return Trader_IAA_MLOFI('MLOFI12', name, 0.00, 0, 12) elif robottype == 'IAA_MLOFI13': return Trader_IAA_MLOFI('MLOFI13', name, 0.00, 0, 13) elif robottype == 'IAA_MLOFI14': return Trader_IAA_MLOFI('MLOFI14', name, 0.00, 0, 14) elif robottype == 'IAA_MLOFI15': return Trader_IAA_MLOFI('MLOFI15', name, 0.00, 0, 15) elif robottype == 'IAA_MLOFI16': return Trader_IAA_MLOFI('MLOFI16', name, 0.00, 0, 16) elif robottype == 'IAA_MLOFI17': return Trader_IAA_MLOFI('MLOFI17', name, 0.00, 0, 17) elif robottype == 'IAA_MLOFI18': return Trader_IAA_MLOFI('MLOFI18', name, 0.00, 0, 18) elif robottype == 'IAA_MLOFI19': return Trader_IAA_MLOFI('MLOFI19', name, 0.00, 0, 19) elif robottype == 'IAA_MLOFI20': return Trader_IAA_MLOFI('MLOFI20', name, 0.00, 0, 20) elif robottype == 'IAA_MLOFI30': return Trader_IAA_MLOFI('MLOFI30', name, 0.00, 0, 30) elif robottype == 'IAA_MLOFI50': return Trader_IAA_MLOFI('MLOFI50', name, 0.00, 0, 50) elif robottype == 'IZIP_3': return Trader_IZIP_MLOFI('IZIP_3',name,0.00,0,3) elif robottype == 'IGDX_3': return Trader_IGDX_MLOFI('IGDX_3', name, 0.00, 0, 3) elif robottype == 'IZIPB_3': return Trader_IZIP_MLOFI('IZIPB_3',name,0.00,0,3) elif robottype == 'IGDXB_3': return Trader_IGDX_MLOFI('IGDXB_3', name, 0.00, 0, 3) elif robottype == 'IAAB_3': return Trader_IAA_MLOFI('IAAB3', name, 0.00,0, 3) elif robottype == 'ASK_IGDX_3': return Trader_IGDX_MLOFI('ASK_IGDX_3', name, 0.00, 0, 3) elif robottype == 'BID_IGDX_3': return Trader_IGDX_MLOFI('BID_IGDX_3', name, 0.00, 0, 3) elif robottype == 'ASK_IZIP_3': return Trader_IZIP_MLOFI('ASK_IZIP_3',name,0.00,0,3) elif robottype == 'BID_IZIP_3': return Trader_IZIP_MLOFI('BID_IZIP_3',name,0.00,0,3) elif robottype == 'ASK_IAA_3': return Trader_IAA_MLOFI('ASK_IAA_3', name, 0.00,0, 3) elif robottype == 'BID_IAA_3': return Trader_IAA_MLOFI('BID_IAA_3', name, 0.00, 0, 3) elif robottype == 'ASK_AA': return Trader_AA('ASK_AA', name, 0.00, 0) elif robottype == 'BID_AA': return Trader_AA('BID_AA', name, 0.00, 0) elif robottype == 'ASK_GDX': return Trader_GDX('ASK_GDX', name, 0.00, 0) elif robottype == 'BID_GDX': return Trader_GDX('BID_GDX', name, 0.00, 0) elif robottype == 'ASK_ZIP': return Trader_ZIP('ASK_ZIP', name, 0.00, 0) elif robottype == 'BID_ZIP': return Trader_ZIP('BID_ZIP', name, 0.00, 0) elif robottype == 'IAA': return Trader_IAA_MLOFI('IAA', name, 0.00,0, 3) elif robottype == 'IAA_3': return Trader_IAA_MLOFI('IAA_3', name, 0.00,0, 3) elif robottype == 'ASK_SHVR': return Trader_Shaver('ASK_SHVR', name, 0.00, 0) elif robottype == 'BID_SHVR': return Trader_Shaver('BID_SHVR', name, 0.00, 0) elif robottype == 'ASK_ISHV_3': return Trader_ISHV('ASK_ISHV_3', name, 0.00, 0) elif robottype == 'BID_ISHV_3': return Trader_ISHV('BID_ISHV_3', name, 0.00, 0) elif robottype == 'GDXXX': return Trader_GDX('GDXXX', name, 0.00, 0) elif robottype == 'GDXX': return Trader_GDX('GDXX', name, 0.00, 0) elif robottype == 'IAA_NEW': return Trader_IAA_NEW('IAA_NEW', name, 0.00, 0, 3) elif robottype == 'ZZISHV': return Trader_ZZISHV('ZZISHV', name, 0.00, 0,3) elif robottype == 'ASK_ZZISHV': return Trader_ZZISHV('ASK_ZZISHV', name, 0.00, 0,3) elif robottype == 'BID_ZZISHV': return Trader_ZZISHV('BID_ZZISHV', name, 0.00, 0,3) elif robottype == 'ASK_ISHV': return Trader_ISHV('ASK_ISHV', name, 0.00, 0) elif robottype == 'BID_ISHV': return Trader_ISHV('BID_ISHV', name, 0.00, 0) elif robottype == 'ZIPPP': return Trader_ZIP('ZIPPP', name, 0.00, 0) elif robottype == 'ZIPP': return Trader_ZIP('ZIPP', name, 0.00, 0) elif robottype == 'IZIP': return Trader_IZIP_MLOFI('IZIP',name,0.00,0,3) elif robottype == 'IGDX': return Trader_IGDX_MLOFI('IGDX', name, 0.00, 0, 3) else: sys.exit('FATAL: don\'t know robot type %s\n' % robottype) def shuffle_traders(ttype_char, n, traders): for swap in range(n): t1 = (n - 1) - swap t2 = random.randint(0, t1) t1name = '%c%02d' % (ttype_char, t1) t2name = '%c%02d' % (ttype_char, t2) traders[t1name].tid = t2name traders[t2name].tid = t1name temp = traders[t1name] traders[t1name] = traders[t2name] traders[t2name] = temp n_buyers = 0 for bs in traders_spec['buyers']: ttype = bs[0] for b in range(bs[1]): tname = 'B%02d' % n_buyers # buyer i.d. string traders[tname] = trader_type(ttype, tname) n_buyers = n_buyers + 1 if n_buyers < 1: sys.exit('FATAL: no buyers specified\n') if shuffle: shuffle_traders('B', n_buyers, traders) n_sellers = 0 for ss in traders_spec['sellers']: ttype = ss[0] for s in range(ss[1]): tname = 'S%02d' % n_sellers # buyer i.d. string traders[tname] = trader_type(ttype, tname) n_sellers = n_sellers + 1 if n_sellers < 1: sys.exit('FATAL: no sellers specified\n') if shuffle: shuffle_traders('S', n_sellers, traders) if verbose: for t in range(n_buyers): bname = 'B%02d' % t print(traders[bname]) for t in range(n_sellers): bname = 'S%02d' % t print(traders[bname]) return {'n_buyers':n_buyers, 'n_sellers':n_sellers} # customer_orders(): allocate orders to traders # this version only issues LIM orders; LIM that crosses the spread executes as MKT # parameter "os" is order schedule # os['timemode'] is either 'periodic', 'drip-fixed', 'drip-jitter', or 'drip-poisson' # os['interval'] is number of seconds for a full cycle of replenishment # drip-poisson sequences will be normalised to ensure time of last replenishment <= interval # parameter "pending" is the list of future orders (if this is empty, generates a new one from os) # revised "pending" is the returned value # # also returns a list of "cancellations": trader-ids for those traders who are now working a new order and hence # need to kill quotes already on LOB from working previous order # # # if a supply or demand schedule mode is "random" and more than one range is supplied in ranges[], # then each time a price is generated one of the ranges is chosen equiprobably and # the price is then generated uniform-randomly from that range # # if len(range)==2, interpreted as min and max values on the schedule, specifying linear supply/demand curve # if len(range)==3, first two vals are min & max, third value should be a function that generates a dynamic price offset # -- the offset value applies equally to the min & max, so gradient of linear sup/dem curve doesn't vary # if len(range)==4, the third value is function that gives dynamic offset for schedule min, # and fourth is a function giving dynamic offset for schedule max, so gradient of sup/dem linear curve can vary # # the interface on this is a bit of a mess... could do with refactoring def customer_orders(time, last_update, traders, trader_stats, os, pending, base_oid, verbose): def sysmin_check(price): if price < bse_sys_minprice: print('WARNING: price < bse_sys_min -- clipped') price = bse_sys_minprice return price def sysmax_check(price): if price > bse_sys_maxprice: print('WARNING: price > bse_sys_max -- clipped') price = bse_sys_maxprice return price def getorderprice(i, sched_end, sched, n, mode, issuetime): # does the first schedule range include optional dynamic offset function(s)? if len(sched[0]) > 2: offsetfn = sched[0][2][0] offsetfn_params = [sched_end] + [p for p in sched[0][2][1] ] if callable(offsetfn): # same offset for min and max offset_min = offsetfn(issuetime, offsetfn_params) offset_max = offset_min else: sys.exit('FAIL: 3rd argument of sched in getorderprice() should be [callable_fn [params]]') if len(sched[0]) > 3: # if second offset function is specfied, that applies only to the max value offsetfn = sched[0][3][0] offsetfn_params = [sched_end] + [p for p in sched[0][3][1] ] if callable(offsetfn): # this function applies to max offset_max = offsetfn(issuetime, offsetfn_params) else: sys.exit('FAIL: 4th argument of sched in getorderprice() should be [callable_fn [params]]') else: offset_min = 0.0 offset_max = 0.0 pmin = sysmin_check(offset_min + min(sched[0][0], sched[0][1])) pmax = sysmax_check(offset_max + max(sched[0][0], sched[0][1])) prange = pmax - pmin stepsize = prange / (n - 1) halfstep = round(stepsize / 2.0) if mode == 'fixed': orderprice = pmin + int(i * stepsize) elif mode == 'jittered': orderprice = pmin + int(i * stepsize) + random.randint(-halfstep, halfstep) elif mode == 'random': if len(sched) > 1: # more than one schedule: choose one equiprobably s = random.randint(0, len(sched) - 1) pmin = sysmin_check(min(sched[s][0], sched[s][1])) pmax = sysmax_check(max(sched[s][0], sched[s][1])) orderprice = random.randint(pmin, pmax) else: sys.exit('FAIL: Unknown mode in schedule') orderprice = sysmin_check(sysmax_check(orderprice)) return orderprice def getissuetimes(n_traders, mode, interval, shuffle, fittointerval): # generates a set of issue times for the customer orders to arrive at interval = float(interval) if n_traders < 1: sys.exit('FAIL: n_traders < 1 in getissuetime()') elif n_traders == 1: tstep = interval else: tstep = interval / (n_traders - 1) arrtime = 0 issuetimes = [] for t in range(n_traders): if mode == 'periodic': arrtime = interval elif mode == 'drip-fixed': arrtime = t * tstep elif mode == 'drip-jitter': arrtime = t * tstep + tstep * random.random() elif mode == 'drip-poisson': # poisson requires a bit of extra work interarrivaltime = random.expovariate(n_traders / interval) arrtime += interarrivaltime else: sys.exit('FAIL: unknown time-mode in getissuetimes()') issuetimes.append(arrtime) # at this point, arrtime is the *last* arrival time if fittointerval and mode == 'drip-poisson' and (arrtime != interval) : # generated sum of interarrival times longer than the interval # squish them back so that last arrival falls at t=interval for t in range(n_traders): issuetimes[t] = interval * (issuetimes[t] / arrtime) # optionally randomly shuffle the times if shuffle: for t in range(n_traders): i = (n_traders - 1) - t j = random.randint(0, i) tmp = issuetimes[i] issuetimes[i] = issuetimes[j] issuetimes[j] = tmp return issuetimes def getschedmode(time, os): # os is order schedules got_one = False for sched in os: if (sched['from'] <= time) and (time < sched['to']) : # within the timezone for this schedule schedrange = sched['ranges'] mode = sched['stepmode'] sched_end_time = sched['to'] got_one = True exit # jump out the loop -- so the first matching timezone has priority over any others if not got_one: sys.exit('Fail: time=%5.2f not within any timezone in os=%s' % (time, os)) return (schedrange, mode, sched_end_time) n_buyers = trader_stats['n_buyers'] n_sellers = trader_stats['n_sellers'] shuffle_times = True cancellations = [] oid = base_oid max_qty = 1 if len(pending) < 1: # list of pending (to-be-issued) customer orders is empty, so generate a new one new_pending = [] # demand side (buyers) issuetimes = getissuetimes(n_buyers, os['timemode'], os['interval'], shuffle_times, True) ordertype = 'Bid' orderstyle = 'LIM' (sched, mode, sched_end) = getschedmode(time, os['dem']) for t in range(n_buyers): issuetime = time + issuetimes[t] tname = 'B%02d' % t ## flag orderprice = getorderprice(t, sched_end, sched, n_buyers, mode, issuetime) # if time<101 or (time>201 and time <301) or (time>401 and time<501): # orderprice = 150 # else: # orderprice = 100 orderqty = random.randint(1,max_qty) # order = Order(tname, ordertype, orderstyle, orderprice, orderqty, issuetime, None, oid) order = Assignment("CUS", tname, ordertype, orderstyle, orderprice, orderqty, issuetime, None, oid) oid += 1 new_pending.append(order) # supply side (sellers) issuetimes = getissuetimes(n_sellers, os['timemode'], os['interval'], shuffle_times, True) ordertype = 'Ask' orderstyle = 'LIM' (sched, mode, sched_end) = getschedmode(time, os['sup']) for t in range(n_sellers): issuetime = time + issuetimes[t] tname = 'S%02d' % t orderprice = getorderprice(t, sched_end, sched, n_sellers, mode, issuetime) # if time<101 or (time>201 and time <301) or (time>401 and time<501): # orderprice = 50 # else: # orderprice = 20 orderqty = random.randint(1, max_qty) # order = Order(tname, ordertype, orderstyle, orderprice, orderqty, issuetime, None, oid) order = Assignment("CUS", tname, ordertype, orderstyle, orderprice, orderqty, issuetime, None, oid) oid += 1 new_pending.append(order) else: # there are pending future orders: issue any whose timestamp is in the past new_pending = [] for order in pending: if order.time < time: # this order should have been issued by now # issue it to the trader tname = order.trad_id response = traders[tname].add_cust_order(order, verbose) if verbose: print('Customer order: %s %s' % (response, order)) if response == 'LOB_Cancel' : cancellations.append(tname) if verbose: print('Cancellations: %s' % (cancellations)) # and then don't add it to new_pending (i.e., delete it) else: # this order stays on the pending list new_pending.append(order) return [new_pending, cancellations, oid] # one session in the market def market_session(sess_id, starttime, endtime, trader_spec, order_schedule, summaryfile, tapedumpfile, blotterdumpfile, dump_each_trade, verbose): n_exchanges = 1 tape_depth = 5 # number of most-recent items from tail of tape to be published at any one time verbosity = False verbose = verbosity # main loop verbosity orders_verbose = verbosity lob_verbose = False process_verbose = False respond_verbose = False bookkeep_verbose = False # fname = 'prices' + sess_id +'.csv' # prices_data_file = open(fname, 'w') # initialise the exchanges exchanges = [] for e in range(n_exchanges): eid = "Exch%d" % e exch = Exchange(eid) exchanges.append(exch) if verbose: print('Exchange[%d] =%s' % (e, str(exchanges[e]))) # create a bunch of traders traders = {} trader_stats = populate_market(trader_spec, traders, True, verbose) # print 'describe traders:' # for tid in traders: # print 'trader.ttype: %s , trader.tid: %s' %(traders[tid].ttype,tid) # timestep set so that can process all traders in one second # NB minimum inter-arrival time of customer orders may be much less than this!! timestep = 1.0 / float(trader_stats['n_buyers'] + trader_stats['n_sellers']) duration = float(endtime - starttime) last_update = -1.0 time = starttime next_order_id = 0 pending_cust_orders = [] if verbose: print('\n%s; ' % (sess_id)) tid = None while time < endtime: # how much time left, as a percentage? time_left = (endtime - time) / duration if verbose: print('\n\n%s; t=%08.2f (percent remaining: %4.1f/100) ' % (sess_id, time, time_left*100)) trade = None # get any new assignments (customer orders) for traders to execute # and also any customer orders that require previous orders to be killed [pending_cust_orders, kills, noid] = customer_orders(time, last_update, traders, trader_stats, order_schedule, pending_cust_orders, next_order_id, orders_verbose) next_order_id = noid if verbose: print('t:%f, noid=%d, pending_cust_orders:' % (time, noid)) for order in pending_cust_orders: print('%s; ' % str(order)) # if any newly-issued customer orders mean quotes on the LOB need to be cancelled, kill them if len(kills) > 0: if verbose: print('Kills: %s' % (kills)) for kill in kills: # if verbose: print('lastquote=%s' % traders[kill].lastquote) if traders[kill].lastquote != None : if verbose: print('Killing order %s' % (str(traders[kill].lastquote))) can_order = traders[kill].lastquote can_order.ostyle = "CAN" exch_response = exchanges[0].process_order(time, can_order, process_verbose) exch_msg = exch_response['trader_msgs'] # do the necessary book-keeping # NB this assumes CAN results in a single message back from the exchange traders[kill].bookkeep(exch_msg[0], time, bookkeep_verbose) for t in traders: if len(traders[t].orders) > 0: # print("Tyme=%5.2d TID=%s Orders[0]=%s" % (time, traders[t].tid, traders[t].orders[0])) dummy = 0 # NOP # get public lob data from each exchange lobs = [] for e in range(n_exchanges): exch = exchanges[e] lob = exch.publish_lob(time, tape_depth, lob_verbose) # if verbose: print ('Exchange %d, Published LOB=%s' % (e, str(lob))) lobs.append(lob) # quantity-spike injection # this next bit is a KLUDGE that is VERY FRAGILE and has lots of ARBITRARY CONSTANTS in it :-( # it is introduced for George Church's project # to edit this you have to know how many traders there are (specified in main loop) # and you have to know the details of the supply and demand curves too (again, spec in main loop) # before public release of this code, tidy it up and parameterise it nicely # triggertime = 20 # replenish_period = 20 # highest_buyer_index = 10 # this buyer has the highest limit price # highest_seller_index = 20 # big_qty = 222 # # if time > (triggertime - 3*timestep) and ((time+3*timestep) % replenish_period) <= (timestep): # # sys.exit('Bailing at injection trigger, time = %f' % time) # print ('time: %f')%(time) # # here we inject big quantities nto both buyer and seller sides... hopefully the injected traders will do a deal # pending_cust_orders[0].qty = big_qty # # # # pending_cust_orders[highest_seller_index-1].qty = big_qty # # if verbose: print ('t:%f SPIKE INJECTION (Post) Exchange %d, Published LOB=%s' % (time, e, str(lob))) # # print('t:%f, Spike Injection: , microp=%s, pending_cust_orders:' % (time, lob['microprice']) ) # for order in pending_cust_orders: print('%s; ' % str(order)) triggertime = 100 replenish_period = 100 highest_buyer_index = 10 # this buyer has the highest limit price highest_seller_index = 20 big_qty = 200 if time > (triggertime - 3*timestep) and ((time+3*timestep) % replenish_period) <= (2 * timestep): # sys.exit('Bailing at injection trigger, time = %f' % time) ##print "inject at", time for assigment in pending_cust_orders: if traders[assigment.trad_id].ttype == 'AAAA': assigment.qty = big_qty # print 'block order comes in' # print 'trad_id: %s, price: %i qty: %i , time : %i' %(assigment.trad_id,assigment.price,assigment.qty,assigment.time) # get a quote (or None) from a randomly chosen trader # first randomly select a trader id old_tid = tid while tid == old_tid: tid = list(traders.keys())[random.randint(0, len(traders) - 1)] # currently, all quotes/orders are issued only to the single exchange at exchanges[0] # it is that exchange's responsibility to then deal with Order Protection / trade-through (Reg NMS Rule611) # i.e. the exchange logic could/should be extended to check the best LOB price of each other exchange # that is yet to be implemented here # if((time >= replenish_period and time % replenish_period <= 0.001)): # print 'time: %f' %(time) # tid = 'B00' # order = traders[tid].getorder(time, time_left, lobs[0], verbose) # print str(order); # print '11111111111111111111111' # # else: # order = traders[tid].getorder(time, time_left, lobs[0], verbose) # if((time >= replenish_period and time % replenish_period <= 0.05)): # print 'time: %f' %(time) # tid = 'B00' # order = traders[tid].getorder(time, time_left, lobs[0], verbose) # print str(order); # print '11111111111111111111111' # # else: # order = traders[tid].getorder(time, time_left, lobs[0], verbose) order = traders[tid].getorder(time, time_left, lobs[0], verbose) if order != None: # print '' # print '' # print '' # print('Trader Order: %s' % str(order)) order.myref = traders[tid].orders[0].assignmentid # attach customer order ID to this exchange order if verbose: print('Order with myref=%s' % order.myref) # Sanity check: catch bad traders here traderprice = traders[tid].orders[0].price if order.otype == 'Ask' and order.price < traderprice: sys.exit('Bad ask: Trader.price %s, Quote: %s' % (traderprice,order)) if order.otype == 'Bid' and order.price > traderprice: sys.exit('Bad bid: Trader.price %s, Quote: %s' % (traderprice,order)) # how many quotes does this trader already have sat on an exchange? if len(traders[tid].quotes) >= traders[tid].max_quotes : # need to clear a space on the trader's list of quotes, by deleting one # new quote replaces trader's oldest previous quote # bit of a kludge -- just deletes oldest quote, which is at head of list # THIS SHOULD BE IN TRADER NOT IN MAIN LOOP?? TODO can_order = traders[tid].quotes[0] if verbose: print('> can_order %s' % str(can_order)) can_order.ostyle = "CAN" if verbose: print('> can_order %s' % str(can_order)) # send cancellation to exchange exch_response = exchanges[0].process_order(time, can_order, process_verbose) exch_msg = exch_response['trader_msgs'] tape_sum = exch_response['tape_summary'] if verbose: print('>Exchanges[0]ProcessOrder: tradernquotes=%d, quotes=[' % len(traders[tid].quotes)) for q in traders[tid].quotes: print('%s' % str(q)) print(']') for t in traders: if len(traders[t].orders) > 0: # print(">Exchanges[0]ProcessOrder: Tyme=%5.2d TID=%s Orders[0]=%s" % (time, traders[t].tid, traders[t].orders[0])) NOP = 0 if len(traders[t].quotes) > 0: # print(">Exchanges[0]ProcessOrder: Tyme=%5.2d TID=%s Quotes[0]=%s" % (time, traders[t].tid, traders[t].quotes[0])) NOP = 0 # do the necessary book-keeping # NB this assumes CAN results in a single message back from the exchange traders[tid].bookkeep(exch_msg[0], time, bookkeep_verbose) if verbose: # print('post-check: tradernquotes=%d, quotes=[' % len(traders[tid].quotes)) for q in traders[tid].quotes: print('%s' % str(q)) print(']') for t in traders: if len(traders[t].orders) > 0: # print("PostCheck Tyme=%5.2d TID=%s Orders[0]=%s" % (time, traders[t].tid, traders[t].orders[0])) if len(traders[t].quotes) > 0: # print("PostCheck Tyme=%5.2d TID=%s Quotes[0]=%s" % (time, traders[t].tid, traders[t].quotes[0])) NOP = 0 if len(traders[t].orders) > 0 and traders[t].orders[0].astyle == "CAN": sys.stdout.flush() sys.exit("CAN error") # add order to list of live orders issued by this trader traders[tid].quotes.append(order) if verbose: print('Trader %s quotes[-1]: %s' % (tid, traders[tid].quotes[-1])) # send this order to exchange and receive response exch_response = exchanges[0].process_order(time, order, process_verbose) exch_msgs = exch_response['trader_msgs'] tape_sum = exch_response['tape_summary'] # because the order just processed might have changed things, now go through each # order resting at the exchange and see if it can now be processed # applies to AON, ICE, OSO, and OCO #print('Exch_Msgs: ') # if exch_msgs == None: pass # else: # for msg in exch_msgs: # print('Msg=%s' % msg) if exch_msgs != None and len(exch_msgs) > 0: # messages to process for msg in exch_msgs: if verbose: print('Message: %s' % msg) traders[msg.tid].bookkeep(msg, time, bookkeep_verbose) # traders respond to whatever happened # needs to be updated for multiple exchanges lob = exchanges[0].publish_lob(time, tape_depth, lob_verbose) s = '%6.2f, ' % time for t in traders: # NB respond just updates trader's internal variables # doesn't alter the LOB, so processing each trader in # sequence (rather than random/shuffle) isn't a problem traders[t].respond(time, lob, tape_sum, respond_verbose) # if traders[t].ttype == 'ISHV': # print('%6.2f, ISHV Print, %s' % (time, str(traders[t]))) # lq = traders[t].lastquote # print('lq = %s' % lq) # if lq != None : # price = lq.price # else: price = None # if price == None: s = s + '-1, ' # else: s = s + '%s, ' % price # prices_data_file.write('%s\n' % s) # if (lob['microprice'] == None or lob['midprice'] == None): # print 'microprice is none' # print 'midprice is none ' # print 'microprice: ' # print lob['microprice'] # print 'midprice: ' # print lob['midprice'] # print 'bid anon:' # print lob['bids']['lob'] # print 'ask anon:' # print lob['asks']['lob'] time = time + timestep # end of an experiment -- dump the tape exchanges[0].dump_tape(sess_id, tapedumpfile, 'keep',traders) # traders dump their blotters for t in traders: tid = traders[t].tid ttype = traders[t].ttype balance = traders[t].balance blot = traders[t].blotter blot_len = len(blot) # build csv string for all events in blotter csv = '' estr = "TODO " for e in blot: # print(blot) # estr = '%s, %s, %s, %s, %s, %s, ' % (e['type'], e['time'], e['price'], e['qty'], e['party1'], e['party2']) csv = csv + estr blotterdumpfile.write('%s, %s, %s, %s, %s, %s\n' % (sess_id, tid, ttype, balance, blot_len, csv)) # write summary trade_stats for this experiment (end-of-session summary ONLY) for e in range(n_exchanges): trade_stats(sess_id, traders, summaryfile, time, exchanges[e].publish_lob(time, None, lob_verbose)) ############################# # # Below here is where we set up and run a series of experiments if __name__ == "__main__": start_time = 0.0 end_time = 200.0 duration = end_time - start_time range1 = (50,50) range2 = (20,20) supply_schedule = [{'from': 0, 'to': end_time, 'ranges': [range1], 'stepmode': 'fixed'}] # supply_schedule = [{'from': 0, 'to': 100, 'ranges': [(50,50)], 'stepmode': 'fixed'}, # {'from': 100, 'to': 200, 'ranges': [(50,150)], 'stepmode': 'fixed'}, # {'from': 200, 'to': 300, 'ranges': [(50,150)], 'stepmode': 'fixed'}, # {'from': 300, 'to': 500, 'ranges': [(50,50)], 'stepmode': 'fixed'} # # ] range3 = (150, 150) range4 = (100, 100) demand_schedule = [{'from': 0, 'to': end_time, 'ranges': [range3], 'stepmode': 'fixed'}] # demand_schedule = [{'from': 0, 'to': 100, 'ranges': [(150,150)], 'stepmode': 'fixed'}, # {'from': 100, 'to': 200, 'ranges': [(50,150)], 'stepmode': 'fixed'}, # {'from': 200, 'to': 300, 'ranges': [(150,150)], 'stepmode': 'fixed'}, # {'from': 300, 'to': 500, 'ranges': [(50,150)], 'stepmode': 'fixed'} # # ] order_sched = {'sup': supply_schedule, 'dem': demand_schedule, 'interval': 100, 'timemode': 'periodic'} ## 'AAAA' holds the block order buyers_spec = [('AAA',10),('AAAA',10)] sellers_spec = [ ('AA',10),('IAA_MLOFI',10)] # buyers_spec = [('BID_IGDX_3', 10), ('BID_IZIP_3', 10), ('BID_IAA_3', 10),('BID_ISHV_3', 10), ('AAAA', 10)] # sellers_spec = [('BID_IGDX_3', 10), ('BID_IZIP_3', 10), ('BID_IAA_3', 10),('BID_ISHV_3', 10), ('AAAA', 10)] traders_spec = {'sellers':sellers_spec, 'buyers':buyers_spec} sys.stdout.flush() fname = 'Mybalances.csv' summary_data_file = open(fname, 'w') fname = 'Mytapes.csv' tape_data_file = open(fname, 'w') fname = 'Myblotters.csv' blotter_data_file = open(fname, 'w') for session in range(100): sess_id = 'Test%02d' % session print('Session %s; ' % sess_id) market_session(sess_id, start_time, end_time, traders_spec, order_sched, summary_data_file, tape_data_file, blotter_data_file, True, False) summary_data_file.close() tape_data_file.close() blotter_data_file.close() print('\n Experiment Finished') ================================================ FILE: ZhenZhang/source/BSE2_msg_classes.py ================================================ # Assignment: # The details of a customer's order/request, assigned to a trader class Assignment: def __init__(self, customer_id, trader_id, otype, ostyle, price, qty, time, endtime, assignmentid): self.cust_id = customer_id # customer identifier self.trad_id = trader_id # trader this customer order is assigned to self.atype = otype # order type (buy or sell) self.astyle = ostyle # order style: MKT, LIM, etc self.price = price # price self.qty = qty # quantity self.time = time # timestamp: time at which customer issued order self.endtime = endtime # time at which order should expire (e.g. for GFD and AON orders) self.assignmentid = assignmentid # i.d. (unique identifier for each assignment) def __str__(self): return '[%s %s %s %s P=%03d Q=%s T=%5.2f AID:%d]' % \ (self.cust_id, self.trad_id, self.atype, self.astyle, self.price, self.qty, self.time, self.assignmentid) # Order/quote, submitted by trader to exchange # has a trader id, a type (buy/sell), a style (LIM, MKT, etc), a price, # a quantity, a timestamp, and a unique i.d. # The order-style may require additional parameters which are bundled into style_params (=None if not) class Order: def __init__(self, trader_id, otype, ostyle, price, qty, time, endtime, orderid): self.tid = trader_id # trader i.d. self.otype = otype # order type (bid or ask -- what side of LOB is it for) self.ostyle = ostyle # order style: MKT, LIM, etc self.price = price # price self.qty = qty # quantity self.time = time # timestamp self.endtime = endtime # time at which exchange deletes order (e.g. for GFD and AON orders) self.orderid = orderid # quote i.d. (unique to each quote, assigned by exchange) self.myref = None # trader's own reference for this order -- used to link back to assignment-ID self.styleparams = None # style parameters -- initially null, filled in later def __str__(self): return '[%s %s %s P=%03d Q=%s T=%5.2f OID:%d Params=%s MyRef=%s]' % \ (self.tid, self.otype, self.ostyle, self.price, self.qty, self.time, self.orderid, str(self.styleparams), self.myref) # structure of the messages that the exchange sends back to the traders after processing an order class Exch_msg: def __init__(self, trader_id, order_id, eventtype, transactions, revised_order, fee, balance): self.tid = trader_id # trader i.d. self.oid = order_id # order i.d. self.event = eventtype # what happened? (ACKnowledged|PARTial|FILLed|FAILure) self.trns = transactions # list of transactions (price, qty, etc) details for this order self.revo = revised_order # revised order as created by exchange matching engine self.fee = fee # exchange fee self.balance = balance # exchange's record of this trader's balance def __str__(self): return 'TID:%s OID:%s Event:%s Trns:%s RevO:%s Fee:%d Bal:%d' % \ (self.tid, self.oid, self.event, str(self.trns), str(self.revo), self.fee, self.balance) ================================================ FILE: ZhenZhang/source/BSE_trader_agents.py ================================================ from BSE2_msg_classes import Assignment, Order, Exch_msg ##################--Traders below here--############# import random import math ##################--Traders below here--############# bse_sys_minprice = 1 # minimum price in the system, in cents/pennies bse_sys_maxprice = 200 # maximum price in the system, in cents/pennies # Trader superclass # all Traders have a trader id, bank balance, blotter, and list of orders to execute class Trader: def __init__(self, ttype, tid, balance, time): self.ttype = ttype # what type / strategy this trader is self.tid = tid # trader unique ID code self.balance = balance # money in the bank self.blotter = [] # record of trades executed self.orders = [] # customer orders currently being worked self.max_cust_orders = 1 # maximum number of distinct customer orders allowed at any one time. self.quotes = [] # distinct quotes currently live on the LOB self.max_quotes = 1 # maximum number of distinct quotes allowed on LOB self.willing = 1 # used in ZIP etc self.able = 1 # used in ZIP etc self.birthtime = time # used when calculating age of a trader/strategy self.profitpertime = 0 # profit per unit time self.n_trades = 0 # how many trades has this trader done? self.lastquote = None # record of what its most recent quote was/is (incl price) def __str__(self): blotterstring = '' for b in self.blotter : blotterstring = blotterstring + '[[%s], %s]' % (str(b[0]), b[1]) return '[TID=%s type=%s balance=%s blotter=%s orders=%s n_trades=%s profitpertime=%s]' \ % (self.tid, self.ttype, self.balance, blotterstring, self.orders, self.n_trades, self.profitpertime) def add_cust_order(self, order, verbose): # add a customer order to trader's records # currently LAZY: keeps to within max_cust_orders by appending new order and deleting head self.orders if len(self.quotes) > 0: # this trader has a live quote on the LOB, from a previous customer order # need response to signal cancellation/withdrawal of that quote response = 'LOB_Cancel' else: response = 'Proceed' if len(self.orders) >= self.max_cust_orders: self.orders = self.orders[1:] self.orders.append(order) if verbose: print('add_order < response=%s self.orders=%s' % (response, str(self.orders))) return response # delete a customer order from trader's list of orders being worked def del_cust_order(self, cust_order_id, verbose): if verbose: print('>del_cust_order: Cust_orderID=%s; self.orders=' % cust_order_id) for o in self.orders: print('%s ' % str(o)) cust_orders = [] for co in self.orders: if co.assignmentid != cust_order_id: cust_orders.append(co) self.orders = cust_orders # revise a customer order: used after a PARTial fill on the exchange def revise_cust_order(self, cust_order_id, revised_order, verbose): if verbose: print('>revise_cust_order: Cust_orderID=%s; revised_order=%s, self.orders=' % (cust_order_id, revised_order)) for o in self.orders: print('%s ' % str(o)) cust_orders = [] for co in self.orders: if co.assignmentid != cust_order_id: cust_orders.append(co) else: revised_assignment = co revised_assignment.qty = revised_order.qty cust_orders.append(revised_assignment) self.orders = cust_orders if verbose: print('del_exch_order: OID:%d; self.quotes=' % oid) for q in self.quotes: print('%s ' % str(q)) exch_orders = [] for eo in self.quotes: if eo.orderid != oid: exch_orders.append(eo) self.quotes= exch_orders def bookkeep(self, msg, time, verbose): # bookkeep(): trader book-keeping in response to message from the exchange # update records of what orders are still being worked, account balance, etc. # trader's blotter is a simple sequential record of each exchange messages received, and the trader's balance after bookeeping that msh if verbose: print('>bookkeep msg=%s bal=%d' % (msg, self.balance)) profit = 0 if msg.event == "CAN": # order was cancelled at the exchange # so delete the order from the trader's records of what quotes it has live on the exchange if verbose: print(">CANcellation: msg=%s quotes=" % str(msg)) for q in self.quotes: print("%s" % str(q)) newquotes = [] for q in self.quotes: if q.orderid != msg.oid: newquotes.append(q) self.quotes = newquotes if verbose: print("bookkeep() deleting customer order ID=%s' % cust_order_id) self.del_cust_order(cust_order_id, verbose) # delete this customer order if verbose: print(">bookkeep() deleting OID:%d from trader's exchange-order records" % exch_order.orderid) self.del_exch_order(exch_order.orderid, verbose) # delete the exchange-order from trader's records elif msg.event == "PART": # the customer order is still live, but its quantity needs updating if verbose: print('>bookkeep() PART-filled order updating qty on customer order ID=%s' % cust_order_id) self.revise_cust_order(cust_order_id, msg.revo, verbose) # delete this customer order if exch_order.ostyle == "IOC": # a partially filled IOC has the non-filled portion cancelled at the exchange, # so the trader's order records need to be updated accordingly if verbose: print(">bookkeep() PART-filled IOC cancels remainder: deleting OID:%d from trader's exchange-order records" % exch_order.orderid) self.del_exch_order(exch_order.orderid, verbose) # delete the exchange-order from trader's records self.blotter.append([msg, self.balance]) # add trade record to trader's blotter # specify how trader responds to events in the market # this is a null action, expect it to be overloaded by specific algos def respond(self, time, lob, trade, verbose): return None # specify how trader mutates its parameter values # this is a null action, expect it to be overloaded by specific algos def mutate(self, time, lob, trade, verbose): return None # Trader subclass Giveaway # even dumber than a ZI-U: just give the deal away # (but never makes a loss) class Trader_Giveaway(Trader): def getorder(self, time, countdown, lob, verbose): if verbose: print('GVWY getorder:') if len(self.orders) < 1: order = None else: quoteprice = self.orders[0].price order = Order(self.tid, self.orders[0].atype, self.orders[0].astyle, quoteprice, self.orders[0].qty, time, None, -1) self.lastquote=order return order # Trader subclass ZI-C # After Gode & Sunder 1993 class Trader_ZIC(Trader): def getorder(self, time, countdown, lob, verbose): if verbose: print('ZIC getorder:') if len(self.orders) < 1: # no orders: return NULL order = None else: minprice = lob['bids']['worstp'] maxprice = lob['asks']['worstp'] limit = self.orders[0].price otype = self.orders[0].atype ostyle = self.orders[0].astyle if otype == 'Bid': oprice = random.randint(minprice, limit) else: oprice = random.randint(limit, maxprice) # NB should check it == 'Ask' and barf if not order = Order(self.tid, otype, ostyle, oprice, self.orders[0].qty, time, None, -1) self.lastquote = order return order # Trader subclass Shaver # shaves a penny off the best price class Trader_Shaver(Trader): def getorder(self, time, countdown, lob, verbose): if verbose: print("SHVR getorder:") if len(self.orders) < 1: order = None else: if verbose: print(" self.orders[0]=%s" % str(self.orders[0])) limitprice = self.orders[0].price otype = self.orders[0].atype ostyle = self.orders[0].astyle if otype == 'Bid': if lob['bids']['n'] > 0: quoteprice = lob['bids']['bestp'] + 1 if quoteprice > limitprice : quoteprice = limitprice else: quoteprice = lob['bids']['worstp'] else: if lob['asks']['n'] > 0: quoteprice = lob['asks']['bestp'] - 1 if quoteprice < limitprice: quoteprice = limitprice else: quoteprice = lob['asks']['worstp'] order = Order(self.tid, otype, ostyle, quoteprice, self.orders[0].qty, time, None, -1) self.lastquote = order return order # Trader subclass Imbalance-sensitive Shaver # shaves X off the best price, where X depends on supply/demand imbalance class Trader_ISHV(Trader): def getorder(self, time, countdown, lob, verbose): if verbose: print("ISHV getorder:") shave_c = 2 # c in the y=mx+c linear mapping from imbalance to shave amount shave_m = 1 # m in the y=mx+c if len(self.orders) < 1: order = None else: if verbose: print(" self.orders[0]=%s" % str(self.orders[0])) limitprice = self.orders[0].price otype = self.orders[0].atype ostyle = self.orders[0].astyle microp = lob['microprice'] midp = lob['midprice'] if microp != None and midp != None: imbalance = microp - midp else: imbalance = 0 # if imbalance is undefined, proceed as if it is equal to zero if otype == 'Bid': # quantity sensitivity if imbalance < 0 : shaving = 1 # imbalance in favour of buyers, so shave slowly else: shaving = shave_c + (shave_m * int(imbalance*100)/100) # shave ever larger amounts # print('t:%f, ISHV (Bid) imbalance=%s shaving=%s' % (time, imbalance, shaving)) if len(lob['bids']['lob']) > 0: quoteprice = lob['bids']['bestp'] + shaving if quoteprice > limitprice : quoteprice = limitprice else: quoteprice = 1 #KLUDGE -- come back to fix todo else: # quantity sensitivity if imbalance > 0 : shaving = 1 else: shaving = shave_c - (shave_m * int(imbalance*100)/100) # print('t:%f, ISHV (Ask) imbalance=%s shaving=%s' % (time, imbalance, shaving)) if len(lob['asks']['lob']) > 0: quoteprice = lob['asks']['bestp'] - shaving if quoteprice < limitprice : quoteprice = limitprice else: quoteprice = 200 #KLUDGE -- come back to fix todo order = Order(self.tid, otype, ostyle, quoteprice, self.orders[0].qty, time, None, verbose) self.lastquote = order return order # Trader subclass Sniper # Based on Shaver, inspired by Kaplan # "lurks" until time remaining < threshold% of the trading session # then gets increasing aggressive, increasing "shave thickness" as time runs out class Trader_Sniper(Trader): def getorder(self, time, countdown, lob, verbose): if verbose: print('SNPR getorder: self.orders[0]=%s' % str(self.orders[0])) lurk_threshold = 0.2 shavegrowthrate = 3 shave = int(1.0 / (0.01 + countdown / (shavegrowthrate * lurk_threshold))) if (len(self.orders) < 1) or (countdown > lurk_threshold): order = None else: limitprice = self.orders[0].price otype = self.orders[0].otype ostyle = self.orders[0].ostyle if otype == 'Bid': if lob['bids']['n'] > 0: oprice = lob['bids']['bestp'] + shave if oprice > limitprice: oprice = limitprice else: oprice = lob['bids']['worstp'] else: if lob['asks']['n'] > 0: oprice = lob['asks']['bestp'] - shave if oprice < limitprice: oprice = limitprice else: oprice = lob['asks']['worstp'] order = Order(self.tid, otype, ostyle, oprice, self.orders[0].qty, time, None, -1) self.lastquote = order return order # Trader subclass ZIP # After Cliff 1997 class Trader_ZIP(Trader): # ZIP init key param-values are those used in Cliff's 1997 original HP Labs tech report # NB this implementation keeps separate margin values for buying & selling, # so a single trader can both buy AND sell # -- in the original, traders were either buyers OR sellers def __init__(self, ttype, tid, balance, time): Trader.__init__(self, ttype, tid, balance, time) m_fix = 0.05 m_var = 0.05 self.job = None # this is 'Bid' or 'Ask' depending on customer order self.active = False # gets switched to True while actively working an order self.prev_change = 0 # this was called last_d in Cliff'97 self.beta = 0.1 + 0.2 * random.random() # learning rate self.momntm = 0.3 * random.random() # momentum self.ca = 0.10 # self.ca & .cr were hard-coded in '97 but parameterised later self.cr = 0.10 self.margin = None # this was called profit in Cliff'97 self.margin_buy = -1.0 * (m_fix + m_var * random.random()) self.margin_sell = m_fix + m_var * random.random() self.price = None self.limit = None # memory of best price & quantity of best bid and ask, on LOB on previous update self.prev_best_bid_p = None self.prev_best_bid_q = None self.prev_best_ask_p = None self.prev_best_ask_q = None # memory of worst prices from customer orders received so far self.worst_bidprice = None self.worst_askprice = None def __str__(self): s = '%s, job=, %s, ' % (self.tid, self.job) if self.active == True: s = s +'actv=,T, ' else: s = s + 'actv=,F, ' if self.margin == None: s = s + 'mrgn=,N, ' else: s = s + 'mrgn=,%5.2f, ' % self.margin s = s + 'lmt=,%s, price=,%s, bestbid=,%s,@,%s, bestask=,%s,@,%s, wrstbid=,%s, wrstask=,%s' %\ (self.limit, self.price, self.prev_best_bid_q, self.prev_best_bid_p, self.prev_best_ask_q, self.prev_best_ask_p, self.worst_bidprice, self.worst_askprice) return(s) def getorder(self, time, countdown, lob, verbose): if verbose: print('ZIP getorder(): LOB=%s' % lob) # random coefficient, multiplier on trader's own estimate of worst possible bid/ask prices # currently in arbitrarily chosen range [2, 5] worst_coeff = 2 + (3 * random.random()) if len(self.orders) < 1: self.active = False order = None else: self.active = True self.limit = self.orders[0].price self.job = self.orders[0].atype if self.job == 'Bid': # currently a buyer (working a bid order) self.margin = self.margin_buy # what is the worst bid price on the LOB right now? if len(lob['bids']['lob']) > 0 : # take price of final entry on LOB worst_bid = lob['bids']['lob'][-1][0] else: # local pessimistic estimate of the worst bid price (own version of stub quote) worst_bid = max(1, int(self.limit / worst_coeff)) if self.worst_bidprice == None: self.worst_bidprice = worst_bid elif self.worst_bidprice > worst_bid: self.worst_bidprice = worst_bid else: # currently a seller (working a sell order) self.margin = self.margin_sell # what is the worst ask price on the LOB right now? if len(lob['asks']['lob']) > 0 : # take price of final entry on LOB worst_ask = lob['asks']['lob'][-1][0] else: # local pessimistic estimate of the worst ask price (own version of stub quote) worst_ask = int(self.limit * worst_coeff) if self.worst_askprice == None: self.worst_askprice = worst_ask elif self.worst_askprice < worst_ask: self.worst_askprice = worst_ask quoteprice = int(self.limit * (1 + self.margin)) self.price = quoteprice order = Order(self.tid, self.job, "LIM", quoteprice, self.orders[0].qty, time, None, -1) self.lastquote = order return order # update margin on basis of what happened in market def respond(self, time, lob, trade, verbose): # ZIP trader responds to market events, altering its margin # does this whether it currently has an order to work or not def target_up(price): # generate a higher target price by randomly perturbing given price ptrb_abs = self.ca * random.random() # absolute shift ptrb_rel = price * (1.0 + (self.cr * random.random())) # relative shift target = int(round(ptrb_rel + ptrb_abs, 0)) if target == price: target = price + 1 # enforce minimal difference # print('TargetUp: %d %d\n' % (price, target)) return(target) def target_down(price): # generate a lower target price by randomly perturbing given price ptrb_abs = self.ca * random.random() # absolute shift ptrb_rel = price * (1.0 - (self.cr * random.random())) # relative shift target = int(round(ptrb_rel - ptrb_abs, 0)) if target == price : target = price -1 # enforce minimal difference # print('TargetDn: %d %d\n' % (price,target)) return(target) def microshade(microprice, price): # shade in the direction of the microprice microweight = 0 if microprice != None: shaded = ((microweight * microprice) + ((1 - microweight) * price)) else: shaded = price # print('Microshade: micro=%s price=%s shaded=%s' % (microprice, price, shaded)) return(shaded) def willing_to_trade(price): # am I willing to trade at this price? willing = False if self.job == 'Bid' and self.active and self.price >= price: willing = True if self.job == 'Ask' and self.active and self.price <= price: willing = True return willing def profit_alter(*argv): # this has variable number of parameters # if passed a single numeric value, that's the target price # if passed three numeric values, that's the price, beta (learning rate), and momentum if len(argv) == 1 : price = argv[0] beta = self.beta momntm = self.momntm elif len(argv) == 3 : price = argv[0] beta = argv[1] momntm = argv[2] else: sys.stdout.flush() sys.exit('Fail: ZIP profit_alter given wrong number of parameters') # print('profit_alter: price=%s beta=%s momntm=%s' % (price, beta, momntm)) oldprice = self.price diff = price - oldprice change = ((1.0 - self.momntm) * (self.beta * diff)) + (self.momntm * self.prev_change) self.prev_change = change newmargin = ((self.price + change) / self.limit) - 1.0 if self.job == 'Bid': margin = min(newmargin, 0) self.margin_buy = margin self.margin = margin else : margin = max(0, newmargin) self.margin_sell = margin self.margin = margin # set the price from limit and profit-margin self.price = int(round(self.limit * (1.0 + self.margin), 0)) # print('old=%d diff=%d change=%d lim=%d price = %d\n' % (oldprice, diff, change, self.limit, self.price)) if verbose and trade != None: print('respond() [ZIP] time=%s tid=%s, trade=%s LOB[bids]=%s LOB[asks]=%s' % (time, self.tid, trade, lob["bids"], lob["asks"])) # what, if anything, has happened on the bid LOB? # if trade != None: print('ZIP respond() trade=%s' % trade) bid_improved = False bid_hit = False if len(lob['bids']['lob']) > 0: lob_best_bid_p = lob['bids']['lob'][0][0] else: lob_best_bid_p = None lob_best_bid_q = None # default assumption if lob_best_bid_p != None: # non-empty bid LOB if self.prev_best_bid_p > lob_best_bid_p : best_bid_p_decreased = True else: best_bid_p_decreased = False if (self.prev_best_bid_p == lob_best_bid_p) and (self.prev_best_bid_q > lob_best_bid_q): same_p_smaller_q = True else: same_p_smaller_q = False lob_best_bid_q = lob['bids']['lob'][0][1] if self.prev_best_bid_p < lob_best_bid_p : # best bid has improved # NB doesn't check if the improvement was by self bid_improved = True elif trade != None and (best_bid_p_decreased or same_p_smaller_q) : # there WAS a trade and either... # ... (best bid price has gone DOWN) or (best bid price is same but quantity at that price has gone DOWN) # then assume previous best bid was hit bid_hit = True elif self.prev_best_bid_p != None: # the bid LOB is empty now but was not previously: so was it canceled or lifted? if trade != None: # a trade has occurred and the previously nonempty ask LOB is now empty # so assume best ask was lifted bid_hit = True else: bid_hit = False # if verbose: print("LOB[bids]=%s bid_improved=%s bid_hit=%s" % (lob['bids'], bid_improved, bid_hit)) # what, if anything, has happened on the ask LOB? ask_improved = False ask_lifted = False if len(lob['asks']['lob']) > 0: lob_best_ask_p = lob['asks']['lob'][0][0] else: lob_best_ask_p = None lob_best_ask_q = None if lob_best_ask_p != None: # non-empty ask LOB if self.prev_best_ask_p < lob_best_ask_p: best_ask_p_increased = True else: best_ask_p_increased = False if (self.prev_best_ask_p == lob_best_ask_p) and (self.prev_best_ask_q > lob_best_ask_q): same_p_smaller_q = True else: same_p_smaller_q = False lob_best_ask_q = lob['asks']['lob'][0][1] if self.prev_best_ask_p > lob_best_ask_p : # best ask has improved -- NB doesn't check if the improvement was by self ask_improved = True elif trade != None and (best_ask_p_increased or same_p_smaller_q): # trade happened and best ask price has got worse, or stayed same but quantity reduced -- assume previous best ask was lifted ask_lifted = True elif self.prev_best_ask_p != None: # the ask LOB is empty now but was not previously: so was it canceled or lifted? if trade != None: # a trade has occurred and the previously nonempty ask LOB is now empty # so assume best ask was lifted ask_lifted = True else: ask_lifted = False # if verbose: print("LOB[asks]=%s ask_improved=%s ask_lifted=%s" % (lob['asks'], ask_improved, ask_lifted)) if verbose and (bid_improved or bid_hit or ask_improved or ask_lifted): print('ZIP respond() B_improved=%s; B_hit=%s A_improved=%s, A_lifted=%s' % (bid_improved, bid_hit, ask_improved, ask_lifted)) print('Trade=%s\n' % trade) # we want to know: did a deal just happen? # if not, did the most recent bid deal = bid_hit or ask_lifted # previously... # when raising margin, tradeprice = trade['price'], targetprice = f(tradeprice) & # i.e. target price will be calculated relative to price of most recent transaction # and when lowering margin, targetprice = f(best_price_on_counterparty_side_of_LOB) or # or if LOB empty then targetprice = f(worst possible counterparty quote) <-- a system constant # new in this version: # take account of LOB's microprice if it is defined (if not, use trade['price'] as before) midp = lob['midprice'] microp = lob['microprice'] # KLUDGE for TESTING if time > 79: microp = 145 if microp != None and midp != None : imbalance = microp - midp else: imbalance = 0 # uses zero instead of None because a zero imbalance reverts ZIP to original form target_price = None # default assumption # print('self.job=%s' % self.job) if self.job == 'Ask': # seller if deal: if verbose: print ('trade',trade) tradeprice = trade['price'] # price of most recent transaction # print('tradeprice=%s lob[microprice]=%s' % (tradeprice, lob['microprice'])) shadetrade = microshade(lob['microprice'], tradeprice) refprice = shadetrade if self.price <= tradeprice: # could sell for more? raise margin target_price = target_up(refprice) profit_alter(target_price) elif ask_lifted and self.active and not willing_to_trade(tradeprice): # previous best ask was hit, # but this trader wouldn't have got the deal cos price to high, # and still working a customer order, so reduce margin target_price = target_down(refprice) profit_alter(target_price) else: # no deal: aim for a target price higher than best bid # print('lob_best_bid_p=%s lob[microprice]=%s' % (lob_best_bid_p, lob['microprice'])) refprice = microshade(lob['microprice'], lob_best_bid_p) if ask_improved and self.price > lob_best_bid_p: if lob_best_bid_p != None: target_price = target_up(lob_best_bid_p) else: if self.worst_askprice != None: target_price = self.worst_askprice # print('worst_askprice = %s' % self.worst_askprice) target_price = None #todo: does this stop the price-spikes? else: target_price = None # target_price = lob['asks']['worstp'] # stub quote if target_price != None: # print('PA1: tp=%s' % target_price) profit_alter(target_price) if self.job == 'Bid': # buyer if deal: tradeprice = trade['price'] shadetrade = microshade(lob['microprice'], tradeprice) refprice = shadetrade if lob['microprice'] != None and lob['midprice'] != None: delta = lob['microprice'] - lob['midprice'] # refprice = refprice + delta if self.price >= tradeprice : # could buy for less? raise margin (i.e. cut the price) target_price = target_down(refprice) profit_alter(target_price) elif bid_hit and self.active and not willing_to_trade(tradeprice): # wouldn't have got this deal, and still working a customer order, # so reduce margin target_price = target_up(refprice) profit_alter(target_price) else: # no deal: aim for target price lower than best ask refprice = microshade(lob['microprice'], lob_best_ask_p) if bid_improved and self.price < lob_best_ask_p: if lob_best_ask_p != None: target_price = target_down(lob_best_ask_p) else: if self.worst_bidprice != None : target_price = self.worst_bidprice target_price = None else: target_price = None # target_price = lob['bids']['worstp'] # stub quote if target_price != None: # print('PA2: tp=%s' % target_price) profit_alter(target_price) # print('time,%f,>>>,microprice,%s,>>>,target_price,%s' % (time, lob['microprice'], target_price)) # remember the best LOB data ready for next response self.prev_best_bid_p = lob_best_bid_p self.prev_best_bid_q = lob_best_bid_q self.prev_best_ask_p = lob_best_ask_p self.prev_best_ask_q = lob_best_ask_q ##########################---trader-types have all been defined now--################ class Trader_AA(Trader): def __init__(self, ttype, tid, balance, time): # Stuff about trader # self.ttype = ttype # self.tid = tid # self.balance = balance # self.birthtime = time # self.profitpertime = 0 # self.n_trades = 0 # self.blotter = [] # self.orders = [] # self.n_quotes = 0 # self.lastquote = None Trader.__init__(self, ttype, tid, balance, time) self.limit = None self.job = None # learning variables self.r_shout_change_relative = 0.05 self.r_shout_change_absolute = 0.05 self.short_term_learning_rate = random.uniform(0.1, 0.5) self.long_term_learning_rate = random.uniform(0.1, 0.5) self.moving_average_weight_decay = 0.95 # how fast weight decays with time, lower is quicker, 0.9 in vytelingum self.moving_average_window_size = 5 self.offer_change_rate = 3.0 self.theta = -2.0 self.theta_max = 2.0 self.theta_min = -8.0 self.marketMax = bse_sys_maxprice # Variables to describe the market self.previous_transactions = [] self.moving_average_weights = [] for i in range(self.moving_average_window_size): self.moving_average_weights.append(self.moving_average_weight_decay**i) self.estimated_equilibrium = [] self.smiths_alpha = [] self.prev_best_bid_p = None self.prev_best_bid_q = None self.prev_best_ask_p = None self.prev_best_ask_q = None # Trading Variables self.r_shout = None self.buy_target = None self.sell_target = None self.buy_r = -1.0 * (0.3 * random.random()) self.sell_r = -1.0 * (0.3 * random.random()) def calcEq(self): ##clear and correct # Slightly modified from paper, it is unclear inpaper # N previous transactions * weights / N in vytelingum, swap N denominator for sum of weights to be correct? if len(self.previous_transactions) == 0: return elif len(self.previous_transactions) < self.moving_average_window_size: # Not enough transactions self.estimated_equilibrium.append(float(sum(self.previous_transactions)) / max(len(self.previous_transactions), 1)) else: N_previous_transactions = self.previous_transactions[-self.moving_average_window_size:] thing = [N_previous_transactions[i]*self.moving_average_weights[i] for i in range(self.moving_average_window_size)] eq = sum( thing ) / sum(self.moving_average_weights) self.estimated_equilibrium.append(eq) def calcAlpha(self): ##correct. but calcAlpha in snashall's version is incorrect alpha = 0.0 for p in self.previous_transactions: alpha += (p - self.estimated_equilibrium[-1])**2 alpha = math.sqrt(alpha/len(self.previous_transactions)) self.smiths_alpha.append( alpha/self.estimated_equilibrium[-1] ) def calcTheta(self): ## clear and correct gamma = 2.0 #not sensitive apparently so choose to be whatever # necessary for intialisation, div by 0 if min(self.smiths_alpha) == max(self.smiths_alpha): alpha_range = 0.4 #starting value i guess else: alpha_range = (self.smiths_alpha[-1] - min(self.smiths_alpha)) / (max(self.smiths_alpha) - min(self.smiths_alpha)) theta_range = self.theta_max - self.theta_min desired_theta = self.theta_min + (theta_range) * (1 - alpha_range) * math.exp(gamma * (alpha_range - 1)) self.theta = self.theta + self.long_term_learning_rate * (desired_theta - self.theta) if self.theta > self.theta_max : self.theta = self.theta_max if self.theta < self.theta_min : self.theta = self.theta_min def calcRshout(self): ## unclear in Vytelingum's paper p = self.estimated_equilibrium[-1] l = self.limit theta = self.theta if self.job == 'Bid': # Currently a buyer if l <= p: #extramarginal! self.r_shout = 0.0 else: #intramarginal :( if self.buy_target > self.estimated_equilibrium[-1]: #r[0,1] self.r_shout = math.log(((self.buy_target - p) * (math.exp(theta) - 1) / (l - p)) + 1) / theta else: #r[-1,0] # print 'buy_target: %f , p: %f , theta: %f' %(self.buy_target,p,theta) self.r_shout = math.log((1 - (self.buy_target/p)) * (math.exp(theta) - 1) + 1) / theta # self.r_shout = self.buy_r if self.job == 'Ask': # Currently a seller if l >= p: #extramarginal! self.r_shout = 0 else: #intramarginal :( if self.sell_target > self.estimated_equilibrium[-1]: # r[-1,0] self.r_shout = math.log((self.sell_target - p) * (math.exp(theta) - 1) / (self.marketMax - p) + 1) / theta else: # r[0,1] a = (self.sell_target-l)/(p-l) self.r_shout = (math.log((1 - a) * (math.exp(theta) - 1) + 1)) / theta # self.r_shout = self.sell_r def calcAgg(self): delta = 0 if self.job == 'Bid': # BUYER if self.buy_target >= self.previous_transactions[-1] : # must be more aggressive delta = (1+self.r_shout_change_relative)*self.r_shout + self.r_shout_change_absolute else : delta = (1-self.r_shout_change_relative)*self.r_shout - self.r_shout_change_absolute self.buy_r = self.buy_r + self.short_term_learning_rate * (delta - self.buy_r) if self.job == 'Ask': # SELLER if self.sell_target > self.previous_transactions[-1] : delta = (1+self.r_shout_change_relative)*self.r_shout + self.r_shout_change_absolute else : delta = (1-self.r_shout_change_relative)*self.r_shout - self.r_shout_change_absolute self.sell_r = self.sell_r + self.short_term_learning_rate * (delta - self.sell_r) def calcTarget(self): if len(self.estimated_equilibrium) > 0: p = self.estimated_equilibrium[-1] if self.limit == p: p = p * 1.000001 # to prevent theta_bar = 0 elif self.job == 'Bid': p = self.limit - self.limit * 0.2 ## Initial guess for eq if no deals yet!!.... elif self.job == 'Ask': p = self.limit + self.limit * 0.2 l = self.limit theta = self.theta if self.job == 'Bid': #BUYER minus_thing = self.buy_r * math.exp(theta*(self.buy_r-1)) if l <= p: #Extramarginal if self.buy_r >= 0: self.buy_target = l else: self.buy_target = l * (1 - minus_thing) else: #intramarginal if self.buy_r >= 0: # theta_ba = (p * math.exp(-theta))/(l-p)-1 theta_ba = theta # print 'theta: %f' %(self.theta) # print 'theta_ba: %f '%(theta_ba) # print 'l-p: %f '%(l-p) # print 'self.buy_r :%f' %(self.buy_r) self.buy_target = (l-p)*(1-(self.buy_r+1)*math.exp(self.buy_r*theta_ba))+p else: self.buy_target = p*(1-minus_thing) if self.buy_target > l: self.buy_target = l if self.buy_target = 0: self.buy_target = p + (p-l)* self.sell_r*math.exp((self.sell_r-1)*theta) else: theta_ba = math.log((self.marketMax-p)/(p-l))-theta self.buy_target = p + (self.marketMax-p)* self.sell_r*math.exp((self.sell_r+1)*theta_ba) else: # Extramarginal if self.buy_r >= 0: self.buy_target = l else: self.buy_target = l + (self.marketMax-l)*self.sell_r*math.exp((self.sell_r-1)*theta) if self.sell_target < l: self.sell_target = l if self.sell_target > bse_sys_maxprice: self.sell_target = bse_sys_maxprice # print 'sell_target = %f'%(self.sell_target) def getorder(self, time, countdown, lob,verbose): if len(self.orders) < 1: self.active = False return None else: self.active = True self.limit = self.orders[0].price self.job = self.orders[0].atype self.calcTarget() if self.prev_best_bid_p == None: o_bid = 0 else: o_bid = self.prev_best_bid_p if self.prev_best_ask_p == None: o_ask = self.marketMax else: o_ask = self.prev_best_ask_p if self.job == 'Bid': #BUYER if self.limit <= o_bid: return None else: if len(self.previous_transactions) <= 0: ## has been at least one transaction o_ask_plus = (1+self.r_shout_change_relative)*o_ask + self.r_shout_change_absolute quoteprice = o_bid + ((min(self.limit, o_ask_plus) - o_bid) / self.offer_change_rate) else: if o_ask <= self.buy_target: quoteprice = o_ask else: quoteprice = o_bid + ((self.buy_target - o_bid) / self.offer_change_rate) if self.job == 'Ask': if self.limit >= o_ask: return None else: if len(self.previous_transactions) <= 0: ## has been at least one transaction o_bid_minus = (1-self.r_shout_change_relative) * o_bid - self.r_shout_change_absolute quoteprice = o_ask - ((o_ask - max(self.limit, o_bid_minus)) / self.offer_change_rate) else: if o_bid >= self.sell_target: quoteprice = o_bid else: quoteprice = o_ask - ((o_ask - self.sell_target) / self.offer_change_rate) def imbalancealter (quoteprice_aa, lob): if(lob['microprice']==None or lob['midprice']==None): return quoteprice_aa quoteprice_iaa = 0 imbalance_ratio = 0 volume_bids = 0 volume_asks = 0 count_bids_depth = 0 count_asks_depth = 0 for item in lob['bids']['lob']: volume_bids += math.exp(-0.5*count_bids_depth) *item[1] count_bids_depth +=1 if(count_bids_depth >=2): break for item in lob['asks']['lob']: volume_asks += math.exp(-0.5*count_asks_depth) *item[1] count_asks_depth +=1 if(count_asks_depth >=2): break if volume_bids == 0 and volume_asks == 0: return quoteprice_aa else : imbalance_ratio = (volume_bids-volume_asks)/(volume_bids+volume_asks) if self.job == 'Bid': quoteprice_iaa = quoteprice_aa+imbalance_ratio*(lob['microprice']-quoteprice_aa) if(quoteprice_iaa>self.limit): quoteprice_iaa = self.limit else: quoteprice_iaa = quoteprice_aa+ imbalance_ratio*(quoteprice_aa-lob['microprice']) if(quoteprice_iaa=3 or count_asks_depth/count_bids_depth>=3 : return quoteprice_iaa else: return quoteprice_aa # return quoteprice_aa return quoteprice_iaa # quoteprice_iaa = imbalancealter(quoteprice,lob) order = Order(self.tid, self.orders[0].atype, 'LIM', quoteprice, self.orders[0].qty, time, None, -1) self.lastquote=order return order def respond(self, time, lob, trade, verbose): ## Begin nicked from ZIP # what, if anything, has happened on the bid LOB? Nicked from ZIP.. bid_improved = False bid_hit = False lob_best_bid_p = lob['bids']['bestp'] lob_best_bid_q = None if lob_best_bid_p != None: # non-empty bid LOB lob_best_bid_q = lob['bids']['lob'][0][1] if self.prev_best_bid_p < lob_best_bid_p : # best bid has improved # NB doesn't check if the improvement was by self bid_improved = True elif trade != None and ((self.prev_best_bid_p > lob_best_bid_p) or ((self.prev_best_bid_p == lob_best_bid_p) and (self.prev_best_bid_q > lob_best_bid_q))): # previous best bid was hit bid_hit = True elif self.prev_best_bid_p != None: # # the bid LOB has been emptied: was it cancelled or hit? # last_tape_item = lob['tape'][-1] # if last_tape_item['type'] == 'Cancel' : # bid_hit = False # else: # bid_hit = True # the bid LOB is empty now but was not previously: so was it canceled or lifted? if trade != None: # a trade has occurred and the previously nonempty ask LOB is now empty # so assume best ask was lifted bid_hit = True else: bid_hit = False # what, if anything, has happened on the ask LOB? ask_improved = False ask_lifted = False lob_best_ask_p = lob['asks']['bestp'] lob_best_ask_q = None if lob_best_ask_p != None: # non-empty ask LOB lob_best_ask_q = lob['asks']['lob'][0][1] if self.prev_best_ask_p > lob_best_ask_p : # best ask has improved -- NB doesn't check if the improvement was by self ask_improved = True elif trade != None and ((self.prev_best_ask_p < lob_best_ask_p) or ((self.prev_best_ask_p == lob_best_ask_p) and (self.prev_best_ask_q > lob_best_ask_q))): # trade happened and best ask price has got worse, or stayed same but quantity reduced -- assume previous best ask was lifted ask_lifted = True elif self.prev_best_ask_p != None: # the ask LOB is empty now but was not previously: canceled or lifted? # last_tape_item = lob['tape'][-1] # if last_tape_item['type'] == 'Cancel' : # ask_lifted = False # else: # ask_lifted = True # the ask LOB is empty now but was not previously: so was it canceled or lifted? if trade != None: # a trade has occurred and the previously nonempty ask LOB is now empty # so assume best ask was lifted ask_lifted = True else: ask_lifted = False self.prev_best_bid_p = lob_best_bid_p self.prev_best_bid_q = lob_best_bid_q self.prev_best_ask_p = lob_best_ask_p self.prev_best_ask_q = lob_best_ask_q deal = bid_hit or ask_lifted ## End nicked from ZIP if deal: self.previous_transactions.append(trade['price']) if self.sell_target == None: self.sell_target = trade['price'] if self.buy_target == None: self.buy_target = trade['price'] self.calcEq() self.calcAlpha() self.calcTheta() self.calcRshout() self.calcAgg() self.calcTarget() #print 'sell: ', self.sell_target, 'buy: ', self.buy_target, 'limit:', self.limit, 'eq: ', self.estimated_equilibrium[-1], 'sell_r: ', self.sell_r, 'buy_r: ', self.buy_r, '\n' class Trader_OAA(Trader): def __init__(self, ttype, tid, balance, time): # Stuff about trader # self.ttype = ttype # self.tid = tid # self.balance = balance # self.birthtime = time # self.profitpertime = 0 # self.n_trades = 0 # self.blotter = [] # self.orders = [] # self.n_quotes = 0 # self.lastquote = None Trader.__init__(self, ttype, tid, balance, time) self.limit = None self.job = None # learning variables self.r_shout_change_relative = 0.05 self.r_shout_change_absolute = 0.05 self.short_term_learning_rate = random.uniform(0.1, 0.5) self.long_term_learning_rate = random.uniform(0.1, 0.5) self.moving_average_weight_decay = 0.95 # how fast weight decays with time, lower is quicker, 0.9 in vytelingum self.moving_average_window_size = 5 self.offer_change_rate = 3.0 self.theta = -2.0 self.theta_max = 2.0 self.theta_min = -8.0 self.marketMax = bse_sys_maxprice # Variables to describe the market self.previous_transactions = [] self.moving_average_weights = [] for i in range(self.moving_average_window_size): self.moving_average_weights.append(self.moving_average_weight_decay**i) self.estimated_equilibrium = [] self.smiths_alpha = [] self.prev_best_bid_p = None self.prev_best_bid_q = None self.prev_best_ask_p = None self.prev_best_ask_q = None # Trading Variables self.r_shout = None self.buy_target = None self.sell_target = None self.buy_r = -1.0 * (0.3 * random.random()) self.sell_r = -1.0 * (0.3 * random.random()) def calcEq(self): # Slightly modified from paper, it is unclear inpaper # N previous transactions * weights / N in vytelingum, swap N denominator for sum of weights to be correct? if len(self.previous_transactions) == 0: return elif len(self.previous_transactions) < self.moving_average_window_size: # Not enough transactions self.estimated_equilibrium.append(float(sum(self.previous_transactions)) / max(len(self.previous_transactions), 1)) else: N_previous_transactions = self.previous_transactions[-self.moving_average_window_size:] thing = [N_previous_transactions[i]*self.moving_average_weights[i] for i in range(self.moving_average_window_size)] eq = sum( thing ) / sum(self.moving_average_weights) self.estimated_equilibrium.append(eq) def calcAlpha(self): alpha = 0.0 for p in self.estimated_equilibrium: alpha += (p - self.estimated_equilibrium[-1])**2 alpha = math.sqrt(alpha/len(self.estimated_equilibrium)) self.smiths_alpha.append( alpha/self.estimated_equilibrium[-1] ) def calcTheta(self): gamma = 2.0 #not sensitive apparently so choose to be whatever # necessary for intialisation, div by 0 if min(self.smiths_alpha) == max(self.smiths_alpha): alpha_range = 0.4 #starting value i guess else: alpha_range = (self.smiths_alpha[-1] - min(self.smiths_alpha)) / (max(self.smiths_alpha) - min(self.smiths_alpha)) theta_range = self.theta_max - self.theta_min desired_theta = self.theta_min + (theta_range) * (1 - (alpha_range * math.exp(gamma * (alpha_range - 1)))) self.theta = self.theta + self.long_term_learning_rate * (desired_theta - self.theta) def calcRshout(self): p = self.estimated_equilibrium[-1] l = self.limit theta = self.theta if self.job == 'Bid': # Currently a buyer if l <= p: #extramarginal! self.r_shout = 0.0 else: #intramarginal :( if self.buy_target > self.estimated_equilibrium[-1]: #r[0,1] self.r_shout = math.log(((self.buy_target - p) * (math.exp(theta) - 1) / (l - p)) + 1) / theta else: #r[-1,0] self.r_shout = math.log((1 - (self.buy_target/p)) * (math.exp(theta) - 1) + 1) / theta if self.job == 'Ask': # Currently a seller if l >= p: #extramarginal! self.r_shout = 0 else: #intramarginal :( if self.sell_target > self.estimated_equilibrium[-1]: # r[-1,0] self.r_shout = math.log((self.sell_target - p) * (math.exp(theta) - 1) / (self.marketMax - p) + 1) / theta else: # r[0,1] a = (self.sell_target-l)/(p-l) self.r_shout = (math.log((1 - a) * (math.exp(theta) - 1) + 1)) / theta def calcAgg(self): delta = 0 if self.job == 'Bid': # BUYER if self.buy_target >= self.previous_transactions[-1] : # must be more aggressive delta = (1+self.r_shout_change_relative)*self.r_shout + self.r_shout_change_absolute else : delta = (1-self.r_shout_change_relative)*self.r_shout - self.r_shout_change_absolute self.buy_r = self.buy_r + self.short_term_learning_rate * (delta - self.buy_r) if self.job == 'Ask': # SELLER if self.sell_target > self.previous_transactions[-1] : delta = (1+self.r_shout_change_relative)*self.r_shout + self.r_shout_change_absolute else : delta = (1-self.r_shout_change_relative)*self.r_shout - self.r_shout_change_absolute self.sell_r = self.sell_r + self.short_term_learning_rate * (delta - self.sell_r) def calcTarget(self): if len(self.estimated_equilibrium) > 0: p = self.estimated_equilibrium[-1] if self.limit == p: p = p * 1.000001 # to prevent theta_bar = 0 elif self.job == 'Bid': p = self.limit - self.limit * 0.2 ## Initial guess for eq if no deals yet!!.... elif self.job == 'Ask': p = self.limit + self.limit * 0.2 l = self.limit theta = self.theta if self.job == 'Bid': #BUYER minus_thing = (math.exp(-self.buy_r * theta) - 1) / (math.exp(theta) - 1) plus_thing = (math.exp(self.buy_r * theta) - 1) / (math.exp(theta) - 1) theta_bar = (theta * l - theta * p) / p if theta_bar == 0: theta_bar = 0.0001 if math.exp(theta_bar) - 1 == 0: theta_bar = 0.0001 bar_thing = (math.exp(-self.buy_r * theta_bar) - 1) / (math.exp(theta_bar) - 1) if l <= p: #Extramarginal if self.buy_r >= 0: self.buy_target = l else: self.buy_target = l * (1 - minus_thing) else: #intramarginal if self.buy_r >= 0: self.buy_target = p + (l-p)*plus_thing else: self.buy_target = p*(1-bar_thing) if self.buy_target > l: self.buy_target = l if self.job == 'Ask': #SELLER minus_thing = (math.exp(-self.sell_r * theta) - 1) / (math.exp(theta) - 1) plus_thing = (math.exp(self.sell_r * theta) - 1) / (math.exp(theta) - 1) theta_bar = (theta * l - theta * p) / p if theta_bar == 0: theta_bar = 0.0001 if math.exp(theta_bar) - 1 == 0: theta_bar = 0.0001 bar_thing = (math.exp(-self.sell_r * theta_bar) - 1) / (math.exp(theta_bar) - 1) #div 0 sometimes what!? if l <= p: #Extramarginal if self.buy_r >= 0: self.buy_target = l else: self.buy_target = l + (self.marketMax - l)*(minus_thing) else: #intramarginal if self.buy_r >= 0: self.buy_target = l + (p-l)*(1-plus_thing) else: self.buy_target = p + (self.marketMax - p)*(bar_thing) if self.sell_target < l: self.sell_target = l def getorder(self, time, countdown, lob,verbose): if len(self.orders) < 1: self.active = False return None else: self.active = True self.limit = self.orders[0].price self.job = self.orders[0].atype self.calcTarget() if self.prev_best_bid_p == None: o_bid = 0 else: o_bid = self.prev_best_bid_p if self.prev_best_ask_p == None: o_ask = self.marketMax else: o_ask = self.prev_best_ask_p if self.job == 'Bid': #BUYER if self.limit <= o_bid: return None else: if len(self.previous_transactions) > 0: ## has been at least one transaction o_ask_plus = (1+self.r_shout_change_relative)*o_ask + self.r_shout_change_absolute quoteprice = o_bid + ((min(self.limit, o_ask_plus) - o_bid) / self.offer_change_rate) else: if o_ask <= self.buy_target: quoteprice = o_ask else: quoteprice = o_bid + ((self.buy_target - o_bid) / self.offer_change_rate) if self.job == 'Ask': if self.limit >= o_ask: return None else: if len(self.previous_transactions) > 0: ## has been at least one transaction o_bid_minus = (1-self.r_shout_change_relative) * o_bid - self.r_shout_change_absolute quoteprice = o_ask - ((o_ask - max(self.limit, o_bid_minus)) / self.offer_change_rate) else: if o_bid >= self.sell_target: quoteprice = o_bid else: quoteprice = o_ask - ((o_ask - self.sell_target) / self.offer_change_rate) order = Order(self.tid, self.orders[0].atype, 'LIM', quoteprice, self.orders[0].qty, time, None, -1) self.lastquote=order return order def respond(self, time, lob, trade, verbose): ## Begin nicked from ZIP # what, if anything, has happened on the bid LOB? Nicked from ZIP.. bid_improved = False bid_hit = False lob_best_bid_p = lob['bids']['bestp'] lob_best_bid_q = None if lob_best_bid_p != None: # non-empty bid LOB lob_best_bid_q = lob['bids']['lob'][0][1] if self.prev_best_bid_p < lob_best_bid_p : # best bid has improved # NB doesn't check if the improvement was by self bid_improved = True elif trade != None and ((self.prev_best_bid_p > lob_best_bid_p) or ((self.prev_best_bid_p == lob_best_bid_p) and (self.prev_best_bid_q > lob_best_bid_q))): # previous best bid was hit bid_hit = True elif self.prev_best_bid_p != None: # # the bid LOB has been emptied: was it cancelled or hit? # last_tape_item = lob['tape'][-1] # if last_tape_item['type'] == 'Cancel' : # bid_hit = False # else: # bid_hit = True # the bid LOB is empty now but was not previously: so was it canceled or lifted? if trade != None: # a trade has occurred and the previously nonempty ask LOB is now empty # so assume best ask was lifted bid_hit = True else: bid_hit = False # what, if anything, has happened on the ask LOB? ask_improved = False ask_lifted = False lob_best_ask_p = lob['asks']['bestp'] lob_best_ask_q = None if lob_best_ask_p != None: # non-empty ask LOB lob_best_ask_q = lob['asks']['lob'][0][1] if self.prev_best_ask_p > lob_best_ask_p : # best ask has improved -- NB doesn't check if the improvement was by self ask_improved = True elif trade != None and ((self.prev_best_ask_p < lob_best_ask_p) or ((self.prev_best_ask_p == lob_best_ask_p) and (self.prev_best_ask_q > lob_best_ask_q))): # trade happened and best ask price has got worse, or stayed same but quantity reduced -- assume previous best ask was lifted ask_lifted = True elif self.prev_best_ask_p != None: # the ask LOB is empty now but was not previously: canceled or lifted? # last_tape_item = lob['tape'][-1] # if last_tape_item['type'] == 'Cancel' : # ask_lifted = False # else: # ask_lifted = True # the ask LOB is empty now but was not previously: so was it canceled or lifted? if trade != None: # a trade has occurred and the previously nonempty ask LOB is now empty # so assume best ask was lifted ask_lifted = True else: ask_lifted = False self.prev_best_bid_p = lob_best_bid_p self.prev_best_bid_q = lob_best_bid_q self.prev_best_ask_p = lob_best_ask_p self.prev_best_ask_q = lob_best_ask_q deal = bid_hit or ask_lifted ## End nicked from ZIP if deal: self.previous_transactions.append(trade['price']) if self.sell_target == None: self.sell_target = trade['price'] if self.buy_target == None: self.buy_target = trade['price'] self.calcEq() self.calcAlpha() self.calcTheta() self.calcRshout() self.calcAgg() self.calcTarget() #print 'sell: ', self.sell_target, 'buy: ', self.buy_target, 'limit:', self.limit, 'eq: ', self.estimated_equilibrium[-1], 'sell_r: ', self.sell_r, 'buy_r: ', self.buy_r, '\n' class Trader_IAAB(Trader): def __init__(self, ttype, tid, balance, time): # Stuff about trader # self.ttype = ttype # self.tid = tid # self.balance = balance # self.birthtime = time # self.profitpertime = 0 # self.n_trades = 0 # self.blotter = [] # self.orders = [] # self.n_quotes = 0 # self.lastquote = None Trader.__init__(self, ttype, tid, balance, time) self.limit = None self.job = None # learning variables self.r_shout_change_relative = 0.05 self.r_shout_change_absolute = 0.05 self.short_term_learning_rate = random.uniform(0.1, 0.5) self.long_term_learning_rate = random.uniform(0.1, 0.5) self.moving_average_weight_decay = 0.95 # how fast weight decays with time, lower is quicker, 0.9 in vytelingum self.moving_average_window_size = 5 self.offer_change_rate = 3.0 self.theta = -2.0 self.theta_max = 2.0 self.theta_min = -8.0 self.marketMax = bse_sys_maxprice # Variables to describe the market self.previous_transactions = [] self.moving_average_weights = [] for i in range(self.moving_average_window_size): self.moving_average_weights.append(self.moving_average_weight_decay**i) self.estimated_equilibrium = [] self.smiths_alpha = [] self.prev_best_bid_p = None self.prev_best_bid_q = None self.prev_best_ask_p = None self.prev_best_ask_q = None # Trading Variables self.r_shout = None self.buy_target = None self.sell_target = None self.buy_r = -1.0 * (0.3 * random.random()) self.sell_r = -1.0 * (0.3 * random.random()) #Block order holder self.remaining_quantity = 0; def add_cust_order(self, order, verbose): # add a customer order to trader's records # currently LAZY: keeps to within max_cust_orders by appending new order and deleting head self.orders if len(self.quotes) > 0: # this trader has a live quote on the LOB, from a previous customer order # need response to signal cancellation/withdrawal of that quote response = 'LOB_Cancel' else: response = 'Proceed' if len(self.orders) >= self.max_cust_orders: self.orders = self.orders[1:] self.orders.append(order) self.remaining_quantity = order.qty if verbose: print('add_order < response=%s self.orders=%s' % (response, str(self.orders))) return response # delete a customer order from trader's list of orders being worked def del_cust_order(self, cust_order_id, verbose): if verbose: print('>del_cust_order: Cust_orderID=%s; self.orders=' % cust_order_id) for o in self.orders: print('%s ' % str(o)) cust_orders = [] for co in self.orders: if co.assignmentid != cust_order_id: cust_orders.append(co) self.orders = cust_orders # revise a customer order: used after a PARTial fill on the exchange def revise_cust_order(self, cust_order_id, revised_order, verbose): if verbose: print('>revise_cust_order: Cust_orderID=%s; revised_order=%s, self.orders=' % ( cust_order_id, revised_order)) for o in self.orders: print('%s ' % str(o)) cust_orders = [] for co in self.orders: if co.assignmentid != cust_order_id: cust_orders.append(co) else: revised_assignment = co revised_assignment.qty = self.remaining_quantity cust_orders.append(revised_assignment) self.orders = cust_orders if verbose: print('del_exch_order: OID:%d; self.quotes=' % oid) for q in self.quotes: print('%s ' % str(q)) exch_orders = [] for eo in self.quotes: if eo.orderid != oid: exch_orders.append(eo) self.quotes = exch_orders def bookkeep(self, msg, time, verbose): # bookkeep(): trader book-keeping in response to message from the exchange # update records of what orders are still being worked, account balance, etc. # trader's blotter is a simple sequential record of each exchange messages received, and the trader's balance after bookeeping that msh if verbose: print('>bookkeep msg=%s bal=%d' % (msg, self.balance)) profit = 0 if msg.event == "CAN": # order was cancelled at the exchange # so delete the order from the trader's records of what quotes it has live on the exchange if verbose: print(">CANcellation: msg=%s quotes=" % str(msg)) for q in self.quotes: print("%s" % str(q)) newquotes = [] for q in self.quotes: if q.orderid != msg.oid: newquotes.append(q) self.quotes = newquotes if verbose: print("bookkeep() PART-filled order updating qty on customer order ID=%s' % cust_order_id) self.revise_cust_order(cust_order_id, msg.revo, verbose) # delete this customer order if exch_order.ostyle == "IOC": # a partially filled IOC has the non-filled portion cancelled at the exchange, # so the trader's order records need to be updated accordingly if verbose: print( ">bookkeep() PART-filled IOC cancels remainder: deleting OID:%d from trader's exchange-order records" % exch_order.orderid) self.del_exch_order(exch_order.orderid, verbose) # delete the exchange-order from trader's records self.blotter.append([msg, self.balance]) # add trade record to trader's blotter def calcEq(self): ##clear and correct # Slightly modified from paper, it is unclear inpaper # N previous transactions * weights / N in vytelingum, swap N denominator for sum of weights to be correct? if len(self.previous_transactions) == 0: return elif len(self.previous_transactions) < self.moving_average_window_size: # Not enough transactions self.estimated_equilibrium.append(float(sum(self.previous_transactions)) / max(len(self.previous_transactions), 1)) else: N_previous_transactions = self.previous_transactions[-self.moving_average_window_size:] thing = [N_previous_transactions[i]*self.moving_average_weights[i] for i in range(self.moving_average_window_size)] eq = sum( thing ) / sum(self.moving_average_weights) self.estimated_equilibrium.append(eq) def calcAlpha(self): ##correct. but calcAlpha in snashall's version is incorrect alpha = 0.0 for p in self.previous_transactions: alpha += (p - self.estimated_equilibrium[-1])**2 alpha = math.sqrt(alpha/len(self.previous_transactions)) self.smiths_alpha.append( alpha/self.estimated_equilibrium[-1] ) def calcTheta(self): ## clear and correct gamma = 2.0 #not sensitive apparently so choose to be whatever # necessary for intialisation, div by 0 if min(self.smiths_alpha) == max(self.smiths_alpha): alpha_range = 0.4 #starting value i guess else: alpha_range = (self.smiths_alpha[-1] - min(self.smiths_alpha)) / (max(self.smiths_alpha) - min(self.smiths_alpha)) theta_range = self.theta_max - self.theta_min desired_theta = self.theta_min + (theta_range) * (1 - alpha_range) * math.exp(gamma * (alpha_range - 1)) self.theta = self.theta + self.long_term_learning_rate * (desired_theta - self.theta) if self.theta > self.theta_max : self.theta = self.theta_max if self.theta < self.theta_min : self.theta = self.theta_min def calcRshout(self): ## unclear in Vytelingum's paper p = self.estimated_equilibrium[-1] l = self.limit theta = self.theta if self.job == 'Bid': # Currently a buyer if l <= p: #extramarginal! self.r_shout = 0.0 else: #intramarginal :( if self.buy_target > self.estimated_equilibrium[-1]: #r[0,1] self.r_shout = math.log(((self.buy_target - p) * (math.exp(theta) - 1) / (l - p)) + 1) / theta else: #r[-1,0] # print 'buy_target: %f , p: %f , theta: %f' %(self.buy_target,p,theta) self.r_shout = math.log((1 - (self.buy_target/p)) * (math.exp(theta) - 1) + 1) / theta # self.r_shout = self.buy_r if self.job == 'Ask': # Currently a seller if l >= p: #extramarginal! self.r_shout = 0 else: #intramarginal :( if self.sell_target > self.estimated_equilibrium[-1]: # r[-1,0] self.r_shout = math.log((self.sell_target - p) * (math.exp(theta) - 1) / (self.marketMax - p) + 1) / theta else: # r[0,1] a = (self.sell_target-l)/(p-l) self.r_shout = (math.log((1 - a) * (math.exp(theta) - 1) + 1)) / theta # self.r_shout = self.sell_r def calcAgg(self): delta = 0 if self.job == 'Bid': # BUYER if self.buy_target >= self.previous_transactions[-1] : # must be more aggressive delta = (1+self.r_shout_change_relative)*self.r_shout + self.r_shout_change_absolute else : delta = (1-self.r_shout_change_relative)*self.r_shout - self.r_shout_change_absolute self.buy_r = self.buy_r + self.short_term_learning_rate * (delta - self.buy_r) if self.job == 'Ask': # SELLER if self.sell_target > self.previous_transactions[-1] : delta = (1+self.r_shout_change_relative)*self.r_shout + self.r_shout_change_absolute else : delta = (1-self.r_shout_change_relative)*self.r_shout - self.r_shout_change_absolute self.sell_r = self.sell_r + self.short_term_learning_rate * (delta - self.sell_r) def calcTarget(self): if len(self.estimated_equilibrium) > 0: p = self.estimated_equilibrium[-1] if self.limit == p: p = p * 1.000001 # to prevent theta_bar = 0 elif self.job == 'Bid': p = self.limit - self.limit * 0.2 ## Initial guess for eq if no deals yet!!.... elif self.job == 'Ask': p = self.limit + self.limit * 0.2 l = self.limit theta = self.theta if self.job == 'Bid': #BUYER minus_thing = self.buy_r * math.exp(theta*(self.buy_r-1)) if l <= p: #Extramarginal if self.buy_r >= 0: self.buy_target = l else: self.buy_target = l * (1 - minus_thing) else: #intramarginal if self.buy_r >= 0: # theta_ba = (p * math.exp(-theta))/(l-p)-1 theta_ba = theta # print 'theta: %f' %(self.theta) # print 'theta_ba: %f '%(theta_ba) # print 'l-p: %f '%(l-p) # print 'self.buy_r :%f' %(self.buy_r) self.buy_target = (l-p)*(1-(self.buy_r+1)*math.exp(self.buy_r*theta_ba))+p else: self.buy_target = p*(1-minus_thing) if self.buy_target > l: self.buy_target = l if self.buy_target = 0: self.buy_target = p + (p-l)* self.sell_r*math.exp((self.sell_r-1)*theta) else: theta_ba = math.log((self.marketMax-p)/(p-l))-theta self.buy_target = p + (self.marketMax-p)* self.sell_r*math.exp((self.sell_r+1)*theta_ba) else: # Extramarginal if self.buy_r >= 0: self.buy_target = l else: self.buy_target = l + (self.marketMax-l)*self.sell_r*math.exp((self.sell_r-1)*theta) if self.sell_target < l: self.sell_target = l if self.sell_target > bse_sys_maxprice: self.sell_target = bse_sys_maxprice # print 'sell_target = %f'%(self.sell_target) def getorder(self, time, countdown, lob,verbose): if len(self.orders) < 1: self.active = False return None else: self.active = True self.limit = self.orders[0].price self.job = self.orders[0].atype self.calcTarget() if self.prev_best_bid_p == None: o_bid = 0 else: o_bid = self.prev_best_bid_p if self.prev_best_ask_p == None: o_ask = self.marketMax else: o_ask = self.prev_best_ask_p if self.job == 'Bid': #BUYER if self.limit <= o_bid: return None else: if len(self.previous_transactions) > 0: ## has been at least one transaction o_ask_plus = (1+self.r_shout_change_relative)*o_ask + self.r_shout_change_absolute quoteprice = o_bid + ((min(self.limit, o_ask_plus) - o_bid) / self.offer_change_rate) else: if o_ask <= self.buy_target: quoteprice = o_ask else: quoteprice = o_bid + ((self.buy_target - o_bid) / self.offer_change_rate) if self.job == 'Ask': if self.limit >= o_ask: return None else: if len(self.previous_transactions) <= 0: ## has been at least one transaction o_bid_minus = (1-self.r_shout_change_relative) * o_bid - self.r_shout_change_absolute quoteprice = o_ask - ((o_ask - max(self.limit, o_bid_minus)) / self.offer_change_rate) else: if o_bid >= self.sell_target: quoteprice = o_bid else: quoteprice = o_ask - ((o_ask - self.sell_target) / self.offer_change_rate) def imbalance_alter (quoteprice_aa, lob): if(lob['microprice']==None or lob['midprice']==None): return quoteprice_aa quoteprice_iaa = 0 imbalance_ratio = 0 # the measurement of size in demand side volume_bids = 0 # the measurement of size in supply side volume_asks = 0 #the depth of lob, how many different price. count_bids_depth = 0 count_asks_depth = 0 for item in lob['bids']['lob']: volume_bids += math.exp(-0.5*count_bids_depth) *item[1] count_bids_depth +=1 #only consider the first 3 quota if(count_bids_depth >=3): break for item in lob['asks']['lob']: volume_asks += math.exp(-0.5*count_asks_depth) *item[1] count_asks_depth +=1 #only consider the first 3 quota if(count_asks_depth >=3): break #lob is none if volume_bids == 0 and volume_asks == 0: return quoteprice_aa else : # imbalance_ratio will be treated as learning rate in widrow imbalance_ratio = (volume_bids-volume_asks)/(volume_bids+volume_asks) if self.job == 'Bid': quoteprice_iaa = quoteprice_aa+imbalance_ratio*(lob['microprice']-lob['midprice']) if(quoteprice_iaa>self.limit): quoteprice_iaa = self.limit else: quoteprice_iaa = quoteprice_aa+ imbalance_ratio*(lob['microprice']-lob['midprice']) if(quoteprice_iaa=3 or count_asks_depth/count_bids_depth>=3 : # return quoteprice_iaa # else: return quoteprice_aa # # return quoteprice_aa # print 'depth_bids: %f' %(count_bids_depth) # print 'depth_asks: %f' %(count_asks_depth) # print 'volume_bids: %f' %(volume_bids) # print 'volume_asks: %f' %(volume_asks) # print 'imbalance ratio: %f' %(imbalance_ratio) # print 'IAA original quotaprice: %d' % (quoteprice_aa) # print 'IAA final quotaprice: %d' % (quoteprice_iaa) return quoteprice_iaa quoteprice_iaa = imbalance_alter(quoteprice,lob) def divide_block_order (): return min(random.randint(1,3),self.remaining_quantity) order = Order(self.tid, self.orders[0].atype, 'LIM', quoteprice_iaa, divide_block_order(), time, None, -1) self.lastquote=order print 'IAAB deal with block order' print str(order) return order def respond(self, time, lob, trade, verbose): ## Begin nicked from ZIP # what, if anything, has happened on the bid LOB? Nicked from ZIP.. bid_improved = False bid_hit = False lob_best_bid_p = lob['bids']['bestp'] lob_best_bid_q = None if lob_best_bid_p != None: # non-empty bid LOB lob_best_bid_q = lob['bids']['lob'][0][1] if self.prev_best_bid_p < lob_best_bid_p : # best bid has improved # NB doesn't check if the improvement was by self bid_improved = True elif trade != None and ((self.prev_best_bid_p > lob_best_bid_p) or ((self.prev_best_bid_p == lob_best_bid_p) and (self.prev_best_bid_q > lob_best_bid_q))): # previous best bid was hit bid_hit = True elif self.prev_best_bid_p != None: # # the bid LOB has been emptied: was it cancelled or hit? # last_tape_item = lob['tape'][-1] # if last_tape_item['type'] == 'Cancel' : # bid_hit = False # else: # bid_hit = True # the bid LOB is empty now but was not previously: so was it canceled or lifted? if trade != None: # a trade has occurred and the previously nonempty ask LOB is now empty # so assume best ask was lifted bid_hit = True else: bid_hit = False # what, if anything, has happened on the ask LOB? ask_improved = False ask_lifted = False lob_best_ask_p = lob['asks']['bestp'] lob_best_ask_q = None if lob_best_ask_p != None: # non-empty ask LOB lob_best_ask_q = lob['asks']['lob'][0][1] if self.prev_best_ask_p > lob_best_ask_p : # best ask has improved -- NB doesn't check if the improvement was by self ask_improved = True elif trade != None and ((self.prev_best_ask_p < lob_best_ask_p) or ((self.prev_best_ask_p == lob_best_ask_p) and (self.prev_best_ask_q > lob_best_ask_q))): # trade happened and best ask price has got worse, or stayed same but quantity reduced -- assume previous best ask was lifted ask_lifted = True elif self.prev_best_ask_p != None: # the ask LOB is empty now but was not previously: canceled or lifted? # last_tape_item = lob['tape'][-1] # if last_tape_item['type'] == 'Cancel' : # ask_lifted = False # else: # ask_lifted = True # the ask LOB is empty now but was not previously: so was it canceled or lifted? if trade != None: # a trade has occurred and the previously nonempty ask LOB is now empty # so assume best ask was lifted ask_lifted = True else: ask_lifted = False self.prev_best_bid_p = lob_best_bid_p self.prev_best_bid_q = lob_best_bid_q self.prev_best_ask_p = lob_best_ask_p self.prev_best_ask_q = lob_best_ask_q deal = bid_hit or ask_lifted ## End nicked from ZIP if deal: self.previous_transactions.append(trade['price']) if self.sell_target == None: self.sell_target = trade['price'] if self.buy_target == None: self.buy_target = trade['price'] self.calcEq() self.calcAlpha() self.calcTheta() self.calcRshout() self.calcAgg() self.calcTarget() #print 'sell: ', self.sell_target, 'buy: ', self.buy_target, 'limit:', self.limit, 'eq: ', self.estimated_equilibrium[-1], 'sell_r: ', self.sell_r, 'buy_r: ', self.buy_r, '\n' ================================================ FILE: ZhenZhang/source/GDX.py ================================================ # Trader subclass ZIP # After Cliff 1997 from BSE2_msg_classes import Assignment, Order, Exch_msg from BSE_trader_agents import Trader; import random import math bse_sys_minprice = 1 # minimum price in the system, in cents/pennies bse_sys_maxprice = 200 # maximum price in the system, in cents/pennies class Trader_GDX(Trader): def __init__(self, ttype, tid, balance, time): Trader.__init__(self, ttype, tid, balance, time) self.prev_orders = [] self.active = False self.limit = None self.job = None #memory of all bids and asks and accepted bids and asks self.outstanding_bids = [] self.outstanding_asks = [] self.accepted_asks = [] self.accepted_bids = [] self.price = -1 # memory of best price & quantity of best bid and ask, on LOB on previous update self.prev_best_bid_p = None self.prev_best_bid_q = None self.prev_best_ask_p = None self.prev_best_ask_q = None self.first_turn = True self.gamma = 0.1 self.holdings = 10 self.remaining_offer_ops = 10 self.values = [[0 for n in range(self.remaining_offer_ops)] for m in range(self.holdings)] def getorder(self, time, countdown, lob, verbose): if len(self.orders) < 1: self.active = False order = None else: self.active = True self.limit = self.orders[0].price self.job = self.orders[0].atype #calculate price if self.job == 'Bid': self.price = self.calc_p_bid(self.holdings - 1, self.remaining_offer_ops - 1) if self.job == 'Ask': self.price = self.calc_p_ask(self.holdings - 1, self.remaining_offer_ops - 1) order = Order(self.tid, self.job, 'LIM',self.price, self.orders[0].qty, time, None, -1) self.lastquote = order if self.first_turn or self.price == -1: if self.job == 'Bid': order = Order(self.tid, self.job, 'LIM',bse_sys_minprice+1 , self.orders[0].qty, time, None, -1) if self.job == 'Ask': order = Order(self.tid, self.job, 'LIM',bse_sys_maxprice-1 , self.orders[0].qty, time, None, -1) return order def calc_p_bid(self, m, n): best_return = 0 best_bid = 0 second_best_return = 0 second_best_bid = 0 #first step size of 1 get best and 2nd best for i in [x*2 for x in range(int(self.limit/2))]: thing = self.belief_buy(i) * ((self.limit - i) + self.gamma*self.values[m-1][n-1]) + (1-self.belief_buy(i) * self.gamma * self.values[m][n-1]) if thing > best_return: second_best_bid = best_bid second_best_return = best_return best_return = thing best_bid = i #always best bid largest one if second_best_bid > best_bid: a = second_best_bid second_best_bid = best_bid best_bid = a #then step size 0.05 for i in [x*0.05 for x in range(int(second_best_bid), int(best_bid))]: thing = self.belief_buy(i + second_best_bid) * ((self.limit - (i + second_best_bid)) + self.gamma*self.values[m-1][n-1]) + (1-self.belief_buy(i + second_best_bid) * self.gamma * self.values[m][n-1]) if thing > best_return: best_return = thing best_bid = i + second_best_bid return best_bid def calc_p_ask(self, m, n): best_return = 0 best_ask = self.limit second_best_return = 0 second_best_ask = self.limit #first step size of 1 get best and 2nd best for i in [x*2 for x in range(int(self.limit/2))]: j = i + self.limit thing = self.belief_sell(j) * ((j - self.limit) + self.gamma*self.values[m-1][n-1]) + (1-self.belief_sell(j) * self.gamma * self.values[m][n-1]) if thing > best_return: second_best_ask = best_ask second_best_return = best_return best_return = thing best_ask = j #always best ask largest one if second_best_ask > best_ask: a = second_best_ask second_best_ask = best_ask best_ask = a #then step size 0.05 for i in [x*0.05 for x in range(int(second_best_ask), int(best_ask))]: thing = self.belief_sell(i + second_best_ask) * (((i + second_best_ask) - self.limit) + self.gamma*self.values[m-1][n-1]) + (1-self.belief_sell(i + second_best_ask) * self.gamma * self.values[m][n-1]) if thing > best_return: best_return = thing best_ask = i + second_best_ask return best_ask def belief_sell(self, price): accepted_asks_greater = 0 bids_greater = 0 unaccepted_asks_lower = 0 for p in self.accepted_asks: if p >= price: accepted_asks_greater += 1 for p in [thing[0] for thing in self.outstanding_bids]: if p >= price: bids_greater += 1 for p in [thing[0] for thing in self.outstanding_asks]: if p <= price: unaccepted_asks_lower += 1 if accepted_asks_greater + bids_greater + unaccepted_asks_lower == 0: return 0 return (accepted_asks_greater + bids_greater) / (accepted_asks_greater + bids_greater + unaccepted_asks_lower) def belief_buy(self, price): accepted_bids_lower = 0 asks_lower = 0 unaccepted_bids_greater = 0 for p in self.accepted_bids: if p <= price: accepted_bids_lower += 1 for p in [thing[0] for thing in self.outstanding_asks]: if p <= price: asks_lower += 1 for p in [thing[0] for thing in self.outstanding_bids]: if p >= price: unaccepted_bids_greater += 1 if accepted_bids_lower + asks_lower + unaccepted_bids_greater == 0: return 0 return (accepted_bids_lower + asks_lower) / (accepted_bids_lower + asks_lower + unaccepted_bids_greater) def respond(self, time, lob, trade, verbose): # what, if anything, has happened on the bid LOB? self.outstanding_bids = lob['bids']['lob'] bid_improved = False bid_hit = False lob_best_bid_p = lob['bids']['bestp'] lob_best_bid_q = None if lob_best_bid_p != None: # non-empty bid LOB lob_best_bid_q = lob['bids']['lob'][-1][1] if self.prev_best_bid_p < lob_best_bid_p : # best bid has improved # NB doesn't check if the improvement was by self bid_improved = True elif trade != None and ((self.prev_best_bid_p > lob_best_bid_p) or ((self.prev_best_bid_p == lob_best_bid_p) and (self.prev_best_bid_q > lob_best_bid_q))): # previous best bid was hit self.accepted_bids.append(self.prev_best_bid_p) bid_hit = True elif self.prev_best_bid_p != None: # the bid LOB has been emptied: was it cancelled or hit? last_tape_item = lob['tape'][-1] if last_tape_item['type'] == 'Cancel' : bid_hit = False else: bid_hit = True # what, if anything, has happened on the ask LOB? self.outstanding_asks = lob['asks']['lob'] ask_improved = False ask_lifted = False lob_best_ask_p = lob['asks']['bestp'] lob_best_ask_q = None if lob_best_ask_p != None: # non-empty ask LOB lob_best_ask_q = lob['asks']['lob'][0][1] if self.prev_best_ask_p > lob_best_ask_p : # best ask has improved -- NB doesn't check if the improvement was by self ask_improved = True elif trade != None and ((self.prev_best_ask_p < lob_best_ask_p) or ((self.prev_best_ask_p == lob_best_ask_p) and (self.prev_best_ask_q > lob_best_ask_q))): # trade happened and best ask price has got worse, or stayed same but quantity reduced -- assume previous best ask was lifted self.accepted_asks.append(self.prev_best_ask_p) ask_lifted = True elif self.prev_best_ask_p != None: # the ask LOB is empty now but was not previously: canceled or lifted? last_tape_item = lob['tape'][-1] if last_tape_item['type'] == 'Cancel' : ask_lifted = False else: ask_lifted = True #populate expected values if self.first_turn: # print "populating" self.first_turn = False for n in range(1, self.remaining_offer_ops): for m in range(1, self.holdings): if self.job == 'Bid': #BUYER self.values[m][n] = self.calc_p_bid(m, n) if self.job == 'Ask': #BUYER self.values[m][n] = self.calc_p_ask(m, n) # print "done" deal = bid_hit or ask_lifted # remember the best LOB data ready for next response self.prev_best_bid_p = lob_best_bid_p self.prev_best_bid_q = lob_best_bid_q self.prev_best_ask_p = lob_best_ask_p self.prev_best_ask_q = lob_best_ask_q ================================================ FILE: ZhenZhang/source/IAA_MLOFI.py ================================================ from BSE2_msg_classes import Assignment, Order, Exch_msg from BSE_trader_agents import Trader; import random import math bse_sys_minprice = 1 # minimum price in the system, in cents/pennies bse_sys_maxprice = 200 # maximum price in the system, in cents/pennies class Trader_IAA_MLOFI(Trader): def __init__(self, ttype, tid, balance, time,m): Trader.__init__(self, ttype, tid, balance, time) self.limit = None self.job = None # learning variables self.r_shout_change_relative = 0.05 self.r_shout_change_absolute = 0.05 self.short_term_learning_rate = random.uniform(0.1, 0.5) self.long_term_learning_rate = random.uniform(0.1, 0.5) self.moving_average_weight_decay = 0.95 # how fast weight decays with time, lower is quicker, 0.9 in vytelingum self.moving_average_window_size = 5 self.offer_change_rate = 3.0 self.theta = -2.0 self.theta_max = 2.0 self.theta_min = -8.0 self.marketMax = bse_sys_maxprice # Variables to describe the market self.previous_transactions = [] self.moving_average_weights = [] for i in range(self.moving_average_window_size): self.moving_average_weights.append(self.moving_average_weight_decay ** i) self.estimated_equilibrium = [] self.smiths_alpha = [] self.prev_best_bid_p = None self.prev_best_bid_q = None self.prev_best_ask_p = None self.prev_best_ask_q = None # Trading Variables self.r_shout = None self.buy_target = None self.sell_target = None self.buy_r = -1.0 * (0.3 * random.random()) self.sell_r = -1.0 * (0.3 * random.random()) # variable for MLOFI self.last_lob = None; self.es_list = []; self.ds_list = []; #variable self.m = m; def calc_level_n_e(self, current_lob, n): b_n = 0 r_n = 0 a_n = 0 q_n = 0 b_n_1 = 0 r_n_1 = 0 a_n_1 = 0 q_n_1 = 0 if (len(current_lob['bids']['lob']) < n): b_n = 0 r_n = 0 else: b_n = current_lob['bids']['lob'][n - 1][0] r_n = current_lob['bids']['lob'][n - 1][1] if (len(self.last_lob['bids']['lob']) < n): b_n_1 = 0 r_n_1 = 0 else: b_n_1 = self.last_lob['bids']['lob'][n - 1][0] r_n_1 = self.last_lob['bids']['lob'][n - 1][1] if (len(current_lob['asks']['lob']) < n): a_n = 0 q_n = 0 else: a_n = current_lob['asks']['lob'][n - 1][0] q_n = current_lob['asks']['lob'][n - 1][1] if (len(self.last_lob['asks']['lob']) < n): a_n_1 = 0 q_n_1 = 0 else: a_n_1 = self.last_lob['asks']['lob'][n - 1][0] q_n_1 = self.last_lob['asks']['lob'][n - 1][1] delta_w = 0; if (b_n > b_n_1): delta_w = r_n elif (b_n == b_n_1): delta_w = r_n - r_n_1 else: delta_w = -r_n_1 delta_v = 0 if (a_n > a_n_1): delta_v = -q_n_1 elif (a_n == a_n_1): delta_v = q_n - q_n_1 else: delta_v = q_n return delta_w - delta_v def calc_es(self, lob, m, verbose): new_e = {} for i in range(1, m + 1): new_e['level' + str(i)] = self.calc_level_n_e(lob, i) self.es_list.append(new_e) def calc_ds(self, lob, m, verbose): new_d = {} for i in range(1, m + 1): new_d['level' + str(i)] = self.cal_depth_n(lob, i) self.ds_list.append(new_d) def cal_depth_n(self, lob, n): if (len(lob['bids']['lob']) < n): r_n = 0 else: r_n = lob['bids']['lob'][n - 1][1] if (len(lob['asks']['lob']) < n): q_n = 0 else: q_n = lob['asks']['lob'][n - 1][1] return (r_n + q_n) / 2 def calcEq(self): ##clear and correct # Slightly modified from paper, it is unclear inpaper # N previous transactions * weights / N in vytelingum, swap N denominator for sum of weights to be correct? if len(self.previous_transactions) == 0: return elif len(self.previous_transactions) < self.moving_average_window_size: # Not enough transactions self.estimated_equilibrium.append( float(sum(self.previous_transactions)) / max(len(self.previous_transactions), 1)) else: N_previous_transactions = self.previous_transactions[-self.moving_average_window_size:] thing = [N_previous_transactions[i] * self.moving_average_weights[i] for i in range(self.moving_average_window_size)] eq = sum(thing) / sum(self.moving_average_weights) self.estimated_equilibrium.append(eq) def calcAlpha(self): ##correct. but calcAlpha in snashall's version is incorrect alpha = 0.0 for p in self.previous_transactions: alpha += (p - self.estimated_equilibrium[-1]) ** 2 alpha = math.sqrt(alpha / len(self.previous_transactions)) self.smiths_alpha.append(alpha / self.estimated_equilibrium[-1]) def calcTheta(self): ## clear and correct gamma = 2.0 # not sensitive apparently so choose to be whatever # necessary for intialisation, div by 0 if min(self.smiths_alpha) == max(self.smiths_alpha): alpha_range = 0.4 # starting value i guess else: alpha_range = (self.smiths_alpha[-1] - min(self.smiths_alpha)) / ( max(self.smiths_alpha) - min(self.smiths_alpha)) theta_range = self.theta_max - self.theta_min desired_theta = self.theta_min + (theta_range) * (1 - alpha_range) * math.exp(gamma * (alpha_range - 1)) self.theta = self.theta + self.long_term_learning_rate * (desired_theta - self.theta) if self.theta > self.theta_max: self.theta = self.theta_max if self.theta < self.theta_min: self.theta = self.theta_min def calcRshout(self): ## unclear in Vytelingum's paper p = self.estimated_equilibrium[-1] l = self.limit theta = self.theta if self.job == 'Bid': # Currently a buyer if l <= p: # extramarginal! self.r_shout = 0.0 else: # intramarginal :( if self.buy_target > self.estimated_equilibrium[-1]: # r[0,1] self.r_shout = math.log(((self.buy_target - p) * (math.exp(theta) - 1) / (l - p)) + 1) / theta else: # r[-1,0] # print 'buy_target: %f , p: %f , theta: %f' %(self.buy_target,p,theta) self.r_shout = math.log((1 - (self.buy_target / p)) * (math.exp(theta) - 1) + 1) / theta # self.r_shout = self.buy_r if self.job == 'Ask': # Currently a seller if l >= p: # extramarginal! self.r_shout = 0 else: # intramarginal :( if self.sell_target > self.estimated_equilibrium[-1]: # r[-1,0] self.r_shout = math.log( (self.sell_target - p) * (math.exp(theta) - 1) / (self.marketMax - p) + 1) / theta else: # r[0,1] a = (self.sell_target - l) / (p - l) self.r_shout = (math.log((1 - a) * (math.exp(theta) - 1) + 1)) / theta # self.r_shout = self.sell_r def calcAgg(self): delta = 0 if self.job == 'Bid': # BUYER if self.buy_target >= self.previous_transactions[-1]: # must be more aggressive delta = (1 + self.r_shout_change_relative) * self.r_shout + self.r_shout_change_absolute else: delta = (1 - self.r_shout_change_relative) * self.r_shout - self.r_shout_change_absolute self.buy_r = self.buy_r + self.short_term_learning_rate * (delta - self.buy_r) if self.job == 'Ask': # SELLER if self.sell_target > self.previous_transactions[-1]: delta = (1 + self.r_shout_change_relative) * self.r_shout + self.r_shout_change_absolute else: delta = (1 - self.r_shout_change_relative) * self.r_shout - self.r_shout_change_absolute self.sell_r = self.sell_r + self.short_term_learning_rate * (delta - self.sell_r) def calcTarget(self): if len(self.estimated_equilibrium) > 0: p = self.estimated_equilibrium[-1] if self.limit == p: p = p * 1.000001 # to prevent theta_bar = 0 elif self.job == 'Bid': p = self.limit - self.limit * 0.2 ## Initial guess for eq if no deals yet!!.... elif self.job == 'Ask': p = self.limit + self.limit * 0.2 l = self.limit theta = self.theta if self.job == 'Bid': # BUYER minus_thing = self.buy_r * math.exp(theta * (self.buy_r - 1)) if l <= p: # Extramarginal if self.buy_r >= 0: self.buy_target = l else: self.buy_target = l * (1 - minus_thing) else: # intramarginal if self.buy_r >= 0: # theta_ba = (p * math.exp(-theta))/(l-p)-1 theta_ba = theta # print 'theta: %f' %(self.theta) # print 'theta_ba: %f '%(theta_ba) # print 'l-p: %f '%(l-p) # print 'self.buy_r :%f' %(self.buy_r) self.buy_target = (l - p) * (1 - (self.buy_r + 1) * math.exp(self.buy_r * theta_ba)) + p else: self.buy_target = p * (1 - minus_thing) if self.buy_target > l: self.buy_target = l if self.buy_target < bse_sys_minprice: self.buy_target = bse_sys_minprice # print 'buy_target = %f'%(self.buy_target) if self.job == 'Ask': # SELLER if l <= p: # Intramarginal if self.buy_r >= 0: self.buy_target = p + (p - l) * self.sell_r * math.exp((self.sell_r - 1) * theta) else: theta_ba = math.log((self.marketMax - p) / (p - l)) - theta self.buy_target = p + (self.marketMax - p) * self.sell_r * math.exp((self.sell_r + 1) * theta_ba) else: # Extramarginal if self.buy_r >= 0: self.buy_target = l else: self.buy_target = l + (self.marketMax - l) * self.sell_r * math.exp((self.sell_r - 1) * theta) if self.sell_target < l: self.sell_target = l if self.sell_target > bse_sys_maxprice: self.sell_target = bse_sys_maxprice # print 'sell_target = %f'%(self.sell_target) def getorder(self, time, countdown, lob, verbose): if len(self.orders) < 1: self.active = False return None else: self.active = True self.limit = self.orders[0].price self.job = self.orders[0].atype self.calcTarget() if self.prev_best_bid_p == None: o_bid = 0 else: o_bid = self.prev_best_bid_p if self.prev_best_ask_p == None: o_ask = self.marketMax else: o_ask = self.prev_best_ask_p if self.job == 'Bid': # BUYER if self.limit <= o_bid: return None else: if len(self.previous_transactions) <= 0: ## has been at least one transaction o_ask_plus = (1 + self.r_shout_change_relative) * o_ask + self.r_shout_change_absolute quoteprice = o_bid + ((min(self.limit, o_ask_plus) - o_bid) / self.offer_change_rate) else: if o_ask <= self.buy_target: quoteprice = o_ask else: quoteprice = o_bid + ((self.buy_target - o_bid) / self.offer_change_rate) if self.job == 'Ask': if self.limit >= o_ask: return None else: if len(self.previous_transactions) <= 0: ## has been at least one transaction o_bid_minus = (1 - self.r_shout_change_relative) * o_bid - self.r_shout_change_absolute quoteprice = o_ask - ((o_ask - max(self.limit, o_bid_minus)) / self.offer_change_rate) else: if o_bid >= self.sell_target: quoteprice = o_bid else: quoteprice = o_ask - ((o_ask - self.sell_target) / self.offer_change_rate) def imbalance_alter(quoteprice_aa, lob, countdown, m): mlofi_list = [0 for i in range(m)] cd_list = [0 for i in range(m)] ad_list = [] n = 1 while len(self.es_list) >= n: for i in range(m): mlofi_list[i] += self.es_list[-n]['level' + str(i+1)] n += 1 if n >= 11: break n = 1 while len(self.ds_list) >= n: for i in range(m): cd_list[i] += self.ds_list[-n]['level' + str(i+1)] n += 1 if n >= 11: break for i in range(m): temp = None if n == 1: temp = cd_list[i]+1 else: temp = cd_list[i]/(n-1)+1 ad_list.append(temp) c = 5 decay = 0.8 offset = 0 for i in range(m): offset += int(mlofi_list[i]*c*pow(decay,i)/ ad_list[i]) benchmark = quoteprice_aa; if(lob['midprice'] != None): benchmark = lob['midprice'] # print 'midprice is %d' % benchmark quoteprice_iaa = quoteprice_aa + 0.8 * (benchmark + offset - quoteprice_aa) if self.job == 'Bid' and quoteprice_iaa > self.limit: quoteprice_iaa = self.limit if self.job == 'Ask' and quoteprice_iaa < self.limit: quoteprice_iaa = self.limit if countdown < 0.3 : print "insert" if self.job == 'Bid' and (len(lob['asks']['lob']) >= 1) and lob['asks']['lob'][0][0] < self.limit: quoteprice_iaa = lob['asks']['lob'][0][0] if self.job == 'Ask' and (len(lob['bids']['lob']) >= 1) and lob['bids']['lob'][0][0] > self.limit: quoteprice_iaa = lob['bids']['lob'][0][0] if self.job == 'Bid' and quoteprice_iaa < bse_sys_minprice: quoteprice_iaa = bse_sys_minprice + 1 if self.job == 'Ask' and quoteprice_iaa > bse_sys_maxprice: quoteprice_iaa = bse_sys_maxprice - 1 return quoteprice_iaa quoteprice_iaa = imbalance_alter(quoteprice, lob, countdown,self.m) order = Order(self.tid, self.orders[0].atype, 'LIM', quoteprice_iaa, self.orders[0].qty, time, None, -1) self.lastquote = order return order def respond(self, time, lob, trade, verbose): ## Begin nicked from ZIP # what, if anything, has happened on the bid LOB? Nicked from ZIP.. bid_improved = False bid_hit = False lob_best_bid_p = lob['bids']['bestp'] lob_best_bid_q = None if lob_best_bid_p != None: # non-empty bid LOB lob_best_bid_q = lob['bids']['lob'][0][1] if self.prev_best_bid_p < lob_best_bid_p: # best bid has improved # NB doesn't check if the improvement was by self bid_improved = True elif trade != None and ((self.prev_best_bid_p > lob_best_bid_p) or ( (self.prev_best_bid_p == lob_best_bid_p) and (self.prev_best_bid_q > lob_best_bid_q))): # previous best bid was hit bid_hit = True elif self.prev_best_bid_p != None: # # the bid LOB has been emptied: was it cancelled or hit? # last_tape_item = lob['tape'][-1] # if last_tape_item['type'] == 'Cancel' : # bid_hit = False # else: # bid_hit = True # the bid LOB is empty now but was not previously: so was it canceled or lifted? if trade != None: # a trade has occurred and the previously nonempty ask LOB is now empty # so assume best ask was lifted bid_hit = True else: bid_hit = False # what, if anything, has happened on the ask LOB? ask_improved = False ask_lifted = False lob_best_ask_p = lob['asks']['bestp'] lob_best_ask_q = None if lob_best_ask_p != None: # non-empty ask LOB lob_best_ask_q = lob['asks']['lob'][0][1] if self.prev_best_ask_p > lob_best_ask_p: # best ask has improved -- NB doesn't check if the improvement was by self ask_improved = True elif trade != None and ((self.prev_best_ask_p < lob_best_ask_p) or ( (self.prev_best_ask_p == lob_best_ask_p) and (self.prev_best_ask_q > lob_best_ask_q))): # trade happened and best ask price has got worse, or stayed same but quantity reduced -- assume previous best ask was lifted ask_lifted = True elif self.prev_best_ask_p != None: # the ask LOB is empty now but was not previously: canceled or lifted? # last_tape_item = lob['tape'][-1] # if last_tape_item['type'] == 'Cancel' : # ask_lifted = False # else: # ask_lifted = True # the ask LOB is empty now but was not previously: so was it canceled or lifted? if trade != None: # a trade has occurred and the previously nonempty ask LOB is now empty # so assume best ask was lifted ask_lifted = True else: ask_lifted = False self.prev_best_bid_p = lob_best_bid_p self.prev_best_bid_q = lob_best_bid_q self.prev_best_ask_p = lob_best_ask_p self.prev_best_ask_q = lob_best_ask_q deal = bid_hit or ask_lifted ## End nicked from ZIP if (self.last_lob == None): self.last_lob = lob else: self.calc_es(lob, self.m, verbose) self.calc_ds(lob, self.m, verbose) self.last_lob = lob; if deal: self.previous_transactions.append(trade['price']) if self.sell_target == None: self.sell_target = trade['price'] if self.buy_target == None: self.buy_target = trade['price'] self.calcEq() self.calcAlpha() self.calcTheta() self.calcRshout() self.calcAgg() self.calcTarget() # print 'sell: ', self.sell_target, 'buy: ', self.buy_target, 'limit:', self.limit, 'eq: ', self.estimated_equilibrium[-1], 'sell_r: ', self.sell_r, 'buy_r: ', self.buy_r, '\n' ================================================ FILE: ZhenZhang/source/IAA_NEW.py ================================================ from BSE2_msg_classes import Assignment, Order, Exch_msg from BSE_trader_agents import Trader; import random import math bse_sys_minprice = 1 # minimum price in the system, in cents/pennies bse_sys_maxprice = 200 # maximum price in the system, in cents/pennies class Trader_IAA_NEW(Trader): def __init__(self, ttype, tid, balance, time,m): Trader.__init__(self, ttype, tid, balance, time) self.limit = None self.job = None # learning variables self.r_shout_change_relative = 0.05 self.r_shout_change_absolute = 0.05 self.short_term_learning_rate = random.uniform(0.1, 0.5) self.long_term_learning_rate = random.uniform(0.1, 0.5) self.moving_average_weight_decay = 0.95 # how fast weight decays with time, lower is quicker, 0.9 in vytelingum self.moving_average_window_size = 5 self.offer_change_rate = 3.0 self.theta = -2.0 self.theta_max = 2.0 self.theta_min = -8.0 self.marketMax = bse_sys_maxprice # Variables to describe the market self.previous_transactions = [] self.moving_average_weights = [] for i in range(self.moving_average_window_size): self.moving_average_weights.append(self.moving_average_weight_decay ** i) self.estimated_equilibrium = [] self.smiths_alpha = [] self.prev_best_bid_p = None self.prev_best_bid_q = None self.prev_best_ask_p = None self.prev_best_ask_q = None # Trading Variables self.r_shout = None self.buy_target = None self.sell_target = None self.buy_r = -1.0 * (0.3 * random.random()) self.sell_r = -1.0 * (0.3 * random.random()) # variable for MLOFI self.last_lob = None; self.es_list = []; self.ds_list = []; #variable for ratio self.bids_volume_list = [] self.asks_volume_list = [] # m self.m = m; def is_imbalance_significant(self, m,threshold): cb_list = [0 for i in range(m)] ab_list = [] ca_list = [0 for i in range(m)] aa_list = [] n = 1 while len(self.bids_volume_list) >= n and len(self.asks_volume_list) >= n: for i in range(m): cb_list[i] += self.bids_volume_list[-n]['level' + str(i + 1)] ca_list[i] += self.asks_volume_list[-n]['level' + str(i + 1)] n += 1 if n >= 11: break for i in range(m): temp1 = None temp2 = None if n == 1: temp1 = cb_list[i] + 1 temp2 = ca_list[i] + 1 else: temp1 = cb_list[i] / (n - 1) + 1 temp2 = ca_list[i] / (n - 1) + 1 ab_list.append(temp1) aa_list.append(temp2) v_bid = 0; v_ask = 0; for i in range(m): v_bid += math.exp(-0.5*i)*ab_list[i]; v_ask += math.exp(-0.5*i)*aa_list[i]; ratio = (v_bid-v_ask)/(v_bid+v_ask); # print self.bids_volume_list # print self.asks_volume_list # print ratio if(ratio>threshold or ratio<-threshold): return True else: return False def calc_bids_volume(self, lob, m, verbose): new_b = {} for i in range(1, m + 1): new_b['level' + str(i)] = self.cal_bids_n(lob, i) self.bids_volume_list.append(new_b) def cal_bids_n(self, lob, n): if (len(lob['bids']['lob']) < n): r_n = 0 else: r_n = lob['bids']['lob'][n - 1][1] return r_n def calc_asks_volume(self, lob, m, verbose): new_a = {} for i in range(1, m + 1): new_a['level' + str(i)] = self.cal_asks_n(lob, i); self.asks_volume_list.append(new_a) def cal_asks_n(self, lob, n): if (len(lob['asks']['lob']) < n): q_n = 0 else: q_n = lob['asks']['lob'][n - 1][1] return q_n def calc_level_n_e(self, current_lob, n): b_n = 0 r_n = 0 a_n = 0 q_n = 0 b_n_1 = 0 r_n_1 = 0 a_n_1 = 0 q_n_1 = 0 if (len(current_lob['bids']['lob']) < n): b_n = 0 r_n = 0 else: b_n = current_lob['bids']['lob'][n - 1][0] r_n = current_lob['bids']['lob'][n - 1][1] if (len(self.last_lob['bids']['lob']) < n): b_n_1 = 0 r_n_1 = 0 else: b_n_1 = self.last_lob['bids']['lob'][n - 1][0] r_n_1 = self.last_lob['bids']['lob'][n - 1][1] if (len(current_lob['asks']['lob']) < n): a_n = 0 q_n = 0 else: a_n = current_lob['asks']['lob'][n - 1][0] q_n = current_lob['asks']['lob'][n - 1][1] if (len(self.last_lob['asks']['lob']) < n): a_n_1 = 0 q_n_1 = 0 else: a_n_1 = self.last_lob['asks']['lob'][n - 1][0] q_n_1 = self.last_lob['asks']['lob'][n - 1][1] delta_w = 0; if (b_n > b_n_1): delta_w = r_n elif (b_n == b_n_1): delta_w = r_n - r_n_1 else: delta_w = -r_n_1 delta_v = 0 if (a_n > a_n_1): delta_v = -q_n_1 elif (a_n == a_n_1): delta_v = q_n - q_n_1 else: delta_v = q_n return delta_w - delta_v def calc_es(self, lob, m, verbose): new_e = {} for i in range(1, m + 1): new_e['level' + str(i)] = self.calc_level_n_e(lob, i) self.es_list.append(new_e) def calc_ds(self, lob, m, verbose): new_d = {} for i in range(1, m + 1): new_d['level' + str(i)] = self.cal_depth_n(lob, i) self.ds_list.append(new_d) def cal_depth_n(self, lob, n): if (len(lob['bids']['lob']) < n): r_n = 0 else: r_n = lob['bids']['lob'][n - 1][1] if (len(lob['asks']['lob']) < n): q_n = 0 else: q_n = lob['asks']['lob'][n - 1][1] return (r_n + q_n) / 2 def calcEq(self): ##clear and correct # Slightly modified from paper, it is unclear inpaper # N previous transactions * weights / N in vytelingum, swap N denominator for sum of weights to be correct? if len(self.previous_transactions) == 0: return elif len(self.previous_transactions) < self.moving_average_window_size: # Not enough transactions self.estimated_equilibrium.append( float(sum(self.previous_transactions)) / max(len(self.previous_transactions), 1)) else: N_previous_transactions = self.previous_transactions[-self.moving_average_window_size:] thing = [N_previous_transactions[i] * self.moving_average_weights[i] for i in range(self.moving_average_window_size)] eq = sum(thing) / sum(self.moving_average_weights) self.estimated_equilibrium.append(eq) def calcAlpha(self): ##correct. but calcAlpha in snashall's version is incorrect alpha = 0.0 for p in self.previous_transactions: alpha += (p - self.estimated_equilibrium[-1]) ** 2 alpha = math.sqrt(alpha / len(self.previous_transactions)) self.smiths_alpha.append(alpha / self.estimated_equilibrium[-1]) def calcTheta(self): ## clear and correct gamma = 2.0 # not sensitive apparently so choose to be whatever # necessary for intialisation, div by 0 if min(self.smiths_alpha) == max(self.smiths_alpha): alpha_range = 0.4 # starting value i guess else: alpha_range = (self.smiths_alpha[-1] - min(self.smiths_alpha)) / ( max(self.smiths_alpha) - min(self.smiths_alpha)) theta_range = self.theta_max - self.theta_min desired_theta = self.theta_min + (theta_range) * (1 - alpha_range) * math.exp(gamma * (alpha_range - 1)) self.theta = self.theta + self.long_term_learning_rate * (desired_theta - self.theta) if self.theta > self.theta_max: self.theta = self.theta_max if self.theta < self.theta_min: self.theta = self.theta_min def calcRshout(self): ## unclear in Vytelingum's paper p = self.estimated_equilibrium[-1] l = self.limit theta = self.theta if self.job == 'Bid': # Currently a buyer if l <= p: # extramarginal! self.r_shout = 0.0 else: # intramarginal :( if self.buy_target > self.estimated_equilibrium[-1]: # r[0,1] self.r_shout = math.log(((self.buy_target - p) * (math.exp(theta) - 1) / (l - p)) + 1) / theta else: # r[-1,0] # print 'buy_target: %f , p: %f , theta: %f' %(self.buy_target,p,theta) self.r_shout = math.log((1 - (self.buy_target / p)) * (math.exp(theta) - 1) + 1) / theta # self.r_shout = self.buy_r if self.job == 'Ask': # Currently a seller if l >= p: # extramarginal! self.r_shout = 0 else: # intramarginal :( if self.sell_target > self.estimated_equilibrium[-1]: # r[-1,0] self.r_shout = math.log( (self.sell_target - p) * (math.exp(theta) - 1) / (self.marketMax - p) + 1) / theta else: # r[0,1] a = (self.sell_target - l) / (p - l) self.r_shout = (math.log((1 - a) * (math.exp(theta) - 1) + 1)) / theta # self.r_shout = self.sell_r def calcAgg(self): delta = 0 if self.job == 'Bid': # BUYER if self.buy_target >= self.previous_transactions[-1]: # must be more aggressive delta = (1 + self.r_shout_change_relative) * self.r_shout + self.r_shout_change_absolute else: delta = (1 - self.r_shout_change_relative) * self.r_shout - self.r_shout_change_absolute self.buy_r = self.buy_r + self.short_term_learning_rate * (delta - self.buy_r) if self.job == 'Ask': # SELLER if self.sell_target > self.previous_transactions[-1]: delta = (1 + self.r_shout_change_relative) * self.r_shout + self.r_shout_change_absolute else: delta = (1 - self.r_shout_change_relative) * self.r_shout - self.r_shout_change_absolute self.sell_r = self.sell_r + self.short_term_learning_rate * (delta - self.sell_r) def calcTarget(self): if len(self.estimated_equilibrium) > 0: p = self.estimated_equilibrium[-1] if self.limit == p: p = p * 1.000001 # to prevent theta_bar = 0 elif self.job == 'Bid': p = self.limit - self.limit * 0.2 ## Initial guess for eq if no deals yet!!.... elif self.job == 'Ask': p = self.limit + self.limit * 0.2 l = self.limit theta = self.theta if self.job == 'Bid': # BUYER minus_thing = self.buy_r * math.exp(theta * (self.buy_r - 1)) if l <= p: # Extramarginal if self.buy_r >= 0: self.buy_target = l else: self.buy_target = l * (1 - minus_thing) else: # intramarginal if self.buy_r >= 0: # theta_ba = (p * math.exp(-theta))/(l-p)-1 theta_ba = theta # print 'theta: %f' %(self.theta) # print 'theta_ba: %f '%(theta_ba) # print 'l-p: %f '%(l-p) # print 'self.buy_r :%f' %(self.buy_r) self.buy_target = (l - p) * (1 - (self.buy_r + 1) * math.exp(self.buy_r * theta_ba)) + p else: self.buy_target = p * (1 - minus_thing) if self.buy_target > l: self.buy_target = l if self.buy_target < bse_sys_minprice: self.buy_target = bse_sys_minprice # print 'buy_target = %f'%(self.buy_target) if self.job == 'Ask': # SELLER if l <= p: # Intramarginal if self.buy_r >= 0: self.buy_target = p + (p - l) * self.sell_r * math.exp((self.sell_r - 1) * theta) else: theta_ba = math.log((self.marketMax - p) / (p - l)) - theta self.buy_target = p + (self.marketMax - p) * self.sell_r * math.exp((self.sell_r + 1) * theta_ba) else: # Extramarginal if self.buy_r >= 0: self.buy_target = l else: self.buy_target = l + (self.marketMax - l) * self.sell_r * math.exp((self.sell_r - 1) * theta) if self.sell_target < l: self.sell_target = l if self.sell_target > bse_sys_maxprice: self.sell_target = bse_sys_maxprice # print 'sell_target = %f'%(self.sell_target) def getorder(self, time, countdown, lob, verbose): if len(self.orders) < 1: self.active = False return None else: self.active = True self.limit = self.orders[0].price self.job = self.orders[0].atype self.calcTarget() if self.prev_best_bid_p == None: o_bid = 0 else: o_bid = self.prev_best_bid_p if self.prev_best_ask_p == None: o_ask = self.marketMax else: o_ask = self.prev_best_ask_p if self.job == 'Bid': # BUYER if self.limit <= o_bid: return None else: if len(self.previous_transactions) <= 0: ## has been at least one transaction o_ask_plus = (1 + self.r_shout_change_relative) * o_ask + self.r_shout_change_absolute quoteprice = o_bid + ((min(self.limit, o_ask_plus) - o_bid) / self.offer_change_rate) else: if o_ask <= self.buy_target: quoteprice = o_ask else: quoteprice = o_bid + ((self.buy_target - o_bid) / self.offer_change_rate) if self.job == 'Ask': if self.limit >= o_ask: return None else: if len(self.previous_transactions) <= 0: ## has been at least one transaction o_bid_minus = (1 - self.r_shout_change_relative) * o_bid - self.r_shout_change_absolute quoteprice = o_ask - ((o_ask - max(self.limit, o_bid_minus)) / self.offer_change_rate) else: if o_bid >= self.sell_target: quoteprice = o_bid else: quoteprice = o_ask - ((o_ask - self.sell_target) / self.offer_change_rate) def imbalance_alter(quoteprice_aa, lob, countdown, m): mlofi_list = [0 for i in range(m)] cd_list = [0 for i in range(m)] ad_list = [] n = 1 while len(self.es_list) >= n: for i in range(m): mlofi_list[i] += self.es_list[-n]['level' + str(i+1)] n += 1 if n >= 11: break n = 1 while len(self.ds_list) >= n: for i in range(m): cd_list[i] += self.ds_list[-n]['level' + str(i+1)] n += 1 if n >= 11: break for i in range(m): temp = None if n == 1: temp = cd_list[i]+1 else: temp = cd_list[i]/(n-1)+1 ad_list.append(temp) c = 5 decay = 0.8 offset = 0 for i in range(m): offset += int(mlofi_list[i]*c*pow(decay,i)/ ad_list[i]) benchmark = quoteprice_aa; if(lob['midprice'] != None): benchmark = lob['midprice'] # print 'midprice is %d' % benchmark quoteprice_iaa = quoteprice_aa + 0.8 * (benchmark + offset - quoteprice_aa) if self.job == 'Bid' and quoteprice_iaa > self.limit: quoteprice_iaa = self.limit if self.job == 'Ask' and quoteprice_iaa < self.limit: quoteprice_iaa = self.limit if countdown < 0.3 : print "insert" if self.job == 'Bid' and (len(lob['asks']['lob']) >= 1) and lob['asks']['lob'][0][0] < self.limit: quoteprice_iaa = lob['asks']['lob'][0][0] if self.job == 'Ask' and (len(lob['bids']['lob']) >= 1) and lob['bids']['lob'][0][0] > self.limit: quoteprice_iaa = lob['bids']['lob'][0][0] return quoteprice_iaa if(self.is_imbalance_significant(self.m,0.6)): # print "abvious" quoteprice_iaa = imbalance_alter(quoteprice, lob, countdown, self.m) else: # print "not abvious" quoteprice_iaa = quoteprice order = Order(self.tid, self.orders[0].atype, 'LIM', quoteprice_iaa, self.orders[0].qty, time, None, -1) self.lastquote = order return order def respond(self, time, lob, trade, verbose): ## Begin nicked from ZIP # what, if anything, has happened on the bid LOB? Nicked from ZIP.. bid_improved = False bid_hit = False lob_best_bid_p = lob['bids']['bestp'] lob_best_bid_q = None if lob_best_bid_p != None: # non-empty bid LOB lob_best_bid_q = lob['bids']['lob'][0][1] if self.prev_best_bid_p < lob_best_bid_p: # best bid has improved # NB doesn't check if the improvement was by self bid_improved = True elif trade != None and ((self.prev_best_bid_p > lob_best_bid_p) or ( (self.prev_best_bid_p == lob_best_bid_p) and (self.prev_best_bid_q > lob_best_bid_q))): # previous best bid was hit bid_hit = True elif self.prev_best_bid_p != None: # # the bid LOB has been emptied: was it cancelled or hit? # last_tape_item = lob['tape'][-1] # if last_tape_item['type'] == 'Cancel' : # bid_hit = False # else: # bid_hit = True # the bid LOB is empty now but was not previously: so was it canceled or lifted? if trade != None: # a trade has occurred and the previously nonempty ask LOB is now empty # so assume best ask was lifted bid_hit = True else: bid_hit = False # what, if anything, has happened on the ask LOB? ask_improved = False ask_lifted = False lob_best_ask_p = lob['asks']['bestp'] lob_best_ask_q = None if lob_best_ask_p != None: # non-empty ask LOB lob_best_ask_q = lob['asks']['lob'][0][1] if self.prev_best_ask_p > lob_best_ask_p: # best ask has improved -- NB doesn't check if the improvement was by self ask_improved = True elif trade != None and ((self.prev_best_ask_p < lob_best_ask_p) or ( (self.prev_best_ask_p == lob_best_ask_p) and (self.prev_best_ask_q > lob_best_ask_q))): # trade happened and best ask price has got worse, or stayed same but quantity reduced -- assume previous best ask was lifted ask_lifted = True elif self.prev_best_ask_p != None: # the ask LOB is empty now but was not previously: canceled or lifted? # last_tape_item = lob['tape'][-1] # if last_tape_item['type'] == 'Cancel' : # ask_lifted = False # else: # ask_lifted = True # the ask LOB is empty now but was not previously: so was it canceled or lifted? if trade != None: # a trade has occurred and the previously nonempty ask LOB is now empty # so assume best ask was lifted ask_lifted = True else: ask_lifted = False self.prev_best_bid_p = lob_best_bid_p self.prev_best_bid_q = lob_best_bid_q self.prev_best_ask_p = lob_best_ask_p self.prev_best_ask_q = lob_best_ask_q deal = bid_hit or ask_lifted ## End nicked from ZIP if (self.last_lob == None): self.last_lob = lob else: self.calc_es(lob, self.m, verbose) self.calc_ds(lob, self.m, verbose) self.calc_bids_volume(lob, self.m, verbose) self.calc_asks_volume(lob, self.m, verbose) self.last_lob = lob; if deal: self.previous_transactions.append(trade['price']) if self.sell_target == None: self.sell_target = trade['price'] if self.buy_target == None: self.buy_target = trade['price'] self.calcEq() self.calcAlpha() self.calcTheta() self.calcRshout() self.calcAgg() self.calcTarget() # print 'sell: ', self.sell_target, 'buy: ', self.buy_target, 'limit:', self.limit, 'eq: ', self.estimated_equilibrium[-1], 'sell_r: ', self.sell_r, 'buy_r: ', self.buy_r, '\n' ================================================ FILE: ZhenZhang/source/IGDX_MLOFI.py ================================================ # Trader subclass ZIP # After Cliff 1997 from BSE2_msg_classes import Assignment, Order, Exch_msg from BSE_trader_agents import Trader; import random import math bse_sys_minprice = 1 # minimum price in the system, in cents/pennies bse_sys_maxprice = 200 # maximum price in the system, in cents/pennies class Trader_IGDX_MLOFI(Trader): def __init__(self, ttype, tid, balance, time,m): Trader.__init__(self, ttype, tid, balance, time) self.active = False self.limit = None self.job = None #memory of all bids and asks and accepted bids and asks self.outstanding_bids = [] self.outstanding_asks = [] self.accepted_asks = [] self.accepted_bids = [] self.price = -1 # memory of best price & quantity of best bid and ask, on LOB on previous update self.prev_best_bid_p = None self.prev_best_bid_q = None self.prev_best_ask_p = None self.prev_best_ask_q = None self.first_turn = True self.gamma = 0.1 self.holdings = 10 self.remaining_offer_ops = 10 self.values = [[0 for n in range(self.remaining_offer_ops)] for m in range(self.holdings)] # variable for MLOFI self.last_lob = None; self.es_list = []; self.ds_list = []; # variable for ratio self.bids_volume_list = [] self.asks_volume_list = [] # variable self.m = m; def is_imbalance_significant(self, m, threshold): cb_list = [0 for i in range(m)] ab_list = [] ca_list = [0 for i in range(m)] aa_list = [] n = 1 while len(self.bids_volume_list) >= n and len(self.asks_volume_list) >= n: for i in range(m): cb_list[i] += self.bids_volume_list[-n]['level' + str(i + 1)] ca_list[i] += self.asks_volume_list[-n]['level' + str(i + 1)] n += 1 if n >= 11: break for i in range(m): temp1 = None temp2 = None if n == 1: temp1 = cb_list[i] + 1 temp2 = ca_list[i] + 1 else: temp1 = cb_list[i] / (n - 1) + 1 temp2 = ca_list[i] / (n - 1) + 1 ab_list.append(temp1) aa_list.append(temp2) v_bid = 0; v_ask = 0; for i in range(m): v_bid += math.exp(-0.5 * i) * ab_list[i]; v_ask += math.exp(-0.5 * i) * aa_list[i]; ratio = (v_bid - v_ask) / (v_bid + v_ask); # print self.bids_volume_list # print self.asks_volume_list # print ratio if (ratio > threshold or ratio < -threshold): return True else: return False def calc_bids_volume(self, lob, m, verbose): new_b = {} for i in range(1, m + 1): new_b['level' + str(i)] = self.cal_bids_n(lob, i) self.bids_volume_list.append(new_b) def cal_bids_n(self, lob, n): if (len(lob['bids']['lob']) < n): r_n = 0 else: r_n = lob['bids']['lob'][n - 1][1] return r_n def calc_asks_volume(self, lob, m, verbose): new_a = {} for i in range(1, m + 1): new_a['level' + str(i)] = self.cal_asks_n(lob, i); self.asks_volume_list.append(new_a) def cal_asks_n(self, lob, n): if (len(lob['asks']['lob']) < n): q_n = 0 else: q_n = lob['asks']['lob'][n - 1][1] return q_n def calc_level_n_e(self, current_lob, n): b_n = 0 r_n = 0 a_n = 0 q_n = 0 b_n_1 = 0 r_n_1 = 0 a_n_1 = 0 q_n_1 = 0 if (len(current_lob['bids']['lob']) < n): b_n = 0 r_n = 0 else: b_n = current_lob['bids']['lob'][n - 1][0] r_n = current_lob['bids']['lob'][n - 1][1] if (len(self.last_lob['bids']['lob']) < n): b_n_1 = 0 r_n_1 = 0 else: b_n_1 = self.last_lob['bids']['lob'][n - 1][0] r_n_1 = self.last_lob['bids']['lob'][n - 1][1] if (len(current_lob['asks']['lob']) < n): a_n = 0 q_n = 0 else: a_n = current_lob['asks']['lob'][n - 1][0] q_n = current_lob['asks']['lob'][n - 1][1] if (len(self.last_lob['asks']['lob']) < n): a_n_1 = 0 q_n_1 = 0 else: a_n_1 = self.last_lob['asks']['lob'][n - 1][0] q_n_1 = self.last_lob['asks']['lob'][n - 1][1] delta_w = 0; if (b_n > b_n_1): delta_w = r_n elif (b_n == b_n_1): delta_w = r_n - r_n_1 else: delta_w = -r_n_1 delta_v = 0 if (a_n > a_n_1): delta_v = -q_n_1 elif (a_n == a_n_1): delta_v = q_n - q_n_1 else: delta_v = q_n return delta_w - delta_v def calc_es(self, lob, m, verbose): new_e = {} for i in range(1, m + 1): new_e['level' + str(i)] = self.calc_level_n_e(lob, i) self.es_list.append(new_e) def calc_ds(self, lob, m, verbose): new_d = {} for i in range(1, m + 1): new_d['level' + str(i)] = self.cal_depth_n(lob, i) self.ds_list.append(new_d) def cal_depth_n(self, lob, n): if (len(lob['bids']['lob']) < n): r_n = 0 else: r_n = lob['bids']['lob'][n - 1][1] if (len(lob['asks']['lob']) < n): q_n = 0 else: q_n = lob['asks']['lob'][n - 1][1] return (r_n + q_n) / 2 def getorder(self, time, countdown, lob, verbose): def imbalance_alter(quoteprice_aa, lob, countdown, m): mlofi_list = [0 for i in range(m)] cd_list = [0 for i in range(m)] ad_list = [] n = 1 while len(self.es_list) >= n: for i in range(m): mlofi_list[i] += self.es_list[-n]['level' + str(i + 1)] n += 1 if n >= 11: break n = 1 while len(self.ds_list) >= n: for i in range(m): cd_list[i] += self.ds_list[-n]['level' + str(i + 1)] n += 1 if n >= 11: break for i in range(m): temp = None if n == 1: temp = cd_list[i] + 1 else: temp = cd_list[i] / (n - 1) + 1 ad_list.append(temp) c = 5 decay = 0.8 offset = 0 for i in range(m): offset += int(mlofi_list[i] * c * pow(decay, i) / ad_list[i]) benchmark = quoteprice_aa; if (lob['midprice'] != None): benchmark = lob['midprice'] # print 'midprice is %d' % benchmark quoteprice_iaa = quoteprice_aa + 0.8 * (benchmark + offset - quoteprice_aa) if self.job == 'Bid' and quoteprice_iaa > self.limit: quoteprice_iaa = self.limit if self.job == 'Ask' and quoteprice_iaa < self.limit: quoteprice_iaa = self.limit if countdown < 0.3: print "insert" if self.job == 'Bid' and (len(lob['asks']['lob']) >= 1) and lob['asks']['lob'][0][0] < self.limit: quoteprice_iaa = lob['asks']['lob'][0][0] if self.job == 'Ask' and (len(lob['bids']['lob']) >= 1) and lob['bids']['lob'][0][0] > self.limit: quoteprice_iaa = lob['bids']['lob'][0][0] if self.job == 'Bid' and quoteprice_iaa < bse_sys_minprice: quoteprice_iaa = bse_sys_minprice+1 if self.job == 'Ask' and quoteprice_iaa > bse_sys_maxprice: quoteprice_iaa = bse_sys_maxprice-1 return quoteprice_iaa if len(self.orders) < 1: self.active = False order = None return order else: self.active = True self.limit = self.orders[0].price self.job = self.orders[0].atype #calculate price if self.job == 'Bid': self.price = self.calc_p_bid(self.holdings - 1, self.remaining_offer_ops - 1) if self.job == 'Ask': self.price = self.calc_p_ask(self.holdings - 1, self.remaining_offer_ops - 1) quoteprice = self.price # print "before:" # print self.price if (self.is_imbalance_significant(self.m, 0.6)): # print "abvious" quoteprice_igdx = imbalance_alter(quoteprice, lob, countdown, self.m) else: # print "not abvious" quoteprice_igdx = quoteprice self.price = quoteprice_igdx # # print "after:" # print self.price order = Order(self.tid, self.job, 'LIM',self.price, self.orders[0].qty, time, None, -1) self.lastquote = order if self.first_turn or self.price == -1: if self.job == 'Bid': order = Order(self.tid, self.job, 'LIM', bse_sys_minprice+1, self.orders[0].qty, time, None, -1) if self.job == 'Ask': order = Order(self.tid, self.job, 'LIM', bse_sys_maxprice-1, self.orders[0].qty, time, None, -1) # print order return order def calc_p_bid(self, m, n): best_return = 0 best_bid = 0 second_best_return = 0 second_best_bid = 0 #first step size of 1 get best and 2nd best for i in [x*2 for x in range(int(self.limit/2))]: thing = self.belief_buy(i) * ((self.limit - i) + self.gamma*self.values[m-1][n-1]) + (1-self.belief_buy(i) * self.gamma * self.values[m][n-1]) if thing > best_return: second_best_bid = best_bid second_best_return = best_return best_return = thing best_bid = i #always best bid largest one if second_best_bid > best_bid: a = second_best_bid second_best_bid = best_bid best_bid = a #then step size 0.05 for i in [x*0.05 for x in range(int(second_best_bid), int(best_bid))]: thing = self.belief_buy(i + second_best_bid) * ((self.limit - (i + second_best_bid)) + self.gamma*self.values[m-1][n-1]) + (1-self.belief_buy(i + second_best_bid) * self.gamma * self.values[m][n-1]) if thing > best_return: best_return = thing best_bid = i + second_best_bid return best_bid def calc_p_ask(self, m, n): best_return = 0 best_ask = self.limit second_best_return = 0 second_best_ask = self.limit #first step size of 1 get best and 2nd best for i in [x*2 for x in range(int(self.limit/2))]: j = i + self.limit thing = self.belief_sell(j) * ((j - self.limit) + self.gamma*self.values[m-1][n-1]) + (1-self.belief_sell(j) * self.gamma * self.values[m][n-1]) if thing > best_return: second_best_ask = best_ask second_best_return = best_return best_return = thing best_ask = j #always best ask largest one if second_best_ask > best_ask: a = second_best_ask second_best_ask = best_ask best_ask = a #then step size 0.05 for i in [x*0.05 for x in range(int(second_best_ask), int(best_ask))]: thing = self.belief_sell(i + second_best_ask) * (((i + second_best_ask) - self.limit) + self.gamma*self.values[m-1][n-1]) + (1-self.belief_sell(i + second_best_ask) * self.gamma * self.values[m][n-1]) if thing > best_return: best_return = thing best_ask = i + second_best_ask return best_ask def belief_sell(self, price): accepted_asks_greater = 0 bids_greater = 0 unaccepted_asks_lower = 0 for p in self.accepted_asks: if p >= price: accepted_asks_greater += 1 for p in [thing[0] for thing in self.outstanding_bids]: if p >= price: bids_greater += 1 for p in [thing[0] for thing in self.outstanding_asks]: if p <= price: unaccepted_asks_lower += 1 if accepted_asks_greater + bids_greater + unaccepted_asks_lower == 0: return 0 return (accepted_asks_greater + bids_greater) / (accepted_asks_greater + bids_greater + unaccepted_asks_lower) def belief_buy(self, price): accepted_bids_lower = 0 asks_lower = 0 unaccepted_bids_greater = 0 for p in self.accepted_bids: if p <= price: accepted_bids_lower += 1 for p in [thing[0] for thing in self.outstanding_asks]: if p <= price: asks_lower += 1 for p in [thing[0] for thing in self.outstanding_bids]: if p >= price: unaccepted_bids_greater += 1 if accepted_bids_lower + asks_lower + unaccepted_bids_greater == 0: return 0 return (accepted_bids_lower + asks_lower) / (accepted_bids_lower + asks_lower + unaccepted_bids_greater) def respond(self, time, lob, trade, verbose): if (self.last_lob == None): self.last_lob = lob else: self.calc_es(lob, self.m, verbose) self.calc_ds(lob, self.m, verbose) self.calc_bids_volume(lob, self.m, verbose) self.calc_asks_volume(lob, self.m, verbose) self.last_lob = lob; # what, if anything, has happened on the bid LOB? self.outstanding_bids = lob['bids']['lob'] bid_improved = False bid_hit = False lob_best_bid_p = lob['bids']['bestp'] lob_best_bid_q = None if lob_best_bid_p != None: # non-empty bid LOB lob_best_bid_q = lob['bids']['lob'][-1][1] if self.prev_best_bid_p < lob_best_bid_p : # best bid has improved # NB doesn't check if the improvement was by self bid_improved = True elif trade != None and ((self.prev_best_bid_p > lob_best_bid_p) or ((self.prev_best_bid_p == lob_best_bid_p) and (self.prev_best_bid_q > lob_best_bid_q))): # previous best bid was hit self.accepted_bids.append(self.prev_best_bid_p) bid_hit = True elif self.prev_best_bid_p != None: # the bid LOB has been emptied: was it cancelled or hit? last_tape_item = lob['tape'][-1] if last_tape_item['type'] == 'Cancel' : bid_hit = False else: bid_hit = True # what, if anything, has happened on the ask LOB? self.outstanding_asks = lob['asks']['lob'] ask_improved = False ask_lifted = False lob_best_ask_p = lob['asks']['bestp'] lob_best_ask_q = None if lob_best_ask_p != None: # non-empty ask LOB lob_best_ask_q = lob['asks']['lob'][0][1] if self.prev_best_ask_p > lob_best_ask_p : # best ask has improved -- NB doesn't check if the improvement was by self ask_improved = True elif trade != None and ((self.prev_best_ask_p < lob_best_ask_p) or ((self.prev_best_ask_p == lob_best_ask_p) and (self.prev_best_ask_q > lob_best_ask_q))): # trade happened and best ask price has got worse, or stayed same but quantity reduced -- assume previous best ask was lifted self.accepted_asks.append(self.prev_best_ask_p) ask_lifted = True elif self.prev_best_ask_p != None: # the ask LOB is empty now but was not previously: canceled or lifted? last_tape_item = lob['tape'][-1] if last_tape_item['type'] == 'Cancel' : ask_lifted = False else: ask_lifted = True #populate expected values if self.first_turn: # print "populating" self.first_turn = False for n in range(1, self.remaining_offer_ops): for m in range(1, self.holdings): if self.job == 'Bid': #BUYER self.values[m][n] = self.calc_p_bid(m, n) if self.job == 'Ask': #BUYER self.values[m][n] = self.calc_p_ask(m, n) # print "done" deal = bid_hit or ask_lifted # remember the best LOB data ready for next response self.prev_best_bid_p = lob_best_bid_p self.prev_best_bid_q = lob_best_bid_q self.prev_best_ask_p = lob_best_ask_p self.prev_best_ask_q = lob_best_ask_q ================================================ FILE: ZhenZhang/source/IZIP_MLOFI.py ================================================ from BSE2_msg_classes import Assignment, Order, Exch_msg from BSE_trader_agents import Trader; import random import math bse_sys_minprice = 1 # minimum price in the system, in cents/pennies bse_sys_maxprice = 200 # maximum price in the system, in cents/pennies class Trader_IZIP_MLOFI(Trader): # ZIP init key param-values are those used in Cliff's 1997 original HP Labs tech report # NB this implementation keeps separate margin values for buying & selling, # so a single trader can both buy AND sell # -- in the original, traders were either buyers OR sellers def __init__(self, ttype, tid, balance, time, m): Trader.__init__(self, ttype, tid, balance, time) m_fix = 0.05 m_var = 0.05 self.job = None # this is 'Bid' or 'Ask' depending on customer order self.active = False # gets switched to True while actively working an order self.prev_change = 0 # this was called last_d in Cliff'97 self.beta = 0.1 + 0.2 * random.random() # learning rate self.momntm = 0.3 * random.random() # momentum self.ca = 0.10 # self.ca & .cr were hard-coded in '97 but parameterised later self.cr = 0.10 self.margin = None # this was called profit in Cliff'97 self.margin_buy = -1.0 * (m_fix + m_var * random.random()) self.margin_sell = m_fix + m_var * random.random() self.price = None self.limit = None # memory of best price & quantity of best bid and ask, on LOB on previous update self.prev_best_bid_p = None self.prev_best_bid_q = None self.prev_best_ask_p = None self.prev_best_ask_q = None # memory of worst prices from customer orders received so far self.worst_bidprice = None self.worst_askprice = None # variable for MLOFI self.last_lob = None; self.es_list = []; self.ds_list = []; #variable for ratio self.bids_volume_list = [] self.asks_volume_list = [] #variable self.m = m; def is_imbalance_significant(self, m,threshold): cb_list = [0 for i in range(m)] ab_list = [] ca_list = [0 for i in range(m)] aa_list = [] n = 1 while len(self.bids_volume_list) >= n and len(self.asks_volume_list) >= n: for i in range(m): cb_list[i] += self.bids_volume_list[-n]['level' + str(i + 1)] ca_list[i] += self.asks_volume_list[-n]['level' + str(i + 1)] n += 1 if n >= 11: break for i in range(m): temp1 = None temp2 = None if n == 1: temp1 = cb_list[i] + 1 temp2 = ca_list[i] + 1 else: temp1 = cb_list[i] / (n - 1) + 1 temp2 = ca_list[i] / (n - 1) + 1 ab_list.append(temp1) aa_list.append(temp2) v_bid = 0; v_ask = 0; for i in range(m): v_bid += math.exp(-0.5*i)*ab_list[i]; v_ask += math.exp(-0.5*i)*aa_list[i]; ratio = (v_bid-v_ask)/(v_bid+v_ask); # print self.bids_volume_list # print self.asks_volume_list # print ratio if(ratio>threshold or ratio<-threshold): return True else: return False def calc_bids_volume(self, lob, m, verbose): new_b = {} for i in range(1, m + 1): new_b['level' + str(i)] = self.cal_bids_n(lob, i) self.bids_volume_list.append(new_b) def cal_bids_n(self, lob, n): if (len(lob['bids']['lob']) < n): r_n = 0 else: r_n = lob['bids']['lob'][n - 1][1] return r_n def calc_asks_volume(self, lob, m, verbose): new_a = {} for i in range(1, m + 1): new_a['level' + str(i)] = self.cal_asks_n(lob, i); self.asks_volume_list.append(new_a) def cal_asks_n(self, lob, n): if (len(lob['asks']['lob']) < n): q_n = 0 else: q_n = lob['asks']['lob'][n - 1][1] return q_n def calc_level_n_e(self, current_lob, n): b_n = 0 r_n = 0 a_n = 0 q_n = 0 b_n_1 = 0 r_n_1 = 0 a_n_1 = 0 q_n_1 = 0 if (len(current_lob['bids']['lob']) < n): b_n = 0 r_n = 0 else: b_n = current_lob['bids']['lob'][n - 1][0] r_n = current_lob['bids']['lob'][n - 1][1] if (len(self.last_lob['bids']['lob']) < n): b_n_1 = 0 r_n_1 = 0 else: b_n_1 = self.last_lob['bids']['lob'][n - 1][0] r_n_1 = self.last_lob['bids']['lob'][n - 1][1] if (len(current_lob['asks']['lob']) < n): a_n = 0 q_n = 0 else: a_n = current_lob['asks']['lob'][n - 1][0] q_n = current_lob['asks']['lob'][n - 1][1] if (len(self.last_lob['asks']['lob']) < n): a_n_1 = 0 q_n_1 = 0 else: a_n_1 = self.last_lob['asks']['lob'][n - 1][0] q_n_1 = self.last_lob['asks']['lob'][n - 1][1] delta_w = 0; if (b_n > b_n_1): delta_w = r_n elif (b_n == b_n_1): delta_w = r_n - r_n_1 else: delta_w = -r_n_1 delta_v = 0 if (a_n > a_n_1): delta_v = -q_n_1 elif (a_n == a_n_1): delta_v = q_n - q_n_1 else: delta_v = q_n return delta_w - delta_v def calc_es(self, lob, m, verbose): new_e = {} for i in range(1, m + 1): new_e['level' + str(i)] = self.calc_level_n_e(lob, i) self.es_list.append(new_e) def calc_ds(self, lob, m, verbose): new_d = {} for i in range(1, m + 1): new_d['level' + str(i)] = self.cal_depth_n(lob, i) self.ds_list.append(new_d) def cal_depth_n(self, lob, n): if (len(lob['bids']['lob']) < n): r_n = 0 else: r_n = lob['bids']['lob'][n - 1][1] if (len(lob['asks']['lob']) < n): q_n = 0 else: q_n = lob['asks']['lob'][n - 1][1] return (r_n + q_n) / 2 def __str__(self): s = '%s, job=, %s, ' % (self.tid, self.job) if self.active == True: s = s + 'actv=,T, ' else: s = s + 'actv=,F, ' if self.margin == None: s = s + 'mrgn=,N, ' else: s = s + 'mrgn=,%5.2f, ' % self.margin s = s + 'lmt=,%s, price=,%s, bestbid=,%s,@,%s, bestask=,%s,@,%s, wrstbid=,%s, wrstask=,%s' % \ (self.limit, self.price, self.prev_best_bid_q, self.prev_best_bid_p, self.prev_best_ask_q, self.prev_best_ask_p, self.worst_bidprice, self.worst_askprice) return (s) def getorder(self, time, countdown, lob, verbose): if verbose: print('ZIP getorder(): LOB=%s' % lob) # random coefficient, multiplier on trader's own estimate of worst possible bid/ask prices # currently in arbitrarily chosen range [2, 5] worst_coeff = 2 + (3 * random.random()) if len(self.orders) < 1: self.active = False order = None else: self.active = True self.limit = self.orders[0].price self.job = self.orders[0].atype if self.job == 'Bid': # currently a buyer (working a bid order) self.margin = self.margin_buy # what is the worst bid price on the LOB right now? if len(lob['bids']['lob']) > 0: # take price of final entry on LOB worst_bid = lob['bids']['lob'][-1][0] else: # local pessimistic estimate of the worst bid price (own version of stub quote) worst_bid = max(1, int(self.limit / worst_coeff)) if self.worst_bidprice == None: self.worst_bidprice = worst_bid elif self.worst_bidprice > worst_bid: self.worst_bidprice = worst_bid else: # currently a seller (working a sell order) self.margin = self.margin_sell # what is the worst ask price on the LOB right now? if len(lob['asks']['lob']) > 0: # take price of final entry on LOB worst_ask = lob['asks']['lob'][-1][0] else: # local pessimistic estimate of the worst ask price (own version of stub quote) worst_ask = int(self.limit * worst_coeff) if self.worst_askprice == None: self.worst_askprice = worst_ask elif self.worst_askprice < worst_ask: self.worst_askprice = worst_ask quoteprice = int(self.limit * (1 + self.margin)) def imbalance_alter(quoteprice_aa, lob, countdown, m): mlofi_list = [0 for i in range(m)] cd_list = [0 for i in range(m)] ad_list = [] n = 1 while len(self.es_list) >= n: for i in range(m): mlofi_list[i] += self.es_list[-n]['level' + str(i+1)] n += 1 if n >= 11: break n = 1 while len(self.ds_list) >= n: for i in range(m): cd_list[i] += self.ds_list[-n]['level' + str(i+1)] n += 1 if n >= 11: break for i in range(m): temp = None if n == 1: temp = cd_list[i]+1 else: temp = cd_list[i]/(n-1)+1 ad_list.append(temp) c = 10 decay = 1 offset = 0 for i in range(m): offset += int(mlofi_list[i]*c*pow(decay,i)/ ad_list[i]) benchmark = quoteprice_aa; if(lob['midprice'] != None): benchmark = lob['midprice'] # print 'midprice is %d' % benchmark quoteprice_iaa = quoteprice_aa + 0.8 * (benchmark + offset - quoteprice_aa) if self.job == 'Bid' and quoteprice_iaa > self.limit: quoteprice_iaa = self.limit if self.job == 'Ask' and quoteprice_iaa < self.limit: quoteprice_iaa = self.limit if countdown < 0.3 : print "insert" if self.job == 'Bid' and (len(lob['asks']['lob']) >= 1) and lob['asks']['lob'][0][0] < self.limit: quoteprice_iaa = lob['asks']['lob'][0][0]+1 if self.job == 'Ask' and (len(lob['bids']['lob']) >= 1) and lob['bids']['lob'][0][0] > self.limit: quoteprice_iaa = lob['bids']['lob'][0][0]-1 if self.job == 'Bid' and quoteprice_iaa < bse_sys_minprice: quoteprice_iaa = bse_sys_minprice + 1 if self.job == 'Ask' and quoteprice_iaa > bse_sys_maxprice: quoteprice_iaa = bse_sys_maxprice - 1 return quoteprice_iaa # print "before" # print quoteprice # if(self.is_imbalance_significant(self.m,0.6)): # print "abvious" # quoteprice_izip = imbalance_alter(quoteprice, lob, countdown, self.m) # else: # print "not abvious" # quoteprice_izip = quoteprice quoteprice_izip = imbalance_alter(quoteprice, lob, countdown, self.m) # print "after" # print quoteprice_izip self.price = quoteprice_izip order = Order(self.tid, self.job, "LIM", quoteprice_izip, self.orders[0].qty, time, None, -1) self.lastquote = order return order # update margin on basis of what happened in market def respond(self, time, lob, trade, verbose): # ZIP trader responds to market events, altering its margin # does this whether it currently has an order to work or not if (self.last_lob == None): self.last_lob = lob else: self.calc_es(lob, self.m, verbose) self.calc_ds(lob, self.m, verbose) self.calc_bids_volume(lob, self.m, verbose) self.calc_asks_volume(lob, self.m, verbose) self.last_lob = lob; def target_up(price): # generate a higher target price by randomly perturbing given price ptrb_abs = self.ca * random.random() # absolute shift ptrb_rel = price * (1.0 + (self.cr * random.random())) # relative shift target = int(round(ptrb_rel + ptrb_abs, 0)) if target == price: target = price + 1 # enforce minimal difference # print('TargetUp: %d %d\n' % (price, target)) return (target) def target_down(price): # generate a lower target price by randomly perturbing given price ptrb_abs = self.ca * random.random() # absolute shift ptrb_rel = price * (1.0 - (self.cr * random.random())) # relative shift target = int(round(ptrb_rel - ptrb_abs, 0)) if target == price: target = price - 1 # enforce minimal difference # print('TargetDn: %d %d\n' % (price,target)) return (target) def microshade(microprice, price): # shade in the direction of the microprice microweight = 0 if microprice != None: shaded = ((microweight * microprice) + ((1 - microweight) * price)) else: shaded = price # print('Microshade: micro=%s price=%s shaded=%s' % (microprice, price, shaded)) return (shaded) def willing_to_trade(price): # am I willing to trade at this price? willing = False if self.job == 'Bid' and self.active and self.price >= price: willing = True if self.job == 'Ask' and self.active and self.price <= price: willing = True return willing def profit_alter(*argv): # this has variable number of parameters # if passed a single numeric value, that's the target price # if passed three numeric values, that's the price, beta (learning rate), and momentum if len(argv) == 1: price = argv[0] beta = self.beta momntm = self.momntm elif len(argv) == 3: price = argv[0] beta = argv[1] momntm = argv[2] else: sys.stdout.flush() sys.exit('Fail: ZIP profit_alter given wrong number of parameters') # print('profit_alter: price=%s beta=%s momntm=%s' % (price, beta, momntm)) oldprice = self.price diff = price - oldprice change = ((1.0 - self.momntm) * (self.beta * diff)) + (self.momntm * self.prev_change) self.prev_change = change newmargin = ((self.price + change) / self.limit) - 1.0 if self.job == 'Bid': margin = min(newmargin, 0) self.margin_buy = margin self.margin = margin else: margin = max(0, newmargin) self.margin_sell = margin self.margin = margin # set the price from limit and profit-margin self.price = int(round(self.limit * (1.0 + self.margin), 0)) # print('old=%d diff=%d change=%d lim=%d price = %d\n' % (oldprice, diff, change, self.limit, self.price)) if verbose and trade != None: print('respond() [ZIP] time=%s tid=%s, trade=%s LOB[bids]=%s LOB[asks]=%s' % (time, self.tid, trade, lob["bids"], lob["asks"])) # what, if anything, has happened on the bid LOB? # if trade != None: print('ZIP respond() trade=%s' % trade) bid_improved = False bid_hit = False if len(lob['bids']['lob']) > 0: lob_best_bid_p = lob['bids']['lob'][0][0] else: lob_best_bid_p = None lob_best_bid_q = None # default assumption if lob_best_bid_p != None: # non-empty bid LOB if self.prev_best_bid_p > lob_best_bid_p: best_bid_p_decreased = True else: best_bid_p_decreased = False if (self.prev_best_bid_p == lob_best_bid_p) and (self.prev_best_bid_q > lob_best_bid_q): same_p_smaller_q = True else: same_p_smaller_q = False lob_best_bid_q = lob['bids']['lob'][0][1] if self.prev_best_bid_p < lob_best_bid_p: # best bid has improved # NB doesn't check if the improvement was by self bid_improved = True elif trade != None and (best_bid_p_decreased or same_p_smaller_q): # there WAS a trade and either... # ... (best bid price has gone DOWN) or (best bid price is same but quantity at that price has gone DOWN) # then assume previous best bid was hit bid_hit = True elif self.prev_best_bid_p != None: # the bid LOB is empty now but was not previously: so was it canceled or lifted? if trade != None: # a trade has occurred and the previously nonempty ask LOB is now empty # so assume best ask was lifted bid_hit = True else: bid_hit = False if verbose: print("LOB[bids]=%s bid_improved=%s bid_hit=%s" % (lob['bids'], bid_improved, bid_hit)) # what, if anything, has happened on the ask LOB? ask_improved = False ask_lifted = False if len(lob['asks']['lob']) > 0: lob_best_ask_p = lob['asks']['lob'][0][0] else: lob_best_ask_p = None lob_best_ask_q = None if lob_best_ask_p != None: # non-empty ask LOB if self.prev_best_ask_p < lob_best_ask_p: best_ask_p_increased = True else: best_ask_p_increased = False if (self.prev_best_ask_p == lob_best_ask_p) and (self.prev_best_ask_q > lob_best_ask_q): same_p_smaller_q = True else: same_p_smaller_q = False lob_best_ask_q = lob['asks']['lob'][0][1] if self.prev_best_ask_p > lob_best_ask_p: # best ask has improved -- NB doesn't check if the improvement was by self ask_improved = True elif trade != None and (best_ask_p_increased or same_p_smaller_q): # trade happened and best ask price has got worse, or stayed same but quantity reduced -- assume previous best ask was lifted ask_lifted = True elif self.prev_best_ask_p != None: # the ask LOB is empty now but was not previously: so was it canceled or lifted? if trade != None: # a trade has occurred and the previously nonempty ask LOB is now empty # so assume best ask was lifted ask_lifted = True else: ask_lifted = False if verbose: print("LOB[asks]=%s ask_improved=%s ask_lifted=%s" % (lob['asks'], ask_improved, ask_lifted)) if verbose and (bid_improved or bid_hit or ask_improved or ask_lifted): print('ZIP respond() B_improved=%s; B_hit=%s A_improved=%s, A_lifted=%s' % ( bid_improved, bid_hit, ask_improved, ask_lifted)) print('Trade=%s\n' % trade) # we want to know: did a deal just happen? # if not, did the most recent bid deal = bid_hit or ask_lifted # previously... # when raising margin, tradeprice = trade['price'], targetprice = f(tradeprice) & # i.e. target price will be calculated relative to price of most recent transaction # and when lowering margin, targetprice = f(best_price_on_counterparty_side_of_LOB) or # or if LOB empty then targetprice = f(worst possible counterparty quote) <-- a system constant # new in this version: # take account of LOB's microprice if it is defined (if not, use trade['price'] as before) midp = lob['midprice'] microp = lob['microprice'] # KLUDGE for TESTING if time > 79: microp = 145 if microp != None and midp != None: imbalance = microp - midp else: imbalance = 0 # uses zero instead of None because a zero imbalance reverts ZIP to original form target_price = None # default assumption # print('self.job=%s' % self.job) if self.job == 'Ask': # seller if deal: if verbose: print ('trade', trade) tradeprice = trade['price'] # price of most recent transaction # print('tradeprice=%s lob[microprice]=%s' % (tradeprice, lob['microprice'])) # shadetrade = microshade(lob['microprice'], tradeprice) # refprice = shadetrade refprice = tradeprice if self.price <= tradeprice: # could sell for more? raise margin target_price = target_up(refprice) profit_alter(target_price) elif ask_lifted and self.active and not willing_to_trade(tradeprice): # previous best ask was hit, # but this trader wouldn't have got the deal cos price to high, # and still working a customer order, so reduce margin target_price = target_down(refprice) profit_alter(target_price) else: # no deal: aim for a target price higher than best bid # print('lob_best_bid_p=%s lob[microprice]=%s' % (lob_best_bid_p, lob['microprice'])) # refprice = microshade(lob['microprice'], lob_best_bid_p) refprice = lob_best_bid_p if ask_improved and self.price > lob_best_bid_p: if lob_best_bid_p != None: target_price = target_up(lob_best_bid_p) else: if self.worst_askprice != None: target_price = self.worst_askprice print('worst_askprice = %s' % self.worst_askprice) target_price = None # todo: does this stop the price-spikes? else: target_price = None # target_price = lob['asks']['worstp'] # stub quote if target_price != None: print('PA1: tp=%s' % target_price) profit_alter(target_price) if self.job == 'Bid': # buyer if deal: tradeprice = trade['price'] # shadetrade = microshade(lob['microprice'], tradeprice) # refprice = shadetrade refprice = tradeprice if lob['microprice'] != None and lob['midprice'] != None: delta = lob['microprice'] - lob['midprice'] # refprice = refprice + delta if self.price >= tradeprice: # could buy for less? raise margin (i.e. cut the price) target_price = target_down(refprice) profit_alter(target_price) elif bid_hit and self.active and not willing_to_trade(tradeprice): # wouldn't have got this deal, and still working a customer order, # so reduce margin target_price = target_up(refprice) profit_alter(target_price) else: # no deal: aim for target price lower than best ask refprice = microshade(lob['microprice'], lob_best_ask_p) if bid_improved and self.price < lob_best_ask_p: if lob_best_ask_p != None: target_price = target_down(lob_best_ask_p) else: if self.worst_bidprice != None: target_price = self.worst_bidprice target_price = None else: target_price = None # target_price = lob['bids']['worstp'] # stub quote if target_price != None: # print('PA2: tp=%s' % target_price) profit_alter(target_price) # print('time,%f,>>>,microprice,%s,>>>,target_price,%s' % (time, lob['microprice'], target_price)) # remember the best LOB data ready for next response self.prev_best_bid_p = lob_best_bid_p self.prev_best_bid_q = lob_best_bid_q self.prev_best_ask_p = lob_best_ask_p self.prev_best_ask_q = lob_best_ask_q ##########################---trader-types have all been defined now--################ ================================================ FILE: ZhenZhang/source/Simple_MLOFI.py ================================================ from BSE2_msg_classes import Assignment, Order, Exch_msg from BSE_trader_agents import Trader; import random import math bse_sys_minprice = 1 # minimum price in the system, in cents/pennies bse_sys_maxprice = 200 # maximum price in the system, in cents/pennies class Trader_Simple_MLOFI(Trader): def __init__(self, ttype, tid, balance, time): Trader.__init__(self, ttype, tid, balance, time) self.limit = None self.job = None # variable for MLOFI self.last_lob = None; self.list_OFI = []; self.list_D = []; def cal_level_n_e(self, current_lob, n): b_n = 0 r_n = 0 a_n = 0 q_n = 0 b_n_1 = 0 r_n_1 = 0 a_n_1 = 0 q_n_1 = 0 if (len(current_lob['bids']['lob']) < n): b_n = 0 r_n = 0 else: b_n = current_lob['bids']['lob'][n - 1][0] r_n = current_lob['bids']['lob'][n - 1][1] if (len(self.last_lob['bids']['lob']) < n): b_n_1 = 0 r_n_1 = 0 else: b_n_1 = self.last_lob['bids']['lob'][n - 1][0] r_n_1 = self.last_lob['bids']['lob'][n - 1][1] if (len(current_lob['asks']['lob']) < n): a_n = 0 q_n = 0 else: a_n = current_lob['asks']['lob'][n - 1][0] q_n = current_lob['asks']['lob'][n - 1][1] if (len(self.last_lob['asks']['lob']) < n): a_n_1 = 0 q_n_1 = 0 else: a_n_1 = self.last_lob['asks']['lob'][n - 1][0] q_n_1 = self.last_lob['asks']['lob'][n - 1][1] delta_w = 0; if (b_n > b_n_1): delta_w = r_n elif (b_n == b_n_1): delta_w = r_n - r_n_1 else: delta_w = -r_n_1 delta_v = 0 if (a_n > a_n_1): delta_v = -q_n_1 elif (a_n == a_n_1): delta_v = q_n - q_n_1 else: delta_v = q_n return delta_w - delta_v def cal_e(self, time, lob, trade, verbose): level_1 = self.cal_level_n_e(lob, 1) level_2 = self.cal_level_n_e(lob, 2) level_3 = self.cal_level_n_e(lob, 3) e = { 'level1': level_1, 'level2': level_2, 'level3': level_3, } # print 'ofi is:' # print str(e) self.list_OFI.append(e) def cal_depth(self, lob): level_1 = self.cal_depth_n(lob, 1) level_2 = self.cal_depth_n(lob, 2) level_3 = self.cal_depth_n(lob, 3) d = { 'level1': level_1, 'level2': level_2, 'level3': level_3, } # print 'depth is:' # print str(d) self.list_D.append(d); def cal_depth_n(self, lob, n): if (len(lob['bids']['lob']) < n): r_n = 0 else: r_n = lob['bids']['lob'][n - 1][1] if (len(lob['asks']['lob']) < n): q_n = 0 else: q_n = lob['asks']['lob'][n - 1][1] return (r_n + q_n) / 2 def getorder(self, time, countdown, lob, verbose): if len(self.orders) < 1: self.active = False return None else: self.limit = self.orders[0].price otype = self.orders[0].atype self.job = self.orders[0].atype ostyle = self.orders[0].astyle if otype == 'Bid': if lob['bids']['n'] > 0: quoteprice = lob['bids']['bestp'] if quoteprice > self.limit: quoteprice = self.limit else: quoteprice = self.limit else: if lob['asks']['n'] > 0: quoteprice = lob['asks']['bestp'] if quoteprice < self.limit: quoteprice = self.limit else: quoteprice = self.limit def imbalance_alter(quoteprice, lob): level_1_ofi_cul = 0 level_2_ofi_cul = 0 level_3_ofi_cul = 0 n = 1 while (len(self.list_OFI) >= n): level_1_ofi_cul = level_1_ofi_cul + self.list_OFI[-n]['level1'] level_2_ofi_cul = level_2_ofi_cul + self.list_OFI[-n]['level2'] level_3_ofi_cul = level_3_ofi_cul + self.list_OFI[-n]['level3'] n = n + 1 if (n >= 6): break level_1_depth_cul = 0; level_2_depth_cul = 0; level_3_depth_cul = 0; m = 1 while (len(self.list_D) >= m): level_1_depth_cul = level_1_depth_cul + self.list_D[-m]['level1'] level_2_depth_cul = level_2_depth_cul + self.list_D[-m]['level2'] level_3_depth_cul = level_3_depth_cul + self.list_D[-m]['level3'] m = m + 1 if (m >= 4): break # if(level_1_depth_cul==0): level_1_depth_cul = 10000 # if(level_2_depth_cul==0): level_2_depth_cul = 10000 # if(level_3_depth_cul==0): level_3_depth_cul = 10000 if m == 1: level_1_depth_averge = level_1_depth_cul + 1 level_2_depth_averge = level_2_depth_cul + 1 level_3_depth_averge = level_3_depth_cul + 1 else: level_1_depth_averge = level_1_depth_cul / (m - 1) + 1 level_2_depth_averge = level_2_depth_cul / (m - 1) + 1 level_3_depth_averge = level_3_depth_cul / (m - 1) + 1 c = 0.5 decay = 0.8 # print 'level_1_depth_averge is %s'%level_1_depth_averge # print 'level_2_depth_averge is %s'%level_2_depth_averge # print 'level_3_depth_averge is %s'%level_3_depth_averge offset = level_1_ofi_cul * c / level_1_depth_averge + decay * level_2_ofi_cul * c / level_2_depth_averge + decay * decay * level_3_ofi_cul * c / level_3_depth_averge # quoteprice_iaa = (quoteprice_aa+offset)*0.9 + 0.1*quoteprice_aa benchmark = quoteprice; if(lob['midprice'] != None): benchmark = lob['midprice'] # print 'midprice is %d' % benchmark # print 'benchmark = %d' % benchmark quoteprice_isimple = benchmark + offset if self.job == 'Bid' and quoteprice_isimple > self.limit: quoteprice_isimple = self.limit if self.job == 'Ask' and quoteprice_isimple < self.limit: quoteprice_isimple = self.limit # print 'IAA_MLOFI original quotaprice: %d' % (quoteprice) # print 'offset is %d'%offset # print 'level1 ofi is %d'%level_1_ofi_cul # print 'level2 ofi is %d'%level_2_ofi_cul # print 'level3 ofi is %d'%level_3_ofi_cul # print 'level1 depth is %d'%level_1_depth_averge # print 'level2 depth is %d'%level_2_depth_averge # print 'level3 depth is %d'%level_3_depth_averge # print 'offset is %d'%offset # print 'IAA_MLOFI final quotaprice: %d' % (quoteprice_isimple) # print 'IAAB_MLOFI JOB IS %s' % self.job return quoteprice_isimple quoteprice_isimple = imbalance_alter(quoteprice, lob) order = Order(self.tid, self.orders[0].atype, 'LIM', quoteprice_isimple, self.orders[0].qty, time, None, -1) self.lastquote = order return order def respond(self, time, lob, trade, verbose): ## End nicked from ZIP if (self.last_lob == None): self.last_lob = lob else: # print '' # print '' # print 'pre lob' # print 'bid anon:' # print self.last_lob['bids']['lob'] # print 'ask anon:' # print self.last_lob['asks']['lob'] # print 'current lob' # print 'bid anon:' # print lob['bids']['lob'] # print 'ask anon:' # print lob['asks']['lob'] self.cal_e(time, lob, trade, verbose) self.cal_depth(lob); self.last_lob = lob; ================================================ FILE: ZhenZhang/source/ZZISHV.py ================================================ from BSE2_msg_classes import Assignment, Order, Exch_msg from BSE_trader_agents import Trader; import random import math bse_sys_minprice = 1 # minimum price in the system, in cents/pennies bse_sys_maxprice = 200 # maximum price in the system, in cents/pennies class Trader_ZZISHV(Trader): def __init__(self, ttype, tid, balance, time,m): Trader.__init__(self, ttype, tid, balance, time) self.limit = None self.job = None # variable for MLOFI self.last_lob = None; self.es_list = []; self.ds_list = []; #variable for ratio self.bids_volume_list = [] self.asks_volume_list = [] # m self.m = m; def is_imbalance_significant(self, m,threshold): cb_list = [0 for i in range(m)] ab_list = [] ca_list = [0 for i in range(m)] aa_list = [] n = 1 while len(self.bids_volume_list) >= n and len(self.asks_volume_list) >= n: for i in range(m): cb_list[i] += self.bids_volume_list[-n]['level' + str(i + 1)] ca_list[i] += self.asks_volume_list[-n]['level' + str(i + 1)] n += 1 if n >= 11: break for i in range(m): temp1 = None temp2 = None if n == 1: temp1 = cb_list[i] + 1 temp2 = ca_list[i] + 1 else: temp1 = cb_list[i] / (n - 1) + 1 temp2 = ca_list[i] / (n - 1) + 1 ab_list.append(temp1) aa_list.append(temp2) v_bid = 0; v_ask = 0; for i in range(m): v_bid += math.exp(-0.5*i)*ab_list[i]; v_ask += math.exp(-0.5*i)*aa_list[i]; ratio = (v_bid-v_ask)/(v_bid+v_ask); # print self.bids_volume_list # print self.asks_volume_list # print ratio if(ratio>threshold or ratio<-threshold): return True else: return False def calc_bids_volume(self, lob, m, verbose): new_b = {} for i in range(1, m + 1): new_b['level' + str(i)] = self.cal_bids_n(lob, i) self.bids_volume_list.append(new_b) def cal_bids_n(self, lob, n): if (len(lob['bids']['lob']) < n): r_n = 0 else: r_n = lob['bids']['lob'][n - 1][1] return r_n def calc_asks_volume(self, lob, m, verbose): new_a = {} for i in range(1, m + 1): new_a['level' + str(i)] = self.cal_asks_n(lob, i); self.asks_volume_list.append(new_a) def cal_asks_n(self, lob, n): if (len(lob['asks']['lob']) < n): q_n = 0 else: q_n = lob['asks']['lob'][n - 1][1] return q_n def calc_level_n_e(self, current_lob, n): b_n = 0 r_n = 0 a_n = 0 q_n = 0 b_n_1 = 0 r_n_1 = 0 a_n_1 = 0 q_n_1 = 0 if (len(current_lob['bids']['lob']) < n): b_n = 0 r_n = 0 else: b_n = current_lob['bids']['lob'][n - 1][0] r_n = current_lob['bids']['lob'][n - 1][1] if (len(self.last_lob['bids']['lob']) < n): b_n_1 = 0 r_n_1 = 0 else: b_n_1 = self.last_lob['bids']['lob'][n - 1][0] r_n_1 = self.last_lob['bids']['lob'][n - 1][1] if (len(current_lob['asks']['lob']) < n): a_n = 0 q_n = 0 else: a_n = current_lob['asks']['lob'][n - 1][0] q_n = current_lob['asks']['lob'][n - 1][1] if (len(self.last_lob['asks']['lob']) < n): a_n_1 = 0 q_n_1 = 0 else: a_n_1 = self.last_lob['asks']['lob'][n - 1][0] q_n_1 = self.last_lob['asks']['lob'][n - 1][1] delta_w = 0; if (b_n > b_n_1): delta_w = r_n elif (b_n == b_n_1): delta_w = r_n - r_n_1 else: delta_w = -r_n_1 delta_v = 0 if (a_n > a_n_1): delta_v = -q_n_1 elif (a_n == a_n_1): delta_v = q_n - q_n_1 else: delta_v = q_n return delta_w - delta_v def calc_es(self, lob, m, verbose): new_e = {} for i in range(1, m + 1): new_e['level' + str(i)] = self.calc_level_n_e(lob, i) self.es_list.append(new_e) def calc_ds(self, lob, m, verbose): new_d = {} for i in range(1, m + 1): new_d['level' + str(i)] = self.cal_depth_n(lob, i) self.ds_list.append(new_d) def cal_depth_n(self, lob, n): if (len(lob['bids']['lob']) < n): r_n = 0 else: r_n = lob['bids']['lob'][n - 1][1] if (len(lob['asks']['lob']) < n): q_n = 0 else: q_n = lob['asks']['lob'][n - 1][1] return (r_n + q_n) / 2 def respond(self, time, lob, trade, verbose): if (self.last_lob == None): self.last_lob = lob else: self.calc_es(lob, self.m, verbose) self.calc_ds(lob, self.m, verbose) self.calc_bids_volume(lob, self.m, verbose) self.calc_asks_volume(lob, self.m, verbose) self.last_lob = lob def getorder(self, time, countdown, lob, verbose): if verbose: print("ISHV getorder:") shave_c = 2 # c in the y=mx+c linear mapping from imbalance to shave amount shave_m = 1 # m in the y=mx+c if len(self.orders) < 1: order = None else: if verbose: print(" self.orders[0]=%s" % str(self.orders[0])) self.limit = self.orders[0].price self.job = self.orders[0].atype otype = self.orders[0].atype ostyle = self.orders[0].astyle microp = lob['microprice'] midp = lob['midprice'] if otype == 'Bid': if len(lob['bids']['lob']) > 0: quoteprice = lob['bids']['bestp'] if quoteprice > self.limit : quoteprice = self.limit else: quoteprice = 1 # KLUDGE -- come back to fix todo else: if len(lob['asks']['lob']) > 0: quoteprice = lob['asks']['bestp'] if quoteprice < self.limit: quoteprice = self.limit else: quoteprice = 200 # KLUDGE -- come back to fix todo def imbalance_alter(quoteprice_aa, lob, countdown, m): mlofi_list = [0 for i in range(m)] cd_list = [0 for i in range(m)] ad_list = [] n = 1 while len(self.es_list) >= n: for i in range(m): mlofi_list[i] += self.es_list[-n]['level' + str(i+1)] n += 1 if n >= 11: break n = 1 while len(self.ds_list) >= n: for i in range(m): cd_list[i] += self.ds_list[-n]['level' + str(i+1)] n += 1 if n >= 11: break for i in range(m): temp = None if n == 1: temp = cd_list[i]+1 else: temp = cd_list[i]/(n-1)+1 ad_list.append(temp) c = 5 decay = 0.8 offset = 0 for i in range(m): offset += int(mlofi_list[i]*c*pow(decay,i)/ ad_list[i]) benchmark = quoteprice_aa; if(lob['midprice'] != None): benchmark = lob['midprice'] # print 'midprice is %d' % benchmark quoteprice_iaa = quoteprice_aa + 0.8 * (benchmark + offset - quoteprice_aa) if self.job == 'Bid' and quoteprice_iaa > self.limit: quoteprice_iaa = self.limit if self.job == 'Ask' and quoteprice_iaa < self.limit: quoteprice_iaa = self.limit if countdown < 0.3 : print "insert" if self.job == 'Bid' and (len(lob['asks']['lob']) >= 1) and lob['asks']['lob'][0][0] < self.limit: quoteprice_iaa = lob['asks']['lob'][0][0] if self.job == 'Ask' and (len(lob['bids']['lob']) >= 1) and lob['bids']['lob'][0][0] > self.limit: quoteprice_iaa = lob['bids']['lob'][0][0] return quoteprice_iaa if(self.is_imbalance_significant(self.m,0.6)): # print "abvious" quoteprice_iaa = imbalance_alter(quoteprice, lob, countdown, self.m) else: # print "not abvious" quoteprice_iaa = quoteprice order = Order(self.tid, otype, ostyle, quoteprice_iaa, self.orders[0].qty, time, None, verbose) self.lastquote = order return order ================================================ FILE: ZhenZhang/source/dataAnalysis/box_analysis.py ================================================ import numpy as np import matplotlib.pyplot as plt import csv from pylab import * # Fixing random state for reproducibility np.random.seed(19680801) csv_file = open("../Mybalances.csv","r") csv_reader = csv.reader(csv_file); y1 = [] y2 = [] y3 = [] y4 = [] name1 = None name2 = None name3 = None name4 = None cy1 = 0; cy2 = 0; cy3 = 0; cy4 = 0; count = 0 for item in csv_reader: y1.append(int(float(item[5]))) # y2.append(int(float(item[13]))) # cy1 += int(float(item[5])) # cy2 += int(float(item[13])) y3.append(int(float(item[17]))) #y4.append(int(float(item[21]))) # y3.append(int(float(item[13]))) # y4.append(int(float(item[17]))) # name1 = item[2] # name2 = item[6] # name3 = item[10] # name4 = item[14] # print '%s,%s'%(item[5],item[9]) count += 1 fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(10, 8)) data = [y1,y3] names = ['AA','IAA'] ax1.set_title('(a) The daily profits for different agents') green_diamond = dict(markerfacecolor='g', marker='D') dp1= ax1.boxplot(data,vert=True,whis=0.75,notch=False, labels=names,showmeans=True,meanline=False,meanprops=green_diamond) x1,y = dp1['medians'][0].get_xydata()[1] ax1.text(x1+0.075, y, '%.1f' % y,horizontalalignment='left') x2,y = dp1['medians'][1].get_xydata()[1] ax1.text(x2+0.075, y, '%.1f' % y,horizontalalignment='left') # for line in dp1['medians']: # # get position data for median line # x, y = line.get_xydata()[1] # top of median line # # overlay median value # print x # print y # ax1.text(x+0.125, y, '%.1f' % y,horizontalalignment='left') for index in range(len(dp1['means'])): y = dp1['means'][index].get_ydata()[0] if index==0: ax1.text(x1 + 0.075, y, '%.1f' % y, horizontalalignment='left') elif index ==1: ax1.text(x2 + 0.075, y, '%.1f' % y, horizontalalignment='left') for index in range(len(dp1['boxes'])): y = dp1['boxes'][index].get_ydata()[0] if index==0: ax1.text(x1 + 0.075, y, '%.1f' % y, horizontalalignment='left') elif index ==1: ax1.text(x2 + 0.075, y, '%.1f' % y, horizontalalignment='left') y = dp1['boxes'][index].get_ydata()[2] if index==0: ax1.text(x1 + 0.075, y, '%.1f' % y, horizontalalignment='left') # elif index ==1: # ax1.text(x2 + 0.075, y, '%.1f' % y, horizontalalignment='left') # for index in range(len(dp1['boxes'])): # # y = dp1['boxes'][index].get_ydata()[0] # # if index==0: # ax1.text(x1 + 0.075, y, '%.1f' % y, horizontalalignment='left') # elif index ==1: # ax1.text(x2 + 0.075, y, '%.1f' % y, horizontalalignment='left') # # y = dp1['boxes'][index].get_ydata()[2] # # if index==0: # ax1.text(x1 + 0.075, y, '%.1f' % y, horizontalalignment='left') # elif index ==1: # ax1.text(x2 + 0.075, y, '%.1f' % y, horizontalalignment='left') for index in range(len(dp1['caps'])): print 'one round' print index y = dp1['caps'][index].get_ydata()[0] print 'bottom' print y if index==0: ax1.text(x1 + 0.075, y, '%.1f' % y, horizontalalignment='left') elif index ==1: ax1.text(x1 + 0.075, y, '%.1f' % y, horizontalalignment='left') elif index ==2: ax1.text(x2 + 0.075, y, '%.1f' % y, horizontalalignment='left') elif index ==3: ax1.text(x2 + 0.075, y, '%.1f' % y, horizontalalignment='left') # y = dp1['caps'][index].get_ydata()[1] # print 'top' # print y # if index==0: # ax1.text(x1 + 0.075, y, '%.1f' % y, horizontalalignment='left') # elif index ==1: # ax1.text(x2 + 0.075, y, '%.1f' % y, horizontalalignment='left') # for line in dp1['means']: # # get position data for median line # x, y = line.get_ydata()[0] # top of median line # # overlay median value # print x # print y # ax1.text(x+0.125, y, '%.1f' % y,horizontalalignment='left') # # print 'boxes' # # for line in dp1['boxes']: # # x, y = line.get_xydata()[0] # bottom of left line # ax1.text(x+0.125,y, '%.1f' % y,horizontalalignment='right') # below # x, y = line.get_xydata()[2] # bottom of right line # ax1.text(x+0.125,y, '%.1f' % y,horizontalalignment='right') # below dif1 = [] for i in range(len(y1)): dif1.append(y3[i]-y1[i]) ax2.set_title('(b) The profit differences for different agents') dp2 = ax2.boxplot(dif1,vert=True,whis=0.75,notch=False, showmeans=True,labels=['IAA-AA'],meanprops=green_diamond) x1,y = dp2['medians'][0].get_xydata()[1] ax2.text(x1+0.075, y, '%.1f' % y,horizontalalignment='left') # x2,y = dp1['medians'][1].get_xydata()[1] # ax1.text(x2+0.075, y, '%.1f' % y,horizontalalignment='left') # for line in dp1['medians']: # # get position data for median line # x, y = line.get_xydata()[1] # top of median line # # overlay median value # print x # print y # ax1.text(x+0.125, y, '%.1f' % y,horizontalalignment='left') for index in range(len(dp2['means'])): y = dp2['means'][index].get_ydata()[0] if index==0: ax2.text(x1 + 0.075, y, '%.1f' % y, horizontalalignment='left') elif index ==1: ax2.text(x2 + 0.075, y, '%.1f' % y, horizontalalignment='left') for index in range(len(dp2['boxes'])): y = dp2['boxes'][index].get_ydata()[0] if index==0: ax2.text(x1 + 0.075, y, '%.1f' % y, horizontalalignment='left') elif index ==1: ax2.text(x2 + 0.075, y, '%.1f' % y, horizontalalignment='left') y = dp2['boxes'][index].get_ydata()[2] if index==0: ax2.text(x1 + 0.075, y, '%.1f' % y, horizontalalignment='left') elif index ==1: ax2.text(x2 + 0.075, y, '%.1f' % y, horizontalalignment='left') for index in range(len(dp2['boxes'])): y = dp2['boxes'][index].get_ydata()[0] if index==0: ax2.text(x1 + 0.075, y, '%.1f' % y, horizontalalignment='left') elif index ==1: ax2.text(x2 + 0.075, y, '%.1f' % y, horizontalalignment='left') y = dp2['boxes'][index].get_ydata()[2] if index==0: ax2.text(x1 + 0.075, y, '%.1f' % y, horizontalalignment='left') elif index ==1: ax2.text(x2 + 0.075, y, '%.1f' % y, horizontalalignment='left') for index in range(len(dp2['caps'])): print 'one round' print index y = dp2['caps'][index].get_ydata()[0] print 'bottom' print y if index==0: ax2.text(x1 + 0.075, y, '%.1f' % y, horizontalalignment='left') elif index ==1: ax2.text(x1 + 0.075, y, '%.1f' % y, horizontalalignment='left') elif index ==2: ax2.text(x2 + 0.075, y, '%.1f' % y, horizontalalignment='left') elif index ==3: ax2.text(x2 + 0.075, y, '%.1f' % y, horizontalalignment='left') plt.savefig("./box1.png") # # fake up some data # spread = np.random.rand(50) * 100 # center = np.ones(25) * 50 # flier_high = np.random.rand(10) * 100 + 100 # flier_low = np.random.rand(10) * -100 # data = np.concatenate((spread, center, flier_high, flier_low)) # # fig1, ax1 = plt.subplots() # ax1.set_title('Basic Plot') # ax1.boxplot(data,vert=False) # # # spread = np.random.rand(50) * 100 # center = np.ones(25) * 40 # flier_high = np.random.rand(10) * 100 + 100 # flier_low = np.random.rand(10) * -100 # d2 = np.concatenate((spread, center, flier_high, flier_low)) # data.shape = (-1, 1) # d2.shape = (-1, 1) # # data = [data, d2, d2[::2,0]] # fig7, ax7 = plt.subplots() # ax7.set_title('Multiple Samples with Different sizes') # ax7.boxplot(data,vert=False) # # plt.show() ================================================ FILE: ZhenZhang/source/dataAnalysis/hypothesisTest.py ================================================ import scipy.stats as stats import csv csv_file = open("../Mybalances.csv","r") csv_reader = csv.reader(csv_file); y1 = [] y2 = [] y3 = [] y4 = [] name1 = None name2 = None name3 = None name4 = None count = 0 for item in csv_reader: y1.append(float(item[5])) y2.append(float(item[17])) # y3.append(int(float(item[13]))) # y4.append(int(float(item[17]))) name1 = item[2] name2 = item[14] # name3 = item[10] # name4 = item[14] # print '%s,%s'%(item[5],item[9]) count += 1 u_statistic, pVal = stats.mannwhitneyu(y1, y2,alternative='less') print "u_statistic is %f"%u_statistic; print "p value is %f"%pVal; import numpy as np #create 95% confidence interval for population mean weight print np.mean(y2) print stats.t.interval(alpha=0.95, df=len(y2)-1, loc=np.mean(y2), scale=stats.sem(y2))[1] - np.mean(y2) print "" print np.mean(y1) print stats.t.interval(alpha=0.95, df=len(y1)-1, loc=np.mean(y1), scale=stats.sem(y1))[1] - np.mean(y1) print "" df = [] for index in range(len(y1)): df.append(y2[index]-y1[index]) print np.mean(df) print stats.t.interval(alpha=0.95, df=len(df)-1, loc=np.mean(df), scale=stats.sem(df))[1] - np.mean(df) ================================================ FILE: ZhenZhang/source/dataAnalysis/matplotlib4.py ================================================ from matplotlib import pyplot as plot import csv import random csv_file = open("../Mybalances.csv","r") csv_reader = csv.reader(csv_file); y1 = [] y2 = [] y3 = [] y4 = [] name1 = None name2 = None name3 = None name4 = None cy1 = 0; cy2 = 0; count = 0 for item in csv_reader: y1.append(int(float(item[5]))) y2.append(int(float(item[17]))) cy1 += int(float(item[5])) cy2 += int(float(item[17])) # y3.append(int(float(item[13]))) # y4.append(int(float(item[17]))) name1 = item[2] name2 = item[14] # name3 = item[10] # name4 = item[14] # print '%s,%s'%(item[5],item[9]) count += 1 x = range(1,count+1) fig, ax = plot.subplots() cost1 = cy1/count cost1_list = [cost1 for i in range(1,count+1)] cost2 = cy2/count cost2_list = [cost2 for i in range(1,count+1)] ax.plot(x,y1,label="AA") ax.plot(x,y2,label="IAA") ax.plot(x,cost1_list,linestyle="dashed", label= "AA's average profit = "+str(float((cy1+0.0)/count))) ax.plot(x,cost2_list,linestyle="dashed", label= "IAA's average profit = "+str(float((cy2+0.0)/count))) # ax.plot(x,y3,label=name3) # ax.plot(x,y4,label=name4) # xticks_label = [i*5 for i in range(1,21)] # plot.xticks( xticks_label) # yticks_label = [i*5 for i in range(10,30)] # plot.yticks( yticks_label) ax.set_xlabel('trading day') ax.set_ylabel('total profit in each trading day') ax.legend() plot.savefig("./balance4.png") ================================================ FILE: clean.sh ================================================ rm *.csv ================================================ FILE: snashall2019.py ================================================ # -*- coding: utf-8 -*- # # BSE: The Bristol Stock Exchange # # Version 1.3; July 21st, 2018. # Version 1.2; November 17th, 2012. # # Copyright (c) 2012-2018, Dave Cliff # # # ------------------------ # # MIT Open-Source License: # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and # associated documentation files (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all copies or substantial # portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT # LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # ------------------------ # # # # BSE is a very simple simulation of automated execution traders # operating on a very simple model of a limit order book (LOB) exchange # # major simplifications in this version: # (a) only one financial instrument being traded # (b) traders can only trade contracts of size 1 (will add variable quantities later) # (c) each trader can have max of one order per single orderbook. # (d) traders can replace/overwrite earlier orders, and/or can cancel # (d) simply processes each order in sequence and republishes LOB to all traders # => no issues with exchange processing latency/delays or simultaneously issued orders. # # NB this code has been written to be readable/intelligible, not efficient! # could import pylab here for graphing etc import sys import math import random bse_sys_minprice = 1 # minimum price in the system, in cents/pennies bse_sys_maxprice = 1000 # maximum price in the system, in cents/pennies ticksize = 1 # minimum change in price, in cents/pennies # an Order/quote has a trader id, a type (buy/sell) price, quantity, timestamp, and unique i.d. class Order: def __init__(self, tid, otype, price, qty, time, qid): self.tid = tid # trader i.d. self.otype = otype # order type self.price = price # price self.qty = qty # quantity self.time = time # timestamp self.qid = qid # quote i.d. (unique to each quote) def __str__(self): return '[%s %s P=%03d Q=%s T=%5.2f QID:%d]' % \ (self.tid, self.otype, self.price, self.qty, self.time, self.qid) # Orderbook_half is one side of the book: a list of bids or a list of asks, each sorted best-first class Orderbook_half: def __init__(self, booktype, worstprice): # booktype: bids or asks? self.booktype = booktype # dictionary of orders received, indexed by Trader ID self.orders = {} # limit order book, dictionary indexed by price, with order info self.lob = {} # anonymized LOB, lists, with only price/qty info self.lob_anon = [] # summary stats self.best_price = None self.best_tid = None self.worstprice = worstprice self.n_orders = 0 # how many orders? self.lob_depth = 0 # how many different prices on lob? def anonymize_lob(self): # anonymize a lob, strip out order details, format as a sorted list # NB for asks, the sorting should be reversed self.lob_anon = [] for price in sorted(self.lob): qty = self.lob[price][0] self.lob_anon.append([price, qty]) def build_lob(self): lob_verbose = False # take a list of orders and build a limit-order-book (lob) from it # NB the exchange needs to know arrival times and trader-id associated with each order # returns lob as a dictionary (i.e., unsorted) # also builds anonymized version (just price/quantity, sorted, as a list) for publishing to traders self.lob = {} for tid in self.orders: order = self.orders.get(tid) price = order.price if price in self.lob: # update existing entry qty = self.lob[price][0] orderlist = self.lob[price][1] orderlist.append([order.time, order.qty, order.tid, order.qid]) self.lob[price] = [qty + order.qty, orderlist] else: # create a new dictionary entry self.lob[price] = [order.qty, [[order.time, order.qty, order.tid, order.qid]]] # create anonymized version self.anonymize_lob() # record best price and associated trader-id if len(self.lob) > 0 : if self.booktype == 'Bid': self.best_price = self.lob_anon[-1][0] else : self.best_price = self.lob_anon[0][0] self.best_tid = self.lob[self.best_price][1][0][2] else : self.best_price = None self.best_tid = None if lob_verbose : print self.lob def book_add(self, order): # add order to the dictionary holding the list of orders # either overwrites old order from this trader # or dynamically creates new entry in the dictionary # so, max of one order per trader per list # checks whether length or order list has changed, to distinguish addition/overwrite #print('book_add > %s %s' % (order, self.orders)) n_orders = self.n_orders self.orders[order.tid] = order self.n_orders = len(self.orders) self.build_lob() #print('book_add < %s %s' % (order, self.orders)) if n_orders != self.n_orders : return('Addition') else: return('Overwrite') def book_del(self, order): # delete order from the dictionary holding the orders # assumes max of one order per trader per list # checks that the Trader ID does actually exist in the dict before deletion # print('book_del %s',self.orders) if self.orders.get(order.tid) != None : del(self.orders[order.tid]) self.n_orders = len(self.orders) self.build_lob() # print('book_del %s', self.orders) def delete_best(self): # delete order: when the best bid/ask has been hit, delete it from the book # the TraderID of the deleted order is return-value, as counterparty to the trade best_price_orders = self.lob[self.best_price] best_price_qty = best_price_orders[0] best_price_counterparty = best_price_orders[1][0][2] if best_price_qty == 1: # here the order deletes the best price del(self.lob[self.best_price]) del(self.orders[best_price_counterparty]) self.n_orders = self.n_orders - 1 if self.n_orders > 0: if self.booktype == 'Bid': self.best_price = max(self.lob.keys()) else: self.best_price = min(self.lob.keys()) self.lob_depth = len(self.lob.keys()) else: self.best_price = self.worstprice self.lob_depth = 0 else: # best_bid_qty>1 so the order decrements the quantity of the best bid # update the lob with the decremented order data self.lob[self.best_price] = [best_price_qty - 1, best_price_orders[1][1:]] # update the bid list: counterparty's bid has been deleted del(self.orders[best_price_counterparty]) self.n_orders = self.n_orders - 1 self.build_lob() return best_price_counterparty # Orderbook for a single instrument: list of bids and list of asks class Orderbook(Orderbook_half): def __init__(self): self.bids = Orderbook_half('Bid', bse_sys_minprice) self.asks = Orderbook_half('Ask', bse_sys_maxprice) self.tape = [] self.quote_id = 0 #unique ID code for each quote accepted onto the book # Exchange's internal orderbook class Exchange(Orderbook): def add_order(self, order, verbose): # add a quote/order to the exchange and update all internal records; return unique i.d. order.qid = self.quote_id self.quote_id = order.qid + 1 # if verbose : print('QUID: order.quid=%d self.quote.id=%d' % (order.qid, self.quote_id)) tid = order.tid if order.otype == 'Bid': response=self.bids.book_add(order) best_price = self.bids.lob_anon[-1][0] self.bids.best_price = best_price self.bids.best_tid = self.bids.lob[best_price][1][0][2] else: response=self.asks.book_add(order) best_price = self.asks.lob_anon[0][0] self.asks.best_price = best_price self.asks.best_tid = self.asks.lob[best_price][1][0][2] return [order.qid, response] def del_order(self, time, order, verbose): # delete a trader's quot/order from the exchange, update all internal records tid = order.tid if order.otype == 'Bid': self.bids.book_del(order) if self.bids.n_orders > 0 : best_price = self.bids.lob_anon[-1][0] self.bids.best_price = best_price self.bids.best_tid = self.bids.lob[best_price][1][0][2] else: # this side of book is empty self.bids.best_price = None self.bids.best_tid = None cancel_record = { 'type': 'Cancel', 'time': time, 'order': order } self.tape.append(cancel_record) elif order.otype == 'Ask': self.asks.book_del(order) if self.asks.n_orders > 0 : best_price = self.asks.lob_anon[0][0] self.asks.best_price = best_price self.asks.best_tid = self.asks.lob[best_price][1][0][2] else: # this side of book is empty self.asks.best_price = None self.asks.best_tid = None cancel_record = { 'type': 'Cancel', 'time': time, 'order': order } self.tape.append(cancel_record) else: # neither bid nor ask? sys.exit('bad order type in del_quote()') def process_order2(self, time, order, verbose): # receive an order and either add it to the relevant LOB (ie treat as limit order) # or if it crosses the best counterparty offer, execute it (treat as a market order) oprice = order.price counterparty = None [qid, response] = self.add_order(order, verbose) # add it to the order lists -- overwriting any previous order order.qid = qid if verbose : print('QUID: order.quid=%d' % order.qid) print('RESPONSE: %s' % response) best_ask = self.asks.best_price best_ask_tid = self.asks.best_tid best_bid = self.bids.best_price best_bid_tid = self.bids.best_tid if order.otype == 'Bid': if self.asks.n_orders > 0 and best_bid >= best_ask: # bid lifts the best ask if verbose: print("Bid $%s lifts best ask" % oprice) counterparty = best_ask_tid price = best_ask # bid crossed ask, so use ask price if verbose: print('counterparty, price', counterparty, price) # delete the ask just crossed self.asks.delete_best() # delete the bid that was the latest order self.bids.delete_best() elif order.otype == 'Ask': if self.bids.n_orders > 0 and best_ask <= best_bid: # ask hits the best bid if verbose: print("Ask $%s hits best bid" % oprice) # remove the best bid counterparty = best_bid_tid price = best_bid # ask crossed bid, so use bid price if verbose: print('counterparty, price', counterparty, price) # delete the bid just crossed, from the exchange's records self.bids.delete_best() # delete the ask that was the latest order, from the exchange's records self.asks.delete_best() else: # we should never get here sys.exit('process_order() given neither Bid nor Ask') # NB at this point we have deleted the order from the exchange's records # but the two traders concerned still have to be notified if verbose: print('counterparty %s' % counterparty) if counterparty != None: # process the trade if verbose: print('>>>>>>>>>>>>>>>>>TRADE t=%5.2f $%d %s %s' % (time, price, counterparty, order.tid)) transaction_record = { 'type': 'Trade', 'time': time, 'price': price, 'party1':counterparty, 'party2':order.tid, 'qty': order.qty } self.tape.append(transaction_record) return transaction_record else: return None def tape_dump(self, fname, fmode, tmode): dumpfile = open(fname, fmode) for tapeitem in self.tape: if tapeitem['type'] == 'Trade' : dumpfile.write('%s, %s\n' % (tapeitem['time'], tapeitem['price'])) dumpfile.close() if tmode == 'wipe': self.tape = [] # this returns the LOB data "published" by the exchange, # i.e., what is accessible to the traders def publish_lob(self, time, verbose): public_data = {} public_data['time'] = time public_data['bids'] = {'best':self.bids.best_price, 'worst':self.bids.worstprice, 'n': self.bids.n_orders, 'lob':self.bids.lob_anon} public_data['asks'] = {'best':self.asks.best_price, 'worst':self.asks.worstprice, 'n': self.asks.n_orders, 'lob':self.asks.lob_anon} public_data['QID'] = self.quote_id public_data['tape'] = self.tape if verbose: print('publish_lob: t=%d' % time) print('BID_lob=%s' % public_data['bids']['lob']) # print('best=%s; worst=%s; n=%s ' % (self.bids.best_price, self.bids.worstprice, self.bids.n_orders)) print('ASK_lob=%s' % public_data['asks']['lob']) # print('qid=%d' % self.quote_id) return public_data ##################--Traders below here--############# # Trader superclass # all Traders have a trader id, bank balance, blotter, and list of orders to execute class Trader: def __init__(self, ttype, tid, balance, time): self.ttype = ttype # what type / strategy this trader is self.tid = tid # trader unique ID code self.balance = balance # money in the bank self.blotter = [] # record of trades executed self.orders = [] # customer orders currently being worked (fixed at 1) self.n_quotes = 0 # number of quotes live on LOB self.willing = 1 # used in ZIP etc self.able = 1 # used in ZIP etc self.birthtime = time # used when calculating age of a trader/strategy self.profitpertime = 0 # profit per unit time self.n_trades = 0 # how many trades has this trader done? self.lastquote = None # record of what its last quote was def __str__(self): return '[TID %s type %s balance %s blotter %s orders %s n_trades %s profitpertime %s]' \ % (self.tid, self.ttype, self.balance, self.blotter, self.orders, self.n_trades, self.profitpertime) def add_order(self, order, verbose): # in this version, trader has at most one order, # if allow more than one, this needs to be self.orders.append(order) if self.n_quotes > 0 : # this trader has a live quote on the LOB, from a previous customer order # need response to signal cancellation/withdrawal of that quote response = 'LOB_Cancel' else: response = 'Proceed' self.orders = [order] if verbose : print('add_order < response=%s' % response) return response def del_order(self, order): # this is lazy: assumes each trader has only one customer order with quantity=1, so deleting sole order # CHANGE TO DELETE THE HEAD OF THE LIST AND KEEP THE TAIL self.orders = [] def bookkeep(self, trade, order, verbose, time): outstr="" for order in self.orders: outstr = outstr + str(order) self.blotter.append(trade) # add trade record to trader's blotter # NB What follows is **LAZY** -- assumes all orders are quantity=1 transactionprice = trade['price'] if self.orders[0].otype == 'Bid': profit = self.orders[0].price - transactionprice else: profit = transactionprice - self.orders[0].price self.balance += profit self.n_trades += 1 self.profitpertime = self.balance/(time - self.birthtime) if profit < 0 : print profit print trade print order sys.exit() if verbose: print('%s profit=%d balance=%d profit/time=%d' % (outstr, profit, self.balance, self.profitpertime)) self.del_order(order) # delete the order # specify how trader responds to events in the market # this is a null action, expect it to be overloaded by specific algos def respond(self, time, lob, trade, verbose): return None # specify how trader mutates its parameter values # this is a null action, expect it to be overloaded by specific algos def mutate(self, time, lob, trade, verbose): return None # Trader subclass Giveaway # even dumber than a ZI-U: just give the deal away # (but never makes a loss) class Trader_Giveaway(Trader): def getorder(self, time, countdown, lob): if len(self.orders) < 1: order = None else: quoteprice = self.orders[0].price order = Order(self.tid, self.orders[0].otype, quoteprice, self.orders[0].qty, time, lob['QID']) self.lastquote=order return order # Trader subclass AA class Trader_AA(Trader): def __init__(self, ttype, tid, balance, time): # Stuff about trader self.ttype = ttype self.tid = tid self.balance = balance self.birthtime = time self.profitpertime = 0 self.n_trades = 0 self.blotter = [] self.orders = [] self.n_quotes = 0 self.lastquote = None self.limit = None self.job = None # learning variables self.r_shout_change_relative = 0.05 self.r_shout_change_absolute = 0.05 self.short_term_learning_rate = random.uniform(0.1, 0.5) self.long_term_learning_rate = random.uniform(0.1, 0.5) self.moving_average_weight_decay = 0.95 # how fast weight decays with time, lower is quicker, 0.9 in vytelingum self.moving_average_window_size = 5 self.offer_change_rate = 3.0 self.theta = -2.0 self.theta_max = 2.0 self.theta_min = -8.0 self.marketMax = bse_sys_maxprice # Variables to describe the market self.previous_transactions = [] self.moving_average_weights = [] for i in range(self.moving_average_window_size): self.moving_average_weights.append(self.moving_average_weight_decay**i) self.estimated_equilibrium = [] self.smiths_alpha = [] self.prev_best_bid_p = None self.prev_best_bid_q = None self.prev_best_ask_p = None self.prev_best_ask_q = None # Trading Variables self.r_shout = None self.buy_target = None self.sell_target = None self.buy_r = -1.0 * (0.3 * random.random()) self.sell_r = -1.0 * (0.3 * random.random()) def calcEq(self): # Slightly modified from paper, it is unclear inpaper # N previous transactions * weights / N in vytelingum, swap N denominator for sum of weights to be correct? if len(self.previous_transactions) == 0: return elif len(self.previous_transactions) < self.moving_average_window_size: # Not enough transactions self.estimated_equilibrium.append(float(sum(self.previous_transactions)) / max(len(self.previous_transactions), 1)) else: N_previous_transactions = self.previous_transactions[-self.moving_average_window_size:] thing = [N_previous_transactions[i]*self.moving_average_weights[i] for i in range(self.moving_average_window_size)] eq = sum( thing ) / sum(self.moving_average_weights) self.estimated_equilibrium.append(eq) def calcAlpha(self): alpha = 0.0 for p in self.estimated_equilibrium: alpha += (p - self.estimated_equilibrium[-1])**2 alpha = math.sqrt(alpha/len(self.estimated_equilibrium)) self.smiths_alpha.append( alpha/self.estimated_equilibrium[-1] ) def calcTheta(self): gamma = 2.0 #not sensitive apparently so choose to be whatever # necessary for intialisation, div by 0 if min(self.smiths_alpha) == max(self.smiths_alpha): alpha_range = 0.4 #starting value i guess else: alpha_range = (self.smiths_alpha[-1] - min(self.smiths_alpha)) / (max(self.smiths_alpha) - min(self.smiths_alpha)) theta_range = self.theta_max - self.theta_min desired_theta = self.theta_min + (theta_range) * (1 - (alpha_range * math.exp(gamma * (alpha_range - 1)))) self.theta = self.theta + self.long_term_learning_rate * (desired_theta - self.theta) def calcRshout(self): p = self.estimated_equilibrium[-1] l = self.limit theta = self.theta if self.job == 'Bid': # Currently a buyer if l <= p: #extramarginal! self.r_shout = 0.0 else: #intramarginal :( if self.buy_target > self.estimated_equilibrium[-1]: #r[0,1] self.r_shout = math.log(((self.buy_target - p) * (math.exp(theta) - 1) / (l - p)) + 1) / theta else: #r[-1,0] self.r_shout = math.log((1 - (self.buy_target/p)) * (math.exp(theta) - 1) + 1) / theta if self.job == 'Ask': # Currently a seller if l >= p: #extramarginal! self.r_shout = 0 else: #intramarginal :( if self.sell_target > self.estimated_equilibrium[-1]: # r[-1,0] self.r_shout = math.log((self.sell_target - p) * (math.exp(theta) - 1) / (self.marketMax - p) + 1) / theta else: # r[0,1] a = (self.sell_target-l)/(p-l) self.r_shout = (math.log((1 - a) * (math.exp(theta) - 1) + 1)) / theta def calcAgg(self): delta = 0 if self.job == 'Bid': # BUYER if self.buy_target >= self.previous_transactions[-1] : # must be more aggressive delta = (1+self.r_shout_change_relative)*self.r_shout + self.r_shout_change_absolute else : delta = (1-self.r_shout_change_relative)*self.r_shout - self.r_shout_change_absolute self.buy_r = self.buy_r + self.short_term_learning_rate * (delta - self.buy_r) if self.job == 'Ask': # SELLER if self.sell_target > self.previous_transactions[-1] : delta = (1+self.r_shout_change_relative)*self.r_shout + self.r_shout_change_absolute else : delta = (1-self.r_shout_change_relative)*self.r_shout - self.r_shout_change_absolute self.sell_r = self.sell_r + self.short_term_learning_rate * (delta - self.sell_r) def calcTarget(self): if len(self.estimated_equilibrium) > 0: p = self.estimated_equilibrium[-1] if self.limit == p: p = p * 1.000001 # to prevent theta_bar = 0 elif self.job == 'Bid': p = self.limit - self.limit * 0.2 ## Initial guess for eq if no deals yet!!.... elif self.job == 'Ask': p = self.limit + self.limit * 0.2 l = self.limit theta = self.theta if self.job == 'Bid': #BUYER minus_thing = (math.exp(-self.buy_r * theta) - 1) / (math.exp(theta) - 1) plus_thing = (math.exp(self.buy_r * theta) - 1) / (math.exp(theta) - 1) theta_bar = (theta * l - theta * p) / p if theta_bar == 0: theta_bar = 0.0001 if math.exp(theta_bar) - 1 == 0: theta_bar = 0.0001 bar_thing = (math.exp(-self.buy_r * theta_bar) - 1) / (math.exp(theta_bar) - 1) if l <= p: #Extramarginal if self.buy_r >= 0: self.buy_target = l else: self.buy_target = l * (1 - minus_thing) else: #intramarginal if self.buy_r >= 0: self.buy_target = p + (l-p)*plus_thing else: self.buy_target = p*(1-bar_thing) if self.buy_target > l: self.buy_target = l if self.job == 'Ask': #SELLER minus_thing = (math.exp(-self.sell_r * theta) - 1) / (math.exp(theta) - 1) plus_thing = (math.exp(self.sell_r * theta) - 1) / (math.exp(theta) - 1) theta_bar = (theta * l - theta * p) / p if theta_bar == 0: theta_bar = 0.0001 if math.exp(theta_bar) - 1 == 0: theta_bar = 0.0001 bar_thing = (math.exp(-self.sell_r * theta_bar) - 1) / (math.exp(theta_bar) - 1) #div 0 sometimes what!? if l <= p: #Extramarginal if self.buy_r >= 0: self.buy_target = l else: self.buy_target = l + (self.marketMax - l)*(minus_thing) else: #intramarginal if self.buy_r >= 0: self.buy_target = l + (p-l)*(1-plus_thing) else: self.buy_target = p + (self.marketMax - p)*(bar_thing) if self.sell_target < l: self.sell_target = l def getorder(self, time, countdown, lob): if len(self.orders) < 1: self.active = False return None else: self.active = True self.limit = self.orders[0].price self.job = self.orders[0].otype self.calcTarget() if self.prev_best_bid_p == None: o_bid = 0 else: o_bid = self.prev_best_bid_p if self.prev_best_ask_p == None: o_ask = self.marketMax else: o_ask = self.prev_best_ask_p if self.job == 'Bid': #BUYER if self.limit <= o_bid: return None else: if len(self.previous_transactions) > 0: ## has been at least one transaction o_ask_plus = (1+self.r_shout_change_relative)*o_ask + self.r_shout_change_absolute quoteprice = o_bid + ((min(self.limit, o_ask_plus) - o_bid) / self.offer_change_rate) else: if o_ask <= self.buy_target: quoteprice = o_ask else: quoteprice = o_bid + ((self.buy_target - o_bid) / self.offer_change_rate) if self.job == 'Ask': if self.limit >= o_ask: return None else: if len(self.previous_transactions) > 0: ## has been at least one transaction o_bid_minus = (1-self.r_shout_change_relative) * o_bid - self.r_shout_change_absolute quoteprice = o_ask - ((o_ask - max(self.limit, o_bid_minus)) / self.offer_change_rate) else: if o_bid >= self.sell_target: quoteprice = o_bid else: quoteprice = o_ask - ((o_ask - self.sell_target) / self.offer_change_rate) order = Order(self.tid, self.orders[0].otype, quoteprice, self.orders[0].qty, time, lob['QID']) self.lastquote=order return order def respond(self, time, lob, trade, verbose): ## Begin nicked from ZIP # what, if anything, has happened on the bid LOB? Nicked from ZIP.. bid_improved = False bid_hit = False lob_best_bid_p = lob['bids']['best'] lob_best_bid_q = None if lob_best_bid_p != None: # non-empty bid LOB lob_best_bid_q = lob['bids']['lob'][-1][1] if self.prev_best_bid_p < lob_best_bid_p : # best bid has improved # NB doesn't check if the improvement was by self bid_improved = True elif trade != None and ((self.prev_best_bid_p > lob_best_bid_p) or ((self.prev_best_bid_p == lob_best_bid_p) and (self.prev_best_bid_q > lob_best_bid_q))): # previous best bid was hit bid_hit = True elif self.prev_best_bid_p != None: # the bid LOB has been emptied: was it cancelled or hit? last_tape_item = lob['tape'][-1] if last_tape_item['type'] == 'Cancel' : bid_hit = False else: bid_hit = True # what, if anything, has happened on the ask LOB? ask_improved = False ask_lifted = False lob_best_ask_p = lob['asks']['best'] lob_best_ask_q = None if lob_best_ask_p != None: # non-empty ask LOB lob_best_ask_q = lob['asks']['lob'][0][1] if self.prev_best_ask_p > lob_best_ask_p : # best ask has improved -- NB doesn't check if the improvement was by self ask_improved = True elif trade != None and ((self.prev_best_ask_p < lob_best_ask_p) or ((self.prev_best_ask_p == lob_best_ask_p) and (self.prev_best_ask_q > lob_best_ask_q))): # trade happened and best ask price has got worse, or stayed same but quantity reduced -- assume previous best ask was lifted ask_lifted = True elif self.prev_best_ask_p != None: # the ask LOB is empty now but was not previously: canceled or lifted? last_tape_item = lob['tape'][-1] if last_tape_item['type'] == 'Cancel' : ask_lifted = False else: ask_lifted = True self.prev_best_bid_p = lob_best_bid_p self.prev_best_bid_q = lob_best_bid_q self.prev_best_ask_p = lob_best_ask_p self.prev_best_ask_q = lob_best_ask_q deal = bid_hit or ask_lifted ## End nicked from ZIP if deal: self.previous_transactions.append(trade['price']) if self.sell_target == None: self.sell_target = trade['price'] if self.buy_target == None: self.buy_target = trade['price'] self.calcEq() self.calcAlpha() self.calcTheta() self.calcRshout() self.calcAgg() self.calcTarget() #print 'sell: ', self.sell_target, 'buy: ', self.buy_target, 'limit:', self.limit, 'eq: ', self.estimated_equilibrium[-1], 'sell_r: ', self.sell_r, 'buy_r: ', self.buy_r, '\n' # Trader subclass ZI-C # After Gode & Sunder 1993 class Trader_ZIC(Trader): def getorder(self, time, countdown, lob): if len(self.orders) < 1: # no orders: return NULL order = None else: minprice = lob['bids']['worst'] maxprice = lob['asks']['worst'] qid = lob['QID'] limit = self.orders[0].price otype = self.orders[0].otype if otype == 'Bid': quoteprice = random.randint(minprice, limit) else: quoteprice = random.randint(limit, maxprice) # NB should check it == 'Ask' and barf if not order = Order(self.tid, otype, quoteprice, self.orders[0].qty, time, qid) self.lastquote = order return order # Trader subclass Shaver # shaves a penny off the best price # if there is no best price, creates "stub quote" at system max/min class Trader_Shaver(Trader): def getorder(self, time, countdown, lob): if len(self.orders) < 1: order = None else: limitprice = self.orders[0].price otype = self.orders[0].otype if otype == 'Bid': if lob['bids']['n'] > 0: quoteprice = lob['bids']['best'] + 1 if quoteprice > limitprice : quoteprice = limitprice else: quoteprice = lob['bids']['worst'] else: if lob['asks']['n'] > 0: quoteprice = lob['asks']['best'] - 1 if quoteprice < limitprice: quoteprice = limitprice else: quoteprice = lob['asks']['worst'] order = Order(self.tid, otype, quoteprice, self.orders[0].qty, time, lob['QID']) self.lastquote = order return order # Trader subclass Sniper # Based on Shaver, # "lurks" until time remaining < threshold% of the trading session # then gets increasing aggressive, increasing "shave thickness" as time runs out class Trader_Sniper(Trader): def getorder(self, time, countdown, lob): lurk_threshold = 0.2 shavegrowthrate = 3 shave = int(1.0 / (0.01 + countdown / (shavegrowthrate * lurk_threshold))) if (len(self.orders) < 1) or (countdown > lurk_threshold): order = None else: limitprice = self.orders[0].price otype = self.orders[0].otype if otype == 'Bid': if lob['bids']['n'] > 0: quoteprice = lob['bids']['best'] + shave if quoteprice > limitprice : quoteprice = limitprice else: quoteprice = lob['bids']['worst'] else: if lob['asks']['n'] > 0: quoteprice = lob['asks']['best'] - shave if quoteprice < limitprice: quoteprice = limitprice else: quoteprice = lob['asks']['worst'] order = Order(self.tid, otype, quoteprice, self.orders[0].qty, time, lob['QID']) self.lastquote = order return order # Trader subclass ZIP # After Cliff 1997 class Trader_ASAD(Trader): # ZIP init key param-values are those used in Cliff's 1997 original HP Labs tech report # NB this implementation keeps separate margin values for buying & selling, # so a single trader can both buy AND sell # -- in the original, traders were either buyers OR sellers def __init__(self, ttype, tid, balance, time): self.ttype = ttype self.tid = tid self.balance = balance self.birthtime = time self.profitpertime = 0 self.n_trades = 0 self.blotter = [] self.orders = [] self.prev_orders = [] self.n_quotes = 0 self.lastquote = None self.job = None # this gets switched to 'Bid' or 'Ask' depending on order-type self.active = False # gets switched to True while actively working an order self.prev_change = 0 # this was called last_d in Cliff'97 self.beta = 0.1 + 0.4 * random.random() self.momntm = 0.1 * random.random() self.ca = 0.05 # self.ca & .cr were hard-coded in '97 but parameterised later self.cr = 0.05 self.margin = None # this was called profit in Cliff'97 self.margin_buy = -1.0 * (0.05 + 0.3 * random.random()) self.margin_sell = 0.05 + 0.3 * random.random() self.price = None self.limit = None self.phi = 0 #measure of market shock for ASAD # memory of best price & quantity of best bid and ask, on LOB on previous update self.prev_best_bid_p = None self.prev_best_bid_q = None self.prev_best_ask_p = None self.prev_best_ask_q = None def getorder(self, time, countdown, lob): if len(self.orders) < 1: self.active = False order = None else: self.active = True self.limit = self.orders[0].price self.job = self.orders[0].otype if self.job == 'Bid': # currently a buyer (working a bid order) self.margin = self.margin_buy else: # currently a seller (working a sell order) self.margin = self.margin_sell quoteprice = int(self.limit * (1 + self.margin)) self.price = quoteprice order = Order(self.tid, self.job, quoteprice, self.orders[0].qty, time, lob['QID']) self.lastquote = order self.prev_orders.append(order) return order # update margin on basis of what happened in market def respond(self, time, lob, trade, verbose): # ZIP trader responds to market events, altering its margin # does this whether it currently has an order to work or not def target_up(price): # generate a higher target price by randomly perturbing given price ptrb_abs = self.ca * random.random() # absolute shift ptrb_rel = price * (1.0 + (self.cr * random.random())) # relative shift target = int(round(ptrb_rel + ptrb_abs, 0)) # # print('TargetUp: %d %d\n' % (price,target)) return(target) def target_down(price): # generate a lower target price by randomly perturbing given price ptrb_abs = self.ca * random.random() # absolute shift ptrb_rel = price * (1.0 - (self.cr * random.random())) # relative shift target = int(round(ptrb_rel - ptrb_abs, 0)) # # print('TargetDn: %d %d\n' % (price,target)) return(target) def willing_to_trade(price): # am I willing to trade at this price? willing = False if self.job == 'Bid' and self.active and self.price >= price: willing = True if self.job == 'Ask' and self.active and self.price <= price: willing = True return willing def profit_alter(price): oldprice = self.price diff = price - oldprice change = ((1.0 - self.momntm) * (self.beta * diff)) + (self.momntm * self.prev_change) self.prev_change = change newmargin = ((self.price + change) / self.limit) - 1.0 if self.job == 'Bid': if newmargin < 0.0 : self.margin_buy = newmargin self.margin = newmargin else : if newmargin > 0.0 : self.margin_sell = newmargin self.margin = newmargin # set the price from limit and profit-margin self.price = int(round(self.limit * (1.0 + self.margin), 0)) # # print('old=%d diff=%d change=%d price = %d\n' % (oldprice, diff, change, self.price)) def calc_phi(): if len(self.prev_orders) < 20: return sumxy = 0 sumx = 0 sumxsq = 0 sumy = 0 for i in range(20): sumxy = sumxy + i * self.prev_orders[-20:][i].price sumx = sumx + i sumxsq = sumxsq + i*i sumy = sumy + self.prev_orders[-20:][i].price delta = (sumxy - (sumy * sumx / 20)) / (sumxsq - (sumx * sumx / 20)) if delta < 0: self.phi = -math.log(1-delta) else: self.phi = math.log(1+delta) # what, if anything, has happened on the bid LOB? bid_improved = False bid_hit = False lob_best_bid_p = lob['bids']['best'] lob_best_bid_q = None if lob_best_bid_p != None: # non-empty bid LOB lob_best_bid_q = lob['bids']['lob'][-1][1] if self.prev_best_bid_p < lob_best_bid_p : # best bid has improved # NB doesn't check if the improvement was by self bid_improved = True elif trade != None and ((self.prev_best_bid_p > lob_best_bid_p) or ((self.prev_best_bid_p == lob_best_bid_p) and (self.prev_best_bid_q > lob_best_bid_q))): # previous best bid was hit bid_hit = True elif self.prev_best_bid_p != None: # the bid LOB has been emptied: was it cancelled or hit? last_tape_item = lob['tape'][-1] if last_tape_item['type'] == 'Cancel' : bid_hit = False else: bid_hit = True # what, if anything, has happened on the ask LOB? ask_improved = False ask_lifted = False lob_best_ask_p = lob['asks']['best'] lob_best_ask_q = None if lob_best_ask_p != None: # non-empty ask LOB lob_best_ask_q = lob['asks']['lob'][0][1] if self.prev_best_ask_p > lob_best_ask_p : # best ask has improved -- NB doesn't check if the improvement was by self ask_improved = True elif trade != None and ((self.prev_best_ask_p < lob_best_ask_p) or ((self.prev_best_ask_p == lob_best_ask_p) and (self.prev_best_ask_q > lob_best_ask_q))): # trade happened and best ask price has got worse, or stayed same but quantity reduced -- assume previous best ask was lifted ask_lifted = True elif self.prev_best_ask_p != None: # the ask LOB is empty now but was not previously: canceled or lifted? last_tape_item = lob['tape'][-1] if last_tape_item['type'] == 'Cancel' : ask_lifted = False else: ask_lifted = True if verbose and (bid_improved or bid_hit or ask_improved or ask_lifted): print ('B_improved', bid_improved, 'B_hit', bid_hit, 'A_improved', ask_improved, 'A_lifted', ask_lifted) deal = bid_hit or ask_lifted calc_phi() if self.job == 'Ask': # seller if deal : tradeprice = trade['price'] if self.price <= tradeprice: # could sell for more? raise margin target_price = target_up(tradeprice) if self.phi > 1: target_price = target_up(target_price) profit_alter(target_price) elif ask_lifted and self.active and not willing_to_trade(tradeprice): # wouldnt have got this deal, still working order, so reduce margin target_price = target_down(tradeprice) if self.phi > 1: target_price = target_up(target_price) profit_alter(target_price) else: # no deal: aim for a target price higher than best bid if ask_improved and self.price > lob_best_ask_p: if lob_best_bid_p != None: target_price = target_up(lob_best_bid_p) else: target_price = lob['asks']['worst'] # stub quote profit_alter(target_price) if self.job == 'Bid': # buyer if deal : tradeprice = trade['price'] if self.price >= tradeprice: # could buy for less? raise margin (i.e. cut the price) target_price = target_down(tradeprice) if self.phi < -1: target_price = target_up(target_price) profit_alter(target_price) elif bid_hit and self.active and not willing_to_trade(tradeprice): # wouldnt have got this deal, still working order, so reduce margin target_price = target_up(tradeprice) if self.phi < -1: target_price = target_up(target_price) profit_alter(target_price) else: # no deal: aim for target price lower than best ask if bid_improved and self.price < lob_best_bid_p: if lob_best_ask_p != None: target_price = target_down(lob_best_ask_p) else: target_price = lob['bids']['worst'] # stub quote profit_alter(target_price) # remember the best LOB data ready for next response self.prev_best_bid_p = lob_best_bid_p self.prev_best_bid_q = lob_best_bid_q self.prev_best_ask_p = lob_best_ask_p self.prev_best_ask_q = lob_best_ask_q # Trader subclass ZIP # After Cliff 1997 class Trader_GDX(Trader): def __init__(self, ttype, tid, balance, time): self.ttype = ttype self.tid = tid self.balance = balance self.birthtime = time self.profitpertime = 0 self.n_trades = 0 self.blotter = [] self.orders = [] self.prev_orders = [] self.n_quotes = 0 self.lastquote = None self.job = None # this gets switched to 'Bid' or 'Ask' depending on order-type self.active = False # gets switched to True while actively working an order #memory of all bids and asks and accepted bids and asks self.outstanding_bids = [] self.outstanding_asks = [] self.accepted_asks = [] self.accepted_bids = [] self.price = -1 # memory of best price & quantity of best bid and ask, on LOB on previous update self.prev_best_bid_p = None self.prev_best_bid_q = None self.prev_best_ask_p = None self.prev_best_ask_q = None self.first_turn = True self.gamma = 0.1 self.holdings = 10 self.remaining_offer_ops = 10 self.values = [[0 for n in range(self.remaining_offer_ops)] for m in range(self.holdings)] def getorder(self, time, countdown, lob): if len(self.orders) < 1: self.active = False order = None else: self.active = True self.limit = self.orders[0].price self.job = self.orders[0].otype #calculate price if self.job == 'Bid': self.price = self.calc_p_bid(self.holdings - 1, self.remaining_offer_ops - 1) if self.job == 'Ask': self.price = self.calc_p_ask(self.holdings - 1, self.remaining_offer_ops - 1) order = Order(self.tid, self.job, self.price, self.orders[0].qty, time, lob['QID']) self.lastquote = order if self.first_turn or self.price == -1: return None return order def calc_p_bid(self, m, n): best_return = 0 best_bid = 0 second_best_return = 0 second_best_bid = 0 #first step size of 1 get best and 2nd best for i in [x*2 for x in range(int(self.limit/2))]: thing = self.belief_buy(i) * ((self.limit - i) + self.gamma*self.values[m-1][n-1]) + (1-self.belief_buy(i) * self.gamma * self.values[m][n-1]) if thing > best_return: second_best_bid = best_bid second_best_return = best_return best_return = thing best_bid = i #always best bid largest one if second_best_bid > best_bid: a = second_best_bid second_best_bid = best_bid best_bid = a #then step size 0.05 for i in [x*0.05 for x in range(int(second_best_bid), int(best_bid))]: thing = self.belief_buy(i + second_best_bid) * ((self.limit - (i + second_best_bid)) + self.gamma*self.values[m-1][n-1]) + (1-self.belief_buy(i + second_best_bid) * self.gamma * self.values[m][n-1]) if thing > best_return: best_return = thing best_bid = i + second_best_bid return best_bid def calc_p_ask(self, m, n): best_return = 0 best_ask = self.limit second_best_return = 0 second_best_ask = self.limit #first step size of 1 get best and 2nd best for i in [x*2 for x in range(int(self.limit/2))]: j = i + self.limit thing = self.belief_sell(j) * ((j - self.limit) + self.gamma*self.values[m-1][n-1]) + (1-self.belief_sell(j) * self.gamma * self.values[m][n-1]) if thing > best_return: second_best_ask = best_ask second_best_return = best_return best_return = thing best_ask = j #always best ask largest one if second_best_ask > best_ask: a = second_best_ask second_best_ask = best_ask best_ask = a #then step size 0.05 for i in [x*0.05 for x in range(int(second_best_ask), int(best_ask))]: thing = self.belief_sell(i + second_best_ask) * (((i + second_best_ask) - self.limit) + self.gamma*self.values[m-1][n-1]) + (1-self.belief_sell(i + second_best_ask) * self.gamma * self.values[m][n-1]) if thing > best_return: best_return = thing best_ask = i + second_best_ask return best_ask def belief_sell(self, price): accepted_asks_greater = 0 bids_greater = 0 unaccepted_asks_lower = 0 for p in self.accepted_asks: if p >= price: accepted_asks_greater += 1 for p in [thing[0] for thing in self.outstanding_bids]: if p >= price: bids_greater += 1 for p in [thing[0] for thing in self.outstanding_asks]: if p <= price: unaccepted_asks_lower += 1 if accepted_asks_greater + bids_greater + unaccepted_asks_lower == 0: return 0 return (accepted_asks_greater + bids_greater) / (accepted_asks_greater + bids_greater + unaccepted_asks_lower) def belief_buy(self, price): accepted_bids_lower = 0 asks_lower = 0 unaccepted_bids_greater = 0 for p in self.accepted_bids: if p <= price: accepted_bids_lower += 1 for p in [thing[0] for thing in self.outstanding_asks]: if p <= price: asks_lower += 1 for p in [thing[0] for thing in self.outstanding_bids]: if p >= price: unaccepted_bids_greater += 1 if accepted_bids_lower + asks_lower + unaccepted_bids_greater == 0: return 0 return (accepted_bids_lower + asks_lower) / (accepted_bids_lower + asks_lower + unaccepted_bids_greater) def respond(self, time, lob, trade, verbose): # what, if anything, has happened on the bid LOB? self.outstanding_bids = lob['bids']['lob'] bid_improved = False bid_hit = False lob_best_bid_p = lob['bids']['best'] lob_best_bid_q = None if lob_best_bid_p != None: # non-empty bid LOB lob_best_bid_q = lob['bids']['lob'][-1][1] if self.prev_best_bid_p < lob_best_bid_p : # best bid has improved # NB doesn't check if the improvement was by self bid_improved = True elif trade != None and ((self.prev_best_bid_p > lob_best_bid_p) or ((self.prev_best_bid_p == lob_best_bid_p) and (self.prev_best_bid_q > lob_best_bid_q))): # previous best bid was hit self.accepted_bids.append(self.prev_best_bid_p) bid_hit = True elif self.prev_best_bid_p != None: # the bid LOB has been emptied: was it cancelled or hit? last_tape_item = lob['tape'][-1] if last_tape_item['type'] == 'Cancel' : bid_hit = False else: bid_hit = True # what, if anything, has happened on the ask LOB? self.outstanding_asks = lob['asks']['lob'] ask_improved = False ask_lifted = False lob_best_ask_p = lob['asks']['best'] lob_best_ask_q = None if lob_best_ask_p != None: # non-empty ask LOB lob_best_ask_q = lob['asks']['lob'][0][1] if self.prev_best_ask_p > lob_best_ask_p : # best ask has improved -- NB doesn't check if the improvement was by self ask_improved = True elif trade != None and ((self.prev_best_ask_p < lob_best_ask_p) or ((self.prev_best_ask_p == lob_best_ask_p) and (self.prev_best_ask_q > lob_best_ask_q))): # trade happened and best ask price has got worse, or stayed same but quantity reduced -- assume previous best ask was lifted self.accepted_asks.append(self.prev_best_ask_p) ask_lifted = True elif self.prev_best_ask_p != None: # the ask LOB is empty now but was not previously: canceled or lifted? last_tape_item = lob['tape'][-1] if last_tape_item['type'] == 'Cancel' : ask_lifted = False else: ask_lifted = True #populate expected values if self.first_turn: print "populating" self.first_turn = False for n in range(1, self.remaining_offer_ops): for m in range(1, self.holdings): if self.job == 'Bid': #BUYER self.values[m][n] = self.calc_p_bid(m, n) if self.job == 'Ask': #BUYER self.values[m][n] = self.calc_p_ask(m, n) print "done" deal = bid_hit or ask_lifted # remember the best LOB data ready for next response self.prev_best_bid_p = lob_best_bid_p self.prev_best_bid_q = lob_best_bid_q self.prev_best_ask_p = lob_best_ask_p self.prev_best_ask_q = lob_best_ask_q # Trader subclass ZIP # After Cliff 1997 class Trader_ZIP(Trader): # ZIP init key param-values are those used in Cliff's 1997 original HP Labs tech report # NB this implementation keeps separate margin values for buying & selling, # so a single trader can both buy AND sell # -- in the original, traders were either buyers OR sellers def __init__(self, ttype, tid, balance, time): self.ttype = ttype self.tid = tid self.balance = balance self.birthtime = time self.profitpertime = 0 self.n_trades = 0 self.blotter = [] self.orders = [] self.n_quotes = 0 self.lastquote = None self.job = None # this gets switched to 'Bid' or 'Ask' depending on order-type self.active = False # gets switched to True while actively working an order self.prev_change = 0 # this was called last_d in Cliff'97 self.beta = 0.1 + 0.4 * random.random() self.momntm = 0.1 * random.random() self.ca = 0.05 # self.ca & .cr were hard-coded in '97 but parameterised later self.cr = 0.05 self.margin = None # this was called profit in Cliff'97 self.margin_buy = -1.0 * (0.05 + 0.3 * random.random()) self.margin_sell = 0.05 + 0.3 * random.random() self.price = None self.limit = None # memory of best price & quantity of best bid and ask, on LOB on previous update self.prev_best_bid_p = None self.prev_best_bid_q = None self.prev_best_ask_p = None self.prev_best_ask_q = None def getorder(self, time, countdown, lob): if len(self.orders) < 1: self.active = False order = None else: self.active = True self.limit = self.orders[0].price self.job = self.orders[0].otype if self.job == 'Bid': # currently a buyer (working a bid order) self.margin = self.margin_buy else: # currently a seller (working a sell order) self.margin = self.margin_sell quoteprice = int(self.limit * (1 + self.margin)) self.price = quoteprice order = Order(self.tid, self.job, quoteprice, self.orders[0].qty, time, lob['QID']) self.lastquote = order return order # update margin on basis of what happened in market def respond(self, time, lob, trade, verbose): # ZIP trader responds to market events, altering its margin # does this whether it currently has an order to work or not def target_up(price): # generate a higher target price by randomly perturbing given price ptrb_abs = self.ca * random.random() # absolute shift ptrb_rel = price * (1.0 + (self.cr * random.random())) # relative shift target = int(round(ptrb_rel + ptrb_abs, 0)) # # print('TargetUp: %d %d\n' % (price,target)) return(target) def target_down(price): # generate a lower target price by randomly perturbing given price ptrb_abs = self.ca * random.random() # absolute shift ptrb_rel = price * (1.0 - (self.cr * random.random())) # relative shift target = int(round(ptrb_rel - ptrb_abs, 0)) # # print('TargetDn: %d %d\n' % (price,target)) return(target) def willing_to_trade(price): # am I willing to trade at this price? willing = False if self.job == 'Bid' and self.active and self.price >= price: willing = True if self.job == 'Ask' and self.active and self.price <= price: willing = True return willing def profit_alter(price): oldprice = self.price diff = price - oldprice change = ((1.0 - self.momntm) * (self.beta * diff)) + (self.momntm * self.prev_change) self.prev_change = change newmargin = ((self.price + change) / self.limit) - 1.0 if self.job == 'Bid': if newmargin < 0.0 : self.margin_buy = newmargin self.margin = newmargin else : if newmargin > 0.0 : self.margin_sell = newmargin self.margin = newmargin # set the price from limit and profit-margin self.price = int(round(self.limit * (1.0 + self.margin), 0)) # # print('old=%d diff=%d change=%d price = %d\n' % (oldprice, diff, change, self.price)) # what, if anything, has happened on the bid LOB? bid_improved = False bid_hit = False lob_best_bid_p = lob['bids']['best'] lob_best_bid_q = None if lob_best_bid_p != None: # non-empty bid LOB lob_best_bid_q = lob['bids']['lob'][-1][1] if self.prev_best_bid_p < lob_best_bid_p : # best bid has improved # NB doesn't check if the improvement was by self bid_improved = True elif trade != None and ((self.prev_best_bid_p > lob_best_bid_p) or ((self.prev_best_bid_p == lob_best_bid_p) and (self.prev_best_bid_q > lob_best_bid_q))): # previous best bid was hit bid_hit = True elif self.prev_best_bid_p != None: # the bid LOB has been emptied: was it cancelled or hit? last_tape_item = lob['tape'][-1] if last_tape_item['type'] == 'Cancel' : bid_hit = False else: bid_hit = True # what, if anything, has happened on the ask LOB? ask_improved = False ask_lifted = False lob_best_ask_p = lob['asks']['best'] lob_best_ask_q = None if lob_best_ask_p != None: # non-empty ask LOB lob_best_ask_q = lob['asks']['lob'][0][1] if self.prev_best_ask_p > lob_best_ask_p : # best ask has improved -- NB doesn't check if the improvement was by self ask_improved = True elif trade != None and ((self.prev_best_ask_p < lob_best_ask_p) or ((self.prev_best_ask_p == lob_best_ask_p) and (self.prev_best_ask_q > lob_best_ask_q))): # trade happened and best ask price has got worse, or stayed same but quantity reduced -- assume previous best ask was lifted ask_lifted = True elif self.prev_best_ask_p != None: # the ask LOB is empty now but was not previously: canceled or lifted? last_tape_item = lob['tape'][-1] if last_tape_item['type'] == 'Cancel' : ask_lifted = False else: ask_lifted = True if verbose and (bid_improved or bid_hit or ask_improved or ask_lifted): print ('B_improved', bid_improved, 'B_hit', bid_hit, 'A_improved', ask_improved, 'A_lifted', ask_lifted) deal = bid_hit or ask_lifted if self.job == 'Ask': # seller if deal : tradeprice = trade['price'] if self.price <= tradeprice: # could sell for more? raise margin target_price = target_up(tradeprice) profit_alter(target_price) elif ask_lifted and self.active and not willing_to_trade(tradeprice): # wouldnt have got this deal, still working order, so reduce margin target_price = target_down(tradeprice) profit_alter(target_price) else: # no deal: aim for a target price higher than best bid if ask_improved and self.price > lob_best_ask_p: if lob_best_bid_p != None: target_price = target_up(lob_best_bid_p) else: target_price = lob['asks']['worst'] # stub quote profit_alter(target_price) if self.job == 'Bid': # buyer if deal : tradeprice = trade['price'] if self.price >= tradeprice: # could buy for less? raise margin (i.e. cut the price) target_price = target_down(tradeprice) profit_alter(target_price) elif bid_hit and self.active and not willing_to_trade(tradeprice): # wouldnt have got this deal, still working order, so reduce margin target_price = target_up(tradeprice) profit_alter(target_price) else: # no deal: aim for target price lower than best ask if bid_improved and self.price < lob_best_bid_p: if lob_best_ask_p != None: target_price = target_down(lob_best_ask_p) else: target_price = lob['bids']['worst'] # stub quote profit_alter(target_price) # remember the best LOB data ready for next response self.prev_best_bid_p = lob_best_bid_p self.prev_best_bid_q = lob_best_bid_q self.prev_best_ask_p = lob_best_ask_p self.prev_best_ask_q = lob_best_ask_q ##########################---trader-types have all been defined now--################ ##########################---Below lies the experiment/test-rig---################## # trade_stats() # dump CSV statistics on exchange data and trader population to file for later analysis # this makes no assumptions about the number of types of traders, or # the number of traders of any one type -- allows either/both to change # between successive calls, but that does make it inefficient as it has to # re-analyse the entire set of traders on each call def trade_stats(expid, traders, dumpfile, time, lob): trader_types = {} n_traders = len(traders) for t in traders: ttype = traders[t].ttype if ttype in trader_types.keys(): t_balance = trader_types[ttype]['balance_sum'] + traders[t].balance n = trader_types[ttype]['n'] + 1 else: t_balance = traders[t].balance n = 1 trader_types[ttype] = {'n':n, 'balance_sum':t_balance} dumpfile.write('%s, %06d, ' % (expid, time)) printing_column = 0 for ttype in sorted(list(trader_types.keys())): n = trader_types[ttype]['n'] #to keep the traders in the same columns, make data easier if (ttype == 'AA'): s = trader_types[ttype]['balance_sum'] dumpfile.write('%s, %d, %d, %f, ' % (ttype, s, n, s / float(n))) printing_column = 1 if (ttype == 'ASAD'): if (printing_column == 0): dumpfile.write('%s, %s, %s, %s, ' % ('', '', '', '')) s = trader_types[ttype]['balance_sum'] dumpfile.write('%s, %d, %d, %f, ' % (ttype, s, n, s / float(n))) printing_column = 2 if (ttype == 'GDX'): for i in range(2 - printing_column): dumpfile.write('%s, %s, %s, %s, ' % ('', '', '', '')) s = trader_types[ttype]['balance_sum'] dumpfile.write('%s, %d, %d, %f, ' % (ttype, s, n, s / float(n))) printing_column = 3 if (ttype == 'ZIP'): for i in range(3 - printing_column): dumpfile.write('%s, %s, %s, %s, ' % ('', '', '', '')) s = trader_types[ttype]['balance_sum'] dumpfile.write('%s, %d, %d, %f, ' % (ttype, s, n, s / float(n))) printing_column = 4 while printing_column < 4: dumpfile.write('%s, %s, %s, %s, ' % ('', '', '', '')) printing_column += 1 if lob['bids']['best'] != None : dumpfile.write('%d, ' % (lob['bids']['best'])) else: dumpfile.write('N, ') if lob['asks']['best'] != None : dumpfile.write('%d, ' % (lob['asks']['best'])) else: dumpfile.write('N, ') dumpfile.write('\n'); # create a bunch of traders from traders_spec # returns tuple (n_buyers, n_sellers) # optionally shuffles the pack of buyers and the pack of sellers def populate_market(traders_spec, traders, shuffle, verbose): def trader_type(robottype, name): if robottype == 'AA': return Trader_AA('AA', name, 0.00, 0) elif robottype == 'ZIC': return Trader_ZIC('ZIC', name, 0.00, 0) elif robottype == 'GDX': return Trader_GDX('GDX', name, 0.00, 0) elif robottype == 'SNPR': return Trader_Sniper('SNPR', name, 0.00, 0) elif robottype == 'ZIP': return Trader_ZIP('ZIP', name, 0.00, 0) elif robottype == 'ASAD': return Trader_ASAD('ASAD', name, 0.00, 0) else: sys.exit('FATAL: don\'t know robot type %s\n' % robottype) def shuffle_traders(ttype_char, n, traders): for swap in range(n): t1 = (n - 1) - swap t2 = random.randint(0, t1) t1name = '%c%02d' % (ttype_char, t1) t2name = '%c%02d' % (ttype_char, t2) traders[t1name].tid = t2name traders[t2name].tid = t1name temp = traders[t1name] traders[t1name] = traders[t2name] traders[t2name] = temp n_buyers = 0 for bs in traders_spec['buyers']: ttype = bs[0] for b in range(bs[1]): tname = 'B%02d' % n_buyers # buyer i.d. string traders[tname] = trader_type(ttype, tname) n_buyers = n_buyers + 1 if n_buyers < 1: sys.exit('FATAL: no buyers specified\n') if shuffle: shuffle_traders('B', n_buyers, traders) n_sellers = 0 for ss in traders_spec['sellers']: ttype = ss[0] for s in range(ss[1]): tname = 'S%02d' % n_sellers # buyer i.d. string traders[tname] = trader_type(ttype, tname) n_sellers = n_sellers + 1 if n_sellers < 1: sys.exit('FATAL: no sellers specified\n') if shuffle: shuffle_traders('S', n_sellers, traders) if verbose : for t in range(n_buyers): bname = 'B%02d' % t print(traders[bname]) for t in range(n_sellers): bname = 'S%02d' % t print(traders[bname]) return {'n_buyers':n_buyers, 'n_sellers':n_sellers} # customer_orders(): allocate orders to traders # parameter "os" is order schedule # os['timemode'] is either 'periodic', 'drip-fixed', 'drip-jitter', or 'drip-poisson' # os['interval'] is number of seconds for a full cycle of replenishment # drip-poisson sequences will be normalised to ensure time of last replenishment <= interval # parameter "pending" is the list of future orders (if this is empty, generates a new one from os) # revised "pending" is the returned value # # also returns a list of "cancellations": trader-ids for those traders who are now working a new order and hence # need to kill quotes already on LOB from working previous order # # # if a supply or demand schedule mode is "random" and more than one range is supplied in ranges[], # then each time a price is generated one of the ranges is chosen equiprobably and # the price is then generated uniform-randomly from that range # # if len(range)==2, interpreted as min and max values on the schedule, specifying linear supply/demand curve # if len(range)==3, first two vals are min & max, third value should be a function that generates a dynamic price offset # -- the offset value applies equally to the min & max, so gradient of linear sup/dem curve doesn't vary # if len(range)==4, the third value is function that gives dynamic offset for schedule min, # and fourth is a function giving dynamic offset for schedule max, so gradient of sup/dem linear curve can vary # # the interface on this is a bit of a mess... could do with refactoring def customer_orders(time, last_update, traders, trader_stats, os, pending, verbose): def sysmin_check(price): if price < bse_sys_minprice: print('WARNING: price < bse_sys_min -- clipped') price = bse_sys_minprice return price def sysmax_check(price): if price > bse_sys_maxprice: print('WARNING: price > bse_sys_max -- clipped') price = bse_sys_maxprice return price def getorderprice(i, sched, n, mode, issuetime): # does the first schedule range include optional dynamic offset function(s)? if len(sched[0]) > 2: offsetfn = sched[0][2] if callable(offsetfn): # same offset for min and max offset_min = offsetfn(issuetime) offset_max = offset_min else: sys.exit('FAIL: 3rd argument of sched in getorderprice() not callable') if len(sched[0]) > 3: # if second offset function is specfied, that applies only to the max value offsetfn = sched[0][3] if callable(offsetfn): # this function applies to max offset_max = offsetfn(issuetime) else: sys.exit('FAIL: 4th argument of sched in getorderprice() not callable') else: offset_min = 0.0 offset_max = 0.0 pmin = sysmin_check(offset_min + min(sched[0][0], sched[0][1])) pmax = sysmax_check(offset_max + max(sched[0][0], sched[0][1])) prange = pmax - pmin stepsize = prange / (n - 1) halfstep = round(stepsize / 2.0) if mode == 'fixed': orderprice = pmin + int(i * stepsize) elif mode == 'jittered': orderprice = pmin + int(i * stepsize) + random.randint(-halfstep, halfstep) elif mode == 'random': if len(sched) > 1: # more than one schedule: choose one equiprobably s = random.randint(0, len(sched) - 1) pmin = sysmin_check(min(sched[s][0], sched[s][1])) pmax = sysmax_check(max(sched[s][0], sched[s][1])) orderprice = random.randint(pmin, pmax) else: sys.exit('FAIL: Unknown mode in schedule') orderprice = sysmin_check(sysmax_check(orderprice)) return orderprice def getissuetimes(n_traders, mode, interval, shuffle, fittointerval): interval = float(interval) if n_traders < 1: sys.exit('FAIL: n_traders < 1 in getissuetime()') elif n_traders == 1: tstep = interval else: tstep = interval / (n_traders - 1) arrtime = 0 issuetimes = [] for t in range(n_traders): if mode == 'periodic': arrtime = interval elif mode == 'drip-fixed': arrtime = t * tstep elif mode == 'drip-jitter': arrtime = t * tstep + tstep * random.random() elif mode == 'drip-poisson': # poisson requires a bit of extra work interarrivaltime = random.expovariate(n_traders / interval) arrtime += interarrivaltime else: sys.exit('FAIL: unknown time-mode in getissuetimes()') issuetimes.append(arrtime) # at this point, arrtime is the last arrival time if fittointerval and ((arrtime > interval) or (arrtime < interval)): # generated sum of interarrival times longer than the interval # squish them back so that last arrival falls at t=interval for t in range(n_traders): issuetimes[t] = interval * (issuetimes[t] / arrtime) # optionally randomly shuffle the times if shuffle: for t in range(n_traders): i = (n_traders - 1) - t j = random.randint(0, i) tmp = issuetimes[i] issuetimes[i] = issuetimes[j] issuetimes[j] = tmp return issuetimes def getschedmode(time, os): got_one = False for sched in os: if (sched['from'] <= time) and (time < sched['to']) : # within the timezone for this schedule schedrange = sched['ranges'] mode = sched['stepmode'] got_one = True exit # jump out the loop -- so the first matching timezone has priority over any others if not got_one: sys.exit('Fail: time=%5.2f not within any timezone in os=%s' % (time, os)) return (schedrange, mode) n_buyers = trader_stats['n_buyers'] n_sellers = trader_stats['n_sellers'] shuffle_times = False cancellations = [] if len(pending) < 1: # list of pending (to-be-issued) customer orders is empty, so generate a new one new_pending = [] # demand side (buyers) issuetimes = getissuetimes(n_buyers, os['timemode'], os['interval'], shuffle_times, True) ordertype = 'Bid' (sched, mode) = getschedmode(time, os['dem']) for t in range(n_buyers): issuetime = time + issuetimes[t] tname = 'B%02d' % t orderprice = getorderprice(t, sched, n_buyers, mode, issuetime) order = Order(tname, ordertype, orderprice, 1, issuetime, -3.14) new_pending.append(order) # supply side (sellers) issuetimes = getissuetimes(n_sellers, os['timemode'], os['interval'], shuffle_times, True) ordertype = 'Ask' (sched, mode) = getschedmode(time, os['sup']) for t in range(n_sellers): issuetime = time + issuetimes[t] tname = 'S%02d' % t orderprice = getorderprice(t, sched, n_sellers, mode, issuetime) order = Order(tname, ordertype, orderprice, 1, issuetime, -3.14) new_pending.append(order) else: # there are pending future orders: issue any whose timestamp is in the past new_pending = [] for order in pending: if order.time < time: # this order should have been issued by now # issue it to the trader tname = order.tid response = traders[tname].add_order(order, verbose) if verbose: print('Customer order: %s %s' % (response, order) ) if response == 'LOB_Cancel' : cancellations.append(tname) if verbose: print('Cancellations: %s' % (cancellations)) # and then don't add it to new_pending (i.e., delete it) else: # this order stays on the pending list new_pending.append(order) return [new_pending, cancellations] # one session in the market def market_session(sess_id, starttime, endtime, trader_spec, order_schedule, dumpfile, dump_each_trade, verbose): # initialise the exchange exchange = Exchange() # create a bunch of traders traders = {} trader_stats = populate_market(trader_spec, traders, True, verbose) # timestep set so that can process all traders in one second # NB minimum interarrival time of customer orders may be much less than this!! timestep = 1.0 / float(trader_stats['n_buyers'] + trader_stats['n_sellers']) duration = float(endtime - starttime) last_update = -1.0 time = starttime orders_verbose = False lob_verbose = False process_verbose = False respond_verbose = False bookkeep_verbose = False pending_cust_orders = [] if verbose: print('\n%s; ' % (sess_id)) while time < endtime: # how much time left, as a percentage? time_left = (endtime - time) / duration # if verbose: print('\n\n%s; t=%08.2f (%4.1f/100) ' % (sess_id, time, time_left*100)) trade = None [pending_cust_orders, kills] = customer_orders(time, last_update, traders, trader_stats, order_schedule, pending_cust_orders, orders_verbose) # if any newly-issued customer orders mean quotes on the LOB need to be cancelled, kill them if len(kills) > 0 : # if verbose : print('Kills: %s' % (kills)) for kill in kills : # if verbose : print('lastquote=%s' % traders[kill].lastquote) if traders[kill].lastquote != None : # if verbose : print('Killing order %s' % (str(traders[kill].lastquote))) exchange.del_order(time, traders[kill].lastquote, verbose) # get a limit-order quote (or None) from a randomly chosen trader tid = list(traders.keys())[random.randint(0, len(traders) - 1)] order = traders[tid].getorder(time, time_left, exchange.publish_lob(time, lob_verbose)) # if verbose: print('Trader Quote: %s' % (order)) if order != None: if order.otype == 'Ask' and order.price < traders[tid].orders[0].price: sys.exit('Bad ask') if order.otype == 'Bid' and order.price > traders[tid].orders[0].price: sys.exit('Bad bid') # send order to exchange traders[tid].n_quotes = 1 trade = exchange.process_order2(time, order, process_verbose) if trade != None: # trade occurred, # so the counterparties update order lists and blotters traders[trade['party1']].bookkeep(trade, order, bookkeep_verbose, time) traders[trade['party2']].bookkeep(trade, order, bookkeep_verbose, time) if dump_each_trade: trade_stats(sess_id, traders, tdump, time, exchange.publish_lob(time, lob_verbose)) # traders respond to whatever happened lob = exchange.publish_lob(time, lob_verbose) for t in traders: # NB respond just updates trader's internal variables # doesn't alter the LOB, so processing each trader in # sequence (rather than random/shuffle) isn't a problem traders[t].respond(time, lob, trade, respond_verbose) time = time + timestep # end of an experiment -- dump the tape exchange.tape_dump('transactions.csv', 'w', 'keep') # write trade_stats for this experiment NB end-of-session summary only trade_stats(sess_id, traders, tdump, time, exchange.publish_lob(time, lob_verbose)) ############################# # # Below here is where we set up and run a series of experiments if __name__ == "__main__": # set up parameters for the session start_time = 0.0 end_time = 330.0 duration = end_time - start_time # schedule_offsetfn returns time-dependent offset on schedule prices #def schedule_offsetfn(t): # pi2 = math.pi * 2 # c = math.pi * 3000 # wavelength = t / c # gradient = 100 * t / (c / pi2) # amplitude = 100 * t / (c / pi2) # offset = gradient + amplitude * math.sin(wavelength * t) # print int(round(offset, 0)) # return int(round(offset, 0)) def schedule_offsetfn(t): return int((t % 75)/2) # def schedule_offsetfn(t): # return int(math.sin(t/30)) # # def schedule_offsetfn(t): # if (t % 100 < 50): # return 5 # else: # return -5 # # range1 = (10, 190, schedule_offsetfn) # # range2 = (200,300, schedule_offsetfn) # # supply_schedule = [ {'from':start_time, 'to':duration/3, 'ranges':[range1], 'stepmode':'fixed'}, # # {'from':duration/3, 'to':2*duration/3, 'ranges':[range2], 'stepmode':'fixed'}, # # {'from':2*duration/3, 'to':end_time, 'ranges':[range1], 'stepmode':'fixed'} # # ] range_supply1 = (10,50) range_supply2 = (25,35) range_supply3 = (10,50) range_supply4 = (20,60) supply_schedule = [ {'from':start_time, 'to':end_time, 'ranges':[range_supply1], 'stepmode':'fixed'}, #{'from':180, 'to':330, 'ranges':[range_supply2], 'stepmode':'fixed'}, #{'from':330, 'to':480, 'ranges':[range_supply3], 'stepmode':'fixed'}, #{'from':330, 'to':end_time, 'ranges':[range_supply3], 'stepmode':'fixed'}, ] range_demand1 = (10,50) range_demand2 = (10,50) range_demand3 = (25,35) range_demand4 = (20,60) demand_schedule = [ {'from':start_time, 'to':end_time, 'ranges':[range_demand1], 'stepmode':'fixed'}, #{'from':180, 'to':330, 'ranges':[range_demand2], 'stepmode':'fixed'}, #{'from':330, 'to':480, 'ranges':[range_demand3], 'stepmode':'fixed'}, #{'from':330, 'to':end_time, 'ranges':[range_demand3], 'stepmode':'fixed'}, ] order_sched = {'sup':supply_schedule, 'dem':demand_schedule, 'interval':30, 'timemode':'periodic'} # buyers_spec = [('AA',2),('SHVR',10),('ZIC',10),('ZIP',10)] # sellers_spec = buyers_spec # traders_spec = {'sellers':sellers_spec, 'buyers':buyers_spec} # # # run a sequence of trials, one session per trial # # n_trials = 10 # tdump=open('avg_balance.csv','w') # trial = 1 # if n_trials > 1: # dump_all = False # else: # dump_all = True # # while (trial<(n_trials+1)): # trial_id = 'trial%04d' % trial # market_session(trial_id, start_time, end_time, traders_spec, order_sched, tdump, False, True) # tdump.flush() # trial = trial + 1 # tdump.close() # # sys.exit('Done Now') # run a sequence of trials that exhaustively varies the ratio of four trader types # NB this has weakness of symmetric proportions on buyers/sellers -- combinatorics of varying that are quite nasty n_trader_types = 4 equal_ratio_n = 4 n_trials_per_ratio = 15 n_traders = n_trader_types * equal_ratio_n fname = '15_balances_withZIP_M1_periodic.csv' tdump = open(fname, 'w') min_n = 0 trialnumber = 1 tdump.write('%s, %s, ' % ('expid', 'time')) for f in range(4): tdump.write('%s, %s, %s, %s, ' % ('type', 'balance', 'number of traders', 'profit per trader')) tdump.write('\n'); # buyers_spec = [('GDX', 11), ('ZIP', 11)] # sellers_spec = buyers_spec # traders_spec = {'sellers':sellers_spec, 'buyers':buyers_spec} # print buyers_spec # trial = 1 # while trial <= n_trials_per_ratio: # trial_id = 'trial%07d' % trialnumber # market_session(trial_id, start_time, end_time, traders_spec, # order_sched, tdump, False, True) # tdump.flush() # trial = trial + 1 # trialnumber = trialnumber + 1 trdr_1_n = min_n while trdr_1_n <= n_traders: trdr_2_n = min_n while trdr_2_n <= n_traders - trdr_1_n: trdr_3_n = min_n while trdr_3_n <= n_traders - (trdr_1_n + trdr_2_n): trdr_4_n = n_traders - (trdr_1_n + trdr_2_n + trdr_3_n) if trdr_4_n >= min_n: buyers_spec = [('AA', trdr_1_n), ('GDX', trdr_2_n), ('ASAD', trdr_3_n), ('ZIP', trdr_4_n)] sellers_spec = buyers_spec traders_spec = {'sellers':sellers_spec, 'buyers':buyers_spec} # print buyers_spec trial = 1 while trial <= n_trials_per_ratio: trial_id = 'trial%07d' % trialnumber market_session(trial_id, start_time, end_time, traders_spec, order_sched, tdump, False, True) tdump.flush() trial = trial + 1 trialnumber = trialnumber + 1 trdr_3_n += 1 trdr_2_n += 1 trdr_1_n += 1 tdump.close() print trialnumber