#![no_main] //! Fuzz market-breadth `Indicator` implementations with //! arbitrary cross-section streams. //! //! Each iteration consumes a byte stream, interprets it as a sequence of `f64` //! values (8 bytes each), packs consecutive pairs into [`Member`]s (a `change` //! and a `volume`, with the high/low flags taken from the value bit parity), and //! groups the members into bounded-size [`CrossSection`] ticks. Cross-sections //! are built with `new_unchecked` so the fuzzer can explore degenerate values //! (non-finite changes, negative volumes, empty-adjacent groups) that the //! validating constructor would reject — the indicators must never panic, //! streaming or batched. use libfuzzer_sys::fuzz_target; use wickra_core::{AbsoluteBreadthIndex, AdVolumeLine, AdvanceDecline, AdvanceDeclineRatio, BatchExt, BreadthThrust, BullishPercentIndex, CrossSection, CumulativeVolumeIndex, HighLowIndex, Indicator, McClellanOscillator, McClellanSummationIndex, Member, NewHighsNewLows, PercentAboveMa, TickIndex, Trin, UpDownVolumeRatio}; #[inline(never)] fn drive(make: impl Fn() -> I, sections: &[CrossSection]) where I: Indicator + BatchExt, { let mut streaming = make(); for section in sections { let _ = streaming.update(section.clone()); } let _ = make().batch(sections); } fuzz_target!(|data: &[u8]| { let floats: Vec = data .chunks_exact(8) .map(|c| f64::from_le_bytes(c.try_into().expect("8 bytes"))) .collect(); let members: Vec = floats .chunks_exact(2) .map(|c| Member::new(c[0], c[1], c[0].to_bits() & 1 == 1, c[1].to_bits() & 1 == 1)) .collect(); // Group members into cross-sections of up to eight symbols each so a single // input yields a stream of ragged universes. let sections: Vec = members .chunks(8) .filter(|chunk| !chunk.is_empty()) .map(|chunk| CrossSection::new_unchecked(chunk.to_vec(), 0)) .collect(); drive(AdvanceDecline::new, §ions); drive(AdvanceDeclineRatio::new, §ions); drive(AdVolumeLine::new, §ions); drive(McClellanOscillator::new, §ions); drive(McClellanSummationIndex::new, §ions); drive(Trin::new, §ions); drive(|| BreadthThrust::new(10).unwrap(), §ions); drive(NewHighsNewLows::new, §ions); drive(|| HighLowIndex::new(10).unwrap(), §ions); drive(PercentAboveMa::new, §ions); drive(UpDownVolumeRatio::new, §ions); drive(BullishPercentIndex::new, §ions); drive(CumulativeVolumeIndex::new, §ions); drive(AbsoluteBreadthIndex::new, §ions); drive(TickIndex::new, §ions); });