NexusTrade · Infrastructure
Four AI models failed to make my Rust backtesting engine faster. Then GPT-6 Astra read a math paper.
GPT-6 Astra convinced me that AGI is here, and that artificial superintelligence is less than two years out. This is how it solved an optimization problem four other frontier models told me was impossible.
The beginning
Begging for Velocity 9
At the end of 2022 I gave up on my own platform.
NextTrade was the first version, written in TypeScript, and I had spent over two years on it. It was too slow to do the thing I built it for. Running a genetic optimization on my MacBook Pro could crash the entire machine, and if I kept the population small enough to survive, a hundred generations on a complex strategy took days.
So I open-sourced it. I read every Medium post and every Reddit thread I could find first. Golang was familiar from my day job, though its collector shows up in exactly the tail latencies a backtest lives in. C++ was the traditional pick and I had Jane Street daydreams. Rust won, and then I spent eighteen months hating it loudly enough to go viral about it before I came around.
ThePrimeTime read the article on stream.
Like Hunter Zolomon injecting himself with speed steroids, I wanted to be the fastest. I used AI to get there, but I have never used anything quite like this.
The baseline
Where I was starting from
Let's run back in time to April. I published an article about using Claude Opus 4.7 to migrate the engine off a 200 GB per worker mmap architecture. That weekend cut my disk bill sevenfold and made warm backtests four times faster.
Same ten-year window, same bars out of my data lake, Fly machines of the same class on both sides. The minute row measures throughput: the TypeScript side made no trades on the runner I built for it. NextTrade is still on GitHub.
Two years of my life bought that gap. When I did the Opus 4.7 migration I could not have derived the cost optimizations myself, but I understood them once they were on screen. It read Parquet instead of decoded binaries, it pruned before it decoded, and it shared one prefetched metadata read across concurrent chunk fetches.
Astra is just... different.
The wall
Four models told me no
That migration bought me a lot, and I wanted more out of it. As April turned to May and May to June, I threw every serious agentic harness at this codebase specifically to make minutely backtests on my Public Portfolio Challenge faster. I used Claude Fable 5 through Claude Code, GPT-5.6 Sol, Grok 4.6 in Cursor, and Claude Opus across a dozen sessions.
I funded a real brokerage account with $25,000 and deployed it in public, so the account, the strategies and every trade are visible while they happen. The strategies themselves are on GitHub.
These models tried their best to make it faster. What none of them did was touch the allocation solver, and eventually one of them told me why:
What Claude Opus 5 told me
"The physics just wouldn't allow it to be faster."
That reading was reasonable. My Rust backtesting engine walks minute bars in a tight event-driven loop it shares with the live trading path, so the code that replays 2019 is the code that places real orders. Every model traced the same path: the LRU cache, the columnar pruning, then an allocator with a fixed iteration budget, already tuned with a leetcode-hard binary search. It was all bounded and predictable, with no garbage collector left to blame, so the remaining cost looked structural.
One hundred guesses to land on one number.
commit 3d679ac6d4
Three assets want 60%, 35% and 15% of the book. No position may exceed its 50% cap, and the three must add up to a 90% deployment target. The solver looks for one offset to subtract from every proposal. It has no formula for that offset, so it guesses, checks, and halves the interval. It ran on every rebalance and every simulated minute.
before it stops
let mut low = min(value - cap); // -35
let mut high = max(value); // 60
for _ in 0..100 {
let mid = 0.5 * (low + high);
let sum = values.iter().zip(caps)
.map(|(v, c)| (v - mid).clamp(0.0, *c))
.sum::<f64>();
if sum > target { low = mid; }
else { high = mid; }
}
Press play. Watch how many passes it takes before the total settles on 90.
I 100% fully believed it. I started pricing hardware.
The commit
What Astra actually did
My engine allocates capital by projecting proposed portfolio weights onto the nearest feasible set: nothing below zero, nothing above its cap, everything summing to the deployment target. Here is how it had done that since I wrote it.
let mut lower = /* lowest possible offset */;
let mut upper = /* highest possible offset */;
for _ in 0..100 {
let midpoint = 0.5 * (lower + upper);
let sum = values
.iter()
.zip(caps.iter())
.map(|(value, cap)| (value - midpoint).clamp(0.0, *cap))
.sum::<f64>();
if sum > feasible_target { lower = midpoint; } else { upper = midpoint; }
}
let offset = 0.5 * (lower + upper);Guess an offset. Add up every weight. Halve the interval. Do it a hundred times, every time any strategy rebalances, on every simulated minute of every backtest.
This is what replaced it in the common case.
// If every projected weight is strictly within its bounds, every slope
// is -1. The common offset follows from sum(values) - n*offset = target.
// Verify that assumption before returning; otherwise use the full sweep.
let goal = target.min(caps.iter().sum::<f64>()).max(0.0);
let offset = (values.iter().sum::<f64>() - goal) / values.len() as f64;
if goal > f64::EPSILON
&& offset.is_finite()
&& values.iter().zip(caps).all(|(v, cap)| {
let w = v - offset;
w > 0.0 && w < *cap
})
{
projected.clear();
projected.extend(values.iter().map(|value| value - offset));
// verify the sum, then return
}Sum the weights, subtract the goal, divide by how many there are. Then check that every weight landed strictly inside its bounds, and if it did, that single number is the answer.
The search only ever existed to find where weights clip at zero or at their cap. When nothing clips, every weight moves by the same amount, and that amount falls out of the arithmetic in one step. The hundred halvings were solving a case that most rebalances never reach.
A hundred scans became one division.
The same answer.
A very different route.
1 SHARED OFFSET
Move every proposed weight by the same offset. Keep each between zero and its cap. Make the total equal 100%.
through all eight weights
Guess, add the clamped weights, then halve the interval.
mid = (low + high) / 2
total = Σ clamp(vᵢ − mid, 0, cᵢ)
if total > target: low = mid
else: high = mid
then a bounds check
offset = (Σ values − target) / n
if every weight is inside its bounds:
return the verified projection
else:
solve the matching linear segment
Press Play or Step to compare the two methods.
Measured against the shipped baseline
Eight-ETF risk parity over a full year of minute bars: 352.762s to 124.343s on a first pass (64.75%) and 309.881s to 100.263s warm (67.64%). Same harness, same primary, same financial contract, only the allocation module differs. Those two figures measure this one change against the release immediately before it, so they are not the campaign totals. The whole-campaign number for the same workload, 470.161s to 51.499s warm, arrives later and folds in every change that came after this one.
The insight
The paper
The reason I could not have written those seven lines is that I did not know what my own problem was called.
Astra named it. My weight projection is a special case of the continuous quadratic knapsack problem, which has a literature going back decades, and it cited Section 2 of Cominetti, Mascarenhas and Silva, A Newton's method for the continuous quadratic knapsack problem, published in 2014.
Once the problem has a name the structure is visible. Every weight is clamp(proposal[i] - offset, 0, cap[i]), so each coordinate changes behavior at exactly two offsets: the one where it pins to its cap, and the one where it falls to zero. Between those breakpoints the total is linear in the offset. Eight assets give you at most sixteen breakpoints. Sort them, sweep once, solve the segment holding your target.
Two breakpoints. A straight line between them. Increase the offset and the weight moves from its cap toward zero.
That alone replaced a hundred scans with a sort and one pass. The guarded interior shortcut then removed the sort too, because when nothing is pinned every slope is exactly −1 and the answer is arithmetic.
I have written this allocator, profiled it, and optimized it twice. I never thought to ask whether somebody had already solved it in 2014.
The full diff
Everything else in the same release
The projection was one of five changes Astra shipped in a single release, plus a repair to its own regression. Three of the five are things Rust lets you say out loud: borrow instead of clone, reuse a buffer instead of allocating, and settle at compile time which of two sweep directions a basket gets.
| Change | What it does | click |
|---|---|---|
| Breakpoint sweep | Solves the projection at its breakpoints instead of bisecting. This is the one that came from the paper. | |
Every weight is
Cominetti, Mascarenhas and Silva, A Newton's method for the continuous quadratic knapsack problem (2014), §2. Measured on eight-ETF risk parity: 352.762s to 124.343s first pass, 309.881s to 100.263s warm. | ||
| Guarded interior projection | This is the seven lines. It solves the offset in closed form when no weight is pinned, and verifies that before returning. | |
The sweep still sorts. When no weight ends up pinned at either bound, every slope is exactly −1, the total is
Above an input scale of 1e6 it still defers to the legacy path, because at that magnitude a tolerance could hide a genuinely wrong allocation. | ||
| Factored risk gradient | Restructures the risk-parity gradient so shared terms are computed once per solve. | |
The risk-parity derivative recomputed the same finite sums for every output coordinate. Distributing them leaves one term that is shared across all coordinates, and one quotient per coordinate rather than one per contribution.
An algebraic identity in exact arithmetic, not a bit-identical rewrite. Measured on top of the breakpoint baseline: 125.990s to 111.236s first pass (11.71%), and 13.48% then 14.56% on warm repeats. | ||
| Reused trial storage | Reuses trial and projection buffers across solver iterations instead of allocating each time. | |
The line search allocated a fresh candidate vector and a fresh projection result on every trial, up to 500 times per solve, and computed a gradient for trials it was about to reject. Only accepted trials need one. The buffers now live for the whole solve and the immutable covariance and current-book inputs are validated once rather than per trial.
Scratch reuse alone was worth about 5%. The release measured 361.546s to 327.589s first pass (9.39%) and 332.047s to 303.831s warm (8.50%), with 1,440 ordinary and 144 boundary solver comparisons passing. | ||
| Exact-zero liquidation | Corrects an opening-time discrepancy when a target weight is exactly zero. | |
This one is a correctness fix that the faster solver exposed rather than caused. An exact order trace found that a zero DBC target produced a sale one representable quantity below the holding, leaving 1.4210854715202004e-14 shares behind. The position never closed, so the next buy inherited the old lot's opening time and every downstream holding-period calculation was wrong.
Nonzero targets, whole-share restrictions and genuine partial fills are untouched. 54 focused tests pass: 14 rebalance, 31 position-accounting, 9 drawdown. The new exact-exit regression fails on the original code at the quantity assertion. | ||
| Large-basket direction fix | Sweeps downward from zero invested above 128 assets, repairing a regression the first version introduced. | |
The first sweep started from every asset at its cap and subtracted. On a 2,048 or 8,192 asset basket that means subtracting small numbers from a very large total, and the cancellation error was enough to fail the feasibility check and drop into the bisection fallback, after the sort had already been paid for. It ran nearly twice as slow as the algorithm it replaced. The repair sweeps downward from zero invested above 128 assets and keeps the measured ascending arithmetic at or below it, chosen as a compile-time specialization rather than per-event dispatch. At 8,192 assets it is 1.17 to 2.20 times faster, and it fell back in 0 of 72 calls where the first version fell back in 72 of 72. | ||
That release is one of twenty-one accepted changes across the campaign. The broad stress workload, 5,655 names of minute options data, came down like this.
Phases reset between measurements. These are not additive percentages.
Across all of it, I have exactly one matched comparison between the engine before any of this and the engine after, on the same saved one-year minute options request with eighteen recorded observations.
| Cache state | Before | After | Less time | Speedup | click |
|---|---|---|---|---|---|
| Fresh process, empty disk | 368.061s | 203.061s | 44.8% | 1.81× | |
Nothing is cached anywhere. A brand new process on a worker whose disk cache is empty, so every Parquet range is fetched from object storage, decoded, and only then replayed. This is what a backtest costs on a machine that has just woken from zero, and it is the number that pays for every later row. The original engine had no disk cache at all, so this single 368.061s figure is also its restart reference. That is why the same before value appears twice. | |||||
| Fresh process, populated disk | 368.061s | 97.726s | 73.4% | 3.77× | |
The Rust process restarted, but the worker's persistent disk cache survived. The engine re-reads decoded ranges from local disk instead of going back to Tigris, which is the single largest structural win in the campaign: the same work, minus the network. This is the row that describes a real user submitting a backtest to a warm fleet, which is most submissions. | |||||
| Warm, same process | 163.704s | 36.386s | 77.8% | 4.50× | |
Another run inside the same process, with reusable entries still in RAM. There is no fetch, no decode and no disk. What remains is close to pure compute, which is exactly why the solver work matters here and barely registers on the empty-disk row. Warm does not mean cached results. Every one of these runs replayed the full year and produced its own output. | |||||
So the Rust backtesting engine is 77.8% faster warm and 73.4% faster from a fresh process on a populated disk. The true number depends on cache state. On the workload the solver actually touches, eight-ETF risk parity, warm execution went from 470.161 seconds to 51.499 seconds. That is 89% less, or 9.1 times.
The bill
What it cost me to ship it
There were two real costs.
It cost storage, and it cost a lot of complexity. The sparse stock layout keeps a second representation of the same daily data beside the canonical one so a reader can take only the rows a strategy actually touches. Active companion data and indexes come to 69.375 GiB, and the whole sparse prefix including retained canaries is 77.044 GiB. At $0.02 per GiB-month that is about $1.54 a month.
The dollar figure was never the problem. The cost is that there are now two layouts, a publisher that has to keep them in step, a reader that falls back to canonical when a companion is missing or stale, and a whole class of bug where a backtest quietly reads the wrong representation. That complexity is permanent and I carry it on every change I make from here.
It is worth it at my volume, and it gets better with scale. Once I am running enough load that a machine has to be always on, a gigabyte and a half a month amortizes into nothing against the compute it saves.
The first version was slower where it counted. On baskets of 2,048 and 8,192 assets the sweep ran nearly twice as slow as the bisection it replaced, because cancellation while subtracting from a large capped total drove it into its own fallback after it had already paid for the sort. The repair sweeps downward from zero invested above 128 assets. At 8,192 the final version is 1.17 to 2.20 times faster, and it fell back in 0 of 72 calls where the first fell back in 72 of 72.
Who found the regression
The regression only showed on input shapes the first benchmark never covered, and I did not catch it. Astra did. It profiled the engine, found the bottleneck, read the code, wrote the fix, benchmarked it, noticed its own 8,192-asset regression, diagnosed the cancellation causing it, repaired it, ran the regression suite, deployed once I approved, and then asked whether I wanted to keep going. I barely asked a follow-up question.
The goal
Five minutes
Five minutes is roughly where a person stops waiting and goes to do something else. That was the target. On September 4 my own notes read: the cold five-minute goal remains unmet.
Broad 5,655-name stress
9m42s → ~4m
581.7s to 218–245s
Year request, app path
5m49s → 1m52s
post-first-fixes baseline
Saved options year, warm
37s
current source, September 7
I wanted five minutes. I got thirty-seven seconds warm, one minute fifty-two on the request my users actually submit, and under four minutes on the heaviest thing I run.
I did not buy anything. My notes from that week say do not buy larger machines yet, with the 160 GiB and 256 GiB resize ideas rejected. The fleet is the same five 128 GB machines it was in August. The bare metal I was pricing is still unbought.
These are the workloads I measured. They do not establish a five-minute ceiling for arbitrary histories, universes, strategies or concurrent load, and I have never claimed they do.
The argument
So is this AGI
AGI is here.
Every model I have used, including ones I have written glowing articles about, is excellent at work I can describe. Make this faster. Remove this allocation. Read four columns instead of twelve. I supply direction, the model supplies competence, and the ceiling on the result is my own imagination.
I could not have asked for this one. Asking required knowing that my allocator was an instance of a named problem, and I did not know it had a name. Astra crossed from a Rust file into a 2014 optimization paper, came back, and left seven lines that took 65% off a workload four other frontier models had looked at and left alone. One of them told me physics forbade it.
It knew something I did not, about my own code.
My codebase is not where it started doing this. Astra scores 97.6% on FrontierMath Tier 4, against 83.0% for the model before it. It pushed the bound on short prime gaps from 240 down to 186, and OpenAI published the conditional Lean 4 formalization and the numerical certificate alongside the paper. In a separate result it improved a term in a large prime gap bound that had not moved in more than eighty years. A model that produces new mathematics and then applies a decade-old paper to a Rust file it had never seen is doing the thing the word was invented for.
I wanted my platform to be the fastest retail trading platform there is. And now, it is.
Zolomon's speed was borrowed and it made him the fastest. Mine was borrowed too, out of a paper I never read, in seven lines I did not write. I will take that trade.
There are still theoretical improvements sitting in my run log, and I do not need a single one of them. The engine is far more than fast enough for my needs on even my most complex books, and it is faster than I ever imagined it would get.
Rust is now by far the best language to build in. It was always the fastest and the strictest; what changed is that the cost of satisfying it moved off me. The model writes the code, the compiler refuses the whole category of mistakes I used to review for, and a model at Astra's level makes even that review a formality. The tax I spent eighteen months complaining about is the exact thing that makes AI-written systems code safe to ship.
AGI stands for artificial general intelligence, and the load-bearing word is general. It means one system that is smarter than the average person across the whole spread of what people know.
Ask someone to explain the citric acid cycle, prototype a GPU from scratch, and interpret the result of a quantum mechanics experiment. We have experts in each of those three. I would wager that well under one percent of the people alive can do all three fluently. Astra does all three, and it also went and found a 2014 optimization paper for a Rust file it had never seen.
Superintelligence is the part that has become a hardware question. On August 6 AMD announced it is acquiring Taalas, a Toronto startup that etches model weights directly into the silicon rather than paging them out of high-bandwidth memory, keeping the KV cache and fine-tuning adapters in SRAM beside them. Its first chip served Llama 3.1 8B at 16,960 tokens a second. Take generality this broad, delete the trip from memory, and you end up with a model that processes about as fast as you can think.
That generality is already showing up a long way from code. Astra drives Blender directly, writing the Python, running it headless, looking at the render and revising it. Two days after launch the AI researcher Peter Gostev posted a clip of it rebuilding Craig Federighi as a 3D character, roughly 900 frames rendered and stitched into 19 seconds, and it cleared 2.5 million views by the next morning.
Astra rebuilding Craig Federighi in Blender. Posted by Peter Gostev, September 5, 2026.
The paper is the smallest part of it. The same model profiled my engine, found the bottleneck, wrote the fix, measured it, caught its own regression, repaired that, deployed, and asked whether I wanted to go again. Put that loop inside the system that builds worlds in Blender at 10,000 tokens per second, and general stops being the right word for what these things are doing.
ASI is coming.
Describe a trading strategy in plain English. The Rust backtesting engine behind it is the one this article is about.
Open the agent
No comments yet.