Change `ledger` CLI to use new `open_brokerd_dialog()`
Instead of effectively (and poorly) duplicating the trade dialog setup logic, just use the new helper we exposed in the EMS module B) Also, handle paper accounts that have no ledger / positions existing.basic_buy_bot
parent
b15e736e3e
commit
bb02775cab
|
@ -19,8 +19,9 @@ CLI front end for trades ledger and position tracking management.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
AsyncContextManager,
|
||||||
)
|
)
|
||||||
|
from types import ModuleType
|
||||||
|
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.markdown import Markdown
|
from rich.markdown import Markdown
|
||||||
|
@ -45,7 +46,11 @@ def broker_init(
|
||||||
|
|
||||||
**start_actor_kwargs,
|
**start_actor_kwargs,
|
||||||
|
|
||||||
) -> dict:
|
) -> tuple[
|
||||||
|
ModuleType,
|
||||||
|
dict,
|
||||||
|
AsyncContextManager,
|
||||||
|
]:
|
||||||
'''
|
'''
|
||||||
Given an input broker name, load all named arguments
|
Given an input broker name, load all named arguments
|
||||||
which can be passed to a daemon + context spawn for
|
which can be passed to a daemon + context spawn for
|
||||||
|
@ -83,13 +88,9 @@ def broker_init(
|
||||||
from ..brokers._daemon import _setup_persistent_brokerd
|
from ..brokers._daemon import _setup_persistent_brokerd
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
brokermod,
|
||||||
start_actor_kwargs, # to `ActorNursery.start_actor()`
|
start_actor_kwargs, # to `ActorNursery.start_actor()`
|
||||||
_setup_persistent_brokerd, # service task ep
|
_setup_persistent_brokerd, # deamon service task ep
|
||||||
getattr( # trades endpoint
|
|
||||||
brokermod,
|
|
||||||
'trades_dialogue',
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -119,10 +120,11 @@ def sync(
|
||||||
console.print(md)
|
console.print(md)
|
||||||
return
|
return
|
||||||
|
|
||||||
start_kwargs, _, trades_ep = broker_init(
|
brokermod, start_kwargs, deamon_ep = broker_init(
|
||||||
brokername,
|
brokername,
|
||||||
loglevel=loglevel,
|
loglevel=loglevel,
|
||||||
)
|
)
|
||||||
|
brokername: str = brokermod.name
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
|
|
||||||
|
@ -136,96 +138,112 @@ def sync(
|
||||||
|
|
||||||
tractor.open_nursery() as an,
|
tractor.open_nursery() as an,
|
||||||
):
|
):
|
||||||
log.info(
|
try:
|
||||||
f'Piker runtime up as {actor.uid}@{sockaddr}'
|
log.info(
|
||||||
)
|
f'Piker runtime up as {actor.uid}@{sockaddr}'
|
||||||
|
)
|
||||||
|
|
||||||
portal = await an.start_actor(
|
portal = await an.start_actor(
|
||||||
loglevel=loglevel,
|
|
||||||
debug_mode=pdb,
|
|
||||||
**start_kwargs,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (
|
|
||||||
brokername == 'paper'
|
|
||||||
or trades_ep is None
|
|
||||||
):
|
|
||||||
from ..clearing import _paper_engine as paper
|
|
||||||
open_trades_endpoint = paper.open_paperboi(
|
|
||||||
fqme=None, # tell paper to not start clearing loop
|
|
||||||
broker=brokername,
|
|
||||||
loglevel=loglevel,
|
loglevel=loglevel,
|
||||||
|
debug_mode=pdb,
|
||||||
|
**start_kwargs,
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
# open live brokerd trades endpoint
|
from ..clearing import (
|
||||||
open_trades_endpoint = portal.open_context(
|
open_brokerd_dialog,
|
||||||
trades_ep,
|
)
|
||||||
|
brokerd_stream: tractor.MsgStream
|
||||||
|
|
||||||
|
async with open_brokerd_dialog(
|
||||||
|
brokermod,
|
||||||
|
portal,
|
||||||
|
exec_mode=(
|
||||||
|
'paper' if account == 'paper'
|
||||||
|
else 'live'
|
||||||
|
),
|
||||||
loglevel=loglevel,
|
loglevel=loglevel,
|
||||||
)
|
) as (
|
||||||
|
brokerd_stream,
|
||||||
|
pp_msg_table,
|
||||||
|
accounts,
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
assert len(accounts) == 1
|
||||||
|
if (
|
||||||
|
not pp_msg_table
|
||||||
|
and account == 'paper'
|
||||||
|
):
|
||||||
|
console.print(
|
||||||
|
'[yellow underline]'
|
||||||
|
f'No pps found for `{brokername}.paper` account!\n'
|
||||||
|
'Do you even have any paper ledger files?'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
positions: dict[str, Any]
|
pps_by_symbol: dict[str, BrokerdPosition] = pp_msg_table[
|
||||||
accounts: list[str]
|
brokername,
|
||||||
async with (
|
account,
|
||||||
open_trades_endpoint as (
|
]
|
||||||
brokerd_ctx,
|
|
||||||
(positions, accounts),
|
summary: str = (
|
||||||
),
|
'[dim underline]Piker Position Summary[/] '
|
||||||
):
|
f'[dim blue underline]{brokername}[/]'
|
||||||
assert len(accounts) == 1
|
'[dim].[/]'
|
||||||
summary: str = (
|
f'[blue underline]{account}[/]'
|
||||||
'[dim underline]Piker Position Summary[/] '
|
f'[dim underline] -> total pps: [/]'
|
||||||
f'[dim blue underline]{brokername}[/]'
|
f'[green]{len(pps_by_symbol)}[/]\n'
|
||||||
'[dim].[/]'
|
|
||||||
f'[blue underline]{account}[/]'
|
|
||||||
f'[dim underline] -> total pps: [/]'
|
|
||||||
f'[green]{len(positions)}[/]\n'
|
|
||||||
)
|
|
||||||
for ppdict in positions:
|
|
||||||
ppmsg = BrokerdPosition(**ppdict)
|
|
||||||
size = ppmsg.size
|
|
||||||
if size:
|
|
||||||
ppu: float = round(
|
|
||||||
ppmsg.avg_price,
|
|
||||||
ndigits=2,
|
|
||||||
)
|
)
|
||||||
cost_basis: str = humanize(size * ppu)
|
# for ppdict in positions:
|
||||||
h_size: str = humanize(size)
|
for fqme, ppmsg in pps_by_symbol.items():
|
||||||
|
# ppmsg = BrokerdPosition(**ppdict)
|
||||||
|
size = ppmsg.size
|
||||||
|
if size:
|
||||||
|
ppu: float = round(
|
||||||
|
ppmsg.avg_price,
|
||||||
|
ndigits=2,
|
||||||
|
)
|
||||||
|
cost_basis: str = humanize(size * ppu)
|
||||||
|
h_size: str = humanize(size)
|
||||||
|
|
||||||
if size < 0:
|
if size < 0:
|
||||||
pcolor = 'red'
|
pcolor = 'red'
|
||||||
else:
|
else:
|
||||||
pcolor = 'green'
|
pcolor = 'green'
|
||||||
|
|
||||||
# sematic-highlight of fqme
|
# sematic-highlight of fqme
|
||||||
fqme = ppmsg.symbol
|
fqme = ppmsg.symbol
|
||||||
tokens = fqme.split('.')
|
tokens = fqme.split('.')
|
||||||
styled_fqme = f'[blue underline]{tokens[0]}[/]'
|
styled_fqme = f'[blue underline]{tokens[0]}[/]'
|
||||||
for tok in tokens[1:]:
|
for tok in tokens[1:]:
|
||||||
styled_fqme += '[dim].[/]'
|
styled_fqme += '[dim].[/]'
|
||||||
styled_fqme += f'[dim blue underline]{tok}[/]'
|
styled_fqme += f'[dim blue underline]{tok}[/]'
|
||||||
|
|
||||||
# TODO: instead display in a ``rich.Table``?
|
# TODO: instead display in a ``rich.Table``?
|
||||||
summary += (
|
summary += (
|
||||||
styled_fqme +
|
styled_fqme +
|
||||||
'[dim]: [/]'
|
'[dim]: [/]'
|
||||||
f'[{pcolor}]{h_size}[/]'
|
f'[{pcolor}]{h_size}[/]'
|
||||||
'[dim blue]u @[/]'
|
'[dim blue]u @[/]'
|
||||||
f'[{pcolor}]{ppu}[/]'
|
f'[{pcolor}]{ppu}[/]'
|
||||||
'[dim blue] = [/]'
|
'[dim blue] = [/]'
|
||||||
f'[{pcolor}]$ {cost_basis}\n[/]'
|
f'[{pcolor}]$ {cost_basis}\n[/]'
|
||||||
)
|
)
|
||||||
|
|
||||||
console.print(summary)
|
console.print(summary)
|
||||||
|
|
||||||
# exit via ctx cancellation.
|
finally:
|
||||||
await brokerd_ctx.cancel(timeout=1)
|
# exit via ctx cancellation.
|
||||||
# TODO: once ported to newer tractor branch we should
|
brokerd_ctx: tractor.Context = brokerd_stream._ctx
|
||||||
# be able to do a loop like this:
|
await brokerd_ctx.cancel(timeout=1)
|
||||||
# while brokerd_ctx.cancel_called_remote is None:
|
|
||||||
# await trio.sleep(0.01)
|
|
||||||
# await brokerd_ctx.cancel()
|
|
||||||
|
|
||||||
await portal.cancel_actor()
|
# TODO: once ported to newer tractor branch we should
|
||||||
|
# be able to do a loop like this:
|
||||||
|
# while brokerd_ctx.cancel_called_remote is None:
|
||||||
|
# await trio.sleep(0.01)
|
||||||
|
# await brokerd_ctx.cancel()
|
||||||
|
|
||||||
|
finally:
|
||||||
|
await portal.cancel_actor()
|
||||||
|
|
||||||
trio.run(main)
|
trio.run(main)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue