Release date: 2026-07-15 Crate tag:
crate-v0.5.0· Python tag:v0.5.0
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.
-
Yearly return per
(year, symbol)changed from compound∏(1+r)-1to 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
- old (compound):
abs_return/alpha_return(per-year, inyearly_metrics) andrecent_abs_return/recent_alpha_return: compound∏(1+r)-1→ simpleΣr.alpha_max_drawdown,history_alpha_max_drawdown,recent_alpha_max_drawdown, andhistory_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 todaily_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.0no 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.
- 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_thresholdassuming a ≤1.0 ratio) should re-tune to the return-space scale.
The library no longer relates to vista. Removed:
python/scripts/compare_yearly_return_with_vista.pypython/tests/test_compare_yearly_return_with_vista_script.py
These were comparison-only tooling and not part of the public API.
src/core/yearly_return.rs,src/core/is_good_strategy.rs: unit tests updated to simple-interest expected values (known_curvedrawdown0.15, cum-zero-with-real-dd0.5, yearly/recentΣr).history_year_passes_via_abs_return_onlyraises itsmax_alpha_dd_thresholdbypass1.0 → 1e9(test sentinel only; thejudgegate logic is unchanged).python/tests/test_yearly_return.py: expected formula updated tosum().- Local:
cargo fmt/cargo clippy -D warningsclean,cargo test --lib220 passed;pytest289 passed. Full CI (ubuntu/macos/windows × py3.10–3.13) green onmain.
- 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 ofdaily_performance.rs. - Model: independent review agent.
- Conclusion: APPROVE for release — no blocking findings. Verified:
(1)
local_max_drawdown_absexactly mirrorscalc_underwater(sameNEG_INFINITYpeak 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 insrc/(only historical-note doc comments mention it); (3) the1.0→1e9change is a test sentinel, productionjudgelogic unchanged; (4) no off-by-one / sign / empty / NaN regressions in the changed functions. - Known issues: none.