Files
vault-dash/app/models/backtest_settings.py
Bu5hm4nn 43067bb275 feat(DATA-DB-002): add BacktestSettings model and repository
- BacktestSettings dataclass with all configuration fields
- BacktestSettingsRepository for persistence per workspace
- Settings independent of portfolio configuration
- Full validation for dates, symbols, LTV, etc.
- 16 comprehensive tests

Fields:
- settings_id, name: identification
- data_source: databento|yfinance|synthetic
- dataset, schema: Databento configuration
- start_date, end_date: date range
- underlying_symbol, start_price, underlying_units: position config
- loan_amount, margin_call_ltv: LTV analysis
- template_slugs: strategies to test
- cache_key, data_cost_usd: caching metadata
- provider_ref: provider configuration
2026-03-29 10:46:25 +02:00

89 lines
3.2 KiB
Python

"""Backtest settings model for configuring backtest scenarios independently of portfolio settings."""
from __future__ import annotations
from dataclasses import dataclass
from datetime import date
from typing import Literal
from uuid import UUID, uuid4
# Self type annotation
from app.models.backtest import ProviderRef
@dataclass(frozen=True)
class BacktestSettings:
"""Configuration for running backtests independent of portfolio settings.
These settings determine what data to fetch and how to run backtests,
separate from the actual portfolio configurations being tested.
"""
settings_id: UUID
name: str
data_source: Literal["databento", "yfinance", "synthetic"]
dataset: str
schema: str
start_date: date
end_date: date
underlying_symbol: Literal["GLD", "GC", "XAU"]
start_price: float
underlying_units: float
loan_amount: float
margin_call_ltv: float
template_slugs: tuple[str, ...]
cache_key: str
data_cost_usd: float
provider_ref: ProviderRef
def __post_init__(self) -> None:
if not self.settings_id:
raise ValueError("settings_id is required")
if not self.name:
raise ValueError("name is required")
if self.start_date > self.end_date:
raise ValueError("start_date must be on or before end_date")
if self.data_source not in ("databento", "yfinance", "synthetic"):
raise ValueError("data_source must be 'databento', 'yfinance', or 'synthetic'")
if self.underlying_symbol not in ("GLD", "GC", "XAU"):
raise ValueError("underlying_symbol must be 'GLD', 'GC', or 'XAU'")
if self.start_price < 0:
raise ValueError("start_price must be non-negative")
if self.underlying_units <= 0:
raise ValueError("underlying_units must be positive")
if self.loan_amount < 0:
raise ValueError("loan_amount must be non-negative")
if not 0 < self.margin_call_ltv < 1:
raise ValueError("margin_call_ltv must be between 0 and 1")
if not self.template_slugs:
raise ValueError("at least one template slug is required")
if self.data_cost_usd < 0:
raise ValueError("data_cost_usd must be non-negative")
@classmethod
def create_default(cls, name: str = "Default Backtest Settings") -> BacktestSettings:
"""Create default backtest settings configuration."""
return cls(
settings_id=uuid4(),
name=name,
data_source="databento",
dataset="XNAS.BASIC",
schema="ohlcv-1d",
start_date=date(2020, 1, 1),
end_date=date(2023, 12, 31),
underlying_symbol="GLD",
start_price=0.0,
underlying_units=1000.0,
loan_amount=0.0,
margin_call_ltv=0.75,
template_slugs=("default-template",),
cache_key="",
data_cost_usd=0.0,
provider_ref=ProviderRef(provider_id="default", pricing_mode="standard"),
)
# For backward compatibility - alias to existing models
BacktestScenario = "app.models.backtest.BacktestScenario"
# TemplateRef and ProviderRef imported from app.models.backtest