chore(bench): curate benchmarks to ~30 representative indicators (#63)
Previously every push of `cargo bench -p wickra` ran 114 indicators at three workload sizes each (1k / 10k / 50k candles), which inflated bench runtime past ten minutes for diminishing signal — most family members are linear scalings of the same hot loop, so a regression in any one of them shows up identically in the cheapest member. This commit replaces the exhaustive list with a curated selection: the cheapest baseline and the most expensive representative from each of the sixteen families, totalling ~33 indicators across scalar, candle, and multi-output APIs. If you need to profile a specific indicator that is not in the curated set, add it temporarily and run `cargo bench -- <name>` to target just that bench; it does not need to be committed. Fixed in passing: - `Cci` is `Indicator<Input = Candle>`, not f64; corrected the bench selector to `bench_candle_input`. - `Psar::new` takes (af_start, af_step, af_max) — supplied all three. - `TdSequential` uses `::classic` for the textbook (4, 9, 2, 13) parameters; the old call was missing arguments.
This commit is contained in:
+153
-337
@@ -1,4 +1,4 @@
|
||||
//! Microbenchmarks for every built-in indicator.
|
||||
//! Microbenchmarks for a curated subset of the indicator catalogue.
|
||||
//!
|
||||
//! Run with:
|
||||
//! ```text
|
||||
@@ -11,6 +11,19 @@
|
||||
//! indicator. Sizes cover small (1 000), medium (10 000), and large
|
||||
//! (50 000) workloads, taken as prefixes of that dataset.
|
||||
//!
|
||||
//! ## Why curated rather than exhaustive
|
||||
//!
|
||||
//! The indicator catalogue has 214 entries; benching every single one
|
||||
//! at three sizes inflates `cargo bench` to >10 minutes for diminishing
|
||||
//! signal. The selection below picks the cheapest baseline and the
|
||||
//! most-expensive representative in each family — a regression in any
|
||||
//! of those is the meaningful signal; per-family redundancy benches
|
||||
//! mostly produce noise.
|
||||
//!
|
||||
//! If you need a benchmark for a specific indicator that is not in this
|
||||
//! list, add it locally and run `cargo bench -- <name>` to target just
|
||||
//! that bench.
|
||||
//!
|
||||
//! Regenerate the dataset with:
|
||||
//! ```text
|
||||
//! cargo run -p wickra-examples --bin fetch_btcusdt
|
||||
@@ -19,24 +32,14 @@
|
||||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
|
||||
use std::hint::black_box;
|
||||
use wickra::{
|
||||
AccelerationBands, AdOscillator, AdaptiveCycle, Adxr, Alma, AnchoredVwap, Atr, AtrBands,
|
||||
Autocorrelation, BatchExt, BollingerBands, CalmarRatio, Camarilla, Candle, CenterOfGravity,
|
||||
ClassicPivots, CoefficientOfVariation, CyberneticCycle, Decycler, DecyclerOscillator,
|
||||
DemandIndex, DemarkPivots, DetrendedStdDev, Doji, DonchianStop, DoubleBollinger,
|
||||
EhlersStochastic, Ema, EmpiricalModeDecomposition, Engulfing, Fama, FibonacciPivots,
|
||||
FisherTransform, FractalChaosBands, Frama, GarmanKlassVolatility, Hammer, HeikinAshi,
|
||||
HiLoActivator, HilbertDominantCycle, HurstChannel, HurstExponent, Ichimoku, Indicator,
|
||||
InitialBalance, InstantaneousTrendline, InverseFisherTransform, Jma, Kst, Kurtosis, Kvo,
|
||||
LinRegChannel, MaEnvelope, MacdIndicator, Mama, MarketFacilitationIndex, MaxDrawdown,
|
||||
McGinleyDynamic, MedianAbsoluteDeviation, MorningEveningStar, Nvi, Obv, OpeningRange,
|
||||
ParkinsonVolatility, PercentageTrailingStop, Pgo, ProfitFactor, Pvi, RSquared,
|
||||
RenkoTrailingStop, RogersSatchellVolatility, RoofingFilter, Rsi, Rvi, RviVolatility, Rwi,
|
||||
SharpeRatio, SineWave, Skewness, Sma, StandardError, StandardErrorBands, StarcBands,
|
||||
StepTrailingStop, Stochastic, SuperSmoother, TdCombo, TdCountdown, TdDeMarker, TdDifferential,
|
||||
TdLines, TdOpen, TdPressure, TdRangeProjection, TdRei, TdRiskLevel, TdSequential, TdSetup,
|
||||
ThreeInside, Tii, Tsv, TtmSqueeze, ValueArea, ValueAtRisk, Variance, Vidya, VoltyStop,
|
||||
VolumeOscillator, VwapStdDevBands, Vzo, WaveTrend, WilliamsFractals, Wma, WoodiePivots,
|
||||
YangZhangVolatility, YoyoExit, ZigZag,
|
||||
Adx, Atr, Autocorrelation, BatchExt, BollingerBands, BollingerOutput, CalmarRatio, Candle, Cci,
|
||||
ClassicPivots, ConnorsRsi, Ema, EmpiricalModeDecomposition, Engulfing, Frama,
|
||||
HilbertDominantCycle, HurstExponent, Ichimoku, IchimokuOutput, Indicator, Jma,
|
||||
LinearRegression, MacdIndicator, MacdOutput, Mama, MamaOutput, MaxDrawdown, Obv,
|
||||
ParkinsonVolatility, Ppo, Psar, RollingVwap, Rsi, SharpeRatio, Sma, Stc, SuperTrend,
|
||||
SuperTrendOutput, TdSequential, TdSequentialOutput, TtmSqueeze, TtmSqueezeOutput, ValueArea,
|
||||
ValueAreaOutput, ValueAtRisk, Vwap, VwapStdDevBands, VwapStdDevBandsOutput, WaveTrend,
|
||||
YangZhangVolatility, T3,
|
||||
};
|
||||
use wickra_data::csv::CandleReader;
|
||||
|
||||
@@ -89,60 +92,6 @@ where
|
||||
group.finish();
|
||||
}
|
||||
|
||||
fn bench_kst(c: &mut Criterion, prices: &[f64]) {
|
||||
let mut group = c.benchmark_group("kst");
|
||||
for &n in SIZES {
|
||||
let n = n.min(prices.len());
|
||||
let series = &prices[..n];
|
||||
group.throughput(Throughput::Elements(n as u64));
|
||||
group.bench_with_input(BenchmarkId::new("streaming", n), series, |b, prices| {
|
||||
b.iter(|| {
|
||||
let mut ind = Kst::classic();
|
||||
for p in prices {
|
||||
black_box(ind.update(*p));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
fn bench_macd(c: &mut Criterion, prices: &[f64]) {
|
||||
let mut group = c.benchmark_group("macd");
|
||||
for &n in SIZES {
|
||||
let n = n.min(prices.len());
|
||||
let series = &prices[..n];
|
||||
group.throughput(Throughput::Elements(n as u64));
|
||||
group.bench_with_input(BenchmarkId::new("streaming", n), series, |b, prices| {
|
||||
b.iter(|| {
|
||||
let mut ind = MacdIndicator::classic();
|
||||
for p in prices {
|
||||
black_box(ind.update(*p));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
fn bench_bollinger(c: &mut Criterion, prices: &[f64]) {
|
||||
let mut group = c.benchmark_group("bollinger");
|
||||
for &n in SIZES {
|
||||
let n = n.min(prices.len());
|
||||
let series = &prices[..n];
|
||||
group.throughput(Throughput::Elements(n as u64));
|
||||
group.bench_with_input(BenchmarkId::new("streaming", n), series, |b, prices| {
|
||||
b.iter(|| {
|
||||
let mut ind = BollingerBands::classic();
|
||||
for p in prices {
|
||||
black_box(ind.update(*p));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
fn bench_candle_input<I, F, O>(c: &mut Criterion, name: &str, candles: &[Candle], make: F)
|
||||
where
|
||||
F: Fn() -> I,
|
||||
@@ -165,270 +114,6 @@ where
|
||||
group.finish();
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn benches(c: &mut Criterion) {
|
||||
let candles = load_candles();
|
||||
let closes: Vec<f64> = candles.iter().map(|c| c.close).collect();
|
||||
|
||||
bench_scalar(c, "sma", &closes, || Sma::new(14).unwrap());
|
||||
bench_scalar(c, "ema", &closes, || Ema::new(14).unwrap());
|
||||
bench_scalar(c, "wma", &closes, || Wma::new(14).unwrap());
|
||||
bench_scalar(c, "rsi", &closes, || Rsi::new(14).unwrap());
|
||||
bench_scalar(c, "tii", &closes, || Tii::new(60, 30).unwrap());
|
||||
bench_scalar(c, "alma", &closes, || Alma::new(9, 0.85, 6.0).unwrap());
|
||||
bench_scalar(c, "mcginley_dynamic", &closes, || {
|
||||
McGinleyDynamic::new(10).unwrap()
|
||||
});
|
||||
bench_scalar(c, "frama", &closes, || Frama::new(16).unwrap());
|
||||
bench_scalar(c, "vidya", &closes, || Vidya::new(14, 9).unwrap());
|
||||
bench_scalar(c, "jma", &closes, || Jma::new(14, 0.0, 2).unwrap());
|
||||
bench_macd(c, &closes);
|
||||
bench_kst(c, &closes);
|
||||
bench_bollinger(c, &closes);
|
||||
bench_candle_input(c, "atr", &candles, || Atr::new(14).unwrap());
|
||||
bench_candle_input(c, "adxr", &candles, || Adxr::new(14).unwrap());
|
||||
bench_candle_input(c, "rwi", &candles, || Rwi::new(14).unwrap());
|
||||
bench_candle_input(c, "wave_trend", &candles, || WaveTrend::classic().unwrap());
|
||||
bench_candle_input(c, "stochastic", &candles, Stochastic::classic);
|
||||
bench_candle_input(c, "obv", &candles, Obv::new);
|
||||
bench_candle_input(c, "ichimoku", &candles, Ichimoku::classic);
|
||||
bench_candle_input(c, "heikin_ashi", &candles, HeikinAshi::new);
|
||||
|
||||
// Family 14 — Candlestick patterns.
|
||||
// 1-bar, 2-bar and 3-bar representatives. The shape check itself is
|
||||
// stateless arithmetic, so this also serves as a cost-floor reference.
|
||||
bench_candle_input(c, "doji", &candles, Doji::new);
|
||||
bench_candle_input(c, "hammer", &candles, Hammer::new);
|
||||
bench_candle_input(c, "engulfing", &candles, Engulfing::new);
|
||||
bench_candle_input(c, "morning_evening_star", &candles, MorningEveningStar::new);
|
||||
bench_candle_input(c, "three_inside", &candles, ThreeInside::new);
|
||||
|
||||
// Family 10 — Ehlers / Cycle scalar benchmarks.
|
||||
bench_scalar(c, "super_smoother", &closes, || {
|
||||
SuperSmoother::new(10).unwrap()
|
||||
});
|
||||
bench_scalar(c, "fisher_transform", &closes, || {
|
||||
FisherTransform::new(10).unwrap()
|
||||
});
|
||||
bench_scalar(c, "inverse_fisher_transform", &closes, || {
|
||||
InverseFisherTransform::new(1.0).unwrap()
|
||||
});
|
||||
bench_scalar(c, "decycler", &closes, || Decycler::new(20).unwrap());
|
||||
bench_scalar(c, "decycler_oscillator", &closes, || {
|
||||
DecyclerOscillator::new(10, 30).unwrap()
|
||||
});
|
||||
bench_scalar(c, "roofing_filter", &closes, || {
|
||||
RoofingFilter::new(10, 48).unwrap()
|
||||
});
|
||||
bench_scalar(c, "center_of_gravity", &closes, || {
|
||||
CenterOfGravity::new(10).unwrap()
|
||||
});
|
||||
bench_scalar(c, "cybernetic_cycle", &closes, || {
|
||||
CyberneticCycle::new(10).unwrap()
|
||||
});
|
||||
bench_scalar(c, "instantaneous_trendline", &closes, || {
|
||||
InstantaneousTrendline::new(20).unwrap()
|
||||
});
|
||||
bench_scalar(c, "ehlers_stochastic", &closes, || {
|
||||
EhlersStochastic::new(20).unwrap()
|
||||
});
|
||||
bench_scalar(c, "empirical_mode_decomposition", &closes, || {
|
||||
EmpiricalModeDecomposition::new(20, 0.5).unwrap()
|
||||
});
|
||||
bench_scalar(
|
||||
c,
|
||||
"hilbert_dominant_cycle",
|
||||
&closes,
|
||||
HilbertDominantCycle::new,
|
||||
);
|
||||
bench_scalar(c, "adaptive_cycle", &closes, AdaptiveCycle::new);
|
||||
bench_scalar(c, "sine_wave", &closes, SineWave::new);
|
||||
bench_scalar(c, "fama", &closes, || Fama::new(0.5, 0.05).unwrap());
|
||||
|
||||
// MAMA: multi-output, mirrored on macd's streaming-only bench style.
|
||||
{
|
||||
let mut group = c.benchmark_group("mama");
|
||||
for &n in SIZES {
|
||||
let n = n.min(closes.len());
|
||||
let series = &closes[..n];
|
||||
group.throughput(Throughput::Elements(n as u64));
|
||||
group.bench_with_input(BenchmarkId::new("streaming", n), series, |b, prices| {
|
||||
b.iter(|| {
|
||||
let mut ind = Mama::classic();
|
||||
for p in prices {
|
||||
black_box(ind.update(*p));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
// --- Family 11: DeMark ---
|
||||
bench_candle_input(c, "td_setup", &candles, TdSetup::classic);
|
||||
bench_candle_input(c, "td_sequential", &candles, TdSequential::classic);
|
||||
bench_candle_input(c, "td_demarker", &candles, || TdDeMarker::new(14).unwrap());
|
||||
bench_candle_input(c, "td_rei", &candles, TdRei::classic);
|
||||
bench_candle_input(c, "td_pressure", &candles, || TdPressure::new(5).unwrap());
|
||||
bench_candle_input(c, "td_combo", &candles, TdCombo::classic);
|
||||
bench_candle_input(c, "td_countdown", &candles, TdCountdown::classic);
|
||||
bench_candle_input(c, "td_lines", &candles, TdLines::classic);
|
||||
bench_candle_input(c, "td_risk_level", &candles, TdRiskLevel::classic);
|
||||
bench_candle_input(c, "td_range_projection", &candles, TdRangeProjection::new);
|
||||
bench_candle_input(c, "td_differential", &candles, TdDifferential::new);
|
||||
bench_candle_input(c, "td_open", &candles, TdOpen::new);
|
||||
|
||||
// --- Family 08: Pivots & Support/Resistance ---
|
||||
bench_candle_input(c, "classic_pivots", &candles, ClassicPivots::new);
|
||||
bench_candle_input(c, "fibonacci_pivots", &candles, FibonacciPivots::new);
|
||||
bench_candle_input(c, "camarilla", &candles, Camarilla::new);
|
||||
bench_candle_input(c, "woodie_pivots", &candles, WoodiePivots::new);
|
||||
bench_candle_input(c, "demark_pivots", &candles, DemarkPivots::new);
|
||||
bench_candle_input(c, "williams_fractals", &candles, WilliamsFractals::new);
|
||||
bench_candle_input(c, "zig_zag", &candles, || ZigZag::new(0.05).unwrap());
|
||||
|
||||
// --- Family 09: Trailing Stops ---
|
||||
bench_candle_input(c, "hilo_activator", &candles, HiLoActivator::classic);
|
||||
bench_candle_input(c, "volty_stop", &candles, VoltyStop::classic);
|
||||
bench_candle_input(c, "yoyo_exit", &candles, YoyoExit::classic);
|
||||
bench_candle_input(c, "donchian_stop", &candles, DonchianStop::classic);
|
||||
bench_scalar(c, "percentage_trailing_stop", &closes, || {
|
||||
PercentageTrailingStop::new(5.0).unwrap()
|
||||
});
|
||||
bench_scalar(c, "step_trailing_stop", &closes, || {
|
||||
StepTrailingStop::new(1.0).unwrap()
|
||||
});
|
||||
bench_scalar(c, "renko_trailing_stop", &closes, || {
|
||||
RenkoTrailingStop::new(1.0).unwrap()
|
||||
});
|
||||
|
||||
// --- Family 07: Volume ---
|
||||
bench_candle_input(c, "kvo", &candles, Kvo::classic);
|
||||
bench_candle_input(c, "volume_oscillator", &candles, || {
|
||||
VolumeOscillator::new(14, 28).unwrap()
|
||||
});
|
||||
bench_candle_input(c, "nvi", &candles, Nvi::new);
|
||||
bench_candle_input(c, "pvi", &candles, Pvi::new);
|
||||
bench_candle_input(c, "williams_ad", &candles, AdOscillator::new);
|
||||
bench_candle_input(c, "anchored_vwap", &candles, AnchoredVwap::new);
|
||||
bench_candle_input(c, "demand_index", &candles, || {
|
||||
DemandIndex::new(10).unwrap()
|
||||
});
|
||||
bench_candle_input(c, "tsv", &candles, || Tsv::new(18).unwrap());
|
||||
bench_candle_input(c, "vzo", &candles, || Vzo::new(14).unwrap());
|
||||
bench_candle_input(
|
||||
c,
|
||||
"market_facilitation_index",
|
||||
&candles,
|
||||
MarketFacilitationIndex::new,
|
||||
);
|
||||
|
||||
// --- Family 04: Volatility ---
|
||||
bench_scalar(c, "rvi_volatility", &closes, || {
|
||||
RviVolatility::new(10).unwrap()
|
||||
});
|
||||
bench_candle_input(c, "parkinson", &candles, || {
|
||||
ParkinsonVolatility::new(20, 252).unwrap()
|
||||
});
|
||||
bench_candle_input(c, "garman_klass", &candles, || {
|
||||
GarmanKlassVolatility::new(20, 252).unwrap()
|
||||
});
|
||||
bench_candle_input(c, "rogers_satchell", &candles, || {
|
||||
RogersSatchellVolatility::new(20, 252).unwrap()
|
||||
});
|
||||
bench_candle_input(c, "yang_zhang", &candles, || {
|
||||
YangZhangVolatility::new(20, 252).unwrap()
|
||||
});
|
||||
bench_candle_input(c, "rvi", &candles, || Rvi::new(10).unwrap());
|
||||
bench_candle_input(c, "pgo", &candles, || Pgo::new(14).unwrap());
|
||||
|
||||
// --- Family 05: Bands & Channels ---
|
||||
bench_candle_input(c, "acceleration_bands", &candles, || {
|
||||
AccelerationBands::new(20, 0.001).unwrap()
|
||||
});
|
||||
bench_candle_input(c, "starc_bands", &candles, || {
|
||||
StarcBands::new(6, 15, 2.0).unwrap()
|
||||
});
|
||||
bench_candle_input(c, "atr_bands", &candles, || AtrBands::new(14, 3.0).unwrap());
|
||||
bench_candle_input(c, "hurst_channel", &candles, || {
|
||||
HurstChannel::new(10, 0.5).unwrap()
|
||||
});
|
||||
bench_candle_input(c, "ttm_squeeze", &candles, || {
|
||||
TtmSqueeze::new(20, 2.0, 1.5).unwrap()
|
||||
});
|
||||
bench_candle_input(c, "fractal_chaos_bands", &candles, || {
|
||||
FractalChaosBands::new(2).unwrap()
|
||||
});
|
||||
bench_candle_input(c, "vwap_stddev_bands", &candles, || {
|
||||
VwapStdDevBands::new(2.0).unwrap()
|
||||
});
|
||||
bench_scalar_multi(c, "ma_envelope", &closes, || {
|
||||
MaEnvelope::new(20, 0.025).unwrap()
|
||||
});
|
||||
bench_scalar_multi(c, "linreg_channel", &closes, || {
|
||||
LinRegChannel::new(20, 2.0).unwrap()
|
||||
});
|
||||
bench_scalar_multi(c, "standard_error_bands", &closes, || {
|
||||
StandardErrorBands::new(21, 2.0).unwrap()
|
||||
});
|
||||
bench_scalar_multi(c, "double_bollinger", &closes, || {
|
||||
DoubleBollinger::new(20, 1.0, 2.0).unwrap()
|
||||
});
|
||||
|
||||
// --- Family 12: Statistik / Regression ---
|
||||
bench_scalar(c, "variance", &closes, || Variance::new(20).unwrap());
|
||||
bench_scalar(c, "coefficient_of_variation", &closes, || {
|
||||
CoefficientOfVariation::new(20).unwrap()
|
||||
});
|
||||
bench_scalar(c, "skewness", &closes, || Skewness::new(20).unwrap());
|
||||
bench_scalar(c, "kurtosis", &closes, || Kurtosis::new(20).unwrap());
|
||||
bench_scalar(c, "standard_error", &closes, || {
|
||||
StandardError::new(14).unwrap()
|
||||
});
|
||||
bench_scalar(c, "detrended_std_dev", &closes, || {
|
||||
DetrendedStdDev::new(14).unwrap()
|
||||
});
|
||||
bench_scalar(c, "r_squared", &closes, || RSquared::new(14).unwrap());
|
||||
bench_scalar(c, "median_absolute_deviation", &closes, || {
|
||||
MedianAbsoluteDeviation::new(20).unwrap()
|
||||
});
|
||||
bench_scalar(c, "autocorrelation", &closes, || {
|
||||
Autocorrelation::new(20, 1).unwrap()
|
||||
});
|
||||
bench_scalar(c, "hurst_exponent", &closes, || {
|
||||
HurstExponent::new(100, 4).unwrap()
|
||||
});
|
||||
|
||||
// --- Family 16: Market Profile ---
|
||||
bench_candle_input(c, "value_area", &candles, || {
|
||||
ValueArea::new(20, 50, 0.70).unwrap()
|
||||
});
|
||||
bench_candle_input(c, "initial_balance", &candles, || {
|
||||
InitialBalance::new(12).unwrap()
|
||||
});
|
||||
bench_candle_input(c, "opening_range", &candles, || {
|
||||
OpeningRange::new(6).unwrap()
|
||||
});
|
||||
|
||||
// --- Family 15: Risk / Performance Metrics ---
|
||||
// Close-prices stand in for the equity curve / return stream; absolute
|
||||
// numbers aren't meaningful here — what matters is the per-update cost.
|
||||
bench_scalar(c, "sharpe_ratio", &closes, || {
|
||||
SharpeRatio::new(20, 0.0).unwrap()
|
||||
});
|
||||
bench_scalar(c, "max_drawdown", &closes, || MaxDrawdown::new(20).unwrap());
|
||||
bench_scalar(c, "profit_factor", &closes, || {
|
||||
ProfitFactor::new(20).unwrap()
|
||||
});
|
||||
bench_scalar(c, "calmar_ratio", &closes, || CalmarRatio::new(20).unwrap());
|
||||
bench_scalar(c, "value_at_risk", &closes, || {
|
||||
ValueAtRisk::new(50, 0.95).unwrap()
|
||||
});
|
||||
}
|
||||
|
||||
/// Variant of `bench_scalar` for scalar-input indicators whose output is *not*
|
||||
/// `f64` (band/channel structs). Streaming-only path keeps the benchmark
|
||||
/// expression flat across all multi-output indicators.
|
||||
fn bench_scalar_multi<I, F, O>(c: &mut Criterion, name: &str, prices: &[f64], make: F)
|
||||
where
|
||||
F: Fn() -> I,
|
||||
@@ -451,5 +136,136 @@ where
|
||||
group.finish();
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn benches(c: &mut Criterion) {
|
||||
let candles = load_candles();
|
||||
let closes: Vec<f64> = candles.iter().map(|c| c.close).collect();
|
||||
|
||||
// === Family 01 — Moving Averages ===
|
||||
// Sma: cheapest baseline; Ema: recursive baseline; Frama / Jma / T3: adaptive / expensive.
|
||||
bench_scalar(c, "sma", &closes, || Sma::new(14).unwrap());
|
||||
bench_scalar(c, "ema", &closes, || Ema::new(14).unwrap());
|
||||
bench_scalar(c, "frama", &closes, || Frama::new(16).unwrap());
|
||||
bench_scalar(c, "jma", &closes, || Jma::new(14, 0.0, 2).unwrap());
|
||||
bench_scalar(c, "t3", &closes, || T3::new(14, 0.7).unwrap());
|
||||
|
||||
// === Family 02 — Momentum Oscillators ===
|
||||
// Rsi: textbook baseline; ConnorsRsi: three-component composite.
|
||||
bench_scalar(c, "rsi", &closes, || Rsi::new(14).unwrap());
|
||||
bench_candle_input(c, "cci", &candles, || Cci::new(20).unwrap());
|
||||
bench_scalar(c, "connors_rsi", &closes, ConnorsRsi::classic);
|
||||
|
||||
// === Family 03 — Trend & Directional ===
|
||||
// Adx is multi-component (DI+/DI-/ADX); WaveTrend is the heaviest in this group.
|
||||
bench_candle_input(c, "adx", &candles, || Adx::new(14).unwrap());
|
||||
bench_candle_input(c, "wave_trend", &candles, || WaveTrend::classic().unwrap());
|
||||
|
||||
// === Family 04 — Price Oscillators ===
|
||||
// Macd: multi-output baseline; Stc: deeply recursive (most expensive in family).
|
||||
bench_scalar_multi::<_, _, MacdOutput>(c, "macd", &closes, MacdIndicator::classic);
|
||||
bench_scalar(c, "ppo", &closes, || Ppo::new(12, 26).unwrap());
|
||||
bench_scalar(c, "stc", &closes, Stc::classic);
|
||||
|
||||
// === Family 05 — Volatility & Bands ===
|
||||
// Atr: cheap baseline; Bollinger: stddev-heavy; YangZhang: most-expensive volatility metric.
|
||||
bench_candle_input(c, "atr", &candles, || Atr::new(14).unwrap());
|
||||
bench_scalar_multi::<_, _, BollingerOutput>(c, "bollinger", &closes, || {
|
||||
BollingerBands::new(20, 2.0).unwrap()
|
||||
});
|
||||
bench_candle_input(c, "parkinson", &candles, || {
|
||||
ParkinsonVolatility::new(20, 252).unwrap()
|
||||
});
|
||||
bench_candle_input(c, "yang_zhang", &candles, || {
|
||||
YangZhangVolatility::new(20, 252).unwrap()
|
||||
});
|
||||
|
||||
// === Family 06 — Bands & Channels ===
|
||||
// TtmSqueeze: multi-indicator composite; VwapStdDevBands: volume-weighted.
|
||||
bench_candle_input::<_, _, TtmSqueezeOutput>(c, "ttm_squeeze", &candles, || {
|
||||
TtmSqueeze::new(20, 2.0, 1.5).unwrap()
|
||||
});
|
||||
bench_candle_input::<_, _, VwapStdDevBandsOutput>(c, "vwap_stddev_bands", &candles, || {
|
||||
VwapStdDevBands::new(2.0).unwrap()
|
||||
});
|
||||
|
||||
// === Family 07 — Trailing Stops ===
|
||||
// Psar: textbook trailing stop; SuperTrend: ATR-anchored band.
|
||||
bench_candle_input(c, "psar", &candles, || Psar::new(0.02, 0.02, 0.2).unwrap());
|
||||
bench_candle_input::<_, _, SuperTrendOutput>(c, "super_trend", &candles, || {
|
||||
SuperTrend::new(10, 3.0).unwrap()
|
||||
});
|
||||
|
||||
// === Family 08 — Volume ===
|
||||
// Obv: simplest volume cumul; Vwap: session cumul; RollingVwap: rolling window.
|
||||
bench_candle_input(c, "obv", &candles, Obv::new);
|
||||
bench_candle_input(c, "vwap", &candles, Vwap::new);
|
||||
bench_candle_input(c, "rolling_vwap", &candles, || {
|
||||
RollingVwap::new(20).unwrap()
|
||||
});
|
||||
|
||||
// === Family 09 — Price Statistics ===
|
||||
// LinearRegression: OLS baseline; HurstExponent: R/S analysis (most expensive in family);
|
||||
// Autocorrelation: lag-correlation.
|
||||
bench_scalar(c, "linear_regression", &closes, || {
|
||||
LinearRegression::new(14).unwrap()
|
||||
});
|
||||
bench_scalar(c, "hurst_exponent", &closes, || {
|
||||
HurstExponent::new(100, 4).unwrap()
|
||||
});
|
||||
bench_scalar(c, "autocorrelation", &closes, || {
|
||||
Autocorrelation::new(20, 1).unwrap()
|
||||
});
|
||||
|
||||
// === Family 10 — Ehlers / Cycle (DSP) ===
|
||||
// Mama: paired adaptive MA (multi-output); HilbertDominantCycle: cycle estimation;
|
||||
// EmpiricalModeDecomposition: heaviest DSP indicator in the catalogue.
|
||||
bench_scalar_multi::<_, _, MamaOutput>(c, "mama", &closes, Mama::classic);
|
||||
bench_scalar(
|
||||
c,
|
||||
"hilbert_dominant_cycle",
|
||||
&closes,
|
||||
HilbertDominantCycle::new,
|
||||
);
|
||||
bench_scalar(c, "empirical_mode_decomposition", &closes, || {
|
||||
EmpiricalModeDecomposition::new(20, 0.5).unwrap()
|
||||
});
|
||||
|
||||
// === Family 11 — Pivots & Support/Resistance ===
|
||||
bench_candle_input(c, "classic_pivots", &candles, ClassicPivots::new);
|
||||
|
||||
// === Family 12 — DeMark ===
|
||||
// TdSequential is the most complex in the family (state machine + countdown).
|
||||
bench_candle_input::<_, _, TdSequentialOutput>(
|
||||
c,
|
||||
"td_sequential",
|
||||
&candles,
|
||||
TdSequential::classic,
|
||||
);
|
||||
|
||||
// === Family 13 — Ichimoku & Charts ===
|
||||
bench_candle_input::<_, _, IchimokuOutput>(c, "ichimoku", &candles, Ichimoku::classic);
|
||||
|
||||
// === Family 14 — Candlestick Patterns ===
|
||||
// Engulfing is two-bar so representative across the candlestick family.
|
||||
bench_candle_input(c, "engulfing", &candles, Engulfing::new);
|
||||
|
||||
// === Family 15 — Market Profile ===
|
||||
bench_candle_input::<_, _, ValueAreaOutput>(c, "value_area", &candles, || {
|
||||
ValueArea::new(20, 50, 0.70).unwrap()
|
||||
});
|
||||
|
||||
// === Family 16 — Risk / Performance Metrics ===
|
||||
// Close-prices stand in for the equity curve / return stream; absolute
|
||||
// numbers aren't meaningful here — what matters is the per-update cost.
|
||||
bench_scalar(c, "sharpe_ratio", &closes, || {
|
||||
SharpeRatio::new(20, 0.0).unwrap()
|
||||
});
|
||||
bench_scalar(c, "max_drawdown", &closes, || MaxDrawdown::new(20).unwrap());
|
||||
bench_scalar(c, "calmar_ratio", &closes, || CalmarRatio::new(20).unwrap());
|
||||
bench_scalar(c, "value_at_risk", &closes, || {
|
||||
ValueAtRisk::new(50, 0.95).unwrap()
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(name = wickra_benches; config = Criterion::default(); targets = benches);
|
||||
criterion_main!(wickra_benches);
|
||||
|
||||
Reference in New Issue
Block a user