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
}
}16659945523771665994552377
================================================
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\n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\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": [
""
]
},
{
"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