Add per session paper position tracking

Generate and maintain position messages in the paper engine for each
`pikerd` session. We no longer tear down the engine on each client
disconnect. Ensure -ve size on sells to make the math work.
pause_feeds_on_sym_switch
Tyler Goodlet 2021-07-12 08:22:15 -04:00
parent 908678da84
commit 449c4210e4
1 changed files with 48 additions and 8 deletions

View File

@ -35,7 +35,7 @@ from ..data._normalize import iterticks
from ..log import get_logger from ..log import get_logger
from ._messages import ( from ._messages import (
BrokerdCancel, BrokerdOrder, BrokerdOrderAck, BrokerdStatus, BrokerdCancel, BrokerdOrder, BrokerdOrderAck, BrokerdStatus,
BrokerdFill, BrokerdFill, BrokerdPosition,
) )
@ -60,6 +60,7 @@ class PaperBoi:
_buys: bidict _buys: bidict
_sells: bidict _sells: bidict
_reqids: bidict _reqids: bidict
_positions: dict[str, BrokerdPosition]
# init edge case L1 spread # init edge case L1 spread
last_ask: Tuple[float, float] = (float('inf'), 0) # price, size last_ask: Tuple[float, float] = (float('inf'), 0) # price, size
@ -101,6 +102,9 @@ class PaperBoi:
# in the broker trades event processing loop # in the broker trades event processing loop
await trio.sleep(0.05) await trio.sleep(0.05)
if action == 'sell':
size = -size
msg = BrokerdStatus( msg = BrokerdStatus(
status='submitted', status='submitted',
reqid=reqid, reqid=reqid,
@ -118,7 +122,7 @@ class PaperBoi:
) or ( ) or (
action == 'sell' and (clear_price := self.last_bid[0]) >= price action == 'sell' and (clear_price := self.last_bid[0]) >= price
): ):
await self.fake_fill(clear_price, size, action, reqid, oid) await self.fake_fill(symbol, clear_price, size, action, reqid, oid)
else: else:
# register this submissions as a paper live order # register this submissions as a paper live order
@ -170,6 +174,8 @@ class PaperBoi:
async def fake_fill( async def fake_fill(
self, self,
symbol: str,
price: float, price: float,
size: float, size: float,
action: str, # one of {'buy', 'sell'} action: str, # one of {'buy', 'sell'}
@ -232,6 +238,39 @@ class PaperBoi:
) )
await self.ems_trades_stream.send(msg.dict()) await self.ems_trades_stream.send(msg.dict())
# lookup any existing position
token = f'{symbol}.{self.broker}'
pp_msg = self._positions.setdefault(
token,
BrokerdPosition(
broker=self.broker,
account='paper',
symbol=symbol,
# TODO: we need to look up the asset currency from
# broker info. i guess for crypto this can be
# inferred from the pair?
currency='',
size=0.0,
avg_price=0,
)
)
# "avg position price" calcs
# TODO: eventually it'd be nice to have a small set of routines
# to do this stuff from a sequence of cleared orders to enable
# so called "contextual positions".
new_size = size + pp_msg.size
if new_size != 0:
pp_msg.avg_price = (size*price + pp_msg.avg_price) / new_size
else:
pp_msg.avg_price = 0
pp_msg.size = new_size
await self.ems_trades_stream.send(pp_msg.dict())
async def simulate_fills( async def simulate_fills(
quote_stream: 'tractor.ReceiveStream', # noqa quote_stream: 'tractor.ReceiveStream', # noqa
@ -255,6 +294,7 @@ async def simulate_fills(
# this stream may eventually contain multiple symbols # this stream may eventually contain multiple symbols
async for quotes in quote_stream: async for quotes in quote_stream:
for sym, quote in quotes.items(): for sym, quote in quotes.items():
for tick in iterticks( for tick in iterticks(
@ -274,6 +314,7 @@ async def simulate_fills(
) )
orders = client._buys.get(sym, {}) orders = client._buys.get(sym, {})
book_sequence = reversed( book_sequence = reversed(
sorted(orders.keys(), key=itemgetter(1))) sorted(orders.keys(), key=itemgetter(1)))
@ -307,6 +348,7 @@ async def simulate_fills(
# clearing price would have filled entirely # clearing price would have filled entirely
await client.fake_fill( await client.fake_fill(
symbol=sym,
# todo slippage to determine fill price # todo slippage to determine fill price
price=tick_price, price=tick_price,
size=size, size=size,
@ -411,6 +453,9 @@ async def trades_dialogue(
_sells={}, _sells={},
_reqids={}, _reqids={},
# TODO: load paper positions from ``positions.toml``
_positions={},
) )
n.start_soon(handle_order_requests, client, ems_stream) n.start_soon(handle_order_requests, client, ems_stream)
@ -452,10 +497,5 @@ async def open_paperboi(
loglevel=loglevel, loglevel=loglevel,
) as (ctx, first): ) as (ctx, first):
try:
yield ctx, first
finally: yield ctx, first
# be sure to tear down the paper service on exit
with trio.CancelScope(shield=True):
await portal.cancel_actor()