"""
配置文件 - API密钥和交易参数配置
支持从数据库读取配置（优先），回退到环境变量和默认值
支持动态重载配置
"""
import os
from typing import Optional
from pathlib import Path

# 加载 .env 文件（优先从 trading_system/.env，其次从 backend/.env，再到项目根目录/.env）
# 重要：对于 ATS_ACCOUNT_ID，如果 supervisor 已设置（交易进程），则不要从 .env 覆盖
# .env 中的 ATS_ACCOUNT_ID 只用于 recommendations_main.py 的默认值
try:
    from dotenv import load_dotenv
    trading_system_dir = Path(__file__).parent
    project_root = trading_system_dir.parent
    backend_dir = project_root / "backend"
    env_files = [
        trading_system_dir / '.env',  # trading_system/.env
        backend_dir / '.env',         # backend/.env（线上常见放这里：DB/REDIS/ATS_MASTER_KEY）
        project_root / '.env',        # 项目根目录/.env
    ]
    # 先保存 supervisor 设置的 ATS_ACCOUNT_ID（如果存在）
    # supervisor 在启动交易进程时会设置这个环境变量，优先级应该高于 .env
    supervisor_account_id = os.getenv("ATS_ACCOUNT_ID")
    
    for env_file in env_files:
        if env_file.exists():
            # 加载 .env 文件（会覆盖环境变量）
            load_dotenv(env_file, override=True)
            # logger.debug(f"[config.py] 已加载 .env 文件: {env_file}")  # 调试信息，已禁用
            # 如果 supervisor 设置了 ATS_ACCOUNT_ID，恢复它（交易进程使用 supervisor 的值，不依赖 .env）
            if supervisor_account_id:
                os.environ["ATS_ACCOUNT_ID"] = supervisor_account_id
                # logger.debug(f"[config.py] 恢复 supervisor 设置的 ATS_ACCOUNT_ID={supervisor_account_id}（交易进程使用此值，不依赖 .env）")  # 调试信息，已禁用
            break
    else:
        raise Exception("supervisor 未设置 ATS_ACCOUNT_ID")
        # # 如果都不存在，尝试加载但不报错
        # load_dotenv(project_root / '.env', override=False)
        # # 同样恢复 supervisor 设置的值
        # if supervisor_account_id:
        #     os.environ["ATS_ACCOUNT_ID"] = supervisor_account_id
except ImportError:
    # python-dotenv 未安装时忽略
    pass
except Exception:
    # 加载 .env 文件失败时忽略，不影响程序运行
    # logger.warning(f"[config.py] 加载ATS_ACCOUNT_ID时出错: {e}")  # 调试信息，已禁用
    pass  # ⚠️ 2026-01-27修复：添加pass语句，修复IndentationError

# 尝试从数据库加载配置
USE_DB_CONFIG = False
_config_manager = None

def _init_config_manager():
    """初始化配置管理器"""
    global USE_DB_CONFIG, _config_manager
    if _config_manager is not None:
        return _config_manager
    
    # ⚠️ 2026-01-27修复：移除所有print语句，避免污染日志格式
    # 只在错误时使用stderr输出，正常初始化过程静默处理
    import sys
    from pathlib import Path
    
    try:
        # 从trading_system目录向上两级到项目根目录，然后找backend
        project_root = Path(__file__).parent.parent
        backend_path = project_root / 'backend'
        
        if not backend_path.exists():
            # 只在错误时输出到stderr，不影响正常日志
            print(f"[配置管理器] ❌ backend目录不存在: {backend_path}", file=sys.stderr)
            USE_DB_CONFIG = False
            return None
        
        # 检查config_manager.py是否存在
        config_manager_file = backend_path / 'config_manager.py'
        
        if not config_manager_file.exists():
            print(f"[配置管理器] ❌ config_manager.py不存在: {config_manager_file}", file=sys.stderr)
            USE_DB_CONFIG = False
            return None
        
        # 添加到sys.path
        backend_str = str(backend_path)
        if backend_str not in sys.path:
            sys.path.insert(0, backend_str)
        
        # 尝试导入
        try:
            from config_manager import ConfigManager  # type: ignore
            
            # 从环境变量获取 account_id（trading_system 进程通过 ATS_ACCOUNT_ID 指定）
            try:
                account_id = int(os.getenv("ATS_ACCOUNT_ID") or os.getenv("ACCOUNT_ID") or 1)
            except Exception:
                account_id = 1
            
            # 为当前账号创建 ConfigManager 实例（而不是使用全局的）
            config_manager = ConfigManager.for_account(account_id)
            
            # 测试数据库连接
            try:
                config_manager.reload()
                # 成功时使用logger输出（此时日志系统应该已初始化）
                import logging
                try:
                    logger = logging.getLogger(__name__)
                    logger.info(f"[配置管理器] ✓ 数据库连接成功，已加载 {len(config_manager._cache)} 个配置项")
                    logger.info("[配置管理器] ✓ 配置管理器初始化成功，将从数据库读取配置")
                except Exception:
                    # 如果日志系统还未初始化，静默处理
                    pass
                
                _config_manager = config_manager
                USE_DB_CONFIG = True
                return config_manager
            except Exception as db_error:
                # 错误时输出到stderr，不影响正常日志
                print(f"[配置管理器] ⚠ 数据库连接失败: {db_error}", file=sys.stderr)
                USE_DB_CONFIG = False
                return None
                
        except ImportError as e:
            print(f"[配置管理器] ❌ 无法导入config_manager: {e}", file=sys.stderr)
            USE_DB_CONFIG = False
            return None
        except Exception as e:
            import traceback
            print(f"[配置管理器] ❌ 配置管理器初始化失败: {e}", file=sys.stderr)
            print(traceback.format_exc(), file=sys.stderr)
            USE_DB_CONFIG = False
            return None
            
    except Exception as e:
        import traceback
        print(f"[配置管理器] ❌ 初始化异常: {e}", file=sys.stderr)
        print(traceback.format_exc(), file=sys.stderr)
        USE_DB_CONFIG = False
        return None

