"""
配置管理API
"""
from fastapi import APIRouter, HTTPException, Header, Depends, Query
from api.models.config import ConfigItem, ConfigUpdate
import sys
from pathlib import Path
import logging
from typing import Dict, Any, Optional

# 添加项目根目录到路径
project_root = Path(__file__).parent.parent.parent.parent
sys.path.insert(0, str(project_root))
sys.path.insert(0, str(project_root / 'backend'))
sys.path.insert(0, str(project_root / 'trading_system'))

from database.models import TradingConfig, Account
from api.auth_deps import get_current_user, get_account_id, require_admin, require_account_owner

logger = logging.getLogger(__name__)
router = APIRouter()

# 全局策略账号（管理员统一维护策略核心）。默认 1，可用环境变量覆盖。
def _global_strategy_account_id() -> int:
    try:
        return int((__import__("os").getenv("ATS_GLOBAL_STRATEGY_ACCOUNT_ID") or "1").strip() or "1")
    except Exception:
        return 1

# 产品模式：平台兜底（策略核心由管理员统一控制），普通用户仅能调“风险旋钮”
# - admin：可修改所有配置
# - 非 admin（account owner）：只允许修改少量风险类配置 + 账号私有密钥/测试网
USER_RISK_KNOBS = {
    # 风险暴露（保证金占用比例/最小保证金）
    "MIN_MARGIN_USDT",
    "MIN_POSITION_PERCENT",
    "MAX_POSITION_PERCENT",
    "MAX_TOTAL_POSITION_PERCENT",
    # 行为控制（傻瓜化）
    "AUTO_TRADE_ENABLED",   # 总开关：关闭则只生成推荐不自动下单
    "MAX_OPEN_POSITIONS",   # 同时持仓数量上限
    "MAX_DAILY_ENTRIES",    # 每日最多开仓次数
    "SUNDAY_MAX_OPENS",     # 周日开仓上限（0=不限制）
    "SUNDAY_MIN_SIGNAL_STRENGTH",  # 周日最低信号强度（0=不提高）
    "NIGHT_HOURS_NO_OPEN_ENABLED",
    "NIGHT_HOURS_START",
    "NIGHT_HOURS_END",
    "NIGHT_HOURS_ONLY_SUNDAY",
    "NO_OPEN_HOURS_BJ",
    # 策略筛选（允许用户调整）
    "TOP_N_SYMBOLS",
    "MIN_SIGNAL_STRENGTH",
    "MIN_VOLUME_24H",
    "MIN_VOLATILITY",
    "SCAN_EXTRA_SYMBOLS_FOR_SUPPLEMENT",
    "EXCLUDE_MAJOR_COINS",
    # 用户自定义扫描参数
    "MAX_SCAN_SYMBOLS",
    "SCAN_INTERVAL",
}

# 普通用户/关联账号可见的配置项默认值（含 category），确保「市场扫描」「仓位控制」「风险控制」分组都能展示
USER_VISIBLE_DEFAULTS = {
    "MIN_MARGIN_USDT": {"value": 5.0, "type": "number", "category": "risk", "description": "最小单笔保证金（USDT），低于此值不下单"},
    "MIN_POSITION_PERCENT": {"value": 0.0, "type": "number", "category": "position", "description": "最小单笔保证金占比（%），0 表示不限制"},
    "MAX_POSITION_PERCENT": {"value": 2.0, "type": "number", "category": "position", "description": "单笔最大保证金占比（%）"},
    "MAX_TOTAL_POSITION_PERCENT": {"value": 20.0, "type": "number", "category": "position", "description": "总仓位最大保证金占比（%）"},
    "AUTO_TRADE_ENABLED": {"value": True, "type": "boolean", "category": "risk", "description": "自动交易总开关：关闭后仅生成推荐，不会自动下单"},
    "MAX_OPEN_POSITIONS": {"value": 3, "type": "number", "category": "position", "description": "同时持仓数量上限"},
    "MAX_DAILY_ENTRIES": {"value": 8, "type": "number", "category": "risk", "description": "每日最多开仓次数"},
    "SUNDAY_MAX_OPENS": {"value": 3, "type": "number", "category": "risk", "description": "周日最多开仓次数，0=不限制（与每日一致）"},
    "SUNDAY_MIN_SIGNAL_STRENGTH": {"value": 8, "type": "number", "category": "risk", "description": "周日最低信号强度(0-10)，0=不提高门槛"},
    "NIGHT_HOURS_NO_OPEN_ENABLED": {"value": True, "type": "boolean", "category": "risk", "description": "晚间/凌晨(21:00～06:00北京)禁止开仓"},
    "NIGHT_HOURS_START": {"value": 21, "type": "number", "category": "risk", "description": "禁止开仓开始小时(含)"},
    "NIGHT_HOURS_END": {"value": 6, "type": "number", "category": "risk", "description": "禁止开仓结束小时(不含)"},
    "NIGHT_HOURS_ONLY_SUNDAY": {"value": True, "type": "boolean", "category": "risk", "description": "True=仅周六21:00～周日06:00；False=每天"},
    "NO_OPEN_HOURS_BJ": {"value": "", "type": "string", "category": "risk", "description": "禁止开仓小时(北京)，逗号分隔如17,19,22；空则不限制"},
    "TOP_N_SYMBOLS": {"value": 8, "type": "number", "category": "scan", "description": "每次扫描后优先处理的交易对数量"},
    "MIN_SIGNAL_STRENGTH": {"value": 8, "type": "number", "category": "scan", "description": "最小信号强度（0-10）"},
    "MIN_VOLUME_24H": {"value": 30_000_000, "type": "number", "category": "scan", "description": "24h 成交量下限（USD）"},
    "MIN_VOLATILITY": {"value": 3.0, "type": "number", "category": "scan", "description": "最小波动率过滤"},
    "SCAN_EXTRA_SYMBOLS_FOR_SUPPLEMENT": {"value": 8, "type": "number", "category": "scan", "description": "智能补单多返回的候选数量"},
    "EXCLUDE_MAJOR_COINS": {"value": True, "type": "boolean", "category": "scan", "description": "是否排除主流币（BTC/ETH 等）"},
    "MAX_SCAN_SYMBOLS": {"value": 1500, "type": "number", "category": "scan", "description": "最大扫描交易对数量"},
    "SCAN_INTERVAL": {"value": 1800, "type": "number", "category": "scan", "description": "市场扫描间隔（秒）"},
    "MANUAL_REDUCED_SYMBOLS": {"value": "", "type": "string", "category": "strategy", "description": "手动减仓交易对列表，逗号/空格/换行分隔。命中后缩小仓位并提高自动交易门槛。"},
    "MANUAL_BLOCKED_SYMBOLS": {"value": "", "type": "string", "category": "strategy", "description": "手动停做交易对列表，逗号/空格/换行分隔。命中后禁止自动开仓。"},
    "MANUAL_REDUCED_SYMBOL_POSITION_FACTOR": {"value": 0.5, "type": "number", "category": "strategy", "description": "手动减仓交易对的仓位系数。0.5 表示只开正常仓位的一半。"},
    "MANUAL_REDUCED_SYMBOL_SIGNAL_BOOST": {"value": 1, "type": "number", "category": "strategy", "description": "手动减仓交易对额外提高的最小信号门槛。1 表示在当前基础上 +1。"},
}

RISK_KNOBS_DEFAULTS = {
    "AUTO_TRADE_ENABLED": {
        "value": True,
        "type": "boolean",
        "category": "risk",
        "description": "自动交易总开关：关闭后仅生成推荐，不会自动下单（适合先观察/体验）。",
    },
    "MAX_OPEN_POSITIONS": {
        "value": 3,
        "type": "number",
        "category": "risk",
        "description": "同时持仓数量上限（防止仓位过多/难管理）。建议 1-5。",
    },
    "MAX_DAILY_ENTRIES": {
        "value": 8,
        "type": "number",
        "category": "risk",
        "description": "每日最多开仓次数（防止高频下单/过度交易）。建议 3-15。",
    },
    "SUNDAY_MAX_OPENS": {
        "value": 3,
        "type": "number",
        "category": "risk",
        "description": "周日最多开仓次数。0=不限制（与每日一致）；设为 2-3 可降低周日亏损又不完全停单。",
    },
    "SUNDAY_MIN_SIGNAL_STRENGTH": {
        "value": 8,
        "type": "number",
        "category": "risk",
        "description": "周日最低信号强度(0-10)。仅当信号强度>=此值才开仓，0=不提高（与平日一致）。建议 8～9。",
    },
    "NIGHT_HOURS_NO_OPEN_ENABLED": {
        "value": True,
        "type": "boolean",
        "category": "risk",
        "description": "晚间/凌晨禁止开新仓。开启后 21:00～06:00（北京时间）不开新仓，亏损多从昨晚21点后开始。",
    },
    "NIGHT_HOURS_START": {"value": 21, "type": "number", "category": "risk", "description": "禁止开仓开始小时（北京），含"},
    "NIGHT_HOURS_END": {"value": 6, "type": "number", "category": "risk", "description": "禁止开仓结束小时（北京），不含"},
    "NIGHT_HOURS_ONLY_SUNDAY": {"value": True, "type": "boolean", "category": "risk", "description": "仅周日夜间。True=周六21:00～周日06:00；False=每天。"},
    "NO_OPEN_HOURS_BJ": {"value": "", "type": "string", "category": "risk", "description": "禁止开仓小时（北京），逗号分隔如 17,19,22 表示 17/19/22 时不开新仓；空则不限制。按历史亏损时段配置。"},
}

