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