"""
Strategy Evolution Engine - 自动策略进化
每周分析交易账本，发现规律，调整策略参数
完全自动化，不需要人工干预
"""
import json, pathlib, subprocess
from datetime import datetime, timezone, timedelta
from collections import defaultdict

PROJECT_ROOT = pathlib.Path(__file__).parent.parent.absolute()
LOG_DIR = PROJECT_ROOT / "logs"
CONFIG_DIR = PROJECT_ROOT / "config"
BACKUP_DIR = CONFIG_DIR / "backups"

class StrategyEvolution:
    def __init__(self):
        BACKUP_DIR.mkdir(parents=True, exist_ok=True)

    # ============ 数据读取 ============
    def load_trading_ledger(self, days=7):
        """读取交易账本"""
        ledger = LOG_DIR / "trading_ledger.ndjson"
        if not ledger.exists(): return []
        records = []
        cutoff = datetime.now(timezone.utc) - timedelta(days=days)
        for line in open(ledger):
            line = line.strip()
            if not line: continue
            try:
                r = json.loads(line)
                ts = datetime.fromisoformat(r.get("ts","").replace("Z","+00:00"))
                if ts >= cutoff: records.append(r)
            except: pass
        return records

    def load_signal_ledger(self, days=7):
        """读取信号账本"""
        ledger = LOG_DIR / "signal_ledger.ndjson"
        if not ledger.exists(): return []
        records = []
        cutoff = datetime.now(timezone.utc) - timedelta(days=days)
        for line in open(ledger):
            line = line.strip()
            if not line: continue
            try:
                r = json.loads(line)
                ts = datetime.fromisoformat(r.get("ts","").replace("Z","+00:00"))
                if ts >= cutoff: records.append(r)
            except: pass
        return records

    def load_risk_events(self, days=7):
        """读取风险事件"""
        ledger = LOG_DIR / "risk_events.ndjson"
        if not ledger.exists(): return []
        records = []
        cutoff = datetime.now(timezone.utc) - timedelta(days=days)
        for line in open(ledger):
            line = line.strip()
            if not line: continue
            try:
                r = json.loads(line)
                ts = datetime.fromisoformat(r.get("ts","").replace("Z","+00:00"))
                if ts >= cutoff: records.append(r)
            except: pass
        return records

    # ============ 策略参数配置 ============
    def load_strategy_config(self):
        """读取当前策略配置"""
        cfg = CONFIG_DIR / "strategy_rules_v3.json"
        if cfg.exists():
            return json.loads(cfg.read_text())
        # 默认配置
        return {
            "trend": {
                "ema_confluence_weight": 0.75,
                "min_adx": 25,
                "rsi_overbought": 70,
                "rsi_oversold": 30,
            },
            "range": {
                "bb_std_dev": 2.0,
                "rsi_oversold": 35,
                "rsi_overbought": 65,
            },
            "exit": {
                "tp_coverage_exit": 0.85,  # TP覆盖85%时考虑早退
                "momentum_divergence_conf": 0.80,
                "partial_close_threshold": 0.70,  # TP覆盖70%部分平仓
            },
            "risk": {
                "max_consecutive_losses": 4,
                "max_daily_trades": 10,
                "min_confidence": 0.68,
            }
        }

    def save_strategy_config(self, config, reason=""):
        """保存策略配置并备份"""
        ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M")
        backup = BACKUP_DIR / f"strategy_{ts}.json"
        current = CONFIG_DIR / "strategy_rules_v3.json"

        # 备份当前配置
        if current.exists():
            current.write_text(json.dumps(config, indent=2))
        backup.write_text(json.dumps(config, indent=2))

        # 记录变更日志
        log = LOG_DIR / "strategy_evolution_log.jsonl"
        with open(log, "a") as f:
            f.write(json.dumps({
                "ts": datetime.now(timezone.utc).isoformat(),
                "reason": reason,
                "changes": config.get("_last_changes", []),
            }, ensure_ascii=False) + "\n")

        return backup

    # ============ 分析引擎 ============
    def analyze(self, trades, signals, risk_events):
        """综合分析，输出优化建议"""
        suggestions = []

        if not trades:
            return {"suggestions": [], "summary": "无足够数据进行分析"}

        # ===== 1. 胜率和盈亏分析 =====
        total_trades = len(trades)
        wins = [t for t in trades if t.get("pnl", 0) > 0]
        losses = [t for t in trades if t.get("pnl", 0) <= 0]
        win_rate = len(wins) / max(total_trades, 1)
        total_pnl = sum(t.get("pnl", 0) for t in trades)
        avg_win = sum(t.get("pnl", 0) for t in wins) / max(len(wins), 1)
        avg_loss = abs(sum(t.get("pnl", 0) for t in losses) / max(len(losses), 1))
        profit_factor = avg_win / max(avg_loss, 0.01)

        # ===== 2. TP vs SL 触发分析 =====
        tp_hits = sum(1 for t in trades if t.get("tp_hit"))
        sl_hits = sum(1 for t in trades if t.get("sl_hit"))
        tp_rate = tp_hits / max(total_trades, 1)

        if tp_rate < 0.3:
            suggestions.append({
                "type": "EXIT_OPTIMIZE",
                "priority": "high",
                "field": "exit.tp_coverage_exit",
                "current": 0.85,
                "suggested": 0.75,
                "reason": f"TP命中率仅{tp_rate:.0%}，建议降低TP目标（85%→75%），让利润更早落袋",
            })
        elif tp_rate > 0.8:
            suggestions.append({
                "type": "EXIT_OPTIMIZE",
                "priority": "medium",
                "field": "exit.tp_coverage_exit",
                "current": 0.85,
                "suggested": 0.90,
                "reason": f"TP命中率{tp_rate:.0%}，TP设置保守，可以适当拉远TP追求更大利润",
            })

        # ===== 3. RSI 极端区域表现 =====
        # 统计 RSI > 70 和 RSI < 30 时的胜率
        high_rsi_signals = [s for s in signals if s.get("indicators", {}).get("rsi14", 50) > 65]
        low_rsi_signals = [s for s in signals if s.get("indicators", {}).get("rsi14", 50) < 35]

        # RSI 超买区间的胜率
        high_rsi_wins = sum(1 for t in trades if t.get("regime") == "RANGING" and t.get("side") == "SELL")
        if len(high_rsi_signals) > 3:
            suggestions.append({
                "type": "ENTRY_OPTIMIZE",
                "priority": "medium",
                "field": "range.rsi_overbought",
                "current": 65,
                "suggested": 70,
                "reason": f"RSI超买区间({'>65'})入场{len(high_rsi_signals)}次，建议提高到70减少假信号",
            })

        # ===== 4. 置信度分析 =====
        if signals:
            avg_conf = sum(s.get("confidence", 0) for s in signals) / len(signals)
            low_conf_approved = sum(1 for s in signals if s.get("confidence", 0) < 0.68 and s.get("result") == "FILLED")
            if low_conf_approved > 2:
                suggestions.append({
                    "type": "CONFIDENCE_ADJUST",
                    "priority": "medium",
                    "field": "risk.min_confidence",
                    "current": 0.68,
                    "suggested": 0.72,
                    "reason": f"{low_conf_approved}次低置信度信号被批准后亏损，建议提高最低置信度门槛",
                })

        # ===== 5. 连续亏损检测 =====
        if len(losses) > 2 and len(wins) > 0:
            loss_streak = self._find_max_loss_streak(trades)
            if loss_streak >= 3:
                suggestions.append({
                    "type": "RISK_TIGHTEN",
                    "priority": "high",
                    "field": "risk.max_consecutive_losses",
                    "current": 4,
                    "suggested": 3,
                    "reason": f"出现{loss_streak}连亏，建议降低连亏上限触发Kill Switch",
                })

        # ===== 6. ATR 参数优化（基于真实波动）====
        if trades:
            atrs = [t.get("sl", 0) for t in trades if t.get("sl", 0) > 0]
            if atrs:
                avg_sl_dist = sum(atrs) / len(atrs)
                # 如果平均SL距离过大，说明止损太松
                if avg_sl_dist > 80:  # 假设平均止损>80点
                    suggestions.append({
                        "type": "SL_OPTIMIZE",
                        "priority": "medium",
                        "field": "trend.sl_atr_multiplier",
                        "current": 1.5,
                        "suggested": 1.2,
                        "reason": f"平均止损距离{avg_sl_dist:.0f}点过大，建议收紧止损（1.5→1.2倍ATR）",
                    })

        # ===== 7. 震荡 vs 趋势模式胜率对比 =====
        regime_wins = defaultdict(list)
        for t in trades:
            regime_wins[t.get("regime", "UNKNOWN")].append(t.get("pnl", 0))

        for regime, pnls in regime_wins.items():
            if len(pnls) >= 2:
                rw = sum(1 for p in pnls if p > 0) / max(len(pnls), 1)
                if rw < 0.3:
                    suggestions.append({
                        "type": "REGIME_DISABLE",
                        "priority": "high",
                        "field": f"regime.{regime}.enabled",
                        "current": True,
                        "suggested": False,
                        "reason": f"{regime}模式胜率仅{rw:.0%}，建议暂时禁用",
                    })

        return {
            "summary": {
                "period_days": 7,
                "total_trades": total_trades,
                "win_rate": round(win_rate, 3),
                "total_pnl": round(total_pnl, 2),
                "profit_factor": round(profit_factor, 2),
                "tp_rate": round(tp_rate, 3),
                "sl_rate": round(sl_hits / max(total_trades, 1), 3),
                "avg_win": round(avg_win, 2),
                "avg_loss": round(avg_loss, 2),
            },
            "suggestions": suggestions,
            "regime_performance": {r: {"trades": len(p), "win_rate": round(sum(1 for p in pnls if p > 0)/max(len(pnls),1), 3)}
                                   for r, pnls in regime_wins.items()},
        }

    def _find_max_loss_streak(self, trades):
        """找出最大连续亏损次数"""
        max_streak = 0
        current_streak = 0
        for t in trades:
            if t.get("pnl", 0) < 0:
                current_streak += 1
                max_streak = max(max_streak, current_streak)
            else:
                current_streak = 0
        return max_streak

    def apply_suggestions(self, suggestions):
        """应用高优先级建议到策略配置"""
        config = self.load_strategy_config()
        config["_last_changes"] = []
        applied = []

        for s in suggestions:
            if s.get("priority") != "high":
                continue

            field = s.get("field", "")
            suggested = s.get("suggested")

            # 按字段路径应用
            if field == "exit.tp_coverage_exit":
                config.setdefault("exit", {})["tp_coverage_exit"] = suggested
                applied.append(f"TP早退阈值: {s['current']}→{suggested}")
            elif field == "risk.min_confidence":
                config.setdefault("risk", {})["min_confidence"] = suggested
                applied.append(f"最低置信度: {s['current']}→{suggested}")
            elif field == "risk.max_consecutive_losses":
                config.setdefault("risk", {})["max_consecutive_losses"] = int(suggested)
                applied.append(f"连亏上限: {s['current']}→{int(suggested)}")
            elif field == "exit.partial_close_threshold":
                config.setdefault("exit", {})["partial_close_threshold"] = suggested
                applied.append(f"部分平仓阈值: {s['current']}→{suggested}")
            elif "regime." in field:
                regime = field.split(".")[1]
                config.setdefault("regime_settings", {})
                config["regime_settings"].setdefault(regime, {})
                config["regime_settings"][regime]["enabled"] = suggested
                applied.append(f"禁用{regime}模式" if not suggested else f"启用{regime}模式")

            config["_last_changes"].append({
                "field": field,
                "old": s.get("current"),
                "new": suggested,
                "reason": s.get("reason"),
            })

        if applied:
            reason = "; ".join(applied)
            self.save_strategy_config(config, reason)
            return applied
        return []

    def run_evolution(self):
        """执行完整的进化流程"""
        trades = self.load_trading_ledger(days=7)
        signals = self.load_signal_ledger(days=7)
        risk_events = self.load_risk_events(days=7)

        analysis = self.analyze(trades, signals, risk_events)

        high_priority = [s for s in analysis["suggestions"] if s.get("priority") == "high"]
        applied = self.apply_suggestions(high_priority)

        return {
            "analysis": analysis,
            "applied_changes": applied,
            "timestamp": datetime.now(timezone.utc).isoformat(),
        }

