Skip to content

feat(strategy): 策略可读取指数/ETF日K (market_data 模块 + 白名单 + 测试) - #207

Open
shuolol wants to merge 1 commit into
shy3130:mainfrom
shuolol:feat/strategy-index-data
Open

feat(strategy): 策略可读取指数/ETF日K (market_data 模块 + 白名单 + 测试)#207
shuolol wants to merge 1 commit into
shy3130:mainfrom
shuolol:feat/strategy-index-data

Conversation

@shuolol

@shuolol shuolol commented Aug 26, 2026

Copy link
Copy Markdown

问题与复现

Custom/AI 策略被沙箱隔离,filter_history(df, params) 只能拿到个股历史窗口,无法读取指数K线,因此做不了「大盘指数 MACD 死叉」这类市场级过滤。复现:在任意策略内 from app.strategy.market_data import get_index_daily 会被 AIStrategyGenerator._validate_safety 拒绝。

根因

策略 import 白名单(ai_generator._ALLOWED_IMPORT_MODULES)仅放行 polars / numpy / app.backtest.matrix / datetime,框架侧没有提供任何可达指数/ETF 日K的只读入口。

解决方案

新增框架侧受信模块 backend/app/strategy/market_data.py,暴露只读纯函数:

  • get_index_daily(symbol, start, end, columns) / get_etf_daily(...):读取指数/ETF 日K(含技术指标,支持列下推)。
  • get_daily(symbol, ...):按 repo.resolve_asset_type 自动分派(指数/ETF/股票)。
  • list_index_symbols():枚举已收录指数。
    模块线程安全懒加载 repo(DataStore() 默认 settings.data_dir,与 main.py 同源);未知 symbol/缺数据返回空 DataFrame 不抛;不向策略暴露文件访问或写能力。

_ALLOWED_IMPORT_MODULES 放行精确"app.strategy.market_data"(非裸 app,不会连带放行其它 app.*),AST 检查对其它模块/危险调用照旧拦截。对既有策略零影响(纯新增模块 + 白名单一行)。

兼容性

  • 不改 filter_history 签名、StrategyDataContext、面板构建,不影响任何现有策略。
  • 指数/ETF/股票均走既有 KlineRepository 通道;ETF 无独立数据时回退旧版 index-enriched(与 get_etf_daily 现状一致)。
  • 未知 symbol/数据缺失返回空 DataFrame,由策略侧自行判定(本项目策略侧 fail-open)。

性能

非热路径。get_index_daily 仅在策略显式调用时触发一次指数 enriched 查询;模块缓存 repo 实例(懒加载一次),不缓存查询结果,v1 足够。

验证结果

  • uv run --frozen --extra dev pytest tests/test_strategy_market_data.py -q9 passed
  • uv run --frozen --extra dev ruff check app/strategy/market_data.py tests/test_strategy_market_data.pyAll checks passed
  • AIStrategyGenerator._validate_safety('from app.strategy.market_data import get_index_daily') → 通过
  • 真实数据端到端:get_index_daily('000001.SH') 返回 248 行 enriched(含 macd_dif/dea、ma5/10/20/30);list_index_symbols() 返回 610 个指数;空串/None/未知 symbol 均返回空、不抛。

界面证据

无前端/UI 改动。

风险与回滚

  • 风险:策略获得读指数/ETF/股票日K能力,属能力面扩大;模块为框架受信代码,仅暴露只读纯函数,不暴露选目录/任意读文件/写能力。
  • 回滚:还原本 PR 三个文件即可(删除 market_data.py + ai_generator.py 白名单一行回退);对既有策略无影响。

Custom/AI 策略此前被沙箱隔离, filter_history(df, params) 只能拿到个股历史窗口,
无法读取指数K线, 也就做不了「大盘指数 MACD 死叉」这类市场级过滤。

本改动新增框架侧受信模块 backend/app/strategy/market_data.py, 暴露只读纯函数:
  - get_index_daily / get_etf_daily: 读取指数/ETF 日K(含技术指标, 支持列下推)
  - get_daily: 按 repo.resolve_asset_type 自动分派(指数/ETF/股票)
  - list_index_symbols: 枚举已收录指数
模块线程安全懒加载 repo (DataStore() 默认 settings.data_dir, 与 main.py 同源),
未知 symbol/缺数据返回空 DataFrame 不抛; 不向策略暴露文件访问或写能力。

ai_generator._ALLOWED_IMPORT_MODULES 放行 "app.strategy.market_data",
使 AI 生成与磁盘 Custom 策略均可 import 该模块; 其余模块/危险调用照旧拦截。

附 9 项框架级测试: 白名单放行/拦截、注入 fake repo 后委托与日期规范化、
按资产类型分派、坏 symbol/缺数据返回空、list_index_symbols。对既有策略零影响。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants