Reformat fake fill in paper engine,

Ensure tests pass, refactor test wrapper
paper_trade_improvements_rebase
jaredgoldman 2023-02-24 13:42:44 -05:00
parent e8714c2d17
commit c8e6312044
3 changed files with 92 additions and 81 deletions

View File

@ -260,22 +260,16 @@ class PaperBoi(Struct):
bsuid=key, bsuid=key,
) )
# Update in memory ledger per trade
ledger_entry = {oid: t.to_dict()}
# Store txn in state for PP update
self._txn_dict[oid] = t
self._trade_ledger.update(ledger_entry)
# Write to ledger toml right now # Write to ledger toml right now
with open_trade_ledger(self.broker, 'paper') as ledger: with (
ledger.update(self._trade_ledger) open_trade_ledger(self.broker, 'paper') as ledger,
open_pps(self.broker, 'piker-paper') as table
# Write to pps toml right now ):
with open_pps(self.broker, 'piker-paper') as table: ledger.update({oid: t.to_dict()})
table.update_from_trans(self._txn_dict) # Write to pps toml right now
# save pps in local state table.update_from_trans({oid: t})
self._positions = table.pps # save pps in local state
self._positions.update(table.pps)
# Ensure we have the latest positioning data when sending pp_msg # Ensure we have the latest positioning data when sending pp_msg
pp = self._positions[key] pp = self._positions[key]

View File

@ -78,7 +78,8 @@ def get_app_dir(app_name, roaming=True, force_posix=False):
# within the tractor runtimes and store testing config data # within the tractor runtimes and store testing config data
# outside of the users filesystem # outside of the users filesystem
if "pytest" in sys.modules: if "pytest" in sys.modules:
app_name += TEST_CONFIG_DIR_PATH log.info("TESTING")
os.path.join(app_name, TEST_CONFIG_DIR_PATH)
# if WIN: # if WIN:
if platform.system() == 'Windows': if platform.system() == 'Windows':

View File

@ -1,6 +1,6 @@
""" '''
Paper-mode testing Paper-mode testing
""" '''
import trio import trio
import math import math
@ -22,6 +22,7 @@ from piker.config import get_app_dir
from piker.log import get_logger from piker.log import get_logger
from piker.clearing._messages import Order from piker.clearing._messages import Order
from piker.pp import ( from piker.pp import (
PpTable,
open_trade_ledger, open_trade_ledger,
open_pps, open_pps,
) )
@ -36,31 +37,33 @@ from piker.clearing._messages import BrokerdPosition
log = get_logger(__name__) log = get_logger(__name__)
@pytest.fixture(scope="module")
@pytest.fixture(scope='module')
def delete_testing_dir(): def delete_testing_dir():
'''This fixture removes the temp directory '''This fixture removes the temp directory
used for storing all config/ledger/pp data used for storing all config/ledger/pp data
created during testing sessions created during testing sessions
''' '''
yield yield
app_dir = Path(get_app_dir("piker")).resolve() app_dir = Path(get_app_dir('piker')).resolve()
if app_dir.is_dir(): if app_dir.is_dir():
rmtree(str(app_dir)) rmtree(str(app_dir))
assert not app_dir.is_dir() assert not app_dir.is_dir()
def get_fqsn(broker, symbol): def get_fqsn(broker, symbol):
fqsn = f"{symbol}.{broker}" fqsn = f'{symbol}.{broker}'
return (fqsn, symbol, broker) return (fqsn, symbol, broker)
def test_paper_trade(open_test_pikerd: AsyncContextManager): def test_paper_trade(open_test_pikerd: AsyncContextManager):
cleared_price: float oid = ''
test_exec_mode = "live" test_exec_mode = 'live'
test_account = "paper" test_account = 'paper'
test_size = 1 test_size = 1
(fqsn, symbol, broker) = get_fqsn("kraken", "xbtusdt") (fqsn, symbol, broker) = get_fqsn('kraken', 'xbtusdt')
brokers = [broker] brokers = [broker]
test_pp_account = "piker-paper" test_pp_account = 'piker-paper'
positions: dict[ positions: dict[
# brokername, acctid # brokername, acctid
tuple[str, str], tuple[str, str],
@ -69,7 +72,7 @@ def test_paper_trade(open_test_pikerd: AsyncContextManager):
async def _async_main( async def _async_main(
open_pikerd: AsyncContextManager, open_pikerd: AsyncContextManager,
action: Literal["buy", "sell"] | None = None, action: Literal['buy', 'sell'] | None = None,
price: int = 30000, price: int = 30000,
assert_entries: bool = False, assert_entries: bool = False,
) -> None: ) -> None:
@ -78,16 +81,14 @@ def test_paper_trade(open_test_pikerd: AsyncContextManager):
that pps from previous trade exists in the ems pps. that pps from previous trade exists in the ems pps.
Finally close the position and ensure that the position in pps.toml is closed. Finally close the position and ensure that the position in pps.toml is closed.
''' '''
nonlocal oid
oid: str = str(uuid4())
book: OrderBook book: OrderBook
nonlocal cleared_price
nonlocal positions nonlocal positions
# Set up piker and EMS # Set up piker and EMS
async with ( async with (
open_pikerd() as (_, _, _, services), open_pikerd() as (_, _, _, services),
open_ems(fqsn, mode="paper") as ( open_ems(fqsn, mode='paper') as (
book, book,
trades_stream, trades_stream,
pps, pps,
@ -97,6 +98,7 @@ def test_paper_trade(open_test_pikerd: AsyncContextManager):
): ):
# Send order to EMS # Send order to EMS
if action: if action:
oid = str(uuid4())
order = Order( order = Order(
exec_mode=test_exec_mode, exec_mode=test_exec_mode,
action=action, action=action,
@ -118,17 +120,15 @@ def test_paper_trade(open_test_pikerd: AsyncContextManager):
cleared_ledger_entry = {} cleared_ledger_entry = {}
with open_trade_ledger(broker, test_account) as ledger: with open_trade_ledger(broker, test_account) as ledger:
cleared_ledger_entry = ledger[oid] cleared_ledger_entry = ledger[oid]
cleared_price = cleared_ledger_entry["price"]
assert list(ledger.keys())[-1] == oid assert list(ledger.keys())[-1] == oid
assert cleared_ledger_entry["size"] == test_size assert cleared_ledger_entry['size'] == test_size
assert cleared_ledger_entry["fqsn"] == fqsn assert cleared_ledger_entry['fqsn'] == fqsn
with open_pps(broker, test_pp_account) as table: with open_pps(broker, test_pp_account) as table:
pp_price = table.conf[broker][test_pp_account][fqsn]["ppu"] pp_price = table.conf[broker][test_pp_account][fqsn]['ppu']
# Ensure the price-per-unit (breakeven) price is close to our clearing price # Ensure the price-per-unit (breakeven) price is close to our clearing price
assert math.isclose( assert math.isclose(
pp_price, cleared_ledger_entry["size"], rel_tol=1 pp_price, cleared_ledger_entry['size'], rel_tol=1
) )
assert table.brokername == broker assert table.brokername == broker
assert table.acctid == test_pp_account assert table.acctid == test_pp_account
@ -140,62 +140,78 @@ def test_paper_trade(open_test_pikerd: AsyncContextManager):
# Open piker load pps locally # Open piker load pps locally
# and ensure last pps price is the same as ledger entry # and ensure last pps price is the same as ledger entry
async def _open_and_assert_pps(): def _assert_pps(ledger, table):
await _async_main(open_test_pikerd) return (
assert positions(broker, test_account)[-1] == cleared_price positions[(broker, test_account)][-1]['avg_price'] == ledger[oid]['price']
)
def _assert_no_pps(ledger, table):
return len(table.pps) == 0
# Close position and assert empty position in pps # Close position and assert empty position in pps
async def _close_pp_and_assert(): def _run_test_and_check(exception, fn, assert_cb=None):
await _async_main(open_test_pikerd, "sell", 1)
with open_pps(broker, test_pp_account) as table:
assert len(table.pps) == 0
def _run_test_and_check(exception, fn):
with pytest.raises(exception) as exc_info: with pytest.raises(exception) as exc_info:
trio.run(fn) trio.run(fn)
for exception in exc_info.value.exceptions: with (
assert isinstance(exception, KeyboardInterrupt) or isinstance( open_trade_ledger(broker, test_account) as ledger,
exception, ContextCancelled open_pps(broker, test_pp_account) as table,
) ):
if assert_cb:
assert assert_cb(ledger, table)
# Send and execute a trade and assert trade for exception in exc_info.value.exceptions:
assert isinstance(exception, KeyboardInterrupt) or isinstance(
exception, ContextCancelled
)
# Setablend and execute a trade and assert trade
_run_test_and_check( _run_test_and_check(
BaseExceptionGroup, BaseExceptionGroup,
partial( partial(
_async_main, _async_main,
open_pikerd=open_test_pikerd, open_pikerd=open_test_pikerd,
action="buy", action='buy',
), ),
) )
_run_test_and_check(BaseExceptionGroup, _open_and_assert_pps)
_run_test_and_check(BaseExceptionGroup, _close_pp_and_assert)
_run_test_and_check(
def test_paper_client( BaseExceptionGroup,
open_test_pikerd: AsyncContextManager partial(_async_main, open_pikerd=open_test_pikerd),
): _assert_pps,
async def _async_main(
open_pikerd: AsyncContextManager,
):
(fqsn, symbol, broker) = get_fqsn("kraken", "xbtusdt")
async with (
open_pikerd() as (_, _, _, services),
open_ems(fqsn, mode="paper") as (
book,
trades_stream,
pps,
accounts,
dialogs,
),
):
async with open_cached_client(broker) as client:
symbol_info = await client.symbol_info()
print(f'client: {symbol_info["XBTUSDT"]}')
trio.run(partial(
_async_main,
open_pikerd=open_test_pikerd,
),
) )
_run_test_and_check(
BaseExceptionGroup,
partial(_async_main, open_pikerd=open_test_pikerd, action='sell', price=1),
_assert_no_pps,
)
# def test_paper_client(
# open_test_pikerd: AsyncContextManager
# ):
#
# async def _async_main(
# open_pikerd: AsyncContextManager,
# ):
# (fqsn, symbol, broker) = get_fqsn('kraken', 'xbtusdt')
# async with (
# open_pikerd() as (_, _, _, services),
# open_ems(fqsn, mode='paper') as (
# book,
# trades_stream,
# pps,
# accounts,
# dialogs,
# ),
# ):
# async with open_cached_client(broker) as client:
# symbol_info = await client.symbol_info()
# print(f'client: {symbol_info['XBTUSDT']}')
#
# trio.run(partial(
# _async_main,
# open_pikerd=open_test_pikerd,
# ),
# )