Flamegraph of precompute_features on 1Q ES showed 62% of CPU time in zstd decompression, 6% in DBN FSM parsing, and only 2% in the actual feature math — single-threaded zstd was the bottleneck, not compute. Two fixes: 1. Per-quarter parallelism on the volume-bar trades loop (was sequential `for file in &trade_files`); brings it in line with the OFI path that already used par_iter. 2. Predecoded sidecar cache in `crates/ml-features/src/predecoded.rs`: first call to a `.dbn.zst` writes a bincode'd Vec<Mbp10Snapshot> or Vec<DbnTrade> under `<output_dir>/predecoded/`. Subsequent calls deserialize the sidecar and skip zstd entirely. An mtime+size header self-invalidates the sidecar when the source changes — no manual flush needed when a quarter is re-downloaded. Local 1Q ES results: - cold (writes sidecar): 40.7s (was 39.3s; +1.4s for write) - warm (HIT): 4.7s (8.7× faster) - zstd in flat perf: 62% → 0% of CPU samples - sidecar disk per Q: ~150MB The sidecar layer also auto-dedupes within a single run: the OFI section re-loads trades, but the second call hits the sidecar that the volume-bar section wrote moments earlier. CLI: `--rebuild-predecoded` purges sidecars for cold-path testing or after a wire-format change to Mbp10Snapshot / DbnTrade. Sidecars also self-invalidate on format-version mismatch so old caches are skipped silently rather than mis-deserializing. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
56 lines
1.3 KiB
TOML
56 lines
1.3 KiB
TOML
[package]
|
|
name = "ml-features"
|
|
version.workspace = true
|
|
edition.workspace = true
|
|
rust-version.workspace = true
|
|
authors.workspace = true
|
|
license.workspace = true
|
|
repository.workspace = true
|
|
homepage.workspace = true
|
|
documentation.workspace = true
|
|
publish.workspace = true
|
|
keywords.workspace = true
|
|
categories.workspace = true
|
|
description = "Feature engineering for Foxhunt ML models"
|
|
|
|
[features]
|
|
default = []
|
|
|
|
[dependencies]
|
|
# Internal workspace crates
|
|
ml-core.workspace = true
|
|
common.workspace = true
|
|
data = { path = "../data" }
|
|
# Alpha Block M (fractional differentiation): reuse `FractionalCoeffs` from
|
|
# ml-labeling instead of duplicating the binomial-coefficient math.
|
|
# ml-labeling only depends on ml-core, so this adds no new transitive weight.
|
|
ml-labeling.workspace = true
|
|
|
|
# Serialization and error handling
|
|
serde = { workspace = true, features = ["derive"] }
|
|
serde_json.workspace = true
|
|
bincode.workspace = true
|
|
anyhow.workspace = true
|
|
chrono.workspace = true
|
|
chrono-tz = "0.10"
|
|
tracing.workspace = true
|
|
once_cell = "1.19"
|
|
|
|
# Async and parallelism
|
|
tokio.workspace = true
|
|
rayon.workspace = true
|
|
|
|
# Data loading (DBN/Databento)
|
|
dbn = "0.42"
|
|
zstd.workspace = true
|
|
rand.workspace = true
|
|
|
|
[dev-dependencies]
|
|
tokio = { workspace = true, features = ["test-util", "macros"] }
|
|
approx.workspace = true
|
|
toml.workspace = true
|
|
tempfile = "3"
|
|
|
|
[lints]
|
|
workspace = true
|