# 初始化配置管理器（在模块加载时执行）
# ⚠️ 2026-01-27修复：静默初始化，只在错误时输出到stderr，避免污染日志格式
try:
    _init_config_manager()
except Exception as e:
    # 错误时输出到stderr，不影响正常日志
    import sys
    import traceback
    print(f"[config.py] 配置管理器初始化异常: {e}", file=sys.stderr)
    print(traceback.format_exc(), file=sys.stderr)

def _get_config_value(key, default=None):
    """获取配置值（支持动态重载）"""
    if _config_manager:
        try:
            # 不每次都reload，避免性能问题，只在需要时reload
            value = _config_manager.get(key)
            # 如果从数据库获取到值，直接返回（排除None和空字符串，除非是敏感配置）
            if value is not None:
                # 对于API密钥，即使是空字符串也返回（表示数据库中有配置但值为空）
                if key in ['BINANCE_API_KEY', 'BINANCE_API_SECRET']:
                    return value if value else default
                # 对于其他配置，空字符串视为无效，返回default
                if value == '':
                    return default
                return value
        except Exception as e:
            import logging
            logger = logging.getLogger(__name__)
            logger.debug(f"从配置管理器获取{key}失败: {e}，尝试环境变量")
    
    # 回退到环境变量
    env_value = os.getenv(key)
    if env_value is not None and env_value != '':
        return env_value
    
    # 最后返回默认值
    return default

