From f658c89fbbed25cfabe4bad92465f4545133bb89 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 30 Sep 2026 15:48:40 +0000 Subject: [PATCH] perf: optimize ticker extraction by replacing chained array methods with bounded loops Co-authored-by: toreleon <42534763+toreleon@users.noreply.github.com> --- .jules/bolt.md | 4 ++++ src/agent/backtestRunner.ts | 23 +++++++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) 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) {