Compare commits

...

7 Commits

Author SHA1 Message Date
Tyler Goodlet d649749e7d TOSQASH 6f92c6b5: xdotool trycatch 2023-03-10 17:59:46 -05:00
Tyler Goodlet 0f747d8d87 `ib`: (cukcit) just presume a stonk if we can read type from existing ledger.. 2023-03-10 17:59:27 -05:00
Tyler Goodlet 4a3c14916d Break out old `.pp` components into submods: `._ledger` and `._pos` 2023-03-10 17:59:27 -05:00
Tyler Goodlet fc848ef34f Start a new `.accounting` subpkg, move `.pp` contents there 2023-03-10 17:59:27 -05:00
Tyler Goodlet e824572d7c '`kraken`: fix pos loading using `digits_to_dec()` to pair info
Our issue was not having the correct value set on each
`Symbol.lot_tick_size`.. and then doing PPU calcs with the default set
for legacy mkts..

Also,
- actually write `pps.toml` on broker mode exit.
- drop `get_likely_pair()` and import from pp module.
2023-03-10 17:59:27 -05:00
Tyler Goodlet 275704235f Add an inverse of `float_digits()`: `digits_to_dec() 2023-03-10 17:59:27 -05:00
Tyler Goodlet de655bfe6a Ensure clearing table entries are time-sorted..
Not sure how this worked before but, the PPU calculation critically
requires that the order of clearing transactions are in the correct
chronological order! Fix this by sorting `trans: dict[str, Transaction]`
in the `PpTable.update_from_trans()` method.

Also, move the `get_likely_pair()` parser from the `kraken` backend here
for future use particularly when we revamp the asset-transaction
processing layer.
2023-03-10 17:59:27 -05:00
13 changed files with 355 additions and 207 deletions

View File

@ -0,0 +1,92 @@
# piker: trading gear for hackers
# Copyright (C) Tyler Goodlet (in stewardship for pikers)
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
"Accounting for degens": count dem numberz that tracks how much you got
for tendiez.
'''
from ..log import get_logger
from ._pos import (
Transaction,
open_trade_ledger,
PpTable,
)
from ._pos import (
open_pps,
load_pps_from_ledger,
Position,
)
log = get_logger(__name__)
__all__ = [
'Transaction',
'open_trade_ledger',
'PpTable',
'open_pps',
'load_pps_from_ledger',
'Position',
]
def get_likely_pair(
src: str,
dst: str,
bsuid: str,
) -> str:
'''
Attempt to get the likely trading pair matching a given destination
asset `dst: str`.
'''
try:
src_name_start = bsuid.rindex(src)
except (
ValueError, # substr not found
):
# TODO: handle nested positions..(i.e.
# positions where the src fiat was used to
# buy some other dst which was furhter used
# to buy another dst..)
log.warning(
f'No src fiat {src} found in {bsuid}?'
)
return
likely_dst = bsuid[:src_name_start]
if likely_dst == dst:
return bsuid
if __name__ == '__main__':
import sys
from pprint import pformat
args = sys.argv
assert len(args) > 1, 'Specifiy account(s) from `brokers.toml`'
args = args[1:]
for acctid in args:
broker, name = acctid.split('.')
trans, updated_pps = load_pps_from_ledger(broker, name)
print(
f'Processing transactions into pps for {broker}:{acctid}\n'
f'{pformat(trans)}\n\n'
f'{pformat(updated_pps)}'
)

View File

@ -0,0 +1,125 @@
# piker: trading gear for hackers
# Copyright (C) Tyler Goodlet (in stewardship for pikers)
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations
from contextlib import contextmanager as cm
import os
from os import path
import time
from typing import (
Any,
Iterator,
Union,
Generator
)
from pendulum import (
datetime,
)
import tomli
import toml
from .. import config
from ..data._source import Symbol
from ..data.types import Struct
from ..log import get_logger
log = get_logger(__name__)
@cm
def open_trade_ledger(
broker: str,
account: str,
) -> Generator[dict, None, None]:
'''
Indempotently create and read in a trade log file from the
``<configuration_dir>/ledgers/`` directory.
Files are named per broker account of the form
``<brokername>_<accountname>.toml``. The ``accountname`` here is the
name as defined in the user's ``brokers.toml`` config.
'''
ldir = path.join(config._config_dir, 'ledgers')
if not path.isdir(ldir):
os.makedirs(ldir)
fname = f'trades_{broker}_{account}.toml'
tradesfile = path.join(ldir, fname)
if not path.isfile(tradesfile):
log.info(
f'Creating new local trades ledger: {tradesfile}'
)
with open(tradesfile, 'w') as cf:
pass # touch
with open(tradesfile, 'rb') as cf:
start = time.time()
ledger = tomli.load(cf)
log.info(f'Ledger load took {time.time() - start}s')
cpy = ledger.copy()
try:
yield cpy
finally:
if cpy != ledger:
# TODO: show diff output?
# https://stackoverflow.com/questions/12956957/print-diff-of-python-dictionaries
log.info(f'Updating ledger for {tradesfile}:\n')
ledger.update(cpy)
# we write on close the mutated ledger data
with open(tradesfile, 'w') as cf:
toml.dump(ledger, cf)
class Transaction(Struct, frozen=True):
# TODO: should this be ``.to`` (see below)?
fqsn: str
sym: Symbol
tid: Union[str, int] # unique transaction id
size: float
price: float
cost: float # commisions or other additional costs
dt: datetime
expiry: datetime | None = None
# optional key normally derived from the broker
# backend which ensures the instrument-symbol this record
# is for is truly unique.
bsuid: Union[str, int] | None = None
# optional fqsn for the source "asset"/money symbol?
# from: Optional[str] = None
def iter_by_dt(
clears: dict[str, Any],
) -> Iterator[tuple[str, dict]]:
'''
Iterate entries of a ``clears: dict`` table sorted by entry recorded
datetime presumably set at the ``'dt'`` field in each entry.
'''
for tid, data in sorted(
list(clears.items()),
key=lambda item: item[1]['dt'],
):
yield tid, data

View File

@ -14,20 +14,18 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
''' '''
Personal/Private position parsing, calculating, summarizing in a way Personal/Private position parsing, calculating, summarizing in a way
that doesn't try to cuk most humans who prefer to not lose their moneys.. that doesn't try to cuk most humans who prefer to not lose their moneys..
(looking at you `ib` and dirt-bird friends) (looking at you `ib` and dirt-bird friends)
''' '''
from __future__ import annotations from __future__ import annotations
from contextlib import contextmanager as cm from contextlib import contextmanager as cm
from pprint import pformat
import os
from os import path
from math import copysign from math import copysign
import re import re
import time
from typing import ( from typing import (
Any, Any,
Iterator, Iterator,
@ -38,104 +36,23 @@ from typing import (
import pendulum import pendulum
from pendulum import datetime, now from pendulum import datetime, now
import tomli
import toml import toml
from . import config from ._ledger import (
from .brokers import get_brokermod Transaction,
from .clearing._messages import BrokerdPosition, Status iter_by_dt,
from .data._source import Symbol, unpack_fqsn open_trade_ledger,
from .log import get_logger )
from .data.types import Struct from .. import config
from ..brokers import get_brokermod
from ..clearing._messages import BrokerdPosition, Status
from ..data._source import Symbol, unpack_fqsn
from ..data.types import Struct
from ..log import get_logger
log = get_logger(__name__) log = get_logger(__name__)
@cm
def open_trade_ledger(
broker: str,
account: str,
) -> Generator[dict, None, None]:
'''
Indempotently create and read in a trade log file from the
``<configuration_dir>/ledgers/`` directory.
Files are named per broker account of the form
``<brokername>_<accountname>.toml``. The ``accountname`` here is the
name as defined in the user's ``brokers.toml`` config.
'''
ldir = path.join(config._config_dir, 'ledgers')
if not path.isdir(ldir):
os.makedirs(ldir)
fname = f'trades_{broker}_{account}.toml'
tradesfile = path.join(ldir, fname)
if not path.isfile(tradesfile):
log.info(
f'Creating new local trades ledger: {tradesfile}'
)
with open(tradesfile, 'w') as cf:
pass # touch
with open(tradesfile, 'rb') as cf:
start = time.time()
ledger = tomli.load(cf)
log.info(f'Ledger load took {time.time() - start}s')
cpy = ledger.copy()
try:
yield cpy
finally:
if cpy != ledger:
# TODO: show diff output?
# https://stackoverflow.com/questions/12956957/print-diff-of-python-dictionaries
log.info(f'Updating ledger for {tradesfile}:\n')
ledger.update(cpy)
# we write on close the mutated ledger data
with open(tradesfile, 'w') as cf:
toml.dump(ledger, cf)
class Transaction(Struct, frozen=True):
# TODO: should this be ``.to`` (see below)?
fqsn: str
sym: Symbol
tid: Union[str, int] # unique transaction id
size: float
price: float
cost: float # commisions or other additional costs
dt: datetime
expiry: datetime | None = None
# optional key normally derived from the broker
# backend which ensures the instrument-symbol this record
# is for is truly unique.
bsuid: Union[str, int] | None = None
# optional fqsn for the source "asset"/money symbol?
# from: Optional[str] = None
def iter_by_dt(
clears: dict[str, Any],
) -> Iterator[tuple[str, dict]]:
'''
Iterate entries of a ``clears: dict`` table sorted by entry recorded
datetime presumably set at the ``'dt'`` field in each entry.
'''
for tid, data in sorted(
list(clears.items()),
key=lambda item: item[1]['dt'],
):
yield tid, data
class Position(Struct): class Position(Struct):
''' '''
Basic pp (personal/piker position) model with attached clearing Basic pp (personal/piker position) model with attached clearing
@ -484,7 +401,9 @@ class Position(Struct):
if self.split_ratio is not None: if self.split_ratio is not None:
size = round(size * self.split_ratio) size = round(size * self.split_ratio)
return float(self.symbol.quantize_size(size)) return float(
self.symbol.quantize_size(size),
)
def minimize_clears( def minimize_clears(
self, self,
@ -564,9 +483,13 @@ class PpTable(Struct):
pps = self.pps pps = self.pps
updated: dict[str, Position] = {} updated: dict[str, Position] = {}
# lifo update all pps from records # lifo update all pps from records, ensuring
for tid, t in trans.items(): # we compute the PPU and size sorted in time!
for t in sorted(
trans.values(),
key=lambda t: t.dt,
reverse=True,
):
pp = pps.setdefault( pp = pps.setdefault(
t.bsuid, t.bsuid,
@ -590,7 +513,10 @@ class PpTable(Struct):
# included in the current pps state. # included in the current pps state.
if ( if (
t.tid in clears t.tid in clears
or first_clear_dt and t.dt < first_clear_dt or (
first_clear_dt
and t.dt < first_clear_dt
)
): ):
# NOTE: likely you'll see repeats of the same # NOTE: likely you'll see repeats of the same
# ``Transaction`` passed in here if/when you are restarting # ``Transaction`` passed in here if/when you are restarting
@ -607,6 +533,8 @@ class PpTable(Struct):
for bsuid, pp in updated.items(): for bsuid, pp in updated.items():
pp.ensure_state() pp.ensure_state()
# deliver only the position entries that were actually updated
# (modified the state) from the input transaction set.
return updated return updated
def dump_active( def dump_active(
@ -701,8 +629,10 @@ class PpTable(Struct):
# active, closed_pp_objs = table.dump_active() # active, closed_pp_objs = table.dump_active()
pp_entries = self.to_toml() pp_entries = self.to_toml()
if pp_entries: if pp_entries:
log.info(f'Updating ``pps.toml`` for {path}:\n') log.info(
log.info(f'Current positions:\n{pp_entries}') f'Updating ``pps.toml``:\n'
f'Current positions:\n{pp_entries}'
)
self.conf[self.brokername][self.acctid] = pp_entries self.conf[self.brokername][self.acctid] = pp_entries
elif ( elif (
@ -1029,19 +959,3 @@ def open_pps(
finally: finally:
if write_on_exit: if write_on_exit:
table.write_config() table.write_config()
if __name__ == '__main__':
import sys
args = sys.argv
assert len(args) > 1, 'Specifiy account(s) from `brokers.toml`'
args = args[1:]
for acctid in args:
broker, name = acctid.split('.')
trans, updated_pps = load_pps_from_ledger(broker, name)
print(
f'Processing transactions into pps for {broker}:{acctid}\n'
f'{pformat(trans)}\n\n'
f'{pformat(updated_pps)}'
)

View File

@ -24,6 +24,10 @@ import subprocess
import tractor import tractor
from piker.log import get_logger
log = get_logger(__name__)
_reset_tech: Literal[ _reset_tech: Literal[
'vnc', 'vnc',
@ -134,54 +138,54 @@ def i3ipc_xdotool_manual_click_hack() -> None:
# 'IB', # gw running in i3 (newer version?) # 'IB', # gw running in i3 (newer version?)
] ]
for name in win_names: try:
results = t.find_titled(name) for name in win_names:
print(f'results for {name}: {results}') results = t.find_titled(name)
if results: print(f'results for {name}: {results}')
con = results[0] if results:
print(f'Resetting data feed for {name}') con = results[0]
win_id = str(con.window) print(f'Resetting data feed for {name}')
w, h = con.rect.width, con.rect.height win_id = str(con.window)
w, h = con.rect.width, con.rect.height
# TODO: seems to be a few libs for python but not sure # TODO: seems to be a few libs for python but not sure
# if they support all the sub commands we need, order of # if they support all the sub commands we need, order of
# most recent commit history: # most recent commit history:
# https://github.com/rr-/pyxdotool # https://github.com/rr-/pyxdotool
# https://github.com/ShaneHutter/pyxdotool # https://github.com/ShaneHutter/pyxdotool
# https://github.com/cphyc/pyxdotool # https://github.com/cphyc/pyxdotool
# TODO: only run the reconnect (2nd) kc on a detected # TODO: only run the reconnect (2nd) kc on a detected
# disconnect? # disconnect?
for key_combo, timeout in [ for key_combo, timeout in [
# only required if we need a connection reset. # only required if we need a connection reset.
# ('ctrl+alt+r', 12), # ('ctrl+alt+r', 12),
# data feed reset. # data feed reset.
('ctrl+alt+f', 6) ('ctrl+alt+f', 6)
]: ]:
subprocess.call([ subprocess.call([
'xdotool', 'xdotool',
'windowactivate', '--sync', win_id, 'windowactivate', '--sync', win_id,
# move mouse to bottom left of window (where there should # move mouse to bottom left of window (where
# be nothing to click). # there should be nothing to click).
'mousemove_relative', '--sync', str(w-4), str(h-4), 'mousemove_relative', '--sync', str(w-4), str(h-4),
# NOTE: we may need to stick a `--retry 3` in here.. # NOTE: we may need to stick a `--retry 3` in here..
'click', '--window', win_id, 'click', '--window', win_id,
'--repeat', '3', '1', '--repeat', '3', '1',
# hackzorzes # hackzorzes
'key', key_combo, 'key', key_combo,
], ],
timeout=timeout, timeout=timeout,
) )
# re-activate and focus original window # re-activate and focus original window
try:
subprocess.call([ subprocess.call([
'xdotool', 'xdotool',
'windowactivate', '--sync', str(orig_win_id), 'windowactivate', '--sync', str(orig_win_id),
'click', '--window', str(orig_win_id), '1', 'click', '--window', str(orig_win_id), '1',
]) ])
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
log.exception(f'xdotool timed out?') log.exception('xdotool timed out?')

View File

@ -51,7 +51,7 @@ from ib_insync.objects import Position as IbPosition
import pendulum import pendulum
from piker import config from piker import config
from piker.pp import ( from piker.accounting import (
Position, Position,
Transaction, Transaction,
open_trade_ledger, open_trade_ledger,
@ -1153,7 +1153,7 @@ def norm_trade_records(
# special handling of symbol extraction from # special handling of symbol extraction from
# flex records using some ad-hoc schema parsing. # flex records using some ad-hoc schema parsing.
asset_type: str = record.get('assetCategory') or record['secType'] asset_type: str = record.get('assetCategory') or record.get('secType', 'STK')
# TODO: XXX: WOA this is kinda hacky.. probably # TODO: XXX: WOA this is kinda hacky.. probably
# should figure out the correct future pair key more # should figure out the correct future pair key more

View File

@ -20,6 +20,7 @@ Kraken web API wrapping.
''' '''
from contextlib import asynccontextmanager as acm from contextlib import asynccontextmanager as acm
from datetime import datetime from datetime import datetime
from decimal import Decimal
import itertools import itertools
from typing import ( from typing import (
Any, Any,
@ -48,7 +49,7 @@ from piker.brokers._util import (
BrokerError, BrokerError,
DataThrottle, DataThrottle,
) )
from piker.pp import Transaction from piker.accounting import Transaction
from . import log from . import log
# <uri>/<version>/ # <uri>/<version>/
@ -248,6 +249,9 @@ class Client:
{}, {},
) )
by_bsuid = resp['result'] by_bsuid = resp['result']
# TODO: we need to pull out the "asset" decimals
# data and return a `decimal.Decimal` instead here!
return { return {
self._atable[sym].lower(): float(bal) self._atable[sym].lower(): float(bal)
for sym, bal in by_bsuid.items() for sym, bal in by_bsuid.items()

View File

@ -21,7 +21,6 @@ Order api and machinery
from collections import ChainMap, defaultdict from collections import ChainMap, defaultdict
from contextlib import ( from contextlib import (
asynccontextmanager as acm, asynccontextmanager as acm,
contextmanager as cm,
) )
from functools import partial from functools import partial
from itertools import count from itertools import count
@ -41,14 +40,18 @@ import pendulum
import trio import trio
import tractor import tractor
from piker.pp import ( from piker.accounting import (
Position, Position,
PpTable, PpTable,
Transaction, Transaction,
open_trade_ledger, open_trade_ledger,
open_pps, open_pps,
get_likely_pair,
)
from piker.data._source import (
Symbol,
digits_to_dec,
) )
from piker.data._source import Symbol
from piker.clearing._messages import ( from piker.clearing._messages import (
Order, Order,
Status, Status,
@ -470,12 +473,14 @@ async def trades_dialogue(
with ( with (
open_pps( open_pps(
'kraken', 'kraken',
acctid acctid,
write_on_exit=True,
) as table, ) as table,
open_trade_ledger( open_trade_ledger(
'kraken', 'kraken',
acctid acctid,
) as ledger_dict, ) as ledger_dict,
): ):
# transaction-ify the ledger entries # transaction-ify the ledger entries
@ -494,7 +499,10 @@ async def trades_dialogue(
# what amount of trades-transactions need # what amount of trades-transactions need
# to be reloaded. # to be reloaded.
balances = await client.get_balances() balances = await client.get_balances()
# await tractor.breakpoint()
for dst, size in balances.items(): for dst, size in balances.items():
# we don't care about tracking positions # we don't care about tracking positions
# in the user's source fiat currency. # in the user's source fiat currency.
if ( if (
@ -508,45 +516,20 @@ async def trades_dialogue(
) )
continue continue
def get_likely_pair(
dst: str,
bsuid: str,
src_fiat: str = src_fiat
) -> str:
'''
Attempt to get the likely trading pair masting
a given destination asset `dst: str`.
'''
try:
src_name_start = bsuid.rindex(src_fiat)
except (
ValueError, # substr not found
):
# TODO: handle nested positions..(i.e.
# positions where the src fiat was used to
# buy some other dst which was furhter used
# to buy another dst..)
log.warning(
f'No src fiat {src_fiat} found in {bsuid}?'
)
return
likely_dst = bsuid[:src_name_start]
if likely_dst == dst:
return bsuid
def has_pp( def has_pp(
dst: str, dst: str,
size: float, size: float,
) -> Position | bool: ) -> Position | None:
src2dst: dict[str, str] = {} src2dst: dict[str, str] = {}
for bsuid in table.pps: for bsuid in table.pps:
likely_pair = get_likely_pair(dst, bsuid) likely_pair = get_likely_pair(
src_fiat,
dst,
bsuid,
)
if likely_pair: if likely_pair:
src2dst[src_fiat] = dst src2dst[src_fiat] = dst
@ -574,7 +557,7 @@ async def trades_dialogue(
) )
return pp return pp
return False return None # signal no entry
pos = has_pp(dst, size) pos = has_pp(dst, size)
if not pos: if not pos:
@ -602,7 +585,11 @@ async def trades_dialogue(
# yet and thus this likely pair grabber will # yet and thus this likely pair grabber will
# likely fail. # likely fail.
for bsuid in table.pps: for bsuid in table.pps:
likely_pair = get_likely_pair(dst, bsuid) likely_pair = get_likely_pair(
src_fiat,
dst,
bsuid,
)
if likely_pair: if likely_pair:
break break
else: else:
@ -724,8 +711,8 @@ async def handle_order_updates(
''' '''
Main msg handling loop for all things order management. Main msg handling loop for all things order management.
This code is broken out to make the context explicit and state variables This code is broken out to make the context explicit and state
defined in the signature clear to the reader. variables defined in the signature clear to the reader.
''' '''
async for msg in ws_stream: async for msg in ws_stream:
@ -1204,7 +1191,13 @@ def norm_trade_records(
fqsn, fqsn,
info={ info={
'lot_size_digits': pair_info.lot_decimals, 'lot_size_digits': pair_info.lot_decimals,
'lot_tick_size': digits_to_dec(
pair_info.lot_decimals,
),
'tick_size_digits': pair_info.pair_decimals, 'tick_size_digits': pair_info.pair_decimals,
'price_tick_size': digits_to_dec(
pair_info.pair_decimals,
),
'asset_type': 'crypto', 'asset_type': 'crypto',
}, },
) )

View File

@ -25,7 +25,7 @@ from bidict import bidict
from ..data._source import Symbol from ..data._source import Symbol
from ..data.types import Struct from ..data.types import Struct
from ..pp import Position from ..accounting import Position
_size_units = bidict({ _size_units = bidict({

View File

@ -39,7 +39,7 @@ import tractor
from .. import data from .. import data
from ..data.types import Struct from ..data.types import Struct
from ..data._source import Symbol from ..data._source import Symbol
from ..pp import ( from ..accounting import (
Position, Position,
Transaction, Transaction,
open_trade_ledger, open_trade_ledger,
@ -58,8 +58,6 @@ from ._messages import (
BrokerdError, BrokerdError,
) )
from ..config import load
log = get_logger(__name__) log = get_logger(__name__)

View File

@ -90,6 +90,21 @@ def float_digits(
return int(-Decimal(str(value)).as_tuple().exponent) return int(-Decimal(str(value)).as_tuple().exponent)
def digits_to_dec(
ndigits: int,
) -> Decimal:
'''
Return the minimum float value for an input integer value.
eg. 3 -> 0.001
'''
if ndigits == 0:
return Decimal('0')
return Decimal('0.' + '0'*(ndigits-1) + '1')
def ohlc_zeros(length: int) -> np.ndarray: def ohlc_zeros(length: int) -> np.ndarray:
"""Construct an OHLC field formatted structarray. """Construct an OHLC field formatted structarray.
@ -213,10 +228,13 @@ class Symbol(Struct):
return Symbol( return Symbol(
key=symbol, key=symbol,
tick_size=tick_size, tick_size=tick_size,
lot_tick_size=lot_size, lot_tick_size=lot_size,
tick_size_digits=float_digits(tick_size), tick_size_digits=float_digits(tick_size),
lot_size_digits=float_digits(lot_size), lot_size_digits=float_digits(lot_size),
suffix=suffix, suffix=suffix,
broker_info={broker: info}, broker_info={broker: info},
) )

View File

@ -47,7 +47,7 @@ from ..calc import (
puterize, puterize,
) )
from ..clearing._allocate import Allocator from ..clearing._allocate import Allocator
from ..pp import Position from ..accounting import Position
from ..data._normalize import iterticks from ..data._normalize import iterticks
from ..data.feed import ( from ..data.feed import (
Feed, Feed,

View File

@ -37,7 +37,7 @@ import trio
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from .. import config from .. import config
from ..pp import Position from ..accounting import Position
from ..clearing._client import open_ems, OrderBook from ..clearing._client import open_ems, OrderBook
from ..clearing._allocate import ( from ..clearing._allocate import (
mk_allocator, mk_allocator,

View File

@ -16,7 +16,7 @@ from functools import partial
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.accounting import (
open_pps, open_pps,
) )