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:
@@ -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),
|
||||
)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user