# ============ CLI ============
if __name__ == "__main__":
    import sys
    engine = StrategyEvolution()

    if len(sys.argv) > 1 and sys.argv[1] == "--apply":
        result = engine.run_evolution()
        print("=== 策略进化报告 ===")
        print(f"分析周期: 最近7天")
        s = result["analysis"]["summary"]
        print(f"总交易: {s['total_trades']} | 胜率: {s['win_rate']:.0%} | 总Pnl: ${s['total_pnl']:.2f}")
        print(f"TP命中率: {s['tp_rate']:.0%} | SL触发率: {s['sl_rate']:.0%}")
        print(f"盈亏比: {s.get('profit_factor','?')}")
        print()
        print(f"建议数量: {len(result['analysis']['suggestions'])}")
        for sug in result["analysis"]["suggestions"]:
            print(f"  [{sug['priority']}] {sug['field']}: {sug['current']}→{sug['suggested']} | {sug['reason']}")
        print()
        if result["applied_changes"]:
            print(f"已自动应用 {len(result['applied_changes'])} 项高优先级变更:")
            for c in result["applied_changes"]:
                print(f"  ✅ {c}")
        else:
            print("无高优先级变更需要自动应用")
    else:
        # 只分析，不应用
        trades = engine.load_trading_ledger(days=7)
        signals = engine.load_signal_ledger(days=7)
        risk_events = engine.load_risk_events(days=7)
        analysis = engine.analyze(trades, signals, risk_events)
        print(json.dumps(analysis, indent=2, ensure_ascii=False))
