Handle too-fast-edits with `defaultdict[str, bidict[str, tuple]]`

Not entirely sure why this all of a sudden became a problem but it seems
price changes on order edits were sometimes resulting in key errors when
modifying paper book entries quickly. This changes the implementation to
not care about matching the last price when keying/popping old orders
and use `bidict`s to more easily pop cleared orders in the paper loop.
size_in_shm_token
Tyler Goodlet 2022-08-23 21:08:37 -04:00
parent 9200e8da57
commit fe3d0c6fdd
1 changed files with 36 additions and 27 deletions

View File

@ -18,6 +18,7 @@
Fake trading for forward testing. Fake trading for forward testing.
""" """
from collections import defaultdict
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from datetime import datetime from datetime import datetime
from operator import itemgetter from operator import itemgetter
@ -72,8 +73,8 @@ class PaperBoi(Struct):
# map of paper "live" orders which be used # map of paper "live" orders which be used
# to simulate fills based on paper engine settings # to simulate fills based on paper engine settings
_buys: dict _buys: defaultdict[str, bidict]
_sells: dict _sells: defaultdict[str, bidict]
_reqids: bidict _reqids: bidict
_positions: dict[str, Position] _positions: dict[str, Position]
_trade_ledger: dict[str, Any] _trade_ledger: dict[str, Any]
@ -166,10 +167,10 @@ class PaperBoi(Struct):
if is_modify: if is_modify:
# remove any existing order for the old price # remove any existing order for the old price
orders[symbol].pop((oid, old_price)) orders[symbol].pop(oid)
# buys/sells: (symbol -> (price -> order)) # buys/sells: {symbol -> bidict[oid, (<price data>)]}
orders.setdefault(symbol, {})[(oid, price)] = (size, reqid, action) orders[symbol][oid] = (price, size, reqid, action)
return reqid return reqid
@ -191,7 +192,6 @@ class PaperBoi(Struct):
msg = BrokerdStatus( msg = BrokerdStatus(
status='canceled', status='canceled',
# account=f'paper_{self.broker}',
account='paper', account='paper',
reqid=reqid, reqid=reqid,
time_ns=time.time_ns(), time_ns=time.time_ns(),
@ -335,9 +335,10 @@ async def simulate_fills(
tick.get('size', client.last_ask[1]), tick.get('size', client.last_ask[1]),
) )
orders = client._buys.get(sym, {}) # orders = client._buys.get(sym, {})
orders = client._buys[sym]
book_sequence = reversed( book_sequence = reversed(
sorted(orders.keys(), key=itemgetter(1))) sorted(orders.values(), key=itemgetter(0)))
def pred(our_price): def pred(our_price):
return tick_price <= our_price return tick_price <= our_price
@ -351,10 +352,11 @@ async def simulate_fills(
tick_price, tick_price,
tick.get('size', client.last_bid[1]), tick.get('size', client.last_bid[1]),
) )
orders = client._sells.get(sym, {}) # orders = client._sells.get(sym, {})
orders = client._sells[sym]
book_sequence = sorted( book_sequence = sorted(
orders.keys(), orders.values(),
key=itemgetter(1) key=itemgetter(0)
) )
def pred(our_price): def pred(our_price):
@ -370,13 +372,20 @@ async def simulate_fills(
continue continue
# iterate book prices descending # iterate book prices descending
for oid, our_price in book_sequence: # for oid, our_price in book_sequence:
# print(tick) # print(tick)
# print((sym, list(book_sequence), client._buys, client._sells)) # print((
# sym,
# list(book_sequence),
# client._buys,
# client._sells,
# ))
for order_info in book_sequence:
(our_price, size, reqid, action) = order_info
clearable = pred(our_price) clearable = pred(our_price)
if clearable: if clearable:
# retreive order info # retreive order info
(size, reqid, action) = orders.pop((oid, our_price)) oid = orders.inverse.pop(order_info)
# clearing price would have filled entirely # clearing price would have filled entirely
await client.fake_fill( await client.fake_fill(
@ -454,20 +463,20 @@ async def handle_order_requests(
_reqids: bidict[str, tuple] = {} _reqids: bidict[str, tuple] = {}
_buys: dict[ _buys: defaultdict[
str, str, # symbol
dict[ bidict[
tuple[str, float], str, # oid
tuple[float, str, str], tuple[float, float, str, str], # order info
] ]
] = {} ] = defaultdict(bidict)
_sells: dict[ _sells: defaultdict[
str, str, # symbol
dict[ bidict[
tuple[str, float], str, # oid
tuple[float, str, str], tuple[float, float, str, str], # order info
] ]
] = {} ] = defaultdict(bidict)
_positions: dict[str, Position] = {} _positions: dict[str, Position] = {}