feat(family-15): add 17 risk/performance metrics (#54)
* feat(family-15): add 17 risk/performance metrics 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. * fix(family-15): satisfy clippy doc_markdown / if_not_else / digit_grouping * fix(family-15): unused TreynorRatio import, duplicate pairFactories, _eq_nan inf handling * fix(family-15): node eq() handles matching infinities for ratio indicators * test(family-15): cover cold paths flagged by codecov patch
This commit is contained in:
@@ -15,18 +15,20 @@
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use wickra_core::{
|
||||
AdaptiveCycle, Alma, Apo, Autocorrelation, BatchExt, Beta, BollingerBands, CenterOfGravity,
|
||||
Cfo, Cmo, CoefficientOfVariation, ConnorsRsi, Coppock, CyberneticCycle, Decycler,
|
||||
DecyclerOscillator, Dema, DetrendedStdDev, DoubleBollinger, Dpo, EhlersStochastic,
|
||||
ElderImpulse, Ema, EmpiricalModeDecomposition, Fama, FisherTransform, Frama,
|
||||
HilbertDominantCycle, HistoricalVolatility, Hma, HurstExponent, Indicator,
|
||||
InstantaneousTrendline, InverseFisherTransform, Jma, Kama, Kst, Kurtosis, LaguerreRsi,
|
||||
LinRegAngle, LinRegChannel, LinRegSlope, LinearRegression, MaEnvelope, MacdIndicator, Mama,
|
||||
McGinleyDynamic, MedianAbsoluteDeviation, Mom, PearsonCorrelation, PercentageTrailingStop,
|
||||
Pmo, Ppo, RSquared, RenkoTrailingStop, Roc, RoofingFilter, Rsi, RviVolatility, SineWave,
|
||||
Skewness, Sma, Smma, SpearmanCorrelation, StandardError, StandardErrorBands, Stc, StdDev,
|
||||
StepTrailingStop, StochRsi, SuperSmoother, T3, Tema, Tii, Trima, Trix, Tsi, UlcerIndex,
|
||||
Variance, VerticalHorizontalFilter, Vidya, Wma, ZScore, ZeroLagMacd, Zlema,
|
||||
AdaptiveCycle, Alma, Apo, Autocorrelation, AverageDrawdown, BatchExt, Beta, BollingerBands,
|
||||
CalmarRatio, CenterOfGravity, Cfo, Cmo, CoefficientOfVariation, ConditionalValueAtRisk,
|
||||
ConnorsRsi, Coppock, CyberneticCycle, Decycler, DecyclerOscillator, Dema, DetrendedStdDev,
|
||||
DoubleBollinger, Dpo, DrawdownDuration, EhlersStochastic, ElderImpulse, Ema,
|
||||
EmpiricalModeDecomposition, Fama, FisherTransform, Frama, GainLossRatio, HilbertDominantCycle,
|
||||
HistoricalVolatility, Hma, HurstExponent, Indicator, InstantaneousTrendline,
|
||||
InverseFisherTransform, Jma, Kama, KellyCriterion, Kst, Kurtosis, LaguerreRsi, LinRegAngle,
|
||||
LinRegChannel, LinRegSlope, LinearRegression, MaEnvelope, MacdIndicator, Mama, MaxDrawdown,
|
||||
McGinleyDynamic, MedianAbsoluteDeviation, Mom, OmegaRatio, PainIndex, PearsonCorrelation,
|
||||
PercentageTrailingStop, Pmo, Ppo, ProfitFactor, RSquared, RecoveryFactor, RenkoTrailingStop,
|
||||
Roc, RoofingFilter, Rsi, RviVolatility, SharpeRatio, SineWave, Skewness, Sma, Smma,
|
||||
SortinoRatio, SpearmanCorrelation, StandardError, StandardErrorBands, Stc, StdDev,
|
||||
StepTrailingStop, StochRsi, SuperSmoother, Tema, Tii, Trima, Trix, Tsi, UlcerIndex,
|
||||
ValueAtRisk, Variance, VerticalHorizontalFilter, Vidya, Wma, ZScore, ZeroLagMacd, Zlema, T3,
|
||||
};
|
||||
|
||||
/// Drive a single streaming + batch run through one scalar indicator. Marked
|
||||
@@ -146,6 +148,37 @@ fuzz_target!(|data: Vec<f64>| {
|
||||
drive(SineWave::new, &data);
|
||||
drive(|| Fama::new(0.5, 0.05).unwrap(), &data);
|
||||
|
||||
// Family 15 — Risk / Performance metrics (scalar inputs).
|
||||
drive(|| SharpeRatio::new(20, 0.0).unwrap(), &data);
|
||||
drive(|| SortinoRatio::new(20, 0.0).unwrap(), &data);
|
||||
drive(|| CalmarRatio::new(20).unwrap(), &data);
|
||||
drive(|| OmegaRatio::new(20, 0.0).unwrap(), &data);
|
||||
drive(|| MaxDrawdown::new(20).unwrap(), &data);
|
||||
drive(|| AverageDrawdown::new(20).unwrap(), &data);
|
||||
drive(|| PainIndex::new(20).unwrap(), &data);
|
||||
drive(|| ValueAtRisk::new(20, 0.95).unwrap(), &data);
|
||||
drive(|| ConditionalValueAtRisk::new(20, 0.95).unwrap(), &data);
|
||||
drive(|| ProfitFactor::new(20).unwrap(), &data);
|
||||
drive(|| GainLossRatio::new(20).unwrap(), &data);
|
||||
drive(|| KellyCriterion::new(20).unwrap(), &data);
|
||||
|
||||
// RecoveryFactor and DrawdownDuration produce non-`f64` outputs / have
|
||||
// no `period` knob, so they cannot use the `drive` helper directly.
|
||||
{
|
||||
let mut rf = RecoveryFactor::new();
|
||||
for &x in &data {
|
||||
let _ = rf.update(x);
|
||||
}
|
||||
let _ = RecoveryFactor::new().batch(&data);
|
||||
}
|
||||
{
|
||||
let mut dd = DrawdownDuration::new();
|
||||
for &x in &data {
|
||||
let _ = dd.update(x);
|
||||
}
|
||||
let _ = DrawdownDuration::new().batch(&data);
|
||||
}
|
||||
|
||||
// MACD, Bollinger Bands and MAMA have non-`f64` outputs, so they cannot
|
||||
// use the generic `drive` helper above. Streaming + batch are still both
|
||||
// exercised.
|
||||
|
||||
Reference in New Issue
Block a user