e5305ffa94
Adds seven information-driven bar builders to the **Alt-Chart Bars** family, the final batch of the family-deepening run. Indicator count **507 → 514**. ## Builders All implement the `BarBuilder` trait (`update(Candle) -> Vec<Bar>`), emitting a data-dependent number of completed bars per candle. | Builder | Driver | Bar fields | |---------|--------|-----------| | `RangeBars` | close | open, close, direction | | `TickBars` | OHLCV | open, high, low, close, volume | | `VolumeBars` | OHLCV | open, high, low, close, volume | | `DollarBars` (Lopez de Prado) | OHLCV | + dollar | | `ImbalanceBars` | OHLC | + imbalance, direction | | `RunBars` | OHLC | + length, direction | | `ThreeLineBreakBars` | close | open, close, direction | ## Touchpoints Seven core modules (each with full unit tests), `mod.rs`/`lib.rs` (builders counted, bar element types on their own re-export lines), README family rows, Python/Node/WASM hand-written bindings for the variable-length output (Python tuples + `(k, N)` ndarray; Node `Vec<object>`; WASM array of objects), the `bar_builder_update_candle` fuzz target, dedicated Python + Node tests, the `BAR_BUILDERS` completeness exclusion, and CHANGELOG. ## Verification - `cargo test -p wickra-core --lib` — 4207 passed - `cargo test -p wickra-core --doc` — 464 passed - `cargo clippy --workspace --all-targets --all-features -- -D warnings` — clean - `npm test` (node) — 584 passed - `pytest` (python) — 957 passed
55 lines
2.3 KiB
Rust
55 lines
2.3 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, DollarBars, ImbalanceBars, KagiBars, PointAndFigureBars, RangeBars, RenkoBars, RunBars, ThreeLineBreakBars, TickBars, VolumeBars};
|
|
|
|
/// 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);
|
|
drive(RangeBars::new(2.0).unwrap(), &candles);
|
|
drive(TickBars::new(5).unwrap(), &candles);
|
|
drive(VolumeBars::new(100.0).unwrap(), &candles);
|
|
drive(DollarBars::new(10000.0).unwrap(), &candles);
|
|
drive(ImbalanceBars::new(5.0).unwrap(), &candles);
|
|
drive(RunBars::new(3).unwrap(), &candles);
|
|
drive(ThreeLineBreakBars::new(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);
|
|
});
|