From ea684e0d488e21bbde6f5faef961f3a8f55985c3 Mon Sep 17 00:00:00 2001 From: kingchenc Date: Sat, 30 May 2026 14:58:16 +0200 Subject: [PATCH] feat(family-api): add FAMILIES const and family-taxonomy tests (#60) * feat(family-api): add FAMILIES const and family-taxonomy tests Introduce `wickra_core::FAMILIES`, a `&'static [(&str, &[&str])]` mapping every built-in indicator to one of 16 families (Moving Averages, Momentum Oscillators, Trend & Directional, Price Oscillators, Volatility & Bands, Bands & Channels, Trailing Stops, Volume, Price Statistics, Ehlers / Cycle (DSP), Pivots & S/R, DeMark, Ichimoku & Charts, Candlestick Patterns, Market Profile, Risk / Performance). Two compile-time-anchored guards make sure the const stays trustworthy: - `no_duplicates_across_families`: no indicator is listed in two families - `total_count_matches_expected`: hard-coded total bumps in lockstep with the indicator catalogue (214 today), so any new indicator added without being filed under a family trips the test. Also corrects the module doc comment which falsely claimed indicators were grouped by category internally; the `mod` block is alphabetical, and the canonical taxonomy now lives in `FAMILIES`. * chore: sync indicator count to 214 --------- Co-authored-by: wickra-bot --- crates/wickra-core/src/indicators/mod.rs | 327 ++++++++++++++++++++++- crates/wickra-core/src/lib.rs | 2 +- 2 files changed, 326 insertions(+), 3 deletions(-) diff --git a/crates/wickra-core/src/indicators/mod.rs b/crates/wickra-core/src/indicators/mod.rs index 73512d51..56be301c 100644 --- a/crates/wickra-core/src/indicators/mod.rs +++ b/crates/wickra-core/src/indicators/mod.rs @@ -1,7 +1,7 @@ //! Built-in indicators. Every indicator implements [`crate::Indicator`]. //! -//! Modules are organised internally by category (trend, momentum, volatility, -//! volume) but every public name is also re-exported flat from this module and +//! Modules are listed alphabetically; the canonical family taxonomy lives in +//! [`FAMILIES`]. Every public name is re-exported flat from this module and //! from the crate root for convenience. mod acceleration_bands; @@ -431,3 +431,326 @@ pub use z_score::ZScore; pub use zero_lag_macd::{ZeroLagMacd, ZeroLagMacdOutput}; pub use zig_zag::{ZigZag, ZigZagOutput}; pub use zlema::Zlema; + +/// Family classification of every built-in indicator. The (family, +/// indicators) list is the single source of truth used by `family_tests` +/// below; README and Wiki taxonomy tables should be kept in sync with it. +/// +/// Each indicator appears in exactly one family. Names are the public +/// struct identifiers re-exported from this module (and the crate root). +pub const FAMILIES: &[(&str, &[&str])] = &[ + ( + "Moving Averages", + &[ + "Sma", + "Ema", + "Wma", + "Dema", + "Tema", + "Hma", + "Kama", + "Smma", + "Trima", + "Zlema", + "T3", + "Vwma", + "Alma", + "McGinleyDynamic", + "Frama", + "Vidya", + "Jma", + "Alligator", + "Evwma", + ], + ), + ( + "Momentum Oscillators", + &[ + "Rsi", + "Stochastic", + "Cci", + "Roc", + "WilliamsR", + "Mfi", + "AwesomeOscillator", + "Mom", + "Cmo", + "Tsi", + "Pmo", + "StochRsi", + "UltimateOscillator", + "Rvi", + "Pgo", + "Kst", + "Smi", + "LaguerreRsi", + "ConnorsRsi", + "Inertia", + ], + ), + ( + "Trend & Directional", + &[ + "MacdIndicator", + "Adx", + "Adxr", + "Aroon", + "Trix", + "AroonOscillator", + "Vortex", + "Rwi", + "Tii", + "WaveTrend", + "MassIndex", + "ChoppinessIndex", + "VerticalHorizontalFilter", + ], + ), + ( + "Price Oscillators", + &[ + "Ppo", + "Dpo", + "Coppock", + "AcceleratorOscillator", + "BalanceOfPower", + "Apo", + "AwesomeOscillatorHistogram", + "Cfo", + "ZeroLagMacd", + "ElderImpulse", + "Stc", + ], + ), + ( + "Volatility & Bands", + &[ + "Atr", + "BollingerBands", + "Keltner", + "Donchian", + "Natr", + "StdDev", + "UlcerIndex", + "HistoricalVolatility", + "BollingerBandwidth", + "PercentB", + "TrueRange", + "ChaikinVolatility", + "RviVolatility", + "ParkinsonVolatility", + "GarmanKlassVolatility", + "RogersSatchellVolatility", + "YangZhangVolatility", + ], + ), + ( + "Bands & Channels", + &[ + "MaEnvelope", + "AccelerationBands", + "StarcBands", + "AtrBands", + "HurstChannel", + "LinRegChannel", + "StandardErrorBands", + "DoubleBollinger", + "TtmSqueeze", + "FractalChaosBands", + "VwapStdDevBands", + ], + ), + ( + "Trailing Stops", + &[ + "Psar", + "SuperTrend", + "ChandelierExit", + "ChandeKrollStop", + "AtrTrailingStop", + "HiLoActivator", + "VoltyStop", + "YoyoExit", + "DonchianStop", + "PercentageTrailingStop", + "StepTrailingStop", + "RenkoTrailingStop", + ], + ), + ( + "Volume", + &[ + "Obv", + "Vwap", + "RollingVwap", + "Adl", + "VolumePriceTrend", + "ChaikinMoneyFlow", + "ChaikinOscillator", + "ForceIndex", + "EaseOfMovement", + "Kvo", + "VolumeOscillator", + "Nvi", + "Pvi", + "AdOscillator", + "AnchoredVwap", + "DemandIndex", + "Tsv", + "Vzo", + "MarketFacilitationIndex", + ], + ), + ( + "Price Statistics", + &[ + "TypicalPrice", + "MedianPrice", + "WeightedClose", + "LinearRegression", + "LinRegSlope", + "ZScore", + "LinRegAngle", + "Variance", + "CoefficientOfVariation", + "Skewness", + "Kurtosis", + "StandardError", + "DetrendedStdDev", + "RSquared", + "MedianAbsoluteDeviation", + "Autocorrelation", + "HurstExponent", + "PearsonCorrelation", + "Beta", + "SpearmanCorrelation", + ], + ), + ( + "Ehlers / Cycle (DSP)", + &[ + "Mama", + "Fama", + "FisherTransform", + "InverseFisherTransform", + "SuperSmoother", + "HilbertDominantCycle", + "SineWave", + "Decycler", + "DecyclerOscillator", + "RoofingFilter", + "CenterOfGravity", + "CyberneticCycle", + "AdaptiveCycle", + "EmpiricalModeDecomposition", + "EhlersStochastic", + "InstantaneousTrendline", + ], + ), + ( + "Pivots & S/R", + &[ + "ClassicPivots", + "FibonacciPivots", + "Camarilla", + "WoodiePivots", + "DemarkPivots", + "WilliamsFractals", + "ZigZag", + ], + ), + ( + "DeMark", + &[ + "TdSetup", + "TdSequential", + "TdDeMarker", + "TdRei", + "TdPressure", + "TdCombo", + "TdCountdown", + "TdLines", + "TdRangeProjection", + "TdDifferential", + "TdOpen", + "TdRiskLevel", + ], + ), + ("Ichimoku & Charts", &["Ichimoku", "HeikinAshi"]), + ( + "Candlestick Patterns", + &[ + "Doji", + "Hammer", + "InvertedHammer", + "HangingMan", + "ShootingStar", + "Engulfing", + "Harami", + "MorningEveningStar", + "ThreeSoldiersOrCrows", + "PiercingDarkCloud", + "Marubozu", + "Tweezer", + "SpinningTop", + "ThreeInside", + "ThreeOutside", + ], + ), + ( + "Market Profile", + &["ValueArea", "InitialBalance", "OpeningRange"], + ), + ( + "Risk / Performance", + &[ + "SharpeRatio", + "SortinoRatio", + "CalmarRatio", + "OmegaRatio", + "MaxDrawdown", + "AverageDrawdown", + "DrawdownDuration", + "PainIndex", + "ValueAtRisk", + "ConditionalValueAtRisk", + "ProfitFactor", + "GainLossRatio", + "RecoveryFactor", + "KellyCriterion", + "TreynorRatio", + "InformationRatio", + "Alpha", + ], + ), +]; + +#[cfg(test)] +mod family_tests { + use super::FAMILIES; + + #[test] + fn no_duplicates_across_families() { + let mut names: Vec<&str> = FAMILIES + .iter() + .flat_map(|(_, ns)| ns.iter().copied()) + .collect(); + let len_before = names.len(); + names.sort_unstable(); + names.dedup(); + assert_eq!( + names.len(), + len_before, + "duplicate indicator across families" + ); + } + + #[test] + fn total_count_matches_expected() { + // Bump together with new indicators. Drift between this number and + // the actual indicator count is the early-warning signal that an + // indicator was added without being assigned a family. + let total: usize = FAMILIES.iter().map(|(_, ns)| ns.len()).sum(); + assert_eq!(total, 214, "FAMILIES total drifted from indicator count"); + } +} diff --git a/crates/wickra-core/src/lib.rs b/crates/wickra-core/src/lib.rs index dfe7c3d7..c39ff2ba 100644 --- a/crates/wickra-core/src/lib.rs +++ b/crates/wickra-core/src/lib.rs @@ -83,7 +83,7 @@ pub use indicators::{ VolumeOscillator, VolumePriceTrend, Vortex, VortexOutput, Vwap, VwapStdDevBands, VwapStdDevBandsOutput, Vwma, Vzo, WaveTrend, WaveTrendOutput, WeightedClose, WilliamsFractals, WilliamsFractalsOutput, WilliamsR, Wma, WoodiePivots, WoodiePivotsOutput, YangZhangVolatility, - YoyoExit, ZScore, ZeroLagMacd, ZeroLagMacdOutput, ZigZag, ZigZagOutput, Zlema, T3, + YoyoExit, ZScore, ZeroLagMacd, ZeroLagMacdOutput, ZigZag, ZigZagOutput, Zlema, FAMILIES, T3, }; pub use ohlcv::{Candle, Tick}; pub use traits::{BatchExt, Chain, Indicator};