fix(data-loading): truncate OFI in lockstep with bars on max_bars path
Two truncation sites in data_loading.rs truncated feature/target vectors to max_bars but left self.ofi_features at the original (cache-full) length. Downstream DqnGpuData::upload_slices asserts OFI len == num_bars and surfaced this as "OFI/market data length mismatch: 175874 OFI rows vs 5000 bars — fxcache is corrupt". The cache was not corrupt — the upstream truncation was the bug. Fixed by moving OFI storage below the truncation block in the fxcache path and by adding a parallel truncate in the DBN fallback path. Added debug_assert_eq on equal lengths post-truncation so future regressions hard-fail in dev. Also corrected the cuda_pipeline/mod.rs error message to point at the actual culprit (upstream truncation desync) instead of blaming the on-disk cache.
This commit is contained in:
@@ -286,7 +286,7 @@ impl DqnGpuData {
|
||||
}
|
||||
if ofi.len() != num_bars {
|
||||
return Err(MLError::ModelError(format!(
|
||||
"OFI/market data length mismatch: {} OFI rows vs {} bars — fxcache is corrupt",
|
||||
"OFI/market data length mismatch: {} OFI rows vs {} bars — upstream truncation desync (check data_loading.rs max_bars paths)",
|
||||
ofi.len(), num_bars,
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -157,8 +157,8 @@ impl DQNTrainer {
|
||||
}
|
||||
let ofi_nonzero = cached.ofi.iter().filter(|r| r.iter().any(|&v| v != 0.0)).count();
|
||||
tracing::info!("OFI loaded from fxcache: {} bars, {} non-zero rows", cached.ofi.len(), ofi_nonzero);
|
||||
self.ofi_features = Some(Arc::from(cached.ofi));
|
||||
|
||||
let mut ofi_data = cached.ofi;
|
||||
let mut all_data: Vec<(FeatureVector, Vec<f64>)> = cached.features
|
||||
.into_iter()
|
||||
.zip(cached.targets.into_iter())
|
||||
@@ -167,11 +167,18 @@ impl DQNTrainer {
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Truncate to max_bars if configured (0 = unlimited)
|
||||
// Truncate to max_bars if configured (0 = unlimited).
|
||||
// OFI must be truncated in lockstep — downstream GPU upload asserts
|
||||
// ofi.len() == num_bars and the desync previously surfaced as a
|
||||
// misleading "fxcache is corrupt" error.
|
||||
if self.hyperparams.max_bars > 0 && all_data.len() > self.hyperparams.max_bars {
|
||||
tracing::info!("max_bars={}: truncating {} → {} bars", self.hyperparams.max_bars, all_data.len(), self.hyperparams.max_bars);
|
||||
tracing::info!("max_bars={}: truncating {} → {} bars (incl. OFI)", self.hyperparams.max_bars, all_data.len(), self.hyperparams.max_bars);
|
||||
all_data.truncate(self.hyperparams.max_bars);
|
||||
ofi_data.truncate(self.hyperparams.max_bars);
|
||||
}
|
||||
debug_assert_eq!(ofi_data.len(), all_data.len(),
|
||||
"OFI and market-data lengths must match after truncation");
|
||||
self.ofi_features = Some(Arc::from(ofi_data));
|
||||
|
||||
let split = (all_data.len() * 80) / 100;
|
||||
let train = all_data[..split].to_vec();
|
||||
@@ -497,11 +504,22 @@ impl DQNTrainer {
|
||||
feature_dim
|
||||
);
|
||||
|
||||
// Truncate to max_bars if configured (0 = unlimited)
|
||||
// Truncate to max_bars if configured (0 = unlimited).
|
||||
// OFI (if present from the DBN path above) must be truncated in lockstep —
|
||||
// downstream GPU upload asserts ofi.len() == num_bars.
|
||||
if self.hyperparams.max_bars > 0 && training_data.len() > self.hyperparams.max_bars {
|
||||
tracing::info!("max_bars={}: truncating {} → {} bars", self.hyperparams.max_bars, training_data.len(), self.hyperparams.max_bars);
|
||||
tracing::info!("max_bars={}: truncating {} → {} bars (incl. OFI)", self.hyperparams.max_bars, training_data.len(), self.hyperparams.max_bars);
|
||||
training_data.truncate(self.hyperparams.max_bars);
|
||||
if let Some(ofi_arc) = self.ofi_features.take() {
|
||||
let mut ofi_vec: Vec<[f64; 20]> = ofi_arc.iter().copied().collect();
|
||||
ofi_vec.truncate(self.hyperparams.max_bars);
|
||||
self.ofi_features = Some(Arc::from(ofi_vec));
|
||||
}
|
||||
}
|
||||
debug_assert!(
|
||||
self.ofi_features.as_ref().map_or(true, |o| o.len() == training_data.len()),
|
||||
"OFI and training-data lengths must match after truncation",
|
||||
);
|
||||
|
||||
// Split training data 80/20 for train/validation
|
||||
let split_idx = (training_data.len() * 80) / 100;
|
||||
|
||||
Reference in New Issue
Block a user