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
+154
View File
@@ -0,0 +1,154 @@
"""Step 13:中枢突破的三关稳健性验证。
Step 12 发现中枢突破跨周期单调为正(PF 1.18/1.52/2.17),但样本偏小、
且切过多个维度,必须过三关才能当真:
关一 多品种 BTC / ETH / SOL —— 信号是否只存在于 BTC
关二 分时段 按年切 —— 是否只靠某一段行情
关三 参数面 sl/tp 网格 —— 是否只在某个参数点成立
任何一关塌掉,都说明 Step 12 是数据挖掘的产物。
"""
from __future__ import annotations
import argparse
import sys
import warnings
from pathlib import Path
import numpy as np
import pandas as pd
warnings.filterwarnings("ignore")
sys.path.insert(0, str(Path(__file__).resolve().parent))
from lib.breakout import run_trades, summarize_trades
from lib.data import fetch_ohlcv
from lib.nested_level import build_htf_zones
from step12_zs_breakout import collect_breakouts
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from chanlun import TF_DF
pd.set_option("display.width", 260)
SYMBOLS = ["BTC/USDT:USDT", "ETH/USDT:USDT", "SOL/USDT:USDT"]
TFS = ["15m", "1h", "4h"]
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--sl", type=float, default=1.5)
ap.add_argument("--tp", type=float, default=3.0)
ap.add_argument("--max-bars", type=int, default=48)
args = ap.parse_args()
pool: list[pd.DataFrame] = []
print("########## 关一:多品种 × 多周期(突破即入,sl1.5 tp3##########")
rows = []
for sym in SYMBOLS:
for tf in TFS:
try:
df = fetch_ohlcv(sym, tf, 10**9)
except Exception:
continue
if df is None or len(df) < 2000:
continue
chan = TF_DF(df, 1, tf)
cdf = chan.dataframe
zones = build_htf_zones(df, tf)
bo = collect_breakouts(cdf, zones)
if bo.empty or len(bo) < 15:
continue
entries = list(zip(bo["bo_idx"].astype(int), bo["dir"].astype(int)))
tr = run_trades(cdf, entries, args.sl, args.tp, args.max_bars)
if tr.empty:
continue
s = summarize_trades(tr, f"{sym.split('/')[0]:>4} {tf:>3}")
s["假突破率"] = f"{bo['is_fake'].mean() * 100:.0f}%"
rows.append(s)
tr = tr.copy()
tr["symbol"] = sym.split("/")[0]
tr["tf"] = tf
tr["date"] = cdf["date"].to_numpy()[tr["entry_idx"].to_numpy()]
pool.append(tr)
print(pd.DataFrame(rows).to_string(index=False))
if not pool:
print("无足够样本")
return
allt = pd.concat(pool, ignore_index=True)
print(f"\n 合并 {len(allt)} 笔:", end="")
r = allt["ret"].to_numpy()
sd = r.std(ddof=1)
win, loss = r[r > 0], r[r <= 0]
print(f"胜率 {(r > 0).mean() * 100:.1f}% 均收益 {r.mean() * 100:+.3f}% "
f"PF {win.sum() / abs(loss.sum()):.2f} t值 {r.mean() / (sd / np.sqrt(len(r))):+.2f}")
print("\n########## 关二:按年分段(合并全部品种周期)##########")
allt["year"] = pd.to_datetime(allt["date"]).dt.year
rows = []
for y, g in allt.groupby("year"):
if len(g) < 25:
continue
s = summarize_trades(g, str(y))
rows.append(s)
print(pd.DataFrame(rows).to_string(index=False))
pos_years = sum(1 for _, g in allt.groupby("year")
if len(g) >= 25 and g["ret"].mean() > 0)
tot_years = sum(1 for _, g in allt.groupby("year") if len(g) >= 25)
print(f"\n 盈利年份 {pos_years}/{tot_years}")
print("\n########## 关三:参数网格(合并全部品种周期,1h 为主)##########")
rows = []
for sl in (1.0, 1.5, 2.0, 2.5):
for tp in (2.0, 3.0, 4.0):
sub = []
for sym in SYMBOLS:
for tf in TFS:
try:
df = fetch_ohlcv(sym, tf, 10**9)
except Exception:
continue
if df is None or len(df) < 2000:
continue
chan = TF_DF(df, 1, tf)
cdf = chan.dataframe
bo = collect_breakouts(cdf, build_htf_zones(df, tf))
if bo.empty:
continue
e = list(zip(bo["bo_idx"].astype(int), bo["dir"].astype(int)))
t = run_trades(cdf, e, sl, tp, args.max_bars)
if not t.empty:
sub.append(t)
if sub:
rows.append(summarize_trades(pd.concat(sub, ignore_index=True),
f"sl{sl} tp{tp}"))
grid = pd.DataFrame(rows)
print(grid.to_string(index=False))
pos = sum(1 for v in grid["均收益"] if v.startswith("+"))
print(f"\n 正收益参数点 {pos}/{len(grid)}")
print("\n########## 补充:合并样本的方向与周期拆分 ##########")
rows = []
for d, nm in [(1, "向上突破"), (-1, "向下突破")]:
g = allt[allt["direction"] == d]
if len(g) >= 25:
rows.append(summarize_trades(g, nm))
for tf in TFS:
g = allt[allt["tf"] == tf]
if len(g) >= 25:
rows.append(summarize_trades(g, f"周期 {tf}"))
print(pd.DataFrame(rows).to_string(index=False))
out = Path(__file__).parent / "out" / "step13_all_trades.csv"
out.parent.mkdir(exist_ok=True)
allt.to_csv(out, index=False)
print(f"\n明细已写入 {out}")
if __name__ == "__main__":
main()