# CLAUDE-data.md — 数据层 + 数据库 ## DataManager (`finance/data/data_manager.py`) 双数据源:Tushare(优先)→ AkShare(fallback)。DB 缓存优先。 ```python from data.data_manager import DataManager dm = DataManager() dm.init_db() # 首次建表(幂等) ``` ### 关键方法 ```python stocks = dm.get_stock_list() # → 5524 只, DB 优先 daily = dm.get_daily("000001.SZ") # DB优先 → Tushare → AkShare fina = dm.get_financial("000001.SZ") # 同花顺 + fallback n = dm.sync_daily("000001.SZ") # 增量同步: latest>=today → 跳过 ``` ### 指数 vs 个股路由 `_try_fetch()` 自动检测 `is_index_code()`: - `000001.SH` / `399001.SZ` → `fetch_index_daily()`(Tushare `index_daily` / AkShare `index_zh_a_hist`) - `000001.SZ` / `600519.SH` → `fetch_daily()`(stock daily) ### 数据源 (`finance/data/sources/`) - `akshare_source.py` — `AkShareSource`(个股+指数+财务) - `tushare_source.py` — `TushareSource`(需 `TUSHARE_TOKEN`,`available=False` 自动跳过) ## 数据库 (`finance/database/`) ### 连接 ```python from database.connection import get_engine, test_connection # get_engine() 自动检测断连 → 运行 autossh.sh → 重建引擎 ``` ### 表结构(mac_ 前缀) | 表 | 内容 | 主键 | |----|------|------| | `mac_stock_basic` | A股列表, 5524只 | ts_code | | `mac_stock_daily` | 日线 OHLCV | (ts_code, trade_date) | | `mac_stock_financial` | 财务指标 | (ts_code, end_date) | | `mac_report` | 报告持久化 | id | ### DAO (`finance/database/dao.py`) - `_DAILY_COLS` / `_FINA_COLS` — 入库前字段筛选 - `get_latest_trade_date(ts_code)` → 增量判断 - `save_report()` / `query_reports()` → 报告管理 ### 配置 ```bash # SSH 隧道 bash shared/script/autossh.sh # host: 127.0.0.1:13306 user: myquant database: myquant ``` ## 缓存策略 - `sync_daily`: 已缓存且最新 → 0.04s 跳过 - 未缓存 → 提示 `agent_cli.py warmup` - 每日增量只更新已缓存股票,未缓存统计跳过 ## ⚠️ 已知 Bug 速查(避免重复踩坑) ### B1: `.env` 加载路径 - **症状**: Tushare available=False, QWEN_API_KEY 读不到 - **根因**: `load_dotenv()` 不传路径,从 CWD 找 .env,而非 `finance/` - **修复**: `config/settings.py` 用 `Path(__file__).parent.parent / ".env"` - **加固**: `tushare_source.py`, `qwen_client.py`, `news_source.py` 顶部 `import config.settings` ### B2: SSH 重连只生效一次 - **症状**: 第一次断连能恢复,第二次断连无法恢复 - **根因**: `_ssh_auto_attempted` 全局变量设 True 后永不重置 - **修复**: 移除该变量。`get_engine()` 每次断连都触发 `_reconnect_ssh()` → `dispose()` → 重建 ### B3: 连接池半开连接 - **症状**: `_test_engine()` 返回 True 但实际查询报 `_read_bytes` 超时 - **根因**: 池中有活连接也有死连接,test 取到活的,查询取到死的 - **修复**: `pool_pre_ping=True` + `pool_recycle=600` + 断连时 `_engine.dispose()` ### B4: save_daily 主键冲突 - **症状**: `IntegrityError: Duplicate entry '000001.SZ-20260603'` - **根因**: Tushare 返回的数据含已存在的日期,`append` 模式遇主键冲突 - **修复**: 写入前 `DELETE FROM mac_stock_daily WHERE ts_code IN (...) AND trade_date IN (...)` ### B5: 数据不更新排查清单 - 检查 SSH 隧道: `lsof -i :13306 | grep LISTEN` - 检查 Tushare: `python -c "from data.sources.tushare_source import TushareSource; print(TushareSource().available)"` - 检查最新数据: `from database.dao import get_latest_trade_date; print(get_latest_trade_date('000001.SZ'))` - 手动触发 sync: `dm.sync_daily('000001.SZ')` - 预期行为: T+1 数据产出,节假日无新数据属于正常