""" config.py — Global configuration for ChanMacro. All weights, thresholds, and paths are configurable. V1 weights are deliberately simple; they will be tuned via Phase 0 validation. """ from dataclasses import dataclass, field from pathlib import Path from typing import Optional @dataclass class Config: """Global configuration. Override via config.json or env vars.""" # ── Paths ────────────────────────────────────────────── db_path: str = "data/macro.db" data_dir: str = "data" # ── Data Provider ────────────────────────────────────── provider_url: str = "https://provider.jackyu66.com" btc_symbol: str = "BTC/USDT:USDT" top50_symbols: list[str] = field(default_factory=lambda: [ "BTC/USDT:USDT", "ETH/USDT:USDT", "SOL/USDT:USDT", "BNB/USDT:USDT", "XRP/USDT:USDT", "DOGE/USDT:USDT", "SUI/USDT:USDT", "TON/USDT:USDT", "ZEC/USDT:USDT", "1000PEPE/USDT:USDT", "SAGA/USDT:USDT", "XAU/USDT:USDT", "XAG/USDT:USDT", "CL/USDT:USDT", "BILL/USDT:USDT", "BZ/USDT:USDT", "LAB/USDT:USDT", "CRCL/USDT:USDT", "SNDK/USDT:USDT", "CHIP/USDT:USDT", ]) # ── Breadth ──────────────────────────────────────────── breadth_top_n: list[int] = field(default_factory=lambda: [20, 30, 50]) breadth_ema_period: int = 20 breadth_new_high_window: int = 20 # ── Regime (factor-locked: Price + Breadth + Vol) ───── regime_version: str = "v1_price_breadth_vol" # Weights for trend_score within regime detection regime_w_price: float = 0.35 regime_w_breadth: float = 0.50 regime_w_vol: float = 0.15 # Weights for panic_score regime_panic_w_anti_trend: float = 0.60 regime_panic_w_vol_extreme: float = 0.40 # ── Price Structure ──────────────────────────────────── ps_ema_fast: int = 20 ps_ema_mid: int = 60 ps_ema_slow: int = 120 ps_adx_period: int = 14 ps_adx_threshold: int = 25 ps_atr_period: int = 14 ps_bb_period: int = 20 ps_roc_periods: list[int] = field(default_factory=lambda: [5, 10, 20]) # ── OI Matrix ────────────────────────────────────────── oi_price_threshold_pct: float = 0.5 # min price change% to classify oi_oi_threshold_pct: float = 0.5 # min OI change% to classify # ── Volatility Regime ────────────────────────────────── vol_atr_period: int = 14 vol_hv_short: int = 20 vol_hv_long: int = 60 # Thresholds (ATR/Close %) vol_low_threshold: float = 2.0 vol_high_threshold: float = 5.0 vol_explosive_threshold: float = 10.0 # ── Trend (L2 aggregation) ───────────────────────────── trend_w_price: float = 0.30 trend_w_breadth: float = 0.70 # ── Maturity Score ───────────────────────────────────── maturity_w_trend: float = 0.50 maturity_w_breadth: float = 0.30 maturity_w_vol: float = 0.20 # ── Expectancy ───────────────────────────────────────── half_life_days: int = 180 sufficiency_min_effective: int = 30 sufficiency_low: int = 50 sufficiency_medium: int = 100 level_min_samples: int = 50 knn_max_distance: float = 0.35 knn_k: int = 200 # ── Validation ───────────────────────────────────────── min_history_days: int = 365 regime_min_avg_duration: int = 5 regime_max_flip_rate: float = 0.15 @classmethod def from_json(cls, path: str = "config.json") -> "Config": """Load config from JSON file, overriding defaults.""" import json config = cls() try: with open(path) as f: data = json.load(f) for key, value in data.items(): if hasattr(config, key): setattr(config, key, value) except FileNotFoundError: pass return config # Global singleton config = Config()