"""Step 7:区间套验证。 逐层加过滤,看每一层带来多少增量: L0 所有 1h 分型 L1 + 背驰(价格创新极值但 MACD 面积衰减) L2 + 大级别中枢位置(买贴支撑 / 卖贴压力) L3 L1 + L2 组合 对照组:单级别三类买卖点(滞后 16~21 根)。 """ from __future__ import annotations import argparse import sys from pathlib import Path import numpy as np import pandas as pd sys.path.insert(0, str(Path(__file__).resolve().parent)) from lib.bsp_eval import baseline_stats from lib.data import fetch_ohlcv from lib.fx_signal import add_forward_returns, extract_fx_signals, signals_to_frame from lib.nested_level import annotate_position, build_htf_zones sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from chanlun import TF_DF pd.set_option("display.width", 240) HORIZONS = (3, 5, 10, 20, 40) def summarize(g: pd.DataFrame, df: pd.DataFrame, label: str, min_n: int = 10) -> list[dict]: base = baseline_stats(df, HORIZONS).set_index("horizon") out = [] for h in HORIZONS: col = f"ret_{h}" if col not in g: continue r = g[col].dropna().to_numpy() if len(r) < min_n: continue dirs = g.loc[g[col].notna(), "direction"].to_numpy() sd = r.std(ddof=1) out.append({ "分组": label, "持有": h, "n": len(r), "收益": r.mean(), "胜率": (r > 0).mean(), "超额": r.mean() - float(np.mean(dirs) * base.loc[h, "base_mean_long"]), "t值": r.mean() / (sd / np.sqrt(len(r))) if sd else np.nan, }) return out def show(rows: list[dict]) -> None: if not rows: print(" (样本不足)") return d = pd.DataFrame(rows) d["收益"] = d["收益"].map(lambda v: f"{v * 100:+.2f}%") d["超额"] = d["超额"].map(lambda v: f"{v * 100:+.2f}%") d["胜率"] = d["胜率"].map(lambda v: f"{v * 100:.0f}%") d["t值"] = d["t值"].map(lambda v: f"{v:+.2f}") print(d.to_string(index=False)) def main() -> None: ap = argparse.ArgumentParser() ap.add_argument("--symbol", default="BTC/USDT:USDT") ap.add_argument("--ltf", default="1h", help="小级别:出分型信号") ap.add_argument("--htf", default="1d", help="大级别:出支撑压力中枢") ap.add_argument("--tol", type=float, default=0.01, help="贴近边界的相对阈值") args = ap.parse_args() df = fetch_ohlcv(args.symbol, args.ltf, 10**9) df_htf = fetch_ohlcv(args.symbol, args.htf, 10**9) print(f"[data] 小级别 {args.ltf} rows={len(df)} 大级别 {args.htf} rows={len(df_htf)}") print(f" {df['date'].iloc[0]} -> {df['date'].iloc[-1]}\n") chan = TF_DF(df, 1, args.ltf) sigs = extract_fx_signals(chan, chan.dataframe) sig = signals_to_frame(sigs) print(f"[分型] 共 {len(sig)} 个(底 {int((sig.direction == 1).sum())} / " f"顶 {int((sig.direction == -1).sum())})") print(f"[确认滞后] 中位数 {sig['lag'].median():.0f} 根 均值 {sig['lag'].mean():.2f} 根 " f"P90 {sig['lag'].quantile(.9):.0f} 根 最大 {sig['lag'].max()} 根") print(f" —— 对照:笔 9~10 根、线段 101~121 根、三类买卖点 16~21 根\n") sig = add_forward_returns(sig, chan.dataframe, HORIZONS) zones = build_htf_zones(df_htf, args.htf) print(f"[大级别中枢] {args.htf} 上共 {len(zones)} 个可用中枢") sig = annotate_position(sig, chan.dataframe, zones, tol=args.tol) div = sig["is_divergence"] pos = sig["position_ok"].astype(bool) print(f"[过滤器覆盖] 背驰 {div.sum()}/{len(sig)} ({div.mean()*100:.0f}%) " f"位置正确 {pos.sum()}/{len(sig)} ({pos.mean()*100:.0f}%) " f"两者兼备 {(div & pos).sum()}\n") print("########## 逐层过滤效果 ##########") rows = [] rows += summarize(sig, chan.dataframe, "L0 全部分型") rows += summarize(sig[div], chan.dataframe, "L1 +背驰") rows += summarize(sig[pos], chan.dataframe, "L2 +位置") rows += summarize(sig[div & pos], chan.dataframe, "L3 背驰+位置") show(rows) print("\n########## L3 多空拆分 ##########") both = sig[div & pos] rows = summarize(both[both.direction == 1], chan.dataframe, "L3 做多", min_n=5) rows += summarize(both[both.direction == -1], chan.dataframe, "L3 做空", min_n=5) show(rows) print("\n########## 背驰强度分层(面积比 ratio,越小背驰越强)##########") rows = [] for lo, hi, name in [(0, 0.5, "ratio<0.5"), (0.5, 0.8, "0.5-0.8"), (0.8, 1.0, "0.8-1.0"), (1.0, 99, "ratio>1 无背驰")]: g = sig[(sig.ratio >= lo) & (sig.ratio < hi)] rows += summarize(g, chan.dataframe, name) show(rows) print("\n########## 中枢内 vs 中枢外(对应两种玩法)##########") rows = summarize(sig[sig.inside_zone.astype(bool)], chan.dataframe, "中枢内做短差") rows += summarize(sig[sig.outside_zone.astype(bool)], chan.dataframe, "中枢外做趋势") show(rows) out_dir = Path(__file__).parent / "out" out_dir.mkdir(exist_ok=True) tag = f"{args.symbol.split('/')[0]}_{args.ltf}_{args.htf}" sig.to_csv(out_dir / f"step7_fx_{tag}.csv", index=False) print(f"\n明细已写入 {out_dir}/step7_fx_{tag}.csv") if __name__ == "__main__": main()