Support clearing ticks ('last' & 'trade') fills
Previously we only simulated paper engine fills when the data feed provide L1 queue-levels matched an execution. This patch add further support for clear-level matches when there are real live clears on the data feed that are faster/not synced with the L1 (aka usually during periods of HFT). The solution was to simply iterate the interleaved paper book entries on both sides for said tick types and instead yield side-specific predicate per entry.size_in_shm_token
parent
fe3d0c6fdd
commit
4cedfedc21
|
@ -22,6 +22,7 @@ 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
|
||||||
|
import itertools
|
||||||
import time
|
import time
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
|
@ -324,6 +325,26 @@ async def simulate_fills(
|
||||||
# dark order price filter(s)
|
# dark order price filter(s)
|
||||||
types=('ask', 'bid', 'trade', 'last')
|
types=('ask', 'bid', 'trade', 'last')
|
||||||
):
|
):
|
||||||
|
tick_price = tick['price']
|
||||||
|
|
||||||
|
buys: bidict[str, tuple] = client._buys[sym]
|
||||||
|
iter_buys = reversed(sorted(
|
||||||
|
buys.values(),
|
||||||
|
key=itemgetter(0),
|
||||||
|
))
|
||||||
|
|
||||||
|
def sell_on_bid(our_price):
|
||||||
|
return tick_price <= our_price
|
||||||
|
|
||||||
|
sells: bidict[str, tuple] = client._sells[sym]
|
||||||
|
iter_sells = sorted(
|
||||||
|
sells.values(),
|
||||||
|
key=itemgetter(0)
|
||||||
|
)
|
||||||
|
|
||||||
|
def buy_on_ask(our_price):
|
||||||
|
return tick_price >= our_price
|
||||||
|
|
||||||
match tick:
|
match tick:
|
||||||
case {
|
case {
|
||||||
'price': tick_price,
|
'price': tick_price,
|
||||||
|
@ -335,13 +356,10 @@ async def simulate_fills(
|
||||||
tick.get('size', client.last_ask[1]),
|
tick.get('size', client.last_ask[1]),
|
||||||
)
|
)
|
||||||
|
|
||||||
# orders = client._buys.get(sym, {})
|
iter_entries = zip(
|
||||||
orders = client._buys[sym]
|
iter_buys,
|
||||||
book_sequence = reversed(
|
itertools.repeat(sell_on_bid)
|
||||||
sorted(orders.values(), key=itemgetter(0)))
|
)
|
||||||
|
|
||||||
def pred(our_price):
|
|
||||||
return tick_price <= our_price
|
|
||||||
|
|
||||||
case {
|
case {
|
||||||
'price': tick_price,
|
'price': tick_price,
|
||||||
|
@ -352,40 +370,48 @@ 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[sym]
|
|
||||||
book_sequence = sorted(
|
|
||||||
orders.values(),
|
|
||||||
key=itemgetter(0)
|
|
||||||
)
|
|
||||||
|
|
||||||
def pred(our_price):
|
iter_entries = zip(
|
||||||
return tick_price >= our_price
|
iter_sells,
|
||||||
|
itertools.repeat(buy_on_ask)
|
||||||
|
)
|
||||||
|
|
||||||
case {
|
case {
|
||||||
'price': tick_price,
|
'price': tick_price,
|
||||||
'type': ('trade' | 'last'),
|
'type': ('trade' | 'last'),
|
||||||
}:
|
}:
|
||||||
# TODO: simulate actual book queues and our
|
# in the clearing price / last price case we
|
||||||
# orders place in it, might require full L2
|
# want to iterate both sides of our book for
|
||||||
# data?
|
# clears since we don't know which direction the
|
||||||
continue
|
# price is going to move (especially with HFT)
|
||||||
|
# and thus we simply interleave both sides (buys
|
||||||
|
# and sells) until one side clears and then
|
||||||
|
# break until the next tick?
|
||||||
|
def interleave():
|
||||||
|
for pair in zip(
|
||||||
|
iter_buys,
|
||||||
|
iter_sells,
|
||||||
|
):
|
||||||
|
for order_info, pred in zip(
|
||||||
|
pair,
|
||||||
|
itertools.cycle([sell_on_bid, buy_on_ask]),
|
||||||
|
):
|
||||||
|
yield order_info, pred
|
||||||
|
|
||||||
# iterate book prices descending
|
iter_entries = interleave()
|
||||||
# for oid, our_price in book_sequence:
|
|
||||||
# print(tick)
|
# iterate all potentially clearable book prices
|
||||||
# print((
|
# in FIFO order per side.
|
||||||
# sym,
|
for order_info, pred in iter_entries:
|
||||||
# list(book_sequence),
|
|
||||||
# client._buys,
|
|
||||||
# client._sells,
|
|
||||||
# ))
|
|
||||||
for order_info in book_sequence:
|
|
||||||
(our_price, size, reqid, action) = order_info
|
(our_price, size, reqid, action) = order_info
|
||||||
|
|
||||||
clearable = pred(our_price)
|
clearable = pred(our_price)
|
||||||
if clearable:
|
if clearable:
|
||||||
# retreive order info
|
# pop and retreive order info
|
||||||
oid = orders.inverse.pop(order_info)
|
oid = {
|
||||||
|
'buy': buys,
|
||||||
|
'sell': sells
|
||||||
|
}[action].inverse.pop(order_info)
|
||||||
|
|
||||||
# clearing price would have filled entirely
|
# clearing price would have filled entirely
|
||||||
await client.fake_fill(
|
await client.fake_fill(
|
||||||
|
@ -397,9 +423,6 @@ async def simulate_fills(
|
||||||
reqid=reqid,
|
reqid=reqid,
|
||||||
oid=oid,
|
oid=oid,
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
# prices are iterated in sorted order so we're done
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
async def handle_order_requests(
|
async def handle_order_requests(
|
||||||
|
|
Loading…
Reference in New Issue