# -*- coding: utf-8 -*-
"""传统封基: 按「年化折价率」(−折价/剩余年限)排序 vs 按折价深度排序。

老封基到期日固定 → 剩余年限精确可算, 是检验"锚点近则收敛快"的干净实验:
年化折价率高 = 折价深 且/或 剩余期短。若它跑赢纯折价深度, 说明该把钱押在
"快到期的折价"上而非"最深的折价"上。复用 cef_early_cache/。"""
import json, os
import numpy as np
import pandas as pd

ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
os.chdir(ROOT)
CACHE = "research/cef_early_cache"
COST = 0.003

fb = pd.read_csv(f"{CACHE}/fund_basic.csv", dtype=str)
trad = fb[fb["ts_code"].str.match(r"(500\d{3}\.SH|184\d{3}\.SZ)")]
trad = trad[trad["ts_code"] != "184801.SZ"]
funds = {}
for _, f in trad.iterrows():
    c = f["ts_code"]
    try:
        d = pd.read_csv(f"{CACHE}/daily_{c}.csv", dtype=str)
        n = pd.read_csv(f"{CACHE}/nav_{c}.csv", dtype=str)
    except FileNotFoundError:
        continue
    if len(d) < 60 or len(n) < 10:
        continue
    px = d.dropna().astype({"close": float}).sort_values("trade_date").set_index("trade_date")["close"]
    nav = n.dropna().astype({"unit_nav": float}).sort_values("nav_date").set_index("nav_date")["unit_nav"]
    funds[c] = {"px": px, "nav": nav, "list": f["list_date"],
                "delist": f["delist_date"] if pd.notna(f["delist_date"]) else "20991231",
                "due": f["due_date"] if pd.notna(f["due_date"]) else None}
print(f"{len(funds)} 只(含到期日 {sum(1 for f in funds.values() if f['due'])})")

alld = sorted(set().union(*[set(f["px"].index) for f in funds.values()]))
months = pd.Series(alld).groupby(pd.Series(alld).str[:6]).first().tolist()


def disc_at(c, d):
    f = funds[c]
    if d not in f["px"].index:
        return None
    navs = f["nav"][f["nav"].index <= d]
    if navs.empty or (pd.Timestamp(d) - pd.Timestamp(navs.index[-1])).days > 14:
        return None
    v = navs.iloc[-1]
    return (f["px"].loc[d] / v - 1) * 100 if v > 0 else None


def years_left(c, d):
    due = funds[c]["due"]
    if not due:
        return None
    yl = (pd.Timestamp(due) - pd.Timestamp(d)).days / 365.25
    return yl if yl > 0.02 else None


def px_at(c, d):
    f = funds[c]
    return f["px"].loc[d] if d in f["px"].index else None


def alive(c, d):
    f = funds[c]
    return f["list"] <= d < f["delist"]


def run(rank):  # rank: 'disc' | 'ann'
    rets, dates, prev = [], [], set()
    loads = []
    for i in range(len(months) - 1):
        d0, d1 = months[i], months[i + 1]
        cand = []
        for c in funds:
            if not (alive(c, d0) and alive(c, d1) and px_at(c, d0) and px_at(c, d1)):
                continue
            dc = disc_at(c, d0)
            yl = years_left(c, d0)
            if dc is None or yl is None:
                continue
            ann = -dc / yl  # 年化折价率(正=折价年化收敛收益)
            cand.append((c, dc, ann, yl))
        if len(cand) < 9:
            continue
        n = len(cand) // 3
        if rank == "disc":
            cand.sort(key=lambda x: x[1])          # 折价最深在前
            sel = set(c for c, *_ in cand[:n])
        else:
            cand.sort(key=lambda x: -x[2])         # 年化折价率最高在前
            sel = set(c for c, *_ in cand[:n])
            loads.append(np.mean([x[3] for x in cand[:n]]))  # 选中组平均剩余年限
        r = float(np.mean([px_at(c, d1) / px_at(c, d0) - 1 for c in sel]))
        r -= COST * (len(sel ^ prev) / max(len(sel), 1))
        prev = sel
        rets.append(r); dates.append(d1)
    if loads:
        print(f"  [ann组平均剩余年限 {np.mean(loads):.1f} 年]")
    return pd.Series(rets, index=dates)


def stats(s, label):
    r = s.values
    nav = np.cumprod(1 + r)
    yrs = len(r) / 12
    cagr = (nav[-1] ** (1 / yrs) - 1) * 100
    sharpe = r.mean() / r.std() * np.sqrt(12)
    mdd = ((nav / np.maximum.accumulate(nav)) - 1).min() * 100
    print(f"  {label:22s} CAGR {cagr:+6.1f}%  Sharpe {sharpe:5.2f}  MDD {mdd:6.1f}%  ({len(r)}月)")
    return dict(cagr=round(float(cagr), 1), sharpe=round(float(sharpe), 2), mdd=round(float(mdd), 1),
                nav=[round(float(x), 4) for x in nav])


print("=== 排序规则对比(2004-2017, 含0.3%/边) ===")
ann_s = run("ann")
disc_s = run("disc")
common = ann_s.index.intersection(disc_s.index)
out = {"months": list(common), "series": {}}
out["series"]["ann"] = stats(ann_s[common], "年化折价率最高1/3")
out["series"]["disc"] = stats(disc_s[common], "折价最深1/3")
diff = ann_s[common] - disc_s[common]
tt = diff.mean() / diff.std() * np.sqrt(len(diff))
print(f"\n  月均差 {diff.mean()*12*100:+.1f}pp/年  t={tt:.2f}  月胜率 {(diff>0).mean()*100:.0f}%")
out["diff"] = {"ann_pp": round(float(diff.mean() * 12 * 100), 1), "t": round(float(tt), 2),
               "win": round(float((diff > 0).mean() * 100))}
json.dump(out, open("research/cef_early_anndisc.json", "w"), ensure_ascii=False)
print("DONE")
