diff --git a/.jules/bolt.md b/.jules/bolt.md index 289fae6..1d240fb 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -23,3 +23,4 @@ ## 2026-09-19 - Avoid chained array methods before Set initialization **Learning:** Chaining `.map().filter()` inside a `new Set()` constructor creates multiple intermediate arrays, causing unnecessary memory allocation and garbage collection overhead in hot paths (like ticker normalization during discovery). **Action:** Replace chained array methods with a direct indexed `for` loop that transforms, validates, and `.add()`s directly to a freshly initialized `Set`. +## 2025-09-29 - [Avoid chained array allocations in backtests]\n**Learning:** High-frequency backtests suffer when allocating intermediate arrays in hot loops (e.g., using chained `.map()`, array spreads, and `.slice()`). These create significant GC overhead.\n**Action:** Replace functional array pipelines with bounded `for` loops and `Set`s with early-exit conditions when extracting deduplicated bounded subsets. diff --git a/src/agent/backtestRunner.ts b/src/agent/backtestRunner.ts index e53b03c..34e222b 100644 --- a/src/agent/backtestRunner.ts +++ b/src/agent/backtestRunner.ts @@ -347,10 +347,18 @@ export async function runBacktestSession( universe: BACKTEST_DISCOVERY_UNIVERSE, limit: maxCandidates, }); - const held = (await broker.snapshot()).positions.map((p) => p.ticker); - const tickers = Array.from( - new Set([...held, ...discovery.candidates.map((c) => c.ticker)]), - ).slice(0, maxCandidates); + const snap = await broker.snapshot(); + // ⚡ Bolt: Removed .map(), spread, and .slice() chain to avoid allocating large intermediate arrays in backtests + const tickersSet = new Set(); + const positions = snap.positions; + for (let i = 0; i < positions.length && tickersSet.size < maxCandidates; i++) { + tickersSet.add(positions[i]!.ticker); + } + const candidates = discovery.candidates; + for (let i = 0; i < candidates.length && tickersSet.size < maxCandidates; i++) { + tickersSet.add(candidates[i]!.ticker); + } + const tickers = Array.from(tickersSet); const decisions: FinalDecision[] = []; for (const ticker of tickers) {