#!/usr/bin/env python3
"""
exit_strategy.py — 持仓中止盈止损策略（已废弃）

⚠️ 已禁用 — 请使用 bridge/exit_optimizer.py
gold_trader.py 通过 exit_optimizer.py 实时管理所有出场逻辑。
原因：
  1. config_manager.py 的 AI 建议通道已注释（params_suggestion.json 无人读取）
  2. gold_trader.py 不调用本模块
  3. STATE_FILE 路径与 gold_trader.py 的 DATA_DIR 不一致

独立运行：python exit_strategy.py  → 显示废弃警告并退出
"""
import json, sys, pathlib, time
from datetime import datetime

ROOT = pathlib.Path(__file__).parent.parent.absolute()
PARAMS_FILE = ROOT / "logs" / "exit_suggestion.json"
LOG_DIR     = ROOT / "logs"
PEAK_FILE   = LOG_DIR / "exit_peak.json"   # 追踪持仓峰值价格
# ─── 状态文件（已废弃，gold_trader.py 不调用本模块）────────────
# 路径与 gold_trader.py 的 DATA_DIR/state/agent_state.json 不一致，
# 且 config_manager.py 的 AI 建议通道已注释（params_suggestion.json 无人读取）。
# 本模块已完全禁用，请使用 bridge/exit_optimizer.py（gold_trader.py 直接调用）。
STATE_FILE = LOG_DIR / "agent_state.json"

# ─── 移动止损配置（固定点数，非ATR倍数）──────────────────────
TRAIL_TREND_MIN_PROFIT  = 200   # 趋势激活门槛：盈利多少点后启用追踪（例：盈利200点后开始保护）
TRAIL_TREND_DISTANCE    = 150   # 趋势追踪：盈利峰值下方的保护点数（例：峰值下方150点）
TRAIL_RANGE_MIN_PROFIT  = 100   # 震荡激活门槛
TRAIL_RANGE_DISTANCE    = 80    # 震荡追踪保护点数

# ============ 从配置文件读取阈值 ============
CONFIG_FILE = ROOT / "config" / "strategy_rules_v3.json"

def _load_config():
    if not CONFIG_FILE.exists():
        return {}
    try:
        return json.loads(CONFIG_FILE.read_text())
    except:
        return {}

CONFIG = _load_config()
EXIT_CFG = CONFIG.get("exit_strategy", {})
RISK_CFG = CONFIG.get("risk_governor", {})

def _v(section, key, default):
    """安全读取配置值"""
    return section.get(key, default) if section else default

# ─── 趋势出场参数 ─────────────────────────
TREND_MIN_ATR       = _v(EXIT_CFG.get("trailing_stop",{}).get("trend",{}), "min_profit_atr", 1.5)
TREND_TRAIL_ATR     = _v(EXIT_CFG.get("trailing_stop",{}).get("trend",{}), "trail_distance_atr", 2.0)
TREND_LOCK_ATR      = _v(EXIT_CFG.get("trailing_stop",{}).get("trend",{}), "lock_atr", 1.2)
TREND_PARTIAL1_ATR  = _v(EXIT_CFG.get("partial_close",{}).get("trend",{}), "partial1_atr", 2.0)
TREND_PARTIAL1_PCT  = _v(EXIT_CFG.get("partial_close",{}).get("trend",{}), "partial1_pct", 0.30)
TREND_PARTIAL2_ATR  = _v(EXIT_CFG.get("partial_close",{}).get("trend",{}), "partial2_atr", 3.5)
TREND_PARTIAL2_PCT  = _v(EXIT_CFG.get("partial_close",{}).get("trend",{}), "partial2_pct", 0.25)
TREND_HOLD_ATR      = _v(EXIT_CFG.get("partial_close",{}).get("trend",{}), "hold_atr", 4.5)
TP1_ATR_TREND       = _v(EXIT_CFG.get("tp_dynamic",{}).get("trend",{}), "tp1_atr", 2.0)
TP2_ATR_TREND       = _v(EXIT_CFG.get("tp_dynamic",{}).get("trend",{}), "tp2_atr", 3.0)
TP3_ATR_TREND       = _v(EXIT_CFG.get("tp_dynamic",{}).get("trend",{}), "tp3_atr", 4.0)
TREND_MAX_TP_PCT    = _v(EXIT_CFG.get("tp_dynamic",{}).get("trend",{}), "expand_pct_max", 0.35)

