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
+141
View File
@@ -0,0 +1,141 @@
"""快速三类买卖点:不等笔确认,突破回抽当根即入场。
引擎的 B3/S3 要等 pullback_bi.sure_time(回拉笔被确认),滞后 9~10 根,
此时价格已从回抽低点反弹完毕,入场价被吃掉。
但三买的形态条件本身是实时可判的:
中枢已成 -> 收盘突破 zg -> 回抽最低不跌回中枢(low >= zg) -> 重新上行
最后一步发生的当根就能下单,滞后 1~2 根。
全部判定只使用当根及之前的数据,无未来函数。
"""
from __future__ import annotations
import numpy as np
import pandas as pd
def find_fast_bsp3(
df: pd.DataFrame,
zones: pd.DataFrame,
scan: int = 200,
pullback_win: int = 30,
tol: float = 0.003,
max_per_zone: int = 1,
diag: dict | None = None,
) -> pd.DataFrame:
"""扫描每个中枢,找突破后回抽不回中枢的入场点。
zones 需含 zg / zd / available_ts,且 available_ts 已是可用时刻。
max_per_zone > 1 时,同一中枢在首次入场后继续往后找二次、三次突破回抽,
用来检验「趋势里同一中枢反复给机会」是否值得做。
返回列:
entry_idx 实时可下单的K线
direction +1 三买 / -1 三卖
bo_idx 突破根
pb_idx 回抽极值根
lag entry_idx - bo_idx
depth 回抽深度(相对中枢边界,负值表示曾插入中枢)
occ 这是该中枢的第几次入场
"""
if zones.empty:
return pd.DataFrame()
ts = df["timestamp"].to_numpy()
close = df["close"].to_numpy(dtype=float)
high = df["high"].to_numpy(dtype=float)
low = df["low"].to_numpy(dtype=float)
n = len(df)
rows = []
def note(key: str) -> None:
if diag is not None:
diag[key] = diag.get(key, 0) + 1
for zone_i, (_, z) in enumerate(zones.iterrows()):
note("中枢总数")
zg, zd = float(z["zg"]), float(z["zd"])
if zg <= zd:
note("×无效中枢")
continue
start = int(np.searchsorted(ts, z["available_ts"], side="left"))
if start >= n - 2:
note("×中枢太靠后")
continue
# 允许多次入场时按比例放宽扫描窗口,否则后几次机会会被窗口截断
scan_end = min(start + scan * max_per_zone, n)
cursor = start
for occ in range(1, max_per_zone + 1):
if cursor >= n - 2:
break
# 第一步:找突破。要求突破前确实待在中枢内,避免把远处的价格当突破。
was_inside = False
bo_idx, d = None, 0
for j in range(cursor, scan_end):
c = close[j]
if zd <= c <= zg:
was_inside = True
continue
if not was_inside:
continue
bo_idx, d = j, (1 if c > zg else -1)
break
if bo_idx is None:
if occ == 1:
note("×窗口内未突破")
break
edge = zg if d == 1 else zd
# 第二步:突破后监控回抽,回抽不跌回中枢且重新顺势 -> 入场
touched = False
pb_idx = None
pb_ext = None
entry_idx = None
fell_back = False
for j in range(bo_idx + 1, min(bo_idx + pullback_win + 1, n)):
# 收盘跌回中枢 -> 突破失效
if zd <= close[j] <= zg:
fell_back = True
break
# 回抽触及边界附近(允许 tol 的毛刺)
near = (low[j] <= edge * (1 + tol)) if d == 1 else (high[j] >= edge * (1 - tol))
if near:
touched = True
ext = low[j] if d == 1 else high[j]
if pb_ext is None or ((ext < pb_ext) if d == 1 else (ext > pb_ext)):
pb_ext, pb_idx = ext, j
continue
# 回抽后重新顺势:收盘创出前一根之上(三买)/ 之下(三卖)
if touched and pb_idx is not None:
go = close[j] > high[j - 1] if d == 1 else close[j] < low[j - 1]
if go:
entry_idx = j
break
if entry_idx is None or pb_ext is None:
if occ == 1:
note("×突破后跌回中枢" if fell_back
else "×回抽未触及边界" if not touched
else "×触及边界但未转强")
# 这次突破没走成,从突破点之后继续找下一次
cursor = bo_idx + 1
continue
if occ == 1:
note("√成交")
# 回抽深度:>0 表示未插入中枢,越大表示回抽越浅
depth = (pb_ext - zg) / zg if d == 1 else (zd - pb_ext) / zd
rows.append({
"entry_idx": entry_idx, "direction": d,
"bo_idx": bo_idx, "pb_idx": pb_idx,
"lag": entry_idx - bo_idx,
"depth": depth,
"zg": zg, "zd": zd,
"width_pct": (zg - zd) / close[bo_idx],
"occ": occ,
"zone_i": zone_i,
})
cursor = entry_idx + 1
return pd.DataFrame(rows)