Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Paper trade improvements #462

Merged
merged 62 commits into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
b67d020
add basic func to load paper_trades file
algorandpa Jan 13, 2023
a4bd51a
change open_trade_ledger typing to return a Generator type
algorandpa Feb 9, 2023
9c28d70
Add Generator as return type of open_trade_ledger
algorandpa Jan 27, 2023
8cd2354
ensure that paper pps are pulled on open
algorandpa Jan 28, 2023
1b2fce4
remove logs, unused args
algorandpa Jan 28, 2023
97627a4
remove more logs
algorandpa Jan 28, 2023
41bb044
remove unnecessary return
algorandpa Jan 28, 2023
6126c4f
restore spacing
algorandpa Jan 28, 2023
3028a8b
restore spacing
algorandpa Jan 28, 2023
5bb93cc
change id to 'piker-paper'
algorandpa Jan 29, 2023
86b4386
minor changes, prepare for rebase of overlays branch
algorandpa Feb 9, 2023
84cd1e0
initial commit on copy
algorandpa Feb 9, 2023
68a1962
force change branch name
algorandpa Feb 9, 2023
df868ce
Assert that trades persist in ems after teardown and startup
algorandpa Feb 9, 2023
95b9dac
Break test into steps
algorandpa Feb 12, 2023
b180602
Make config grab _testing dir in pytest env,
algorandpa Feb 13, 2023
dff8abd
Minor reformatting
algorandpa Feb 14, 2023
7142a6a
Add hacky cleanup solution for _testng data
algorandpa Feb 14, 2023
e5cefeb
Format to prep for PR
algorandpa Feb 15, 2023
730906a
Minor formatting
algorandpa Feb 15, 2023
acc86ae
more formatting
algorandpa Feb 15, 2023
0dec2b9
Enable backpressure during data-feed layer startup to avoid overruns
algorandpa Feb 15, 2023
4b6d3fe
Scope cleanup fixture to module
algorandpa Feb 15, 2023
316ead5
Remove scoping
algorandpa Feb 15, 2023
9acbfac
only clean up if _testing file exists
algorandpa Feb 15, 2023
2c366d7
Fix type
algorandpa Feb 15, 2023
7e87dc5
Scope fixture to session
algorandpa Feb 15, 2023
8122e6c
Disable cleanup to see if CI passes
algorandpa Feb 15, 2023
7bd8019
Add back cleanup fixture
algorandpa Feb 16, 2023
8c9c165
Remove broken import
algorandpa Feb 16, 2023
3bc54e3
Use Path.mkdir instead of os.mkdir
algorandpa Feb 16, 2023
db2e2ed
Use constants value for test config dir path
algorandpa Feb 16, 2023
c993812
Ensure actual pp is sent to ems
jaredgoldman Feb 23, 2023
e54d928
Reformat fake fill in paper engine,
jaredgoldman Feb 24, 2023
2d25d1f
Push failing assert no pps test
jaredgoldman Feb 24, 2023
3fcad16
Ensure not to write to pps when asserting?
jaredgoldman Feb 24, 2023
1e748f1
Ensure config path is being updated with _testing correctly during te…
jaredgoldman Feb 24, 2023
4c2e776
Ensure to cleanup by passing fixture in paper_test signature
jaredgoldman Feb 24, 2023
76736a5
Refactor to avoid global state while testing
jaredgoldman Feb 25, 2023
15525c2
Add functionality and tests for executing mutliple orders
jaredgoldman Feb 25, 2023
85ad23a
Remove uneeded assert_precision arg
jaredgoldman Feb 25, 2023
3a6fbab
Minor formatting
jaredgoldman Feb 25, 2023
3e83764
Remove whitespace, uneeded comments
jaredgoldman Feb 25, 2023
fcd8b8e
Remove breaking call to load pps from ledger
jaredgoldman Feb 25, 2023
36f466f
Ensure tests are running and working up until asserting pps
jaredgoldman Feb 26, 2023
61296bb
Minor formatting, removing whitespace
jaredgoldman Feb 26, 2023
4b72d3b
Add backpressure setting back as it wasn't altering test behaviour
jaredgoldman Feb 26, 2023
55253c8
Remove whitespace and correct typo
jaredgoldman Feb 26, 2023
342aec6
Skip zero test and change use Path when creating a config folder in m…
jaredgoldman Feb 26, 2023
e4e3689
Add specific kwarg key to open_pps call when starting paperboi
jaredgoldman Feb 28, 2023
802af30
Add specific location of _testing dir in delete_testing_dir fixture
jaredgoldman Feb 28, 2023
6f15d47
Add space in docstrings,
jaredgoldman Feb 28, 2023
ecb22dd
Remove whitespace, remove stale comments
jaredgoldman Feb 28, 2023
87eb9c5
Format assertion conditions
jaredgoldman Feb 28, 2023
d7317c3
Shorten assertion docstring
jaredgoldman Feb 28, 2023
973c068
Assert conditions like a nerd
jaredgoldman Feb 28, 2023
a6257ae
Add docstrings to test cases,
jaredgoldman Feb 28, 2023
882032e
Change skip to xfail
jaredgoldman Feb 28, 2023
1323981
Format lines in conftest
jaredgoldman Feb 28, 2023
05fdc9d
Add xfail
jaredgoldman Feb 28, 2023
41f81eb
Make write on exit default false
jaredgoldman Feb 28, 2023
c83fe5a
Fix typo in test docstring
jaredgoldman Feb 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion piker/clearing/_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class Order(Struct):
price: float
size: float # -ve is "sell", +ve is "buy"