# ─── 震荡出场参数 ─────────────────────────
RANGE_MIN_ATR       = _v(EXIT_CFG.get("trailing_stop",{}).get("range",{}), "min_profit_atr", 0.8)
RANGE_TRAIL_ATR     = _v(EXIT_CFG.get("trailing_stop",{}).get("range",{}), "trail_distance_atr", 0.6)
RANGE_LOCK_ATR      = _v(EXIT_CFG.get("trailing_stop",{}).get("range",{}), "lock_atr", 0.5)
RANGE_PARTIAL1_ATR  = _v(EXIT_CFG.get("partial_close",{}).get("range",{}), "partial1_atr", 1.0)
RANGE_PARTIAL1_PCT  = _v(EXIT_CFG.get("partial_close",{}).get("range",{}), "partial1_pct", 0.50)
RANGE_PARTIAL2_ATR  = _v(EXIT_CFG.get("partial_close",{}).get("range",{}), "partial2_atr", 1.5)
RANGE_PARTIAL2_PCT  = _v(EXIT_CFG.get("partial_close",{}).get("range",{}), "partial2_pct", 0.50)
RANGE_TIME_EXIT     = _v(EXIT_CFG.get("time_exit",{}).get("range",{}), "max_hold_secs", 3600)

# ─── 保本止损 ──────────────────────────
BREAKEVEN_MODE      = _v(EXIT_CFG.get("break_even",{}), "enabled", True)
BREAKEVEN_TRIG      = _v(EXIT_CFG.get("break_even",{}), "trigger_atr", 1.0)
BREAKEVEN_LOCK_ATR  = _v(EXIT_CFG.get("break_even",{}), "lock_atr", 0.5)

# ─── RSI 出场阈值 ─────────────────────
RSI_EXIT_LONG       = _v(EXIT_CFG.get("rsi_exit",{}).get("trend",{}), "rsi_long_exit", 80)
RSI_EXIT_SHORT      = _v(EXIT_CFG.get("rsi_exit",{}).get("trend",{}), "rsi_short_exit", 20)
RSI_CHECK_ACTIVE   = True

# ============ 辅助函数 ============

def _load_json(path, default=None):
    if not path.exists():
        return default or {}
    try:
        return json.loads(path.read_text())
    except:
        return default or {}

def _now():
    return datetime.now().strftime("%Y-%m-%dT%H:%M:%S+08:00")

def _atr_value():
    """从 parallel_analysis 或 strategy 读取最新 ATR"""
    try:
        # 尝试从 parallel_analysis_latest 读取
        f = ROOT / "logs" / "parallel_analysis_latest.json"
        if f.exists():
            d = json.loads(f.read_text())
            tech = d.get("results", {}).get("technical", {})
            return tech.get("indicators", {}).get("atr14")
    except:
        pass
    # 备用：从 strategy_stats 读取
    try:
        f = ROOT / "logs" / "strategy_stats.ndjson"
        if f.exists():
            lines = f.read_text().strip().split("\n")
            if lines:
                last = json.loads(lines[-1])
                return last.get("atr", {}).get("atr14")
    except:
        pass
    return None

def _read_state():
    s = _load_json(STATE_FILE)
    if not s:
        return {}
    return s

