Use `MktPair.price_tick: Decimal` in dark triggers

This was actually incorrect prior, we were rounding triggered limit
orders with the `.size_tick` value's digits when we should have been
using the `.price_tick` (facepalm). So fix that and compute the rounding
number of digits (as passed to the round(<value>, ndigits=<here>)`
builtin) and store it in the `DarkBook.triggers` tuples so that at
trigger/match time the round call is done *just prior* to msg send to
`brokerd` given the last known live L1 queue price.
basic_buy_bot
Tyler Goodlet 2023-06-19 13:05:34 -04:00
parent a149e71fb1
commit 80461e18a5
1 changed files with 41 additions and 25 deletions

View File

@ -24,6 +24,7 @@ from collections import (
# ChainMap,
)
from contextlib import asynccontextmanager as acm
from decimal import Decimal
from math import isnan
from pprint import pformat
import time
@ -49,7 +50,7 @@ from ._util import (
from ..data._normalize import iterticks
from ..accounting._mktinfo import (
unpack_fqme,
float_digits,
dec_digits,
)
from ..ui._notify import notify_from_ems_status_msg
from ..data.types import Struct
@ -130,11 +131,16 @@ class DarkBook(Struct):
triggers: dict[
str, # symbol
dict[
str, # uuid
str, # uuid for triggerable execution
tuple[
Callable[[float], bool], # predicate
str, # name
dict, # cmd / msg type
tuple[str, ...], # tickfilter
dict | Order, # cmd / msg type
# live submission constraint parameters
float, # percent_away max price diff
float, # abs_diff_away max price diff
int, # min_tick_digits to round the clearable price
]
]
] = {}
@ -177,7 +183,8 @@ async def clear_dark_triggers(
async for quotes in quote_stream:
# start = time.time()
for sym, quote in quotes.items():
execs = book.triggers.get(sym, {})
# TODO: make this a msg-compat struct
execs: tuple = book.triggers.get(sym, {})
for tick in iterticks(
quote,
# dark order price filter(s)
@ -200,7 +207,8 @@ async def clear_dark_triggers(
# TODO: send this msg instead?
cmd,
percent_away,
abs_diff_away
abs_diff_away,
price_tick_digits,
) in (
tuple(execs.items())
):
@ -233,8 +241,11 @@ async def clear_dark_triggers(
size=size,
):
bfqme: str = symbol.replace(f'.{broker}', '')
submit_price = price + abs_diff_away
resp = 'triggered' # hidden on client-side
submit_price: float = round(
price + abs_diff_away,
ndigits=price_tick_digits,
)
resp: str = 'triggered' # hidden on client-side
log.info(
f'Dark order triggered for price {price}\n'
@ -264,11 +275,11 @@ async def clear_dark_triggers(
)
# remove exec-condition from set
log.info(f'removing pred for {oid}')
pred = execs.pop(oid, None)
if not pred:
log.info(f'Removing trigger for {oid}')
trigger: tuple | None = execs.pop(oid, None)
if not trigger:
log.warning(
f'pred for {oid} was already removed!?'
f'trigger for {oid} was already removed!?'
)
# update actives
@ -1215,14 +1226,15 @@ async def process_client_order_cmds(
and status.resp == 'dark_open'
):
# remove from dark book clearing
entry = dark_book.triggers[fqme].pop(oid, None)
entry: tuple | None = dark_book.triggers[fqme].pop(oid, None)
if entry:
(
pred,
tickfilter,
cmd,
percent_away,
abs_diff_away
abs_diff_away,
min_tick_digits,
) = entry
# tell client side that we've cancelled the
@ -1357,33 +1369,36 @@ async def process_client_order_cmds(
# TODO: make this configurable from our top level
# config, prolly in a .clearing` section?
spread_slap: float = 5
min_tick = float(flume.mkt.size_tick)
min_tick_digits = float_digits(min_tick)
min_tick = Decimal(flume.mkt.price_tick)
min_tick_digits: int = dec_digits(min_tick)
tickfilter: tuple[str, ...]
percent_away: float
if action == 'buy':
tickfilter = ('ask', 'last', 'trade')
percent_away = 0.005
percent_away: float = 0.005
# TODO: we probably need to scale this based
# on some near term historical spread
# measure?
abs_diff_away = round(
abs_diff_away = float(round(
spread_slap * min_tick,
ndigits=min_tick_digits,
)
))
elif action == 'sell':
tickfilter = ('bid', 'last', 'trade')
percent_away = -0.005
abs_diff_away = round(
percent_away: float = -0.005
abs_diff_away: float = float(round(
-spread_slap * min_tick,
ndigits=min_tick_digits,
)
))
else: # alert
tickfilter = ('trade', 'utrade', 'last')
percent_away = 0
abs_diff_away = 0
percent_away: float = 0
abs_diff_away: float = 0
# submit execution/order to EMS scan loop
# NOTE: this may result in an override of an existing
@ -1395,7 +1410,8 @@ async def process_client_order_cmds(
tickfilter,
req,
percent_away,
abs_diff_away
abs_diff_away,
min_tick_digits,
)
resp = 'dark_open'