fix(critical): remove dead manifold.get_probability() from legacy scan and fix BUY_NO payout calculation
CI/CD / build-and-push (push) Successful in 29s

- Legacy scan called ManifoldClient.get_probability(), removed in the v3
  matcher migration, causing AttributeError when positions had changed
  family keys. The block used Manifold to escalate positions to
  CLOSE_RECOMMENDED (inversion detection) — a trading decision forbidden
  under MANIFOLD_SIGNAL_ENABLED=false — so the dependency is removed
  entirely; the scan keeps family re-keying and sibling-conflict logic.

- PaperExecutor.close_position() computed cash += position_cost * resolution,
  ignoring direction: a winning BUY_NO (resolution=0.0) paid out $0 and
  reported a loss. Now settles per trade:
    BUY_YES: payout = shares * resolution
    BUY_NO:  payout = shares * (1 - resolution)
  with pnl = payout - net_cost; Telegram win/loss keys off pnl > 0.
  Adds read-only Database.get_open_trades_for_market().

- tests/test_paper_close.py covers the 4 deterministic payout cases;
  tests/conftest.py shims datetime.UTC for local Python 3.10 (prod is 3.11).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
chemavx
2026-06-11 12:23:44 +00:00
co-authored by Claude Fable 5
parent 3a353c7e5b
commit 340c8523cf
5 changed files with 172 additions and 43 deletions
+9 -34
View File
@@ -223,14 +223,17 @@ async def run_trading_loop(
async def run_legacy_scan(
db: Database,
markets: list,
manifold: ManifoldClient,
executor: PaperExecutor,
paper_mode: bool,
) -> None:
"""
One-time startup scan: re-key all open DB positions with the current
market_family_key() logic, detect contradictions, re-validate Manifold
signals, and report KEEP / REVIEW / CLOSE_RECOMMENDED per position.
market_family_key() logic, detect family conflicts, and report
KEEP / REVIEW / CLOSE_RECOMMENDED per position.
Manifold is intentionally not consulted here: with
MANIFOLD_SIGNAL_ENABLED=false it is observational-only and must not
drive position closures.
In paper_mode: auto-closes all CLOSE_RECOMMENDED positions after logging.
"""
@@ -269,8 +272,6 @@ async def run_legacy_scan(
"family_key_old": old_fk,
"family_key_new": new_fk,
"fk_changed": new_fk != old_fk,
"manifold_prob_new": None,
"manifold_inverted": False,
"recommendation": "legacy_incomplete" if is_legacy_incomplete else "OK",
"rec_reason": "edge_net and live market unavailable" if is_legacy_incomplete else "no family conflict",
})
@@ -308,31 +309,7 @@ async def run_legacy_scan(
p["market_id"], p["family_key_old"] or "none", p["family_key_new"],
)
# Step 3: Manifold re-query for positions whose family key changed
for p in enriched:
if p["live_market"] and p["fk_changed"]:
prob = await manifold.get_probability(p["question"])
p["manifold_prob_new"] = prob
if prob is not None:
# Detect if original trade direction conflicts with corrected Manifold signal
if prob < 0.40 and p["direction"] == "BUY_YES":
p["manifold_inverted"] = True
note = f"Manifold:{prob:.3f} contradicts BUY_YES (inversion bug confirmed)"
if p["recommendation"] in ("OK", "REVIEW"):
p["recommendation"] = "CLOSE_RECOMMENDED"
p["rec_reason"] = note
else:
p["rec_reason"] += f" | {note}"
elif prob > 0.60 and p["direction"] == "BUY_NO":
p["manifold_inverted"] = True
note = f"Manifold:{prob:.3f} contradicts BUY_NO (inversion bug confirmed)"
if p["recommendation"] in ("OK", "REVIEW"):
p["recommendation"] = "CLOSE_RECOMMENDED"
p["rec_reason"] = note
else:
p["rec_reason"] += f" | {note}"
# Step 4: log the full scan report (before any closures)
# Step 3: log the full scan report (before any closures)
n_close = sum(1 for p in enriched if p["recommendation"] == "CLOSE_RECOMMENDED")
n_keep = sum(1 for p in enriched if p["recommendation"] == "KEEP")
n_ok = sum(1 for p in enriched if p["recommendation"] == "OK")
@@ -348,7 +325,6 @@ async def run_legacy_scan(
" [%-18s] market=%-8s | dir=%-8s | edge_net=%+.3f\n"
" stored_family: %s\n"
" new_family: %s%s\n"
" manifold_new: %s\n"
" reason: %s",
p["recommendation"],
p["market_id"], p["direction"],
@@ -356,12 +332,11 @@ async def run_legacy_scan(
p["family_key_old"] or "none",
p["family_key_new"],
" [CHANGED]" if p["fk_changed"] else "",
f"{p['manifold_prob_new']:.3f}" if p["manifold_prob_new"] is not None else "n/a",
p["rec_reason"],
)
log.warning("" * 70)
# Step 5: auto-close in paper mode
# Step 4: auto-close in paper mode
if paper_mode and n_close > 0 and isinstance(executor, PaperExecutor):
log.warning("PAPER MODE: auto-closing %d CLOSE_RECOMMENDED position(s)...", n_close)
for p in enriched:
@@ -414,7 +389,7 @@ async def main() -> None:
except Exception as e:
log.warning("Could not fetch markets for legacy scan: %s — scan skipped", e)
scan_markets = []
await run_legacy_scan(db, scan_markets, manifold, executor, PAPER_MODE)
await run_legacy_scan(db, scan_markets, executor, PAPER_MODE)
try:
await run_trading_loop(poly, external, strategy, risk, executor, metrics, db)