fix: market_data에 unrealized_pnl_pct/holding_days 추가하여 SELL 시나리오 정상화 (#259) #263

Merged
jihoson merged 2 commits from feature/issue-259-market-data-pnl-holding-days into main 2026-02-26 00:23:56 +09:00
3 changed files with 19 additions and 2 deletions

View File

@@ -254,7 +254,7 @@ def get_open_position(
"""Return open position if latest trade is BUY, else None.""" """Return open position if latest trade is BUY, else None."""
cursor = conn.execute( cursor = conn.execute(
""" """
SELECT action, decision_id, price, quantity SELECT action, decision_id, price, quantity, timestamp
FROM trades FROM trades
WHERE stock_code = ? WHERE stock_code = ?
AND market = ? AND market = ?
@@ -266,7 +266,7 @@ def get_open_position(
row = cursor.fetchone() row = cursor.fetchone()
if not row or row[0] != "BUY": if not row or row[0] != "BUY":
return None 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( def get_recent_symbols(

View File

@@ -576,6 +576,22 @@ async def trading_cycle(
market_data["rsi"] = candidate.rsi market_data["rsi"] = candidate.rsi
market_data["volume_ratio"] = candidate.volume_ratio 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) # 1.3. Record L7 real-time context (market-scoped keys)
timeframe = datetime.now(UTC).isoformat() timeframe = datetime.now(UTC).isoformat()
context_store.set_context( context_store.set_context(

View File

@@ -28,6 +28,7 @@ def mock_settings() -> Settings:
KIS_APP_SECRET="test_secret", KIS_APP_SECRET="test_secret",
KIS_ACCOUNT_NO="12345678-01", KIS_ACCOUNT_NO="12345678-01",
GEMINI_API_KEY="test_gemini_key", GEMINI_API_KEY="test_gemini_key",
MODE="paper", # Explicitly set to avoid .env MODE=live override
) )