# 默认配置字典（偏向山寨短线、适时落袋：4H 中性可开单、大盘过滤略放宽、信号门槛适中）
DEFAULT_TRADING_CONFIG = {
    # ===== 用户风险旋钮（山寨币专属策略）=====
    'AUTO_TRADE_ENABLED': True,      # 自动交易总开关
    'MAX_OPEN_POSITIONS': 4,         # 同时持仓数量上限（总仓位12% / 单笔1.5% = 最多4个）
    'MAX_DAILY_ENTRIES': 15,          # 每日最多15笔（快速验证模式：提高上限以快速验证策略）
    # 周日开仓上限：周日最多允许开仓次数，0=不限制（与 MAX_DAILY_ENTRIES 一致）。设为 2～3 可降低周日亏损又不完全停单
    'SUNDAY_MAX_OPENS': 3,
    # 周日信号门槛：周日仅当信号强度>=此值才开仓，0=不提高。建议 8～9
    'SUNDAY_MIN_SIGNAL_STRENGTH': 8,
    'NIGHT_HOURS_NO_OPEN_ENABLED': True,
    'NIGHT_HOURS_START': 21,
    'NIGHT_HOURS_END': 6,
    'NIGHT_HOURS_ONLY_SUNDAY': True,
    # 禁止开仓小时（北京）：逗号分隔，如 "17,19,22" 表示 17/19/22 时不开新仓；空则不限制。按历史亏损时段配置
    'NO_OPEN_HOURS_BJ': '',
    'ONE_WAY_POSITION_ONLY': True,   # 账号固定为单向持仓模式，不传 positionSide，不检测对冲模式（避免 -4061）

    # ===== 影子模式半自动化（config/current_suggestions.json，由 daily_report / 分析脚本生成）=====
    'SHADOW_MODE_AUTO_APPLY': False,  # True：按建议自动跳过黑名单/差时段，并对加仓/减仓名单调整杠杆倍数
    'SHADOW_MODE_MIN_CONFIDENCE': 0.7,  # 低于此置信度（shadow_mode_tracking.json 中 accuracy 等）则不应用建议
    'SHADOW_MODE_SUGGESTIONS_PATH': 'config/current_suggestions.json',  # 相对项目根目录
    'SHADOW_MODE_TRACKING_PATH': 'config/shadow_mode_tracking.json',
    'SHADOW_MODE_INCREASE_LEVERAGE_MULT': 1.5,  # increase_position 名单：杠杆 × 此值（上限仍受 MAX_LEVERAGE 约束）
    'SHADOW_MODE_DECREASE_LEVERAGE_MULT': 0.5,  # decrease_position 名单：杠杆 × 此值

    'MAX_POSITION_PERCENT': 0.20,  # 单笔仓位上限20%（作为风控熔断，实际仓位由固定风险模型决定）
    'MAX_TOTAL_POSITION_PERCENT': 0.80,  # 总仓位80%（避免满仓，留有余地）
    'MIN_POSITION_PERCENT': 0.01,  # 最小仓位1%
    'MIN_MARGIN_USDT': 2.0,  # 最小保证金2美元（降低门槛，允许高杠杆小资金交易，避免被 risk_manager 拦截）
    'MIN_CHANGE_PERCENT': 0.5,  # 最小价格变动0.5%
    'TOP_N_SYMBOLS': 30,  # 扩大候选池到30个，增加机会
    'SCAN_EXTRA_SYMBOLS_FOR_SUPPLEMENT': 20,  # 智能补单候选增加
    'MAX_SCAN_SYMBOLS': 500,  # 扫描前500个
    'EXCLUDE_MAJOR_COINS': True,  # 排除主流币
    'STOP_LOSS_PERCENT': 0.05,  # 基础止损5%（配合ATR动态止损，作为保底）
    'TAKE_PROFIT_PERCENT': 0.25,  # 第二目标止盈25%（略拉近止盈以提高触达率）
    'TAKE_PROFIT_1_PERCENT': 0.12,  # 第一目标止盈12%（山寨币不必久拿，早点落袋为安，降低反转风险）
    'MIN_RR_FOR_TP1': 1.2,  # 第一目标止盈的最小盈亏比（相对于止损距离），略放宽以提高触发率
    'MIN_STOP_LOSS_PRICE_PCT': 0.03,  # 最小止损价格变动3%（适当放宽止损，减少正常波动被扫）
    'MIN_TAKE_PROFIT_PRICE_PCT': 0.02,  # 最小止盈价格变动2%
    'USE_ATR_STOP_LOSS': True,  # 使用ATR动态止损
    'ATR_STOP_LOSS_MULTIPLIER': 2.0,  # ATR止损倍数2.0（适中止损宽度，兼顾胜率与回撤）
    'ATR_TAKE_PROFIT_MULTIPLIER': 1.5,  # ATR止盈倍数1.5（配合适中盈亏比，更容易触及止盈）
    'RISK_REWARD_RATIO': 1.5,  # 盈亏比1.5:1（在当前波动水平下更实际可达）
    'USE_MARGIN_CAP_FOR_TP': True,  # 止盈按保证金上限封顶，避免 TP 过远难以触及、亏损扛单。勿关。
    'USE_MARGIN_CAP_FOR_SL': True,  # 止损按保证金上限封顶，避免 ATR 过宽导致 -60%～-80% 保证金扛单。勿关。
    'ATR_PERIOD': 14,  # ATR计算周期14
    'USE_DYNAMIC_ATR_MULTIPLIER': False,  # 不使用动态ATR
    'ATR_MULTIPLIER_MIN': 1.5,  # 动态ATR倍数最小值（回归盈利期设置）
    'ATR_MULTIPLIER_MAX': 2.5,  # 动态ATR倍数最大值（回归盈利期设置）
    'SCAN_INTERVAL': 900,  # 扫描间隔15分钟（900秒），快速验证模式：提高扫描频率以增加交易机会
    'SCAN_SYMBOL_ANALYSIS_TIMEOUT_SEC': 12,  # 单个交易对「详细分析」超时（秒）；已并行拉取主周期/确认周期K线，12秒通常够用；网络慢可调大（18~25）
    'SCAN_KLINE_FETCH_TIMEOUT_SEC': 8,  # K线拉取单独超时（秒）；超时则返回降级结果（仅涨跌幅/成交量），不拖满整分析超时
    'SCAN_KLINE_LIMIT': 50,  # 扫描用 K 线根数（主周期 4h 时 MACD 需≥35、EMA50/市场状态需≥50，否则信号恒为 0/unknown）
    'SCAN_PREFER_WEBSOCKET': True,  # 优先使用 WebSocket 方式：如果缓存不完整，等待 WebSocket 推送数据（最多1-1.5秒），而不是立即回退 REST API
    'SCAN_LIMIT_KLINE_SUBSCRIBE': True,  # 限制 K 线订阅：只在缓存完全没有数据时才订阅，避免订阅过多导致负载上升
    'SCAN_PREWARM_KLINE_ENABLED': True,  # 扫描前预热 K 线：批量预订阅 WS + REST 预取，提高缓存命中率、减少分析超时
    'SCAN_PREWARM_CONCURRENT': 5,  # 预热阶段同时预取多少个交易对的 K 线（避免并发过高）
    # 多账号/低配服务器（如 2 CPU 4G）：降低并发与错峰扫描，避免 CPU 打满
    'SCAN_CONCURRENT_SYMBOLS': 2,  # 扫描时同时分析多少个交易对（2 CPU 4G 多账号建议 2，单账号可 3～5）
    'SCAN_STAGGER_BY_ACCOUNT': False,  # 多账号错峰开关：单账号可关闭；多账号同时跑时改为 True 避免同时扫
    'SCAN_STAGGER_RANDOM': True,  # True=随机延迟(更分散)，False=按 account_id 固定步长延迟
    'SCAN_STAGGER_MIN_SEC': 10,  # 随机延迟下限（秒）
    'SCAN_STAGGER_MAX_SEC': 120,  # 随机延迟上限（秒）
    'SCAN_STAGGER_SEC': 60,  # 非随机模式下每账号步长（account_id-1 * 此值）
    'KLINE_INTERVAL': '1h',
    'PRIMARY_INTERVAL': '4h',  # 主周期4小时，过滤噪音
    'CONFIRM_INTERVAL': '1d',  # 确认周期日线，看大趋势
    'ENTRY_INTERVAL': '1h',  # 入场周期1小时，避免太小的时间框架
    # 入场短周期（用于快速方向确认，例如15m），不要太小以免噪音太大
    'ENTRY_SHORT_INTERVAL': '15m',
    # 是否开启“短周期方向过滤”，避免在15m明显上涨时做空、明显下跌时做多（快速验证模式：关闭以增加交易机会）
    'ENTRY_SHORT_TREND_FILTER_ENABLED': True,
    # 短周期方向过滤使用的最小趋势幅度（例如0.003=0.3%），变化太小视为震荡不过滤
    'ENTRY_SHORT_TREND_MIN_PCT': 0.003,
    # 检查最近多少根短周期K线来评估方向（例如3根15m约等于45分钟）
    'ENTRY_SHORT_CONFIRM_CANDLES': 3,
    'MIN_VOLUME_24H': 30000000,  # 24小时成交额≥3000万美元，过滤垃圾币
    'MIN_VOLUME_24H_STRICT': 50000000,  # 严格过滤≥5000万美元
    'MIN_VOLATILITY': 0.03,  # 最小波动率3%，过滤死币
    'MIN_SIGNAL_STRENGTH': 7,  # 信号强度≥7（山寨短线：适度放宽以多抓机会，配合扫描器回退弱信号；偏保守可改为8）
    # ===== 资金费率过滤（开仓前）=====
    'FUNDING_RATE_FILTER_ENABLED': True,  # 开仓前检查资金费率，避免在费率极端不利于己方时进场
    'MAX_FUNDING_RATE_FOR_LONG': 0.001,   # 做多时 lastFundingRate 超过此值则跳过（0.1%=多头付费高）
    'MIN_FUNDING_RATE_FOR_SHORT': -0.001, # 做空时 lastFundingRate 低于此值则跳过（-0.1%=空头付费高）
    # 扫描阶段资金费率过滤：排除 |lastFundingRate| 过大的标的，避免进入 TOP_N
    'SCAN_FUNDING_RATE_FILTER_ENABLED': True,
    'SCAN_FUNDING_RATE_MAX_ABS': 0.001,  # 绝对值超过 0.1% 则从扫描候选剔除
    # ===== 市场节奏自动识别（低波动期 vs 正常期，自动切换参数，无需手动）=====
    'MARKET_REGIME_AUTO': True,  # 是否根据波动率自动识别并切换低波动期参数
    'LOW_VOLATILITY_THRESHOLD': 2.5,  # 24h |changePercent| 中位数低于此值判为低波动期（%）
    'LOW_VOLATILITY_MAX_POSITION_PERCENT': 0.15,  # 低波动期单币最大仓位（正常期用 MAX_POSITION_PERCENT）
    'LOW_VOLATILITY_MIN_SIGNAL_STRENGTH': 9,  # 低波动期信号强度门槛（更严）
    'LOW_VOLATILITY_SCAN_FUNDING_RATE_MAX_ABS': 0.0005,  # 低波动期扫描阶段资金费率更严（0.05%）
    'TIME_STOP_MAX_HOLD_HOURS': 8,  # 持仓超过 N 小时且未达最小盈利则考虑平仓（低波动期更适用）
    'TIME_STOP_MIN_PNL_PCT_TO_HOLD': 0.0,  # 持仓超过时间阈值时，若 pnl% 低于此值则平仓（0=允许小亏即走）
    'TIME_STOP_ENABLED': True,  # 是否启用时间/无盈利止损（可与 regime 结合：仅低波动期或始终启用）
    # 山寨币早止盈：盈利达标且持仓满 N 小时则市价止盈，不等挂单 TP，避免反转回吐
    'EARLY_TAKE_PROFIT_ENABLED': True,  # 是否启用「盈利达标+持仓时长」早止盈
    'EARLY_TAKE_PROFIT_MIN_PNL_PCT': 10.0,  # 盈利达到此比例（% of margin）即符合条件，默认 10%
    'EARLY_TAKE_PROFIT_MIN_HOLD_HOURS': 2.0,  # 且持仓至少 N 小时才触发（避免刚开仓就平）
    # 滞涨早止盈：曾涨到约 N% 后 X 小时内未创新高则分批减仓+抬止损（动能衰减）
    'STAGNATION_EARLY_EXIT_ENABLED': False,  # 是否启用「滞涨早止盈」（默认关，按需开启）
    'STAGNATION_MIN_RUNUP_PCT': 10.0,  # 曾达到的最低浮盈%（基于保证金）才考虑滞涨，例如 10 表示 10%
    'STAGNATION_NO_NEW_HIGH_HOURS': 3.0,  # 多少小时内未创新高视为滞涨，例如 2～4
    'STAGNATION_PARTIAL_CLOSE_PCT': 0.5,  # 触发时先平掉仓位比例，例如 0.5=50%
    'STAGNATION_LOCK_PCT': 5.0,  # 剩余仓位止损上移锁住的利润%（与「最高浮盈的一半」取大）
    # 开仓前流动性检查（价差/深度）：低量期可减少滑点与冲击成本
    'LIQUIDITY_CHECK_BEFORE_ENTRY': False,  # 默认关闭；开启后开仓前检查盘口价差与深度，不通过则跳过
    'LIQUIDITY_MAX_SPREAD_PCT': 0.005,  # 买一卖一价差超过此比例（0.5%）则跳过
    'LIQUIDITY_MIN_DEPTH_USDT': 30000,   # 前 5 档买卖深度合计名义价值低于此值（USDT）则跳过
    # 主动买卖量过滤（可选）：与方向一致时通过，极端反向时跳过
    'TAKER_RATIO_FILTER_ENABLED': False,  # 默认关闭，开启后做多要求 buySellRatio≥下限、做空要求≤上限
    'MIN_TAKER_BUY_RATIO_FOR_LONG': 0.85,   # 做多时最近1h buySellRatio 低于此值则跳过（主动买不足）
    'MAX_TAKER_BUY_RATIO_FOR_SHORT': 1.15,  # 做空时最近1h buySellRatio 高于此值则跳过（主动卖不足）
    
    # ===== 动态过滤优化 =====
    'BETA_FILTER_ENABLED': True,  # 大盘共振过滤：BTC/ETH下跌时屏蔽多单（山寨常先于大盘动，可关或放宽）
    'BETA_FILTER_THRESHOLD': -0.01,  # -1%（默认放宽：仅 15m 跌超 1% 才屏蔽多单，减少错失山寨机会；保守可改为 -0.005）
    # ===== RSI / 24h 涨跌幅过滤（避免追高杀跌）=====
    'MAX_RSI_FOR_LONG': 65,   # 做多时 RSI 超过此值则不开多（2026-02-12：65 避免追高）
    'MAX_CHANGE_PERCENT_FOR_LONG': 25,  # 做多时 24h 涨跌幅超过此值则不开多（避免追大涨）
    'MIN_RSI_FOR_SHORT': 30,  # 做空时 RSI 低于此值则不做空（避免深超卖反弹）
    'MAX_CHANGE_PERCENT_FOR_SHORT': 10,  # 做空时 24h 涨跌幅超过此值则不做空（24h 仍大涨时不做空）
    
    # ===== RSI 极限反转策略（抄底/逃顶）=====
    'RSI_EXTREME_REVERSE_ENABLED': False,  # 关闭RSI极限反转，与盈利期一致；开启易在趋势里逆势导致止损
    'RSI_EXTREME_REVERSE_ONLY_NEUTRAL_4H': True,  # 若将来开启反向，仅允许4H中性时
    
    'ATR_SPIKE_THRESHOLD': 2.0,  # ATR异常激增阈值（当前ATR / 平均ATR）
    'SIGNAL_STRENGTH_POSITION_MULTIPLIER': {7: 0.8, 8: 0.9, 9: 1.0, 10: 1.1},  # 信号强度分级：7分80%，8分90%，9分100%，10分110%（适度加仓）

    # ===== 仓位管理优化（山寨币专属）=====
    'USE_FIXED_RISK_SIZING': True,  # 固定每笔风险，避免亏损扩大
    'FIXED_RISK_PERCENT': 0.01,  # 每笔最多亏总资金1%（回归盈利期风控水平）
    'MAX_LEVERAGE_SMALL_CAP': 4,  # 高波动/小众币最大杠杆（回归盈利期设置）
    'MIN_LEVERAGE': 4,  # 动态杠杆下限，允许在风险较大时降低杠杆
    'ATR_LEVERAGE_REDUCTION_THRESHOLD': 0.05,  # ATR超过5%时降低杠杆
    'LEVERAGE': 4,  # 基础杠杆（回归盈利期设置）
    'USE_DYNAMIC_LEVERAGE': True,  # 开启动态杠杆（基于止损宽度自动调整）
    'MAX_SINGLE_TRADE_LOSS_PERCENT': 20.0,  # 单笔交易最大本金亏损率（20%），用于限制杠杆
    'MAX_LEVERAGE': 12,  # 最大杠杆（回归盈利期上限）
    # 移动止损与保本：全局总开关（关闭后不执行移动止损、不执行盈利保本）
    'PROFIT_PROTECTION_ENABLED': True,  # True=启用移动止损与保本，False=全部关闭
    'USE_TRAILING_STOP': True,  # 在 PROFIT_PROTECTION_ENABLED 为 True 时生效
    'TRAILING_STOP_ACTIVATION': 0.10,  # 盈利10%后激活（回归盈利期更早锁盈设置）
    'TRAILING_STOP_PROTECT': 0.02,  # 保护2%利润（回归盈利期紧凑保护）
    # 盈利保护：保本含手续费，避免“保本”实为小亏（在 PROFIT_PROTECTION_ENABLED 为 True 时生效）
    'FEE_BUFFER_PCT': 0.0015,  # 保本价相对入场价缓冲（0.15% 覆盖开平双向手续费）
    'LOCK_PROFIT_AT_BREAKEVEN_AFTER_PCT': 0.03,  # 盈利达3%保证金时移至保本（建议3%～5%平衡「少亏」与「拿住趋势」，0=关闭）
    # 分层锁盈：在“保本”和“全量移动止损”之间增加两级台阶，减少盈利回吐
    'LOCK_PROFIT_STAGE1_TRIGGER_PCT': 0.08,  # 盈利达8%保证金后，进入第一层锁盈
    'LOCK_PROFIT_STAGE1_PCT': 0.02,  # 第一层锁住约2%保证金利润
    'LOCK_PROFIT_STAGE2_TRIGGER_PCT': 0.15,  # 盈利达15%保证金后，进入第二层锁盈
    'LOCK_PROFIT_STAGE2_PCT': 0.05,  # 第二层锁住约5%保证金利润
    
    # 最小持仓时间锁：立即取消！山寨币30分钟可能暴涨暴跌50%
    'MIN_HOLD_TIME_SEC': 0,  # 取消持仓时间锁
    'POSITION_SYNC_INTERVAL': 60,  # 持仓状态同步间隔60秒

    # ===== 自动交易过滤（用于提升胜率/控频）=====
    # 是否仅在 marketRegime=trending 时才自动交易；否则只生成推荐
    'AUTO_TRADE_ONLY_TRENDING': True,
    # 当不强制 only_trending 时，是否允许在震荡市自动交易；默认关闭，避免震荡扫损
    'AUTO_TRADE_ALLOW_RANGING': False,
    # 当 marketRegime=unknown（K线不足/未判定）时，是否允许自动交易；默认关闭
    'AUTO_TRADE_ALLOW_UNKNOWN': False,
    # 是否允许 4H 趋势为 neutral 时自动交易（山寨短线：默认开，4H 中性也开单以多抓机会；偏保守可改为 False）
    'AUTO_TRADE_ALLOW_4H_NEUTRAL': True,
    # 震荡市额外提高的最小信号门槛（0-3 建议范围）；默认 +2，减少边缘信号
    'RANGING_MARKET_SIGNAL_BOOST': 2,

    # ===== 趋势入场过滤（防止追在半山腰）=====
    # 是否启用基于趋势状态的入场过滤：
    # - 扫描阶段会为每个强信号交易对写入一份“趋势状态”到缓存（Redis）
    # - 开仓时根据当前价格相对于信号价格的偏移，过滤“过晚追价”的入场
    'USE_TREND_ENTRY_FILTER': True,
    # 在信号方向上允许的最大累计趋势幅度（相对于信号价），超过则认为“时机太晚”，不再入场
    # 例如：0.08 表示价格沿趋势方向已经走了 8% 以上还没上车，则跳过本轮机会（快速验证模式：放宽阈值以增加交易机会）
    'MAX_TREND_MOVE_BEFORE_ENTRY': 0.04,  # 2026-01-29优化：从0.05收紧到0.04，更严格避免追高杀跌
    # 趋势状态缓存的 TTL（秒），用于控制一轮趋势的“有效期”
    'TREND_STATE_TTL_SEC': 3600,

    # ===== 推荐系统优化（优先推荐“入场时机不算太晚”的趋势单）=====
    # 是否在生成推荐时启用趋势入场时机过滤（仅对趋势市场的顺势推荐生效）
    # - 会对比当前价与趋势信号价的偏离，如果已经沿趋势方向走得太远，则不再生成该推荐
    'RECO_USE_TREND_ENTRY_FILTER': True,
    # 推荐系统使用的最大允许趋势幅度（相对于趋势信号价），默认与自动交易相同或略微更严格
    'RECO_MAX_TREND_MOVE_BEFORE_ENTRY': 0.04,

    # ===== 回撤/区间入场过滤（参考近N根K线前低、前高，减少买在区间顶部、卖在区间底部）=====
    # 与 USE_TREND_ENTRY_FILTER（相对「信号价」的追价幅度）互补：本组用 K 线区间衡量是否已跑到局部极值附近。
    'ENTRY_PULLBACK_FILTER_ENABLED': False,  # True 启用；建议先 True 观察日志中 [回撤过滤] 跳过比例再调阈值
    'ENTRY_PULLBACK_INTERVAL': None,  # None=沿用 ENTRY_INTERVAL（默认1h）；可改为 '15m' 等更敏感周期
    'ENTRY_PULLBACK_LOOKBACK_BARS': 24,  # 统计区间用的 K 线根数（需 >= MIN_BARS）
    'ENTRY_PULLBACK_MIN_BARS': 5,  # 至少多少根有效 K 才启用过滤，不足则跳过本过滤
    # 现价在 [区间低,区间高] 中的相对位置 pos∈[0,1]：0=贴前低，1=贴前高
    'ENTRY_PULLBACK_MAX_LONG_IN_RANGE': 0.62,  # 做多：pos 超过此值则不开（默认拒绝最上约 38% 区域）
    'ENTRY_PULLBACK_MIN_SHORT_IN_RANGE': 0.38,  # 做空：pos 低于此值则不开（默认拒绝最下约 38% 区域）

    # ===== 智能入场（方案C）=====
    # 根治方案：默认关闭。关闭后回归“纯限价单模式”（不追价/不市价兜底/未成交撤单跳过）
    'SMART_ENTRY_ENABLED': True,  # 开启智能入场，提高成交率
    'SMART_ENTRY_STRONG_SIGNAL': 7,  # 强信号阈值（与 MIN_SIGNAL_STRENGTH 一致，便于智能入场触发）
    'ENTRY_SYMBOL_COOLDOWN_SEC': 1800,  # 同一币种冷却30分钟（1800秒），快速验证模式：缩短冷却以增加交易频率
    
    # ===== 同一交易对连续亏损过滤（避免连续亏损后继续交易）=====
    'SYMBOL_LOSS_COOLDOWN_ENABLED': True,  # 是否启用同一交易对连续亏损后的冷却
    'SYMBOL_MAX_CONSECUTIVE_LOSSES': 2,  # 最大允许连续亏损次数（超过则禁止交易）
    'SYMBOL_LOSS_COOLDOWN_SEC': 3600,  # 连续亏损后的冷却时间（秒），默认1小时
    'ENTRY_TIMEOUT_SEC': 180,  # 智能入场总预算（秒）（限价/追价逻辑内部使用）
    'ENTRY_STEP_WAIT_SEC': 15,  # 每步等待成交时间（秒）
    'ENTRY_CHASE_MAX_STEPS': 4,  # 最多追价步数（逐步减少 offset）
    'ENTRY_MARKET_FALLBACK_AFTER_SEC': 45,  # 趋势强时：超过该秒数仍未成交 -> 评估是否市价兜底
    'ENTRY_CONFIRM_TIMEOUT_SEC': 30,  # 下单后最终确认成交的等待时间（秒）
    'ENTRY_MAX_DRIFT_PCT_TRENDING': 0.8,  # 追价偏离放宽到0.8%（山寨币跳空大）
    'ENTRY_MAX_DRIFT_PCT_RANGING': 0.3,  # 震荡/弱趋势时允许的最大追价偏离
    'LIMIT_ORDER_OFFSET_PCT': 0.5,  # 限价单偏移百分比（默认0.5%）
    'MIN_HOLD_TIME_SEC': 0,  # 默认30分钟（1800秒），已取消

    # 持仓监控详细日志：用于排查问题时观察每次检查的当前价/目标价/ROE 等
    'POSITION_DETAILED_LOG_ENABLED': False,

    # ===== 市场方案与多空过滤（与 MARKET_SCHEME 配合）=====
    # 牛市不推空单、熊市不推多单（由 config_manager 根据 MARKET_SCHEME 覆盖）
    'BLOCK_SHORT_WHEN_BULL_MARKET': True,   # 市场方案=牛市时禁止开空
    'BLOCK_LONG_WHEN_BEAR_MARKET': True,    # 市场方案=熊市时禁止开多
    # ===== 系统单标识（用于同步时区分本系统开仓 vs 手动开仓）=====
    # 下单时写入 newClientOrderId = SYSTEM_ORDER_ID_PREFIX_时间戳_随机，同步/补建时根据订单 clientOrderId 前缀判断是否系统单
    'SYSTEM_ORDER_ID_PREFIX': 'SYS',
    # ===== 仅币安有仓时的监管策略 =====
    # 当 SYNC_CREATE_MANUAL_ENTRY_RECORD=False 时，仍对「仅币安有仓且存在止损/止盈条件单」的持仓接入监控并补建记录（多为系统单）
    'SYNC_MONITOR_BINANCE_POSITIONS_WITH_SLTP': True,
}

