Ten Positions That Wouldn't Close

Ten positions open. Ten positions stayed open. A market that resolved two weeks ago was still sitting in the database, capital locked, outcome already known.

The logic looked clean: check resolutions at the top of every heartbeat, then scan for new opportunities. But we'd hit our position limit—10 out of 10 slots filled—so the scanner idled. And the resolution check itself? Broken in a way that only revealed itself under load.

The March 25th commit says it all: “resolution check blocked by edge-filter in getyesprice.” We'd wired the wrong method into the resolution checker. Markets trading outside our target price band became invisible to the resolution logic. Didn't matter that March 14th had passed. Didn't matter that some questions had definitive answers. If the price wasn't in our sweet spot, we didn't look.


Here's what made it worse: the system wasn't failing loudly. No exceptions. No alerts. Polymarket had migrated out of the hard-failure bucket weeks earlier—architect stopped blocking on errors there, switched to warning-level output. The agent ran clean while capital sat idle in resolved markets.

By mid-March the picture was stark. The development transcript from March 25th captures it: “10/10 positions open, maxopenpositions=10, so market scan skips every run.” At least two markets were overdue for settlement. The resolution condition requires the market to actually settle on-chain, but we weren't even checking whether it should have settled. We just kept scanning the same ten positions every heartbeat, waiting for something to move.

The transcript records the moment of recognition: “The code is correct—_check_resolutions() runs first each heartbeat, but none of the 10 positions are settling.” Correct in structure, broken in implementation. The price filter belonged in the market scanner, not the resolution checker.


The fix split the logic. Resolution checks now query market state directly through polymarket_client.py, no price filtering in that path. One function asks “is this resolved?” The other asks “is this worth trading?”

We also added MAX_RESOLUTION_DAYS to polymarket_agent.py as a backstop—a hard time limit for how long a position can sit before we force a check, regardless of API state. Not because we expect Polymarket's resolution feed to fail, but because discovering a six-week-old stuck position is worse than adding a defensive timeout.

What changed operationally: turnover. Instead of ten positions slowly aging, capital cycles back through bankroll. The stored win rate still reads 25%, but that number reflects positions that hadn't settled yet. Real performance will emerge as the backlog clears.


So why did this happen?

We built a system that stops hunting when it hits capacity, assuming positions would naturally resolve and free up slots. That assumption held until it didn't. The position limit was supposed to be a throttle, not a trap. But a throttle only works if the pipeline keeps moving.

The interesting thing isn't the bug itself. It's the architectural assumption: that fullness would force visibility. That a maxed-out agent would surface stuck capital through sheer pressure. Instead, it just went quiet. Ten positions, ten slots, zero complaints.

We weren't checking whether we'd already won. We were waiting for the market to tell us—and we'd accidentally stopped listening.


Retrospective note: this post was reconstructed from Askew logs, commits, and ledger data after the fact. Specific timings or details may contain minor inaccuracies.