🔧 Wave 104 Part 1: Stub Elimination + Panic Fixes
## Critical Production Fixes (2/7 blockers resolved) ### ✅ Fixed: Performance Metric Stubs - **File**: backtesting/src/metrics.rs - **Before**: calculate_monthly_performance() → Ok(Vec::new()) // stub - **After**: Full implementation with BTreeMap grouping, trade counts, win rates - **Lines**: +74 lines (monthly), +82 lines (yearly) - **Impact**: Enables monthly/yearly performance reporting ### ✅ Fixed: Connection Pool Panic - **File**: storage/src/model_helpers.rs:101 - **Before**: panic!("Connection pool is empty") - **After**: StorageResult<Arc<dyn ObjectStore>> with proper error handling - **Impact**: Service resilience on connection pool exhaustion ### 📝 Documentation Update - **File**: CLAUDE.md - **Status**: Updated to Wave 104 (89.5% → 90%+ target) - **Progress**: Waves 102-103 achievements documented **Next**: Wave 104 Part 2 - Launch 12 agents for final push to 90%+ 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1422,8 +1422,78 @@ impl MetricsCalculator {
|
||||
/// # Returns
|
||||
/// * `Result<Vec<MonthlyPerformance>>` - Vector of monthly performance summaries
|
||||
fn calculate_monthly_performance(&self) -> Result<Vec<MonthlyPerformance>> {
|
||||
// Implementation for monthly performance calculation
|
||||
Ok(Vec::new())
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
if self.snapshots.is_empty() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
// Group snapshots by month
|
||||
let mut monthly_groups: BTreeMap<(i32, u32), Vec<&PerformanceSnapshot>> = BTreeMap::new();
|
||||
|
||||
for snapshot in &self.snapshots {
|
||||
let key = (snapshot.timestamp.year(), snapshot.timestamp.month());
|
||||
monthly_groups.entry(key).or_default().push(snapshot);
|
||||
}
|
||||
|
||||
// Calculate metrics for each month
|
||||
let mut monthly_performance = Vec::new();
|
||||
|
||||
for ((year, month), snapshots) in monthly_groups {
|
||||
if snapshots.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let start_value = snapshots.first().unwrap().portfolio_value;
|
||||
let end_value = snapshots.last().unwrap().portfolio_value;
|
||||
|
||||
let return_pct = if start_value > Decimal::ZERO {
|
||||
((end_value - start_value) / start_value) * Decimal::from(100)
|
||||
} else {
|
||||
Decimal::ZERO
|
||||
};
|
||||
|
||||
// Count trades in this month
|
||||
let trade_count = self.trades.iter()
|
||||
.filter(|t| {
|
||||
let trade_month = (t.exit_time.year(), t.exit_time.month());
|
||||
trade_month == (year, month)
|
||||
})
|
||||
.count() as u64;
|
||||
|
||||
// Calculate win rate for month
|
||||
let month_trades: Vec<_> = self.trades.iter()
|
||||
.filter(|t| {
|
||||
let trade_month = (t.exit_time.year(), t.exit_time.month());
|
||||
trade_month == (year, month)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let winning_trades = month_trades.iter()
|
||||
.filter(|t| t.pnl > Decimal::ZERO)
|
||||
.count();
|
||||
|
||||
let win_rate = if !month_trades.is_empty() {
|
||||
Decimal::from(winning_trades) / Decimal::from(month_trades.len())
|
||||
} else {
|
||||
Decimal::ZERO
|
||||
};
|
||||
|
||||
// Use first day of month for timestamp
|
||||
let month_timestamp = chrono::Utc.with_ymd_and_hms(year, month, 1, 0, 0, 0)
|
||||
.single()
|
||||
.unwrap_or_else(|| snapshots.first().unwrap().timestamp);
|
||||
|
||||
monthly_performance.push(MonthlyPerformance {
|
||||
month: month_timestamp,
|
||||
return_pct,
|
||||
trade_count,
|
||||
win_rate,
|
||||
portfolio_value: end_value,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(monthly_performance)
|
||||
}
|
||||
|
||||
/// Calculate detailed yearly performance metrics
|
||||
@@ -1431,8 +1501,88 @@ impl MetricsCalculator {
|
||||
/// # Returns
|
||||
/// * `Result<Vec<YearlyPerformance>>` - Vector of yearly performance summaries
|
||||
fn calculate_yearly_performance(&self) -> Result<Vec<YearlyPerformance>> {
|
||||
// Implementation for yearly performance calculation
|
||||
Ok(Vec::new())
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
if self.snapshots.is_empty() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
// Group snapshots by year
|
||||
let mut yearly_groups: BTreeMap<i32, Vec<&PerformanceSnapshot>> = BTreeMap::new();
|
||||
|
||||
for snapshot in &self.snapshots {
|
||||
yearly_groups.entry(snapshot.timestamp.year()).or_default().push(snapshot);
|
||||
}
|
||||
|
||||
// Calculate metrics for each year
|
||||
let mut yearly_performance = Vec::new();
|
||||
|
||||
for (year, snapshots) in yearly_groups {
|
||||
if snapshots.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let start_value = snapshots.first().unwrap().portfolio_value;
|
||||
let end_value = snapshots.last().unwrap().portfolio_value;
|
||||
|
||||
let return_pct = if start_value > Decimal::ZERO {
|
||||
((end_value - start_value) / start_value) * Decimal::from(100)
|
||||
} else {
|
||||
Decimal::ZERO
|
||||
};
|
||||
|
||||
// Count trades in this year
|
||||
let trade_count = self.trades.iter()
|
||||
.filter(|t| t.exit_time.year() == year)
|
||||
.count() as u64;
|
||||
|
||||
// Calculate win rate for year
|
||||
let year_trades: Vec<_> = self.trades.iter()
|
||||
.filter(|t| t.exit_time.year() == year)
|
||||
.collect();
|
||||
|
||||
let winning_trades = year_trades.iter()
|
||||
.filter(|t| t.pnl > Decimal::ZERO)
|
||||
.count();
|
||||
|
||||
let win_rate = if !year_trades.is_empty() {
|
||||
Decimal::from(winning_trades) / Decimal::from(year_trades.len())
|
||||
} else {
|
||||
Decimal::ZERO
|
||||
};
|
||||
|
||||
// Calculate max drawdown for this year
|
||||
let year_snapshots: Vec<_> = snapshots.clone();
|
||||
let mut peak = year_snapshots[0].portfolio_value;
|
||||
let mut max_drawdown = Decimal::ZERO;
|
||||
|
||||
for snapshot in &year_snapshots {
|
||||
if snapshot.portfolio_value > peak {
|
||||
peak = snapshot.portfolio_value;
|
||||
}
|
||||
|
||||
let drawdown = if peak > Decimal::ZERO {
|
||||
((snapshot.portfolio_value - peak) / peak) * Decimal::from(100)
|
||||
} else {
|
||||
Decimal::ZERO
|
||||
};
|
||||
|
||||
if drawdown < max_drawdown {
|
||||
max_drawdown = drawdown;
|
||||
}
|
||||
}
|
||||
|
||||
yearly_performance.push(YearlyPerformance {
|
||||
year,
|
||||
return_pct,
|
||||
trade_count,
|
||||
win_rate,
|
||||
portfolio_value: end_value,
|
||||
max_drawdown,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(yearly_performance)
|
||||
}
|
||||
|
||||
/// Calculate skewness of returns distribution
|
||||
|
||||
Reference in New Issue
Block a user