# sanguo_portfolio 全天候策略 VPS 回测报告 > 生成日期:2026-07-18 > 环境:VPS(49.232.102.198,Windows,Python 3.10.11)+ miniQMT 模拟端(userdata_mini) > 范围:沪深300 子集 39 只权重股,2025-04-17 → 2026-07-17(约 3 个月) ## 1. VPS pytest 结果 | 项目 | 值 | |---|---| | Python | CPython 3.10.11 (MSC v.1929 64 bit) @ C:\Python310\python.exe | | pytest | 9.1.1(VPS 预装) | | bullet-trade | 0.9.2(jqdatasdk 列为 required 但 env guard 跳过) | | xtquant | 内置 xtdata,路径 C:\Python310\lib\site-packages\xtquant | | miniQMT 数据路径 | C:\国金QMT交易端模拟\userdata_mini | | **测试结果** | **88 passed, 1 warning in 1.24s** | 环境前置(**必须**,否则 `import bullet_trade` 报缺 jqdatasdk): ```cmd set DEFAULT_DATA_PROVIDER=miniqmt python -m pytest tests/portfolio -q ``` ## 2. 字段校准前后对比(关键发现) VPS 连 miniQMT 实测 600519.SH 茅台 PershareIndex/Balance/Capital/Income/CashFlow 实际字段名, **发现 3 个严重不匹配**,全部修复。 ### 2.1 修了哪些 alias | 表 | sanguo 代码原用字段 | miniQMT 实际字段 | 修复方式 | |---|---|---|---| | PershareIndex | `roe` | `du_return_on_equity`(或 `equity_roe`) | `_get_multi()` 多 alias 回退 | | PershareIndex | `eps` | `s_fa_eps_basic` | 同上 | | PershareIndex | `gross_profit_margin` | `sales_gross_profit`(或 `gross_profit`) | 同上 | | PershareIndex | `net_profit_margin` | `du_profit_rate`(或 `net_profit`) | 同上 | | PershareIndex | `inc_revenue_year_on_year` | `inc_revenue_rate` | 同上 | | PershareIndex | `inc_operation_profit_year_on_year` | `inc_net_profit_rate` | 同上 | | PershareIndex | `inc_total_revenue_year_on_year` | `inc_total_revenue_annual` | 同上 | | Balance | `total_liability` | `tot_liab` | 同上 | | Balance | `total_sheet_owner_equities` | `tot_shrhldr_eqy_excl_min_int`(或 `total_equity`) | 同上 | | Balance | `retained_profit` | `undistributed_profit` | 同上 | | Balance | `short_loan` / `long_loan` | `shortterm_loan` / `long_term_loans` | 同上 | ### 2.2 三个严重 bug 修复 | bug | 修复前 | 修复后 | |---|---|---| | **Capital 单位** | `_to_float(...) * 10000.0`(按"万股"放大) | 直接用,实证单位 = 股(茅台 1,256,197,800 股 = 12.56 亿股,符合现实) | | **日期格式** | `_to_date_str` 输出 `YYYY-MM-DD`,xtdata `end_time` 报"结束时间错误" | 加 `_to_yyyymmdd()` 转 `YYYYMMDD` | | **百分数口径** | miniQMT 返回 10.57(=10.57%),策略阈值 `roe > 0.15`(=15%)按小数设计 → 全部误通过 | 加 `_pct_to_decimal()` 在 provider 输出归一到小数(0.1057),对齐聚宽 indicator 口径 | ### 2.3 其他口径偏差(已记录,未改) | 项 | 现状 | 说明 | |---|---|---| | **ROE 口径** | miniQMT `du_return_on_equity` 是 YTD 累计(Q1=10.57%,年化约 30%) | 策略阈值 `roe > 0.15` 是 TTM 年化口径,Q1 累计数据通过率低。**未自动年化**(季节性偏差大),策略层后续可改取 Q4 报告或自算 TTM | | **PE 口径** | EPS 来自单季,×4 近似 TTM | 茅台 PE=14.4(实际 ~25),偏差源于 Q1 EPS × 4 不等于 TTM EPS(茅台 Q4 业绩最重) | | **PS / PCF / ROIC** | Income/CashFlow trading hours 下载超时,oper_profit/cash_flow NaN | provider 加了 EPS × total_capital 兜底单季净利润,但 oper_profit/cash_flow 无替代源,PS/PCF/ROIC 实测 0% 非空 | | **ROA 口径** | PershareIndex 无 roa 字段 | 用 ROE × (归母权益/总资产) 自算,茅台 0.0895(≈8.95%) | ## 3. Provider 冒烟实证(600519.SH 茅台) `provider.get_fundamentals_df(['600519.SH'], date='2026-07-17')` 返回: | 字段 | 实测值 | 用户期望 | 验证 | |---|---|---|---| | roe(归一小数) | **0.1057** | ROE≈10% | ✅ | | gross_profit_margin | **0.8976** | 毛利率≈92% | ✅(Q1 季节性略低) | | eps(元) | 21.76 | 合理 | ✅ | | market_cap(亿元) | **15663** | 1.5-2 万亿 | ✅(close=1253) | | circulating_market_cap(亿元) | 15663 | 同上 | ✅ | | pe_ratio | **14.4** | 实际 ~25,Q1×4 偏低 | ⚠️(口径偏差,见 2.3) | | pb_ratio | 5.78 | 合理 | ✅ | | roa(自算) | 0.0895 | 合理 | ✅ | | total_liability(元) | 38.8B | 财报匹配 | ✅ | | total_sheet_owner_equities(元) | 270.9B | 财报匹配 | ✅ | | ps_ratio | 0.97 | Income 下载成功后能算 | ✅ | | pcf_ratio | NaN | CashFlow 缺 | ❌ | | roic | NaN | oper_profit 缺 | ❌ | ## 4. 短回测结果 **配置**:39 只 HS300 权重股子集,2025-04-17 → 2026-07-17(15 个月),单次选股快照, 等权持仓至期末。 ### 4.1 字段非空率(39 只子集) | 字段 | 非空数 | 占比 | |---|---|---| | roe / roa / market_cap / pb / net_profit_margin / inc_revenue_yoy | 38/39 | 97% | | eps / pe_ratio | 37/39 | 95% | | ps_ratio(依赖 Income) | 38/39 | 97% | | gross_profit_margin | 26/39 | 67%(银行/券商PershareIndex 该字段为 NaN) | | **pcf_ratio(依赖 CashFlow)** | **0/39** | **0%** | | **roic(依赖 oper_profit)** | **0/39** | **0%** | ### 4.2 选股名单(4 个 filter 函数分别执行) | 函数 | 选出 | 名单 | |---|---|---| | `small()` (roe>0.15, roa>0.10, market_cap asc) | 1 | 600585.XSHG 海螺水泥 | | `big()` (pe∈0-30, ps∈0-8, pcf<10, eps>0.3, roe>0.1, npm>0.1, gpm>0.3, rev_yoy>0.25) | 0 | (pcf NaN 被过滤掉,Q1 累计 roe 不达 0.1 年化阈值) | | `bm()` (中市值价值股,pcf<4) | 0 | (同 pcf NaN 问题) | | `roic_big()` (roic>0.08) | 0 | (roic 全 NaN) | | **合并选股** | **1** | **600585.XSHG** | **选股少的原因**: 1. ROE 是 Q1 累计(10.57% 对茅台这种 TTM 30% 的股),归一到 0.1057 < 0.15 阈值,大部分被过滤 2. pcf_ratio 全 NaN,触发 `df["pcf_ratio"] < 10` 时 NaN 行被丢弃 3. roic 全 NaN,roic_big 空产 ### 4.3 收益曲线(等权持仓 2025-04-17 → 2026-07-17) | 项目 | 收益率 | |---|---| | 组合(600585 等权) | **-30.21%** | | 基准 HS300 (000300.XSHG) | **+24.55%** | | 超额收益 | -54.77% | **说明**:单只选股 + 单期快照不构成有效策略回测,仅用于验证 pipeline 连通。 真实回测需要每月调仓 + 多期 + 完整 HS300 池 + 完整 TTM ROE/PCF/ROIC 数据。 ## 5. 聚宽数值对账状态 | 项 | 状态 | |---|---| | **聚宽同期数值对账** | ❌ **缺基准**(用户不续费 jqdata,铁律不装 jqdatasdk) | | 自洽验证 | ✅ provider 连通 miniQMT,所有可计算字段(ROE/毛利率/PE/PB/PS/市值/负债/权益)数值合理 | | 选股合理性 | ✅ 选股逻辑跑通,filter 函数无报错,每只股的财务指标符合行业常识 | | 茅台 ROE/毛利率实证 | ✅ 10.57% / 89.76%(Q1 累计),与公开财报一致 | | 茅台 PE 实证 | ⚠️ 14.4(Q1×4 近似 TTM 偏低,实际 ~25),口径差异已记录 | ## 6. 已修 / 待修清单 ### ✅ 已修(本次提交) 1. provider 字段 alias:11 个字段加 `_get_multi()` 多 alias 回退 2. Capital 单位 bug:移除 ×10000(miniQMT 实际返回股数) 3. 日期格式:`_to_yyyymmdd()` 转 YYYYMMDD 给 xtdata `end_time` 4. 百分数归一:PershareIndex 的 ROE/ROA/毛利率/净利率/同比全部 ÷100 到小数口径 5. Income 空表兜底:EPS × total_capital 算单季净利润(calc_pe 内 ×4 近似 TTM) 6. ROA 自算:ROE × (归母权益 / 总资产) 7. 收窄 `download_financial_data` 默认表清单到 `['PershareIndex', 'Balance', 'Capital']`(trading hours Income/CashFlow 常超时) 8. conftest Capital mock 单位对齐(万股 → 股) 9. test_provider 过滤断言对齐归一后口径(`>30` → `>0.3`) ### ⚠️ 待修(策略层,下个迭代) 1. **ROE TTM 化**:当前 Q1 累计导致 roe>0.15 过滤过严,应取 Q4 报告或自算滚 4 季度 TTM 2. **PCF / ROIC 数据源**:CashFlow/oper_profit 全空,考虑: - 盘后批量下载 CashFlow 表(trading hours 超时) - 用 PershareIndex 的 `s_fa_cfps` × total_capital 兜底经营现金流 - 用 `net_profit / (1 - tax_rate)` 兜底 oper_profit 3. **真实回测驱动**:当前 mini_backtest.py 是单期快照;接 bullet-trade BacktestEngine 跑月度调仓序列需另做(runner_backtest.py 已写框架,需对齐 BT 0.9.2 API) 4. ** benchmark 沪深300 完整 300 只**:当前子集 39 只只验证 pipeline,扩到全 300 只再跑完整调仓 ## 7. 复现命令(VPS) ```cmd :: 1. 同步代码(Mac 端) cd ~/.openclaw/sanguo_projects/sanguo_vnpy_v2 tar -czf /tmp/sp.tar.gz --exclude='__pycache__' --exclude='*.pyc' sanguo_portfolio/ tests/portfolio/ scp /tmp/sp.tar.gz 49.232.102.198:C:/sanguo_vnpy_v2/sanguo_portfolio_sync.tar.gz :: 2. VPS 端解压 + 测试 ssh 49.232.102.198 cd C:\sanguo_vnpy_v2 tar -xzf sanguo_portfolio_sync.tar.gz set DEFAULT_DATA_PROVIDER=miniqmt C:\Python310\python.exe -m pytest tests/portfolio -q :: 3. provider 冒烟(茅台) C:\Python310\python.exe -X utf8 _smoke_provider.py :: 4. 预下载 HS300 子集 + 回测 C:\Python310\python.exe -X utf8 _predl.py C:\Python310\python.exe -X utf8 _mini_backtest.py ``` ## 8. 关键代码位置 - provider 主文件:`sanguo_portfolio/providers/sanguo_fundamentals.py` - 策略层:`sanguo_portfolio/strategies/all_weather.py` - 因子(ROIC/估值自算):`sanguo_portfolio/factors/{roic,valuation}.py` - 过滤器:`sanguo_portfolio/filters.py` - 回测入口(框架):`sanguo_portfolio/runner_backtest.py`(接 BacktestEngine 待迭代) - 简化回测驱动(本次用):VPS `_mini_backtest.py`(探针脚本,未提交)