#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 沃社区 · 量化工具箱 —— 多币种动量轮动(组合策略)
# 依赖: pip install 无(仅标准库); 需要同目录 data.py 或自行替换取数
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""策略7: 多币种动量轮动 (组合策略)

思路: 每 K 根重新计算各币种最近 N 根涨幅, 持有动量最强且为正的币种;
      全部动量转负时持币观望(空仓)。这是加密市场长期有效的经典策略。
"""
import data
import engine

SYMBOLS = ["BTCUSDT", "ETHUSDT", "SOLUSDT"]
LOOKBACK = 30       # 动量回看根数
REBAL = 7           # 调仓间隔(根)
RISK = 0.03         # 单币仓位(占总权益比例)


def momentum(closes, i, n):
    if i < n:
        return None
    return closes[i] / closes[i - n] - 1


def run(interval="1d", start="2019-01-01", end="2026-09-14", top=1, rebal=REBAL, lookback=LOOKBACK, risk=RISK):
    series = {s: data.fetch_klines(s, interval, start, end) for s in SYMBOLS}
    # 以 BTC 的时间轴为基准
    base = series["BTCUSDT"]
    idx = {s: {b["t"]: k for k, b in enumerate(series[s])} for s in SYMBOLS}
    closes = {s: [b["c"] for b in series[s]] for s in SYMBOLS}
    highs = {s: [b["h"] for b in series[s]] for s in SYMBOLS}
    lows = {s: [b["l"] for b in series[s]] for s in SYMBOLS}
    op = {s: [b["o"] for b in series[s]] for s in SYMBOLS}

    capital = 10000.0
    cash = capital
    holds = {}          # symbol -> qty
    entry = {}
    last_rebal = -10 ** 9
    equity_curve = []
    trades = []

    for i, b in enumerate(base):
        t = b["t"]
        # 每日结算权益
        eq = cash
        for s, q in holds.items():
            k = idx[s].get(t)
            if k is not None:
                eq += q * (closes[s][k] - entry[s])
        # 调仓
        if i - last_rebal >= rebal:
            ranks = []
            for s in SYMBOLS:
                k = idx[s].get(t)
                if k is None:
                    continue
                m = momentum(closes[s], k, lookback)
                if m is not None:
                    ranks.append((m, s, k))
            ranks.sort(reverse=True)
            want = {r[1]: r[2] for r in ranks[:top] if r[0] > 0}
            # 平掉不在目标里的
            for s in list(holds):
                if s not in want:
                    k = idx[s].get(t) or idx[s][list(idx[s])[-1]]
                    px = closes[s][k]
                    pnl = holds[s] * (px - entry[s]) - abs(holds[s]) * px * 0.0003
                    cash += holds[s] * (px - entry[s]) - abs(holds[s]) * px * 0.0003
                    trades.append({"side": "多", "symbol": s, "entry": entry[s], "exit": px,
                                   "pnl": pnl, "t": t})
                    del holds[s]
            # 建仓/调仓
            for s, k in want.items():
                px = closes[s][k]
                target = cash * risk / px if s not in holds else holds[s]
                if s not in holds:
                    q = cash * risk / px
                    cash -= q * px * 0.0003
                    holds[s] = q
                    entry[s] = px
                else:
                    # 简单再平衡: 保持数量不动(避免过度交易)
                    pass
            last_rebal = i
        equity_curve.append(eq)

    eqs = [capital] + equity_curve
    peak, mdd = eqs[0], 0.0
    for v in eqs:
        peak = max(peak, v)
        mdd = max(mdd, (peak - v) / peak if peak > 0 else 0)
    days = (base[-1]["t"] - base[0]["t"]) / 86400000.0
    wins = [t for t in trades if t["pnl"] > 0]
    return {"final": eqs[-1], "total_return": eqs[-1] / capital - 1, "max_drawdown": -mdd,
            "trades": len(trades), "win_rate": len(wins) / len(trades) if trades else 0,
            "days": days, "cagr": (eqs[-1] / capital) ** (365 / days) - 1 if days else 0,
            "equity": eqs[:: max(1, len(eqs) // 300)]}


if __name__ == "__main__":
    print("=== 多币种动量轮动 (日线) ===")
    for top, risk in ((1, 0.5), (1, 1.0), (2, 0.5), (2, 0.35), (3, 0.33)):
        for rebal in (7,):
            r = run(top=top, rebal=rebal, risk=risk)
            print("  持%d个 单币%.0f%%权益 调仓%d根: 总收益 %8.1f%% | 年化 %6.1f%% | 回撤 %6.1f%% | 交易 %3d | 胜率 %3.0f%%" % (
                top, risk * 100, rebal, r["total_return"] * 100, r["cagr"] * 100,
                r["max_drawdown"] * 100, r["trades"], r["win_rate"] * 100))
