Alpaca AI Trading Agents · Options Alpha
Every analyst carries a track record. Each one states a probability, not a verdict, and is Brier-scored against what actually happened. An analyst that is confidently wrong loses its vote. The trades themselves are built by deterministic code, so the models choose between them or refuse, and can never invent one.
The inversion
Asking a model “what should I trade?” is quick to build and impossible to trust. It can hallucinate a strike, size a position wrongly, or be confidently wrong with no record of why. So the model is handed a numbered menu it did not write, and may return one id, or the word ABSTAIN. A hallucinated id is treated as an abstention. Every decision, including each refusal, is appended to a hash-chained journal that anyone can verify without credentials.
Four stages, every one of them able to say no
Python reads the live option chain and constructs every legal defined-risk structure it supports: bull put spreads, bear call spreads, iron condors, long straddles. Each one is fully specified before any model is called: strikes, legs, quantity, limit price, max loss, breakevens. Candidates that fail the liquidity gate, or that the guard would certainly refuse, are dropped before the model ever sees them. There is no function in this codebase that produces a naked short option.
A volatility analyst weighs implied against realized vol. A dedicated adversary argues against the trade and looks for the failure mode. Each returns a probability and its reasoning. An analyst that abstains is removed from both the numerator and the denominator of the aggregate, so no opinion ever counts as neutral. In a recorded cycle the adversary's objection about a thin hedge moved the trader off the highest-credit candidate onto a safer one.
One is pure code: it checks the position's own delta against the structure's stated thesis, and fails closed when the Greeks cannot be measured. The other is a model shown the candidate and the price action but never the committee's reasoning. Two calls to the same model on the same context agree with each other and prove nothing, so decorrelation is engineered rather than assumed. Both must pass.
Every order is judged against risk.yaml: max loss per position, concurrent positions, net delta and vega, daily loss, one new trade per underlying per day. It returns allow, deny, or a smaller size than requested. Any error, any missing data, any exception inside the guard is a refusal, verified by mutation testing rather than asserted.
Who is on the desk
Quotes are verbatim from a recorded cycle, not written for this page. In that cycle the adversary's objection about a thin hedge moved the trader off the highest-credit candidate, which is the reason a committee is not a rubber stamp.
Measured over 43 replayed decisions
That is not timidity, and it is not a failure rate. Replaying the real committee over 43 post-cutoff windows, 31 ended in a refusal. Every one of them gave a reason, and the reasons are recorded in the journal whether or not they prevailed. Two examples, verbatim:
The rate did not fall when we fixed the menu: 8 of 10 before, 8 of 10 after, on matched dates. We are reporting that as measured. What changed is the reason, and the reason is the part that matters.
The question this literature keeps failing
The standing criticism of LLM trading agents is
knowledge contamination. Papers such as TradingAgents (arXiv 2412.20138)
and FinMem (arXiv 2311.13743) backtest models over dates that sit inside their own
training data, so a good result may be recall wearing the costume of analysis.
Reviewers flag Sharpe ratios above the plausible empirical range for exactly this
reason.
Our position is checkable. The model's knowledge cutoff is May 2026. Every
live committee decision runs on August 2026 market data: prices, chains and a
volatility regime that post-date the training corpus. A decision here cannot be
memorised, because there was nothing to memorise.
The converse, stated plainly: the walk-forward harness below spans 720 days that
partly predate the cutoff. That is acceptable only because it runs no model at
all. It is a deterministic proxy, measured in code. We would not put an LLM
over those dates and call the result evidence.
Out-of-sample, computed from real bars
| symbol | windows | trades | win rate | expectancy | profit factor | max drawdown |
|---|---|---|---|---|---|---|
| SPY | 13 | 8 | 87.5% | +0.62R | 3.50 | 2.0R |
| QQQ | 13 | 7 | 85.7% | +0.57R | 3.00 | 2.0R |
| AAPL | 13 | 8 | 62.5% | -0.12R | 0.83 | 6.0R |
| MSFT | 13 | 7 | 85.7% | +0.57R | 3.00 | 2.0R |
python3 scripts/replay.py --all --verify reproduces every recorded verdict offline, with the environment stripped.make verify-journal verifies the chain. Empty, intact and tampered are three distinct outcomes.make session runs the whole pipeline against the live chain and sends nothing. Submitting needs an explicit flag.risk.yaml is the single source of truth. If a number matters, it lives there and nowhere else.The fitted volatility smile per expiry is rendered at /smile, with every strike whose deviation sits inside its own bid-ask noise drawn grey and scored zero. Across the live chain only about a fifth clear that bar.
Four recorded cycles, including two refusals. Verdicts are recomputed in your browser from the committed fixtures.
Clone it and run the verifier offline with the environment stripped. It reproduces the same verdicts.
Where the audit trail stops