# wbt v0.5.0
> Release date: 2026-07-15
> Crate tag: `crate-v0.5.0` · Python tag: `v0.5.0`
## Summary
MINOR release. **BREAKING CHANGE** (SKZ-195): all return / net-value accumulation
inside `wbt` is unified to **simple interest** (`Σr`). Previously the yearly
return and the `is_good_strategy` yearly/recent metrics used **compound**
(`∏(1+r)-1`) while the main stats (absolute return, max drawdown, equity curve)
used simple interest — the two conventions were mixed within a single backtest.
After this release there is **no compound (`∏(1+r)` / net-value) accumulation
anywhere in the framework**; everything is single-interest, consistent with
`daily_performance`'s absolute return and the `np.cumsum` equity curve.
Public API signatures are **unchanged**; only the numeric convention changes.
## BREAKING CHANGE: compound → simple interest
### `WeightBacktest.yearly_return()`
- Yearly return per `(year, symbol)` changed from compound `∏(1+r)-1` to simple
`Σr`. Schema (`year, symbol, return`) is unchanged; values differ.
Old → new (example, daily returns `[0.10, 0.10]` in one year):
- old (compound): `1.10 * 1.10 - 1 = 0.21`
- new (simple): `0.10 + 0.10 = 0.20`
### `is_good_strategy()` (both `history` and `recent` modes)
- `abs_return` / `alpha_return` (per-year, in `yearly_metrics`) and
`recent_abs_return` / `recent_alpha_return`: compound `∏(1+r)-1` → simple `Σr`.
- `alpha_max_drawdown`, `history_alpha_max_drawdown`,
`recent_alpha_max_drawdown`, and `history_alpha_max_drawdown_excl_recent`:
changed from compound net-value drawdown (`nav *= 1+r`, `dd = (peak-nav)/peak`)
to **simple cumsum-underwater** (`cum += r`, `dd = peak - cum`), identical in
semantics to `daily_performance::calc_underwater`.
- Note: simple-interest drawdown is an **absolute value in return space** and has
**no 1.0 upper bound** (unlike a compound net-value ratio). Callers comparing
drawdown against thresholds should keep this in mind; a threshold of `1.0` no
longer means "effectively disabled".
- Return-dict **keys are unchanged**; only the numeric convention of the values
above changes. Verdicts near thresholds may therefore differ from v0.4.x.
### Migration
- If you persisted or charted `yearly_return()` / `is_good_strategy()` numbers
from v0.4.x, recompute them under v0.5.0 — old compound values are not
comparable to new simple values.
- Any downstream (skz) gate that hard-coded a compound-scale drawdown threshold
(e.g. `max_alpha_dd_threshold` assuming a ≤1.0 ratio) should re-tune to the
return-space scale.
## Removed: vista comparison artifacts
The library no longer relates to `vista`. Removed:
- `python/scripts/compare_yearly_return_with_vista.py`
- `python/tests/test_compare_yearly_return_with_vista_script.py`
These were comparison-only tooling and not part of the public API.
## Tests
- `src/core/yearly_return.rs`, `src/core/is_good_strategy.rs`: unit tests updated
to simple-interest expected values (`known_curve` drawdown `0.15`,
cum-zero-with-real-dd `0.5`, yearly/recent `Σr`). `history_year_passes_via_abs_return_only`
raises its `max_alpha_dd_threshold` bypass `1.0 → 1e9` (test sentinel only; the
`judge` gate logic is unchanged).
- `python/tests/test_yearly_return.py`: expected formula updated to `sum()`.
- Local: `cargo fmt` / `cargo clippy -D warnings` clean, `cargo test --lib`
220 passed; `pytest` 289 passed. Full CI (ubuntu/macos/windows × py3.10–3.13)
green on `main`.
## §4 LLM full-repo review
- Scope: full diff `crate-v0.4.3..HEAD` (`src/core/{yearly_return,is_good_strategy}.rs`,
`python/wbt/backtest.py`, tests, vista removals), plus cold-read of
`daily_performance.rs`.
- Model: independent review agent.
- Conclusion: **APPROVE for release** — no blocking findings. Verified:
(1) `local_max_drawdown_abs` exactly mirrors `calc_underwater` (same
`NEG_INFINITY` peak init so day-0 never registers drawdown; `max(peak-cum) ≡
max(-underwater)`; empty→0; non-finite→NaN preserved); (2) no compound
`∏(1+r)` / `nav*=1+r` / `.prod()` accumulation remains in `src/` (only
historical-note doc comments mention it); (3) the `1.0→1e9` change is a test
sentinel, production `judge` logic unchanged; (4) no off-by-one / sign / empty /
NaN regressions in the changed functions.
- Known issues: none.