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.ordermodepps_backup
parent
5a303ede1e
commit
581134f39c
|
@ -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()
|
|
||||||
|
|
Loading…
Reference in New Issue