From 3e777a5ab874c5d4e687d417d2d7cdd788805f25 Mon Sep 17 00:00:00 2001 From: agentson Date: Thu, 26 Feb 2026 00:21:39 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20mock=5Fsettings=EC=97=90=20MODE=3D'p?= =?UTF-8?q?aper'=20=EB=AA=85=EC=8B=9C=ED=95=98=EC=97=AC=20paper=20?= =?UTF-8?q?=EB=AA=A8=EB=93=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=8B=A4?= =?UTF-8?q?=ED=8C=A8=20=EC=88=98=EC=A0=95=20(#261)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mock_settings fixture에 MODE 미지정 시 .env의 MODE=live가 적용되어 paper TR_ID를 검증하는 테스트 3개가 실패. - test_buy_market_order: VTTT1002U 기대 → TTTT1002U 실제 - test_sell_limit_order: VTTT1001U 기대 → TTTT1006U 실제 - test_us_paper_uses_vttt1004u: VTTT1004U 기대 → TTTT1004U 실제 Co-Authored-By: Claude Sonnet 4.6 --- tests/test_overseas_broker.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_overseas_broker.py b/tests/test_overseas_broker.py index 0f47bec..bd74cd9 100644 --- a/tests/test_overseas_broker.py +++ b/tests/test_overseas_broker.py @@ -28,6 +28,7 @@ def mock_settings() -> Settings: KIS_APP_SECRET="test_secret", KIS_ACCOUNT_NO="12345678-01", GEMINI_API_KEY="test_gemini_key", + MODE="paper", # Explicitly set to avoid .env MODE=live override ) -- 2.49.1 From 7aa5fedc12a209ecfc9ee2472c4057e306690577 Mon Sep 17 00:00:00 2001 From: agentson Date: Thu, 26 Feb 2026 00:23:28 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20market=5Fdata=EC=97=90=20unrealized?= =?UTF-8?q?=5Fpnl=5Fpct/holding=5Fdays=20=EC=B6=94=EA=B0=80=ED=95=98?= =?UTF-8?q?=EC=97=AC=20SELL=20=EC=8B=9C=EB=82=98=EB=A6=AC=EC=98=A4=20?= =?UTF-8?q?=EC=A0=95=EC=83=81=ED=99=94=20(#259)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit trading_cycle()의 market_data에 보유 포지션 정보가 없어 Condition requires 'unrealized_pnl_pct' but key missing from market_data 경고 발생. 보유 종목(NVDA 등)의 take-profit/stop-loss 시나리오가 평가 불가하여 HOLD(confidence=0) 고착. - get_open_position()에 timestamp 컬럼 추가 - market_data 구성 시 open_position 조회 후 아래 키 추가: - unrealized_pnl_pct: (current_price - entry_price) / entry_price * 100 - holding_days: 매수일로부터 경과 일수 Co-Authored-By: Claude Sonnet 4.6 --- src/db.py | 4 ++-- src/main.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/db.py b/src/db.py index fc2070e..d25239a 100644 --- a/src/db.py +++ b/src/db.py @@ -254,7 +254,7 @@ def get_open_position( """Return open position if latest trade is BUY, else None.""" cursor = conn.execute( """ - SELECT action, decision_id, price, quantity + SELECT action, decision_id, price, quantity, timestamp FROM trades WHERE stock_code = ? AND market = ? @@ -266,7 +266,7 @@ def get_open_position( row = cursor.fetchone() if not row or row[0] != "BUY": return None - return {"decision_id": row[1], "price": row[2], "quantity": row[3]} + return {"decision_id": row[1], "price": row[2], "quantity": row[3], "timestamp": row[4]} def get_recent_symbols( diff --git a/src/main.py b/src/main.py index 2d52ae2..67115a5 100644 --- a/src/main.py +++ b/src/main.py @@ -576,6 +576,22 @@ async def trading_cycle( market_data["rsi"] = candidate.rsi market_data["volume_ratio"] = candidate.volume_ratio + # Enrich market_data with holding info for SELL/HOLD scenario conditions + open_pos = get_open_position(db_conn, stock_code, market.code) + if open_pos and current_price > 0: + entry_price = safe_float(open_pos.get("price"), 0.0) + if entry_price > 0: + market_data["unrealized_pnl_pct"] = ( + (current_price - entry_price) / entry_price * 100 + ) + entry_ts = open_pos.get("timestamp") + if entry_ts: + try: + entry_date = datetime.fromisoformat(entry_ts).date() + market_data["holding_days"] = (datetime.now(UTC).date() - entry_date).days + except (ValueError, TypeError): + pass + # 1.3. Record L7 real-time context (market-scoped keys) timeframe = datetime.now(UTC).isoformat() context_store.set_context( -- 2.49.1