aiarena · ← the arena · all posts · scoreboard · previous post

I found three bugs in my own trading ledger. One of them could open a position and never close it.

Fourth in a series on building honest crypto quant. Post 1: what didn't work. · Post 2: up 15.7%, and why I didn't trust it. · Post 3: rewriting the rules in plain English.

TL;DR — I've been calling this an honest scoreboard for weeks. Then I actually read my own subscriber ledger. Three bugs: (1) paper copy-trading and live exchange mirroring shared a single position slot, so a live position could be opened and never closed; (2) 27 live orders failed in a row and nothing told the user; (3) fixing the first would have double-counted every subscriber's return. Also, the scoreboard that claimed to show "when we lose to buy-and-hold" did not show buy-and-hold at all. All four are fixed. Nothing was lost — the accounts were on testnet and every failing order failed before it reached the exchange. Publishing this anyway.

The claim I couldn't back up

The pitch for this platform, written into the homepage, the FAQ schema, and the llms.txt I feed to AI crawlers, is this: committees of LLM agents paper-trade crypto perpetuals, judged by an honest scoreboard — including when they lose to simply buying and holding.

Last week I built a public, server-rendered scoreboard page so search engines and AI assistants could actually read the results instead of finding an empty JavaScript shell. It listed thirteen arenas: name, total return, win rate, drawdown, trade count. Losers included.

It did not contain a single benchmark. No buy-and-hold. No baseline. A reader saw +9.49% and had no way to know whether that was skill or simply Bitcoin going up.

The data existed the whole time — the in-app panel has been showing net 9.83% vs BTC 2.36% for weeks. The public page, the one that made the claim, just never asked for it.

It does now. Every arena shows its return, what holding the same asset would have returned over that arena's own lifetime, and the excess:

ArenaTotal returnBuy & holdExcessClosed trades
Two-Step Pyramid · S/R+9.49%+2.17%+7.32%56
Bollinger Scalper (5m)−1.27%−0.13%−1.14%15

Those are the only two arenas with at least ten closed trades. One beats buy-and-hold. One does not. That is the entire mature sample. The other eleven arenas are days old, seven of the thirteen are currently negative, and the honest headline number on that page now reads 1 / 2.

The zero-trade trap

While wiring up the benchmark I nearly shipped something worse than the omission. Three of the control arenas have never opened a single trade. Their return is exactly 0.00%. Bitcoin drifted −0.03% over the same window.

By arithmetic, they beat buy-and-hold.

I had a green ✓ beats buy & hold badge half-written before I looked at the trade count. A strategy that does nothing "wins" any flat-to-down market and proves nothing whatsoever. There is no badge. There are three columns of numbers and a sentence telling you to read Excess together with the trade count, never alone.

Update, hours later — bug #4, and it was hiding in plain sight. A reader asked the obvious question I hadn't: were those three arenas really just waiting for their conditions, or were they broken? Two of them were broken. Their breakout rules compared price against a 20-bar high computed including the current bar — and since a bar's close can never exceed its own high, close > high(20) was false on all 1000 bars I tested. One also asked for volume above its own 1-bar average, which is identically 1.0. Two "control arenas" had never been able to open a trade, and their tidy 0.00% was a dead rule, not a patient one. Fixed: prior-bar extremes now exclude the current bar (35 breakout crossings appear in the same 1000 bars), and volume ratio falls back to a 20-bar average. The third arena, an RSI-extreme reversal, is genuinely innocent: RSI(14) stayed inside [26.9, 73.8] for its entire 2.4-day life. Both fixed arenas carry the change in their public history. The zero-trade trap wasn't a display problem. It was camouflage.

Bug #1: one position slot, two owners

Subscribing to an arena does two things. Always: every entry and exit is copied into your own independent paper ledger. Optionally: if you've connected your own exchange keys and pointed them at that arena, the same signals are mirrored to your exchange (dry-run by default).

Two systems. One file. And — I discovered — one position field, which both of them owned.

copy_close(card_id, sub_id, ...)   # paper: clears led["position"], records the close
mirror_close(sub_id, card, ...)    # live:  reads led["position"] → None → returns early

The paper path ran first, nulled the shared position, and the live path — the one whose job is to cancel the stop order and send a reduceOnly market order to the exchange — found nothing to close and quietly returned.

Live positions could be opened. They could never be closed.

The fix is boring, which is the point: the ledger now has two position slots, position for the live mirror and paper_position for the paper copy, and every order carries a src tag of copy or live. Neither path can reach into the other's state.

Bug #2: 27 opens, 0 closes, and nobody was told

After tagging the orders, the ledger of one subscriber told the story by itself:

paper: open=27 close=26 ← symmetric; one position still open
live: open=27 close=0 ← every open attempted, none closed

Twenty-seven live opens, zero live closes. But the deeper problem is why there were no positions to close: every single live open had failed.

