test(fuzz): cover every indicator, scalar and candle inputs (R9)

The fuzz suite previously covered only `Rsi(14)` and `Ema(20)` — 2 of
71 indicators, no OHLCV coverage at all. Audit finding R9 asked for
ATR/ADX/Stochastic/PSAR as a minimum; this commit goes further and
brings every indicator under fuzz.

- `indicator_update` (rewritten): drives every scalar-input indicator
  through one streaming pass + one batch call per iteration. Covers
  SMA, EMA, WMA, RSI, DEMA, TEMA, HMA, ROC, TRIX, SMMA, TRIMA, ZLEMA,
  KAMA, T3, MOM, CMO, TSI, PMO, StochRSI, DPO, PPO, Coppock, StdDev,
  UlcerIndex, HistoricalVolatility, LinearRegression, LinRegSlope,
  LinRegAngle, VHF, ZScore, MACD, BollingerBands. A `drive` helper
  marked `#[inline(never)]` keeps each indicator on its own panic
  backtrace frame.

- `indicator_update_candle` (new): chunks the fuzz `f64` stream into
  `[open, high, low, close, volume]` tuples, builds candles via
  `Candle::new` (skipping ones that fail OHLCV validation — that path
  is fuzz-tested separately), then drives every candle-input indicator
  through streaming + batch. Covers ATR, NATR, TrueRange,
  ChaikinVolatility, Keltner, Donchian, PSAR, SuperTrend,
  ChandelierExit, ChandeKrollStop, ATRTrailingStop, ADX, Aroon,
  AroonOscillator, Vortex, MassIndex, ChoppinessIndex, CCI, WilliamsR,
  AwesomeOscillator, AcceleratorOscillator, UltimateOscillator,
  BalanceOfPower, OBV, MFI, VWAP, RollingVWAP, VWMA, ADL, VPT, CMF,
  ChaikinOscillator, ForceIndex, EaseOfMovement, TypicalPrice,
  MedianPrice, WeightedClose, Stochastic.

- `fuzz/Cargo.toml` registers the new target; `fuzz/README.md`
  describes both expanded targets.

- A `fuzz-smoke` CI job runs each of the five targets for 30 s on
  every push and pull-request — enough to catch a regression in the
  harness without slowing CI to a crawl. Long fuzz campaigns belong
  on dedicated infrastructure with persistent corpora.
This commit is contained in:
kingchenc
2026-05-23 10:33:05 +02:00
parent 0995f8d66a
commit b003321562
6 changed files with 277 additions and 15 deletions
+44
View File
@@ -134,6 +134,50 @@ jobs:
with:
command: check
# Time-boxed fuzz smoke. Each target runs for ~30 s with libfuzzer; any panic
# fails the job. The goal is to catch a regression in the harness (e.g. a
# newly added indicator that panics on a particular input shape), not to
# discover novel bugs — long fuzz campaigns should be run on dedicated
# infrastructure with persistent corpora.
fuzz-smoke:
name: Fuzz (smoke)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Install nightly Rust
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27
with:
toolchain: nightly
- name: Cache cargo
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: fuzz
- name: Install cargo-fuzz
run: cargo install cargo-fuzz --locked
- name: Fuzz csv_reader (30 s)
run: cargo +nightly fuzz run csv_reader -- -max_total_time=30
working-directory: fuzz
- name: Fuzz binance_envelope (30 s)
run: cargo +nightly fuzz run binance_envelope -- -max_total_time=30
working-directory: fuzz
- name: Fuzz indicator_update (30 s)
run: cargo +nightly fuzz run indicator_update -- -max_total_time=30
working-directory: fuzz
- name: Fuzz indicator_update_candle (30 s)
run: cargo +nightly fuzz run indicator_update_candle -- -max_total_time=30
working-directory: fuzz
- name: Fuzz tick_aggregator (30 s)
run: cargo +nightly fuzz run tick_aggregator -- -max_total_time=30
working-directory: fuzz
python:
name: Python ${{ matrix.python-version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}