5.5 KiB
原项目 B1 图形相似度评分调研
结论
本任务迁移的是原项目 Python 已执行并持久化的 0–100 B1 完美图形相似度评分,不包含 prompt/b1.md 定义的 1–5 主观视觉评分。相似度评分属于 B1 命中后的 enrichment,不参与七个子信号的命中判断。
原执行链为 SelectionPipeline._enrich_with_pattern_match() 调用 B1PatternLibrary.find_b1_best_match(),对每只候选股票计算一次结果,再把同一结果写入该股票的信号详情。关键源码位于:
/Users/yuxuanhui/bcc-github/quant-project/zgnb/zgnb-project/src/zgnb/application/pipeline.py:168-220/Users/yuxuanhui/bcc-github/quant-project/zgnb/zgnb-project/src/zgnb/domain/pattern/library.py:22-101/Users/yuxuanhui/bcc-github/quant-project/zgnb/zgnb-project/src/zgnb/domain/pattern/feature_extractor.py:22-154/Users/yuxuanhui/bcc-github/quant-project/zgnb/zgnb-project/src/zgnb/domain/pattern/matcher.py:19-128/Users/yuxuanhui/bcc-github/quant-project/zgnb/zgnb-project/src/zgnb/domain/pattern/config.py:8-38
算法事实
候选与案例都取最近 25 个升序交易日,提取四组特征:趋势结构、KDJ 状态、量能形态和价格形态。四组实际代码权重分别为 0.10、0.20、0.25 和 0.45,总分为分项相似度加权和乘以 100,保留两位小数。文档中 0.30/0.20/0.25/0.25 的权重与当前运行代码不一致,不能作为迁移基线。
价格曲线源码先尝试 fastdtw 和 SciPy 欧氏距离,异常时回退 _simple_dtw;原项目把 fastdtw>=0.3.4 与 scipy>=1.10.0 声明为正式依赖。实施门禁在 Python 3.12 上用相同的一维数组调用点验,scipy.spatial.distance.euclidean 接收到标量后稳定抛出 AxisError: axis -1 is out of bounds for array of dimension 0,因此原 _shape() 实际捕获异常并使用 _simple_dtw。这说明旧项目落地运行结果的曲线分数来自 simple-DTW fallback,而不是 FastDTW 成功路径。匹配十个固定案例后只保留最高分案例,最高分达到 60.0 才向外提供 similarity_score、match_case 和四个分项。
案例窗口严格使用 breakout_date 之前的数据,不包含突破日。十个案例为 688799.SH、600366.SH、688321.SH、600601.SH、002074.SZ、605378.SH、600184.SH、301076.SZ、002940.SZ 和 000547.SZ;原编号缺少 case_005,迁移时保持既有十条定义,不自行补案例。
案例资产与兼容风险
原项目 data/raw/ 行情和 data/cache/b1_pattern_library_cache.json 都被 .gitignore 排除,不属于可部署资产。缓存没有算法版本、案例定义哈希、行情修订或完整性校验;本机缓存与当前 CSV 重算结果已有八个案例发生差异。因此新系统不能复制该缓存作为事实源,应迁移案例定义并从 PostgreSQL 最新 qfq 行情构建案例特征。
原特征提取器先截取 25 行,再计算最长 114 日均线,导致部分趋势字段为非有限值。迁移必须通过固定 fixture 锁定原 matcher 对这些中间值的最终有限分数行为,禁止把 NaN 写入 PostgreSQL 或 HTTP。若无法得到有限、确定的结果,应将评分标记为失败,但不得改变选股结果。
原项目没有案例特征、窗口截断或评分数值 golden 测试,只测试了字段透传与排序。新系统必须把从旧 CSV 提取的最小窗口和离线期望结果纳入测试 fixture;测试运行时不得依赖原项目、本机缓存、Tushare 或生产数据库。
FastDTW 决策
用户确认版本一不兼容旧 _simple_dtw fallback,而是直接修正为真正生效的 FastDTW,因为允许局部时间轴对齐更符合业务期望。新版本使用一维标量欧氏距离、显式 radius=1 和版本标识 zhixing_b1_pattern_fastdtw_v1;不得在 FastDTW 异常时静默切回 simple-DTW。原项目的十案例、特征、权重、容忍参数和 60 分阈值继续复用,数值 golden 以修正后的新算法为准。
当前系统接缝
当前 B1 执行链为 HTTP 创建 run、批量读取 StockHistory、并发评估、写入 selection_run_item 与 selection_signal、查询并按股票聚合到 stocks[].signals[]。评分是每股一次的结果,最合适的持久化位置是 selection_run_item,而不是每条 selection_signal.details。
关键依据:
.trellis/spec/backend/selection.mdzhixing-server/src/zhixing_server/modules/selection/application/run.py:91-159zhixing-server/src/zhixing_server/modules/selection/domain/runs.py:19-58zhixing-server/src/zhixing_server/modules/selection/infrastructure/postgres_runs.py:184-229,355-454zhixing-server/src/zhixing_server/modules/selection/presentation/http.py:97-137,273-323zhixing-web/src/features/selection/api/selection.types.ts:36-83
当前结果以股票为分页实体,一股可以拥有多个 category。把 score 复制到 signal details 会造成重复与 category 语义混淆,也不利于数据库级排序。为 selection_run_item 增加可空、版本化的评分列能复用现有主键、批量 upsert、重跑级联与股票聚合读取。
已验证基线
调研阶段后端全量测试基线为 83 passed, 2 skipped,两个跳过项需要 ZHIXING_TEST_DATABASE_URL;B1 相关单元、golden 与 HTTP 测试为 42 passed。原项目运行点验因环境导入名不匹配失败,过程中临时产生的 .venv 与 uv.lock 已移至系统废纸篓,没有保留对原项目的改动。