From 31d68181308207bc8e724e2b5c069c08ae148b19 Mon Sep 17 00:00:00 2001 From: Bill Date: Fri, 7 Nov 2025 16:38:33 -0500 Subject: [PATCH] fix: enable cross-job portfolio continuity in get_starting_holdings Remove job_id filter from get_starting_holdings() SQL JOIN to enable holdings continuity across jobs. This completes the cross-job portfolio continuity fix started in the previous commit. Root cause: get_starting_holdings() joined on job_id, preventing it from finding previous day's holdings when queried from a different job. This caused starting_position.holdings to be empty in API results for new jobs even though starting_cash was correctly retrieved. Changes: - api/database.py: Remove job_id from JOIN condition in get_starting_holdings() - tests/unit/test_database_helpers.py: Add test for cross-job holdings retrieval Together with the previous commit fixing get_previous_trading_day(), this ensures complete portfolio continuity (both cash and holdings) across jobs. --- api/database.py | 4 ++- tests/unit/test_database_helpers.py | 53 +++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/api/database.py b/api/database.py index 6207ae1..020b722 100644 --- a/api/database.py +++ b/api/database.py @@ -661,6 +661,9 @@ class Database: def get_starting_holdings(self, trading_day_id: int) -> list: """Get starting holdings from previous day's ending holdings. + NOTE: Queries across ALL jobs for the given model to enable portfolio + continuity even when new jobs are created with overlapping date ranges. + Returns: List of dicts with keys: symbol, quantity Empty list if first trading day @@ -671,7 +674,6 @@ class Database: SELECT td_prev.id FROM trading_days td_current JOIN trading_days td_prev ON - td_prev.job_id = td_current.job_id AND td_prev.model = td_current.model AND td_prev.date < td_current.date WHERE td_current.id = ? diff --git a/tests/unit/test_database_helpers.py b/tests/unit/test_database_helpers.py index 87e58e5..df7f5da 100644 --- a/tests/unit/test_database_helpers.py +++ b/tests/unit/test_database_helpers.py @@ -262,6 +262,59 @@ class TestDatabaseHelpers: assert holdings[0]["symbol"] == "AAPL" assert holdings[0]["quantity"] == 10 + def test_get_starting_holdings_across_jobs(self, db): + """Test starting holdings retrieval across different jobs (cross-job continuity).""" + # Setup: Create two jobs + db.connection.execute( + "INSERT INTO jobs (job_id, status, config_path, date_range, models, created_at) VALUES (?, ?, ?, ?, ?, ?)", + ("job-1", "completed", "config.json", "2025-10-07,2025-10-07", "deepseek-chat-v3.1", "2025-11-07T00:00:00Z") + ) + db.connection.execute( + "INSERT INTO jobs (job_id, status, config_path, date_range, models, created_at) VALUES (?, ?, ?, ?, ?, ?)", + ("job-2", "running", "config.json", "2025-10-08,2025-10-08", "deepseek-chat-v3.1", "2025-11-07T01:00:00Z") + ) + + # Day 1 in job-1 with holdings + day1_id = db.create_trading_day( + job_id="job-1", + model="deepseek-chat-v3.1", + date="2025-10-07", + starting_cash=10000.0, + starting_portfolio_value=10000.0, + daily_profit=214.58, + daily_return_pct=2.15, + ending_cash=329.825, + ending_portfolio_value=10666.135 + ) + db.create_holding(day1_id, "AAPL", 10) + db.create_holding(day1_id, "AMD", 4) + db.create_holding(day1_id, "MSFT", 8) + db.create_holding(day1_id, "NVDA", 12) + db.create_holding(day1_id, "TSLA", 1) + + # Day 2 in job-2 (different job) + day2_id = db.create_trading_day( + job_id="job-2", + model="deepseek-chat-v3.1", + date="2025-10-08", + starting_cash=329.825, + starting_portfolio_value=10609.475, + daily_profit=-56.66, + daily_return_pct=-0.53, + ending_cash=33.62, + ending_portfolio_value=329.825 + ) + + # Test: Day 2 should get Day 1's holdings from different job + holdings = db.get_starting_holdings(day2_id) + + assert len(holdings) == 5 + assert {"symbol": "AAPL", "quantity": 10} in holdings + assert {"symbol": "AMD", "quantity": 4} in holdings + assert {"symbol": "MSFT", "quantity": 8} in holdings + assert {"symbol": "NVDA", "quantity": 12} in holdings + assert {"symbol": "TSLA", "quantity": 1} in holdings + def test_create_action(self, db): """Test creating an action record.""" db.connection.execute(