"""System configuration defaults and DB-backed accessors.""" from __future__ import annotations import os from app.db.runtime_config_db import ( deep_merge, get_bootstrap_admin_config, get_email_config, get_event_driven_config, get_llm_config, get_live_trading_config, get_monitoring_config, get_notification_config, get_paper_trading_config, get_price_streamer_config, get_scheduler_config, get_sentiment_config, seed_system_defaults, ) def _env_bool(name, default=False): value = os.getenv(name) if value is None: return default return str(value).strip().lower() in ("1", "true", "yes", "on") def _env_int(name, default): try: return int(os.getenv(name, str(default)) or default) except Exception: return default def _env_float(name, default): try: return float(os.getenv(name, str(default)) or default) except Exception: return default def _env_str(name, default=""): return os.getenv(name, default).strip() def _env_list(name, default): raw = os.getenv(name, ",".join(default)) return [x.strip().lower() for x in raw.split(",") if x.strip()] or list(default) def _env_present(name): return os.getenv(name) is not None def default_llm_config(): return { "enabled": _env_bool("ALPHAX_LLM_ENABLED", False), "base_url": _env_str("ALPHAX_LLM_BASE_URL", "https://api.openai.com/v1"), "api_key_env": _env_str("ALPHAX_LLM_API_KEY_ENV", "ALPHAX_LLM_API_KEY"), "model": _env_str("ALPHAX_LLM_MODEL", "gpt-4o-mini"), "timeout": _env_int("ALPHAX_LLM_TIMEOUT", 20), "max_tokens": _env_int("ALPHAX_LLM_MAX_TOKENS", 900), "reasoning_effort": _env_str("ALPHAX_LLM_REASONING_EFFORT", ""), "thinking_enabled": _env_bool("ALPHAX_LLM_THINKING_ENABLED", False), "modules": { "recommendations": _env_bool("ALPHAX_LLM_RECOMMENDATIONS_ENABLED", True), "sentiment": _env_bool("ALPHAX_LLM_SENTIMENT_ENABLED", True), "review": _env_bool("ALPHAX_LLM_REVIEW_ENABLED", True), "chat": _env_bool("ALPHAX_LLM_CHAT_ENABLED", True), }, } def _llm_env_overrides(): """Honor explicit LLM env vars even when DB runtime config exists.""" overrides = {} checks = { "ALPHAX_LLM_ENABLED": ("enabled", lambda: _env_bool("ALPHAX_LLM_ENABLED", False)), "ALPHAX_LLM_BASE_URL": ("base_url", lambda: _env_str("ALPHAX_LLM_BASE_URL", "https://api.openai.com/v1")), "ALPHAX_LLM_API_KEY_ENV": ("api_key_env", lambda: _env_str("ALPHAX_LLM_API_KEY_ENV", "ALPHAX_LLM_API_KEY")), "ALPHAX_LLM_MODEL": ("model", lambda: _env_str("ALPHAX_LLM_MODEL", "gpt-4o-mini")), "ALPHAX_LLM_TIMEOUT": ("timeout", lambda: _env_int("ALPHAX_LLM_TIMEOUT", 20)), "ALPHAX_LLM_MAX_TOKENS": ("max_tokens", lambda: _env_int("ALPHAX_LLM_MAX_TOKENS", 900)), "ALPHAX_LLM_REASONING_EFFORT": ("reasoning_effort", lambda: _env_str("ALPHAX_LLM_REASONING_EFFORT", "")), "ALPHAX_LLM_THINKING_ENABLED": ("thinking_enabled", lambda: _env_bool("ALPHAX_LLM_THINKING_ENABLED", False)), } for env_name, (key, loader) in checks.items(): if _env_present(env_name): overrides[key] = loader() module_overrides = {} module_checks = { "ALPHAX_LLM_RECOMMENDATIONS_ENABLED": ("recommendations", lambda: _env_bool("ALPHAX_LLM_RECOMMENDATIONS_ENABLED", True)), "ALPHAX_LLM_SENTIMENT_ENABLED": ("sentiment", lambda: _env_bool("ALPHAX_LLM_SENTIMENT_ENABLED", True)), "ALPHAX_LLM_REVIEW_ENABLED": ("review", lambda: _env_bool("ALPHAX_LLM_REVIEW_ENABLED", True)), "ALPHAX_LLM_CHAT_ENABLED": ("chat", lambda: _env_bool("ALPHAX_LLM_CHAT_ENABLED", True)), } for env_name, (key, loader) in module_checks.items(): if _env_present(env_name): module_overrides[key] = loader() if module_overrides: overrides["modules"] = module_overrides return overrides def default_paper_trading_config(): # One shared default keeps buy-now entries and wait-pullback orders from # drifting into two unrelated RR standards. The explicit entry/order envs # remain supported for advanced overrides. paper_min_rr = _env_float("ALPHAX_PAPER_MIN_RR", 1.25) return { "enabled": _env_bool("ALPHAX_PAPER_TRADING_ENABLED", True), "trading_mode": _env_str("ALPHAX_PAPER_TRADING_MODE", "intraday_trading"), "target_trades_per_day_min": _env_int("ALPHAX_PAPER_TARGET_TRADES_PER_DAY_MIN", 3), "target_trades_per_day_max": _env_int("ALPHAX_PAPER_TARGET_TRADES_PER_DAY_MAX", 5), "account_equity_usdt": _env_float("ALPHAX_PAPER_ACCOUNT_EQUITY_USDT", 20000), "trade_notional_usdt": _env_float("ALPHAX_PAPER_TRADE_NOTIONAL_USDT", 5000), "trade_leverage": _env_float("ALPHAX_PAPER_TRADE_LEVERAGE", 5), "max_cumulative_leverage": _env_float("ALPHAX_PAPER_MAX_CUMULATIVE_LEVERAGE", 5.0), "fee_rate": _env_float("ALPHAX_PAPER_TRADE_FEE_RATE", 0.001), "slippage_pct": _env_float("ALPHAX_PAPER_TRADE_SLIPPAGE_PCT", 0.05), "trailing_stop_enabled": _env_bool("ALPHAX_PAPER_TRAILING_STOP_ENABLED", True), "trailing_mode": _env_str("ALPHAX_PAPER_TRAILING_MODE", "volatility"), "trailing_activate_pnl_pct": _env_float("ALPHAX_PAPER_TRAILING_ACTIVATE_PNL_PCT", 2.0), "trailing_min_lock_profit_pct": _env_float("ALPHAX_PAPER_TRAILING_MIN_LOCK_PROFIT_PCT", 0.5), "trailing_distance_pct": _env_float("ALPHAX_PAPER_TRAILING_DISTANCE_PCT", 1.5), "trailing_volatility_min_activation_pct": _env_float("ALPHAX_PAPER_TRAILING_VOL_MIN_ACTIVATE_PCT", 1.8), "trailing_volatility_max_activation_pct": _env_float("ALPHAX_PAPER_TRAILING_VOL_MAX_ACTIVATE_PCT", 8.0), "trailing_volatility_activation_mult": _env_float("ALPHAX_PAPER_TRAILING_VOL_ACTIVATE_MULT", 0.6), "trailing_volatility_min_distance_pct": _env_float("ALPHAX_PAPER_TRAILING_VOL_MIN_DISTANCE_PCT", 1.2), "trailing_volatility_max_distance_pct": _env_float("ALPHAX_PAPER_TRAILING_VOL_MAX_DISTANCE_PCT", 8.0), "trailing_volatility_distance_mult": _env_float("ALPHAX_PAPER_TRAILING_VOL_DISTANCE_MULT", 0.7), "trailing_move_push_min_interval_seconds": _env_int("ALPHAX_PAPER_TRAILING_MOVE_PUSH_MIN_INTERVAL_SECONDS", 300), "trailing_move_push_min_step_pct": _env_float("ALPHAX_PAPER_TRAILING_MOVE_PUSH_MIN_STEP_PCT", 2.0), "order_gate_enabled": _env_bool("ALPHAX_PAPER_ORDER_GATE_ENABLED", True), "entry_gate_enabled": _env_bool("ALPHAX_PAPER_ENTRY_GATE_ENABLED", True), "entry_min_rec_score": _env_float("ALPHAX_PAPER_ENTRY_MIN_REC_SCORE", 25.0), "min_rr": paper_min_rr, "entry_min_rr": _env_float("ALPHAX_PAPER_ENTRY_MIN_RR", paper_min_rr), "max_stop_loss_leverage_risk_pct": _env_float("ALPHAX_PAPER_MAX_STOP_LOSS_LEVERAGE_RISK_PCT", 20.0), "dynamic_leverage_enabled": _env_bool("ALPHAX_PAPER_DYNAMIC_LEVERAGE_ENABLED", True), "dynamic_leverage_min": _env_float("ALPHAX_PAPER_DYNAMIC_LEVERAGE_MIN", 1.0), "max_account_drawdown_pause_pct": _env_float("ALPHAX_PAPER_MAX_ACCOUNT_DRAWDOWN_PAUSE_PCT", 3.0), "pause_after_weak_entries": _env_int("ALPHAX_PAPER_PAUSE_AFTER_WEAK_ENTRIES", 3), "weak_entry_window_hours": _env_float("ALPHAX_PAPER_WEAK_ENTRY_WINDOW_HOURS", 6.0), "weak_entry_min_max_pnl_pct": _env_float("ALPHAX_PAPER_WEAK_ENTRY_MIN_MAX_PNL_PCT", 1.0), "position_guard_enabled": _env_bool("ALPHAX_PAPER_POSITION_GUARD_ENABLED", True), "position_guard_soft_hours": _env_float("ALPHAX_PAPER_POSITION_GUARD_SOFT_HOURS", 6.0), "position_guard_soft_min_max_pnl_pct": _env_float("ALPHAX_PAPER_POSITION_GUARD_SOFT_MIN_MAX_PNL_PCT", 1.5), "position_guard_hard_hours": _env_float("ALPHAX_PAPER_POSITION_GUARD_HARD_HOURS", 18.0), "position_guard_hard_min_max_pnl_pct": _env_float("ALPHAX_PAPER_POSITION_GUARD_HARD_MIN_MAX_PNL_PCT", 2.5), "position_guard_tighten_lock_profit_pct": _env_float("ALPHAX_PAPER_POSITION_GUARD_TIGHTEN_LOCK_PROFIT_PCT", 0.15), "position_guard_profit_giveback_enabled": _env_bool("ALPHAX_PAPER_POSITION_GUARD_PROFIT_GIVEBACK_ENABLED", True), "position_guard_giveback_min_max_pnl_pct": _env_float("ALPHAX_PAPER_POSITION_GUARD_GIVEBACK_MIN_MAX_PNL_PCT", 2.0), "position_guard_giveback_exit_pct": _env_float("ALPHAX_PAPER_POSITION_GUARD_GIVEBACK_EXIT_PCT", 70.0), "position_guard_giveback_exit_current_pnl_pct": _env_float("ALPHAX_PAPER_POSITION_GUARD_GIVEBACK_EXIT_CURRENT_PNL_PCT", 0.6), "position_guard_critical_exit_enabled": _env_bool("ALPHAX_PAPER_POSITION_GUARD_CRITICAL_EXIT_ENABLED", True), "position_guard_critical_min_age_hours": _env_float("ALPHAX_PAPER_POSITION_GUARD_CRITICAL_MIN_AGE_HOURS", 0.5), "position_guard_critical_max_pnl_pct": _env_float("ALPHAX_PAPER_POSITION_GUARD_CRITICAL_MAX_PNL_PCT", 1.0), "global_risk_gate_enabled": _env_bool("ALPHAX_PAPER_GLOBAL_RISK_GATE_ENABLED", True), "global_risk_block_critical": _env_bool("ALPHAX_PAPER_GLOBAL_RISK_BLOCK_CRITICAL", False), "global_risk_critical_min_rec_score": _env_float("ALPHAX_PAPER_GLOBAL_RISK_CRITICAL_MIN_REC_SCORE", 25.0), "global_risk_score_blocks_intraday": _env_bool("ALPHAX_PAPER_GLOBAL_RISK_SCORE_BLOCKS_INTRADAY", False), "global_risk_min_position_multiplier": _env_float("ALPHAX_PAPER_GLOBAL_RISK_MIN_POSITION_MULTIPLIER", 0.2), "global_risk_high_min_rec_score": _env_float("ALPHAX_PAPER_GLOBAL_RISK_HIGH_MIN_REC_SCORE", 25.0), "global_risk_high_drawdown_pct": _env_float("ALPHAX_PAPER_GLOBAL_RISK_HIGH_DRAWDOWN_PCT", 3.0), "global_risk_critical_drawdown_pct": _env_float("ALPHAX_PAPER_GLOBAL_RISK_CRITICAL_DRAWDOWN_PCT", 6.0), "global_risk_max_open_positions": _env_int("ALPHAX_PAPER_GLOBAL_RISK_MAX_OPEN_POSITIONS", 0), "global_risk_max_same_sector_positions": _env_int("ALPHAX_PAPER_GLOBAL_RISK_MAX_SAME_SECTOR_POSITIONS", 3), "global_risk_max_same_direction_positions": _env_int("ALPHAX_PAPER_GLOBAL_RISK_MAX_SAME_DIRECTION_POSITIONS", 6), "order_min_rec_score": _env_float("ALPHAX_PAPER_ORDER_MIN_REC_SCORE", 25.0), "order_min_rr": _env_float("ALPHAX_PAPER_ORDER_MIN_RR", paper_min_rr), "order_require_risk_reward_ok": _env_bool("ALPHAX_PAPER_ORDER_REQUIRE_RISK_REWARD_OK", True), "order_min_distance_to_entry_pct": _env_float("ALPHAX_PAPER_ORDER_MIN_DISTANCE_TO_ENTRY_PCT", 0.0), "order_max_distance_to_entry_pct": _env_float("ALPHAX_PAPER_ORDER_MAX_DISTANCE_TO_ENTRY_PCT", 8.0), "order_require_current_trigger": _env_bool("ALPHAX_PAPER_ORDER_REQUIRE_CURRENT_TRIGGER", False), "order_cancel_far_from_entry_pct": _env_float("ALPHAX_PAPER_ORDER_CANCEL_FAR_FROM_ENTRY_PCT", 12.0), "order_expire_hours": _env_float("ALPHAX_PAPER_ORDER_EXPIRE_HOURS", 8.0), "trailing_tiers": [ {"min_pnl_pct": 8.0, "distance_pct": 1.0, "label": "紧贴"}, {"min_pnl_pct": 5.0, "distance_pct": 1.2, "label": "锁利"}, {"min_pnl_pct": 3.0, "distance_pct": 1.8, "label": "防震"}, ], } def _paper_trading_env_overrides(): """Honor explicit paper-trading env vars even when DB runtime config exists.""" overrides = {} if _env_present("ALPHAX_PAPER_MIN_RR"): shared_min_rr = _env_float("ALPHAX_PAPER_MIN_RR", 1.5) overrides.update({ "min_rr": shared_min_rr, "entry_min_rr": shared_min_rr, "order_min_rr": shared_min_rr, }) checks = { "ALPHAX_PAPER_TRADING_ENABLED": ("enabled", lambda: _env_bool("ALPHAX_PAPER_TRADING_ENABLED", True)), "ALPHAX_PAPER_TRADING_MODE": ("trading_mode", lambda: _env_str("ALPHAX_PAPER_TRADING_MODE", "intraday_trading")), "ALPHAX_PAPER_TARGET_TRADES_PER_DAY_MIN": ("target_trades_per_day_min", lambda: _env_int("ALPHAX_PAPER_TARGET_TRADES_PER_DAY_MIN", 3)), "ALPHAX_PAPER_TARGET_TRADES_PER_DAY_MAX": ("target_trades_per_day_max", lambda: _env_int("ALPHAX_PAPER_TARGET_TRADES_PER_DAY_MAX", 5)), "ALPHAX_PAPER_ACCOUNT_EQUITY_USDT": ("account_equity_usdt", lambda: _env_float("ALPHAX_PAPER_ACCOUNT_EQUITY_USDT", 20000)), "ALPHAX_PAPER_TRADE_NOTIONAL_USDT": ("trade_notional_usdt", lambda: _env_float("ALPHAX_PAPER_TRADE_NOTIONAL_USDT", 5000)), "ALPHAX_PAPER_TRADE_LEVERAGE": ("trade_leverage", lambda: _env_float("ALPHAX_PAPER_TRADE_LEVERAGE", 5)), "ALPHAX_PAPER_MAX_CUMULATIVE_LEVERAGE": ("max_cumulative_leverage", lambda: _env_float("ALPHAX_PAPER_MAX_CUMULATIVE_LEVERAGE", 5.0)), "ALPHAX_PAPER_TRADE_FEE_RATE": ("fee_rate", lambda: _env_float("ALPHAX_PAPER_TRADE_FEE_RATE", 0.001)), "ALPHAX_PAPER_TRADE_SLIPPAGE_PCT": ("slippage_pct", lambda: _env_float("ALPHAX_PAPER_TRADE_SLIPPAGE_PCT", 0.05)), "ALPHAX_PAPER_ORDER_GATE_ENABLED": ("order_gate_enabled", lambda: _env_bool("ALPHAX_PAPER_ORDER_GATE_ENABLED", True)), "ALPHAX_PAPER_ENTRY_GATE_ENABLED": ("entry_gate_enabled", lambda: _env_bool("ALPHAX_PAPER_ENTRY_GATE_ENABLED", True)), "ALPHAX_PAPER_ENTRY_MIN_REC_SCORE": ("entry_min_rec_score", lambda: _env_float("ALPHAX_PAPER_ENTRY_MIN_REC_SCORE", 25.0)), "ALPHAX_PAPER_ENTRY_MIN_RR": ("entry_min_rr", lambda: _env_float("ALPHAX_PAPER_ENTRY_MIN_RR", overrides.get("min_rr", 1.25))), "ALPHAX_PAPER_MAX_STOP_LOSS_LEVERAGE_RISK_PCT": ("max_stop_loss_leverage_risk_pct", lambda: _env_float("ALPHAX_PAPER_MAX_STOP_LOSS_LEVERAGE_RISK_PCT", 20.0)), "ALPHAX_PAPER_DYNAMIC_LEVERAGE_ENABLED": ("dynamic_leverage_enabled", lambda: _env_bool("ALPHAX_PAPER_DYNAMIC_LEVERAGE_ENABLED", True)), "ALPHAX_PAPER_DYNAMIC_LEVERAGE_MIN": ("dynamic_leverage_min", lambda: _env_float("ALPHAX_PAPER_DYNAMIC_LEVERAGE_MIN", 1.0)), "ALPHAX_PAPER_MAX_ACCOUNT_DRAWDOWN_PAUSE_PCT": ("max_account_drawdown_pause_pct", lambda: _env_float("ALPHAX_PAPER_MAX_ACCOUNT_DRAWDOWN_PAUSE_PCT", 3.0)), "ALPHAX_PAPER_PAUSE_AFTER_WEAK_ENTRIES": ("pause_after_weak_entries", lambda: _env_int("ALPHAX_PAPER_PAUSE_AFTER_WEAK_ENTRIES", 3)), "ALPHAX_PAPER_WEAK_ENTRY_WINDOW_HOURS": ("weak_entry_window_hours", lambda: _env_float("ALPHAX_PAPER_WEAK_ENTRY_WINDOW_HOURS", 6.0)), "ALPHAX_PAPER_WEAK_ENTRY_MIN_MAX_PNL_PCT": ("weak_entry_min_max_pnl_pct", lambda: _env_float("ALPHAX_PAPER_WEAK_ENTRY_MIN_MAX_PNL_PCT", 1.0)), "ALPHAX_PAPER_POSITION_GUARD_ENABLED": ("position_guard_enabled", lambda: _env_bool("ALPHAX_PAPER_POSITION_GUARD_ENABLED", True)), "ALPHAX_PAPER_POSITION_GUARD_SOFT_HOURS": ("position_guard_soft_hours", lambda: _env_float("ALPHAX_PAPER_POSITION_GUARD_SOFT_HOURS", 6.0)), "ALPHAX_PAPER_POSITION_GUARD_SOFT_MIN_MAX_PNL_PCT": ("position_guard_soft_min_max_pnl_pct", lambda: _env_float("ALPHAX_PAPER_POSITION_GUARD_SOFT_MIN_MAX_PNL_PCT", 1.5)), "ALPHAX_PAPER_POSITION_GUARD_HARD_HOURS": ("position_guard_hard_hours", lambda: _env_float("ALPHAX_PAPER_POSITION_GUARD_HARD_HOURS", 18.0)), "ALPHAX_PAPER_POSITION_GUARD_HARD_MIN_MAX_PNL_PCT": ("position_guard_hard_min_max_pnl_pct", lambda: _env_float("ALPHAX_PAPER_POSITION_GUARD_HARD_MIN_MAX_PNL_PCT", 2.5)), "ALPHAX_PAPER_POSITION_GUARD_TIGHTEN_LOCK_PROFIT_PCT": ("position_guard_tighten_lock_profit_pct", lambda: _env_float("ALPHAX_PAPER_POSITION_GUARD_TIGHTEN_LOCK_PROFIT_PCT", 0.15)), "ALPHAX_PAPER_POSITION_GUARD_PROFIT_GIVEBACK_ENABLED": ("position_guard_profit_giveback_enabled", lambda: _env_bool("ALPHAX_PAPER_POSITION_GUARD_PROFIT_GIVEBACK_ENABLED", True)), "ALPHAX_PAPER_POSITION_GUARD_GIVEBACK_MIN_MAX_PNL_PCT": ("position_guard_giveback_min_max_pnl_pct", lambda: _env_float("ALPHAX_PAPER_POSITION_GUARD_GIVEBACK_MIN_MAX_PNL_PCT", 2.0)), "ALPHAX_PAPER_POSITION_GUARD_GIVEBACK_EXIT_PCT": ("position_guard_giveback_exit_pct", lambda: _env_float("ALPHAX_PAPER_POSITION_GUARD_GIVEBACK_EXIT_PCT", 70.0)), "ALPHAX_PAPER_POSITION_GUARD_GIVEBACK_EXIT_CURRENT_PNL_PCT": ("position_guard_giveback_exit_current_pnl_pct", lambda: _env_float("ALPHAX_PAPER_POSITION_GUARD_GIVEBACK_EXIT_CURRENT_PNL_PCT", 0.6)), "ALPHAX_PAPER_POSITION_GUARD_CRITICAL_EXIT_ENABLED": ("position_guard_critical_exit_enabled", lambda: _env_bool("ALPHAX_PAPER_POSITION_GUARD_CRITICAL_EXIT_ENABLED", True)), "ALPHAX_PAPER_POSITION_GUARD_CRITICAL_MIN_AGE_HOURS": ("position_guard_critical_min_age_hours", lambda: _env_float("ALPHAX_PAPER_POSITION_GUARD_CRITICAL_MIN_AGE_HOURS", 0.5)), "ALPHAX_PAPER_POSITION_GUARD_CRITICAL_MAX_PNL_PCT": ("position_guard_critical_max_pnl_pct", lambda: _env_float("ALPHAX_PAPER_POSITION_GUARD_CRITICAL_MAX_PNL_PCT", 1.0)), "ALPHAX_PAPER_GLOBAL_RISK_GATE_ENABLED": ("global_risk_gate_enabled", lambda: _env_bool("ALPHAX_PAPER_GLOBAL_RISK_GATE_ENABLED", True)), "ALPHAX_PAPER_GLOBAL_RISK_BLOCK_CRITICAL": ("global_risk_block_critical", lambda: _env_bool("ALPHAX_PAPER_GLOBAL_RISK_BLOCK_CRITICAL", False)), "ALPHAX_PAPER_GLOBAL_RISK_CRITICAL_MIN_REC_SCORE": ("global_risk_critical_min_rec_score", lambda: _env_float("ALPHAX_PAPER_GLOBAL_RISK_CRITICAL_MIN_REC_SCORE", 25.0)), "ALPHAX_PAPER_GLOBAL_RISK_SCORE_BLOCKS_INTRADAY": ("global_risk_score_blocks_intraday", lambda: _env_bool("ALPHAX_PAPER_GLOBAL_RISK_SCORE_BLOCKS_INTRADAY", False)), "ALPHAX_PAPER_GLOBAL_RISK_MIN_POSITION_MULTIPLIER": ("global_risk_min_position_multiplier", lambda: _env_float("ALPHAX_PAPER_GLOBAL_RISK_MIN_POSITION_MULTIPLIER", 0.2)), "ALPHAX_PAPER_GLOBAL_RISK_HIGH_MIN_REC_SCORE": ("global_risk_high_min_rec_score", lambda: _env_float("ALPHAX_PAPER_GLOBAL_RISK_HIGH_MIN_REC_SCORE", 25.0)), "ALPHAX_PAPER_GLOBAL_RISK_HIGH_DRAWDOWN_PCT": ("global_risk_high_drawdown_pct", lambda: _env_float("ALPHAX_PAPER_GLOBAL_RISK_HIGH_DRAWDOWN_PCT", 3.0)), "ALPHAX_PAPER_GLOBAL_RISK_CRITICAL_DRAWDOWN_PCT": ("global_risk_critical_drawdown_pct", lambda: _env_float("ALPHAX_PAPER_GLOBAL_RISK_CRITICAL_DRAWDOWN_PCT", 6.0)), "ALPHAX_PAPER_GLOBAL_RISK_MAX_OPEN_POSITIONS": ("global_risk_max_open_positions", lambda: _env_int("ALPHAX_PAPER_GLOBAL_RISK_MAX_OPEN_POSITIONS", 0)), "ALPHAX_PAPER_GLOBAL_RISK_MAX_SAME_SECTOR_POSITIONS": ("global_risk_max_same_sector_positions", lambda: _env_int("ALPHAX_PAPER_GLOBAL_RISK_MAX_SAME_SECTOR_POSITIONS", 3)), "ALPHAX_PAPER_GLOBAL_RISK_MAX_SAME_DIRECTION_POSITIONS": ("global_risk_max_same_direction_positions", lambda: _env_int("ALPHAX_PAPER_GLOBAL_RISK_MAX_SAME_DIRECTION_POSITIONS", 6)), "ALPHAX_PAPER_ORDER_MIN_REC_SCORE": ("order_min_rec_score", lambda: _env_float("ALPHAX_PAPER_ORDER_MIN_REC_SCORE", 25.0)), "ALPHAX_PAPER_ORDER_MIN_RR": ("order_min_rr", lambda: _env_float("ALPHAX_PAPER_ORDER_MIN_RR", overrides.get("min_rr", 1.25))), "ALPHAX_PAPER_ORDER_REQUIRE_RISK_REWARD_OK": ("order_require_risk_reward_ok", lambda: _env_bool("ALPHAX_PAPER_ORDER_REQUIRE_RISK_REWARD_OK", True)), "ALPHAX_PAPER_ORDER_MIN_DISTANCE_TO_ENTRY_PCT": ("order_min_distance_to_entry_pct", lambda: _env_float("ALPHAX_PAPER_ORDER_MIN_DISTANCE_TO_ENTRY_PCT", 0.0)), "ALPHAX_PAPER_ORDER_MAX_DISTANCE_TO_ENTRY_PCT": ("order_max_distance_to_entry_pct", lambda: _env_float("ALPHAX_PAPER_ORDER_MAX_DISTANCE_TO_ENTRY_PCT", 8.0)), "ALPHAX_PAPER_ORDER_REQUIRE_CURRENT_TRIGGER": ("order_require_current_trigger", lambda: _env_bool("ALPHAX_PAPER_ORDER_REQUIRE_CURRENT_TRIGGER", False)), "ALPHAX_PAPER_ORDER_CANCEL_FAR_FROM_ENTRY_PCT": ("order_cancel_far_from_entry_pct", lambda: _env_float("ALPHAX_PAPER_ORDER_CANCEL_FAR_FROM_ENTRY_PCT", 12.0)), "ALPHAX_PAPER_ORDER_EXPIRE_HOURS": ("order_expire_hours", lambda: _env_float("ALPHAX_PAPER_ORDER_EXPIRE_HOURS", 8.0)), } for env_name, (key, loader) in checks.items(): if _env_present(env_name): overrides[key] = loader() return overrides def default_live_trading_config(): return { "enabled": _env_bool("ALPHAX_LIVE_TRADING_ENABLED", False), "execution_mode": _env_str("ALPHAX_LIVE_TRADING_EXECUTION_MODE", "exchange_api"), "require_human_approval": _env_bool("ALPHAX_LIVE_TRADING_REQUIRE_HUMAN_APPROVAL", True), "exchange": _env_str("ALPHAX_LIVE_TRADING_EXCHANGE", "binance"), "market_type": _env_str("ALPHAX_LIVE_TRADING_MARKET_TYPE", "um_futures"), "testnet": _env_bool("ALPHAX_LIVE_TRADING_TESTNET", True), "sandbox_mode": _env_str("ALPHAX_LIVE_TRADING_SANDBOX_MODE", "demo"), "account_code": _env_str("ALPHAX_LIVE_TRADING_ACCOUNT_CODE", "binance_um_futures_main"), "api_key_env": _env_str("ALPHAX_BINANCE_API_KEY_ENV", "ALPHAX_BINANCE_API_KEY"), "api_secret_env": _env_str("ALPHAX_BINANCE_API_SECRET_ENV", "ALPHAX_BINANCE_API_SECRET"), "supported_exchanges": ["binance"], "supported_market_types": ["spot", "um_futures"], "default_leverage": _env_float("ALPHAX_LIVE_TRADING_DEFAULT_LEVERAGE", 1), "risk": { "max_order_margin_usdt": _env_float("ALPHAX_LIVE_TRADING_MAX_ORDER_MARGIN_USDT", 10), "max_order_notional_usdt": _env_float("ALPHAX_LIVE_TRADING_MAX_ORDER_NOTIONAL_USDT", 50), "max_symbol_leverage": _env_float("ALPHAX_LIVE_TRADING_MAX_SYMBOL_LEVERAGE", 1), "max_cumulative_leverage": _env_float("ALPHAX_LIVE_TRADING_MAX_CUMULATIVE_LEVERAGE", 1), "max_daily_order_count": _env_int("ALPHAX_LIVE_TRADING_MAX_DAILY_ORDER_COUNT", 5), "allowed_symbols": _env_list("ALPHAX_LIVE_TRADING_ALLOWED_SYMBOLS", []), }, } def default_price_streamer_config(): return { "enabled": _env_bool("ALPHAX_PRICE_STREAMER_ENABLED", True), "provider": _env_str("ALPHAX_PRICE_STREAMER_PROVIDER", "binance_spot"), "stream_url": _env_str("ALPHAX_PRICE_STREAMER_URL", "wss://stream.binance.com:9443/stream"), "refresh_symbols_seconds": _env_float("ALPHAX_PRICE_STREAMER_REFRESH_SYMBOLS_SECONDS", 20), "idle_sleep_seconds": _env_float("ALPHAX_PRICE_STREAMER_IDLE_SLEEP_SECONDS", 5), "reconnect_delay_seconds": _env_float("ALPHAX_PRICE_STREAMER_RECONNECT_DELAY_SECONDS", 5), "ping_interval_seconds": _env_float("ALPHAX_PRICE_STREAMER_PING_INTERVAL_SECONDS", 30), "ping_timeout_seconds": _env_float("ALPHAX_PRICE_STREAMER_PING_TIMEOUT_SECONDS", 60), "close_timeout_seconds": _env_float("ALPHAX_PRICE_STREAMER_CLOSE_TIMEOUT_SECONDS", 5), "transient_log_interval_seconds": _env_float("ALPHAX_PRICE_STREAMER_TRANSIENT_LOG_INTERVAL_SECONDS", 900), "max_stream_symbols": _env_int("ALPHAX_PRICE_STREAMER_MAX_SYMBOLS", 200), "include_actionable_recommendations": True, "include_open_paper_trades": True, "update_latest_price_cache": True, "sync_paper_trading": True, "log_every_events": _env_int("ALPHAX_PRICE_STREAMER_LOG_EVERY_EVENTS", 100), } def default_sentiment_config(): return { "enabled": _env_bool("ALPHAX_SENTIMENT_ENABLED", True), "provider": _env_str("ALPHAX_SENTIMENT_PROVIDER", "coingecko"), "max_bonus": _env_float("ALPHAX_SENTIMENT_MAX_BONUS", 2), "trending_top5_bonus": _env_float("ALPHAX_SENTIMENT_TOP5_BONUS", 2.0), "trending_top10_bonus": _env_float("ALPHAX_SENTIMENT_TOP10_BONUS", 1.0), "new_entry_bonus": _env_float("ALPHAX_SENTIMENT_NEW_ENTRY_BONUS", 1.0), "decay_hours": _env_float("ALPHAX_SENTIMENT_DECAY_HOURS", 6), "decay_factor": _env_float("ALPHAX_SENTIMENT_DECAY_FACTOR", 0.1), "min_decay": _env_float("ALPHAX_SENTIMENT_MIN_DECAY", 0.3), "collect_interval_min": _env_int("ALPHAX_SENTIMENT_COLLECT_INTERVAL_MIN", 30), "alert_conditions": { "holding_top3": True, "new_trending_top10": True, }, } def default_event_driven_config(): return { "enabled": _env_bool("ALPHAX_EVENT_DRIVEN_ENABLED", True), "poll_interval_min": _env_int("ALPHAX_EVENT_POLL_INTERVAL_MIN", 1), "decision_target_seconds": _env_int("ALPHAX_EVENT_DECISION_TARGET_SECONDS", 60), "news_time_window_hours": _env_float("ALPHAX_EVENT_NEWS_TIME_WINDOW_HOURS", 3), "max_event_age_hours": _env_float("ALPHAX_EVENT_MAX_EVENT_AGE_HOURS", 6), "dedup_window_hours": _env_float("ALPHAX_EVENT_DEDUP_WINDOW_HOURS", 24), "min_importance_level": _env_str("ALPHAX_EVENT_MIN_IMPORTANCE_LEVEL", "A"), "sources": {}, "importance": { "s_keywords": [ "will list", "will launch", "futures will launch", "perpetual contract", "launchpool", "megadrop", "hodler airdrops", "coinbase will add", "upbit listing", "bithumb listing", ], "a_keywords": [ "margin will add", "new trading pairs", "earn", "convert", "roadmap", "mainnet", "tokenomics", "airdrop", "burn", "buyback", "partnership", "integration", "upgrade", ], "negative_keywords": [ "delist", "suspend trading", "remove", "cease trading", "risk warning", ], }, "technical_check": { "min_tech_score_recommend": 6, "min_tech_score_observe": 3, "reject_if_24h_gain_gt": 30, "warn_if_24h_gain_gt": 18, "reject_if_funding_gt": 0.003, "allow_static_accumulation": True, "allow_volume_breakout": True, "allow_ignition": True, }, "push": { "recommend": True, "observe": True, "risk": True, "cooldown_hours": 6, }, "theme_expansion": { "enabled": True, "min_theme_importance": "A", "max_expanded_symbols": 12, "static_accumulation_bonus": { "enabled": True, "min_static_count": 8, "score_bonus": 3, "note": "重大生态事件命中后,强静K蓄力币提前升权", }, "themes": {}, }, } def default_monitoring_config(): return { "untouched_rate": { "description": "未触达率自动监控", "threshold_pct": 35, "check_window_days": 2, "auto_bump": { "enabled": True, "min_score_5_to_6": True, "min_score_max": 6, "require_human_if_exceeded": True, }, }, "param_audit": { "description": "参数变更审计", "validate_script": "scripts/validate_params.py", "hash_algorithm": "semantic_sha256", "critical_sections": ["confirm", "screener", "pa_engine", "signal_weights", "tracker", "sentiment"], }, } def default_notification_config(): return { "enabled": _env_bool("ALPHAX_NOTIFICATION_ENABLED", True), "feishu": { "enabled": _env_bool("ALPHAX_FEISHU_ENABLED", True), "webhook_env": _env_str("ALPHAX_FEISHU_WEBHOOK_ENV", "ALTCOIN_FEISHU_WEBHOOK"), "timeout": _env_int("ALPHAX_FEISHU_TIMEOUT", 10), }, } def default_email_config(): return { "enabled": _env_bool("ALPHAX_EMAIL_ENABLED", True), "smtp": { "host": _env_str("ALPHAX_SMTP_HOST", _env_str("ASTOCK_SMTP_HOST", "")), "port": _env_int("ALPHAX_SMTP_PORT", _env_int("ASTOCK_SMTP_PORT", 465)), "username_env": _env_str("ALPHAX_SMTP_USERNAME_ENV", "ASTOCK_SMTP_USERNAME"), "password_env": _env_str("ALPHAX_SMTP_PASSWORD_ENV", "ASTOCK_SMTP_PASSWORD"), "sender": _env_str("ALPHAX_SMTP_SENDER", ""), "sender_env": _env_str("ALPHAX_SMTP_SENDER_ENV", "ASTOCK_SMTP_SENDER"), "timeout": _env_int("ALPHAX_SMTP_TIMEOUT", 12), }, } def default_bootstrap_admin_config(): return { "enabled": _env_bool("ALPHAX_BOOTSTRAP_ADMIN", True), "email_env": _env_str("ALPHAX_DEFAULT_ADMIN_EMAIL_ENV", "ALPHAX_DEFAULT_ADMIN_EMAIL"), "password_env": _env_str("ALPHAX_DEFAULT_ADMIN_PASSWORD_ENV", "ALPHAX_DEFAULT_ADMIN_PASSWORD"), } def default_scheduler_config(): return { "dry_run": _env_bool("ALPHAX_SCHEDULER_DRY_RUN", True), "poll_seconds": _env_float("ALPHAX_SCHEDULER_POLL_SECONDS", 1.0), "config_reload_seconds": _env_float("ALPHAX_SCHEDULER_CONFIG_RELOAD_SECONDS", 5.0), "pending_warn_seconds": _env_float("ALPHAX_SCHEDULER_PENDING_WARN_SECONDS", 30.0), } def seed_runtime_system_defaults(): return seed_system_defaults({ "llm": (default_llm_config(), "LLM provider and module switches; API key remains in env"), "paper_trading": (default_paper_trading_config(), "Paper trading account and execution model"), "live_trading": (default_live_trading_config(), "Live trading exchange, account and risk settings; API secrets remain in env"), "price_streamer": (default_price_streamer_config(), "Realtime websocket price streamer settings"), "sentiment": (default_sentiment_config(), "Sentiment monitoring settings"), "event_driven": (default_event_driven_config(), "Event/news driven screening settings"), "monitoring": (default_monitoring_config(), "Monitoring and audit settings"), "notification": (default_notification_config(), "Notification channel switches and env pointers"), "email": (default_email_config(), "SMTP email settings; password remains in env"), "bootstrap_admin": (default_bootstrap_admin_config(), "Default admin bootstrap env pointers"), "scheduler": (default_scheduler_config(), "Scheduler runtime process settings"), }) def _seed_one(key: str, value, description: str): return seed_system_defaults({key: (value, description)}) def llm_config(): cfg = get_llm_config(default=None) if cfg is None: _seed_one("llm", default_llm_config(), "LLM provider and module switches; API key remains in env") cfg = get_llm_config(default=None) merged = deep_merge(default_llm_config(), cfg or {}) return deep_merge(merged, _llm_env_overrides()) def paper_trading_config(): cfg = get_paper_trading_config(default=None) if cfg is None: _seed_one("paper_trading", default_paper_trading_config(), "Paper trading account and execution model") cfg = get_paper_trading_config(default=None) merged = deep_merge(default_paper_trading_config(), cfg or {}) merged = deep_merge(merged, _paper_trading_env_overrides()) if str(merged.get("trading_mode") or "").strip() == "intraday_trading": intraday_defaults = { "min_rr": 1.25, "entry_min_rr": 1.25, "order_min_rr": 1.25, "entry_min_rec_score": 25.0, "order_min_rec_score": 25.0, "global_risk_high_min_rec_score": 25.0, "global_risk_critical_min_rec_score": 25.0, "global_risk_score_blocks_intraday": False, "order_min_distance_to_entry_pct": 0.0, "order_expire_hours": 8.0, "trailing_activate_pnl_pct": 2.0, "trailing_volatility_min_activation_pct": 1.8, } explicit = _paper_trading_env_overrides() for key, value in intraday_defaults.items(): if key in explicit: continue current = merged.get(key) # Treat old seeded defaults as compatibility noise, while still # honoring deliberately more active manual settings. if key in { "min_rr", "entry_min_rr", "order_min_rr", "entry_min_rec_score", "order_min_rec_score", "global_risk_high_min_rec_score", "global_risk_critical_min_rec_score", "order_min_distance_to_entry_pct", "order_expire_hours", "trailing_activate_pnl_pct", "trailing_volatility_min_activation_pct", } and _env_float("__ALPHAX_UNUSED__", value) < _env_float("__ALPHAX_UNUSED__", current or value): merged[key] = value elif key == "global_risk_score_blocks_intraday" and current is None: merged[key] = value return merged def live_trading_config(): cfg = get_live_trading_config(default=None) if cfg is None: _seed_one("live_trading", default_live_trading_config(), "Live trading exchange, account and risk settings; API secrets remain in env") cfg = get_live_trading_config(default=None) return deep_merge(default_live_trading_config(), cfg or {}) def price_streamer_config(): cfg = get_price_streamer_config(default=None) if cfg is None: _seed_one("price_streamer", default_price_streamer_config(), "Realtime websocket price streamer settings") cfg = get_price_streamer_config(default=None) return cfg or default_price_streamer_config() def notification_config(): cfg = get_notification_config(default=None) if cfg is None: _seed_one("notification", default_notification_config(), "Notification channel switches and env pointers") cfg = get_notification_config(default=None) return cfg or default_notification_config() def sentiment_config(): cfg = get_sentiment_config(default=None) if cfg is None: _seed_one("sentiment", default_sentiment_config(), "Sentiment monitoring settings") cfg = get_sentiment_config(default=None) return cfg or default_sentiment_config() def event_driven_config(): cfg = get_event_driven_config(default=None) if cfg is None: _seed_one("event_driven", default_event_driven_config(), "Event/news driven screening settings") cfg = get_event_driven_config(default=None) return cfg or default_event_driven_config() def monitoring_config(): cfg = get_monitoring_config(default=None) if cfg is None: _seed_one("monitoring", default_monitoring_config(), "Monitoring and audit settings") cfg = get_monitoring_config(default=None) return cfg or default_monitoring_config() def email_config(): cfg = get_email_config(default=None) if cfg is None: _seed_one("email", default_email_config(), "SMTP email settings; password remains in env") cfg = get_email_config(default=None) return cfg or default_email_config() def bootstrap_admin_config(): cfg = get_bootstrap_admin_config(default=None) if cfg is None: _seed_one("bootstrap_admin", default_bootstrap_admin_config(), "Default admin bootstrap env pointers") cfg = get_bootstrap_admin_config(default=None) return cfg or default_bootstrap_admin_config() def scheduler_config(): cfg = get_scheduler_config(default=None) if cfg is None: _seed_one("scheduler", default_scheduler_config(), "Scheduler runtime process settings") cfg = get_scheduler_config(default=None) return cfg or default_scheduler_config() __all__ = [ "bootstrap_admin_config", "default_bootstrap_admin_config", "default_email_config", "default_event_driven_config", "default_llm_config", "default_live_trading_config", "default_monitoring_config", "default_notification_config", "default_paper_trading_config", "default_price_streamer_config", "default_scheduler_config", "default_sentiment_config", "email_config", "event_driven_config", "llm_config", "live_trading_config", "monitoring_config", "notification_config", "paper_trading_config", "price_streamer_config", "scheduler_config", "sentiment_config", "seed_runtime_system_defaults", ]