Files
wickra/fuzz/fuzz_targets/bar_builder_update_candle.rs
T
kingchenc d4b3f9dbd1 feat: add Alt-Chart Bars (Renko, Kagi, Point & Figure) via a BarBuilder trait (#146)
Introduces a BarBuilder trait for price-driven chart constructors that emit a variable number of bars per candle (deliberately not Indicator). Adds Renko (box-size bricks, 2-box reversal), Kagi (reversal-amount segments) and Point & Figure (box-size X/O columns, N-box reversal) in a new Alt-Chart Bars family, with custom Python/Node/WASM bindings, a dedicated fuzz target, tests and docs. Indicator count 292 -> 295.
2026-06-02 21:56:00 +02:00

48 lines
1.8 KiB
Rust

#![no_main]
//! Fuzz the alt-chart bar builders (Renko, Kagi, Point-and-Figure) with arbitrary
//! candle streams.
//!
//! Each fuzz iteration runs the same candle sequence through every bar builder
//! twice — once as a streaming `update` loop, once as a full `batch` call — and
//! asserts neither path panics. Renko's brick count per candle is proportional
//! to `(price_move / box_size)`, so the candle magnitudes are bounded here (a
//! sensibly scaled `box_size` relative to the instrument price is a documented
//! precondition); the fuzzer still freely varies ordering, gaps, equal prices
//! and zero ranges within that band.
use libfuzzer_sys::fuzz_target;
use wickra_core::{BarBuilder, Candle, KagiBars, PointAndFigureBars, RenkoBars};
/// Reinterpret the fuzz bytes as `[open, high, low, close, volume]` groups,
/// keeping only structurally-valid candles whose magnitudes stay in a band that
/// keeps Renko's brick count bounded.
fn candles_from(data: &[f64]) -> Vec<Candle> {
data.chunks_exact(5)
.enumerate()
.filter_map(|(i, ch)| Candle::new(ch[0], ch[1], ch[2], ch[3], ch[4], i as i64).ok())
.filter(|candle| candle.high.abs() < 1.0e4 && candle.low.abs() < 1.0e4)
.collect()
}
#[inline(never)]
fn drive<B: BarBuilder>(mut builder: B, candles: &[Candle]) {
for candle in candles {
let _ = builder.update(*candle);
}
}
fuzz_target!(|data: Vec<f64>| {
let candles = candles_from(&data);
if candles.is_empty() {
return;
}
drive(RenkoBars::new(5.0).unwrap(), &candles);
drive(KagiBars::new(5.0).unwrap(), &candles);
drive(PointAndFigureBars::new(5.0, 3).unwrap(), &candles);
let _ = RenkoBars::new(5.0).unwrap().batch(&candles);
let _ = KagiBars::new(5.0).unwrap().batch(&candles);
let _ = PointAndFigureBars::new(5.0, 3).unwrap().batch(&candles);
});