Add a super basic "order bot" example B)
Shows how to boot the piker runtime, submit an order to the ems, cancel said order right away. NOTE, this uses piker's built in paper engine but can be easily tweaked to use a live backend at the user's whim.basic_buy_bot
parent
f2fff5a5fa
commit
63a6c6efde
|
@ -0,0 +1,143 @@
|
|||
import tractor
|
||||
import trio
|
||||
from uuid import uuid4
|
||||
|
||||
from piker.clearing import (
|
||||
open_ems,
|
||||
OrderClient,
|
||||
)
|
||||
|
||||
# TODO: we should probably expose these top level in this subsys?
|
||||
from piker.clearing._messages import (
|
||||
Order,
|
||||
Status,
|
||||
BrokerdPosition,
|
||||
)
|
||||
|
||||
|
||||
async def wait_for_order_status(
|
||||
trades_stream: tractor.MsgStream,
|
||||
oid: str,
|
||||
expect_status: str,
|
||||
|
||||
) -> tuple[
|
||||
list[Status],
|
||||
list[BrokerdPosition],
|
||||
]:
|
||||
'''
|
||||
Wait for a specific order status for a given dialog, return msg flow
|
||||
up to that msg and any position update msgs in a tuple.
|
||||
|
||||
'''
|
||||
# Wait for position message before moving on to verify flow(s)
|
||||
# for the multi-order position entry/exit.
|
||||
status_msgs: list[Status] = []
|
||||
pp_msgs: list[BrokerdPosition] = []
|
||||
|
||||
async for msg in trades_stream:
|
||||
match msg:
|
||||
case {'name': 'position'}:
|
||||
ppmsg = BrokerdPosition(**msg)
|
||||
pp_msgs.append(ppmsg)
|
||||
|
||||
case {
|
||||
'name': 'status',
|
||||
}:
|
||||
msg = Status(**msg)
|
||||
status_msgs.append(msg)
|
||||
|
||||
# if we get the status we expect then return all
|
||||
# collected msgs from the brokerd dialog up to the
|
||||
# exected msg B)
|
||||
if (
|
||||
msg.resp == expect_status
|
||||
and msg.oid == oid
|
||||
):
|
||||
return status_msgs, pp_msgs
|
||||
|
||||
|
||||
async def bot_main():
|
||||
'''
|
||||
Boot the piker runtime, open an ems connection, submit
|
||||
and process orders statuses in real-time.
|
||||
|
||||
'''
|
||||
from piker.service import maybe_open_pikerd
|
||||
|
||||
port: int = 6666 # non-default pikerd addr
|
||||
ll: str = 'info'
|
||||
reg_addr: tuple[str, int] = ('127.0.0.1', port)
|
||||
|
||||
# open an order ctl client
|
||||
client: OrderClient
|
||||
trades_stream: tractor.MsgStream
|
||||
accounts: list[str]
|
||||
|
||||
fqme: str = 'btcusdt.usdtm.perp.binance'
|
||||
|
||||
async with (
|
||||
|
||||
# init and sync actor-service runtime
|
||||
maybe_open_pikerd(
|
||||
registry_addr=reg_addr,
|
||||
loglevel=ll,
|
||||
debug_mode=False,
|
||||
|
||||
),
|
||||
tractor.wait_for_actor(
|
||||
'pikerd',
|
||||
arbiter_sockaddr=reg_addr,
|
||||
),
|
||||
|
||||
open_ems(
|
||||
fqme,
|
||||
mode='paper', # {'live', 'paper'}
|
||||
loglevel=ll,
|
||||
|
||||
) as (
|
||||
client, # OrderClient
|
||||
trades_stream, # tractor.MsgStream startup_pps,
|
||||
_, # positions
|
||||
accounts,
|
||||
_, # dialogs
|
||||
)
|
||||
):
|
||||
print(f'Loaded binance accounts: {accounts}')
|
||||
|
||||
price: float = 10e3 # non-clearable
|
||||
size: float = 0.01
|
||||
oid: str = str(uuid4())
|
||||
|
||||
order = Order(
|
||||
exec_mode='live', # {'dark', 'live', 'alert'}
|
||||
action='buy', # TODO: remove this from our schema?
|
||||
oid=oid,
|
||||
account='paper', # use binance.usdtm for binance futes
|
||||
size=size,
|
||||
symbol=fqme,
|
||||
price=price,
|
||||
brokers=['binance'],
|
||||
)
|
||||
await client.send(order)
|
||||
|
||||
msgs, pps = await wait_for_order_status(
|
||||
trades_stream,
|
||||
oid,
|
||||
'open'
|
||||
)
|
||||
|
||||
assert not pps
|
||||
assert msgs[-1].oid == oid
|
||||
|
||||
# cancel the open order
|
||||
await client.cancel(oid)
|
||||
msgs, pps = await wait_for_order_status(
|
||||
trades_stream,
|
||||
oid,
|
||||
'canceled'
|
||||
)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
trio.run(bot_main)
|
Loading…
Reference in New Issue