def _read_position():
    """从 MT5 terminal_status 读取持仓"""
    MT5_COMMON = pathlib.Path.home() / "Library/Application Support/net.metaquotes.wine.metatrader5/drive_c/users/user/AppData/Roaming/MetaQuotes/Terminal/Common/Files"
    f = MT5_COMMON / "gold_autotrade/bridge/terminal_status.json"
    if not f.exists():
        return {}
    try:
        d = json.loads(f.read_text())
        positions = d.get("positions", [])
        return positions[0] if positions else {}
    except:
        return {}

# ============ 峰值价格追踪（持久化）============
def _load_peak(side, entry):
    """读取持仓以来的峰值价格"""
    try:
        if PEAK_FILE.exists():
            d = json.loads(PEAK_FILE.read_text())
            # 只复用同向的峰值
            if d.get("side") == side and abs(d.get("entry", 0) - entry) < 1.0:
                return d.get("peak", entry)
    except Exception:
        pass
    return entry

def _save_peak(side, entry, peak):
    """保存当前峰值价格"""
    try:
        PEAK_FILE.write_text(json.dumps({"side": side, "entry": entry, "peak": peak, "saved_at": time.time()}))
    except Exception:
        pass

# ============ 核心计算函数 ============

def calc_exit_signals(state):
    """
    根据持仓状态计算所有出场信号。
    移动止损逻辑：
    1. 先追踪持仓以来的最高/最低价（peak）
    2. 盈利超过门槛后，SL 跟随 peak 下移
    3. SL 永远不低于开仓价（保本）
    """
    pos_open = state.get("position_open", False)
    if not pos_open:
        # 无持仓 → 清除峰值记录
        try:
            PEAK_FILE.unlink(missing_ok=True)
        except Exception:
            pass
        return {"action": "NO_POSITION"}

    side    = state.get("position_side", "BUY")
    entry   = state.get("position_entry", 0)
    sl      = state.get("position_sl", 0)
    tp      = state.get("position_tp", 0)
    vol     = state.get("position_vol", 0.01)
    open_time_str = state.get("position_open_time", "")
    spread  = state.get("spread", 0)

    atr = _atr_value()
    if not atr or atr == 0:
        atr = 10.0  # 默认 ATR（黄金约10美元）

    # 当前价格
    MT5_COMMON = pathlib.Path.home() / "Library/Application Support/net.metaquotes.wine.metatrader5/drive_c/users/user/AppData/Roaming/MetaQuotes/Terminal/Common/Files"
    try:
        f = MT5_COMMON / "gold_autotrade/bridge/terminal_status.json"
        d = json.loads(f.read_text())
        positions = d.get("positions", [])
        if positions:
            p = positions[0]
            bid = p.get("bid", 0)
            ask = p.get("ask", 0)
        else:
            bid = ask = 0
    except:
        bid = ask = 0

    if not bid or not ask:
        return {"action": "HOLD", "reason": "无法获取价格"}

    # 计算浮盈
    if side == "BUY":
        pnl_points  = round((bid - entry) * 100, 1)
        pnl_dollars = round(pnl_points * vol, 2)
        dist_to_tp  = round((tp - ask) * 100, 1)
        dist_to_sl  = round((bid - sl) * 100, 1)
        tp_pct      = round((bid - entry) / max(tp - entry, 0.001) * 100, 1)
        new_sl      = round(entry + atr * (1 - LOCKED_PROFIT), 3)  # 保本止损候选
    else:
        pnl_points  = round((entry - ask) * 100, 1)
        pnl_dollars = round(pnl_points * vol, 2)
        dist_to_tp  = round((bid - tp) * 100, 1)
        dist_to_sl  = round((sl - ask) * 100, 1)
        tp_pct      = round((entry - ask) / max(entry - tp, 0.001) * 100, 1)
        new_sl      = round(entry - atr * (1 - LOCKED_PROFIT), 3)  # 保本止损候选

    profit_atr = abs(pnl_points) / atr  # 盈利多少倍 ATR

    result = {
        "action": "HOLD",
        "atr": atr,
        "bid": bid,
        "ask": ask,
        "entry": entry,
        "sl": sl,
        "tp": tp,
        "side": side,
        "pnl_points": pnl_points,
        "pnl_dollars": pnl_dollars,
        "profit_atr": round(profit_atr, 2),
        "tp_pct": tp_pct,
        "dist_to_tp": dist_to_tp,
        "dist_to_sl": dist_to_sl,
        "signals": [],
        "recommended_sl": sl,   # 推荐的新止损
        "recommended_action": "HOLD",
    }

    # ── 1. SL/TP 固定触发 ──────────────
    # FIX: BUY 的 SL 触发 = bid <= sl（价格跌到止损位），TP 触发 = ask >= tp（价格涨到止盈位）
    #      SELL 的 SL 触发 = ask >= sl（价格涨到止损位），TP 触发 = bid <= tp（价格跌到止盈位）
    if side == "BUY":
        if bid <= sl:
            result["action"] = "CLOSE"
            result["recommended_action"] = "CLOSE"
            result["reason"] = f"SL触发 bid={bid}<=sl={sl}"
            result["signals"].append({"type": "SL", "triggered": True})
        elif ask >= tp:
            result["action"] = "CLOSE"
            result["recommended_action"] = "CLOSE"
            result["reason"] = f"TP触发 ask={ask}>=tp={tp}"
            result["signals"].append({"type": "TP", "triggered": True})
    else:
        if ask >= sl:
            result["action"] = "CLOSE"
            result["recommended_action"] = "CLOSE"
            result["reason"] = f"SL触发 ask={ask}>=sl={sl}"
            result["signals"].append({"type": "SL", "triggered": True})
        elif bid <= tp:
            result["action"] = "CLOSE"
            result["recommended_action"] = "CLOSE"
            result["reason"] = f"TP触发 bid={bid}<=tp={tp}"
            result["signals"].append({"type": "TP", "triggered": True})

    if result["action"] == "CLOSE":
        return result

    # ── 2. 判断市场格局 ─────────────────────
    adx_val = 0
    try:
        f = ROOT / "logs" / "parallel_analysis_latest.json"
        if f.exists():
            d = json.loads(f.read_text())
            adx_val = d.get("results", {}).get("technical", {}).get("indicators", {}).get("adx", 0)
    except:
        pass
    is_trending = adx_val >= 25

    # 根据市场格局选择参数
    if is_trending:
        min_profit   = TREND_MIN_ATR
        trail_offset = TREND_TRAIL_ATR
        lock_atr     = TREND_LOCK_ATR
        partial1_atr = TREND_PARTIAL1_ATR
        partial1_pct = TREND_PARTIAL1_PCT
        partial2_atr = TREND_PARTIAL2_ATR
        partial2_pct = TREND_PARTIAL2_PCT
        hold_atr     = TREND_HOLD_ATR
        tp1_atr      = TP1_ATR_TREND
        tp2_atr      = TP2_ATR_TREND
        tp3_atr      = TP3_ATR_TREND
        max_tp_pct   = TREND_MAX_TP_PCT
        time_exit    = 0  # 趋势市场不强制时间止损
        market_type  = "TRENDING"
    else:
        min_profit   = RANGE_MIN_ATR
        trail_offset = RANGE_TRAIL_ATR
        lock_atr     = RANGE_LOCK_ATR
        partial1_atr = RANGE_PARTIAL1_ATR
        partial1_pct = RANGE_PARTIAL1_PCT
        partial2_atr = RANGE_PARTIAL2_ATR
        partial2_pct = RANGE_PARTIAL2_PCT
        hold_atr     = 999  # 震荡市场不保留仓位
        tp1_atr      = partial1_atr
        tp2_atr      = partial2_atr
        tp3_atr      = 0
        max_tp_pct   = 0.15
        time_exit    = RANGE_TIME_EXIT
        market_type  = "RANGING"

    result["market_type"] = market_type
    result["adx"] = adx_val

    # ── 3. 保本止损（Break-Even）─────────
    if BREAKEVEN_MODE and profit_atr >= BREAKEVEN_TRIG:
        be_sl = round(entry + atr * (1 - lock_atr) if side == "BUY"
                      else entry - atr * (1 - lock_atr), 3)
        if side == "BUY":
            if be_sl > sl:
                result["recommended_sl"] = be_sl
                result["signals"].append({
                    "type": "BREAKEVEN",
                    "triggered": True,
                    "old_sl": sl, "new_sl": be_sl,
                    "profit_atr": round(profit_atr, 2),
                    "market": market_type,
                })
        else:
            if be_sl < sl:
                result["recommended_sl"] = be_sl
                result["signals"].append({
                    "type": "BREAKEVEN",
                    "triggered": True,
                    "old_sl": sl, "new_sl": be_sl,
                    "profit_atr": round(profit_atr, 2),
                    "market": market_type,
                })

    # ── 4. 移动止损（基于峰值价格）───────────────────────
    # 读取持仓以来的峰值价格
    peak = _load_peak(side, entry)

    # 更新峰值
    if side == "BUY":
        new_peak = max(peak, bid)
    else:
        new_peak = min(peak, ask)

    if new_peak != peak:
        _save_peak(side, entry, new_peak)
        peak = new_peak

    # 根据市场格局选择门槛和保护距离（点数）
    if is_trending:
        min_profit_pts = TRAIL_TREND_MIN_PROFIT
        trail_dist     = TRAIL_TREND_DISTANCE
    else:
        min_profit_pts = TRAIL_RANGE_MIN_PROFIT
        trail_dist     = TRAIL_RANGE_DISTANCE

    # 盈利点数
    if side == "BUY":
        profit_pts = (peak - entry) * 100
    else:
        profit_pts = (entry - peak) * 100

    # 满足盈利门槛后激活追踪止损
    if profit_pts >= min_profit_pts:
        if side == "BUY":
            # SL = 峰值 - 保护距离（点数）
            candidate_sl = round(peak - trail_dist / 100, 3)
            # SL 永远不低于开仓价（保本）
            if candidate_sl < entry:
                candidate_sl = round(entry, 3)
        else:
            candidate_sl = round(peak + trail_dist / 100, 3)
            if candidate_sl > entry:
                candidate_sl = round(entry, 3)

        # SL 只朝有利方向移动
        old_sl = result.get("recommended_sl", sl)
        moved = False
        if side == "BUY" and candidate_sl > old_sl:
            moved = True
        elif side == "SELL" and candidate_sl < old_sl:
            moved = True

        if moved:
            result["recommended_sl"] = candidate_sl
            result["signals"].append({
                "type": "TRAILING",
                "triggered": True,
                "old_sl": old_sl,
                "new_sl": candidate_sl,
                "profit_pts": round(profit_pts, 1),
                "peak": peak,
                "market": market_type,
                "note": f"[{market_type}] 移动止损 峰值={peak} 盈利={profit_pts:.0f}点 保护距离={trail_dist}点 → SL={candidate_sl}",
            })

    # ── 5. 动态调整 TP（趋势市场扩大，震荡市场收紧）───────
    if is_trending and profit_atr >= tp1_atr:
        # 趋势市场：盈利越大，TP 越高
        if profit_atr >= tp3_atr:
            new_tp_mult = tp3_atr * (1 + max_tp_pct)
        elif profit_atr >= tp2_atr:
            new_tp_mult = tp2_atr * (1 + max_tp_pct * 0.7)
        else:
            new_tp_mult = tp1_atr * (1 + max_tp_pct * 0.5)

        if side == "BUY":
            new_tp = round(entry + new_tp_mult * atr, 3)
            if new_tp > tp:
                result["signals"].append({
                    "type": "TP_EXPAND",
                    "triggered": True,
                    "old_tp": tp,
                    "new_tp": new_tp,
                    "profit_atr": round(profit_atr, 2),
                    "market": market_type,
                    "note": f"[{market_type}] TP扩大至{new_tp}（盈利{profit_atr}ATR）",
                })
                result["recommended_tp"] = new_tp
        else:
            new_tp = round(entry - new_tp_mult * atr, 3)
            if new_tp < tp:
                result["signals"].append({
                    "type": "TP_EXPAND",
                    "triggered": True,
                    "old_tp": tp,
                    "new_tp": new_tp,
                    "profit_atr": round(profit_atr, 2),
                    "market": market_type,
                    "note": f"[{market_type}] TP扩大至{new_tp}（盈利{profit_atr}ATR）",
                })
                result["recommended_tp"] = new_tp

    # ── 6. 分批平仓（趋势/震荡不同策略）───────────
    # 趋势市场：分批平，保留剩余跑趋势
    # 震荡市场：50%+50% 全部平，见好就收
    tp1_price = entry + partial1_atr * atr if side == "BUY" else entry - partial1_atr * atr
    tp2_price = entry + partial2_atr * atr if side == "BUY" else entry - partial2_atr * atr

    if side == "BUY":
        if ask >= tp1_price:
            result["signals"].append({
                "type": "PARTIAL_TP1",
                "triggered": True,
                "close_pct": partial1_pct,
                "tp_price": tp1_price,
                "profit_atr": round(profit_atr, 2),
                "market": market_type,
                "note": f"[{market_type}] 第1批平{partial1_pct*100:.0f}%（盈利{profit_atr}ATR）",
            })
        if hold_atr < 999 and ask >= entry + hold_atr * atr:
            result["signals"].append({
                "type": "PARTIAL_TP2",
                "triggered": True,
                "close_pct": partial2_pct,
                "tp_price": tp2_price,
                "profit_atr": round(profit_atr, 2),
                "market": market_type,
                "note": f"[{market_type}] 第2批平{partial2_pct*100:.0f}%，剩余跑趋势",
            })
        elif hold_atr >= 999 and ask >= tp2_price:
            # 震荡市场全平
            result["signals"].append({
                "type": "PARTIAL_TP2",
                "triggered": True,
                "close_pct": partial2_pct,
                "tp_price": tp2_price,
                "profit_atr": round(profit_atr, 2),
                "market": market_type,
                "note": f"[{market_type}] 第2批平{partial2_pct*100:.0f}%，全部平仓",
            })
    else:
        if bid <= tp1_price:
            result["signals"].append({
                "type": "PARTIAL_TP1",
                "triggered": True,
                "close_pct": partial1_pct,
                "tp_price": tp1_price,
                "profit_atr": round(profit_atr, 2),
                "market": market_type,
                "note": f"[{market_type}] 第1批平{partial1_pct*100:.0f}%（盈利{profit_atr}ATR）",
            })
        if hold_atr < 999 and bid <= entry - hold_atr * atr:
            result["signals"].append({
                "type": "PARTIAL_TP2",
                "triggered": True,
                "close_pct": partial2_pct,
                "tp_price": tp2_price,
                "profit_atr": round(profit_atr, 2),
                "market": market_type,
                "note": f"[{market_type}] 第2批平{partial2_pct*100:.0f}%，剩余跑趋势",
            })
        elif hold_atr >= 999 and bid <= tp2_price:
            result["signals"].append({
                "type": "PARTIAL_TP2",
                "triggered": True,
                "close_pct": partial2_pct,
                "tp_price": tp2_price,
                "profit_atr": round(profit_atr, 2),
                "market": market_type,
                "note": f"[{market_type}] 第2批平{partial2_pct*100:.0f}%，全部平仓",
            })

    # ── 7. 时间止损（震荡市场强制，趋势市场不做限制）────
    if time_exit > 0 and open_time_str:
        try:
            from datetime import datetime as dt
            open_time = dt.fromisoformat(open_time_str.replace("Z", "+00:00"))
            held_secs = (dt.now().astimezone() - open_time).total_seconds()
            if held_secs >= time_exit:
                result["signals"].append({
                    "type": "TIME_EXIT",
                    "triggered": True,
                    "held_secs": held_secs,
                    "limit_secs": time_exit,
                    "market": market_type,
                })
                if pnl_points > 0:
                    result["recommended_action"] = "CLOSE"
                    result["reason"] = f"[{market_type}] 时间止损，持仓{int(held_secs/60)}分钟无果"
        except:
            pass

    # ── 6. RSI 极端信号 ─────────────────
    if RSI_CHECK_ACTIVE:
        try:
            f = ROOT / "logs" / "parallel_analysis_latest.json"
            if f.exists():
                d = json.loads(f.read_text())
                rsi = d.get("results", {}).get("technical", {}).get("indicators", {}).get("rsi14", 50)
                if side == "BUY" and rsi >= RSI_EXIT_LONG:
                    result["signals"].append({
                        "type": "RSI_EXTREME",
                        "triggered": True,
                        "rsi": rsi,
                        "note": f"RSI={rsi} 超买区域，多头考虑平仓",
                    })
                    if profit_atr >= 0.8:  # 有利润才平
                        result["recommended_action"] = "CLOSE"
                        result["reason"] = f"RSI={rsi} 超买，锁定利润"
                elif side == "SELL" and rsi <= RSI_EXIT_SHORT:
                    result["signals"].append({
                        "type": "RSI_EXTREME",
                        "triggered": True,
                        "rsi": rsi,
                        "note": f"RSI={rsi} 超卖区域，空头考虑平仓",
                    })
                    if profit_atr >= 0.8:
                        result["recommended_action"] = "CLOSE"
                        result["reason"] = f"RSI={rsi} 超卖，锁定利润"
        except:
            pass

    return result

