Factor order request processing into new func

basic_orders
Tyler Goodlet 2021-03-07 16:25:47 -05:00
parent e71bcb363c
commit 5deea50963
1 changed files with 149 additions and 129 deletions

View File

@ -448,6 +448,145 @@ async def process_broker_trades(
await ctx.send_yield(resp) await ctx.send_yield(resp)
async def process_order_cmds(
ctx: tractor.Context,
cmd_stream: 'tractor.ReceiveStream', # noqa
symbol: str,
feed: 'Feed', # noqa
client: 'Client', # noqa
dark_book: _DarkBook,
) -> None:
async for cmd in cmd_stream:
log.info(f'Received order cmd:\n{pformat(cmd)}')
action = cmd['action']
oid = cmd['oid']
brid = dark_book._broker2ems_ids.inverse.get(oid)
# TODO: can't wait for this stuff to land in 3.10
# https://www.python.org/dev/peps/pep-0636/#going-to-the-cloud-mappings
if action in ('cancel',):
# check for live-broker order
if brid:
log.info("Submitting cancel for live order")
await client.submit_cancel(reqid=brid)
# check for EMS active exec
else:
try:
dark_book.orders[symbol].pop(oid, None)
await ctx.send_yield({
'resp': 'dark_cancelled',
'oid': oid
})
except KeyError:
log.exception(f'No dark order for {symbol}?')
elif action in ('alert', 'buy', 'sell',):
sym = cmd['symbol']
trigger_price = cmd['price']
size = cmd['size']
brokers = cmd['brokers']
exec_mode = cmd['exec_mode']
broker = brokers[0]
last = dark_book.lasts[(broker, sym)]
if exec_mode == 'live' and action in ('buy', 'sell',):
# register broker id for ems id
order_id = await client.submit_limit(
oid=oid, # no ib support for oids...
# if this is None, creates a new order
# otherwise will modify any existing one
brid=brid,
symbol=sym,
action=action,
price=trigger_price,
size=size,
)
if brid:
assert dark_book._broker2ems_ids[brid] == oid
# if we already had a broker order id then
# this is likely an order update commmand.
log.info(f"Modifying order: {brid}")
else:
dark_book._broker2ems_ids[order_id] = oid
# XXX: the trades data broker response loop
# (``process_broker_trades()`` above) will
# handle sending the ems side acks back to
# the cmd sender from here
elif exec_mode in ('dark', 'paper') or (
action in ('alert')
):
# submit order to local EMS
# Auto-gen scanner predicate:
# we automatically figure out what the alert check
# condition should be based on the current first
# price received from the feed, instead of being
# like every other shitty tina platform that makes
# the user choose the predicate operator.
pred = mk_check(trigger_price, last)
min_tick = feed.symbols[sym].tick_size
if action == 'buy':
tickfilter = ('ask', 'last', 'trade')
percent_away = 0.005
# TODO: we probably need to scale this based
# on some near term historical spread
# measure?
abs_diff_away = 3 * min_tick
elif action == 'sell':
tickfilter = ('bid', 'last', 'trade')
percent_away = -0.005
abs_diff_away = -3 * min_tick
else: # alert
tickfilter = ('trade', 'utrade', 'last')
percent_away = 0
abs_diff_away = 0
# submit execution/order to EMS scan loop
# FYI: this may result in an override of an existing
# dark book entry if the order id already exists
dark_book.orders.setdefault(
sym, {}
)[oid] = (
pred,
tickfilter,
cmd,
percent_away,
abs_diff_away
)
# TODO: if the predicate resolves immediately send the
# execution to the broker asap? Or no?
# ack-response that order is live in EMS
await ctx.send_yield({
'resp': 'dark_submitted',
'oid': oid
})
@tractor.stream @tractor.stream
async def _emsd_main( async def _emsd_main(
ctx: tractor.Context, ctx: tractor.Context,
@ -513,133 +652,14 @@ async def _emsd_main(
# acting as an EMS client and will submit orders) to # acting as an EMS client and will submit orders) to
# receive requests pushed over a tractor stream # receive requests pushed over a tractor stream
# using (for now) an async generator. # using (for now) an async generator.
async for cmd in await portal.run(send_order_cmds): order_stream = await portal.run(send_order_cmds)
log.info(f'Received order cmd:\n{pformat(cmd)}') # start inbound order request processing
await process_order_cmds(
action = cmd['action'] ctx,
oid = cmd['oid'] order_stream,
symbol,
brid = dark_book._broker2ems_ids.inverse.get(oid) feed,
client,
# TODO: can't wait for this stuff to land in 3.10 dark_book,
# https://www.python.org/dev/peps/pep-0636/#going-to-the-cloud-mappings )
if action in ('cancel',):
# check for live-broker order
if brid:
log.info("Submitting cancel for live order")
await client.submit_cancel(reqid=brid)
# check for EMS active exec
else:
try:
dark_book.orders[symbol].pop(oid, None)
await ctx.send_yield({
'resp': 'dark_cancelled',
'oid': oid
})
except KeyError:
log.exception(f'No dark order for {symbol}?')
elif action in ('alert', 'buy', 'sell',):
sym = cmd['symbol']
trigger_price = cmd['price']
size = cmd['size']
brokers = cmd['brokers']
exec_mode = cmd.get('exec_mode', _mode)
broker = brokers[0]
last = dark_book.lasts[(broker, sym)]
if exec_mode == 'live' and action in ('buy', 'sell',):
# register broker id for ems id
order_id = await client.submit_limit(
oid=oid, # no ib support for oids...
# if this is None, creates a new order
# otherwise will modify any existing one
brid=brid,
symbol=sym,
action=action,
price=trigger_price,
size=size,
)
if brid:
assert dark_book._broker2ems_ids[brid] == oid
# if we already had a broker order id then
# this is likely an order update commmand.
log.info(f"Modifying order: {brid}")
else:
dark_book._broker2ems_ids[order_id] = oid
# XXX: the trades data broker response loop
# (``process_broker_trades()`` above) will
# handle sending the ems side acks back to
# the cmd sender from here
elif exec_mode in ('dark', 'paper') or (
action in ('alert')
):
# submit order to local EMS
# Auto-gen scanner predicate:
# we automatically figure out what the alert check
# condition should be based on the current first
# price received from the feed, instead of being
# like every other shitty tina platform that makes
# the user choose the predicate operator.
pred = mk_check(trigger_price, last)
min_tick = feed.symbols[sym].tick_size
if action == 'buy':
tickfilter = ('ask', 'last', 'trade')
percent_away = 0.005
# TODO: we probably need to scale this based
# on some near term historical spread
# measure?
abs_diff_away = 3 * min_tick
elif action == 'sell':
tickfilter = ('bid', 'last', 'trade')
percent_away = -0.005
abs_diff_away = -3 * min_tick
else: # alert
tickfilter = ('trade', 'utrade', 'last')
percent_away = 0
abs_diff_away = 0
# submit execution/order to EMS scan loop
# FYI: this may result in an override of an existing
# dark book entry if the order id already exists
dark_book.orders.setdefault(
sym, {}
)[oid] = (
pred,
tickfilter,
cmd,
percent_away,
abs_diff_away
)
# TODO: if the predicate resolves immediately send the
# execution to the broker asap? Or no?
# ack-response that order is live in EMS
await ctx.send_yield({
'resp': 'dark_submitted',
'oid': oid
})
# continue and wait on next order cmd