fix: family_key repair, reentry guard, legacy_incomplete, trades status filter
CI/CD / build-and-push (push) Successful in 1m54s
CI/CD / build-and-push (push) Successful in 1m54s
- db: update_family_key() persists corrected family slugs for open trades - db: get_recently_closed_inverted() returns markets closed for inversion within N hours; used as reentry guard in the trading loop - db: get_recent_trades() accepts status=open|closed|None and adds a computed "status" field to every row - bot/main.py: legacy scan now computes family_key from stored question alone (dummy Market) when a position's market is no longer active — fixes NULL family_key on legacy trades like Ken Paxton (562186) - bot/main.py: legacy scan (Step 2.5) persists corrected family_keys in DB so family conflict guards work correctly on next restart - bot/main.py: positions with NULL edge_net and no live market are tagged legacy_incomplete instead of OK; counted separately in scan summary - bot/main.py: reentry_guard blocks re-entering any market closed for inversion bug within 24h; logs reentry_guard_triggered per skip - api/main.py: /api/trades now accepts ?status=open|closed|all (default open) and includes status_filter in response DB fix (applied directly): 629558 family_key politics-2026 → ohio-gubernatorial-2026; 562186 family_key NULL → texas-republican-2026 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+41
-3
@@ -112,12 +112,50 @@ class Database:
|
||||
market_id, reason,
|
||||
)
|
||||
|
||||
async def get_recent_trades(self, limit: int = 100) -> list[dict]:
|
||||
async def update_family_key(self, market_id: str, new_key: str) -> None:
|
||||
"""Persist a corrected family_key for all open trades of a market."""
|
||||
async with self._pool.acquire() as conn:
|
||||
await conn.execute(
|
||||
"UPDATE trades SET family_key = $2 WHERE market_id = $1 AND closed_at IS NULL",
|
||||
market_id, new_key,
|
||||
)
|
||||
|
||||
async def get_recently_closed_inverted(self, hours: int = 24) -> set[str]:
|
||||
"""Return market_ids closed for inversion bug within the last N hours.
|
||||
|
||||
Used as a reentry guard: prevents re-entering a market that was just
|
||||
closed because the signal direction was inverted.
|
||||
"""
|
||||
async with self._pool.acquire() as conn:
|
||||
rows = await conn.fetch("""
|
||||
SELECT DISTINCT market_id FROM trades
|
||||
WHERE closed_at > NOW() - ($1 || ' hours')::interval
|
||||
AND close_reason ILIKE '%inversion bug%'
|
||||
""", str(hours))
|
||||
return {r["market_id"] for r in rows}
|
||||
|
||||
async def get_recent_trades(self, limit: int = 100, status: Optional[str] = None) -> list[dict]:
|
||||
"""Return trades ordered by timestamp DESC.
|
||||
|
||||
status: None (all) | "open" (closed_at IS NULL) | "closed" (closed_at IS NOT NULL)
|
||||
Each row includes a computed "status" field ("open" or "closed").
|
||||
"""
|
||||
if status == "open":
|
||||
where = "WHERE closed_at IS NULL"
|
||||
elif status == "closed":
|
||||
where = "WHERE closed_at IS NOT NULL"
|
||||
else:
|
||||
where = ""
|
||||
async with self._pool.acquire() as conn:
|
||||
rows = await conn.fetch(
|
||||
"SELECT * FROM trades ORDER BY timestamp DESC LIMIT $1", limit
|
||||
f"SELECT * FROM trades {where} ORDER BY timestamp DESC LIMIT $1", limit
|
||||
)
|
||||
return [dict(r) for r in rows]
|
||||
result = []
|
||||
for r in rows:
|
||||
d = dict(r)
|
||||
d["status"] = "closed" if d.get("closed_at") else "open"
|
||||
result.append(d)
|
||||
return result
|
||||
|
||||
async def get_metrics_history(self, days: int = 42) -> list[dict]:
|
||||
async with self._pool.acquire() as conn:
|
||||
|
||||
Reference in New Issue
Block a user