from __future__ import annotations from dataclasses import dataclass, field from strategy29.common.models import Regime from strategy29.config import Strategy29Config DEFAULT_QUOTE_ASSETS = ("USDT", "USDC") DEFAULT_EXCLUDED_BASE_ASSETS = ("USDT", "USDC", "BUSD", "FDUSD", "TUSD", "USDP", "DAI", "USDS", "USDD", "EUR", "AEUR") PROFILE_V5_BASELINE = "v5_baseline" PROFILE_V7_DEFAULT = "v7_default" @dataclass(slots=True) class Strategy32Budgets: strong_up_momentum: float = 0.85 strong_up_carry: float = 0.10 strong_up_sideways: float = 0.00 up_momentum: float = 0.25 up_carry: float = 0.15 up_sideways: float = 0.00 sideways_momentum: float = 0.00 sideways_carry: float = 0.20 sideways_sideways: float = 0.10 down_momentum: float = 0.00 down_carry: float = 0.10 down_sideways: float = 0.03 def for_regime(self, regime: Regime) -> tuple[float, float, float]: if regime == Regime.STRONG_UP: return self.strong_up_momentum, self.strong_up_carry, self.strong_up_sideways if regime == Regime.UP: return self.up_momentum, self.up_carry, self.up_sideways if regime == Regime.SIDEWAYS: return self.sideways_momentum, self.sideways_carry, self.sideways_sideways if regime == Regime.DOWN: return self.down_momentum, self.down_carry, self.down_sideways return 0.0, 0.0, 0.0 @dataclass(slots=True) class Strategy32Config: symbols: list[str] = field(default_factory=list) auto_discover_symbols: bool = True quote_assets: tuple[str, ...] = DEFAULT_QUOTE_ASSETS excluded_base_assets: tuple[str, ...] = DEFAULT_EXCLUDED_BASE_ASSETS discovery_min_quote_volume_24h: float = 5_000_000.0 timeframe: str = "4h" warmup_days: int = 90 max_symbol_staleness_days: int = 3 universe_size: int = 0 universe_lookback_bars: int = 30 universe_min_avg_dollar_volume: float = 5_000_000.0 hard_filter_refresh_cadence: str = "4h" hard_filter_min_history_bars: int = 120 hard_filter_lookback_bars: int = 30 hard_filter_min_avg_dollar_volume: float = 50_000_000.0 momentum_min_history_bars: int = 120 momentum_min_score: float = 0.55 momentum_min_relative_strength: float = 0.0 momentum_min_7d_return: float = 0.0 momentum_max_7d_return: float = 1.0 momentum_min_positive_bar_ratio: float = 0.0 momentum_max_short_volatility: float = 1.0 momentum_max_beta: float = 10.0 momentum_max_latest_funding_rate: float = 1.0 enable_liquidity_universe_fallback: bool = True universe_fallback_min_avg_dollar_volume: float = 2_500_000.0 universe_fallback_top_n: int = 8 enable_momentum_filter_fallback: bool = True momentum_fallback_min_score: float = 0.45 momentum_fallback_min_relative_strength: float = -0.03 momentum_fallback_min_7d_return: float = -0.02 momentum_fallback_top_n: int = 3 carry_min_expected_edge: float = 0.0 position_vol_lookback_bars: int = 36 correlation_lookback_bars: int = 36 max_pairwise_correlation: float = 0.78 target_annualized_vol: float = 0.55 governor_vol_lookback_bars: int = 42 drawdown_window_days: int = 30 drawdown_scale_1_trigger: float = 0.08 drawdown_scale_1: float = 0.70 drawdown_scale_2_trigger: float = 0.12 drawdown_scale_2: float = 0.40 drawdown_stop_trigger: float = 0.18 vol_scale_floor: float = 0.35 up_btc_hedge_ratio: float = 0.35 sideways_btc_hedge_ratio: float = 0.65 sideways_top_n: int = 1 carry_relaxed_top_n: int = 128 carry_relaxed_min_positive_ratio: float = 0.52 carry_relaxed_min_mean_funding_rate: float = 0.000015 carry_relaxed_max_basis_volatility: float = 0.0075 carry_relaxed_roundtrip_cost_pct: float = 0.0022 carry_relaxed_basis_risk_multiplier: float = 1.0 carry_deep_relaxed_min_positive_ratio: float = 0.48 carry_deep_relaxed_min_mean_funding_rate: float = 0.0 carry_deep_relaxed_max_basis_volatility: float = 0.012 carry_deep_relaxed_roundtrip_cost_pct: float = 0.0018 carry_deep_relaxed_basis_risk_multiplier: float = 0.75 enable_carry_score_fallback: bool = True carry_score_fallback_min_expected_edge: float = -0.0002 carry_score_fallback_min_positive_ratio: float = 0.48 carry_score_fallback_top_n: int = 2 enable_sideways_engine: bool = False enable_strong_kill_switch: bool = True enable_daily_trend_filter: bool = True enable_expanded_hedge: bool = False enable_max_holding_exit: bool = False enable_execution_refinement: bool = True execution_refinement_timeframe: str = "1h" execution_refinement_lookback_bars: int = 48 execution_refinement_fast_ema: int = 8 execution_refinement_slow_ema: int = 21 execution_refinement_scale_down_gap: float = 0.008 execution_refinement_max_chase_gap: float = 0.018 execution_refinement_max_recent_return: float = 0.03 execution_refinement_scale_down_factor: float = 0.5 strong_kill_drawdown_window_days: int = 60 strong_kill_scale_1_trigger: float = 0.05 strong_kill_scale_1: float = 0.60 strong_kill_scale_2_trigger: float = 0.08 strong_kill_scale_2: float = 0.35 strong_kill_stop_trigger: float = 0.10 long_trend_fast_ema_days: int = 50 long_trend_slow_ema_days: int = 200 expanded_up_btc_hedge_ratio: float = 0.55 expanded_sideways_btc_hedge_ratio: float = 0.85 expanded_strong_up_btc_hedge_ratio: float = 0.10 max_holding_bars: int = 42 trend_fail_ema_span: int = 18 min_hold_bars_for_trend_fail: int = 12 budgets: Strategy32Budgets = field(default_factory=Strategy32Budgets) PROFILE_OVERRIDES: dict[str, dict[str, bool]] = { PROFILE_V5_BASELINE: { "enable_sideways_engine": True, "enable_strong_kill_switch": False, "enable_daily_trend_filter": False, "enable_expanded_hedge": False, "enable_max_holding_exit": False, }, PROFILE_V7_DEFAULT: { "enable_sideways_engine": False, "enable_strong_kill_switch": True, "enable_daily_trend_filter": True, "enable_expanded_hedge": False, "enable_max_holding_exit": False, }, } def apply_strategy32_profile(config: Strategy32Config, profile: str) -> Strategy32Config: try: overrides = PROFILE_OVERRIDES[profile] except KeyError as exc: supported = ", ".join(sorted(PROFILE_OVERRIDES)) raise ValueError(f"Unsupported Strategy32 profile: {profile}. Supported: {supported}") from exc for attr, value in overrides.items(): setattr(config, attr, value) return config def build_strategy32_config(profile: str = PROFILE_V7_DEFAULT, **overrides: object) -> Strategy32Config: config = Strategy32Config() apply_strategy32_profile(config, profile) for attr, value in overrides.items(): setattr(config, attr, value) return config def build_engine_config() -> Strategy29Config: config = Strategy29Config() config.momentum.top_n = 128 config.momentum.max_positions = 128 config.momentum.rebalance_bars = 24 config.momentum.trailing_stop_pct = 0.08 config.momentum.stop_loss_pct = 0.07 config.momentum.overheat_funding_rate = 0.00025 config.carry.top_n = 128 return config