feat(execution,obs): 호가단위 스냅(dcy-030/#28) + 운영 관측성(dcy-029/#29) + REL-007 환류
dcy-030 (issue #28, P0 체결 복구): - REQ-083 KRX 호가단위(tick) 순수 유틸 (domain/pricing.py) - REQ-070 CHG: 슬리피지 실효가 호가단위 스냅 — 상한 준수(매수 floor/매도 ceil) + band<1tick 하한/상한 보장 + 부동소수 epsilon 가드. 147700→148400/54500→54700. - BAT-044 release-candidate dcy-029 (issue #29, 운영 관측성): - REQ-084 구조화 로깅 기반 (runtime/logging_setup.py): ISO8601 KST·레벨·Loki JSON ·correlation id(slot_id) - REQ-085 주문 결과 로깅 (event=order_submit|fill|reject) - REQ-087 거부 ERROR 가시성 (조용한 롤백 제거) - REQ-086 청산 reason+actor 동반 노출 (안전망/전략 구분, 신규 필드 없음) - REQ-088 운영 조회 CLI report (application/operator_report.py, 보유·오늘 체결 ·실현 PnL net·대기, direct-DB) - REQ-089 운영자 런북 docs/operations.md - REQ-090 baseline 정합 (runtime-flows·project-structure·interface-contract) - BAT-045(P0)·BAT-046(P1) release-candidate REL-007: confirmed → feedback-captured (운영 인스턴스 라이브 검증 + #28/#29 환류 정규화). BAT-037~041 released. 검증: 537 passed, 47 skipped, 회귀 0, ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -395,9 +395,67 @@ def _build_parser() -> argparse.ArgumentParser:
|
||||
_add_intent_guards(p_cancel)
|
||||
_add_common(p_cancel)
|
||||
|
||||
# REQ-088 (dcy-029): operator report — holdings / today fills / realized PnL /
|
||||
# pending. Direct-DB (DB-backed point-in-time aggregate, no new RPC).
|
||||
p_report = sub.add_parser(
|
||||
"report", help="Holdings, today's fills, realized PnL, pending orders (direct-DB)"
|
||||
)
|
||||
_add_common(p_report)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def _cmd_report(args, cfg) -> int:
|
||||
"""REQ-088: holdings / today's fills / realized PnL / pending — direct-DB."""
|
||||
import psycopg
|
||||
|
||||
from trex_core.application.operator_report import (
|
||||
Holding,
|
||||
PendingOrder,
|
||||
TradeRow,
|
||||
build_report,
|
||||
kst_day_bounds_utc,
|
||||
render_json,
|
||||
render_text,
|
||||
)
|
||||
|
||||
start_utc, end_utc = kst_day_bounds_utc()
|
||||
holdings: list[Holding] = []
|
||||
pending: list[PendingOrder] = []
|
||||
trades: list[TradeRow] = []
|
||||
with psycopg.connect(cfg.dsn) as conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"SELECT symbol, portfolio_id, qty, avg_price FROM daily_slot "
|
||||
"WHERE status = 'holding' ORDER BY symbol"
|
||||
)
|
||||
for symbol, pid, qty, avg in cur.fetchall():
|
||||
holdings.append(Holding(symbol or "", pid or "",
|
||||
float(qty or 0), float(avg or 0)))
|
||||
cur.execute(
|
||||
"SELECT symbol, portfolio_id, status FROM daily_slot "
|
||||
"WHERE status IN ('pending_buy','pending_sell') ORDER BY symbol"
|
||||
)
|
||||
for symbol, pid, status in cur.fetchall():
|
||||
pending.append(PendingOrder(symbol or "", pid or "", status))
|
||||
cur.execute(
|
||||
"SELECT asset_id, side, qty, price, fee, tax FROM ledger_outbox "
|
||||
"WHERE COALESCE(executed_at, created_at) >= %s "
|
||||
"AND COALESCE(executed_at, created_at) < %s",
|
||||
(start_utc, end_utc),
|
||||
)
|
||||
for asset_id, side, qty, price, fee, tax in cur.fetchall():
|
||||
trades.append(TradeRow(asset_id, side, float(qty), float(price),
|
||||
float(fee or 0), float(tax or 0)))
|
||||
|
||||
rep = build_report(holdings, pending, trades)
|
||||
if getattr(args, "json", False):
|
||||
print(_json.dumps(render_json(rep), ensure_ascii=False))
|
||||
else:
|
||||
print(render_text(rep))
|
||||
return 0
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None, *, rpc=None, confirm=None) -> int:
|
||||
"""CLI entrypoint.
|
||||
|
||||
@@ -417,6 +475,10 @@ def main(argv: list[str] | None = None, *, rpc=None, confirm=None) -> int:
|
||||
cfg = DaemonConfig.from_env()
|
||||
confirm = confirm or _default_confirm
|
||||
|
||||
# REQ-088 (dcy-029): operator report — direct-DB point-in-time aggregate.
|
||||
if args.cmd == "report":
|
||||
return _cmd_report(args, cfg)
|
||||
|
||||
# direct-DB control commands (pause/resume stay direct-DB, OQ-3).
|
||||
if args.cmd in ("pause", "resume", "paused"):
|
||||
store = PostgreSQLPauseStore(cfg.dsn)
|
||||
|
||||
Reference in New Issue
Block a user