Skip to content

【ETF】DEFAULT_BASIC_FILTER 股票口径(boards 前缀匹配 + price_min=3)导致 ETF 回测/选股静默零信号 #215

Description

@MarlowFeng

现象

ETF 上跑任意策略回测,返回"在指定区间内未产生买入信号",0 笔交易——全程无任何报错。数据管道本身健康:同一窗口实测矩阵里有 50 次 MA5/MA20 金叉、7 个完整入场信号。

环境

  • 版本:v0.2.1,TickFlow 免费档 + 自定义日K数据源
  • ETF enriched 已正常落盘(矩阵 64 列指标现算正常)
  • 复现资产:ETF(510300 等,代码前缀 51/15/56/58)

复现步骤

curl -X POST "http://localhost:3018/api/backtest/strategy/run" \
  -H "Content-Type: application/json" \
  -d '{"strategy_id":"ma_golden_cross","symbols":null,
       "start":"2025-09-01","end":"2026-08-28",
       "asset_type":"etf","holding_days":10,"max_positions":5,"mode":"position"}'

返回:"在指定区间内未产生买入信号",0 笔交易。但用同一份矩阵数据手工数金叉,一年窗口内有 50 次——信号是被过滤链静默清掉的,不是数据或策略问题。

根因

app/strategy/engine.py:36-48 的兜底过滤 DEFAULT_BASIC_FILTER 是纯股票口径:

DEFAULT_BASIC_FILTER: dict = {
    "price_min": 3,          # ETF 单价普遍 0.5~7 元, 整列误杀
    "price_max": 300,
    "market_cap_min": 10e8,  # ETF 无股本概念
    "amount_min": 0.2e8,
    ...
    "boards": ["沪主板", "深主板", "创业板", "科创板", "北交所"],
}

其中 boardsapp/backtest/matrix.py:4136_symbol_in_boards,按股票代码前缀匹配:

def _symbol_in_boards(symbol: str, boards: list[str]) -> bool:
    for board in boards:
        if board == "沪主板" and symbol.startswith("60"):
            return True
        if board == "深主板" and symbol.startswith(("00", "001")):
            ...

ETF 代码(51/15/56/58 开头)不属于任何板块 → board_mask 全 False → basic 过滤掩码全 False → 所有 ETF 在入场前就被静默团灭。这比 #187 的报错更隐蔽:回测"正常完成",只是零信号,用户会误以为该策略在 ETF 上没有机会。

建议修法

运行期(不只是依赖解析期)对 asset_type != "stock" 中和股票专属键(boards / price_min / price_max / market_cap_min / float_cap_*)。项目里已有先例:tests/test_matrix_etf_share_fields.py 针对市值界做了依赖解析前的中和,但运行期 basic_filter 用的是原始值(app/backtest/strategy.py 的回测/挖掘 run 路径、app/strategy/engine.py 的 screener _run_matrix_strategy 路径),没有同等处理。

验证

本地在以下三处调用点对非 stock 资产做了中和后,同一回测立刻恢复出信号(参数未变,一年窗口 7 笔交易、7 年窗口 98 笔):

  • app/backtest/strategy.py 回测 run 路径(约 line 847)
  • app/backtest/strategy.py 挖掘 run 路径(约 line 1014)
  • app/strategy/engine.py screener _run_matrix_strategy(约 line 1167)

关联

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions