From 24bae7d1013500fbb431e1e0ccd697f86d0e6c3c Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Sun, 5 Apr 2026 13:49:01 +0200 Subject: [PATCH] fix: test_data_dir returns base dir for fxcache key match MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test_data_dir() was returning the symbol-specific path (test_data/futures-baseline/ES.FUT) causing fxcache key mismatch — the cache was created with the base dir. Now returns the parent directory so trainer.train(data_dir, "ES.FUT", ...) can discover the fxcache via matching cache key. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/trainers/dqn/smoke_tests/helpers.rs | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/crates/ml/src/trainers/dqn/smoke_tests/helpers.rs b/crates/ml/src/trainers/dqn/smoke_tests/helpers.rs index 28df328a4..3164cb186 100644 --- a/crates/ml/src/trainers/dqn/smoke_tests/helpers.rs +++ b/crates/ml/src/trainers/dqn/smoke_tests/helpers.rs @@ -125,37 +125,45 @@ pub(super) fn load_smoke_data() -> anyhow::Result<( Ok((train, val)) } -/// Resolve single-symbol test data directory. +/// Resolve base data directory containing symbol subdirectories. +/// +/// Returns the PARENT of the symbol directory (e.g. `test_data/futures-baseline`), +/// not the symbol-specific path. Callers pass the symbol separately to +/// `trainer.train(data_dir, "ES.FUT", ...)` which enables fxcache key matching +/// (the cache key hashes all files at data_dir, not just one symbol). /// /// Uses `FOXHUNT_TEST_DATA` env var if set, otherwise auto-detects -/// `test_data/ES.FUT` relative to the workspace root. -/// Returns `None` if the directory doesn't exist. +/// relative to the workspace root. Returns `None` if no data found. pub(super) fn test_data_dir() -> Option { // Check env vars: FOXHUNT_TEST_DATA (explicit), TEST_DATA_DIR (CI PVC root) for var in &["FOXHUNT_TEST_DATA", "TEST_DATA_DIR"] { if let Ok(dir) = std::env::var(var) { let path = std::path::Path::new(&dir); - // Search for ES.FUT in common layouts: - // 1. $DIR/ES.FUT (direct) - // 2. $DIR/ohlcv/ES.FUT (CI PVC layout) + // If the env var points directly at a symbol dir, return its parent + if path.file_name().and_then(|n| n.to_str()) == Some("ES.FUT") { + if let Some(parent) = path.parent() { + return Some(parent.to_string_lossy().into_owned()); + } + } + // Check if ES.FUT exists as a subdirectory for sub in &["ES.FUT", "ohlcv/ES.FUT"] { - let candidate = path.join(sub); - if candidate.exists() { - return Some(candidate.to_string_lossy().into_owned()); + if path.join(sub).exists() { + return Some(path.to_string_lossy().into_owned()); } } } } - // Auto-detect from workspace root: test_data/ES.FUT or test_data/ohlcv/ES.FUT + // Auto-detect from workspace root let manifest = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_default(); let workspace = std::path::Path::new(&manifest) .parent() // crates/ .and_then(|p| p.parent()) // repo root .unwrap_or(std::path::Path::new("../..")); let test_data = workspace.join("test_data"); - for sub in &["futures-baseline/ES.FUT", "ES.FUT", "ohlcv/ES.FUT"] { - let candidate = test_data.join(sub); - if candidate.exists() { + // Return the base dir that contains ES.FUT as a subdirectory + for base in &["futures-baseline", ".", "ohlcv"] { + let candidate = test_data.join(base); + if candidate.join("ES.FUT").exists() { return Some(candidate.to_string_lossy().into_owned()); } }