Spaces:
Sleeping
Sleeping
| from pydantic import BaseModel, Field, model_validator | |
| from typing import Dict, List, Any | |
| from constants import DEFAULT_ADV | |
| class ConfigError(Exception): | |
| pass | |
| class BenchmarksConfig(BaseModel): | |
| equity: str = "SPY" | |
| volatility: str = "^VIX" | |
| risk_free: str = "^TNX" | |
| def get(self, key: str, default: Any = None) -> Any: | |
| return getattr(self, key, default) | |
| class AppConfig(BaseModel): | |
| risk_free_rate: float = Field(0.04, ge=0.0, le=0.20) | |
| transaction_cost: float = Field(0.001, ge=0.0, le=0.05) | |
| trading_days_per_year: int = Field(252, ge=100, le=365) | |
| rolling_cov_days: int = 756 | |
| currency_symbol: str = "$" | |
| default_adv_proxy: float = Field(DEFAULT_ADV, ge=0.0) | |
| benchmarks: BenchmarksConfig = Field(default_factory=BenchmarksConfig) | |
| single_asset_min: float = Field(-1.0, ge=-1.0, le=1.0) | |
| single_asset_max: float = Field(0.40, ge=0.01, le=1.0) | |
| sector_limit: float = Field(0.40, ge=0.01, le=1.0) | |
| gross_leverage_cap: float = Field(2.0, ge=1.0, le=5.0) | |
| short_borrow_cost: float = Field(0.015, ge=0.0, le=0.50) | |
| max_turnover: float = Field(3.0, ge=0.0) | |
| tax_rate_lt: float = Field(0.20, ge=0.0, le=1.0) | |
| tax_rate_st: float = Field(0.35, ge=0.0, le=1.0) | |
| lt_days: int = Field(366, ge=1) | |
| hrp_tax_lambda: float = Field(2.5, ge=0.0) | |
| cvar_alpha: float = Field(0.95, ge=0.50, le=0.999) | |
| cvar_lambda: float = Field(0.5, ge=0.0, le=20.0) | |
| baseline_risk_factor: float = Field(3.0, ge=0.1, le=25.0) | |
| monte_carlo_sims: int = Field(1500, ge=100, le=100_000) | |
| monte_carlo_years: float = Field(1.0, ge=0.1, le=50.0) | |
| garch_enabled: bool = True | |
| cvar_enabled: bool = True | |
| tax_enabled: bool = False | |
| dynamic_risk: bool = True | |
| hmm_regime: bool = True | |
| arima_enabled: bool = False | |
| anova_enabled: bool = False | |
| with_futures: bool = False | |
| overlay_mode: str = "beta_hedge" | |
| futures_universe: List[str] = Field(default_factory=lambda: ["MES", "ES"]) | |
| futures_safety_multiplier: float = Field(3.0, ge=0.0, le=10.0) | |
| futures_target_beta: float = Field(0.0, ge=-5.0, le=5.0) | |
| futures_margin_headroom: float = Field(0.05, ge=0.0, le=1.0) | |
| return_frequency: str = "daily" | |
| # End-to-End Differentiable Optimization (Model 6) | |
| e2e_loss_type: str = "spo" # "spo", "sharpe", or "calmar" | |
| e2e_epochs: int = Field(default=50) | |
| e2e_batch_size: int = Field(default=32) | |
| e2e_lr: float = 1e-3 | |
| e2e_cache_dir: str = ".e2e_cache" | |
| universe_categories: Dict[str, List[str]] = Field(default_factory=lambda: { | |
| "Core Equities": ["SPY", "QQQ", "DIA", "IWM"], | |
| "Bonds & Rates": ["TLT", "IEF", "SHY", "AGG"], | |
| "Tech & Growth": ["AAPL", "MSFT", "NVDA", "TSLA"], | |
| "Defensive/Value": ["JNJ", "PG", "KO", "XLP"], | |
| "Commodities": ["GLD", "SLV", "USO", "PDBC"], | |
| "International": ["VEA", "VWO", "EFA", "EEM"], | |
| "Crypto Proxies": ["IBIT", "FBTC", "ETHE", "MSTR"] | |
| }) | |
| # βββββββββββββββββββββββββββββββββββββββββββββ | |
| # EXTENDED HISTORY & BOOTSTRAPPING | |
| # βββββββββββββββββββββββββββββββββββββββββββββ | |
| extended_history: bool = False | |
| bootstrap_samples: int = 100 | |
| stitch_overlap_days: int = 252 | |
| proxy_mappings: Dict[str, Dict] = Field(default_factory=lambda: { | |
| 'SPY': {'proxy': '^GSPC', 'proxy_start': '1950-01-03', 'overlap_days': 252}, | |
| 'TLT': {'proxy': '^TYX', 'proxy_start': '1977-01-03', 'is_yield': True}, | |
| 'GLD': {'proxy': 'GC=F', 'proxy_start': '1974-12-31'}, | |
| 'QQQ': {'proxy': '^IXIC', 'proxy_start': '1971-02-05'} | |
| }) | |
| bond_metadata: Dict[str, Any] = Field(default_factory=dict) | |
| model_config = {"extra": "allow"} | |
| def check_logic(self): | |
| if self.single_asset_min > self.single_asset_max: | |
| raise ConfigError("single_asset_min cannot be greater than single_asset_max.") | |
| if self.sector_limit < self.single_asset_max: | |
| raise ConfigError(f"sector_limit ({self.sector_limit}) cannot be smaller than single_asset_max ({self.single_asset_max}).") | |
| if self.tax_rate_lt > self.tax_rate_st: | |
| raise ConfigError("Long-term tax rate is mathematically expected to be <= short-term tax rate.") | |
| return self | |
| def get(self, key: str, default: Any = None) -> Any: | |
| """Backward compatibility for dict-like access.""" | |
| if hasattr(self, key): | |
| return getattr(self, key) | |
| if self.model_extra is not None and key in self.model_extra: | |
| return self.model_extra[key] | |
| return default | |
| def setdefault(self, key: str, default: Any = None) -> Any: | |
| val = self.get(key, None) | |
| if val is None: | |
| self[key] = default | |
| return default | |
| return val | |
| def __getitem__(self, key: str) -> Any: | |
| if hasattr(self, key): | |
| return getattr(self, key) | |
| if self.model_extra is not None and key in self.model_extra: | |
| return self.model_extra[key] | |
| raise KeyError(key) | |
| def __setitem__(self, key: str, value: Any) -> None: | |
| if hasattr(self, key) or key in self.model_fields: | |
| setattr(self, key, value) | |
| elif key.startswith('_'): | |
| # Internal transient keys (e.g., _risk_input, _is_historical_backtest) are allowed silently | |
| if self.model_extra is None: | |
| self.__dict__['__pydantic_extra__'] = {} | |
| self.model_extra[key] = value | |
| else: | |
| import warnings | |
| warnings.warn( | |
| f"AppConfig: setting unknown key '{key}'. Did you mean one of " | |
| f"{sorted(list(self.model_fields.keys()))[:8]}...?", | |
| stacklevel=2 | |
| ) | |
| if self.model_extra is None: | |
| self.__dict__['__pydantic_extra__'] = {} | |
| self.model_extra[key] = value | |
| def update(self, other: dict) -> None: | |
| for k, v in other.items(): | |
| setattr(self, k, v) | |
| DEFAULT_CONFIG = AppConfig() | |