def _get_trading_config():
    """获取交易配置（支持动态重载，优先从Redis读取最新值）"""
    config = DEFAULT_TRADING_CONFIG.copy()
    
    if _config_manager:
        # 从Redis重新加载配置（轻量级，只从Redis读取，不查数据库）
        # 注意：如果Redis不可用或没有数据，reload_from_redis()会保持现有缓存，不会触发数据库加载
        _config_manager.reload_from_redis()
        db_config = _config_manager.get_trading_config()
        if db_config:
            # 使用数据库配置覆盖默认配置
            config.update(db_config)
            return config
            
    return config


# 币安API配置（优先从数据库，回退到环境变量和默认值）
# 注意：在模块加载时，配置管理器可能还未初始化完成，所以先使用默认值
# 在main.py中会调用reload_config()重新加载
BINANCE_API_KEY: Optional[str] = _get_config_value('BINANCE_API_KEY', 'your_api_key_here')
BINANCE_API_SECRET: Optional[str] = _get_config_value('BINANCE_API_SECRET', 'your_api_secret_here')
USE_TESTNET: bool = _get_config_value('USE_TESTNET', False) if _get_config_value('USE_TESTNET') is not None else os.getenv('USE_TESTNET', 'False').lower() == 'true'

# 交易参数配置（优先从数据库读取，支持动态重载）
TRADING_CONFIG = _get_trading_config()

