fix(ml): fix TFT OOB in historical features — 51 not 54 elements

Feature vectors are [f64; 51] after WAVE 10 (Proxy OFI removal):
  [0..5] static, [5..15] known, [15..51] unknown (36 features)

Old code used fv[15..54].min(fv.len()) which silently produced 36
features per step but expected 39 in Array2 shape → ShapeError.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-02-27 18:52:35 +01:00
parent 20e67d4d64
commit 3564efe961

View File

@@ -23,7 +23,7 @@ impl TFTTrainer {
///
/// # Arguments
///
/// * `parquet_path` - Path to Parquet file containing OHLCV bars and 54 features
/// * `parquet_path` - Path to Parquet file containing OHLCV bars
///
/// # Returns
///
@@ -32,7 +32,7 @@ impl TFTTrainer {
/// # Features
///
/// - Lazy batch loading (10,000 rows at a time)
/// - 54 feature extraction from OHLCV bars (core features)
/// - 51-feature extraction from OHLCV bars (WAVE 10: 5 static + 10 known + 36 unknown)
/// - Sliding window creation (lookback=60, horizon=10)
/// - Memory-efficient processing for large datasets
pub async fn train_from_parquet(&mut self, parquet_path: &str) -> MLResult<TrainingMetrics> {
@@ -373,15 +373,18 @@ impl TFTTrainer {
for i in 0..(feature_vectors.len().saturating_sub(LOOKBACK + HORIZON)) {
let static_feats = Array1::from_vec(feature_vectors[i + LOOKBACK][0..5].to_vec());
let mut hist_data = Vec::new();
// Historical features: fv[15..51] = 36 unknown features per timestep
// Feature vector is [f64; 51]: [0..5] static, [5..15] known, [15..51] unknown
const HIST_START: usize = 15;
const HIST_END: usize = 51;
const NUM_HIST_FEATURES: usize = HIST_END - HIST_START; // 36
let mut hist_data = Vec::with_capacity(LOOKBACK * NUM_HIST_FEATURES);
for j in i..(i + LOOKBACK) {
let fv = &feature_vectors[j];
let start = 15.min(fv.len());
let end = 54.min(fv.len());
hist_data.extend_from_slice(&fv[start..end]);
hist_data.extend_from_slice(&feature_vectors[j][HIST_START..HIST_END]);
}
let historical_feats =
Array2::from_shape_vec((LOOKBACK, 39), hist_data).map_err(|e| {
Array2::from_shape_vec((LOOKBACK, NUM_HIST_FEATURES), hist_data).map_err(|e| {
MLError::ModelError(format!(
"Failed to create historical features array: {}",
e