diff --git a/.jules/bolt.md b/.jules/bolt.md index 289fae6..3ac930d 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -23,3 +23,7 @@ ## 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`. + +## 2026-09-30 - Avoid chained array operations during unique subset generation +**Learning:** Generating a subset of unique elements by mapping arrays, concatenating via spread operators into a Set, converting back via Array.from(), and slicing creates multiple intermediate objects and unneeded iterations. This creates excessive garbage collection, particularly when dealing with large inner loops like backtesting tickers. +**Action:** When extracting a unique, bounded subset from multiple arrays, use an explicitly bounded `for` loop and track seen elements with a `Set` to terminate iteration exactly when the limit is reached, skipping allocations for discarded elements. diff --git a/src/agent/backtestRunner.ts b/src/agent/backtestRunner.ts index e53b03c..9a63a7c 100644 --- a/src/agent/backtestRunner.ts +++ b/src/agent/backtestRunner.ts @@ -347,10 +347,25 @@ 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 snapshotPositions = (await broker.snapshot()).positions; + const tickers: string[] = []; + const seen = new Set(); + + // ⚡ Bolt: Optimize ticker extraction by avoiding chained .map(), Set spread, and .slice() allocations + for (let i = 0; i < snapshotPositions.length && tickers.length < maxCandidates; i++) { + const ticker = snapshotPositions[i]?.ticker; + if (ticker && !seen.has(ticker)) { + seen.add(ticker); + tickers.push(ticker); + } + } + for (let i = 0; i < discovery.candidates.length && tickers.length < maxCandidates; i++) { + const ticker = discovery.candidates[i]?.ticker; + if (ticker && !seen.has(ticker)) { + seen.add(ticker); + tickers.push(ticker); + } + } const decisions: FinalDecision[] = []; for (const ticker of tickers) {