Phase A-C complete: 4 core factors, regime detection, signal tracking, Bayesian expectancy. chanmacro/ (32 files, ~4000 lines): - models: 12 enums + 15 Pydantic v2 models (DateAwareModel, MarketStateVector, etc.) - fetchers: OHLCV + Breadth (from data_provider) + Derivatives (new endpoint) - scoring: Price Structure / Breadth (quantile buckets) / OI Matrix (5 discrete states) / Volatility Regime - regime_detector: 3-state (TREND/RANGE/PANIC), factor-locked (Price+Breadth+Vol), versioned, 2-day confirmation - expectancy: SignalTracker (record+outcomes), TimeDecay (half-life=180d), BayesianExpectancyEngine (Empirical Bayes, Leveled, SufficiencyGuard) - validation: FactorValidator (IC/ICIR/Hit Ratio), RegimeValidator (MI/KL/ANOVA), TransitionValidator (stability) - CLI: fetch|score|regime|track|backfill|expectancy|validate|serve - tests: 52 passing (models, scoring, regime, expectancy) data_provider: - /api/derivatives endpoint: funding rate, OI, OI change, basis - _derivatives storage: same persist pattern as K-line (merge→lock→snapshot→atomic write) - background refresh every 60s Co-Authored-By: Claude <noreply@anthropic.com>
115 lines
4.6 KiB
Python
115 lines
4.6 KiB
Python
"""
|
|
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 = "http://127.0.0.1:80"
|
|
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",
|
|
"ADA/USDT:USDT", "AVAX/USDT:USDT", "DOT/USDT:USDT",
|
|
"LINK/USDT:USDT", "MATIC/USDT:USDT", "UNI/USDT:USDT",
|
|
"ATOM/USDT:USDT", "LTC/USDT:USDT", "ETC/USDT:USDT",
|
|
"FIL/USDT:USDT", "APT/USDT:USDT", "ARB/USDT:USDT",
|
|
"OP/USDT:USDT", "NEAR/USDT:USDT",
|
|
"INJ/USDT:USDT", "TIA/USDT:USDT", "SUI/USDT:USDT",
|
|
"SEI/USDT:USDT", "RUNE/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()
|