#!/usr/bin/env python3
"""
定时任务：将最近 N 天的交易数据聚合到 trade_stats_daily / trade_stats_time_bucket。
优先从 binance_trades 读取（需先跑 sync_binance_orders.py），无数据时用 trades 表。

用法:
  python scripts/aggregate_trade_stats.py           # 默认 7 天、默认账号
  python scripts/aggregate_trade_stats.py -d 30    # 最近 30 天
  python scripts/aggregate_trade_stats.py -a 2     # 指定账号
"""
import argparse
import sys
from pathlib import Path

proj = Path(__file__).resolve().parent.parent
if (proj / "backend").exists():
    sys.path.insert(0, str(proj / "backend"))
sys.path.insert(0, str(proj))


def main():
    parser = argparse.ArgumentParser(description="聚合交易统计到 trade_stats_* 表")
    parser.add_argument("-a", "--account", type=int, default=None, help="账号 ID，不传则用默认")
    parser.add_argument("-d", "--days", type=int, default=7, help="聚合最近 N 天，默认 7")
    args = parser.parse_args()
    if args.days <= 0:
        print("days 须 > 0")
        sys.exit(1)
    try:
        from datetime import datetime, timezone
        from database.models import TradeStats, GlobalStrategyConfig, TradingConfig

        # 1) 先按账号聚合到 trade_stats_* 表（供仪表盘等使用）
        TradeStats.aggregate_recent_days(days=args.days, account_id=args.account)
        print(f"已聚合最近 {args.days} 天统计 (account_id={args.account or 'default'})")

        # 2) 再基于 trade_stats_* 做一次【全局】评估：按交易对 / 按小时生成软黑名单与时段过滤建议
        days = max(1, int(args.days))
        symbol_rows = TradeStats.get_global_symbol_stats(days=days)
        hour_rows = TradeStats.get_global_hourly_stats(days=days)

        # 规则参数（后续可改成全局配置，这里先写死一版直观规则）：
        min_trades_for_symbol = 5         # 至少 N 笔才纳入评估，避免样本太少
        min_trades_for_hour = 10          # 小时至少 N 笔才纳入评估
        blacklist_pnl_threshold = 0.0     # 净盈亏 < 0 视为表现不佳（软黑名单）
        hard_blacklist_pnl_threshold = -3.0  # 净亏绝对值较大时考虑升级为硬黑名单
        hard_blacklist_min_trades = 10       # 硬黑名单至少需要的样本数
        hard_blacklist_max_win_rate = 10.0   # 胜率≤10% 且净亏较大时视为严重拖累
        good_hour_pnl_threshold = 0.0        # 净盈亏 > 0 视为表现较好

        symbol_blacklist = []
        symbol_whitelist = []
        for r in symbol_rows or []:
            try:
                sym = (r.get("symbol") or "").strip()
                tc = int(r.get("trade_count") or 0)
                net_pnl = float(r.get("net_pnl") or 0.0)
                win = int(r.get("win_count") or 0)
                loss = int(r.get("loss_count") or 0)
            except Exception:
                continue
            if not sym or tc < min_trades_for_symbol:
                continue
            total = max(1, win + loss)
            win_rate = 100.0 * win / total

            # 黑名单：近期净亏 + 有一定笔数 -> 降权或升级为硬黑名单
            if net_pnl < blacklist_pnl_threshold:
                mode = "reduced"
                if (
                    net_pnl <= hard_blacklist_pnl_threshold
                    and tc >= hard_blacklist_min_trades
                    and win_rate <= hard_blacklist_max_win_rate
                ):
                    mode = "hard"
                symbol_blacklist.append(
                    {
                        "symbol": sym,
                        "trade_count": tc,
                        "net_pnl": round(net_pnl, 4),
                        "win_rate_pct": round(win_rate, 1),
                        "mode": mode,  # reduced=软黑名单（降权+提门槛）；hard=硬黑名单（自动不再开仓）
                    }
                )
            # 白名单：近期净盈且胜率尚可
            elif net_pnl > 0 and win_rate >= 50.0:
                symbol_whitelist.append(
                    {
                        "symbol": sym,
                        "trade_count": tc,
                        "net_pnl": round(net_pnl, 4),
                        "win_rate_pct": round(win_rate, 1),
                    }
                )

        hour_filters = []
        for r in hour_rows or []:
            try:
                h = int(r.get("hour"))
                tc = int(r.get("trade_count") or 0)
                net_pnl = float(r.get("net_pnl") or 0.0)
            except Exception:
                continue
            if h < 0 or h > 23:
                continue
            if tc < min_trades_for_hour:
                bucket = "unknown"
            elif net_pnl > good_hour_pnl_threshold:
                bucket = "good"
            elif net_pnl < -good_hour_pnl_threshold:
                bucket = "bad"
            else:
                bucket = "neutral"

            # 约定：bad 时段默认 0.5 仓位系数 + 信号门槛+1；good 时段略微放宽（可视需求调整）
            if bucket == "bad":
                position_factor = 0.5
                signal_boost = 1
            elif bucket == "good":
                position_factor = 1.1
                signal_boost = 0
            else:
                position_factor = 1.0
                signal_boost = 0

            hour_filters.append(
                {
                    "hour": h,
                    "bucket": bucket,
                    "trade_count": tc,
                    "net_pnl": round(net_pnl, 4),
                    "position_factor": position_factor,
                    "signal_boost": signal_boost,
                }
            )

        now_iso = datetime.now(timezone.utc).isoformat()
        symbol_payload = {
            "generated_at": now_iso,
            "days": days,
            "min_trades": min_trades_for_symbol,
            "blacklist": sorted(symbol_blacklist, key=lambda x: (x["net_pnl"], -x["trade_count"])),
            "whitelist": sorted(symbol_whitelist, key=lambda x: (-x["net_pnl"], -x["trade_count"])),
        }
        hour_payload = {
            "generated_at": now_iso,
            "days": days,
            "min_trades": min_trades_for_hour,
            "hours": sorted(hour_filters, key=lambda x: x["hour"]),
        }

        # 写入全局策略配置（json），供交易进程 & 前端全局配置页读取
        GlobalStrategyConfig.set(
            "STATS_SYMBOL_FILTERS",
            symbol_payload,
            "json",
            "stats_filters",
            description="基于最近 N 天交易统计自动生成的 symbol 白/黑名单（软降权），请勿手动修改。",
        )
        GlobalStrategyConfig.set(
            "STATS_HOUR_FILTERS",
            hour_payload,
            "json",
            "stats_filters",
            description="基于最近 N 天交易统计自动生成的按小时时段过滤建议，请勿手动修改。",
        )
        print(f"已更新全局统计过滤配置：{len(symbol_blacklist)} 个黑名单 symbol，{len(symbol_whitelist)} 个白名单 symbol。")
    except Exception as e:
        print(f"聚合失败: {e}")
        sys.exit(1)


if __name__ == "__main__":
    main()
