fix: Tushare 日线下载 stock_daily 列数不匹配并补充单元测试#33
Open
DrIsDr wants to merge 16 commits into
Open
Conversation
Tushare daily 接口返回的 DataFrame 使用 ts_code 列表示股票代码, 但 get_price 只重命名了 trade_date/vol,未将 ts_code 重命名为 symbol。 后续按 ['symbol', 'date', ...] 选取列时触发 KeyError,被外层捕获后返回 None, 使得 HybridDataManager 在 QMT 无数据时 fallback 到 Tushare 也失败, 最终导致回测获取不到价格、无法产生交易。
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列表
修复 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/ 作为测试目录。
There was a problem hiding this comment.
Pull request overview
该 PR 修复了 GUI 中 Tushare 日线下载写入 DuckDB 时 stock_daily 表列数与写入 DataFrame 列数不一致导致的插入失败问题,并将日线保存逻辑抽取为可单测的静态方法,同时补齐单元测试与 pytest 配置以覆盖关键写入场景。
Changes:
- 修订
stock_daily建表结构与主键,移除遗留列,并将写入改为显式列插入以避免列数不匹配。 - 抽取
_save_daily_dataframe(conn, df),统一并测试按列名对齐写入、忽略多余列、以及冲突替换时的时间戳行为。 - 新增
unit_tests/测试目录与 pytest 配置,使测试可在仓库内稳定运行。
Reviewed changes
Copilot reviewed 3 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| gui_app/widgets/tushare_data_widget.py | 修复 stock_daily 建表/写入逻辑,新增可测试的 _save_daily_dataframe 保存方法 |
| unit_tests/gui_app/widgets/test_tushare_data_widget.py | 新增 5 个场景的单元测试覆盖日线写入与冲突替换行为 |
| unit_tests/gui_app/widgets/init.py | 使测试目录成为可导入包结构的一部分 |
| unit_tests/gui_app/init.py | 使测试目录成为可导入包结构的一部分 |
| unit_tests/init.py | 使测试目录成为可导入包结构的一部分 |
| pyproject.toml | 增加 pytest 配置(testpaths 指向 unit_tests) |
| .gitignore | 增加对 unit_tests 目录的例外规则,避免被忽略 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 将日线 DataFrame 保存到 stock_daily 表。 | ||
|
|
||
| 设计要点: | ||
| 1. INSERT 列名从 DataFrame 列动态推导,避免 SQL 列名与 DataFrame 列顺序硬耦合。 |
Comment on lines
1063
to
1067
| amount DOUBLE, | ||
| adjust_type VARCHAR DEFAULT 'none', | ||
| factor DOUBLE DEFAULT 1.0, | ||
| created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | ||
| updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | ||
| PRIMARY KEY (stock_code, date, period, adjust_type) | ||
| PRIMARY KEY (stock_code, date, period) | ||
| ) |
SmallCapStrategy 和 SmallCapStrategyV2 中使用了 logger.info, 但模块未导入 logging 也未定义 logger,导致实例化时抛出 NameError。
新增测试验证: - 模块已正确定义 logger - SmallCapStrategy 实例化时不会因缺失 logger 而抛 NameError - 自定义参数实例化时 logger 正常工作 同时配置 unit_tests/ 目录与 pytest: - .gitignore 新增 unit_tests/ 例外规则 - pyproject.toml 新增 pytest 配置
- 验证 is_available() 仅依据 is_connected 返回状态 - 验证 is_available() 不再调用 xtdata API 做真实探测 - 验证 close() 正确重置连接状态、清空缓存、重置 _last_used - 配置 pytest 入口:pyproject.toml [tool.pytest.ini_options] - gitignore 保留 unit_tests/ 目录,忽略 .worktrees/
回测引擎记录的交易方向为 '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列表
修复 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/ 作为测试目录。
- 将 docstring 中“动态推导 INSERT 列名”改为“预定义白名单 data_cols”,与实现一致。 - 移除 local_data_manager_widget.py 中 stock_daily 写入残留的 adjust_type 列, 避免对新版 stock_daily 表(无主键 adjust_type)写入时报 column not found。
…-mismatch # Conflicts: # .gitignore # gui_app/widgets/tushare_data_widget.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题描述
在 GUI 中运行 Tushare 日线下载时,DuckDB 报错:
原因是
stock_daily表定义包含adjust_type、factor两列(共 14 列),但实际插入的 DataFrame 只有 12 列,导致INSERT OR REPLACE INTO stock_daily SELECT * FROM _tmp_daily列数不匹配。修复内容
修复列数不匹配
stock_daily建表语句中删除adjust_type、factor两列(stock_code, date, period, adjust_type)改为(stock_code, date, period)提取可测试的保存逻辑
TushareDownloadThread._save_daily_dataframe(conn, df)静态方法created_at不再写入 INSERT,新建行使用 DEFAULT,冲突替换时保留原值测试
新增单元测试目录:
覆盖 5 个场景:
adjust_type/factorcreated_at保留,updated_at刷新运行结果:
5 passed其他变更
.gitignore:新增unit_tests/例外规则pyproject.toml:新增 pytest 配置,指定testpaths = ["unit_tests"]