Skip to main content

Autonomous trading: the board's biggest ratio misses by 0.003

· 20 min read
Vadim Nicolai
Senior Software Engineer

The largest spread-to-standard-error ratio on the board is 4.2996, and it is not significant. It has to clear 4.302653 — the two-sided 5% critical value on 2 degrees of freedom. That is not the 1.959964 the rest of the one-day table is measured against. 4.2996 falls 0.003 short. Nothing about the number is wrong; it was judged against a bar that belonged to a different sample size — the error Bailey and López de Prado (2014) built the deflated Sharpe ratio to catch, where the length of the track record behind a statistic is part of the threshold and not a footnote to it.

That gap is worth a long article not because 0.003 is large, but because the board prints no column that says so, and because the system that produced the number declined to promote, demote or score anything on the strength of it. The failure mode has a name. Bailey and López de Prado (2014) describe it as an undeflated ratio: a performance statistic reported without controlling for the number of trials behind it, the length of the track record, and the non-normality of the sample. The correction applied to this cell is the crudest possible version of their adjustment — the one that comes free with a t-table.

Autonomous trading: extreme-move alpha survives a liquidity cut

· 20 min read
Vadim Nicolai
Senior Software Engineer

Every robustness test is a confession. It names the failure mode its author fears most, then tries to kill it. The test behind this record was aimed at the most respectable fear in cross-sectional equity work: that a screen ranking stocks on how violently they trade is a small-cap artifact wearing a ranking's clothes. The surprise is not that the screen survived the knife. The surprise is how little blood the knife drew — and what that reveals about which statistics are worth robustness-testing in the first place.

The research board this record comes from screens thousands of names each day. One lane ranks them by intraday high-low range as a percentage of price and asks whether its top names concentrate five-day extremes: an up-tail of fifty percent over five days and a log-symmetric down-tail at minus one-third. The obvious objection is the one any quant makes on sight: rank on realised volatility and you surface the smallest, thinnest names that clear the gates, and those names move fifty percent for reasons that have nothing to do with the ranking being informative.

How often does anyone bother to test that objection instead of asserting it? An audit-oriented evidence map of 77 LLM-trading studies found that of the 19 that met a closed-loop evaluation bar, only 1 documents universe or survivorship handling at all (Xia et al., 2026). Universe handling is the unglamorous act this whole measurement exists to perform — and the literature audit says publishing it is the exception, not the default.

So here is the answer in the form the question deserves: the extreme-move lift persists after excluding the smallest, least-liquid names. It is not a small-cap illusion; it survives a liquidity filter. What matters is the size of that survival — and why the survival is so much larger than the standard small-cap story would predict.

Autonomous trading: news does not predict stock direction

· 22 min read
Vadim Nicolai
Senior Software Engineer

Twelve combinations of event feed and window, measured: a catalyst does not separate the up tail from the down tail on this equities screen. The one arm that crossed t = 2 was logged as a lead and refused promotion.

Does a catalyst separate winners from losers? The measurement cannot say. No separation was detected across the twelve feed-and-window combinations, but the test can only rule out an effect larger than about 2.5pp — see the correction below. Measured: catalyst and no-catalyst names posted nearly identical spreads. The one signal that looked like it did — insider purchases within five days — was logged as a lead, priced against its own sixteen-test background, and refused promotion.

Self-evolving agents: survivorship bias wrong way in stocks

· 19 min read
Vadim Nicolai
Senior Software Engineer

Survivorship bias is supposed to flatter a backtest. A survivor-only universe deletes the names that died along the way. Every number computed on it should therefore come out looking better than the truth. That is the textbook direction — and for this board, the textbooks had it backwards.

The measurement that broke the assumption came from a 10-minute autonomous research loop. It ran the previous evening and logged the result as a measurement only: no lane, constant, module, or gate default was changed.

The loop re-screened its own universe. The survivor-only reference — a single active=true snapshot of Polygon's ticker list — had been used to type every name on all 236 point-in-time dates. That reference produced a benchmark that was too low.

Readmitting every name the gate had silently excluded moved the equal-weighted screened universe from +5.64 to +6.54 bps at k=1, and from +27.34 to +29.07 bps at k=5.

Read that table twice.

equal-weighted screened universesurvivor-onlyall names readmitted
k=1+5.64 bps+6.54 bps
k=5+27.34 bps+29.07 bps

The bias did not flatter the backtest. It censored the names that made the backtest look worse. The reason is structural, not mystical: this panel never observes a delisting as a return. There is no −100% row to be spared.

