feat: use day's low price for margin call evaluation

- Extend DailyClosePoint to include low, high, open (optional)
- Update Databento source to extract OHLC data from ohlcv-1d schema
- Update YFinance source to extract Low, High, Open from history
- Modify backtest engine to use worst-case (low) price for margin call detection

This ensures margin calls are evaluated at the day's worst price,
not just the closing price, providing more realistic risk assessment.
This commit is contained in:
Bu5hm4nn
2026-04-04 23:06:15 +02:00
parent 1e567775f9
commit a8e710f790
3 changed files with 144 additions and 119 deletions

View File

@@ -68,24 +68,39 @@ class SyntheticBacktestEngine:
remaining_positions.append(position)
open_positions = remaining_positions
underlying_value = scenario.initial_portfolio.underlying_units * day.close
net_portfolio_value = underlying_value + option_market_value + cash_balance
ltv_unhedged = scenario.initial_portfolio.loan_amount / underlying_value
ltv_hedged = scenario.initial_portfolio.loan_amount / net_portfolio_value
# Use closing price for portfolio value calculations
underlying_value_close = scenario.initial_portfolio.underlying_units * day.close
net_portfolio_value_close = underlying_value_close + option_market_value + cash_balance
# Use day's low for margin call evaluation (worst case during the day)
# If low is not available, fall back to close
worst_price = day.low if day.low is not None else day.close
underlying_value_worst = scenario.initial_portfolio.underlying_units * worst_price
net_portfolio_value_worst = underlying_value_worst + option_market_value + cash_balance
# LTVs for display (end-of-day at close)
ltv_unhedged = scenario.initial_portfolio.loan_amount / underlying_value_close
ltv_hedged = scenario.initial_portfolio.loan_amount / net_portfolio_value_close
# Margin calls use worst-case (low price) scenario
ltv_unhedged_worst = scenario.initial_portfolio.loan_amount / underlying_value_worst
ltv_hedged_worst = scenario.initial_portfolio.loan_amount / net_portfolio_value_worst
daily_points.append(
BacktestDailyPoint(
date=day.date,
spot_close=day.close,
underlying_value=underlying_value,
underlying_value=underlying_value_close,
option_market_value=option_market_value,
premium_cashflow=premium_cashflow,
realized_option_cashflow=realized_option_cashflow,
net_portfolio_value=net_portfolio_value,
net_portfolio_value=net_portfolio_value_close,
loan_amount=scenario.initial_portfolio.loan_amount,
ltv_unhedged=ltv_unhedged,
ltv_hedged=ltv_hedged,
margin_call_unhedged=ltv_unhedged >= scenario.initial_portfolio.margin_call_ltv,
margin_call_hedged=ltv_hedged >= scenario.initial_portfolio.margin_call_ltv,
margin_call_unhedged=ltv_unhedged_worst >= scenario.initial_portfolio.margin_call_ltv,
margin_call_hedged=ltv_hedged_worst >= scenario.initial_portfolio.margin_call_ltv,
active_position_ids=tuple(active_position_ids),
)
)