feat: max_bars config for fast smoketests (truncates dataset after loading)

Smoketest sets max_bars=60000 (~1 quarter). Applied in load_training_data()
on both fxcache and DBN paths. 0 = unlimited (production).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-04-04 16:42:05 +02:00
parent 6565221b9f
commit 2df09f5fbf
4 changed files with 26 additions and 1 deletions

View File

@@ -21,6 +21,7 @@ hidden_dim_base = 64
reward_scale = 1.0
huber_delta = 1.0
symbol = "ES.FUT"
max_bars = 60000
imbalance_bar_threshold = 1.0
imbalance_bar_ewma_alpha = 0.1

View File

@@ -1218,6 +1218,10 @@ pub struct DQNHyperparameters {
/// one instrument's DBN files are loaded. Default: "ES.FUT".
pub symbol: String,
/// Maximum bars to load for training. 0 = unlimited.
/// Truncates dataset after loading, before walk-forward split.
pub max_bars: usize,
/// Imbalance bar threshold for MBP-10 to bar conversion.
/// Higher = fewer bars (less noise), lower = more bars (more signal).
/// Only used when `data_source = "mbp10"`. Default: 100.0.
@@ -1705,6 +1709,7 @@ impl DQNHyperparameters {
// Data source: "ohlcv" (backward compatible) or "mbp10" (imbalance bars)
data_source: "ohlcv".to_string(),
symbol: "ES.FUT".to_string(),
max_bars: 0, // 0 = unlimited
imbalance_bar_threshold: 100.0,
imbalance_bar_ewma_alpha: 0.1,

View File

@@ -126,7 +126,7 @@ impl DQNTrainer {
self.ofi_features = Some(Arc::from(cached.ofi));
}
let all_data: Vec<(FeatureVector, Vec<f64>)> = cached.features
let mut all_data: Vec<(FeatureVector, Vec<f64>)> = cached.features
.into_iter()
.zip(cached.targets.into_iter())
.map(|(f, t)| {
@@ -134,6 +134,12 @@ impl DQNTrainer {
})
.collect();
// Truncate to max_bars if configured (0 = unlimited)
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);
all_data.truncate(self.hyperparams.max_bars);
}
let split = (all_data.len() * 80) / 100;
let train = all_data[..split].to_vec();
let val = all_data[split..].to_vec();
@@ -419,6 +425,12 @@ impl DQNTrainer {
feature_dim
);
// Truncate to max_bars if configured (0 = unlimited)
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);
training_data.truncate(self.hyperparams.max_bars);
}
// Split training data 80/20 for train/validation
let split_idx = (training_data.len() * 80) / 100;
let train_data = training_data[..split_idx].to_vec(); // cpu-side split

View File

@@ -92,6 +92,10 @@ pub struct TrainingSection {
/// EWMA alpha for adaptive imbalance threshold. Default: 0.1.
pub imbalance_bar_ewma_alpha: Option<f64>,
/// Maximum bars to load for training. 0 = unlimited (load all data).
/// Useful for fast smoketests on large datasets.
pub max_bars: Option<usize>,
}
/// Epsilon-greedy exploration parameters (DQN-specific).
@@ -741,6 +745,9 @@ impl DqnTrainingProfile {
if let Some(ref v) = t.symbol {
hp.symbol = v.clone();
}
if let Some(v) = t.max_bars {
hp.max_bars = v;
}
if let Some(v) = t.imbalance_bar_threshold {
hp.imbalance_bar_threshold = v;
}