From cdd5a218a7a63d3de79ea77ad056253898b15fd5 Mon Sep 17 00:00:00 2001 From: agentson Date: Sun, 22 Feb 2026 11:49:03 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20CB=20=EA=B2=8C=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=EC=86=8C=EB=A5=BC=20context=20tree=20?= =?UTF-8?q?=E2=86=92=20system=5Fmetrics=20=EB=B3=84=EB=8F=84=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 대시보드 표시 전용 데이터를 AI 의사결정용 context tree에 저장하는 것은 관심사 분리 위반. system_metrics 경량 테이블을 신설하여 완전히 분리. (PR #197 코드리뷰 반영) - db.py: system_metrics 테이블 추가 (key/value/updated_at) - main.py: context_store.set_context(L6_DAILY) → db_conn.execute(system_metrics) - app.py: contexts 쿼리 → system_metrics 쿼리 - tests: _seed_cb_context를 system_metrics 삽입으로 변경 Co-Authored-By: Claude Sonnet 4.6 --- src/dashboard/app.py | 9 +++------ src/db.py | 12 ++++++++++++ src/main.py | 15 +++++++++------ tests/test_dashboard.py | 12 ++---------- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/dashboard/app.py b/src/dashboard/app.py index d1b1558..40ba5c3 100644 --- a/src/dashboard/app.py +++ b/src/dashboard/app.py @@ -84,14 +84,11 @@ def create_dashboard_app(db_path: str) -> FastAPI: pnl_pct_rows = conn.execute( """ SELECT key, value - FROM contexts - WHERE layer = 'L6_DAILY' - AND timeframe = ? - AND key LIKE 'portfolio_pnl_pct_%' + FROM system_metrics + WHERE key LIKE 'portfolio_pnl_pct_%' ORDER BY updated_at DESC LIMIT 20 - """, - (today,), + """ ).fetchall() current_pnl_pct: float | None = None if pnl_pct_rows: diff --git a/src/db.py b/src/db.py index ade215b..60dae11 100644 --- a/src/db.py +++ b/src/db.py @@ -138,6 +138,18 @@ def init_db(db_path: str) -> sqlite3.Connection: " ON trades (stock_code, market, timestamp DESC)" ) + # Lightweight key-value store for trading system runtime metrics (dashboard use only) + # Intentionally separate from the AI context tree to preserve separation of concerns. + conn.execute( + """ + CREATE TABLE IF NOT EXISTS system_metrics ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL, + updated_at TEXT NOT NULL + ) + """ + ) + conn.commit() return conn diff --git a/src/main.py b/src/main.py index 166b241..3a06b13 100644 --- a/src/main.py +++ b/src/main.py @@ -430,13 +430,16 @@ async def trading_cycle( {"volume_ratio": candidate.volume_ratio}, ) - # Store latest pnl_pct in L6 (daily P&L layer) so the dashboard can display the CB gauge - context_store.set_context( - ContextLayer.L6_DAILY, - datetime.now(UTC).date().isoformat(), - f"portfolio_pnl_pct_{market.code}", - {"pnl_pct": round(pnl_pct, 4)}, + # Write pnl_pct to system_metrics (dashboard-only table, separate from AI context tree) + db_conn.execute( + "INSERT OR REPLACE INTO system_metrics (key, value, updated_at) VALUES (?, ?, ?)", + ( + f"portfolio_pnl_pct_{market.code}", + json.dumps({"pnl_pct": round(pnl_pct, 4)}), + datetime.now(UTC).isoformat(), + ), ) + db_conn.commit() # Build portfolio data for global rule evaluation portfolio_data = { diff --git a/tests/test_dashboard.py b/tests/test_dashboard.py index b88cb5f..dd3b8cb 100644 --- a/tests/test_dashboard.py +++ b/tests/test_dashboard.py @@ -355,20 +355,12 @@ def test_positions_empty_when_no_trades(tmp_path: Path) -> None: def _seed_cb_context(conn: sqlite3.Connection, pnl_pct: float, market: str = "KR") -> None: import json as _json - from datetime import UTC, datetime - today = datetime.now(UTC).date().isoformat() conn.execute( - """ - INSERT OR REPLACE INTO contexts (layer, timeframe, key, value, created_at, updated_at) - VALUES (?, ?, ?, ?, ?, ?) - """, + "INSERT OR REPLACE INTO system_metrics (key, value, updated_at) VALUES (?, ?, ?)", ( - "L6_DAILY", - today, f"portfolio_pnl_pct_{market}", _json.dumps({"pnl_pct": pnl_pct}), - f"{today}T10:00:00+00:00", - f"{today}T10:00:00+00:00", + "2026-02-22T10:00:00+00:00", ), ) conn.commit()