# API key/secret 脱敏
def _mask(s: str) -> str:
    s = "" if s is None else str(s)
    if not s:
        return ""
    if len(s) <= 8:
        return "****"
    return f"{s[:4]}...{s[-4:]}"
# 智能入场（方案C）配置：为了“配置页可见”，即使数据库尚未创建，也在 GET /api/config 返回默认项
SMART_ENTRY_CONFIG_DEFAULTS = {
    "SMART_ENTRY_ENABLED": {
        "value": False,
        "type": "boolean",
        "category": "strategy",
        "description": "智能入场开关。关闭后回归“纯限价单模式”（不追价/不市价兜底/未成交则撤单跳过），更适合低频波段。",
    },
    "SMART_ENTRY_STRONG_SIGNAL": {
        "value": 8,
        "type": "number",
        "category": "strategy",
        "description": "强信号阈值（0-10）。≥该值且4H趋势明确时，允许更积极的入场（可控市价兜底）。",
    },
    "ENTRY_SYMBOL_COOLDOWN_SEC": {
        "value": 120,
        "type": "number",
        "category": "strategy",
        "description": "同一交易对入场冷却时间（秒）。避免短时间内反复挂单/重入导致高频噪音单。",
    },
    "ENTRY_TIMEOUT_SEC": {
        "value": 180,
        "type": "number",
        "category": "strategy",
        "description": "智能入场总预算时间（秒）。超过预算仍未成交将根据规则取消/兜底。",
    },
    "ENTRY_STEP_WAIT_SEC": {
        "value": 15,
        "type": "number",
        "category": "strategy",
        "description": "每次追价/调整前等待成交时间（秒）。",
    },
    "ENTRY_CHASE_MAX_STEPS": {
        "value": 4,
        "type": "number",
        "category": "strategy",
        "description": "最大追价步数（逐步减小限价回调幅度，靠近当前价）。",
    },
    "ENTRY_MARKET_FALLBACK_AFTER_SEC": {
        "value": 45,
        "type": "number",
        "category": "strategy",
        "description": "趋势强时：超过该时间仍未成交，会在偏离不超过上限时转市价兜底（减少错过）。",
    },
    "ENTRY_CONFIRM_TIMEOUT_SEC": {
        "value": 30,
        "type": "number",
        "category": "strategy",
        "description": "下单后确认成交等待时间（秒）。",
    },
    "ALGO_ORDER_TIMEOUT_SEC": {
        "value": 45,
        "type": "number",
        "category": "strategy",
        "description": "Algo 条件单（止损/止盈）单次请求超时（秒）。币安接口高负载时易超时，可调大至 60。",
    },
    "ENTRY_MAX_DRIFT_PCT_TRENDING": {
        "value": 0.006,  # 0.6%
        "type": "number",
        "category": "strategy",
        "description": "趋势强时最大追价偏离（%）。例如 0.6 表示 0.6%。越小越保守。",
    },
    "ENTRY_MAX_DRIFT_PCT_RANGING": {
        "value": 0.003,  # 0.3%
        "type": "number",
        "category": "strategy",
        "description": "震荡/弱趋势最大追价偏离（%）。例如 0.3 表示 0.3%。越小越保守。",
    },
}

# 自动交易过滤项：用于“提升胜率/控频”，避免震荡行情来回扫损导致胜率极低
AUTO_TRADE_FILTER_DEFAULTS = {
    "AUTO_TRADE_ONLY_TRENDING": {
        "value": True,
        "type": "boolean",
        "category": "strategy",
        "description": "自动交易仅在市场状态=trending时执行（ranging/unknown只生成推荐，不自动下单）。用于显著降低震荡扫损与交易次数。",
    },
    "AUTO_TRADE_ALLOW_RANGING": {
        "value": False,
        "type": "boolean",
        "category": "strategy",
        "description": "当不强制 only_trending 时，是否允许在震荡市自动交易。默认关闭，避免在横盘里来回扫损。",
    },
    "AUTO_TRADE_ALLOW_UNKNOWN": {
        "value": False,
        "type": "boolean",
        "category": "strategy",
        "description": "当市场状态=unknown（K线不足或未判定）时，是否允许自动交易。默认关闭，更稳健。",
    },
    "AUTO_TRADE_ALLOW_4H_NEUTRAL": {
        "value": False,
        "type": "boolean",
        "category": "strategy",
        "description": "是否允许4H趋势=neutral时自动交易。默认关闭（中性趋势最容易被扫损）；若你希望更积极可开启。",
    },
    "RANGING_MARKET_SIGNAL_BOOST": {
        "value": 2,
        "type": "number",
        "category": "strategy",
        "description": "震荡市额外提高的最小信号门槛。比如 2 表示在基础 MIN_SIGNAL_STRENGTH 上再 +2。",
    },
}

# 风险/策略预设（用于一键切换“稳健 / 快速验证”等模式）
PROFILE_CONFIG_DEFAULTS = {
    "TRADING_PROFILE": {
        "value": "conservative",
        "type": "string",
        "category": "strategy",
        "description": "交易预设：conservative(稳健，低频+高门槛) / fast(快速验证，高频+宽松过滤)。仅作为默认值，具体参数仍可单独调整。",
    },
}


# 核心策略参数（仅管理员可见/在全局策略账号中修改）
CORE_STRATEGY_CONFIG_DEFAULTS = {
    "ATR_STOP_LOSS_MULTIPLIER": {
        "value": 2.0,  # 2026-01-29优化：从1.5提高到2.0，减少被正常波动扫出
        "type": "number",
        "category": "strategy",
        "description": "ATR止损倍数。建议 2.0-3.0。放宽止损可以给波动留出空间，提高胜率。2026-01-29优化：默认值从1.5提高到2.0。",
    },
    "ATR_TAKE_PROFIT_MULTIPLIER": {
        "value": 1.5,
        "type": "number",
        "category": "strategy",
        "description": "ATR止盈倍数。建议 1.0-2.0。对应盈亏比 1:1 到 2:1，更容易触及目标。",
    },
    "RISK_REWARD_RATIO": {
        "value": 1.5,
        "type": "number",
        "category": "strategy",
        "description": "目标盈亏比（止损距离的倍数）。配合ATR止盈使用。",
    },
    "MIN_STOP_LOSS_PRICE_PCT": {
        "value": 0.03,
        "type": "number",
        "category": "strategy",
        "description": "最小止损距离（%）。例如 0.03 表示 3%。适当放宽止损，减少在正常波动中被扫损。",
    },
    "MIN_TAKE_PROFIT_PRICE_PCT": {
        "value": 0.02,
        "type": "number",
        "category": "strategy",
        "description": "最小止盈距离（%）。例如 0.02 表示 2%。防止止盈过近。",
    },
    "POSITION_DETAILED_LOG_ENABLED": {
        "value": False,
        "type": "boolean",
        "category": "strategy",
        "description": "是否启用持仓详细监控日志（记录每次检查的当前价/止损/止盈/ROE 等）。仅用于排查问题时临时打开，平时建议关闭以减少日志噪音。",
    },
}