Removing names did not remove disasters. It removed a type of name — and that type was exactly what the extreme-return lanes were looking for.

An AI That Audits Trading Alpha

· 18 min read
Vadim Nicolai
Senior Software Engineer

Take a statistic that cannot exist and give it a p-value that means something else. That is what one paper in the queue did: it reported Spearman rho = 0.94, p = 0.017 over five assets. On five untied ranks, rho lives on a finite grid spaced exactly 0.1 apart. The smallest two-sided p the test can produce is 0.0167, and 0.017 is the exact p-value of a perfect ranking. The nearest attainable rho, 0.90, carries p = 0.0833 — not significant at 5%. The claim is not subtly wrong; it is printed arithmetic that could not have come from the test the paper claims to have run.

The system that caught it is not another return-predicting model. It is an auditor: a loop over a local corpus of 21,305 quant-finance paper abstracts, with 21,119 still queued, 29 papers read end to end by a human, and 82 machine screens completed. Each tick claims one paper, asks a language model two questions about it, runs deterministic nulls against real market data, and records a verdict under a schema that refuses records which certify themselves. The most important thing I can tell you about this loop is not that it found fake alpha. It is that its ceiling is the corpus, not the model — and that honesty about that ceiling is the actual product.

NautilusTrader + candle: A Rust AI Trading Stack

· 19 min read
Vadim Nicolai
Senior Software Engineer

Your model says buy. Your risk engine says no. Who wins?

In most trading stacks the honest answer is "whoever is louder." A Python notebook model outshouts a config-file risk limit by default. But there is a sharper question hiding behind that one, and it changes the outcome: what happens when the model is wrong, and the architecture is built so it cannot hide?

This is the story of a two-plane algorithmic trading stack in Rust — four crates, 388 tests, zero failures — where an ML model fitted with Hugging Face's candle was given every chance to earn its place, and then lost to a momentum factor on out-of-sample data. The model did not fail because someone judged it unworthy. It failed because it could not show a number, and the thing that checked the number lived in a different process and a different dependency graph.

That is the design. Everything else in this build follows from it.

AI-First Crypto Trading Principles

· 169 min read
Vadim Nicolai
Senior Software Engineer

An optimistic backtest can show Sharpe 5 where live reality is negative, and an AI-first crypto trading system graded by it will optimise into the gap with total conviction — because inside that simulator the strategy genuinely works.

The simulator is not a test. It is the model's reward function. That is the sharpest trap I know, and the rest of this post is what I found while walking into it.

Here is the shape of what I found. Give a router a third action — quote, cross, or abstain — and it takes the third. Doing nothing scores zero; every alternative scores less; and zero wins 11 of 11 panels without the signal being consulted at all. The fitted policies that do trade pick about 1.6% of rows and still end below zero. A model that has learned to almost-not-play is not broken. It is reporting the absence of an edge — the one output no trade-count metric will ever reward.

Everything that decides whether such a system makes money on a perp lives in the coupling between model and market: fees, funding, regime, and the evidence you are willing to accept. A round trip costs 4–14 bps before the model says a word.

I went looking for that edge at sub-minute horizons and did not find it. What the search produced instead was thirty-nine principles about how to run a model against a market — each ending with the condition that breaks it, each carrying the measured number behind it, and several carrying the number that killed an earlier version of the same claim. They are worth more than the strategy would have been.

Concurrency and parallelism in LlamaIndex

· 11 min read
Vadim Nicolai
Senior Software Engineer

Most write-ups about concurrency in LlamaIndex tell you half the story. They show you how to decorate a step with num_workers=5 and call it a day. What they don't tell you is that the storage layer under those parallel steps fails in opposite ways. The default 4 workers guarantee you'll hit both failure modes eventually. I’ve timed the workflow side on llama-index-workflows 2.22.2 (PyPI release) and I’ve lived through the silent corpus inflation and lock-contention meltdowns that happen when those workers hammer the vector store. Here’s the full picture.

The Four-Component Feedback Loop That Turns a Static Agent Into a Search Problem

· 18 min read
Vadim Nicolai
Senior Software Engineer

Most AI agents you deploy today are frozen the moment they go live. You handcraft the prompts, select the tools, wire up the memory, and hope the configuration survives contact with real users. It doesn't. Tasks drift, APIs change, user intents shift – and your agent silently degrades. The conventional fix is another round of manual reconfiguration. But there's a more principled path: treat agent design not as a one-time assembly but as a continuous search problem.