An AI agent that screens stocks: strategy backtesting over MCP
Agentz agents have been able to talk about stocks since January. As of this week they can do something more interesting: run screening strategies against real market data, backtest those strategies over two years of history, and report whether the idea actually beats buying the index. This post is about that system — and about the unglamorous truth that the strategy layer sits on top of a data layer that took three times as long to get right.
Nine tenths iceberg: the data layer
Market data providers rate-limit you, gap their data, and disagree about currencies. Everything above them is built to compensate.
Caching is three tiers: an in-memory LRU (a thousand entries, 50MB cap, per-entry TTL) in front of SQLite persistence, in front of the provider API. Freshness policy is per data category — a quote goes stale in moments, fundamentals last 24 hours — and TTLs tighten while US markets are open, with stale-while-revalidate so a screener never blocks on a refresh it can serve from cache.
Rate limiting got civilised. The original provider was Alpha Vantage, whose free tier allows 5 calls a minute and 25 a day — numbers that turn a 30-stock screen into a lifestyle. EODHD, added this week, allows 100,000 calls a day at roughly 1,000 a minute; the client wraps it in a concurrency-safe promise queue with a sliding window of request timestamps, pacing to ~850 a minute so bursts never trip the cap.
Currency handling is where the sea monsters live. London-listed stocks are quoted in GBX — pence, not pounds — and 1 GBX is 0.01 GBP. Get this wrong and a portfolio is off by a factor of a hundred, which has the distinction of being both catastrophic and easy to not notice on a mixed portfolio. The rule that finally stuck: the authoritative currency comes from the exchange's own symbol list, and nothing downstream may ever overwrite it with a provider default. Valuation then converts GBX→GBP before any GBP→base-currency FX is applied, and the FX layer treats GBP/GBX/GBp as one currency family.
Gaps are treated as data, not surprises. Every price series can be
analysed into edge gaps and internal gaps; fetches proactively fill up to
five internal gaps per request, and agents get a check_data_gaps tool so
"is this data trustworthy" is a question they can ask before screening on
it. A backtest over gappy data doesn't fail — it lies, which is worse.
Three servers, one concern each
The stock tooling moved out of the Electron app into a standalone MCP server earlier this year; this week that server split into three, one process exposing three HTTP endpoints that share databases:
- Stock (port 3101): prices, charts, fundamentals, news, gap checks
- Portfolio (3102): accounts, transactions, holdings, valuation, FX
- Strategy (3103): scanners, screeners, backtesting, market regime detection
The split mirrors how agents actually use them: a portfolio question needs no screener, a screener needs no transaction history, and each server's tool list stays short enough for an LLM to choose well from.
The strategies, and what the backtest said
Four strategy families are documented: Fundamental Growth (GARP, long-horizon), Momentum Growth, Thematic Growth, and the one that got the full backtest treatment — Price Action Hunter, a short-term technical strategy with six precisely-specified setups, from Volume Spike Reversal to Failed Breakdown Recovery, each with exact entry, stop, and target rules.
The backtest engine walks day by day over the test window: check stop losses, apply tiered exits (partial profit at +1R/+2R/+3R, move to breakeven, trail, time-stop), open new entries at strategy-generated prices, record the equity curve. Out the other end come total and annualised return, Sharpe, max drawdown, win rate, profit factor, per-setup statistics, and — the number that keeps you honest — alpha against just holding SPY.
Two years, $50k starting equity, 1% risk per trade:
| Version | Universe | Setups | Return | Max DD | Win rate | vs SPY |
|---|---|---|---|---|---|---|
| v1 | 22 stocks | all 6 | +4.9% | 9.5% | 49% | −26.0% |
| v2 | 22 stocks | drop worst | +10.6% | 2.1% | 59% | −20.4% |
| v3 | 64 stocks | same | +1.9% | 10.0% | 48% | −29.0% |
| v4 | 30 curated | best 2 only | +40.8% | 1.8% | 72% | +9.8% |
(SPY buy-and-hold over the window: +30.9%.)
The journey from v1 to v4 taught more than the final number:
- Some setups never fire. The two volume-breakout-style setups — Volume Spike Reversal and Compression Breakout — produced zero trades across the whole window. Their trigger conditions (RSI under 30, tight ATR and Bollinger compression) are simply too rare in a bull-market universe of a few dozen stocks. A setup that never triggers isn't conservative; it's dead weight you can't even evaluate.
- More stocks isn't more signal. Tripling the universe (v3) made everything worse — more marginal triggers, weaker average quality. Curating back to 30 names beat both.
- Cutting losers matters more than adding winners. One setup was suspended outright after posting a 33% win rate and a five-figure hole.
- The regime filter was the single biggest improvement. Teaching the system to detect the market regime and stand down when conditions don't suit the strategy did more for the equity curve than any entry tweak.
v4's edge came from two setups — Relative Strength Explosion and Failed Breakdown Recovery — running only in favourable regimes on a curated list. Whether that survives out-of-sample is the correct next question; a two-year window ending in a bull run flatters everything, and the honest reading of the table is "v4 earned the right to be paper-traded", not "v4 is a money machine".
Where the agent comes in
All of this is reachable by the Stock Strategist specialist over MCP, which changes what the workflow feels like. The screener isn't a dashboard you remember to open — it's something the agent runs on a schedule (weekday evenings, after close), interprets against the current regime, and reports on in a portfolio brief, in chat, where you actually are. The strategy definitions live as data, so "tighten the RSI filter and re-run" is a sentence, not a code change.
The system's real lesson sits underneath all of it: the LLM was never the hard part. The hard parts were pence, rate limits, and gaps — and every hour spent there is what makes the number in the backtest table worth reading at all.