@router.get("")
@router.get("/")
async def get_all_configs(
    user: Dict[str, Any] = Depends(get_current_user),
    account_id: int = Depends(get_account_id),
):
    """获取所有配置"""
    try:
        configs = TradingConfig.get_all(account_id=account_id)
        result = {}
        for config in configs:
            result[config['config_key']] = {
                'value': TradingConfig._convert_value(
                    config['config_value'], 
                    config['config_type']
                ),
                'type': config['config_type'],
                'category': config['category'],
                'description': config['description']
            }

        # 合并账号级 API Key/Secret（从 accounts 表读，避免把密钥当普通配置存）
        try:
            api_key, api_secret, use_testnet, status = Account.get_credentials(account_id)
        except Exception:
            api_key, api_secret, use_testnet, status = "", "", False, "active"
        # 仅用于配置页展示/更新：不返回 secret 明文；api_key 仅脱敏展示
        result["BINANCE_API_KEY"] = {
            "value": _mask(api_key or ""),
            "type": "string",
            "category": "api",
            "description": "币安API密钥（账号私有，仅脱敏展示；账号 owner/admin 可修改）",
        }
        result["BINANCE_API_SECRET"] = {
            "value": "",
            "type": "string",
            "category": "api",
            "description": "币安API密钥Secret（账号私有，不回传明文；账号 owner/admin 可修改）",
        }
        result["USE_TESTNET"] = {
            "value": bool(use_testnet),
            "type": "boolean",
            "category": "api",
            "description": "是否使用测试网（账号私有）",
        }

        # 合并“默认但未入库”的配置项（用于新功能上线时直接在配置页可见）
        for k, meta in SMART_ENTRY_CONFIG_DEFAULTS.items():
            if k not in result:
                result[k] = meta

        for k, meta in AUTO_TRADE_FILTER_DEFAULTS.items():
            if k not in result:
                result[k] = meta

        # 交易预设（profile）：用于前端一键切换“稳健 / 快速验证”
        for k, meta in PROFILE_CONFIG_DEFAULTS.items():
            if k not in result:
                result[k] = meta

        for k, meta in RISK_KNOBS_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,  # 2%
                "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

        for k, meta in CORE_STRATEGY_CONFIG_DEFAULTS.items():
            if k not in result:
                result[k] = meta

        # 普通用户：只展示风险旋钮 + 账号密钥（尽量傻瓜化，避免改坏策略）
        # 管理员：若当前不是“全局策略账号”，同样只展示风险旋钮，避免误以为这里改策略能生效
        is_admin = (user.get("role") or "user") == "admin"
        gid = _global_strategy_account_id()
        if (not is_admin) or (is_admin and int(account_id) != int(gid)):
            # 先补齐用户可见项的默认值（含 category），避免关联账号因 DB 无记录而缺少「市场扫描」「仓位控制」等分组
            for k in USER_RISK_KNOBS:
                if k not in result and k in USER_VISIBLE_DEFAULTS:
                    result[k] = USER_VISIBLE_DEFAULTS[k]
            allowed = set(USER_RISK_KNOBS) | {"BINANCE_API_KEY", "BINANCE_API_SECRET", "USE_TESTNET"}
            result = {k: v for k, v in result.items() if k in allowed}
        return result
    except Exception as e:
        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

        # 全局交易预设（profile），用于控制一组参数的默认值
        for k, meta in PROFILE_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

        # 周日/晚间控制（周六 21 点～周日 06 点等），确保全局配置页可见可改
        for k, meta in RISK_KNOBS_DEFAULTS.items():
            if k not in result:
                result[k] = meta

        # 添加更多核心策略配置的默认值（确保前端能显示所有重要配置）
        ADDITIONAL_STRATEGY_DEFAULTS = {
            "BETA_FILTER_ENABLED": {
                "value": True,
                "type": "boolean",
                "category": "strategy",
                "description": "大盘共振过滤：BTC/ETH 下跌时屏蔽多单",
            },
            "BETA_FILTER_THRESHOLD": {
                "value": -0.005,
                "type": "number",
                "category": "strategy",
                "description": "大盘共振阈值（比例，如 -0.005 表示 -0.5%）",
            },
            "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,  # 2026-01-29优化：从7提高到8，减少低质量信号
                "type": "number",
                "category": "strategy",
                "description": "最小信号强度（0-10），只交易高质量信号。2026-01-29优化：默认值从7提高到8。",
            },
            "SCAN_INTERVAL": {
                "value": 1800,
                "type": "number",
                "category": "scan",
                "description": "市场扫描间隔（秒），默认30分钟",
            },
            "TOP_N_SYMBOLS": {
                "value": 8,
                "type": "number",
                "category": "scan",
                "description": "每次扫描后优先处理的交易对数量",
            },
            "MAX_SCAN_SYMBOLS": {
                "value": 1500,
                "type": "number",
                "category": "scan",
                "description": "最大扫描交易对数量，控制市场扫描的交易对总数，数值越大覆盖范围越广但扫描时间越长",
            },
            "SCAN_EXTRA_SYMBOLS_FOR_SUPPLEMENT": {
                "value": 8,
                "type": "number",
                "category": "scan",
                "description": "智能补单：多返回的候选数量。当前 TOP_N 中部分因冷却等被跳过时，仍会尝试这批额外候选，避免无单可下。",
            },
            "TAKE_PROFIT_1_PERCENT": {
                "value": 0.15,
                "type": "number",
                "category": "strategy",
                "description": "分步止盈第一目标（保证金百分比，如 0.15=15%）。第一目标触发后了结50%仓位，剩余追求第二目标。",
            },
            "MIN_VOLUME_24H_STRICT": {
                "value": 10000000,
                "type": "number",
                "category": "scan",
                "description": "严格成交量过滤，24H Volume低于此值（USD）直接剔除",
            },
            "EXCLUDE_MAJOR_COINS": {
                "value": True,
                "type": "boolean",
                "category": "scan",
                "description": "是否排除主流币（BTC、ETH、BNB等），专注于山寨币。山寨币策略建议开启。",
            },
            "PROFIT_PROTECTION_ENABLED": {
                "value": True,
                "type": "boolean",
                "category": "strategy",
                "description": "盈利保护总开关：True=启用保本+移动止损，False=全部关闭（不做保本、不做移动止损）",
            },
            "LOCK_PROFIT_AT_BREAKEVEN_AFTER_PCT": {
                "value": 0.03,
                "type": "number",
                "category": "strategy",
                "description": "盈利达保证金的该比例时移至保本（如 0.03=3%，0=关闭保本步骤）",
            },
            "LOCK_PROFIT_STAGE1_TRIGGER_PCT": {
                "value": 0.08,
                "type": "number",
                "category": "strategy",
                "description": "分层锁盈第一层触发阈值（相对保证金，如 0.08=8%）。达到后将止损从保本抬到小幅锁利。",
            },
            "LOCK_PROFIT_STAGE1_PCT": {
                "value": 0.02,
                "type": "number",
                "category": "strategy",
                "description": "分层锁盈第一层锁利比例（相对保证金，如 0.02=2%）。",
            },
            "LOCK_PROFIT_STAGE2_TRIGGER_PCT": {
                "value": 0.15,
                "type": "number",
                "category": "strategy",
                "description": "分层锁盈第二层触发阈值（相对保证金，如 0.15=15%）。达到后进一步抬高止损。",
            },
            "LOCK_PROFIT_STAGE2_PCT": {
                "value": 0.05,
                "type": "number",
                "category": "strategy",
                "description": "分层锁盈第二层锁利比例（相对保证金，如 0.05=5%）。",
            },
            "USE_TRAILING_STOP": {
                "value": False,
                "type": "boolean",
                "category": "strategy",
                "description": "是否启用移动止损（默认关闭，让利润奔跑）",
            },
            "BLOCK_SHORT_WHEN_BULL_MARKET": {
                "value": True,
                "type": "boolean",
                "category": "strategy",
                "description": "市场方案=牛市时禁止开空（与 MARKET_SCHEME 一致时生效，默认 True）",
            },
            "BLOCK_LONG_WHEN_BEAR_MARKET": {
                "value": True,
                "type": "boolean",
                "category": "strategy",
                "description": "市场方案=熊市时禁止开多（与 MARKET_SCHEME 一致时生效，默认 True）",
            },
            "ONLY_AUTO_TRADE_CREATES_RECORDS": {
                "value": True,
                "type": "boolean",
                "category": "position",
                "description": "为 True 时仅自动开仓写入 DB，不补建「仅币安有仓」；改为 False 并保持 SYNC_RECOVER_MISSING_POSITIONS=True 可使币安持仓与 DB 一致（补建缺失记录并挂 SL/TP）",
            },
            "SYNC_RECOVER_MISSING_POSITIONS": {
                "value": True,
                "type": "boolean",
                "category": "position",
                "description": "同步时补建「币安有仓、DB 无记录」的交易记录（须 ONLY_AUTO_TRADE_CREATES_RECORDS=False 才生效）",
            },
            "SYNC_RECOVER_ONLY_WHEN_HAS_SLTP": {
                "value": True,
                "type": "boolean",
                "category": "position",
                "description": "仅当该持仓存在止损/止盈单时才补建（未配置 SYSTEM_ORDER_ID_PREFIX 时生效）",
            },
            "SYSTEM_ORDER_ID_PREFIX": {
                "value": "SYS",
                "type": "string",
                "category": "position",
                "description": "系统单标识：下单时写入 newClientOrderId 前缀，同步时仅对「开仓订单 clientOrderId 以此前缀开头」的持仓补建；设空则用「是否有止损止盈单」判断",
            },
            "SMART_ENTRY_ENABLED": {
                "value": False,
                "type": "boolean",
                "category": "strategy",
                "description": "智能入场开关。关闭后回归纯限价单模式",
            },
            "SYMBOL_LOSS_COOLDOWN_ENABLED": {
                "value": True,
                "type": "boolean",
                "category": "strategy",
                "description": "是否启用同一交易对连续亏损后的冷却（避免连续亏损后继续交易）。2026-01-29新增。",
            },
            "SYMBOL_MAX_CONSECUTIVE_LOSSES": {
                "value": 2,
                "type": "number",
                "category": "strategy",
                "description": "最大允许连续亏损次数（超过则禁止交易该交易对一段时间）。2026-01-29新增。",
            },
            "SYMBOL_LOSS_COOLDOWN_SEC": {
                "value": 3600,
                "type": "number",
                "category": "strategy",
                "description": "连续亏损后的冷却时间（秒），默认1小时。2026-01-29新增。",
            },
            "MAX_RSI_FOR_LONG": {
                "value": 70,
                "type": "number",
                "category": "strategy",
                "description": "做多时 RSI 超过此值则不开多（避免超买区追多）。2026-01-31新增。",
            },
            "MAX_CHANGE_PERCENT_FOR_LONG": {
                "value": 25,
                "type": "number",
                "category": "strategy",
                "description": "做多时 24h 涨跌幅超过此值则不开多（避免追大涨）。单位：百分比数值，如 25 表示 25%。2026-01-31新增。",
            },
            "MIN_RSI_FOR_SHORT": {
                "value": 30,
                "type": "number",
                "category": "strategy",
                "description": "做空时 RSI 低于此值则不做空（避免深超卖反弹）。2026-01-31新增。",
            },
            "MAX_CHANGE_PERCENT_FOR_SHORT": {
                "value": 10,
                "type": "number",
                "category": "strategy",
                "description": "做空时 24h 涨跌幅超过此值则不做空（24h 仍大涨时不做空）。单位：百分比数值。2026-01-31新增。",
            },
            # 多账号错峰启动扫描（避免多个账号同时扫同时下单）
            "SCAN_STAGGER_BY_ACCOUNT": {
                "value": False,
                "type": "boolean",
                "category": "scan",
                "description": "多账号错峰扫描总开关：True=不同账号在首次扫描前随机/按账号ID延迟一段时间，减少并发压力；单账号可保持 False。",
            },
            "SCAN_STAGGER_RANDOM": {
                "value": True,
                "type": "boolean",
                "category": "scan",
                "description": "错峰方式：True=在区间 [SCAN_STAGGER_MIN_SEC, SCAN_STAGGER_MAX_SEC] 内随机延迟；False=按账号ID固定间隔延迟 (account_id-1)*SCAN_STAGGER_SEC。",
            },
            "SCAN_STAGGER_MIN_SEC": {
                "value": 10,
                "type": "number",
                "category": "scan",
                "description": "随机错峰模式下的最小延迟秒数（含）。例如 10 表示至少延迟 10 秒。",
            },
            "SCAN_STAGGER_MAX_SEC": {
                "value": 120,
                "type": "number",
                "category": "scan",
                "description": "随机错峰模式下的最大延迟秒数（含）。例如 120 表示至多延迟 120 秒。",
            },
            "SCAN_STAGGER_SEC": {
                "value": 60,
                "type": "number",
                "category": "scan",
                "description": "固定错峰模式下每个账号之间的步长秒数：延迟 = (account_id-1)*SCAN_STAGGER_SEC。",
            },
            # ===== 滞涨早止盈（涨到约 N% 后长时间不创新高就分批减仓+抬止损）=====
            "STAGNATION_EARLY_EXIT_ENABLED": {
                "value": False,
                "type": "boolean",
                "category": "strategy",
                "description": "是否启用「滞涨早止盈」：曾达到一定浮盈后，长时间未创新高则主动分批减仓并抬升止损锁利。",
            },
            "STAGNATION_MIN_RUNUP_PCT": {
                "value": 10.0,
                "type": "number",
                "category": "strategy",
                "description": "触发滞涨判断的最低历史浮盈百分比（基于保证金），如 10 表示曾浮盈≥10% 才会考虑滞涨早止盈。",
            },
            "STAGNATION_NO_NEW_HIGH_HOURS": {
                "value": 3.0,
                "type": "number",
                "category": "strategy",
                "description": "在达到最高浮盈后，若连续这么多个小时未再创新高，则视为动能衰减并触发滞涨早止盈。",
            },
            "STAGNATION_PARTIAL_CLOSE_PCT": {
                "value": 0.5,
                "type": "number",
                "category": "strategy",
                "description": "滞涨触发时首先平掉的仓位比例，如 0.5 表示先平掉 50% 锁定一半利润。",
            },
            "STAGNATION_LOCK_PCT": {
                "value": 5.0,
                "type": "number",
                "category": "strategy",
                "description": "滞涨触发后对剩余仓位抬升止损时，至少要锁住的利润百分比（与“最高浮盈的一半”取较大者）。",
            },
            # ===== 回撤/区间入场（近 N 根 K 线前低前高，避免买在区间顶部）=====
            "ENTRY_PULLBACK_FILTER_ENABLED": {
                "value": False,
                "type": "boolean",
                "category": "strategy",
                "description": "是否启用回撤区间入场过滤：多单不贴在近 N 根 K 线区间上沿、空单不贴下沿，减少「追高/追空后一回撤就扫损」。与 USE_TREND_ENTRY_FILTER（相对信号价）互补。",
            },
            "ENTRY_PULLBACK_INTERVAL": {
                "value": "",
                "type": "string",
                "category": "strategy",
                "description": "回撤过滤使用的 K 线周期。留空则使用 ENTRY_INTERVAL（通常 1h）；可填 15m、4h 等。",
            },
            "ENTRY_PULLBACK_LOOKBACK_BARS": {
                "value": 24,
                "type": "number",
                "category": "strategy",
                "description": "统计区间高低点所用的 K 线根数（需 ≥ MIN_BARS）。",
            },
            "ENTRY_PULLBACK_MIN_BARS": {
                "value": 5,
                "type": "number",
                "category": "strategy",
                "description": "至少多少根有效 K 线才应用回撤过滤；不足则跳过本过滤。",
            },
            "ENTRY_PULLBACK_MAX_LONG_IN_RANGE": {
                "value": 0.62,
                "type": "number",
                "category": "strategy",
                "description": "做多：现价在区间 [低,高] 的相对位置 pos∈[0,1]，pos 超过此值则不开仓（默认约拒绝最上 38% 区域）。越小越严。",
            },
            "ENTRY_PULLBACK_MIN_SHORT_IN_RANGE": {
                "value": 0.38,
                "type": "number",
                "category": "strategy",
                "description": "做空：相对位置 pos 低于此值则不开仓（默认约拒绝最下 38% 区域）。越大越严。",
            },
            "SHADOW_MODE_AUTO_APPLY": {
                "value": False,
                "type": "boolean",
                "category": "strategy",
                "description": "影子模式半自动：按 config/current_suggestions.json 跳过黑名单/差时段，并对加仓减仓名单调整杠杆。",
            },
            "SHADOW_MODE_MIN_CONFIDENCE": {
                "value": 0.7,
                "type": "number",
                "category": "strategy",
                "description": "影子跟踪置信度下限（读 shadow_mode_tracking.json），低于则不应用建议。",
            },
            "SHADOW_MODE_SUGGESTIONS_PATH": {
                "value": "config/current_suggestions.json",
                "type": "string",
                "category": "strategy",
                "description": "优化建议 JSON 路径（相对项目根）。",
            },
            "SHADOW_MODE_TRACKING_PATH": {
                "value": "config/shadow_mode_tracking.json",
                "type": "string",
                "category": "strategy",
                "description": "影子跟踪准确率 JSON 路径。",
            },
            "SHADOW_MODE_INCREASE_LEVERAGE_MULT": {
                "value": 1.5,
                "type": "number",
                "category": "strategy",
                "description": "increase_position 名单杠杆乘数。",
            },
            "SHADOW_MODE_DECREASE_LEVERAGE_MULT": {
                "value": 0.5,
                "type": "number",
                "category": "strategy",
                "description": "decrease_position 名单杠杆乘数。",
            },
        }
        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),
    account_id: int = Depends(get_account_id),
):
    """
    检查配置可行性，基于当前账户余额和杠杆倍数计算可行的配置建议
    """
    try:
        # 获取账户余额
        try:
            from api.routes.account import get_realtime_account_data
            account_data = await get_realtime_account_data(account_id=account_id)
            available_balance = account_data.get('available_balance', 0)
            total_balance = account_data.get('total_balance', 0)
        except Exception as e:
            logger.warning(f"获取账户余额失败: {e}，使用默认值")
            available_balance = 0
            total_balance = 0
        
        if available_balance <= 0:
            return {
                "feasible": False,
                "error": "无法获取账户余额，请检查API配置",
                "suggestions": []
            }
        
        # 获取当前“有效配置”（平台兜底：策略核心可能来自全局账号）
        try:
            import config_manager as _cfg_mgr  # type: ignore

            mgr = _cfg_mgr.ConfigManager.for_account(int(account_id)) if hasattr(_cfg_mgr, "ConfigManager") else None
            tc = mgr.get_trading_config() if mgr else {}
        except Exception:
            tc = {}

        def _tc(key: str, default):
            try:
                return tc.get(key, default)
            except Exception:
                return default

        min_margin_usdt = float(_tc('MIN_MARGIN_USDT', 5.0))
        min_position_percent = float(_tc('MIN_POSITION_PERCENT', 0.02))
        max_position_percent = float(_tc('MAX_POSITION_PERCENT', 0.08))
        base_leverage = int(_tc('LEVERAGE', 10))
        max_leverage = int(_tc('MAX_LEVERAGE', 15))
        use_dynamic_leverage = bool(_tc('USE_DYNAMIC_LEVERAGE', True))
        
        # 检查所有可能的杠杆倍数（考虑动态杠杆）
        leverage_to_check = [base_leverage]
        if use_dynamic_leverage and max_leverage > base_leverage:
            # 检查基础杠杆、最大杠杆，以及中间的关键值（如15x, 20x, 30x, 40x）
            leverage_to_check.append(max_leverage)
            # 添加常见的杠杆倍数
            for lev in [15, 20, 25, 30, 40, 50]:
                if base_leverage < lev <= max_leverage and lev not in leverage_to_check:
                    leverage_to_check.append(lev)
        
        leverage_to_check = sorted(set(leverage_to_check))
        
        # 检查每个杠杆倍数下的可行性
        leverage_results = []
        all_feasible = True
        worst_case_leverage = None
        worst_case_margin = None
        
        for leverage in leverage_to_check:
            # 重要语义说明（与 trading_system 保持一致）：
            # - MAX/MIN_POSITION_PERCENT 表示“保证金占用比例”（不是名义价值比例）
            # - MIN_MARGIN_USDT 是最小保证金（USDT）
            # 同时考虑币安最小名义价值约束：名义价值>=5USDT => margin >= 5/leverage
            min_notional = 5.0
            required_margin_for_notional = (min_notional / leverage) if leverage and leverage > 0 else min_notional
            required_position_value = max(min_margin_usdt, required_margin_for_notional)  # 实际需要的最小保证金（USDT）
            required_position_percent = required_position_value / available_balance if available_balance > 0 else 0
            
            # 计算使用最小仓位百分比时，实际能得到的保证金（直接就是保证金）
            min_position_value = available_balance * min_position_percent
            actual_min_margin = min_position_value
            
            # 检查是否可行：
            # 1) 最大保证金占比是否能满足 required_position_value
            # 2) 如果 MIN_POSITION_PERCENT > 0，则最小保证金占比也应 >= required_position_value（否则最小仓位配置会“阻碍”满足最小保证金/最小名义价值）
            condition1_ok = required_position_percent <= max_position_percent
            condition2_ok = (min_position_percent <= 0) or (actual_min_margin >= required_position_value)
            is_feasible_at_leverage = condition1_ok and condition2_ok
            
            leverage_results.append({
                'leverage': leverage,
                'required_position_value': required_position_value,  # 实际需要的最小保证金（USDT）
                'required_position_percent': required_position_percent * 100,
                'actual_min_margin': actual_min_margin,
                'feasible': is_feasible_at_leverage,
                'condition1_ok': condition1_ok,
                'condition2_ok': condition2_ok
            })
            
            if not is_feasible_at_leverage:
                all_feasible = False
                # 记录最坏情况（实际保证金最小的）
                if worst_case_margin is None or actual_min_margin < worst_case_margin:
                    worst_case_leverage = leverage
                    worst_case_margin = actual_min_margin
        
        # 使用基础杠杆的结果作为主要判断
        base_result = next((r for r in leverage_results if r['leverage'] == base_leverage), leverage_results[0])
        is_feasible = all_feasible
        
        suggestions = []
        
        if not is_feasible:
            # 不可行，给出建议
            # 找出不可行的杠杆倍数
            infeasible_leverages = [r for r in leverage_results if not r['feasible']]
            
            if infeasible_leverages:
                # 找出最坏情况（实际保证金最小的）
                worst = min(infeasible_leverages, key=lambda x: x['actual_min_margin'])
                worst_leverage = worst['leverage']
                worst_margin = worst['actual_min_margin']
                
                # 方案1：基于最坏情况（最大杠杆）降低最小保证金
                # 建议值应该比计算值小一点，确保可行（比如0.51 -> 0.5）
                if not worst['condition2_ok']:
                    # 计算建议值：比实际支持值小0.01-0.05，确保可行
                    suggested_margin = max(0.01, worst_margin - 0.01)  # 至少保留0.01的余量
                    # 如果差值较大，可以多减一点
                    if worst_margin > 1.0:
                        suggested_margin = worst_margin - 0.05
                    # 保留2位小数，向下取整
                    suggested_margin = round(suggested_margin - 0.005, 2)  # 减0.005确保向下取整
                    if suggested_margin < 0.01:
                        suggested_margin = 0.01
                    
                    suggestions.append({
                        "type": "reduce_min_margin_to_supported",
                        "title": f"降低最小保证金（考虑{worst_leverage}x杠杆）",
                        "description": f"将 MIN_MARGIN_USDT 调整为 {suggested_margin:.2f} USDT（当前: {min_margin_usdt:.2f} USDT）。在{worst_leverage}x杠杆下，实际支持 {worst_margin:.2f} USDT，建议设置为 {suggested_margin:.2f} USDT 以确保可行",
                        "config_key": "MIN_MARGIN_USDT",
                        "suggested_value": suggested_margin,
                        "reason": f"在{worst_leverage}x杠杆下，使用最小仓位 {min_position_percent*100:.1f}% 时，实际保证金只有 {worst_margin:.2f} USDT，无法满足 {min_margin_usdt:.2f} USDT 的要求"
                    })
                    
                    # 方案1b：增加最小仓位百分比（作为替代方案）
                    # 计算需要的最小仓位百分比：min_position_percent >= (min_margin_usdt * leverage) / available_balance
                    required_min_position_percent = (min_margin_usdt * worst_leverage) / available_balance if available_balance > 0 else 0
                    # 建议值应该比计算值大一点，确保可行（比如0.0102 -> 0.011）
                    suggested_min_position_percent = required_min_position_percent + 0.001  # 多0.1%
                    # 如果差值较大，可以多加一点
                    if required_min_position_percent > 0.02:
                        suggested_min_position_percent = required_min_position_percent + 0.002  # 多0.2%
                    # 确保不超过最大仓位百分比
                    if suggested_min_position_percent > max_position_percent:
                        suggested_min_position_percent = max_position_percent
                    # 保留4位小数
                    suggested_min_position_percent = round(suggested_min_position_percent, 4)
                    
                    if suggested_min_position_percent > min_position_percent and suggested_min_position_percent <= max_position_percent:
                        suggestions.append({
                            "type": "increase_min_position_percent",
                            "title": f"增加最小仓位百分比（考虑{worst_leverage}x杠杆）",
                            "description": f"将 MIN_POSITION_PERCENT 调整为 {suggested_min_position_percent*100:.2f}%（当前: {min_position_percent*100:.2f}%）。在{worst_leverage}x杠杆下，需要至少 {required_min_position_percent*100:.2f}% 才能满足最小保证金 {min_margin_usdt:.2f} USDT 的要求，建议设置为 {suggested_min_position_percent*100:.2f}% 以确保可行",
                            "config_key": "MIN_POSITION_PERCENT",
                            "suggested_value": suggested_min_position_percent,
                            "reason": f"在{worst_leverage}x杠杆下，当前最小仓位 {min_position_percent*100:.2f}% 只能提供 {worst_margin:.2f} USDT 保证金，需要至少 {required_min_position_percent*100:.2f}% 才能满足 {min_margin_usdt:.2f} USDT 的要求"
                        })
                
                # 方案2：基于最大杠杆计算需要的最小保证金
                max_leverage_result = next((r for r in leverage_results if r['leverage'] == max_leverage), None)
                if max_leverage_result and not max_leverage_result['feasible']:
                    if max_leverage_result['required_position_percent'] > max_position_percent:
                        suggested_min_margin_max_lev = (available_balance * max_position_percent) / max_leverage
                        # 同样，建议值应该比计算值小一点
                        suggested_margin_max_lev = max(0.01, suggested_min_margin_max_lev - 0.01)
                        if suggested_min_margin_max_lev > 1.0:
                            suggested_margin_max_lev = suggested_min_margin_max_lev - 0.05
                        suggested_margin_max_lev = round(suggested_margin_max_lev - 0.005, 2)
                        if suggested_margin_max_lev < 0.01:
                            suggested_margin_max_lev = 0.01
                        
                        suggestions.append({
                            "type": "reduce_min_margin_for_max_leverage",
                            "title": f"降低最小保证金（支持{max_leverage}x杠杆）",
                            "description": f"将 MIN_MARGIN_USDT 调整为 {suggested_margin_max_lev:.2f} USDT（当前: {min_margin_usdt:.2f} USDT），以支持{max_leverage}x杠杆",
                            "config_key": "MIN_MARGIN_USDT",
                            "suggested_value": suggested_margin_max_lev,
                            "reason": f"在{max_leverage}x杠杆下，需要 {max_leverage_result['required_position_percent']:.1f}% 的仓位价值，但最大允许 {max_position_percent*100:.1f}%"
                        })
                    
                    # 方案2b：如果condition2不满足，也可以建议增加最小仓位百分比
                    if not max_leverage_result['condition2_ok']:
                        required_min_position_percent_max = (min_margin_usdt * max_leverage) / available_balance if available_balance > 0 else 0
                        suggested_min_position_percent_max = required_min_position_percent_max + 0.001
                        if required_min_position_percent_max > 0.02:
                            suggested_min_position_percent_max = required_min_position_percent_max + 0.002
                        if suggested_min_position_percent_max > max_position_percent:
                            suggested_min_position_percent_max = max_position_percent
                        suggested_min_position_percent_max = round(suggested_min_position_percent_max, 4)
                        
                        if suggested_min_position_percent_max > min_position_percent and suggested_min_position_percent_max <= max_position_percent:
                            suggestions.append({
                                "type": "increase_min_position_percent_for_max_leverage",
                                "title": f"增加最小仓位百分比（支持{max_leverage}x杠杆）",
                                "description": f"将 MIN_POSITION_PERCENT 调整为 {suggested_min_position_percent_max*100:.2f}%（当前: {min_position_percent*100:.2f}%），以支持{max_leverage}x杠杆下的最小保证金要求",
                                "config_key": "MIN_POSITION_PERCENT",
                                "suggested_value": suggested_min_position_percent_max,
                                "reason": f"在{max_leverage}x杠杆下，需要至少 {required_min_position_percent_max*100:.2f}% 的最小仓位才能满足 {min_margin_usdt:.2f} USDT 的要求"
                            })
                
                # 方案3：降低最大杠杆
                if use_dynamic_leverage:
                    # 计算能支持的最大杠杆
                    max_supported_leverage = int((available_balance * max_position_percent) / min_margin_usdt)
                    if max_supported_leverage < max_leverage and max_supported_leverage >= base_leverage:
                        suggestions.append({
                            "type": "reduce_max_leverage",
                            "title": "降低最大杠杆倍数",
                            "description": f"将 MAX_LEVERAGE 调整为 {max_supported_leverage}x（当前: {max_leverage}x），以支持当前配置",
                            "config_key": "MAX_LEVERAGE",
                            "suggested_value": max_supported_leverage,
                            "reason": f"当前配置下，最大只能支持 {max_supported_leverage}x 杠杆才能满足最小保证金要求"
                        })
                
                # 方案4：增加账户余额
                if worst['required_position_percent'] > max_position_percent:
                    required_balance = worst['required_position_value'] / max_position_percent
                    suggestions.append({
                        "type": "increase_balance",
                        "title": "增加账户余额",
                        "description": f"将账户余额增加到至少 {required_balance:.2f} USDT（当前: {available_balance:.2f} USDT），以支持{worst_leverage}x杠杆",
                        "config_key": None,
                        "suggested_value": round(required_balance, 2),
                        "reason": f"在{worst_leverage}x杠杆下，当前余额不足以满足最小保证金 {min_margin_usdt:.2f} USDT 的要求"
                    })
            
            # 显示所有杠杆倍数的检查结果
            suggestions.append({
                "type": "leverage_analysis",
                "title": "各杠杆倍数检查结果",
                "description": "详细检查结果见下方",
                "config_key": None,
                "suggested_value": None,
                "reason": None,
                "leverage_results": leverage_results
            })
        else:
            # 可行，显示当前配置信息
            actual_min_position_value = available_balance * min_position_percent
            actual_min_margin = base_result['actual_min_margin']
            
            suggestions.append({
                "type": "info",
                "title": "配置可行",
                "description": f"当前配置可以正常下单。最小保证金占比对应保证金: {actual_min_margin:.2f} USDT（MIN_POSITION_PERCENT={min_position_percent*100:.2f}%），最小保证金要求: {min_margin_usdt:.2f} USDT",
                "config_key": None,
                "suggested_value": None,
                "reason": None
            })
            
            # 如果启用了动态杠杆，显示所有杠杆倍数的检查结果
            if use_dynamic_leverage and len(leverage_to_check) > 1:
                suggestions.append({
                    "type": "leverage_analysis",
                    "title": "各杠杆倍数检查结果（全部可行）",
                    "description": "详细检查结果见下方",
                    "config_key": None,
                    "suggested_value": None,
                    "reason": None,
                    "leverage_results": leverage_results
                })
        
        # 普通用户：只返回可调整的风险旋钮建议，避免前端一键应用时触发 403
        if (user.get("role") or "user") != "admin":
            try:
                suggestions = [s for s in (suggestions or []) if s.get("config_key") in USER_RISK_KNOBS]
            except Exception:
                suggestions = []

        max_open_positions = int(_tc("MAX_OPEN_POSITIONS", 3))
        max_daily_entries = int(_tc("MAX_DAILY_ENTRIES", 8))
        max_single_position_margin_usdt = round(available_balance * max_position_percent, 2)

        return {
            "feasible": is_feasible,
            "account_balance": available_balance,
            "base_leverage": base_leverage,
            "max_leverage": max_leverage,
            "use_dynamic_leverage": use_dynamic_leverage,
            "current_config": {
                "min_margin_usdt": min_margin_usdt,
                "min_position_percent": min_position_percent,
                "max_position_percent": max_position_percent,
                "max_open_positions": max_open_positions,
                "max_daily_entries": max_daily_entries,
                "max_single_position_margin_usdt": max_single_position_margin_usdt,
            },
            "calculated_values": {
                "required_position_value": base_result['required_position_value'],
                "required_position_percent": base_result['required_position_percent'],
                "max_allowed_position_percent": max_position_percent * 100,
                "min_position_value": available_balance * min_position_percent,
                "actual_min_margin": base_result['actual_min_margin'],
                "min_position_percent": min_position_percent * 100
            },
            "leverage_analysis": {
                "leverages_checked": leverage_to_check,
                "all_feasible": all_feasible,
                "results": leverage_results
            },
            "suggestions": suggestions
        }
    except Exception as e:
        logger.error(f"检查配置可行性失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"检查配置可行性失败: {str(e)}")


@router.put("/global/{key}")
async def update_global_config(
    key: str,
    item: ConfigUpdate,
    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
        
        # 验证配置值
        config_type = item.type or "string"
        if config_type == 'number':
            try:
                float(item.value)
            except (ValueError, TypeError):
                raise HTTPException(status_code=400, detail=f"Invalid number value for {key}")
        elif config_type == 'boolean':
             if not isinstance(item.value, bool):
                if isinstance(item.value, str):
                    item.value = item.value.lower() in ('true', '1', 'yes', 'on')
                else:
                    item.value = bool(item.value)

        # 特殊验证：百分比配置应该在0-1之间
        if ('PERCENT' in key or 'PCT' in key) and config_type == 'number':
            if not (0 <= float(item.value) <= 1):
                raise HTTPException(
                    status_code=400, 
                    detail=f"{key} must be between 0 and 1 (0% to 100%)"
                )

        # 更新数据库
        GlobalStrategyConfig.set(
            key, 
            item.value, 
            config_type, 
            item.category or "strategy", 
            item.description,
            updated_by=user.get("username")
        )
        
        # 更新缓存 (ConfigManager)
        try:
            import config_manager
            # 更新 GlobalStrategyConfigManager 的缓存
            if hasattr(config_manager, 'GlobalStrategyConfigManager'):
                mgr = config_manager.GlobalStrategyConfigManager()
                # 写入Redis (会更新 redis 和 _cache)
                mgr._set_to_redis(key, item.value)
                # 手动更新本地缓存，确保当前进程也生效
                mgr._cache[key] = item.value
                logger.info(f"全局配置已更新到Redis缓存: {key} = {item.value}")
        except Exception as e:
            logger.warning(f"更新全局配置缓存失败: {e}")
            
        return {
            "message": "全局配置已更新",
            "key": key,
            "value": item.value
        }
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/global/batch")
async def update_global_configs_batch(
    configs: list[ConfigItem],
    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
        import config_manager
        
        updated_count = 0
        mgr = None
        if hasattr(config_manager, 'GlobalStrategyConfigManager'):
            mgr = config_manager.GlobalStrategyConfigManager()
            
        for item in configs:
            # 简单验证
            config_type = item.type
            val = item.value
            if config_type == 'number':
                try:
                    val = float(val)
                except:
                    continue
            elif config_type == 'boolean':
                if not isinstance(val, bool):
                    val = str(val).lower() in ('true', '1', 'yes', 'on')
            
            # 更新DB
            GlobalStrategyConfig.set(
                item.key, 
                val, 
                config_type, 
                item.category, 
                item.description,
                updated_by=user.get("username")
            )
            
            # 更新缓存
            if mgr:
                mgr._set_to_redis(item.key, val)
                mgr._cache[item.key] = val
                
            updated_count += 1
            
        return {"message": f"成功更新 {updated_count} 个全局配置项"}
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))





@router.get("/meta")
async def get_config_meta(user: Dict[str, Any] = Depends(get_current_user)) -> Dict[str, Any]:
    is_admin = (user.get("role") or "user") == "admin"
    return {
        "is_admin": bool(is_admin),
        "user_risk_knobs": sorted(list(USER_RISK_KNOBS)),
        "note": "平台兜底模式：策略核心由全局配置表统一管理（管理员专用）；普通用户仅可调整风险旋钮。",
    }


@router.get("/{key}")
async def get_config(
    key: str,
    user: Dict[str, Any] = Depends(get_current_user),
    account_id: int = Depends(get_account_id),
):
    """获取单个配置"""
    try:
        # 虚拟字段：从 accounts 表读取
        if key in {"BINANCE_API_KEY", "BINANCE_API_SECRET", "USE_TESTNET"}:
            api_key, api_secret, use_testnet, status = Account.get_credentials(account_id)
            if key == "BINANCE_API_KEY":
                return {"key": key, "value": _mask(api_key or ""), "type": "string", "category": "api", "description": "币安API密钥（仅脱敏展示）"}
            if key == "BINANCE_API_SECRET":
                return {"key": key, "value": "", "type": "string", "category": "api", "description": "币安API密钥Secret（不回传明文）"}
            return {"key": key, "value": bool(use_testnet), "type": "boolean", "category": "api", "description": "是否使用测试网（账号私有）"}

        config = TradingConfig.get(key, account_id=account_id)
        if not config:
            raise HTTPException(status_code=404, detail="Config not found")
        
        return {
            'key': config['config_key'],
            'value': TradingConfig._convert_value(
                config['config_value'], 
                config['config_type']
            ),
            'type': config['config_type'],
            'category': config['category'],
            'description': config['description']
        }
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@router.put("/{key}")
async def update_config(
    key: str,
    item: ConfigUpdate,
    user: Dict[str, Any] = Depends(get_current_user),
    account_id: int = Depends(get_account_id),
):
    """更新配置"""
    try:
        # 非管理员：必须是该账号 owner 才允许修改配置
        if (user.get("role") or "user") != "admin":
            require_account_owner(account_id, user)

        # 管理员：若不是全局策略账号，则禁止修改策略核心（避免误操作）
        if (user.get("role") or "user") == "admin":
            gid = _global_strategy_account_id()
            if int(account_id) != int(gid):
                if key not in (USER_RISK_KNOBS | {"BINANCE_API_KEY", "BINANCE_API_SECRET", "USE_TESTNET"}):
                    raise HTTPException(status_code=403, detail=f"该配置由全局策略账号 #{gid} 统一管理，请切换到该账号修改")

        # 产品模式：普通用户只能改“风险旋钮”与账号私有密钥/测试网
        if (user.get("role") or "user") != "admin":
            if key not in (USER_RISK_KNOBS | {"BINANCE_API_KEY", "BINANCE_API_SECRET", "USE_TESTNET"}):
                raise HTTPException(status_code=403, detail="该配置由平台统一管理（仅管理员可修改）")

        # API Key/Secret/Testnet：写入 accounts 表（账号私有）
        if key in {"BINANCE_API_KEY", "BINANCE_API_SECRET", "USE_TESTNET"}:
            if (user.get("role") or "user") != "admin":
                require_account_owner(account_id, user)
            try:
                if key == "BINANCE_API_KEY":
                    Account.update_credentials(account_id, api_key=str(item.value or ""))
                elif key == "BINANCE_API_SECRET":
                    Account.update_credentials(account_id, api_secret=str(item.value or ""))
                else:
                    Account.update_credentials(account_id, use_testnet=bool(item.value))
            except Exception as e:
                raise HTTPException(status_code=500, detail=f"更新账号API配置失败: {e}")
            return {
                "message": "配置已更新",
                "key": key,
                "value": item.value,
                "note": "账号API配置已更新（建议重启对应账号的交易进程以立即生效）",
            }

        # 获取现有配置以确定类型和分类
        existing = TradingConfig.get(key, account_id=account_id)
        if existing:
            config_type = item.type or existing['config_type']
            category = item.category or existing['category']
            description = item.description or existing['description']
        else:
            # 允许创建新配置（用于新功能首次上线，DB 里还没有 key 的情况）
            meta = SMART_ENTRY_CONFIG_DEFAULTS.get(key) or AUTO_TRADE_FILTER_DEFAULTS.get(key) or RISK_KNOBS_DEFAULTS.get(key)
            config_type = item.type or (meta.get("type") if meta else "string")
            category = item.category or (meta.get("category") if meta else "strategy")
            description = item.description or (meta.get("description") if meta else f"{key}配置")
        
        # 验证配置值
        if config_type == 'number':
            try:
                float(item.value)
            except (ValueError, TypeError):
                raise HTTPException(status_code=400, detail=f"Invalid number value for {key}")
        elif config_type == 'boolean':
            if not isinstance(item.value, bool):
                # 尝试转换
                if isinstance(item.value, str):
                    item.value = item.value.lower() in ('true', '1', 'yes', 'on')
                else:
                    item.value = bool(item.value)
        
        # 特殊验证：百分比配置应该在0-1之间
        # 兼容：PERCENT / PCT
        if ('PERCENT' in key or 'PCT' in key) and config_type == 'number':
            if not (0 <= float(item.value) <= 1):
                raise HTTPException(
                    status_code=400, 
                    detail=f"{key} must be between 0 and 1 (0% to 100%)"
                )
        
        # 更新配置（会同时更新数据库和Redis缓存）
        TradingConfig.set(key, item.value, config_type, category, description, account_id=account_id)
        
        # 更新config_manager的缓存（包括Redis）
        try:
            import config_manager
            if hasattr(config_manager, 'ConfigManager') and hasattr(config_manager.ConfigManager, "for_account"):
                mgr = config_manager.ConfigManager.for_account(account_id)
                mgr.set(key, item.value, config_type, category, description)
                logger.info(f"配置已更新到Redis缓存(account_id={account_id}): {key} = {item.value}")
            elif hasattr(config_manager, 'config_manager') and config_manager.config_manager:
                # 调用set方法会同时更新数据库、Redis和本地缓存
                config_manager.config_manager.set(key, item.value, config_type, category, description)
                logger.info(f"配置已更新到Redis缓存: {key} = {item.value}")
        except Exception as e:
            logger.warning(f"更新配置缓存失败: {e}")
        
        return {
            "message": "配置已更新",
            "key": key,
            "value": item.value,
            "note": "配置已同步到Redis，交易系统将立即使用新配置"
        }
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/batch")
async def update_configs_batch(
    configs: list[ConfigItem],
    user: Dict[str, Any] = Depends(get_current_user),
    account_id: int = Depends(get_account_id),
):
    """批量更新配置"""
    try:
        # 非管理员：必须是该账号 owner 才允许修改配置
        if (user.get("role") or "user") != "admin":
            require_account_owner(account_id, user)

        # 管理员：若不是全局策略账号，则批量只允许风险旋钮/密钥
        if (user.get("role") or "user") == "admin":
            gid = _global_strategy_account_id()
            if int(account_id) != int(gid):
                # 直接过滤掉不允许的项（给出 errors，避免“部分成功但实际无效”的错觉）
                pass
        updated_count = 0
        errors = []
        
        for item in configs:
            try:
                if (user.get("role") or "user") == "admin":
                    gid = _global_strategy_account_id()
                    if int(account_id) != int(gid):
                        if item.key not in (USER_RISK_KNOBS | {"BINANCE_API_KEY", "BINANCE_API_SECRET", "USE_TESTNET"}):
                            errors.append(f"{item.key}: 该配置由全局策略账号 #{gid} 统一管理，请切换账号修改")
                            continue

                # 产品模式：普通用户只能改“风险旋钮”与账号私有密钥/测试网
                if (user.get("role") or "user") != "admin":
                    if item.key not in (USER_RISK_KNOBS | {"BINANCE_API_KEY", "BINANCE_API_SECRET", "USE_TESTNET"}):
                        errors.append(f"{item.key}: 该配置由平台统一管理（仅管理员可修改）")
                        continue

                if item.key in {"BINANCE_API_KEY", "BINANCE_API_SECRET", "USE_TESTNET"}:
                    if (user.get("role") or "user") != "admin":
                        require_account_owner(account_id, user)
                # 验证配置值
                if item.type == 'number':
                    try:
                        float(item.value)
                    except (ValueError, TypeError):
                        errors.append(f"{item.key}: Invalid number value")
                        continue
                
                # 特殊验证：百分比配置（兼容 PERCENT / PCT）
                if ('PERCENT' in item.key or 'PCT' in item.key) and item.type == 'number':
                    if not (0 <= float(item.value) <= 1):
                        errors.append(f"{item.key}: Must be between 0 and 1")
                        continue
                
                if item.key in {"BINANCE_API_KEY", "BINANCE_API_SECRET", "USE_TESTNET"}:
                    # 账号私有API配置：写入 accounts
                    if item.key == "BINANCE_API_KEY":
                        Account.update_credentials(account_id, api_key=str(item.value or ""))
                    elif item.key == "BINANCE_API_SECRET":
                        Account.update_credentials(account_id, api_secret=str(item.value or ""))
                    else:
                        Account.update_credentials(account_id, use_testnet=bool(item.value))
                else:
                    TradingConfig.set(
                        item.key,
                        item.value,
                        item.type,
                        item.category,
                        item.description,
                        account_id=account_id,
                    )
                updated_count += 1
            except Exception as e:
                errors.append(f"{item.key}: {str(e)}")
        
        if errors:
            return {
                "message": f"部分配置更新成功: {updated_count}/{len(configs)}",
                "updated": updated_count,
                "errors": errors,
                "note": "交易系统将在下次扫描时自动使用新配置"
            }
        
        return {
            "message": f"成功更新 {updated_count} 个配置",
            "updated": updated_count,
            "note": "交易系统将在下次扫描时自动使用新配置"
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@router.put("/global/{key}")
async def update_global_config(
    key: str,
    item: ConfigUpdate,
    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
        
        # 获取现有配置以确定类型和分类
        existing = GlobalStrategyConfig.get(key)
        if existing:
            config_type = item.type or existing['config_type']
            category = item.category or existing['category']
            description = item.description or existing['description']
        else:
            # 从默认配置获取
            meta = CORE_STRATEGY_CONFIG_DEFAULTS.get(key) or {
                "USE_FIXED_RISK_SIZING": {"type": "boolean", "category": "risk", "description": "使用固定风险百分比计算仓位"},
                "FIXED_RISK_PERCENT": {"type": "number", "category": "risk", "description": "每笔单子承受的风险百分比"},
            }.get(key)
            config_type = item.type or (meta.get("type") if meta else "string")
            category = item.category or (meta.get("category") if meta else "strategy")
            description = item.description or (meta.get("description") if meta else f"{key}配置")
        
        # 验证配置值
        if config_type == 'number':
            try:
                float(item.value)
            except (ValueError, TypeError):
                raise HTTPException(status_code=400, detail=f"Invalid number value for {key}")
        elif config_type == 'boolean':
            if not isinstance(item.value, bool):
                item.value = str(item.value).lower() in ('true', '1', 'yes', 'on')
        
        # 更新全局配置
        GlobalStrategyConfig.set(
            key,
            item.value,
            config_type,
            category,
            description,
            updated_by=user.get("username")
        )
        
        # 更新Redis缓存
        try:
            from config_manager import GlobalStrategyConfigManager
            global_mgr = GlobalStrategyConfigManager()
            if isinstance(item.value, (dict, list, bool, int, float)):
                import json
                value_str = json.dumps(item.value, ensure_ascii=False)
            else:
                value_str = str(item.value)
            global_mgr._set_to_redis(key, item.value)
        except Exception as e:
            logger.warning(f"更新全局配置Redis缓存失败: {e}")
        
        return {"message": f"全局配置 {key} 已更新", "key": key, "value": item.value}
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/global/batch")
async def update_global_configs_batch(
    configs: list[ConfigItem],
    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
        from config_manager import GlobalStrategyConfigManager
        
        updated_count = 0
        errors = []
        global_mgr = GlobalStrategyConfigManager()
        
        for item in configs:
            try:
                # 获取现有配置
                existing = GlobalStrategyConfig.get(item.key)
                if existing:
                    config_type = item.type or existing['config_type']
                    category = item.category or existing['category']
                    description = item.description or existing['description']
                else:
                    # 从默认配置获取
                    meta = CORE_STRATEGY_CONFIG_DEFAULTS.get(item.key) or {
                        "USE_FIXED_RISK_SIZING": {"type": "boolean", "category": "risk"},
                        "FIXED_RISK_PERCENT": {"type": "number", "category": "risk"},
                    }.get(item.key)
                    config_type = item.type or (meta.get("type") if meta else "string")
                    category = item.category or (meta.get("category") if meta else "strategy")
                    description = item.description or (meta.get("description") if meta else f"{item.key}配置")
                
                # 验证配置值
                if config_type == 'number':
                    try:
                        float(item.value)
                    except (ValueError, TypeError):
                        errors.append(f"{item.key}: Invalid number value")
                        continue
                
                # 更新全局配置
                GlobalStrategyConfig.set(
                    item.key,
                    item.value,
                    config_type,
                    category,
                    description,
                    updated_by=user.get("username")
                )
                
                # 更新Redis缓存
                global_mgr._set_to_redis(item.key, item.value)
                updated_count += 1
            except Exception as e:
                errors.append(f"{item.key}: {str(e)}")
        
        if errors:
            return {
                "message": f"成功更新 {updated_count} 个配置，{len(errors)} 个失败",
                "updated": updated_count,
                "errors": errors,
                "note": "交易系统将在下次扫描时自动使用新配置"
            }
        
        return {
            "message": f"成功更新 {updated_count} 个全局配置",
            "updated": updated_count,
            "note": "交易系统将在下次扫描时自动使用新配置"
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))
