# Backtest Performance Optimizations

This document tracks identified performance optimization opportunities for `BacktestExecutor.cs` based on analysis of the foreach loop that processes thousands of candles.

## Current Performance Baseline

- **Processing Rate**: ~1,707 candles/sec
- **Execution Time**: ~3.365 seconds for 5,760 candles
- **Memory Peak**: ~36.29 MB

## Optimization Opportunities

### 🔴 Priority 1: Reuse HashSet Instead of Recreating (CRITICAL)

**Location**: `BacktestExecutor.cs` line 267

**Current Code**:
```csharp
var fixedCandles = new HashSet<Candle>(rollingWindowCandles);
```

**Problem**: Creates a new HashSet 5,760 times (once per candle iteration). This is extremely expensive in terms of:
- Memory allocations
- GC pressure
- CPU cycles for hash calculations

**Solution**: Reuse HashSet and update incrementally:
```csharp
// Initialize before loop
var fixedCandles = new HashSet<Candle>(RollingWindowSize);

// Inside loop (replace lines 255-267):
if (rollingWindowCandles.Count >= RollingWindowSize)
{
    var removedCandle = rollingWindowCandles.Dequeue();
    fixedCandles.Remove(removedCandle);
}
rollingWindowCandles.Enqueue(candle);
fixedCandles.Add(candle);
// fixedCandles is now up-to-date, no need to recreate
```

**Expected Impact**: 20-30% performance improvement

---

### 🟠 Priority 2: Optimize Wallet Balance Tracking

**Location**: `BacktestExecutor.cs` line 283

**Current Code**:
```csharp
lastWalletBalance = tradingBot.WalletBalances.Values.LastOrDefault();
```

**Problem**: `LastOrDefault()` on `Dictionary.Values` is O(n) operation, called every 10 candles.

**Solution**: Track balance directly or use more efficient structure:
```csharp
// Option 1: Cache last balance when wallet updates
// Option 2: Use SortedDictionary if order matters
// Option 3: Maintain separate variable that updates when wallet changes
```

**Expected Impact**: 2-5% performance improvement

---

### 🟡 Priority 3: Optimize TradingBox.GetSignal Input

**Location**: `TradingBox.cs` line 130

**Current Code**:
```csharp
var limitedCandles = newCandles.ToList(); // Converts HashSet to List
```

**Problem**: Converts HashSet to List every time `GetSignal` is called.

**Solution**: 
- Modify `TradingBox.GetSignal` to accept `IEnumerable<Candle>` or `List<Candle>`
- Pass List directly from rolling window instead of HashSet

**Expected Impact**: 1-3% performance improvement

---

### 🟢 Priority 4: Cache Progress Percentage Calculation

**Location**: `BacktestExecutor.cs` line 297

**Current Code**:
```csharp
var currentPercentage = (currentCandle * 100) / totalCandles;
```

**Problem**: Integer division recalculated every iteration (minor but can be optimized).

**Solution**:
```csharp
// Before loop
const double percentageMultiplier = 100.0 / totalCandles;

// Inside loop
var currentPercentage = (int)(currentCandle * percentageMultiplier);
```

**Expected Impact**: <1% performance improvement (minor optimization)

---

### 🟢 Priority 5: Use Stopwatch for Time Checks

**Location**: `BacktestExecutor.cs` line 298

**Current Code**:
```csharp
var timeSinceLastUpdate = (DateTime.UtcNow - lastProgressUpdate).TotalMilliseconds;
```

**Problem**: `DateTime.UtcNow` is relatively expensive when called frequently.

**Solution**: Use `Stopwatch` for timing:
```csharp
var progressStopwatch = Stopwatch.StartNew();
// Then check: progressStopwatch.ElapsedMilliseconds >= progressUpdateIntervalMs
```

**Expected Impact**: <1% performance improvement (minor optimization)

---

## Future Considerations

### Batching Candle Processing
If business logic allows, process multiple candles before updating signals to reduce `UpdateSignals()` call frequency. Requires careful validation.

### Object Pooling
Reuse List/HashSet instances if possible to reduce GC pressure. May require careful state management.

### Parallel Processing
If signals are independent, consider parallel indicator calculations. Requires careful validation to ensure business logic integrity.

## Implementation Checklist

- [ ] Priority 1: Reuse HashSet instead of recreating
- [ ] Priority 2: Optimize wallet balance tracking
- [ ] Priority 3: Optimize TradingBox.GetSignal input
- [ ] Priority 4: Cache progress percentage calculation
- [ ] Priority 5: Use Stopwatch for time checks
- [ ] Run benchmark-backtest-performance.sh to validate improvements
- [ ] Ensure business logic validation passes (Final PnL matches baseline)

## Expected Total Impact

**Combined Expected Improvement**: 25-40% faster execution

**Target Performance**:
- Processing Rate: ~2,100-2,400 candles/sec (up from ~1,707)
- Execution Time: ~2.0-2.5 seconds (down from ~3.365)
- Memory: Similar or slightly reduced

## Notes

- Always validate business logic after optimizations
- Run benchmarks multiple times to account for system variance
- Monitor memory usage to ensure optimizations don't increase GC pressure
- Priority 1 (HashSet reuse) should provide the largest performance gain

