# -*- coding: utf-8 -*-
"""封基深折价篮子 vs 科创50/创业板指: 走势对比 + alpha 回归 + 0.5x/1x 对冲。

问题: 篮子里多数是科创板封基(持仓=科创板股票), 2023-07 以来的收益到底是
折价收敛 alpha 还是科创/创业板 beta? 对冲掉指数后还剩多少?

口径: 篮子=每月折价最深1/3等权、场内价收益(与报告/策略页同源 backtest_strategy);
指数=tushare index_daily 收盘; 月频对齐同一批月初交易日。
对冲净额外成本: A股无科创50/创业板股指期货, 实际用融券ETF(~9%/年)——净结果按
9%/年×h 扣除, 这是"真的能不能做"的口径。
"""
import json, glob, os, sys
import numpy as np
import pandas as pd

ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
os.chdir(ROOT)
D = "my-app/public/data/cef/detail"
HEDGE_COST = 0.09  # 融券年成本(index-carry 页口径 8-10% 取中)

funds = {}
for f in glob.glob(f"{D}/*.json"):
    d = json.load(open(f))
    if len(d["dates"]) < 250:
        continue
    funds[d["code"]] = pd.DataFrame({"d": d["dates"], "px": d["price"], "disc": d["disc"]}).set_index("d")
alld = sorted(set().union(*[set(s.index) for s in funds.values()]))
months = pd.Series(alld).groupby(pd.Series(alld).str[:6]).first().tolist()
print(f"{len(funds)} 只封基, {len(months)} 个月初节点 {months[0]}~{months[-1]}")

# 指数日线: 创业板指(本地缓存) + 科创50(tushare 直连)
import sqlite3
conn = sqlite3.connect("data/fundamental_cache.db")
cyb = pd.read_sql("SELECT trade_date,close FROM index_daily WHERE ts_code='399006.SZ'", conn).set_index("trade_date")["close"]
conn.close()
import tushare as ts
pro = ts.pro_api(os.environ["TUSHARE_TOKEN"], timeout=60)
pro._DataApi__token = os.environ["TUSHARE_TOKEN"]
pro._DataApi__http_url = os.environ["TUSHARE_API_URL"]
kc = pro.index_daily(ts_code="000688.SH", start_date="20230101",
                     fields="trade_date,close").set_index("trade_date")["close"].sort_index()
print(f"科创50 {len(kc)}日, 创业板指 {len(cyb)}日")


def idx_ret(series, d0, d1):
    s = series[(series.index >= d0) & (series.index <= d1)]
    if len(s) < 2:
        return None
    # 月初节点可能非指数交易日(封基停牌日历差), 取窗口首尾
    return float(s.iloc[-1] / s.iloc[0] - 1)


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


def disc(c, d):
    s = funds[c]; return s.loc[d, "disc"] if d in s.index else None


# 深折篮子月收益(与 backtest_strategy run('deep') 同口径)
rows = []
for i in range(len(months) - 1):
    d0, d1 = months[i], months[i + 1]
    cand = [(c, disc(c, d0)) for c in funds if disc(c, d0) is not None and px(c, d0) and px(c, d1)]
    if len(cand) < 9:
        continue
    cand.sort(key=lambda x: x[1])
    sel = [c for c, _ in cand[: len(cand) // 3]]
    r = float(np.mean([px(c, d1) / px(c, d0) - 1 for c in sel]))
    rk = idx_ret(kc, d0, d1)
    rc = idx_ret(cyb, d0, d1)
    if rk is None or rc is None:
        continue
    rows.append({"d0": d0, "d1": d1, "basket": r, "kc50": rk, "cyb": rc})
df = pd.DataFrame(rows)
print(f"对齐月数: {len(df)} ({df.d0.iloc[0]}~{df.d1.iloc[-1]})")


def stats(rets, label):
    rets = np.asarray(rets, dtype=float)
    nav = np.cumprod(1 + rets)
    yrs = len(rets) / 12
    cagr = (nav[-1] ** (1 / yrs) - 1) * 100
    sharpe = rets.mean() / rets.std() * np.sqrt(12) if rets.std() > 0 else 0
    mdd = ((nav / np.maximum.accumulate(nav)) - 1).min() * 100
    print(f"  {label:26s} CAGR {cagr:+6.1f}%  Sharpe {sharpe:5.2f}  MDD {mdd:6.1f}%")
    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])


def ols(y, x):
    x = np.asarray(x); y = np.asarray(y)
    beta = np.cov(y, x)[0, 1] / np.var(x)
    alpha_m = y.mean() - beta * x.mean()
    resid = y - (alpha_m + beta * x)
    se = resid.std(ddof=2) / np.sqrt(len(y)) if len(y) > 2 else np.nan
    t = alpha_m / se if se and se > 0 else np.nan
    r2 = 1 - resid.var() / y.var()
    ann_alpha = ((1 + alpha_m) ** 12 - 1) * 100
    return dict(beta=round(float(beta), 2), alpha_ann=round(float(ann_alpha), 1),
                t=round(float(t), 2), r2=round(float(r2), 2))


print("\n=== 走势与基线 ===")
out = {"months": df.d1.tolist(), "series": {}}
out["series"]["basket"] = stats(df.basket, "深折篮子(场内价)")
out["series"]["kc50"] = stats(df.kc50, "科创50")
out["series"]["cyb"] = stats(df.cyb, "创业板指")

print("\n=== 月频回归(篮子 ~ 指数) ===")
reg = {}
for k, lbl in (("kc50", "科创50"), ("cyb", "创业板指")):
    reg[k] = ols(df.basket, df[k])
    print(f"  vs {lbl}: beta {reg[k]['beta']}  年化alpha {reg[k]['alpha_ann']:+.1f}%  t={reg[k]['t']}  R²={reg[k]['r2']}")
# 双指数回归
X = np.column_stack([np.ones(len(df)), df.kc50, df.cyb])
b, *_ = np.linalg.lstsq(X, df.basket.values, rcond=None)
resid = df.basket.values - X @ b
t2 = b[0] / (resid.std(ddof=3) / np.sqrt(len(df)))
reg["both"] = {"alpha_ann": round(float(((1 + b[0]) ** 12 - 1) * 100), 1),
               "beta_kc": round(float(b[1]), 2), "beta_cyb": round(float(b[2]), 2),
               "t": round(float(t2), 2)}
print(f"  双指数: beta_kc {reg['both']['beta_kc']} beta_cyb {reg['both']['beta_cyb']} 年化alpha {reg['both']['alpha_ann']:+.1f}% t={reg['both']['t']}")

print("\n=== 对冲结果(毛 / 扣9%/年融券成本×h) ===")
hedged = {}
for k, lbl in (("kc50", "科创50"), ("cyb", "创业板指")):
    for h in (0.5, 1.0):
        gross = df.basket - h * df[k]
        net = gross - HEDGE_COST * h / 12
        key = f"{k}_h{h}"
        print(f" 对冲 {lbl} ×{h} 毛:")
        hedged[key] = stats(gross, f"毛 {lbl}×{h}")
        hedged[key + "_net"] = stats(net, f"净(扣融券) {lbl}×{h}")
out["reg"] = reg
out["hedged"] = hedged
json.dump(out, open("research/cef_hedge_alpha.json", "w"), ensure_ascii=False)
print("\nDONE -> research/cef_hedge_alpha.json")
