186 lines
7.1 KiB
Python
186 lines
7.1 KiB
Python
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
|