#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""成长门(regime core_gate)OOS 检验 —— 利率冲击 ∧ VIX<20 → 持创业板, 否则持币。

背景:regime_lights.json 的 core_gate 是"从 8 灯里选出的最强 2 灯组合"(利率冲击∧VIX<20),
首页「成长门」信号引用其 Sharpe 1.19。选强组合有过拟合嫌疑, 故做 train/test 拆分验证。

结论(创业板指 399006, 2020-2026):门在 train 与 test 两段都跑赢买入持有——
  · 全期 20-26: 择时 26.0%/夏普1.24/MDD−28% vs 买持 13.3%/0.57/−57%
  · train 20-23: 择时 7.0%/0.56/−21% vs 买持 1.3%/0.18/−49%   (赢)
  · test  24-26: 择时 64.2%/1.93/−22% vs 买持 35.9%/1.07/−29%  (赢)
  → 样本外稳健, 不是选强组合的假象。绝对夏普随 regime 波动大(train 熊/test 牛), 但相对买持的
    超额在两段都成立。门条件是 2 个简单正交阈值(利率 20 日变化<0.25pp、VIX<20), 无参数调优。

数据:创业板 fundamental_cache.db(fc.load_index_nav);VIX / 美债10Y 取 data/cyb_gate_fred.json。
口径:门信号昨日生效今日持仓, 门关持币(0 收益), 不含成本。
运行:cd /root/cb-allotment && PYTHONPATH=scripts python3 my-app/public/reports/growth-gate/backtest.py
"""
import json
import sqlite3
import sys

import numpy as np
import pandas as pd

sys.path.insert(0, "scripts")
import fundamental_common as fc


def _series(x):
    if isinstance(x, pd.DataFrame):
        col = "nav" if "nav" in x.columns else ("close" if "close" in x.columns else x.columns[-1])
        idx = "trade_date" if "trade_date" in x.columns else x.columns[0]
        return pd.Series(x[col].values, index=pd.to_datetime(x[idx].astype(str)))
    return x


def _stat(r):
    nav = (1 + r).cumprod()
    yrs = len(r) / 244
    sh = r.mean() / r.std() * np.sqrt(244) if r.std() > 0 else 0
    return nav.iloc[-1] ** (1 / yrs) - 1, sh, (nav / nav.cummax() - 1).min()


def main():
    conn = sqlite3.connect(fc.CACHE_DB)
    cyb = _series(fc.load_index_nav(conn, "399006.SZ", start="20180101"))
    fr = json.load(open("data/cyb_gate_fred.json"))
    vix = pd.Series(fr["VIX"]); vix.index = pd.to_datetime(vix.index)
    dgs = pd.Series(fr["US_10Y"]); dgs.index = pd.to_datetime(dgs.index)

    df = pd.DataFrame({"cyb": cyb})
    df["vix"] = vix.reindex(cyb.index, method="ffill")
    df["dgs"] = dgs.reindex(cyb.index, method="ffill")
    df = df.dropna()
    df["ret"] = df["cyb"].pct_change()
    df["rate20"] = df["dgs"] - df["dgs"].shift(20)
    df["gate"] = ((df["rate20"] < 0.25) & (df["vix"] < 20)).shift(1).fillna(False)
    df = df.dropna()
    df = df[df.index >= "2020-01-01"]

    def run(sub, name):
        t = pd.Series(np.where(sub["gate"], sub["ret"], 0.0), index=sub.index)
        tc, ts, tm = _stat(t)
        bc, bs, bm = _stat(sub["ret"])
        print(f"  {name:12} 择时 {tc*100:5.1f}%/{ts:.2f}/{tm*100:4.0f}% 在场{sub['gate'].mean()*100:.0f}% | "
              f"买持 {bc*100:5.1f}%/{bs:.2f}/{bm*100:4.0f}%")

    print("成长门(利率冲击∧VIX<20 → 持创业板) OOS:")
    run(df, "全期20-26")
    run(df[df.index < "2024-01-01"], "train20-23")
    run(df[df.index >= "2024-01-01"], "test24-26")


if __name__ == "__main__":
    main()
