Files
zhixing-system/.trellis/spec/backend/selection.md
T
2026-08-08 22:41:45 +08:00

3.8 KiB
Raw Blame History

历史选股代码规格

Scenario: zhixing_b1 历史公式评估

1. Scope / Trigger

  • 触发:新增 modules/selection bounded context,基于 PostgreSQL 已保存的 qfq 日线执行历史知行 B1 公式。
  • 边界:selection 只读市场事实并返回领域评估结果;不负责市场数据同步、信号 持久化、批次调度、HTTP 路由或前端展示。

2. Signatures

  • MarketDataReader.load_history(ts_code: str, target_trade_date: date) -> StockHistory
  • ZhixingB1Strategy.evaluate(history: StockHistory, target_trade_date: date) -> SelectionEvaluation
  • EvaluateZhixingB1.execute(ts_code: str, target_trade_date: date) -> SelectionEvaluation
  • SelectionSignal.identity -> tuple[str, date, str, str]

3. Contracts

  • StockHistory.bars 必须是升序、去重的 qfq 行情,且不得包含目标交易日之后的 数据;daily_basic 按交易日保存同日可空指标。
  • SelectionBar 使用有限的 float | None 表示 OHLCV;目标日的 open/high/low/ close/volume 任一缺失时不得生成信号。
  • 策略名称固定为 zhixing_b1;7 个分类固定为 zhixing_b1_oversold_turn、zhixing_b1_oversold_volume、 zhixing_b1_original_b1、zhixing_b1_extreme_volume、 zhixing_b1_pullback_white、zhixing_b1_pullback_super、 zhixing_b1_pullback_yellow。
  • 同一股票同一交易日可以返回多个分类;唯一身份是 (ts_code, target_trade_date, strategy, category),返回顺序遵循 ZHIXING_B1_SIGNAL_ORDER。
  • PostgreSQL reader 必须参数化查询 source_adj = 'qfq' 且 trade_date <= target_trade_date,左连接同日 market_daily_basic;不得回退到 CSV、Tushare 或当前最后一行。
  • 评估状态区分 selected、no_signal、insufficient_history、 missing_target_bar 和 data_error。业务状态不是异常,数据库读取失败才映射为 data_error。

4. Validation & Error Matrix

条件 行为
少于公式最小暖机长度 返回 insufficient_history,不返回信号
目标日没有 qfq bar 或 OHLCV 不完整 返回 missing_target_bar
目标日数据完整但无分类命中 返回 no_signal
PostgreSQL 读取失败 返回 data_error,保留股票和目标日上下文
rolling 窗口不足 只使用已到达的交易行;EVERY 等需要完整窗口的条件不命中
除零、NaN 或无穷中间值 转为 NaN/False,不得静默制造命中

5. Good / Base / Bad Cases

  • Good:给定历史目标日,reader 只返回该日及之前的 qfq 行,策略返回可序列化详情 和全部命中分类。
  • Base:同日多分类命中时每类都保留稳定身份;同日 daily_basic 缺失只影响实际 依赖该指标的条件。
  • Bad:用当前最新市值覆盖历史 K 线、取 bars 最后一行代替显式目标日,或把 7 类 mask OR 成一个结果后丢失分类。

6. Tests Required

  • 指标单元测试:rolling 暖机、交易日 REF、窗口边界、除零、NaN、宽幅代码参数。
  • 策略单元测试:目标日截断、暖机/缺失状态、7 个 mask 独立存在和同日多分类身份。
  • reader 单元测试:参数化 SQL、qfq 过滤、目标日截断、升序映射和 left join 可空值。
  • golden 测试:离线固定 fixture 可重复运行;不得在测试运行时导入旧项目或访问生产库。

7. Wrong vs Correct

Wrong

# 会产生历史前视数据,并丢弃同日的其他分类。
target = history.bars[-1]
first = next(category for category in categories if masks[category].iloc[-1])

Correct

# 用显式交易日定位,并保留所有独立 mask 的命中。
target_index = frame.index[frame["trade_date"] == target_trade_date][0]
matched = tuple(category for category in signal_order if masks[category].iloc[target_index])