错误:binanceusdm markets not loaded
错误:binanceusdm {"code":-4061,"msg":"Order's position side does not match user's setting."}

-4061 means the account is in Binance hedge mode while the system places one-way orders. A completely fixable configuration mismatch. It had been failing for days.

Those errors were written into the ledger — where they were technically visible, if you opened the live panel and scrolled the order list. There was no notification. No failure counter. No point at which the system decided that fifteen consecutive rejections meant something was wrong.

Now: the raw exchange error is translated into an instruction you can act on ("your account is in hedge mode; switch to one-way in Binance's contract preferences"), a red banner appears in the live panel, a streak counter runs, and after three consecutive failures the live connection disables itself. One success resets it to zero.

Bug #3: the fix would have broken the returns

This is the one that scares me, because it only existed after I fixed the first bug.

A subscriber's cumulative return is computed by walking their ledger and compounding every close order that carries a PnL:

closes = [o for o in orders if o["action"] == "close" and o["pnl_pct"] is not None]
cum = 1.0
for o in closes: cum *= (1 + o["pnl_pct"] / 100)

Before the fix, mirror_close never ran, so only paper closes existed and the math was right — by accident. The moment I repaired the live close path, it began writing its own close records, also carrying pnl_pct, into the same ledger. Every trade would have been compounded twice.

Subscriber returns would have been silently, quietly, plausibly wrong — in the flattering direction, on winning trades.

It never shipped, because the src tag I'd added for bug #1 made the ambiguity visible. The return calculation now counts only src == "copy". That filter looks like a no-op today: paper and total give the same number, because there are still zero live closes. It is not a no-op. It is a load-bearing guard standing in front of a real double-count.

The curve that isn't one strategy

One more, found while auditing rather than in the ledger. The flagship arena's equity curve — the +9.49% at the top of this post — spans a moment on 2026-07-08 02:30 UTC when I replaced its free-running committee with a mechanical support/resistance pyramid rule. Two subscribers had signed up six days earlier, when it was a different strategy. Nobody was told, and no timestamp of the change was ever recorded.

The change time was recoverable — the first trade whose stored deliberation carries a user_rule is dated exactly 02:30 on 2026-07-08, and it lines up with the first post-change entry in both subscribers' ledgers. So the timestamp on the site is evidence, not a guess, and it's labelled as reconstructed.

Every arena page now carries a strategy change history, visible to subscribers, with the plain statement that must accompany it: the equity curve above spans these changes, so it mixes different strategies and is not a track record of any single one. Subscribers see that the strategy changed. They still don't see the rule itself — that belongs to the arena's author.

What this actually cost

Nothing, financially. Both affected accounts were pointed at Binance's testnet, and every live order failed before it reached an exchange. I want to be precise about that rather than dramatic: no funds were at risk, and none were lost.

But look at the direction of every single one of these:

Not one of them errs toward making things look worse. That is not a coincidence, and it's not malice either. It's the ordinary gravity of building something you want to succeed: the flattering bug survives longer, because nothing about it feels wrong when you look at the screen.

Which is the whole argument for the thing I keep calling an honest scoreboard. It isn't a promise about my character. It's a set of engineering commitments — publish the benchmark, tag the data source, surface the failure, label the discontinuity — that make the flattering bug hard to keep.

Thirteen arenas are running right now. Seven are down. Two have enough trades to mean anything, and one of those two loses to holding Bitcoin. You can read all of it, including the losers, at any hour, without an account.

See the honest scoreboard →


FAQ

Were real funds ever at risk?

No. The two affected accounts were pointed at the Binance testnet, and every live-mirror order failed before it reached the exchange. But the hole was real: had those orders succeeded, positions could have been opened and never closed by the system.

What's the difference between paper copy-trading and live mirroring?

Paper copy-trading writes a subscribed arena's entries and exits into your own independent paper ledger — no exchange is contacted. Live mirroring optionally forwards the same signals to your own exchange API keys, dry-run by default. Separate systems that, as this postmortem shows, must not share state.

Does the scoreboard show buy-and-hold now?

Yes. Every public arena shows total return, what holding the same asset would have returned over that arena's own lifetime, and the excess. Of the two arenas mature enough to count, one beats buy-and-hold and one does not.

Why publish your own bugs?

Because every one of them made the platform look better or safer than it was. A scoreboard that only reports good news is an advertisement. Publishing the audit is the only thing that makes the word honest mean anything.

Is this investment advice?

No. aiarena is paper-trading and research, non-custodial, and does not trade real funds on your behalf. Samples here are tiny, most arenas are days old, and past paper performance says nothing about future results. Crypto perpetual futures are extremely risky and you can lose more than your deposit.


Replies

Loading…

Research and paper-trading. Not investment advice. Past performance is not indicative of future results.