fix(backtesting): Add mock() method to DefaultRepositories for tests

- Implements DefaultRepositories::mock() for wave_comparison tests
- Mock implementations use in-memory Arc<RwLock<>> for thread-safe testing
- Method is #[cfg(test)] scoped to test builds only
- Fixes compilation errors in wave_comparison.rs (lines 711, 730)
- All backtesting tests pass (2/2 wave_comparison tests OK)

Additional updates:
- Update .dockerignore, .env.runpod, CLAUDE.md
- Update Cargo.lock and Dockerfile.foxhunt-build
This commit is contained in:
jgrusewski
2025-11-02 21:31:49 +01:00
parent 7a5c84ff0c
commit 2cf07a9086
6 changed files with 486 additions and 25 deletions

View File

@@ -171,3 +171,201 @@ impl BacktestingRepositories for DefaultRepositories {
self.news.as_ref()
}
}
#[cfg(test)]
impl DefaultRepositories {
/// Create a mock instance for testing
///
/// This method is only available in test builds and creates
/// in-memory mock repositories for unit testing.
pub fn mock() -> Self {
use std::sync::Arc;
use tokio::sync::RwLock;
use std::collections::HashMap;
// Mock market data repository
struct MockMarketData {
data: Arc<RwLock<Vec<crate::strategy_engine::MarketData>>>,
}
#[async_trait]
impl MarketDataRepository for MockMarketData {
async fn load_historical_data(
&self,
_symbols: &[String],
_start_time: i64,
_end_time: i64,
) -> Result<Vec<crate::strategy_engine::MarketData>> {
Ok(self.data.read().await.clone())
}
async fn check_data_availability(
&self,
symbols: &[String],
_start_time: i64,
_end_time: i64,
) -> Result<HashMap<String, bool>> {
let mut availability = HashMap::new();
for symbol in symbols {
availability.insert(symbol.clone(), true);
}
Ok(availability)
}
}
// Mock trading repository
struct MockTrading {
trades: Arc<RwLock<HashMap<String, Vec<BacktestTrade>>>>,
metrics: Arc<RwLock<HashMap<String, PerformanceMetrics>>>,
backtests: Arc<RwLock<Vec<BacktestSummary>>>,
}
#[async_trait]
impl TradingRepository for MockTrading {
async fn save_backtest_results(
&self,
backtest_id: &str,
trades: &[BacktestTrade],
metrics: &PerformanceMetrics,
) -> Result<()> {
self.trades.write().await.insert(backtest_id.to_string(), trades.to_vec());
self.metrics.write().await.insert(backtest_id.to_string(), metrics.clone());
Ok(())
}
async fn load_backtest_results(
&self,
backtest_id: &str,
) -> Result<(Vec<BacktestTrade>, PerformanceMetrics)> {
let trades = self.trades.read().await
.get(backtest_id)
.cloned()
.unwrap_or_default();
let metrics = self.metrics.read().await
.get(backtest_id)
.cloned()
.unwrap_or_default();
Ok((trades, metrics))
}
async fn create_backtest_record(
&self,
backtest_id: &str,
strategy_name: &str,
symbols: &[String],
start_date: DateTime<Utc>,
end_date: DateTime<Utc>,
_initial_capital: f64,
_parameters: &HashMap<String, String>,
description: &str,
) -> Result<()> {
let summary = BacktestSummary {
backtest_id: backtest_id.to_string(),
strategy_name: strategy_name.to_string(),
symbols: symbols.to_vec(),
status: BacktestStatus::Queued,
total_return: 0.0,
sharpe_ratio: 0.0,
max_drawdown: 0.0,
created_at: Utc::now(),
start_date,
end_date,
description: description.to_string(),
};
self.backtests.write().await.push(summary);
Ok(())
}
async fn update_backtest_status(
&self,
backtest_id: &str,
status: BacktestStatus,
_error_message: Option<&str>,
) -> Result<()> {
let mut backtests = self.backtests.write().await;
if let Some(bt) = backtests.iter_mut().find(|b| b.backtest_id == backtest_id) {
bt.status = status;
}
Ok(())
}
async fn list_backtests(
&self,
limit: u32,
offset: u32,
strategy_name: Option<String>,
status_filter: Option<BacktestStatus>,
) -> Result<Vec<BacktestSummary>> {
let backtests = self.backtests.read().await;
let filtered: Vec<BacktestSummary> = backtests
.iter()
.filter(|bt| {
let name_match = strategy_name.as_ref()
.map(|n| bt.strategy_name == *n)
.unwrap_or(true);
let status_match = status_filter.map(|s| bt.status == s).unwrap_or(true);
name_match && status_match
})
.skip(offset as usize)
.take(limit as usize)
.cloned()
.collect();
Ok(filtered)
}
async fn store_time_series_data(
&self,
_backtest_id: &str,
_timestamp: DateTime<Utc>,
_equity: f64,
_drawdown: f64,
) -> Result<()> {
Ok(())
}
}
// Mock news repository
struct MockNews {
events: Arc<RwLock<Vec<crate::strategy_engine::NewsEvent>>>,
}
#[async_trait]
impl NewsRepository for MockNews {
async fn load_news_events(
&self,
_symbols: &[String],
_start_time: DateTime<Utc>,
_end_time: DateTime<Utc>,
) -> Result<Vec<crate::strategy_engine::NewsEvent>> {
Ok(self.events.read().await.clone())
}
async fn get_sentiment_data(
&self,
symbols: &[String],
_timestamp: DateTime<Utc>,
_lookback_hours: i32,
) -> Result<HashMap<String, f64>> {
let mut sentiment_map = HashMap::new();
for symbol in symbols {
sentiment_map.insert(symbol.clone(), 0.0);
}
Ok(sentiment_map)
}
}
Self {
market_data: Box::new(MockMarketData {
data: Arc::new(RwLock::new(Vec::new())),
}),
trading: Box::new(MockTrading {
trades: Arc::new(RwLock::new(HashMap::new())),
metrics: Arc::new(RwLock::new(HashMap::new())),
backtests: Arc::new(RwLock::new(Vec::new())),
}),
news: Box::new(MockNews {
events: Arc::new(RwLock::new(Vec::new())),
}),
}
}
}