# -*- coding: utf-8 -*-
"""市场日内动量 (Gao, Han, Li & Zhou 2018, JFE "Market Intraday Momentum") A股实证。

问题:开盘头半小时(09:30-10:00)的收益, 能不能预测尾盘半小时(14:30-15:00)的收益?
美股原论文: 首半小时收益显著正向预测末半小时, 且末半小时可交易(ETF/期货 T+0)。

A股版: 用 68 只大盘 pilot 的 30 分钟 bar (data/factor_lab/eod30/) 构等权"市场",
first30 = 10:00 bar (close/open-1), last30 = 15:00 bar (close/open-1)。
回归 last30~first30 + 择时策略 sign(first30)*last30 + 阈值 + train/test。
关键落地: 尾盘单日往返 = T+0, A股个股 T+1 做不了 → 只能用 IF/IC/IH 股指期货表达。
"""
import glob, json, numpy as np, pandas as pd

fr_first, fr_last = {}, {}
for fp in glob.glob("/root/cb-allotment/data/factor_lab/eod30/*.parquet"):
    code = fp.split("/")[-1].replace(".parquet", "").replace("_", ".", 1)
    d = pd.read_parquet(fp); d["dt"] = pd.to_datetime(d.trade_time); d["date"] = d.dt.dt.date
    t = d.dt.dt.time.astype(str)
    b10 = d[t == "10:00:00"].set_index("date")   # 09:30->10:00
    b15 = d[t == "15:00:00"].set_index("date")    # 14:30->15:00
    fr_first[code] = b10.close / b10.open - 1
    fr_last[code] = b15.close / b15.open - 1
F = pd.DataFrame(fr_first); L = pd.DataFrame(fr_last)
F.index = pd.to_datetime(F.index); L.index = pd.to_datetime(L.index)
mf, ml = F.mean(axis=1), L.mean(axis=1)          # 等权市场
df = pd.concat([mf.rename("first"), ml.rename("last")], axis=1).dropna()

import scipy.stats as st
b, a, r, p, se = st.linregress(df["first"], df["last"])

def ann(x): return float(x.mean() * 244 * 100)
def sh(x): return float(x.mean() / x.std() * np.sqrt(244)) if x.std() > 0 else 0

strat = np.sign(df["first"]) * df["last"]          # first30 涨则做多 last30, 跌则做空
alw = df["last"]
tr = df[df.index < "2022-01-01"]; te = df[df.index >= "2022-01-01"]

out = {
    "n_stocks": int(F.shape[1]), "n_days": int(len(df)),
    "span": [str(df.index.min().date()), str(df.index.max().date())],
    "reg": {"beta": round(b, 4), "t": round(b / se, 2), "r2": round(r**2, 4)},
    "strategy": {"ann": round(ann(strat), 2), "sharpe": round(sh(strat), 2),
                 "winrate": round((strat > 0).mean() * 100, 0)},
    "always_long_last30": {"ann": round(ann(alw), 2), "sharpe": round(sh(alw), 2)},
    "train": {"ann": round(ann(np.sign(tr["first"]) * tr["last"]), 2),
              "sharpe": round(sh(np.sign(tr["first"]) * tr["last"]), 2), "n": int(len(tr))},
    "test": {"ann": round(ann(np.sign(te["first"]) * te["last"]), 2),
             "sharpe": round(sh(np.sign(te["first"]) * te["last"]), 2), "n": int(len(te))},
    "thresholds": [],
}
for thr in [0.0, 0.002, 0.004, 0.006]:
    m = df["first"].abs() > thr
    s = np.sign(df.loc[m, "first"]) * df.loc[m, "last"]
    out["thresholds"].append({"thr_pct": thr * 100, "n_days": int(m.sum()),
                              "daily_mean_pct": round(float(s.mean()) * 100, 3),
                              "ann_pct": round(ann(s), 1)})
# 分解: 首半小时 涨/跌 时, 尾盘的条件均值 (方向性证据)
up = df[df["first"] > 0]["last"]; dn = df[df["first"] < 0]["last"]
out["conditional"] = {"first_up_last_ann": round(ann(up), 1), "first_dn_last_ann": round(ann(dn), 1),
                      "n_up": int(len(up)), "n_dn": int(len(dn))}
json.dump(out, open("/root/cb-allotment/my-app/public/reports/intraday-momentum/backtest_result.json", "w"),
          ensure_ascii=False, indent=1)
print(json.dumps(out, ensure_ascii=False, indent=1))
