Skip to content

fix: TushareSource.get_price 修复 ts_code 重命名并校验必要列#30

Open
DrIsDr wants to merge 15 commits into
quant-king299:mainfrom
DrIsDr:fix-tushare-price-column-rename
Open

fix: TushareSource.get_price 修复 ts_code 重命名并校验必要列#30
DrIsDr wants to merge 15 commits into
quant-king299:mainfrom
DrIsDr:fix-tushare-price-column-rename

Conversation

@DrIsDr

@DrIsDr DrIsDr commented Jul 11, 2026

Copy link
Copy Markdown

修复 TushareSource.get_price 因列名缺失导致返回 None 的问题。

问题现象

运行 学习实例/04_策略回测.py 时,HybridDataManager 尝试从 QMT 获取股价失败后 fallback 到 Tushare,但 Tushare 也返回空,导致:

  • 每次调仓都因"无法获取价格数据"被跳过
  • 回测最终 0 笔交易、0% 收益
  • 但控制台仍打印 ✅ 回测完成!

根因

Tushare daily 接口返回的 DataFrame 列名为 ts_code/trade_date/vol,而 get_price 只做了部分重命名:

df.rename(columns={
    'trade_date': 'date',
    'vol': 'volume'
}, inplace=True)

未将 ts_code 重命名为 symbol。后续按 ['symbol', 'date', ...] 选取列时触发:

KeyError: "['symbol'] not in index"

异常被外层 except 捕获后返回 None,于是上层认为 Tushare 也没有数据。

修复

  1. 在重命名映射中加入 'ts_code': 'symbol'
  2. 列选择时只保留实际存在的列,提高对上游接口字段差异的兼容性

验证

修复后,TushareSource.get_price('002808.SZ', '20241231', '20241231') 已能正常返回:

symbol      date  open  high   low  close   volume     amount
002808.SZ  20241231  3.07  3.22  3.07   3.07  50601.4  15664.648

同时 HybridDataManager.get_price 对策略选出的小市值股票组合也能从 Tushare 成功取到价格数据。

DrIsDr added 2 commits July 11, 2026 07:49
SmallCapStrategy 和 SmallCapStrategyV2 中使用了 logger.info,
但模块未导入 logging 也未定义 logger,导致实例化时抛出 NameError。
Tushare daily 接口返回的 DataFrame 使用 ts_code 列表示股票代码,
但 get_price 只重命名了 trade_date/vol,未将 ts_code 重命名为 symbol。
后续按 ['symbol', 'date', ...] 选取列时触发 KeyError,被外层捕获后返回 None,
使得 HybridDataManager 在 QMT 无数据时 fallback 到 Tushare 也失败,
最终导致回测获取不到价格、无法产生交易。
Copilot AI review requested due to automatic review settings July 11, 2026 00:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

该 PR 修复 TushareSource.get_price 在对 Tushare daily 接口返回结果做列选择时,由于缺少 symbol 列导致 KeyError 进而返回 None 的问题,从而让 HybridDataManager 在 QMT 获取失败时能够正确 fallback 到 Tushare 并拿到价格数据。

Changes:

  • 将 Tushare daily 返回的 ts_code 重命名为 symbol,避免后续按 symbol 取列时报错。
  • 调整列选择逻辑:仅保留实际存在的列,以提升对上游字段差异的兼容性。

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread core/data_manager/sources/tushare_source.py Outdated
@DrIsDr DrIsDr changed the title fix: TushareSource.get_price 因 ts_code 未重命名为 symbol 导致 KeyError fix: TushareSource.get_price 修复 ts_code 重命名并校验必要列 Jul 11, 2026
DrIsDr added 12 commits July 11, 2026 08:52
回测引擎记录的交易方向为 'long'/'short',但展示代码按 'buy'/'sell' 过滤,导致买入/卖出次数始终为 0。
stock_daily 表定义包含 adjust_type、factor 两列(共14列),
但实际插入的 DataFrame 只有12列,导致 DuckDB 在执行
INSERT OR REPLACE ... SELECT * 时因 excluded 伪表列数不匹配而报错。

修复:
- 删除 stock_daily 表中无用的 adjust_type、factor 列
- 主键从 (stock_code, date, period, adjust_type) 改为 (stock_code, date, period)
- INSERT 语句显式指定12个目标列,兼容已存在的旧14列表
- 验证 is_available() 仅依据 is_connected 返回状态
- 验证 is_available() 不再调用 xtdata API 做真实探测
- 验证 close() 正确重置连接状态、清空缓存、重置 _last_used
- 配置 pytest 入口:pyproject.toml [tool.pytest.ini_options]
- gitignore 保留 unit_tests/ 目录,忽略 .worktrees/
修复 Code Review 发现的两个问题:
1. INSERT 列顺序与 DataFrame 顺序强耦合:将保存逻辑提取为
   TushareDownloadThread._save_daily_dataframe(),INSERT 列名从
   DataFrame 列动态推导,避免硬编码列名导致的数据错位风险。
2. created_at 在冲突替换时被覆盖:INSERT 不再包含 created_at,
   新建行使用 DEFAULT CURRENT_TIMESTAMP,冲突行保留原 created_at。

新增测试:
- unit_tests/gui_app/widgets/test_tushare_data_widget.py
- 覆盖正确写入、无废弃列、列顺序无关、created_at 保留、忽略多余列

同时更新 .gitignore 与 pyproject.toml,使用 unit_tests/ 作为测试目录。
@DrIsDr DrIsDr force-pushed the fix-tushare-price-column-rename branch from e04bc9a to d01c3bc Compare July 11, 2026 03:27
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