# ============ 主逻辑 ============

def generate_exit_params():
    """
    将出场信号写入 params_suggestion.json。
    gold_trader.py 下次轮询读取。
    """
    state = _read_state()
    result = calc_exit_signals(state)

    if result.get("action") == "NO_POSITION":
        return None
    if result.get("action") == "HOLD" and not result.get("signals"):
        return None  # 无信号，不写

    suggestions = []
    sigs = result.get("signals", [])

    # 保本止损
    be_sig = next((s for s in sigs if s["type"] == "BREAKEVEN"), None)
    if be_sig:
        suggestions.append({
            "id": "EXIT_BREAKEVEN",
            "param": "suggested_sl",
            "category": "exit",
            "current_value": result["sl"],
            "suggested_value": be_sig["new_sl"],
            "reason": f"保本止损：盈利{result['profit_atr']}×ATR[{result.get('market_type','?')}]，移至{be_sig['new_sl']}",
            "trigger_evidence": {"type": "BREAKEVEN", "profit_atr": result["profit_atr"], "market": result.get("market_type")},
            "priority": "HIGH",
            "auto_apply": True,
            "m2_review_required": False,
        })

    # 移动止损
    trail_sig = next((s for s in sigs if s["type"] == "TRAILING"), None)
    if trail_sig:
        suggestions.append({
            "id": "EXIT_TRAILING",
            "param": "suggested_sl",
            "category": "exit",
            "current_value": result["recommended_sl"],
            "suggested_value": trail_sig["new_sl"],
            "reason": f"动态追踪：盈利{result['profit_atr']}×ATR[{result.get('market_type','?')}]，SL追至{trail_sig['new_sl']} | {trail_sig.get('note','')}",
            "trigger_evidence": {"type": "TRAILING", "profit_atr": result["profit_atr"], "market": result.get("market_type"), "new_sl": trail_sig["new_sl"]},
            "priority": "HIGH",
            "auto_apply": True,
            "m2_review_required": False,
        })

    # TP 动态扩大（趋势市场）
    tp_sig = next((s for s in sigs if s["type"] == "TP_EXPAND"), None)
    if tp_sig:
        suggestions.append({
            "id": "EXIT_TP_EXPAND",
            "param": "suggested_tp",
            "category": "exit",
            "current_value": result.get("tp", 0),
            "suggested_value": tp_sig["new_tp"],
            "reason": f"TP扩大：盈利{result['profit_atr']}×ATR[{result.get('market_type','?')}]，TP {tp_sig['old_tp']}→{tp_sig['new_tp']}",
            "trigger_evidence": {"type": "TP_EXPAND", "profit_atr": result["profit_atr"], "market": result.get("market_type"), "new_tp": tp_sig["new_tp"]},
            "priority": "HIGH",
            "auto_apply": True,
            "m2_review_required": False,
        })

    # 分批平仓
    for tp_sig in [s for s in sigs if s["type"] in ("PARTIAL_TP1", "PARTIAL_TP2")]:
        pct = tp_sig["close_pct"]
        suggestions.append({
            "id": f"EXIT_{tp_sig['type']}",
            "param": "partial_close",
            "category": "exit",
            "current_value": 0,
            "suggested_value": pct,
            "reason": f"{tp_sig['type']}：{tp_sig.get('note','')}，平{pct*100:.0f}%仓位",
            "trigger_evidence": {"type": tp_sig["type"], "tp_price": tp_sig["tp_price"], "pct": pct, "market": result.get("market_type")},
            "priority": "HIGH",
            "auto_apply": True,
            "m2_review_required": False,
        })

    # 强制平仓
    close_sig = next((s for s in sigs if s["type"] in ("TIME_EXIT", "RSI_EXTREME") and s.get("triggered")), None)
    if close_sig:
        suggestions.append({
            "id": f"EXIT_{close_sig['type']}",
            "param": "force_close",
            "category": "exit",
            "current_value": False,
            "suggested_value": True,
            "reason": f"{close_sig.get('note', close_sig.get('reason', ''))}",
            "trigger_evidence": {**close_sig, "market": result.get("market_type")},
            "priority": "CRITICAL",
            "auto_apply": True,
            "m2_review_required": False,
        })

    if not suggestions:
        return None

    report = {
        "timestamp": _now(),
        "source": "exit_strategy.py",
        "exit_analysis": {
            "action": result["action"],
            "reason": result.get("reason", ""),
            "pnl_points": result.get("pnl_points"),
            "pnl_dollars": result.get("pnl_dollars"),
            "profit_atr": result.get("profit_atr"),
            "recommended_sl": result.get("recommended_sl"),
            "recommended_action": result.get("recommended_action"),
            "atr": result.get("atr"),
            "signals": result.get("signals", []),
        },
        "suggestions": suggestions,
        "summary": {
            "total": len(suggestions),
            "auto_apply": sum(1 for s in suggestions if s["auto_apply"]),
            "signals_summary": [s["type"] for s in sigs],
        },
    }

    PARAMS_FILE.write_text(json.dumps(report, indent=2, ensure_ascii=False))
    return report

def main():
    """
    已废弃的独立出场策略计算入口。

    原因：
    1. STATE_FILE 路径与 gold_trader.py 的 DATA_DIR/state/ 不一致，导致状态错位
    2. params_suggestion.json 输出无人消费（config_manager.py AI 建议通道已注释）
    3. gold_trader.py 已直接内联调用 exit_optimizer.py（实时、无状态错位）

    请使用 bridge/exit_optimizer.py（由 gold_trader.py 高频监控循环直接调用）。
    """
    print("[exit_strategy] ⚠️ 已废弃 — 请使用 bridge/exit_optimizer.py")
    print("[exit_strategy] gold_trader.py 通过 exit_optimizer.py 实时管理所有出场逻辑")
    print("[exit_strategy] params_suggestion.json 输出通道已被禁用（无人读取）")
    sys.exit(0)

if __name__ == "__main__":
    main()