brokers: Optional[list[str]] = []
brokers: list[str] = []


class Cancel(Struct):
Expand Down
60 changes: 29 additions & 31 deletions piker/clearing/_paper_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@
import tractor

from .. import data
from ..data._source import Symbol
from ..data.types import Struct
from ..pp import (
Position,
Transaction,
open_trade_ledger,
open_pps,
)
from ..data._normalize import iterticks
from ..data._source import unpack_fqsn
Expand All @@ -56,6 +57,7 @@
BrokerdError,
)

from ..config import load

log = get_logger(__name__)

Expand Down Expand Up @@ -234,8 +236,6 @@ async def fake_fill(
log.info(f'Fake filling order:\n{fill_msg}')
await self.ems_trades_stream.send(fill_msg)

self._trade_ledger.update(fill_msg.to_dict())

if order_complete:
msg = BrokerdStatus(
reqid=reqid,
Expand All @@ -250,18 +250,6 @@ async def fake_fill(

# lookup any existing position
key = fqsn.rstrip(f'.{self.broker}')
pp = self._positions.setdefault(
fqsn,
Position(
Symbol(
key=key,
broker_info={self.broker: {}},
),
size=size,
ppu=price,
bsuid=key,
)
)
t = Transaction(
fqsn=fqsn,
tid=oid,
Expand All @@ -271,21 +259,29 @@ async def fake_fill(
dt=pendulum.from_timestamp(fill_time_s),
bsuid=key,
)
pp.add_clear(t)

pp_msg = BrokerdPosition(
broker=self.broker,
account='paper',
symbol=fqsn,
# 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.ppu,
)
with (
open_trade_ledger(self.broker, 'paper') as ledger,
open_pps(self.broker, 'paper', True) as table
):
ledger.update({oid: t.to_dict()})
# Write to pps toml right now
table.update_from_trans({oid: t})

pp = table.pps[key]
pp_msg = BrokerdPosition(
broker=self.broker,
account='paper',
symbol=fqsn,
# TODO: we need to look up the asset currency from
# broker info. i guess for crypto this can be
# inferred from the pair?
currency=key,
size=pp.size,
avg_price=pp.ppu,
)

await self.ems_trades_stream.send(pp_msg)
await self.ems_trades_stream.send(pp_msg)


async def simulate_fills(
Expand Down Expand Up @@ -533,6 +529,11 @@ async def trades_dialogue(
) as feed,

):

with open_pps(broker, 'paper') as table:
# save pps in local state
_positions.update(table.pps)

pp_msgs: list[BrokerdPosition] = []
pos: Position
token: str # f'{symbol}.{self.broker}'
Expand All @@ -545,8 +546,6 @@ async def trades_dialogue(
avg_price=pos.ppu,
))

# TODO: load paper positions per broker from .toml config file
# and pass as symbol to position data mapping: ``dict[str, dict]``
await ctx.started((
pp_msgs,
['paper'],
Expand All @@ -564,7 +563,6 @@ async def trades_dialogue(

_reqids=_reqids,

# TODO: load paper positions from ``positions.toml``
_positions=_positions,

# TODO: load postions from ledger file
Expand Down
18 changes: 15 additions & 3 deletions piker/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
from os.path import dirname
import shutil
from typing import Optional

from pathlib import Path
jaredgoldman marked this conversation as resolved.
Show resolved Hide resolved
from bidict import bidict
import toml

from piker.testing import TEST_CONFIG_DIR_PATH
from .log import get_logger

log = get_logger('broker-config')
Expand Down Expand Up @@ -75,6 +75,13 @@ def get_app_dir(app_name, roaming=True, force_posix=False):
def _posixify(name):
return "-".join(name.split()).lower()

# TODO: This is a hacky way to a) determine we're testing
jaredgoldman marked this conversation as resolved.
Show resolved Hide resolved
# and b) creating a test dir. We should aim to set a variable
# within the tractor runtimes and store testing config data
# outside of the users filesystem
if "pytest" in sys.modules:
app_name = os.path.join(app_name, TEST_CONFIG_DIR_PATH)

# if WIN:
if platform.system() == 'Windows':
key = "APPDATA" if roaming else "LOCALAPPDATA"
Expand Down Expand Up @@ -115,6 +122,7 @@ def _posixify(name):
'pps',
'trades',
'watchlists',
'paper_trades'
jaredgoldman marked this conversation as resolved.
Show resolved Hide resolved
}

_watchlists_data_path = os.path.join(_config_dir, 'watchlists.json')
Expand Down Expand Up @@ -198,7 +206,7 @@ def load(
path = path or get_conf_path(conf_name)

if not os.path.isdir(_config_dir):
os.mkdir(_config_dir)
Path(_config_dir).mkdir(parents=True, exist_ok=True)

if not os.path.isfile(path):
fn = _conf_fn_w_ext(conf_name)
Expand All @@ -212,6 +220,10 @@ def load(
# if one exists.
if os.path.isfile(template):
shutil.copyfile(template, path)
else:
# create an empty file
with open(path, 'x'):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other thing i was going to maybe suggest is just use pathlib here as well to ensure the file exists?

Not a big deal, we can always do it later if needed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which file exists? We are creating an empty file in this case, the file would def not exist.

pass
else:
with open(path, 'r'):
pass # touch it
Expand Down
3 changes: 2 additions & 1 deletion piker/data/marketstore.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
)
import time
from math import isnan
from pathlib import Path

from bidict import bidict
from msgspec.msgpack import encode, decode
Expand Down Expand Up @@ -134,7 +135,7 @@ def start_marketstore(

# create dirs when dne
if not os.path.isdir(config._config_dir):
os.mkdir(config._config_dir)
Path(config._config_dir).mkdir(parents=True, exist_ok=True)

if not os.path.isdir(mktsdir):
os.mkdir(mktsdir)
Expand Down
11 changes: 6 additions & 5 deletions piker/pp.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
(looking at you `ib` and dirt-bird friends)

'''
from __future__ import annotations
from contextlib import contextmanager as cm
from pprint import pformat
import os
Expand All @@ -32,6 +33,7 @@
Iterator,
Optional,
Union,
Generator
)

import pendulum
Expand All @@ -54,7 +56,7 @@ def open_trade_ledger(
broker: str,
account: str,

) -> dict:
) -> Generator[dict, None, None]:
'''
Indempotently create and read in a trade log file from the
``<configuration_dir>/ledgers/`` directory.
Expand Down Expand Up @@ -90,8 +92,7 @@ def open_trade_ledger(
# TODO: show diff output?
# https://stackoverflow.com/questions/12956957/print-diff-of-python-dictionaries
print(f'Updating ledger for {tradesfile}:\n')
ledger.update(cpy)

ledger.update(cpy)
# we write on close the mutated ledger data
with open(tradesfile, 'w') as cf:
toml.dump(ledger, cf)
Expand Down Expand Up @@ -879,9 +880,9 @@ def dump_sections(self, o, sup):
def open_pps(
brokername: str,
acctid: str,
write_on_exit: bool = True,
write_on_exit: bool = False,

) -> PpTable:
) -> Generator[PpTable, None, None]:
'''
Read out broker-specific position entries from
incremental update file: ``pps.toml``.
Expand Down
1 change: 1 addition & 0 deletions piker/testing/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TEST_CONFIG_DIR_PATH = '_testing'
3 changes: 3 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#[pytest]
#trio_mode=True
#log_cli=1
53 changes: 53 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from contextlib import asynccontextmanager as acm
from functools import partial
import os
from typing import AsyncContextManager
from pathlib import Path
from shutil import rmtree

import pytest
import tractor
Expand All @@ -11,6 +14,7 @@
from piker._daemon import (
Services,
)
from piker.clearing._client import open_ems


def pytest_addoption(parser):
Expand Down Expand Up @@ -132,3 +136,52 @@ def open_test_pikerd(
# - no leaked subprocs or shm buffers
# - all requested container service are torn down
# - certain ``tractor`` runtime state?


@acm
async def _open_test_pikerd_and_ems(
fqsn,
mode,
loglevel,
open_test_pikerd
):
async with (
open_test_pikerd() as (_, _, _, services),
open_ems(
fqsn,
mode=mode,
loglevel=loglevel,
) as ems_services):
yield (services, ems_services)


@pytest.fixture
def open_test_pikerd_and_ems(
open_test_pikerd,
fqsn: str = 'xbtusdt.kraken',
jaredgoldman marked this conversation as resolved.
Show resolved Hide resolved
mode: str = 'paper',
loglevel: str = 'info',
):
yield partial(
_open_test_pikerd_and_ems,
fqsn,
mode,
loglevel,
open_test_pikerd
)


@pytest.fixture(scope='module')
def delete_testing_dir():
'''
This fixture removes the temp directory
used for storing all config/ledger/pp data
created during testing sessions. During test runs
this file can be found in .config/piker/_testing

'''
yield
app_dir = Path(config.get_app_dir('piker')).resolve()
if app_dir.is_dir():
rmtree(str(app_dir))
assert not app_dir.is_dir()
Loading