From fe420ad1a40ba4d5043ead9b59ebdb4607cfcd15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=87=E8=96=87=E5=AE=89?= Date: Fri, 23 Jan 2026 17:36:31 +0800 Subject: [PATCH] a --- backend/api/routes/config.py | 261 ++++++++++++++++++----------------- 1 file changed, 131 insertions(+), 130 deletions(-) diff --git a/backend/api/routes/config.py b/backend/api/routes/config.py index 54d9255..fea26a3 100644 --- a/backend/api/routes/config.py +++ b/backend/api/routes/config.py @@ -282,6 +282,137 @@ async def get_all_configs( raise HTTPException(status_code=500, detail=str(e)) +# ⚠️ 重要:全局配置路由必须在 /{key} 之前,否则会被动态路由匹配 +@router.get("/global") +async def get_global_configs( + user: Dict[str, Any] = Depends(get_current_user), +): + """获取全局策略配置(仅管理员)""" + if (user.get("role") or "user") != "admin": + raise HTTPException(status_code=403, detail="仅管理员可访问全局策略配置") + + try: + from database.models import GlobalStrategyConfig + configs = GlobalStrategyConfig.get_all() + logger.info(f"从数据库加载了 {len(configs)} 个全局配置项") + result = {} + for config in configs: + key = config['config_key'] + value = GlobalStrategyConfig._convert_value( + config['config_value'], + config['config_type'] + ) + result[key] = { + "value": value, + "type": config['config_type'], + "category": config['category'], + "description": config.get('description'), + } + logger.debug(f"加载配置项: {key} = {value} (type: {config['config_type']}, category: {config['category']})") + + # 添加默认配置(如果数据库中没有) + for k, meta in CORE_STRATEGY_CONFIG_DEFAULTS.items(): + if k not in result: + result[k] = meta + + # 固定风险百分比配置 + FIXED_RISK_CONFIG_DEFAULTS = { + "USE_FIXED_RISK_SIZING": { + "value": True, + "type": "boolean", + "category": "risk", + "description": "使用固定风险百分比计算仓位(凯利公式)。启用后,每笔单子承受的风险固定为 FIXED_RISK_PERCENT,避免大额亏损。", + }, + "FIXED_RISK_PERCENT": { + "value": 0.02, + "type": "number", + "category": "risk", + "description": "每笔单子承受的风险百分比(相对于总资金)。例如 0.02 表示 2%。启用固定风险后,每笔亏损限制在该百分比内。", + }, + } + for k, meta in FIXED_RISK_CONFIG_DEFAULTS.items(): + if k not in result: + result[k] = meta + + # 添加更多核心策略配置的默认值(确保前端能显示所有重要配置) + ADDITIONAL_STRATEGY_DEFAULTS = { + "USE_ATR_STOP_LOSS": { + "value": True, + "type": "boolean", + "category": "strategy", + "description": "是否使用ATR动态止损(优先于固定百分比)", + }, + "ATR_PERIOD": { + "value": 14, + "type": "number", + "category": "strategy", + "description": "ATR计算周期(默认14)", + }, + "USE_DYNAMIC_ATR_MULTIPLIER": { + "value": False, + "type": "boolean", + "category": "strategy", + "description": "是否根据波动率动态调整ATR倍数", + }, + "ATR_MULTIPLIER_MIN": { + "value": 1.5, + "type": "number", + "category": "strategy", + "description": "动态ATR倍数最小值", + }, + "ATR_MULTIPLIER_MAX": { + "value": 2.5, + "type": "number", + "category": "strategy", + "description": "动态ATR倍数最大值", + }, + "MIN_SIGNAL_STRENGTH": { + "value": 8, + "type": "number", + "category": "strategy", + "description": "最小信号强度(0-10),只交易高质量信号", + }, + "SCAN_INTERVAL": { + "value": 1800, + "type": "number", + "category": "scan", + "description": "市场扫描间隔(秒),默认30分钟", + }, + "TOP_N_SYMBOLS": { + "value": 8, + "type": "number", + "category": "scan", + "description": "每次扫描后处理的交易对数量", + }, + "MIN_VOLUME_24H_STRICT": { + "value": 10000000, + "type": "number", + "category": "scan", + "description": "严格成交量过滤,24H Volume低于此值(USD)直接剔除", + }, + "USE_TRAILING_STOP": { + "value": False, + "type": "boolean", + "category": "strategy", + "description": "是否启用移动止损(默认关闭,让利润奔跑)", + }, + "SMART_ENTRY_ENABLED": { + "value": False, + "type": "boolean", + "category": "strategy", + "description": "智能入场开关。关闭后回归纯限价单模式", + }, + } + for k, meta in ADDITIONAL_STRATEGY_DEFAULTS.items(): + if k not in result: + result[k] = meta + + logger.info(f"返回全局配置项数量: {len(result)}") + return result + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + @router.get("/feasibility-check") async def check_config_feasibility( user: Dict[str, Any] = Depends(get_current_user), @@ -840,136 +971,6 @@ async def get_config_meta(user: Dict[str, Any] = Depends(get_current_user)) -> D } -@router.get("/global") -async def get_global_configs( - user: Dict[str, Any] = Depends(get_current_user), -): - """获取全局策略配置(仅管理员)""" - if (user.get("role") or "user") != "admin": - raise HTTPException(status_code=403, detail="仅管理员可访问全局策略配置") - - try: - from database.models import GlobalStrategyConfig - configs = GlobalStrategyConfig.get_all() - logger.info(f"从数据库加载了 {len(configs)} 个全局配置项") - result = {} - for config in configs: - key = config['config_key'] - value = GlobalStrategyConfig._convert_value( - config['config_value'], - config['config_type'] - ) - result[key] = { - "value": value, - "type": config['config_type'], - "category": config['category'], - "description": config.get('description'), - } - logger.debug(f"加载配置项: {key} = {value} (type: {config['config_type']}, category: {config['category']})") - - # 添加默认配置(如果数据库中没有) - for k, meta in CORE_STRATEGY_CONFIG_DEFAULTS.items(): - if k not in result: - result[k] = meta - - # 固定风险百分比配置 - FIXED_RISK_CONFIG_DEFAULTS = { - "USE_FIXED_RISK_SIZING": { - "value": True, - "type": "boolean", - "category": "risk", - "description": "使用固定风险百分比计算仓位(凯利公式)。启用后,每笔单子承受的风险固定为 FIXED_RISK_PERCENT,避免大额亏损。", - }, - "FIXED_RISK_PERCENT": { - "value": 0.02, - "type": "number", - "category": "risk", - "description": "每笔单子承受的风险百分比(相对于总资金)。例如 0.02 表示 2%。启用固定风险后,每笔亏损限制在该百分比内。", - }, - } - for k, meta in FIXED_RISK_CONFIG_DEFAULTS.items(): - if k not in result: - result[k] = meta - - # 添加更多核心策略配置的默认值(确保前端能显示所有重要配置) - ADDITIONAL_STRATEGY_DEFAULTS = { - "USE_ATR_STOP_LOSS": { - "value": True, - "type": "boolean", - "category": "strategy", - "description": "是否使用ATR动态止损(优先于固定百分比)", - }, - "ATR_PERIOD": { - "value": 14, - "type": "number", - "category": "strategy", - "description": "ATR计算周期(默认14)", - }, - "USE_DYNAMIC_ATR_MULTIPLIER": { - "value": False, - "type": "boolean", - "category": "strategy", - "description": "是否根据波动率动态调整ATR倍数", - }, - "ATR_MULTIPLIER_MIN": { - "value": 1.5, - "type": "number", - "category": "strategy", - "description": "动态ATR倍数最小值", - }, - "ATR_MULTIPLIER_MAX": { - "value": 2.5, - "type": "number", - "category": "strategy", - "description": "动态ATR倍数最大值", - }, - "MIN_SIGNAL_STRENGTH": { - "value": 8, - "type": "number", - "category": "strategy", - "description": "最小信号强度(0-10),只交易高质量信号", - }, - "SCAN_INTERVAL": { - "value": 1800, - "type": "number", - "category": "scan", - "description": "市场扫描间隔(秒),默认30分钟", - }, - "TOP_N_SYMBOLS": { - "value": 8, - "type": "number", - "category": "scan", - "description": "每次扫描后处理的交易对数量", - }, - "MIN_VOLUME_24H_STRICT": { - "value": 10000000, - "type": "number", - "category": "scan", - "description": "严格成交量过滤,24H Volume低于此值(USD)直接剔除", - }, - "USE_TRAILING_STOP": { - "value": False, - "type": "boolean", - "category": "strategy", - "description": "是否启用移动止损(默认关闭,让利润奔跑)", - }, - "SMART_ENTRY_ENABLED": { - "value": False, - "type": "boolean", - "category": "strategy", - "description": "智能入场开关。关闭后回归纯限价单模式", - }, - } - for k, meta in ADDITIONAL_STRATEGY_DEFAULTS.items(): - if k not in result: - result[k] = meta - - logger.info(f"返回全局配置项数量: {len(result)}") - return result - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) - - @router.put("/global/{key}") async def update_global_config( key: str,