fix: backtest replay field mapping and logging

- Map DB 'id' field to 'recommendation_id' for evaluate_recommendation()
- Ensure confidence is cast to float (asyncpg may return Decimal)
- Add per-day logging showing rec count, act/skip, positions, pool balance
- Helps diagnose why backtests produce 0 trades
This commit is contained in:
Celes Renata
2026-04-15 22:55:26 +00:00
parent 4501bbebd4
commit ff5055ee4e
+16
View File
@@ -127,6 +127,12 @@ class BacktestReplay:
rec["current_price"] = company_prices.get(ticker, 50.0) rec["current_price"] = company_prices.get(ticker, 50.0)
if "sector" not in rec or not rec.get("sector"): if "sector" not in rec or not rec.get("sector"):
rec["sector"] = company_sectors.get(ticker, "Unknown") rec["sector"] = company_sectors.get(ticker, "Unknown")
# Map 'id' to 'recommendation_id' for evaluate_recommendation()
if "recommendation_id" not in rec and "id" in rec:
rec["recommendation_id"] = str(rec["id"])
# Ensure confidence is a float
if rec.get("confidence") is not None:
rec["confidence"] = float(rec["confidence"])
recs_by_date.setdefault(d, []).append(rec) recs_by_date.setdefault(d, []).append(rec)
@@ -139,6 +145,8 @@ class BacktestReplay:
continue continue
day_recs = recs_by_date.get(current_date, []) day_recs = recs_by_date.get(current_date, [])
act_count = 0
skip_count = 0
# Process recommendations for this day # Process recommendations for this day
for rec in day_recs: for rec in day_recs:
@@ -164,6 +172,7 @@ class BacktestReplay:
) )
if decision.decision == "act": if decision.decision == "act":
act_count += 1
ticker = decision.ticker ticker = decision.ticker
price = rec.get("current_price", 0.0) price = rec.get("current_price", 0.0)
qty = decision.computed_share_quantity or 0 qty = decision.computed_share_quantity or 0
@@ -183,6 +192,13 @@ class BacktestReplay:
portfolio_state.active_pool -= cost portfolio_state.active_pool -= cost
portfolio_state.open_position_count += 1 portfolio_state.open_position_count += 1
if day_recs:
logger.info(
"Backtest day %s: %d recs, %d act, %d skip, positions=%d, pool=$%.2f",
current_date, len(day_recs), act_count, len(day_recs) - act_count,
len(simulated_positions), portfolio_state.active_pool,
)
# Simulate simple exit logic: close positions held > 5 days # Simulate simple exit logic: close positions held > 5 days
# (simplified — real engine uses stop-loss/take-profit) # (simplified — real engine uses stop-loss/take-profit)
tickers_to_close = [] tickers_to_close = []