# 确保包含所有必要的默认值
for key, value in DEFAULT_TRADING_CONFIG.items():
    if key not in TRADING_CONFIG:
        TRADING_CONFIG[key] = value


def get_effective_config(key: str, default=None):
    """
    按当前市场节奏返回有效配置：若 MARKET_REGIME_AUTO 且为低波动期，
    则返回 LOW_VOLATILITY_<key>（若存在），否则返回 TRADING_CONFIG[key]。
    用于仓位、信号门槛、资金费率阈值、时间止损等，实现自动把握市场节奏。
    """
    try:
        from . import market_regime
        return market_regime.get_effective_config(key, default)
    except ImportError:
        return TRADING_CONFIG.get(key, default)


# 提供一个函数来重新加载配置
def reload_config():
    """重新加载配置（供外部调用）"""
    global TRADING_CONFIG, BINANCE_API_KEY, BINANCE_API_SECRET, USE_TESTNET, _config_manager, USE_DB_CONFIG
    global REDIS_URL, REDIS_USE_TLS, REDIS_SSL_CERT_REQS, REDIS_SSL_CA_CERTS, REDIS_USERNAME, REDIS_PASSWORD, REDIS_SELECT
    
    # 如果配置管理器不存在，尝试初始化
    if _config_manager is None:
        _init_config_manager()
    
    # 如果配置管理器存在，重新加载
    if _config_manager:
        try:
            _config_manager.reload()
            USE_DB_CONFIG = True
        except Exception as e:
            import logging
            logger = logging.getLogger(__name__)
            logger.warning(f"重新加载配置失败: {e}")
            USE_DB_CONFIG = False
    
    # 重新获取API密钥（优先从配置管理器）
    new_api_key = _get_config_value('BINANCE_API_KEY', 'your_api_key_here')
    new_api_secret = _get_config_value('BINANCE_API_SECRET', 'your_api_secret_here')
    
    # 如果获取到有效值，更新全局变量
    if new_api_key and new_api_key != 'your_api_key_here':
        BINANCE_API_KEY = new_api_key
    if new_api_secret and new_api_secret != 'your_api_secret_here':
        BINANCE_API_SECRET = new_api_secret
    
    USE_TESTNET = _get_config_value('USE_TESTNET', False) if _get_config_value('USE_TESTNET') is not None else os.getenv('USE_TESTNET', 'False').lower() == 'true'
    TRADING_CONFIG = _get_trading_config()
    # 重新加载 Redis 配置
    REDIS_URL = _get_config_value('REDIS_URL', os.getenv('REDIS_URL', REDIS_URL))
    REDIS_USE_TLS = _get_config_value('REDIS_USE_TLS', False) if _get_config_value('REDIS_USE_TLS') is not None else os.getenv('REDIS_USE_TLS', 'False').lower() == 'true'
    REDIS_SSL_CERT_REQS = _get_config_value('REDIS_SSL_CERT_REQS', REDIS_SSL_CERT_REQS)
    REDIS_SSL_CA_CERTS = _get_config_value('REDIS_SSL_CA_CERTS', REDIS_SSL_CA_CERTS)
    REDIS_USERNAME = _get_config_value('REDIS_USERNAME', os.getenv('REDIS_USERNAME', REDIS_USERNAME))
    REDIS_PASSWORD = _get_config_value('REDIS_PASSWORD', os.getenv('REDIS_PASSWORD', REDIS_PASSWORD))
    REDIS_SELECT = _get_config_value('REDIS_SELECT', os.getenv('REDIS_SELECT', REDIS_SELECT))
    # 确保默认值
    for key, value in DEFAULT_TRADING_CONFIG.items():
        if key not in TRADING_CONFIG:
            TRADING_CONFIG[key] = value

