feat(scan): legacy position scan — re-key, Manifold re-validate, auto-close
CI/CD / build-and-push (push) Successful in 2m21s
CI/CD / build-and-push (push) Successful in 2m21s
Adds run_legacy_scan() that executes once at startup before the trading loop:
1. Re-keys every open DB position using the current market_family_key()
2. Groups by new family key; KEEP = highest edge_net, CLOSE_RECOMMENDED = sibling
3. Manifold re-query for positions whose family key changed; if corrected
probability contradicts the trade direction → CLOSE_RECOMMENDED
4. Logs full report (KEEP / REVIEW / CLOSE_RECOMMENDED) before any closures
5. In paper mode: auto-closes all CLOSE_RECOMMENDED positions
For the existing Ohio bug:
- Democrats win Ohio governor (629557): CLOSE_RECOMMENDED
family changed ohio-democrat-2026 → ohio-gubernatorial-2026
Manifold re-query confirms prob=0.05 contradicts BUY_YES (inversion bug)
$X returned to cash at break-even
- Republicans win Ohio governor (629558): KEEP
higher edge_net (0.349 > 0.247)
Infrastructure:
- schema.sql: closed_at TIMESTAMPTZ, close_reason TEXT on trades
- db.py: all open-position queries filter WHERE closed_at IS NULL
+ close_paper_position(market_id, reason)
- paper.py: close_legacy_position(market_id, reason) → float
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+15
-9
@@ -65,15 +65,11 @@ class Database:
|
||||
)
|
||||
|
||||
async def get_open_positions(self) -> dict[str, float]:
|
||||
"""Return {market_id: total_net_cost} for all trades in DB.
|
||||
|
||||
Since there is no closed flag, every trade in the DB is treated as an
|
||||
open position. After a TRUNCATE the query returns nothing, so the
|
||||
portfolio correctly resets to a full bankroll.
|
||||
"""
|
||||
"""Return {market_id: total_net_cost} for all open (not closed) trades in DB."""
|
||||
async with self._pool.acquire() as conn:
|
||||
rows = await conn.fetch(
|
||||
"SELECT market_id, SUM(net_cost) AS total FROM trades GROUP BY market_id"
|
||||
"SELECT market_id, SUM(net_cost) AS total "
|
||||
"FROM trades WHERE closed_at IS NULL GROUP BY market_id"
|
||||
)
|
||||
return {r["market_id"]: float(r["total"]) for r in rows}
|
||||
|
||||
@@ -85,7 +81,8 @@ class Database:
|
||||
"""
|
||||
async with self._pool.acquire() as conn:
|
||||
rows = await conn.fetch(
|
||||
"SELECT DISTINCT family_key FROM trades WHERE family_key IS NOT NULL"
|
||||
"SELECT DISTINCT family_key FROM trades "
|
||||
"WHERE family_key IS NOT NULL AND closed_at IS NULL"
|
||||
)
|
||||
return {r["family_key"] for r in rows if r["family_key"]}
|
||||
|
||||
@@ -101,11 +98,20 @@ class Database:
|
||||
SELECT DISTINCT ON (market_id)
|
||||
market_id, question, direction, edge_net, family_key, timestamp
|
||||
FROM trades
|
||||
WHERE paper = TRUE
|
||||
WHERE paper = TRUE AND closed_at IS NULL
|
||||
ORDER BY market_id, timestamp DESC
|
||||
""")
|
||||
return [dict(r) for r in rows]
|
||||
|
||||
async def close_paper_position(self, market_id: str, reason: str = "") -> None:
|
||||
"""Mark a paper position as closed (sets closed_at timestamp)."""
|
||||
async with self._pool.acquire() as conn:
|
||||
await conn.execute(
|
||||
"UPDATE trades SET closed_at = NOW(), close_reason = $2 "
|
||||
"WHERE market_id = $1 AND closed_at IS NULL",
|
||||
market_id, reason,
|
||||
)
|
||||
|
||||
async def get_recent_trades(self, limit: int = 100) -> list[dict]:
|
||||
async with self._pool.acquire() as conn:
|
||||
rows = await conn.fetch(
|
||||
|
||||
Reference in New Issue
Block a user