Add functionality and tests for executing mutliple orders

paper_trade_improvements_rebase
jaredgoldman 2023-02-25 15:59:14 -05:00
parent 4eb9b68b0e
commit 1397a75d2c
1 changed files with 84 additions and 73 deletions

View File

@ -11,6 +11,7 @@ from typing import (
Literal, Literal,
) )
from pathlib import Path from pathlib import Path
from operator import attrgetter
import pytest import pytest
import tractor import tractor
@ -60,7 +61,6 @@ def test_paper_trade(open_test_pikerd: AsyncContextManager, delete_testing_dir):
oid = "" oid = ""
test_exec_mode = "live" test_exec_mode = "live"
test_account = "paper" test_account = "paper"
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"
@ -76,16 +76,19 @@ def test_paper_trade(open_test_pikerd: AsyncContextManager, delete_testing_dir):
assert_entries: bool = False, assert_entries: bool = False,
assert_pps: bool = False, assert_pps: bool = False,
assert_zeroed_pps: bool = False, assert_zeroed_pps: bool = False,
assert_precision: bool = False,
executions: int = 1,
size: float = 0.01,
) -> None: ) -> None:
"""Spawn a paper piper actor, place a trade and assert entries are present """Start piker, place a trade and assert entries are present
in both trade ledger and pps tomls. Then restart piker and ensure in both trade ledger and pps tomls. Then restart piker and ensure
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 nonlocal oid
book: OrderBook
nonlocal positions nonlocal positions
book: OrderBook
msg = ()
# Set up piker and EMS # Set up piker and EMS
async with ( async with (
open_test_pikerd() as (_, _, _, services), open_test_pikerd() as (_, _, _, services),
@ -99,56 +102,80 @@ def test_paper_trade(open_test_pikerd: AsyncContextManager, delete_testing_dir):
): ):
# Send order to EMS # Send order to EMS
if action: if action:
oid = str(uuid4()) for x in range(executions):
order = Order( print(f"Sending {action} order num {x}")
exec_mode=test_exec_mode, oid = str(uuid4())
action=action, order = Order(
oid=oid, exec_mode=test_exec_mode,
account=test_account, action=action,
size=test_size, oid=oid,
symbol=fqsn, account=test_account,
price=price, size=size,
brokers=brokers, symbol=fqsn,
) price=price,
# This is actually a syncronous call to push a message brokers=brokers,
# to the async ems clue - hence why we call trio.sleep afterwards )
book.send(order) # This is actually a syncronous call to push a message
# to the async ems clue - hence why we call trio.sleep afterwards
await trio.sleep(2) book.send(order)
async for msg in trades_stream:
msg = await trades_stream.receive()
try:
if msg["name"] == "position":
break
except (NameError, AttributeError):
pass
# Do nothing, message isn't a position
await trio.sleep(1)
# Assert entries are made in both ledger and PPS # Assert entries are made in both ledger and PPS
if assert_entries or assert_pps or assert_zeroed_pps: if assert_entries or assert_pps or assert_zeroed_pps or assert_precision:
_assert(assert_entries, assert_pps, assert_zeroed_pps, pps) _assert(
assert_entries,
assert_pps,
assert_zeroed_pps,
assert_precision,
pps,
msg,
)
# Close piker like a user would # Close piker like a user would
raise KeyboardInterrupt raise KeyboardInterrupt
def _assert(assert_entries: bool, assert_pps: bool, assert_zerod_pps, pps): def _assert(
assert_entries, assert_pps, assert_zerod_pps, assert_precision, pps, msg
):
with ( with (
open_trade_ledger(broker, test_account) as ledger, open_trade_ledger(broker, test_account) as ledger,
open_pps(broker, test_pp_account) as table, open_pps(broker, test_pp_account) as table,
): ):
# TODO: Assert between msg and pp, ledger and pp, ledger and message
# for proper values
print(f"assertion msg: {msg}")
# assert that entires are have been written # assert that entires are have been written
if assert_entries: if assert_entries:
cleared_ledger_entry = ledger[oid] latest_ledger_entry = ledger[oid]
assert list(ledger.keys())[-1] == oid latest_position = pps[(broker, test_account)][-1]
assert cleared_ledger_entry["size"] == test_size
assert cleared_ledger_entry["fqsn"] == fqsn
pp_price = table.conf[broker][test_pp_account][fqsn]["ppu"] pp_price = table.conf[broker][test_pp_account][fqsn]["ppu"]
# assert most
assert list(ledger.keys())[-1] == oid
assert latest_ledger_entry["size"] == test_size
assert latest_ledger_entry["fqsn"] == fqsn
# 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(pp_price, cleared_ledger_entry["size"], rel_tol=1) assert math.isclose(pp_price, latest_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
# assert that the last pps price is the same as the ledger price # assert that the last pps price is the same as the ledger price
if assert_pps: if assert_pps:
assert ( latest_ledger_entry = ledger[oid]
pps[(broker, test_account)][-1]["avg_price"] == ledger[oid]["price"] latest_position = pps[(broker, test_account)][-1]
) assert latest_position["avg_price"] == latest_ledger_entry["price"]
if assert_zerod_pps: if assert_zerod_pps:
# assert that positions are present # assert that positions are present
assert not bool(table) assert not bool(table.pps)
# Close position and assert empty position in pps # Close position and assert empty position in pps
def _run_test_and_check(exception, fn): def _run_test_and_check(exception, fn):
@ -156,61 +183,45 @@ def test_paper_trade(open_test_pikerd: AsyncContextManager, delete_testing_dir):
trio.run(fn) trio.run(fn)
for exception in exc_info.value.exceptions: for exception in exc_info.value.exceptions:
assert isinstance(exception, KeyboardInterrupt) or isinstance( assert (
exception, ContextCancelled isinstance(exception, KeyboardInterrupt)
or isinstance(exception, ContextCancelled)
or isinstance(exception, KeyError)
) )
# Setablend and execute a trade and assert trade # Enter a trade and assert entries are made in pps and ledger files
_run_test_and_check( _run_test_and_check(
BaseExceptionGroup, BaseExceptionGroup,
partial(_async_main, action="buy", assert_entries=True), partial(_async_main, action="buy", assert_entries=True),
) )
# Open ems and assert existence of pps entries
_run_test_and_check( _run_test_and_check(
BaseExceptionGroup, BaseExceptionGroup,
partial(_async_main, assert_pps=True), partial(_async_main, assert_pps=True),
) )
# Sell position
_run_test_and_check(
BaseExceptionGroup,
partial(_async_main, action="sell", price=1),
)
# Ensure pps are zeroed
_run_test_and_check(
BaseExceptionGroup,
partial(_async_main, assert_zeroed_pps=True),
)
# Make 5 market limit buy orders
_run_test_and_check(
BaseExceptionGroup, partial(_async_main, action="buy", executions=5)
)
# Sell 5 slots at the same price, assert cleared positions
_run_test_and_check( _run_test_and_check(
BaseExceptionGroup, BaseExceptionGroup,
partial( partial(
_async_main, action="sell", price=1 _async_main, action="sell", executions=5, price=1, assert_zeroed_pps=True
), ),
) )
_run_test_and_check(
BaseExceptionGroup,
partial(
_async_main, assert_zeroed_pps=True
),
)
# 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']}')
# with (open_pps(broker, "piker-paper") as table,):
# print(f"table: {table}")
#
# trio.run(
# partial(
# _async_main,
# open_pikerd=open_test_pikerd,
# ),
# )