Skip to content

feat(family-15): add 17 risk/performance metrics#54

Open
kingchenc wants to merge 2 commits into
mainfrom
feat/family-15-risk-performance
Open

feat(family-15): add 17 risk/performance metrics#54
kingchenc wants to merge 2 commits into
mainfrom
feat/family-15-risk-performance

Conversation

@kingchenc
Copy link
Copy Markdown
Owner

Summary

Adds Family 15 — Risk / Performance as 17 standard Indicators, taking
the codebase from 71 → 88 indicators across nine families. Implemented
pragmatically as Indicator<f64> / Indicator<(f64, f64)> rather than a
separate wickra-metrics crate (as the original
indicator-ideas/families/15-risk-performance.md suggested) — keeps the
public 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, output u32.
  • 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

  • Core: 17 new files under crates/wickra-core/src/indicators/,
    mod.rs + crates/wickra-core/src/lib.rs re-exports.
  • Python bindings: 17 new #[pyclass]es + add_class registrations
    • __init__.py __all__ extension.
  • Node bindings: 17 new #[napi] wrappers (scalar + pair) +
    index.js destructure / exports.
  • WASM bindings: 12 scalar metrics via the existing
    wasm_scalar_indicator! macro + 5 handwritten (DrawdownDuration,
    RecoveryFactor, Treynor, InformationRatio, Alpha).
  • Fuzz: scalar metrics appended to fuzz_targets/indicator_update.rs,
    new fuzz_targets/indicator_update_pair.rs for (f64, f64) inputs
    (registered in fuzz/Cargo.toml).
  • Python tests: SCALAR list extended in test_new_indicators.py,
    new PAIR parameter list with test_pair_streaming_matches_batch,
    17 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 new Family-15 benches in
    crates/wickra/benches/indicators.rs.
  • Docs: README family-table row, indicator counter 71 → 88 (line 112
    • structure diagram), CHANGELOG.md [Unreleased] entry.
  • Wiki drafts (not pushed; live in 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.md
    deltas).

Notes on Family 12 / merge conflict

Family 12 (PR #51feat/family-12-statistik-regression) introduces
node_pair_indicator! and wasm_pair_indicator! macros for the
Indicator<(f64, f64)> pattern (Pearson / Beta / Spearman). Family 12
is not yet merged into main and 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.rs and bindings/wasm/src/lib.rs flagging the
conflict.

Merge resolution when PR #51 lands: keep the macros from Family 12
and replace the three handwritten Family 15 pair wrappers with macro
invocations:

// node
node_pair_indicator!(TreynorRatioNode, "TreynorRatio", wc::TreynorRatio);    // needs risk_free arg
node_pair_indicator!(InformationRatioNode, "InformationRatio", wc::InformationRatio);
node_pair_indicator!(AlphaNode, "Alpha", wc::Alpha);                          // needs risk_free arg

Two of the three (Treynor / Alpha) have an extra risk_free parameter
that the existing node_pair_indicator! macro doesn't take — those will
need a small macro generalisation or stay handwritten. Trivial.

Stolpersteine encountered

  1. MaxDrawdown semantics: implemented as rolling-window only
    (consistent with other rolling indicators). Setting period ≥ full
    bar count gives effectively cumulative behaviour. RecoveryFactor
    uses cumulative-from-start.
  2. VaR / CVaR rolling: VecDeque + clone + sort per update,
    O(period log period). Adequate for typical period ≤ 252.
  3. Edge cases: flat windows (zero stddev / variance / drawdown) return
    0.0 rather than NaN; no-loser windows for Profit Factor / GLR /
    Omega return f64::INFINITY; Kelly clamps to −1.0 on all-loser
    windows.
  4. DrawdownDuration output is u32, not f64 — Node batch() and
    Python batch() widen to f64 (NaN for warmup, integer values cast
    to float) to keep Float64Array / np.float64 array contract.

Test plan

  • cargo check --workspace --all-features (green).
  • cargo test -p wickra-core --lib (740 tests pass, includes the
    new families).
  • CI: 28 jobs + codecov/patch on the PR.
  • Python pytest bindings/python post-build (deferred to CI).
  • Node npm test post-build (deferred to CI).

Per the project workflow, full cargo test --workspace / cargo clippy
runs are left to CI; this PR only ran cargo check locally per the
lean-batch instruction.

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
Copy link
Copy Markdown

codecov Bot commented May 25, 2026

…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant