fix: backtest force-closes open positions at end + uses real market prices for exits

This commit is contained in:
Celes Renata
2026-04-18 00:03:51 +00:00
parent 6136a767da
commit ee5fd30398
+42 -6
View File
@@ -212,7 +212,7 @@ class BacktestReplay:
)
# Simulate simple exit logic: close positions held > 5 days
# (simplified — real engine uses stop-loss/take-profit)
# or use actual market price if available
tickers_to_close = []
for ticker, pos_info in simulated_positions.items():
hold_days = (current_date - pos_info["entry_date"]).days
@@ -221,8 +221,8 @@ class BacktestReplay:
for ticker in tickers_to_close:
pos_info = simulated_positions.pop(ticker)
# Simulate a small random-ish exit based on entry price
exit_price = pos_info["entry_price"] * 1.01 # simplified
# Use actual market price if available, otherwise estimate
exit_price = company_prices.get(ticker, pos_info["entry_price"] * 1.01)
qty = pos_info["quantity"]
pnl = (exit_price - pos_info["entry_price"]) * qty
pnl_pct = (
@@ -253,10 +253,10 @@ class BacktestReplay:
0, portfolio_state.open_position_count - 1
)
# Compute daily portfolio value
# Compute daily portfolio value using latest market prices
positions_value = sum(
p["entry_price"] * p["quantity"]
for p in simulated_positions.values()
company_prices.get(t, p["entry_price"]) * p["quantity"]
for t, p in simulated_positions.items()
)
current_value = portfolio_state.active_pool + positions_value
portfolio_state.total_value = current_value
@@ -277,6 +277,42 @@ class BacktestReplay:
current_date += timedelta(days=1)
# Force-close any remaining open positions at end of backtest
# using the latest available market prices
for ticker in list(simulated_positions.keys()):
pos_info = simulated_positions.pop(ticker)
exit_price = company_prices.get(ticker, pos_info["entry_price"])
qty = pos_info["quantity"]
pnl = (exit_price - pos_info["entry_price"]) * qty
pnl_pct = (
(exit_price - pos_info["entry_price"]) / pos_info["entry_price"]
if pos_info["entry_price"] > 0
else 0.0
)
hold_duration = timedelta(
days=(config.end_date - pos_info["entry_date"]).days
)
trade = ClosedTrade(
ticker=ticker,
entry_price=pos_info["entry_price"],
exit_price=exit_price,
quantity=qty,
pnl=pnl,
pnl_pct=pnl_pct,
hold_duration=hold_duration,
recommendation_id=pos_info.get("recommendation_id"),
)
closed_trades.append(trade)
trade_log.append(self._perf.compute_trade_metrics(trade))
portfolio_state.active_pool += exit_price * qty
if closed_trades:
logger.info(
"Backtest %s completed: %d trades, final value=$%.2f",
backtest_id, len(closed_trades), portfolio_state.active_pool,
)
# Compute final metrics
metrics = self._perf.compute_metrics(
closed_trades=closed_trades,