feat(family-08): Pivots & Support/Resistance (7 indicators) (#47)

* feat(family-08): add Classic, Fibonacci, Camarilla, Woodie and DeMark pivots + Williams Fractals + ZigZag

Seven new indicators land the previously empty Pivots & S/R family
(family 08), each implemented in wickra-core with the full Indicator
trait surface (update / reset / warmup_period / is_ready / name),
exposed across Python (PyO3), Node (napi-rs) and WASM (wasm-bindgen)
with the standard streaming + batch APIs, and covered by Rust unit
tests, Python streaming-vs-batch + reference-value tests, Node
streaming-vs-batch tests, the candle-input fuzz target and Rust
microbenchmarks.

- ClassicPivots (7 levels): PP = (H+L+C)/3, three R/S tiers per the
  floor-trader formulas.
- FibonacciPivots (7 levels): PP plus R/S spaced by 0.382 / 0.618 /
  1.000 of the prior range.
- Camarilla (9 levels): Nick Stott's four-tier `C +/- (H - L) * 1.1 /
  {12, 6, 4, 2}` levels.
- WoodiePivots (5 levels): close-weighted PP = (H + L + 2*C) / 4 plus
  two R/S tiers.
- DemarkPivots (3 levels): conditional X sum based on the previous
  bar's open-vs-close relationship.
- WilliamsFractals: five-bar swing detector emitting optional up/down
  fractal prices at the centre of each window.
- ZigZag: percent-threshold swing tracker, non-repainting; emits the
  just-completed extreme and direction on confirmed reversals only.

README family table updated to nine families / 78 indicators;
CHANGELOG records the family-08 addition under [Unreleased].

* fix(family-08 tests): unify MULTI dict to 3-tuple (factory, batch_call, k)

The HEAD-side family-08 test parametrised MULTI[name] as
`(factory, batch_call, output_arity)` so that pivots with arity 3/5/7/9
fit the same harness. Main's entries arrived as 2-tuples; convert them
all to the 3-tuple shape so `make, batch_call, k = MULTI[name]` unpacks
cleanly. Lifecycle test now indexes the tuple instead of destructuring.

* test(zig_zag): tighten flat-oscillation test (drop dead counter branch)

The previous version of `small_oscillations_yield_no_swings` counted
emitted swings, but the assertion proves the counter never increments
so codecov flagged `emitted += 1` as uncovered. Switch to a per-bar
`assert!(...is_none())` — same coverage of the no-swing path, no dead
branch.
This commit is contained in:
kingchenc
2026-05-25 20:06:46 +02:00
committed by GitHub
parent f10b8c2e2d
commit 7e1e988596
19 changed files with 3379 additions and 45 deletions
+17 -7
View File
@@ -20,13 +20,14 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Through
use std::hint::black_box;
use wickra::{
AccelerationBands, AdOscillator, Adxr, Alma, AnchoredVwap, Atr, AtrBands, BatchExt,
BollingerBands, Candle, DemandIndex, DonchianStop, DoubleBollinger, Ema, FractalChaosBands,
Frama, GarmanKlassVolatility, HiLoActivator, HurstChannel, Indicator, Jma, Kst, Kvo,
LinRegChannel, MaEnvelope, MacdIndicator, MarketFacilitationIndex, McGinleyDynamic, Nvi, Obv,
ParkinsonVolatility, PercentageTrailingStop, Pgo, Pvi, RenkoTrailingStop,
RogersSatchellVolatility, Rsi, Rvi, RviVolatility, Rwi, Sma, StandardErrorBands, StarcBands,
StepTrailingStop, Stochastic, Tii, Tsv, TtmSqueeze, Vidya, VoltyStop, VolumeOscillator,
VwapStdDevBands, Vzo, WaveTrend, Wma, YangZhangVolatility, YoyoExit,
BollingerBands, Camarilla, Candle, ClassicPivots, DemandIndex, DemarkPivots, DonchianStop,
DoubleBollinger, Ema, FibonacciPivots, FractalChaosBands, Frama, GarmanKlassVolatility,
HiLoActivator, HurstChannel, Indicator, Jma, Kst, Kvo, LinRegChannel, MaEnvelope,
MacdIndicator, MarketFacilitationIndex, McGinleyDynamic, Nvi, Obv, ParkinsonVolatility,
PercentageTrailingStop, Pgo, Pvi, RenkoTrailingStop, RogersSatchellVolatility, Rsi, Rvi,
RviVolatility, Rwi, Sma, StandardErrorBands, StarcBands, StepTrailingStop, Stochastic, Tii,
Tsv, TtmSqueeze, Vidya, VoltyStop, VolumeOscillator, VwapStdDevBands, Vzo, WaveTrend,
WilliamsFractals, Wma, WoodiePivots, YangZhangVolatility, YoyoExit, ZigZag,
};
use wickra_data::csv::CandleReader;
@@ -182,6 +183,15 @@ fn benches(c: &mut Criterion) {
bench_candle_input(c, "stochastic", &candles, Stochastic::classic);
bench_candle_input(c, "obv", &candles, Obv::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);