🔧 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:
jgrusewski
2025-10-04 19:55:23 +02:00
parent c05ca70e50
commit d16fa83cf4
3 changed files with 170 additions and 13 deletions

View File

@@ -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