fix(resolution): cast $3 explicitly in close_paper_position and persist before mutating portfolio
CI/CD / build-and-push (push) Successful in 8s
CI/CD / build-and-push (push) Successful in 8s
Cycle-10 resolution check found market 562186 resolved (Paxton YES) but the close failed with asyncpg AmbiguousParameterError: Postgres cannot infer the type of a bare '$3 IS NOT NULL' in the close_pnl CASE. Reproduced via PREPARE in the postgres pod; fixed by casting every $3 use to double precision. The failed DB write also left memory/DB diverged: close_position() popped the position and credited cash before persisting, so the retry at cycle 20 skipped the market (pnl=n/a) while the DB row stayed open. Now the DB write happens first and memory mutates only on success; check_resolutions() also isolates per-market close failures so one error doesn't abort the cycle. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
e137116e7f
commit
5aa54eb423
@@ -103,3 +103,28 @@ def test_unknown_market_returns_none():
|
||||
ex = PaperExecutor(db=FakeDB({}), bankroll=1000.0)
|
||||
return await ex.close_position("nope", 1.0)
|
||||
assert asyncio.run(run()) is None
|
||||
|
||||
|
||||
def test_db_failure_keeps_position_for_retry():
|
||||
"""Regression: a DB error during close must not mutate the in-memory
|
||||
portfolio — otherwise the next resolution check skips the market
|
||||
(not in positions) and the DB row stays open forever."""
|
||||
|
||||
class FailingDB(FakeDB):
|
||||
async def close_paper_position(self, market_id, reason="", resolution=None):
|
||||
raise RuntimeError("db down")
|
||||
|
||||
async def run():
|
||||
db = FailingDB({
|
||||
"mkt1": [{"direction": "BUY_YES", "shares": 200.0, "net_cost": 102.0}],
|
||||
})
|
||||
ex = PaperExecutor(db=db, bankroll=1000.0)
|
||||
ex._portfolio.cash = 898.0
|
||||
ex._portfolio.positions["mkt1"] = 100.0
|
||||
with pytest.raises(RuntimeError):
|
||||
await ex.close_position("mkt1", 1.0)
|
||||
return ex
|
||||
|
||||
ex = asyncio.run(run())
|
||||
assert ex._portfolio.positions == {"mkt1": 100.0} # still open in memory
|
||||
assert ex._portfolio.cash == pytest.approx(898.0) # payout not credited
|
||||
|
||||
Reference in New Issue
Block a user