Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
## 2024-05-14 - Time-Series O(log n) Search Pattern
**Learning:** `src/agent/backtestRunner.ts` makes heavy use of `.filter((b) => b.time <= asOf)` to get the latest close price inside loop bounds, changing potentially O(1) loop lookups or O(log n) lookups into repetitive O(n) filtering over large arrays.
**Action:** Expose `findLastBarIndex` in `src/data/sources/dnsePublic.ts` and use binary search or track indices explicitly in tight loops.
## 2024-05-24 - O(log n) over O(n) array operations for Time-Series
**Learning:** Backtest intervals involve frequent repeated lookups on chronologically sorted time-series market data arrays. Existing implementations using `.filter()` and `.sort()` on large `Bar` arrays have an $O(N \log N)$ complexity and unnecessarily reiterate arrays that are already inherently sorted.
**Action:** Replace full-array filters and sorts on `Bar` data with a combination of `findLastBarIndex()` (for $O(\log n)$ boundary lookups) followed by `.slice()`. This pattern is dramatically more CPU-efficient on large historical datasets.
14 changes: 10 additions & 4 deletions src/agent/backtestRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,16 @@ function parseBacktestInterval(value: string | undefined): { label: string; minu
}

function intervalCloses(vnindexBars: Bar[], startSec: number, endSec: number, intervalMinutes: number): number[] {
const base = vnindexBars
.filter((b) => b.time >= startSec && b.time <= endSec)
.map((b) => b.time)
.sort((a, b) => a - b);
// ⚡ Bolt: Optimize sub-interval extraction from O(n log n) filter+sort to O(log n) binary search
const startIdx = findLastBarIndex(vnindexBars, startSec - 1) + 1;
const endIdx = findLastBarIndex(vnindexBars, endSec);

if (startIdx > endIdx || startIdx >= vnindexBars.length || endIdx < 0) {
return [];
}

const base = vnindexBars.slice(startIdx, endIdx + 1).map((b) => b.time);

const step = Math.max(1, Math.round(intervalMinutes / 30));
if (step === 1) return base;

Expand Down
Loading