basic bot: add real-time price trailer (task) that keeps bid price 0.0005% below last clear value

basic_buy_bot
Tyler Goodlet 2023-06-26 15:22:51 -04:00
parent 57399e4f5d
commit 46b22958f0
1 changed files with 93 additions and 11 deletions

View File

@ -1,18 +1,29 @@
from pprint import pformat
from functools import partial
from decimal import Decimal
from typing import Callable
import tractor import tractor
import trio import trio
from uuid import uuid4 from uuid import uuid4
from piker.accounting import dec_digits
from piker.clearing import ( from piker.clearing import (
open_ems, open_ems,
OrderClient, OrderClient,
) )
# TODO: we should probably expose these top level in this subsys? # TODO: we should probably expose these top level in this subsys?
from piker.clearing._messages import ( from piker.clearing._messages import (
Order, Order,
Status, Status,
BrokerdPosition, BrokerdPosition,
) )
from piker.data import (
Flume,
open_feed,
Feed,
ShmArray,
)
async def wait_for_order_status( async def wait_for_order_status(
@ -66,25 +77,24 @@ async def bot_main():
ll: str = 'info' ll: str = 'info'
# open an order ctl client # open an order ctl client, live data feed, trio nursery for
# spawning an order trailer task
client: OrderClient client: OrderClient
trades_stream: tractor.MsgStream trades_stream: tractor.MsgStream
feed: Feed
accounts: list[str] accounts: list[str]
fqme: str = 'btcusdt.usdtm.perp.binance' fqme: str = 'btcusdt.usdtm.perp.binance'
async with ( async with (
# TODO: do this implicitly inside `open_ems()` ep below?
# init and sync actor-service runtime # init and sync actor-service runtime
maybe_open_pikerd( maybe_open_pikerd(
loglevel=ll, loglevel=ll,
debug_mode=False, debug_mode=True,
), ),
tractor.wait_for_actor(
'pikerd',
),
open_ems( open_ems(
fqme, fqme,
mode='paper', # {'live', 'paper'} mode='paper', # {'live', 'paper'}
@ -97,14 +107,81 @@ async def bot_main():
_, # positions _, # positions
accounts, accounts,
_, # dialogs _, # dialogs
) ),
open_feed(
fqmes=[fqme],
loglevel=ll,
# TODO: if you want to throttle via downsampling
# how many tick updates your feed received on
# quote streams B)
# tick_throttle=10,
) as feed,
trio.open_nursery() as tn,
): ):
print(f'Loaded binance accounts: {accounts}') print(f'Loaded binance accounts: {accounts}')
price: float = 30e3 # non-clearable flume: Flume = feed.flumes[fqme]
size: float = 0.01 min_tick = Decimal(flume.mkt.price_tick)
oid: str = str(uuid4()) min_tick_digits: int = dec_digits(min_tick)
price_round: Callable = partial(
round,
ndigits=min_tick_digits,
)
quote_stream: trio.abc.ReceiveChannel = feed.streams['binance']
clear_margin: float = 0.9995
async def trailer(
order: Order,
):
# ref shm OHLCV array
s_shm: ShmArray = flume.rt_shm
m_shm: ShmArray = flume.hist_shm
# NOTE: if you wanted to frame ticks by type like the
# the quote throttler does.
# from piker.data._sampling import frame_ticks
async for quotes in quote_stream:
for fqme, quote in quotes.items():
for tick in quote.get('ticks', ()):
print(
f'{fqme} ticks:\n{pformat(tick)}\n\n'
f'last 1s OHLC:\n{s_shm.array[-1]}\n'
f'last 1m OHLC:\n{m_shm.array[-1]}\n'
)
# always keep live limit 2% below last
# clearing price
if tick['type'] == 'trade':
await client.update(
uuid=order.oid,
price=price_round(
clear_margin
*
tick['price']
),
)
msgs, pps = await wait_for_order_status(
trades_stream,
oid,
'open'
)
# setup order dialog via first msg
size: float = 0.01
price: float = price_round(
clear_margin
*
flume.first_quote['last']
)
oid: str = str(uuid4())
order = Order( order = Order(
exec_mode='live', # {'dark', 'live', 'alert'} exec_mode='live', # {'dark', 'live', 'alert'}
action='buy', # TODO: remove this from our schema? action='buy', # TODO: remove this from our schema?
@ -116,6 +193,7 @@ async def bot_main():
price=price, price=price,
brokers=['binance'], brokers=['binance'],
) )
await client.send(order) await client.send(order)
msgs, pps = await wait_for_order_status( msgs, pps = await wait_for_order_status(
@ -127,6 +205,9 @@ async def bot_main():
assert not pps assert not pps
assert msgs[-1].oid == oid assert msgs[-1].oid == oid
# start "trailer task" which tracks rt quote stream
tn.start_soon(trailer, order)
try: try:
# wait for ctl-c from user.. # wait for ctl-c from user..
await trio.sleep_forever() await trio.sleep_forever()
@ -138,6 +219,7 @@ async def bot_main():
oid, oid,
'canceled' 'canceled'
) )
raise
if __name__ == '__main__': if __name__ == '__main__':