# 连接配置
CONNECTION_TIMEOUT = int(os.getenv('CONNECTION_TIMEOUT', '30'))  # 连接超时时间（秒）
CONNECTION_RETRIES = int(os.getenv('CONNECTION_RETRIES', '3'))  # 连接重试次数
# 仅用于 get_open_positions / get_recent_trades 等只读接口的单次等待时间，不影响下单/止损止盈的快速失败
# 调大此值会延长单次请求最大等待时间，在同步/查询持仓时可能阻塞事件循环，影响实时性；保持 60 秒，通过增加重试+退避应对偶发超时
READ_ONLY_REQUEST_TIMEOUT = int(os.getenv('READ_ONLY_REQUEST_TIMEOUT', '60'))
# 创建 Algo 条件单（止损/止盈）单次请求超时（秒），币安 Algo 接口在高负载时易超时；可由 TRADING_CONFIG['ALGO_ORDER_TIMEOUT_SEC'] 或环境变量覆盖
ALGO_ORDER_TIMEOUT_SEC = int(os.getenv('ALGO_ORDER_TIMEOUT_SEC', '45'))
# 获取持仓时过滤掉名义价值低于此值的仓位（USDT），与币安仪表板不一致时可调低或设为 0
POSITION_MIN_NOTIONAL_USDT = float(os.getenv('POSITION_MIN_NOTIONAL_USDT', '1.0'))

