Adjust paper-engine to use `Transaction` for pps updates

ppu_history
Tyler Goodlet 2022-07-27 10:28:23 -04:00
parent 7cbdc6a246
commit a0c238daa7
1 changed files with 80 additions and 53 deletions

View File

@ -22,17 +22,25 @@ from contextlib import asynccontextmanager
from datetime import datetime from datetime import datetime
from operator import itemgetter from operator import itemgetter
import time import time
from typing import Tuple, Optional, Callable from typing import (
Any,
Optional,
Callable,
)
import uuid import uuid
from bidict import bidict from bidict import bidict
import pendulum
import trio import trio
import tractor import tractor
from dataclasses import dataclass from dataclasses import dataclass
from .. import data from .. import data
from ..data._source import Symbol from ..data._source import Symbol
from ..pp import Position from ..pp import (
Position,
Transaction,
)
from ..data._normalize import iterticks from ..data._normalize import iterticks
from ..data._source import unpack_fqsn from ..data._source import unpack_fqsn
from ..log import get_logger from ..log import get_logger
@ -63,11 +71,12 @@ class PaperBoi:
_buys: bidict _buys: bidict
_sells: bidict _sells: bidict
_reqids: bidict _reqids: bidict
_positions: dict[str, BrokerdPosition] _positions: dict[str, Position]
_trade_ledger: dict[str, Any]
# 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
last_bid: Tuple[float, float] = (0, 0) last_bid: tuple[float, float] = (0, 0)
async def submit_limit( async def submit_limit(
self, self,
@ -77,22 +86,23 @@ class PaperBoi:
action: str, action: str,
size: float, size: float,
reqid: Optional[str], reqid: Optional[str],
) -> int: ) -> int:
"""Place an order and return integer request id provided by client. '''
Place an order and return integer request id provided by client.
""" '''
is_modify: bool = False is_modify: bool = False
if reqid is None:
reqid = str(uuid.uuid4())
else: entry = self._reqids.get(reqid)
if entry:
# order is already existing, this is a modify # order is already existing, this is a modify
(oid, symbol, action, old_price) = self._reqids[reqid] (oid, symbol, action, old_price) = entry
assert old_price != price assert old_price != price
is_modify = True is_modify = True
else:
# register order internally # register order internally
self._reqids[reqid] = (oid, symbol, action, price) self._reqids[reqid] = (oid, symbol, action, price)
if action == 'alert': if action == 'alert':
# bypass all fill simulation # bypass all fill simulation
@ -197,16 +207,15 @@ class PaperBoi:
""" """
# TODO: net latency model # TODO: net latency model
await trio.sleep(0.05) await trio.sleep(0.05)
fill_time_ns = time.time_ns()
fill_time_s = time.time()
msg = BrokerdFill( fill_msg = BrokerdFill(
reqid=reqid, reqid=reqid,
time_ns=time.time_ns(), time_ns=fill_time_ns,
action=action, action=action,
size=size, size=size,
price=price, price=price,
broker_time=datetime.now().timestamp(), broker_time=datetime.now().timestamp(),
broker_details={ broker_details={
'paper_info': { 'paper_info': {
@ -216,7 +225,9 @@ class PaperBoi:
'name': self.broker + '_paper', 'name': self.broker + '_paper',
}, },
) )
await self.ems_trades_stream.send(msg) await self.ems_trades_stream.send(fill_msg)
self._trade_ledger.update(fill_msg.to_dict())
if order_complete: if order_complete:
@ -243,29 +254,37 @@ class PaperBoi:
# lookup any existing position # lookup any existing position
token = f'{symbol}.{self.broker}' token = f'{symbol}.{self.broker}'
pp_msg = self._positions.setdefault( pp = self._positions.setdefault(
token, token,
BrokerdPosition( Position(
broker=self.broker, Symbol(key=symbol),
account='paper', size=size,
symbol=symbol, be_price=price,
# TODO: we need to look up the asset currency from bsuid=symbol,
# broker info. i guess for crypto this can be
# inferred from the pair?
currency='',
size=0.0,
avg_price=0,
) )
) )
t = Transaction(
# delegate update to `.pp.Position.lifo_update()` fqsn=symbol,
pp = Position( tid=oid,
Symbol(key=symbol), size=size,
size=pp_msg.size, price=price,
be_price=pp_msg.avg_price, cost=1., # todo cost model
dt=pendulum.from_timestamp(fill_time_s),
bsuid=symbol, bsuid=symbol,
) )
pp_msg.size, pp_msg.avg_price = pp.lifo_update(size, price) pp.add_clear(t)
pp_msg = 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=pp.size,
avg_price=pp.be_price,
)
await self.ems_trades_stream.send(pp_msg) await self.ems_trades_stream.send(pp_msg)
@ -273,6 +292,7 @@ class PaperBoi:
async def simulate_fills( async def simulate_fills(
quote_stream: 'tractor.ReceiveStream', # noqa quote_stream: 'tractor.ReceiveStream', # noqa
client: PaperBoi, client: PaperBoi,
) -> None: ) -> None:
# TODO: more machinery to better simulate real-world market things: # TODO: more machinery to better simulate real-world market things:
@ -389,6 +409,24 @@ async def handle_order_requests(
# validate # validate
order = BrokerdOrder(**request_msg) order = BrokerdOrder(**request_msg)
if order.reqid is None:
reqid = str(uuid.uuid4())
else:
reqid = order.reqid
# deliver ack that order has been submitted to broker routing
await ems_order_stream.send(
BrokerdOrderAck(
# ems order request id
oid=order.oid,
# broker specific request id
reqid=reqid,
)
)
# call our client api to submit the order # call our client api to submit the order
reqid = await client.submit_limit( reqid = await client.submit_limit(
@ -402,20 +440,7 @@ async def handle_order_requests(
# there is no existing order so ask the client to create # there is no existing order so ask the client to create
# a new one (which it seems to do by allocating an int # a new one (which it seems to do by allocating an int
# counter - collision prone..) # counter - collision prone..)
reqid=order.reqid, reqid=reqid,
)
# deliver ack that order has been submitted to broker routing
await ems_order_stream.send(
BrokerdOrderAck(
# ems order request id
oid=order.oid,
# broker specific request id
reqid=reqid,
)
) )
elif action == 'cancel': elif action == 'cancel':
@ -468,6 +493,9 @@ async def trades_dialogue(
# TODO: load paper positions from ``positions.toml`` # TODO: load paper positions from ``positions.toml``
_positions={}, _positions={},
# TODO: load postions from ledger file
_trade_ledger={},
) )
n.start_soon(handle_order_requests, client, ems_stream) n.start_soon(handle_order_requests, client, ems_stream)
@ -510,5 +538,4 @@ async def open_paperboi(
loglevel=loglevel, loglevel=loglevel,
) as (ctx, first): ) as (ctx, first):
yield ctx, first yield ctx, first