feat(family-15): add 17 risk/performance metrics#54
Open
kingchenc wants to merge 2 commits into
Open
Conversation
Implements Family 15 pragmatically as standard `Indicator`s instead of a separate `wickra-metrics` crate. Input is scalar `f64` per bar — period return, equity sample, or per-trade P&L depending on the metric. Scalar `Indicator<f64>` (14): - SharpeRatio(period, risk_free) - SortinoRatio(period, mar) - CalmarRatio(period) - OmegaRatio(period, threshold) - MaxDrawdown(period) — rolling, peak-to-trough - AverageDrawdown(period) - DrawdownDuration — cumulative, bars under water (u32 output) - PainIndex(period) - ValueAtRisk(period, confidence) - ConditionalValueAtRisk(period, confidence) - ProfitFactor(period) - GainLossRatio(period) - RecoveryFactor — cumulative, net return / max drawdown - KellyCriterion(period) Two-series `Indicator<(f64, f64)>` for (asset, benchmark) returns (3): - TreynorRatio(period, risk_free) - InformationRatio(period) - Alpha(period, risk_free) — Jensen / CAPM Touchpoints: - 17 new files under `crates/wickra-core/src/indicators/`. - `mod.rs` + `lib.rs` re-exports. - Python bindings (`bindings/python/src/lib.rs`, `__init__.py`). - Node bindings (`bindings/node/src/lib.rs`, `index.js`). - WASM bindings (`bindings/wasm/src/lib.rs`). - Fuzz: scalar metrics appended to `indicator_update.rs`; new `indicator_update_pair.rs` fuzz target for `(f64, f64)` indicators. - Python tests: SCALAR + new PAIR parameter lists in `test_new_indicators.py`, reference-value cases in `test_known_values.py`. - Node tests: scalar factories + new pair-factory block in `bindings/node/__tests__/indicators.test.js`. - Benches: 5 Family-15 benches added in `crates/wickra/benches/indicators.rs`. - Docs: README family-table row + counter (71 -> 88), CHANGELOG entry under [Unreleased]. Note: Family 12 (statistik-regression, PR #51) introduces `node_pair_indicator!` and `wasm_pair_indicator!` macros for Pearson / Beta / Spearman. Family 15 needs the same pair-input pattern but Family 12 is not yet in main, so the three pair wrappers below are written by hand in this PR. When PR #51 lands, the trivial merge-conflict is resolved by keeping the macros from Family 12 and re-using them for Treynor / IR / Alpha (drop the three handwritten wrappers). cargo check --workspace --all-features: green.
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
…isk-performance Half-finished merge resolution — conflict markers cleared but build/tests NOT verified yet. Continuing on another machine: needs fmt, test, clippy, and codecov patch coverage cleanup before merge.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Family 15 — Risk / Performance as 17 standard
Indicators, takingthe codebase from 71 → 88 indicators across nine families. Implemented
pragmatically as
Indicator<f64>/Indicator<(f64, f64)>rather than aseparate
wickra-metricscrate (as the originalindicator-ideas/families/15-risk-performance.mdsuggested) — keeps thepublic surface consistent and lets every binding pick the metrics up for
free.
14 scalar metrics (
Indicator<Input = f64>)Input = period return / equity sample / per-trade P&L.
SharpeRatio(period, risk_free)—(mean − rf) / sample_stddev.SortinoRatio(period, mar)— Sharpe with downside-only volatility.CalmarRatio(period)— mean return / max drawdown of equity curve.OmegaRatio(period, threshold)— Σ gains-above-threshold / Σ losses-below.MaxDrawdown(period)— rolling peak-to-trough decline, monotone deque.AverageDrawdown(period)— mean drawdown depth (= Pain Index by another name).DrawdownDuration— cumulative bars under water, outputu32.PainIndex(period)— Becker's mean-dd risk measure.ValueAtRisk(period, confidence)— historical VaR with linear-interpolated percentile.ConditionalValueAtRisk(period, confidence)— expected loss in tail (CVaR / ES).ProfitFactor(period)— Σ gains / Σ losses.GainLossRatio(period)— avg win / avg loss.RecoveryFactor— cumulative net return / max drawdown.KellyCriterion(period)—win_rate − (1 − win_rate) / payoff.3 pair metrics (
Indicator<Input = (f64, f64)>)Input =
(asset_return, benchmark_return).TreynorRatio(period, risk_free)—(mean_asset − rf) / Beta_vs_bench.InformationRatio(period)—mean(active) / tracking_error.Alpha(period, risk_free)— Jensen's Alpha (CAPM regression).Touchpoints
crates/wickra-core/src/indicators/,mod.rs+crates/wickra-core/src/lib.rsre-exports.#[pyclass]es +add_classregistrations__init__.py__all__extension.#[napi]wrappers (scalar + pair) +index.jsdestructure / exports.wasm_scalar_indicator!macro + 5 handwritten (DrawdownDuration,RecoveryFactor, Treynor, InformationRatio, Alpha).
fuzz_targets/indicator_update.rs,new
fuzz_targets/indicator_update_pair.rsfor(f64, f64)inputs(registered in
fuzz/Cargo.toml).SCALARlist extended intest_new_indicators.py,new
PAIRparameter list withtest_pair_streaming_matches_batch,17 reference-value cases in
test_known_values.py.bindings/node/__tests__/indicators.test.js.crates/wickra/benches/indicators.rs.71 → 88(line 112CHANGELOG.md[Unreleased]entry.indicator-ideas/families/wiki/family-15-risk-performance/in this branch's working tree but ghost-ignored — 17 page drafts +
_Sidebar.md/Indicators-Overview.md/Warmup-Periods.md/Home.mddeltas).
Notes on Family 12 / merge conflict
Family 12 (PR #51 —
feat/family-12-statistik-regression) introducesnode_pair_indicator!andwasm_pair_indicator!macros for theIndicator<(f64, f64)>pattern (Pearson / Beta / Spearman). Family 12is not yet merged into
mainand this PR also needs that pattern —the three pair wrappers (Treynor / InformationRatio / Alpha) are
therefore written by hand here, plus a header note in
bindings/node/src/lib.rsandbindings/wasm/src/lib.rsflagging theconflict.
Merge resolution when PR #51 lands: keep the macros from Family 12
and replace the three handwritten Family 15 pair wrappers with macro
invocations:
Two of the three (Treynor / Alpha) have an extra
risk_freeparameterthat the existing
node_pair_indicator!macro doesn't take — those willneed a small macro generalisation or stay handwritten. Trivial.
Stolpersteine encountered
MaxDrawdownsemantics: implemented as rolling-window only(consistent with other rolling indicators). Setting
period≥ fullbar count gives effectively cumulative behaviour.
RecoveryFactoruses cumulative-from-start.
VaR/CVaRrolling:VecDeque + clone + sortper update,O(period log period). Adequate for typical
period ≤ 252.0.0rather thanNaN; no-loser windows for Profit Factor / GLR /Omega return
f64::INFINITY; Kelly clamps to−1.0on all-loserwindows.
DrawdownDurationoutput isu32, notf64— Nodebatch()andPython
batch()widen tof64(NaN for warmup, integer values castto float) to keep
Float64Array/np.float64array contract.Test plan
cargo check --workspace --all-features(green).cargo test -p wickra-core --lib(740 tests pass, includes thenew families).
pytest bindings/pythonpost-build (deferred to CI).npm testpost-build (deferred to CI).Per the project workflow, full
cargo test --workspace/cargo clippyruns are left to CI; this PR only ran
cargo checklocally per thelean-batch instruction.