We killed our trading bot
On 12 September 2026 we turned off the intraday lane of our own trading bot. It had been running since June, it looked like it was working, and it had no edge at all. This is the whole thing, including the numbers that flattered us for two months.
The thesis
The idea was ordinary enough. Our early strategy used a tight 0.35% stop-loss, and it was losing: a 33% win rate and negative expectancy. The suspicion was that the stop itself was the problem — that it kept selling dips moments before they recovered, converting temporary drawdowns into permanent losses.
So in July we widened it to 1.5%, then to 2.0%. In August we removed it entirely and let positions run to a 3% take-profit or a 48-hour time-stop. Each change was justified by a counterfactual replay: re-simulate every historical entry forward under stop widths from 0.35% to none, hold the exits constant, and compare.
The replay was unambiguous. No-stop won, and it kept winning as we added data.
What the numbers said while it ran
| Measure | No-stop era |
|---|---|
| Replay expectancy | +0.709% per trade |
| Profit factor | 1.83 |
| SQN | 2.37 |
| Live, 28 days | +0.581% per trade over 59 trades |
Those are good numbers. They are also, in hindsight, almost meaningless — because every one of them was computed on the same 2026 data the rules had been tuned on, in a market that spent the period going up.
The test that ended it
We had cached 15-minute bars for our ten symbols going back to January 2022. In September we finally ran the frozen rules over all of it: 4.7 years, no parameter fitting, entry slippage charged at the median we had measured on real fills.
Then we ran the control that matters. Same cadence, same four position slots, same exits, same entry rate — but entries drawn at random. A long-only strategy in a rising market earns from exposure alone, so the only question worth asking is whether the signal beats a coin flip with identical mechanics.
| Expectancy per trade | |
|---|---|
| Our entry rule | +0.084 |
| Random entries, same slots and exits | +0.127 |
The rule was worse than random. When we re-ran it later with 40 seeded control runs, our rule landed on the 50th percentile — z = 0.02. Not marginally better, not marginally worse. Exactly average.
And against doing nothing at all
Expectancy above zero is a low bar when the underlying stocks tripled. So we ran the benchmark we should have run first: equal-weight the ten symbols on day one and never trade again.
| Approach | $1,000 over 4.47 years |
|---|---|
| Buy and hold | $2,653 |
| Our strategy | $1,492 |
2,221 trades, a 32.9% drawdown, and $1,161 destroyed against sitting still.
Three attempts to rescue it
Before retiring it we tried to find the conditions under which it did work. All three failed:
- Volume character: we expected dips on heavy volume to revert better. The relationship ran backwards — quiet dips were the worst, not the best.
- News: 74% of dips had news within six hours, which left no variation to exploit. News presence predicted nothing about which dips recovered.
- Exit geometry: relaxing the exits walked the result monotonically toward buy-and-hold. From $1,492 at a 48-hour hold with a 3% cap, to $2,218 at a 30-day hold with no cap. Every constraint we removed helped.
That last one is the diagnosis. The strategy was a tax on holding: it sold every winner at +3% in a market that tripled, while letting the losers run to their time-stop.
What we would do differently
Run the out-of-sample test and the random-entry control first, before a single live trade. We had the historical bars the whole time. We spent two months and 2,221 simulated trades learning something a four-minute backtest would have told us in June.
The forward-running lane produced roughly 0.6 entries a day. A 30-trade verdict took weeks. The backwards test produced 2,526 trades in minutes and could see the 2022 drawdown, which no amount of waiting in 2026 would have given us.