From 34cf081c961883a224377313b39a2ecaad1a35c7 Mon Sep 17 00:00:00 2001 From: agentson Date: Fri, 27 Feb 2026 08:46:22 +0900 Subject: [PATCH] fix: backfill split pnl migration and harden partial pnl inputs --- src/db.py | 14 ++++++++++++-- tests/test_db.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/db.py b/src/db.py index c8638c4..96fb1c7 100644 --- a/src/db.py +++ b/src/db.py @@ -60,6 +60,16 @@ def init_db(db_path: str) -> sqlite3.Connection: conn.execute("ALTER TABLE trades ADD COLUMN strategy_pnl REAL DEFAULT 0.0") if "fx_pnl" not in columns: conn.execute("ALTER TABLE trades ADD COLUMN fx_pnl REAL DEFAULT 0.0") + # Backfill legacy rows where only pnl existed before split accounting columns. + conn.execute( + """ + UPDATE trades + SET strategy_pnl = pnl, fx_pnl = 0.0 + WHERE pnl != 0.0 + AND strategy_pnl = 0.0 + AND fx_pnl = 0.0 + """ + ) # Context tree tables for multi-layered memory management conn.execute( @@ -211,9 +221,9 @@ def log_trade( strategy_pnl = pnl fx_pnl = 0.0 elif strategy_pnl is None: - strategy_pnl = pnl - float(fx_pnl or 0.0) + strategy_pnl = pnl - float(fx_pnl or 0.0) if pnl != 0.0 else 0.0 elif fx_pnl is None: - fx_pnl = pnl - float(strategy_pnl) + fx_pnl = pnl - float(strategy_pnl) if pnl != 0.0 else 0.0 if pnl == 0.0 and (strategy_pnl or fx_pnl): pnl = float(strategy_pnl) + float(fx_pnl) diff --git a/tests/test_db.py b/tests/test_db.py index 9705ca2..49b822c 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -184,6 +184,13 @@ def test_mode_migration_adds_column_to_existing_db() -> None: decision_id TEXT )""" ) + old_conn.execute( + """ + INSERT INTO trades ( + timestamp, stock_code, action, confidence, rationale, quantity, price, pnl + ) VALUES ('2026-01-01T00:00:00+00:00', 'AAPL', 'SELL', 90, 'legacy', 1, 100.0, 123.45) + """ + ) old_conn.commit() old_conn.close() @@ -194,6 +201,13 @@ def test_mode_migration_adds_column_to_existing_db() -> None: assert "mode" in columns assert "strategy_pnl" in columns assert "fx_pnl" in columns + migrated = conn.execute( + "SELECT pnl, strategy_pnl, fx_pnl FROM trades WHERE stock_code='AAPL' LIMIT 1" + ).fetchone() + assert migrated is not None + assert migrated[0] == 123.45 + assert migrated[1] == 123.45 + assert migrated[2] == 0.0 conn.close() finally: os.unlink(db_path) @@ -241,3 +255,25 @@ def test_log_trade_backward_compat_sets_strategy_pnl_from_pnl() -> None: assert row[0] == 50.0 assert row[1] == 50.0 assert row[2] == 0.0 + + +def test_log_trade_partial_fx_input_does_not_infer_negative_strategy_pnl() -> None: + conn = init_db(":memory:") + log_trade( + conn=conn, + stock_code="AAPL", + action="SELL", + confidence=70, + rationale="fx only", + pnl=0.0, + fx_pnl=10.0, + market="US_NASDAQ", + exchange_code="NASD", + ) + row = conn.execute( + "SELECT pnl, strategy_pnl, fx_pnl FROM trades ORDER BY id DESC LIMIT 1" + ).fetchone() + assert row is not None + assert row[0] == 10.0 + assert row[1] == 0.0 + assert row[2] == 10.0