diff --git a/backend/api/routes/config.py b/backend/api/routes/config.py index d757c00..1b08620 100644 --- a/backend/api/routes/config.py +++ b/backend/api/routes/config.py @@ -242,6 +242,12 @@ AUTO_TRADE_FILTER_DEFAULTS = { "category": "strategy", "description": "震荡市额外提高的最小信号门槛。比如 2 表示在基础 MIN_SIGNAL_STRENGTH 上再 +2。", }, + "AUTO_TRADE_SYMBOL_WHITELIST": { + "value": "", + "type": "string", + "category": "strategy", + "description": "自动交易白名单(逗号/空格分隔)。非空则仅这些合约自动下单,其余仍扫描并生成推荐;空表示不限制。用于「窄宇宙强执行」。", + }, } # 风险/策略预设(用于一键切换“稳健 / 快速验证”等模式) diff --git a/backend/config_manager.py b/backend/config_manager.py index c935fb0..5f90686 100644 --- a/backend/config_manager.py +++ b/backend/config_manager.py @@ -921,6 +921,8 @@ class ConfigManager: 'AUTO_TRADE_ALLOW_UNKNOWN': eff_get('AUTO_TRADE_ALLOW_UNKNOWN', False), 'AUTO_TRADE_ALLOW_4H_NEUTRAL': eff_get('AUTO_TRADE_ALLOW_4H_NEUTRAL', allow_neutral_default), 'RANGING_MARKET_SIGNAL_BOOST': eff_get('RANGING_MARKET_SIGNAL_BOOST', 2), + # 自动交易白名单:非空则仅这些合约自动下单;空表示不限制(窄宇宙) + 'AUTO_TRADE_SYMBOL_WHITELIST': (eff_get('AUTO_TRADE_SYMBOL_WHITELIST', '') or '').strip(), # 智能入场/限价偏移(部分逻辑会直接读取 TRADING_CONFIG) 'LIMIT_ORDER_OFFSET_PCT': eff_get('LIMIT_ORDER_OFFSET_PCT', 0.5), diff --git a/backend/market_overview.py b/backend/market_overview.py index cb99750..812b31a 100644 --- a/backend/market_overview.py +++ b/backend/market_overview.py @@ -206,6 +206,7 @@ def get_strategy_execution_overview() -> Dict[str, Any]: mgr.reload_from_redis() for key in ( "AUTO_TRADE_ENABLED", "AUTO_TRADE_ONLY_TRENDING", "AUTO_TRADE_ALLOW_4H_NEUTRAL", + "AUTO_TRADE_SYMBOL_WHITELIST", "MIN_SIGNAL_STRENGTH", "LOW_VOLATILITY_MIN_SIGNAL_STRENGTH", "MARKET_REGIME_AUTO", "TOP_N_SYMBOLS", "SCAN_INTERVAL", "PRIMARY_INTERVAL", "CONFIRM_INTERVAL", "MAX_OPEN_POSITIONS", "MAX_DAILY_ENTRIES", "FIXED_RISK_PERCENT", "USE_FIXED_RISK_SIZING", @@ -236,6 +237,7 @@ def get_strategy_execution_overview() -> Dict[str, Any]: auto_on = _g("AUTO_TRADE_ENABLED", True, cfg) only_trending = _g("AUTO_TRADE_ONLY_TRENDING", True, cfg) allow_4h_neutral = _g("AUTO_TRADE_ALLOW_4H_NEUTRAL", False, cfg) + wl_raw = str(cfg.get("AUTO_TRADE_SYMBOL_WHITELIST") or "").strip() min_strength = _g("MIN_SIGNAL_STRENGTH", 8, cfg) low_vol_strength = _g("LOW_VOLATILITY_MIN_SIGNAL_STRENGTH", 9, cfg) regime_auto = _g("MARKET_REGIME_AUTO", True, cfg) @@ -249,6 +251,10 @@ def get_strategy_execution_overview() -> Dict[str, Any]: c1.append("• 信号强度 ≥ " + str(min_strength) + "(技术指标综合评分);低波动期自动提高至 " + str(low_vol_strength) + "(" + ("已开启" if regime_auto else "未开启") + "市场节奏识别)。") c1.append("• 市场状态:仅当「仅做趋势市」开启时,要求市场状态为 trending 才下单;ranging/unknown 只生成推荐、不自动下单。当前「仅做趋势市」=" + ("是" if only_trending else "否") + "。") c1.append("• 4H 趋势:允许 4H 中性时自动交易 = " + ("是" if allow_4h_neutral else "否") + ";为否时 4H 为中性会跳过自动下单。") + if wl_raw: + c1.append("• 自动交易白名单:已配置(仅名单内合约会自动下单;其余仍可出现在推荐中)。") + else: + c1.append("• 自动交易白名单:未启用(空表示不限制合约)。") sections.append({ "title": "一、总开关与自动交易条件", "content": "\n".join(c1), diff --git a/config/strategy_narrow_universe.json b/config/strategy_narrow_universe.json new file mode 100644 index 0000000..c087598 --- /dev/null +++ b/config/strategy_narrow_universe.json @@ -0,0 +1,110 @@ +{ + "AUTO_TRADE_ONLY_TRENDING": { + "value": true, + "type": "boolean", + "category": "strategy", + "description": "仅趋势市自动下单;震荡/unknown 只出推荐" + }, + "AUTO_TRADE_ALLOW_4H_NEUTRAL": { + "value": false, + "type": "boolean", + "category": "strategy", + "description": "4H 中性时不自动下单,减少震荡扫损" + }, + "AUTO_TRADE_ALLOW_RANGING": { + "value": false, + "type": "boolean", + "category": "strategy", + "description": "与 ONLY_TRENDING 配合:不在横盘自动开仓" + }, + "AUTO_TRADE_ALLOW_UNKNOWN": { + "value": false, + "type": "boolean", + "category": "strategy", + "description": "市场状态未判定时不自动开仓" + }, + "MIN_SIGNAL_STRENGTH": { + "value": 9, + "type": "number", + "category": "strategy", + "description": "提高入场门槛,减少边缘单" + }, + "RANGING_MARKET_SIGNAL_BOOST": { + "value": 3, + "type": "number", + "category": "strategy", + "description": "震荡市在基础门槛上再加 3" + }, + "TOP_N_SYMBOLS": { + "value": 12, + "type": "number", + "category": "strategy", + "description": "每次扫描深入分析的前 N 个标的,略缩小候选池" + }, + "AUTO_TRADE_SYMBOL_WHITELIST": { + "value": "", + "type": "string", + "category": "strategy", + "description": "核心:填逗号分隔合约如 SOLUSDT,LINKUSDT 则仅这些自动下单;留空则不限(仅启用下面黑名单/降仓)。" + }, + "MANUAL_BLOCKED_SYMBOLS": { + "value": "MOVRUSDT", + "type": "string", + "category": "strategy", + "description": "曾出现单日大亏的标的可拉黑" + }, + "MANUAL_REDUCED_SYMBOLS": { + "value": "HYPEUSDT,XANUSDT,WIFUSDT,ANKRUSDT,RIVERUSDT,POLYXUSDT,PIPPINUSDT,AINUSDT,ANIMEUSDT,COSUSDT,GUNUSDT,HYPERUSDT,WETUSDT,MONUSDT,ONUSDT,MYXUSDT,TRADOORUSDT", + "type": "string", + "category": "strategy", + "description": "弱势/反复试错币:降仓并抬高信号门槛" + }, + "MANUAL_REDUCED_SYMBOL_POSITION_FACTOR": { + "value": 0.35, + "type": "number", + "category": "strategy", + "description": "降仓名单仓位系数" + }, + "MANUAL_REDUCED_SYMBOL_SIGNAL_BOOST": { + "value": 1, + "type": "number", + "category": "strategy", + "description": "降仓名单额外 +1 信号门槛" + }, + "ENTRY_PULLBACK_FILTER_ENABLED": { + "value": true, + "type": "boolean", + "category": "strategy", + "description": "回踩/区间入场过滤" + }, + "ENTRY_PULLBACK_MIN_BARS": { + "value": 6, + "type": "number", + "category": "strategy", + "description": "略严一档,减少追高" + }, + "ENTRY_PULLBACK_MAX_LONG_IN_RANGE": { + "value": 0.56, + "type": "number", + "category": "strategy", + "description": "做多要求在区间偏低位置" + }, + "ENTRY_PULLBACK_MIN_SHORT_IN_RANGE": { + "value": 0.44, + "type": "number", + "category": "strategy", + "description": "做空要求在区间偏高位置" + }, + "BETA_FILTER_ENABLED": { + "value": true, + "type": "boolean", + "category": "strategy", + "description": "大盘共振过滤" + }, + "BETA_FILTER_THRESHOLD": { + "value": -0.01, + "type": "number", + "category": "strategy", + "description": "与推荐脚本默认一致" + } +} diff --git a/scripts/apply_recommended_config.py b/scripts/apply_recommended_config.py index e90337f..b58a1f1 100644 --- a/scripts/apply_recommended_config.py +++ b/scripts/apply_recommended_config.py @@ -32,6 +32,8 @@ if str(PROJECT_ROOT) not in sys.path: # 默认推荐配置(可按你的风格长期维护) DEFAULT_RECOMMENDED: Dict[str, Dict[str, Any]] = { + # 空=不限制;非空则仅名单内合约自动下单(窄宇宙),见 config/strategy_narrow_universe.json + "AUTO_TRADE_SYMBOL_WHITELIST": {"value": "", "type": "string", "category": "strategy"}, "AUTO_TRADE_ONLY_TRENDING": {"value": True, "type": "boolean", "category": "strategy"}, "BETA_FILTER_ENABLED": {"value": True, "type": "boolean", "category": "strategy"}, "BETA_FILTER_THRESHOLD": {"value": -0.01, "type": "number", "category": "strategy"}, diff --git a/trading_system/config.py b/trading_system/config.py index 630d1cb..a10fb20 100644 --- a/trading_system/config.py +++ b/trading_system/config.py @@ -352,6 +352,8 @@ DEFAULT_TRADING_CONFIG = { 'AUTO_TRADE_ALLOW_4H_NEUTRAL': True, # 震荡市额外提高的最小信号门槛(0-3 建议范围);默认 +2,减少边缘信号 'RANGING_MARKET_SIGNAL_BOOST': 2, + # 自动交易白名单(逗号分隔)。非空则仅名单内合约自动下单;空=不限制。扫描仍可覆盖全市场,仅自动分支拦截。 + 'AUTO_TRADE_SYMBOL_WHITELIST': '', # ===== 趋势入场过滤(防止追在半山腰)===== # 是否启用基于趋势状态的入场过滤: diff --git a/trading_system/strategy.py b/trading_system/strategy.py index 5bb6c25..cb5b1d6 100644 --- a/trading_system/strategy.py +++ b/trading_system/strategy.py @@ -13,7 +13,7 @@ try: from .position_manager import PositionManager from .signal_filters import SignalFilterContext, apply_signal_filters from . import config - from .symbol_policy import resolve_symbol_trading_policy + from .symbol_policy import resolve_symbol_trading_policy, auto_trade_whitelist_allows from .shadow_mode import evaluate_shadow_mode, invalidate_cache as shadow_invalidate_cache except ImportError: from binance_client import BinanceClient @@ -22,7 +22,7 @@ except ImportError: from position_manager import PositionManager from signal_filters import SignalFilterContext, apply_signal_filters import config - from symbol_policy import resolve_symbol_trading_policy + from symbol_policy import resolve_symbol_trading_policy, auto_trade_whitelist_allows from shadow_mode import evaluate_shadow_mode, invalidate_cache as shadow_invalidate_cache logger = logging.getLogger(__name__) @@ -183,6 +183,14 @@ class TradingStrategy: logger.info(f"{symbol} 自动交易已关闭(AUTO_TRADE_ENABLED=false),跳过自动下单(推荐已生成)") continue + # 窄宇宙:可选自动交易白名单(非空则仅名单内合约自动下单) + wl_raw = config.TRADING_CONFIG.get("AUTO_TRADE_SYMBOL_WHITELIST", "") or "" + if not auto_trade_whitelist_allows(symbol, wl_raw): + logger.info( + f"{symbol} 不在自动交易白名单内,跳过自动下单(仅生成推荐)|AUTO_TRADE_SYMBOL_WHITELIST" + ) + continue + # 提升胜率:可配置的“仅 trending 自动交易”过滤 only_trending = bool(config.TRADING_CONFIG.get("AUTO_TRADE_ONLY_TRENDING", True)) allow_ranging = bool(config.TRADING_CONFIG.get("AUTO_TRADE_ALLOW_RANGING", False)) diff --git a/trading_system/symbol_policy.py b/trading_system/symbol_policy.py index 2360d30..323fca9 100644 --- a/trading_system/symbol_policy.py +++ b/trading_system/symbol_policy.py @@ -11,6 +11,22 @@ from typing import Any, Dict, Set logger = logging.getLogger(__name__) +def auto_trade_whitelist_allows(symbol: str, whitelist_raw: Any) -> bool: + """ + 自动交易白名单(窄宇宙)。 + + - 当 AUTO_TRADE_SYMBOL_WHITELIST 为空/未配置时:不限制,与历史行为一致。 + - 非空时:仅名单内的合约允许自动下单;仍会扫描并生成推荐(由 strategy 层在自动分支前拦截)。 + """ + sym = (symbol or "").strip().upper() + if not sym: + return False + allowed = _parse_symbol_list(whitelist_raw) + if not allowed: + return True + return sym in allowed + + def _parse_symbol_list(raw: Any) -> Set[str]: """兼容 string/list/dict 三种配置格式,统一转成大写 symbol 集合。""" if raw is None: