refactor: 精简仓库为 chanlun 核心与 web 分析,移除威科夫与遗留模块

删除根目录旧 Chan 模块、策略、配置、文档及 wyckoff 相关代码;更新缠论 pipeline 与笔中枢计算;补充 research 研究与 web 测试。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
jackyu66git
2026-08-27 01:05:12 +08:00
co-authored by Cursor
parent 5c10e35b76
commit 7f393b93ed
360 changed files with 140008 additions and 41167 deletions
+135
View File
@@ -0,0 +1,135 @@
"""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()