# 市场 WS 多进程共用：有 Redis 时默认开启，仅 Leader 进程建连接，其余从 Redis 读
USE_SHARED_MARKET_WS = os.getenv('USE_SHARED_MARKET_WS', 'true').lower() in ('1', 'true', 'yes')

# Redis 缓存配置（优先从数据库，回退到环境变量和默认值）
REDIS_URL = _get_config_value('REDIS_URL', os.getenv('REDIS_URL', 'redis://localhost:6379'))
REDIS_USE_TLS = _get_config_value('REDIS_USE_TLS', False) if _get_config_value('REDIS_USE_TLS') is not None else os.getenv('REDIS_USE_TLS', 'False').lower() == 'true'
REDIS_SSL_CERT_REQS = _get_config_value('REDIS_SSL_CERT_REQS', 'required')
REDIS_SSL_CA_CERTS = _get_config_value('REDIS_SSL_CA_CERTS', None)
REDIS_USERNAME = _get_config_value('REDIS_USERNAME', os.getenv('REDIS_USERNAME', None))
REDIS_PASSWORD = _get_config_value('REDIS_PASSWORD', os.getenv('REDIS_PASSWORD', None))
REDIS_SELECT = _get_config_value('REDIS_SELECT', os.getenv('REDIS_SELECT', 0))

# 日志配置
LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO')
LOG_FILE = 'trading_bot.log'
