feat: persist unmanaged position lifecycle state

Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-09-12 13:24:51 +00:00
committed by GitHub
co-authored by genichin
parent ae2e598209
commit fe51d211d9
12 changed files with 1274 additions and 5 deletions
+104
View File
@@ -11,6 +11,8 @@ Subcommands:
cancel <slot_id> — cancel a pending order (control RPC; safeguarded)
sync — sync slot qty to broker ground truth (direct-DB; REQ-105)
verify — re-check C-case reconcile-warning (direct-DB; REQ-106)
unmanaged list — query durable unmanaged-position lifecycle state
unmanaged record — record explicit operator disposition + evidence reference
ledger backfill — evidence-backed historical ledger recovery
ledger skips list — query durable skipped-ledger records
ledger skips resolve — explicitly resolve a skipped-ledger record
@@ -640,6 +642,37 @@ def _build_parser() -> argparse.ArgumentParser:
)
_add_common(p_verify)
p_unmanaged = sub.add_parser(
"unmanaged",
help="List or annotate durable unmanaged broker positions (direct-DB)",
)
p_unmanaged_sub = p_unmanaged.add_subparsers(dest="unmanaged_cmd", required=True)
p_unmanaged_list = p_unmanaged_sub.add_parser(
"list",
help="List unmanaged broker positions with lifecycle state",
)
p_unmanaged_list.add_argument(
"--status",
choices=("active", "resolved", "all"),
default="active",
)
p_unmanaged_list.add_argument("--symbol", default=None)
_add_common(p_unmanaged_list)
p_unmanaged_record = p_unmanaged_sub.add_parser(
"record",
help="Record an explicit operator disposition for an unmanaged position",
)
p_unmanaged_record.add_argument("symbol")
p_unmanaged_record.add_argument(
"action",
choices=("investigating", "approved_exception", "managed_inclusion"),
)
p_unmanaged_record.add_argument("--evidence-ref", required=True)
p_unmanaged_record.add_argument("--note", default="")
p_unmanaged_record.add_argument("--actor", default="operator")
_add_common(p_unmanaged_record)
p_ledger = sub.add_parser(
"ledger",
help="Ledger operator tools (direct-DB enqueue + immediate writer path)",
@@ -971,6 +1004,73 @@ def _cmd_verify(args, cfg) -> int:
return exit_code
def _cmd_unmanaged_list(args, cfg) -> int:
from trex_core.adapters.portfolio.unmanaged_position_store import (
PostgreSQLUnmanagedPositionStore,
)
instance = _resolve_instance(args)
states = PostgreSQLUnmanagedPositionStore(cfg.dsn).list_states(
instance_key=instance,
lifecycle_state=args.status,
symbol=args.symbol,
)
payload = {
"instance": instance,
"status": args.status,
"count": len(states),
"states": states,
}
if args.json:
_emit(payload, as_json=True)
else:
print(
f"instance={instance} status={args.status} count={len(states)} "
f"symbol={args.symbol or '*'}"
)
for state in states:
print(
f" {state['symbol']}\tqty={state['quantity']}\t"
f"lifecycle={state['lifecycle_state']}\t"
f"first_seen={state['first_seen_at']}\tlast_seen={state['last_seen_at']}\t"
f"resolved_at={state['resolved_at']}\t"
f"operator_action={state.get('operator_action') or '-'}\t"
f"evidence_ref={state.get('evidence_ref') or '-'}"
)
return 0
def _cmd_unmanaged_record(args, cfg) -> int:
from trex_core.adapters.portfolio.unmanaged_position_store import (
PostgreSQLUnmanagedPositionStore,
)
instance = _resolve_instance(args)
state = PostgreSQLUnmanagedPositionStore(cfg.dsn).record_operator_action(
instance_key=instance,
symbol=args.symbol,
action=args.action,
evidence_ref=args.evidence_ref,
actor=args.actor,
note=args.note,
)
if state is None:
_fail(
f"unmanaged position not found: instance={instance} symbol={args.symbol}",
args.json,
)
return 1
payload = {"result": "recorded", "state": state}
if args.json:
_emit(payload, as_json=True)
else:
print(
f"recorded unmanaged position instance={instance} symbol={args.symbol} "
f"action={args.action} evidence_ref={args.evidence_ref}"
)
return 0
def _cmd_report(args, cfg) -> int:
"""REQ-088/110/111: holdings / today's fills / WAC realized PnL / pending — direct-DB."""
import psycopg
@@ -1434,6 +1534,10 @@ def main(argv: list[str] | None = None, *, rpc=None, confirm=None) -> int:
# REQ-106 (BAT-051): verify — direct-DB, C-case re-check (read-centric).
if args.cmd == "verify":
return _cmd_verify(args, cfg)
if args.cmd == "unmanaged" and getattr(args, "unmanaged_cmd", None) == "list":
return _cmd_unmanaged_list(args, cfg)
if args.cmd == "unmanaged" and getattr(args, "unmanaged_cmd", None) == "record":
return _cmd_unmanaged_record(args, cfg)
if args.cmd == "ledger" and getattr(args, "ledger_cmd", None) == "backfill":
return _cmd_ledger_backfill(args, cfg, confirm)