wbt 0.2.3

Weight-based backtesting engine for quantitative trading
# wbt v0.2.3

> Release date: 2026-05-23
> Crate tag: `crate-v0.2.3` · Python tag: `v0.2.3`

## Summary

PATCH release with two related corrections on top of v0.2.2:

1. `segment_stats` / `long_stats` / `short_stats` now apply **kind-aware**
   long/short rate semantics: single-side `kind` zeros the opposite rate.
2. HTML report's multi-strategy comparison chart restores the
   **volatility-normalized middle subplot** (lost when this repo forked
   from czsc) and adds a **多头超额** curve on top of it.

No public API additions/removals.

## Fixes

- **`kind` 单边语义生效到 `多头占比` / `空头占比`**:
  v0.2.2 让 `segment_stats(kind=...)` 在三种 `kind` 下都返回同一对 long/short
  比例,与 `kind="多头"` 只看多头侧的语义不一致。现修复:
  - `kind="多头"``空头占比 = 0`,仅保留多头占比;
  - `kind="空头"``多头占比 = 0`,仅保留空头占比;
  - `kind="多空"` → 两者照旧给出 [sdt, edt] 范围内的真实权重行占比。
  - `wb.long_stats` / `wb.short_stats` 也遵循同一规则。
  - Rust 测试 `segment_stats_contains_long_short_rate` + Python 测试
    `TestSegmentStatsLongShortRate::test_kind_single_side_zeros_opposite` 显式覆盖。

- **找回 HTML 报告的「波动率归一收益对比」子图 + 新增「多头超额」曲线**:
  `wbt.report._plot_backtest.plot_long_short_comparison` 是从上游 czsc fork 出来
  的简化版,错过了 czsc commit `e7610f5` 加入的波动率调整子图。现把图表从 2 行
  扩展到 3 行:
  - 上:原始累计收益对比
  - 中:按 `target_volatility`(默认 20%)归一后的累计收益对比 + **多头超额** 曲线
    `多头超额 = scaled(策略多头).cumsum() − scaled(基准等权).cumsum()`    口径与 `WeightBacktest.long_alpha_stats` 一致);多头超额用 `#FF1493` 深粉、
    3.5px 实线,与策略主曲线明显区分。
  - 下:绩效指标对比表
  - 配套 `_generator.py``fig_ls` 高度从 1000 提到 1400 避免压缩。
  - 上、中两图通过 `legendgroup` 联动显隐;中图主曲线 `showlegend=False`
    避免与上图图例重复。
  - 三条 Python 单测覆盖:子图存在、多头超额数值正确(`np.testing.assert_allclose`
    与手算 `scaled(策略多头).cumsum() - scaled(基准等权).cumsum()` 比较)、
    缺列时优雅跳过。

## Compatibility

- `BREAKING CHANGE`: 严格意义无破坏性。`segment_stats` / `long_stats` /
  `short_stats` 三个 dict 的字段集合不变,仅 v0.2.2 新增的 `多头占比` /
  `空头占比` 在单边 `kind` 下变成 0;依赖这俩字段恒等于 `wb.stats` 全局占比的
  代码会观察到差异——这正是被修复的语义不一致。
- Python: requires ≥ 3.10 (unchanged).
- Rust: edition 2024, `pyo3 = 0.28` / `numpy = 0.28` (unchanged).

## Release checklist outcome

- **§1 SemVer**: 修两处语义错位,公共 API 表面 (`__init__.py` / `_wbt.pyi`)
  无 diff → PATCH bump (`0.2.2 → 0.2.3`)。
- **§2 Lint/typecheck**: `cargo fmt --check``cargo clippy -D warnings`  `cargo test --lib`(143 passed)、`ruff check``ruff format --check`  `basedpyright` 全清。
- **§3 Tests**: `pytest` 241 passed / 1 skipped(含 4 条新增测试:
  1 条 kind 单边占比、3 条波动率归一子图 / 多头超额曲线 / 缺列保护)。
- **§4 LLM review**: 范围限定在
  `src/core/backtest.rs:280`(do_backtest 注入零)+
  `src/core/backtest.rs:443`(segment_stats kind-match)+
  `python/wbt/report/_plot_backtest.py:636`(subplot 重排 + 多头超额)+
  `python/wbt/report/_generator.py:296`(高度调整)。重点核查:
  (a) `match kind` 分支覆盖("多头" / "空头" / 其他默认 "多空",对齐
  现有 `segment_stats_invalid_kind_defaults_to_all` 单测);
  (b) 多头超额的 cumsum 顺序——`A.cumsum() − B.cumsum() ≡ (A − B).cumsum()`  数值无差;(c) 长度对齐——`adjusted_returns``dailys_pivot.index` 共享 index,
  cumsum 自然按行对齐;(d) `legendgroup` 联动不污染图例显示。
- **§5 doc-vs-code**: 同步更新 `WeightBacktest.segment_stats` docstring,
  显式说明 `kind` 单边语义。

## Known issues (carried forward from v0.2.0)

Unchanged; see `docs/release_notes/v0.2.0.md`.