279 lines
6.6 KiB
Python
279 lines
6.6 KiB
Python
'''
|
|
`piker.accounting` mgmt calculations for
|
|
- positioning
|
|
- ledger updates
|
|
- config file IO
|
|
|
|
'''
|
|
from decimal import Decimal
|
|
from pathlib import Path
|
|
from types import ModuleType
|
|
import tomllib
|
|
|
|
import pytest
|
|
from piker import config
|
|
from piker.accounting import (
|
|
Account,
|
|
Asset,
|
|
calc,
|
|
open_account,
|
|
load_account,
|
|
load_account_from_ledger,
|
|
MktPair,
|
|
open_trade_ledger,
|
|
Position,
|
|
TransactionLedger,
|
|
)
|
|
from piker.data._symcache import SymbologyCache
|
|
import tractor
|
|
|
|
|
|
def test_root_conf_networking_section(
|
|
root_conf: dict,
|
|
):
|
|
conf, path = config.load(
|
|
'conf',
|
|
touch_if_dne=True,
|
|
)
|
|
assert conf['network']['tsdb']
|
|
|
|
|
|
def test_account_file_default_empty(
|
|
tmpconfdir: Path,
|
|
):
|
|
conf, path = load_account(
|
|
'kraken',
|
|
'paper',
|
|
)
|
|
|
|
# ensure the account file empty but created
|
|
# and in the correct place in the filesystem!
|
|
assert not conf
|
|
assert path.parent.is_dir()
|
|
assert path.parent.name == 'accounting'
|
|
|
|
|
|
def test_paper_ledger_fuzzy_qualifies_mktmap_keys(
|
|
tmp_path: Path,
|
|
):
|
|
'''
|
|
Qualify legacy paper-ledger FQMEs via symcache string keys.
|
|
|
|
``SymbologyCache.search()`` previously passed its ``mktmaps``
|
|
mapping directly to RapidFuzz. RapidFuzz searches mapping values,
|
|
so a paper fill with an unqualified FQME made RapidFuzz call
|
|
``len()`` on a ``MktPair`` while
|
|
``TransactionLedger.write_config()`` exited.
|
|
This test installs both native-ID and FQME keys for one market,
|
|
proves search preserves the matched alias, then writes the same
|
|
unqualified paper transaction shape. The persisted fully
|
|
qualified keys prove the ledger exit path resolves the market
|
|
without treating ``MktPair`` values as fuzzy-search sequences.
|
|
|
|
'''
|
|
usdt = Asset(
|
|
name='usdt',
|
|
atype='crypto',
|
|
tx_tick=Decimal('0.00000001'),
|
|
)
|
|
nvda = Asset(
|
|
name='nvda',
|
|
atype='stock',
|
|
tx_tick=Decimal('0.00000001'),
|
|
)
|
|
mkt = MktPair(
|
|
dst=nvda,
|
|
src=usdt,
|
|
price_tick=Decimal('0.01'),
|
|
size_tick=Decimal('0.001'),
|
|
bs_mktid='NVDAUSDT',
|
|
broker='binance',
|
|
venue='usdtm',
|
|
expiry='perp',
|
|
)
|
|
symcache = SymbologyCache(
|
|
mod=ModuleType('binance'),
|
|
fp=tmp_path / 'binance.symcache.toml',
|
|
mktmaps={
|
|
mkt.bs_mktid: mkt,
|
|
mkt.fqme: mkt,
|
|
},
|
|
)
|
|
|
|
matches = symcache.search(mkt.bs_mktid)
|
|
assert next(iter(matches)) == mkt.bs_mktid
|
|
assert matches[mkt.bs_mktid] is mkt
|
|
|
|
ledger_path = tmp_path / 'trades_binance_paper.toml'
|
|
unqualified_fqme = 'nvdausdt.usdtm.perp'
|
|
ledger = TransactionLedger(
|
|
ledger_dict={
|
|
'fill-id': {
|
|
'fqme': unqualified_fqme,
|
|
'bs_mktid': unqualified_fqme,
|
|
},
|
|
},
|
|
file_path=ledger_path,
|
|
account='paper',
|
|
mod=symcache.mod,
|
|
tx_sort=lambda txns: txns.items(),
|
|
symcache=symcache,
|
|
)
|
|
|
|
ledger.write_config()
|
|
|
|
with ledger_path.open('rb') as ledger_file:
|
|
saved = tomllib.load(ledger_file)['fill-id']
|
|
|
|
assert saved['fqme'] == mkt.fqme
|
|
assert saved['bs_mktid'] == mkt.fqme
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'fq_acnt',
|
|
[
|
|
('binance', 'paper'),
|
|
],
|
|
)
|
|
def test_paper_ledger_position_calcs(
|
|
fq_acnt: tuple[str, str],
|
|
debug_mode: bool,
|
|
):
|
|
broker: str
|
|
acnt_name: str
|
|
broker, acnt_name = fq_acnt
|
|
|
|
accounts_path: Path = (
|
|
config.repodir()
|
|
/ 'tests'
|
|
/ '_inputs' # tests-local-subdir
|
|
)
|
|
|
|
ldr: TransactionLedger
|
|
with (
|
|
open_trade_ledger(
|
|
broker,
|
|
acnt_name,
|
|
allow_from_sync_code=True,
|
|
|
|
_fp=accounts_path,
|
|
) as ldr,
|
|
|
|
# open `polars` acnt dfs Bo
|
|
calc.open_ledger_dfs(
|
|
broker,
|
|
acnt_name,
|
|
ledger=ldr,
|
|
|
|
_fp=accounts_path,
|
|
debug_mode=debug_mode,
|
|
|
|
) as (dfs, ledger),
|
|
|
|
):
|
|
acnt: Account = load_account_from_ledger(
|
|
broker,
|
|
acnt_name,
|
|
ledger=ldr,
|
|
_fp=accounts_path,
|
|
)
|
|
|
|
# do manual checks on expected pos calcs based on input
|
|
# ledger B)
|
|
|
|
# xrpusdt should have a net-zero size
|
|
xrp: str = 'xrpusdt.spot.binance'
|
|
pos: Position = acnt.pps[xrp]
|
|
|
|
# XXX: turns out our old dict-style-processing
|
|
# get's this wrong i think due to dt-sorting..
|
|
# lcum: float = pos.cumsize
|
|
|
|
df = dfs[xrp]
|
|
assert df['cumsize'][-1] == 0
|
|
assert pos.cumsize == 0
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'fq_acnt',
|
|
[
|
|
('ib', 'algopaper'),
|
|
],
|
|
)
|
|
def test_ib_account_with_duplicated_mktids(
|
|
fq_acnt: tuple[str, str],
|
|
debug_mode: bool,
|
|
):
|
|
# ?TODO, once we start symcache-incremental-update-support?
|
|
# from piker.data import (
|
|
# open_symcache,
|
|
# )
|
|
#
|
|
# async def main():
|
|
# async with (
|
|
# # TODO: do this as part of `open_account()`!?
|
|
# open_symcache(
|
|
# 'ib',
|
|
# only_from_memcache=True,
|
|
# ) as symcache,
|
|
# ):
|
|
|
|
|
|
from piker.brokers.ib.ledger import (
|
|
tx_sort,
|
|
|
|
# ?TODO, once we want to pull lowlevel txns and process them?
|
|
# norm_trade_records,
|
|
# update_ledger_from_api_trades,
|
|
)
|
|
|
|
broker: str
|
|
acnt_id: str = 'algopaper'
|
|
broker, acnt_id = fq_acnt
|
|
accounts_def = config.load_accounts([broker])
|
|
assert accounts_def[f'{broker}.{acnt_id}']
|
|
|
|
ledger: TransactionLedger
|
|
acnt: Account
|
|
with (
|
|
tractor.devx.maybe_open_crash_handler(pdb=debug_mode),
|
|
|
|
open_trade_ledger(
|
|
'ib',
|
|
acnt_id,
|
|
tx_sort=tx_sort,
|
|
|
|
# TODO, eventually incrementally updated for IB..
|
|
# symcache=symcache,
|
|
symcache=None,
|
|
allow_from_sync_code=True,
|
|
|
|
) as ledger,
|
|
|
|
open_account(
|
|
'ib',
|
|
acnt_id,
|
|
write_on_exit=True,
|
|
) as acnt,
|
|
):
|
|
# per input params
|
|
symcache = ledger.symcache
|
|
assert not (
|
|
symcache.pairs
|
|
or
|
|
symcache.pairs
|
|
or
|
|
symcache.mktmaps
|
|
)
|
|
# re-compute all positions that have changed state.
|
|
# TODO: likely we should change the API to return the
|
|
# position updates from `.update_from_ledger()`?
|
|
active, closed = acnt.dump_active()
|
|
|
|
# breakpoint()
|
|
|
|
# TODO, (see above imports as well) incremental update from
|
|
# (updated) ledger?
|
|
# -[ ] pull some code from `.ib.broker` content.
|