kronos-data / quad_resolutions.csv
40Hz's picture
quad
8fdaeba verified
Raw History Blame Contribute Delete
4.89 kB
#!/usr/bin/env python3
"""quad_resolve.py - resolve paper BUY_YES signals against real market outcomes.
Reads the append-only ledger (mode=paper, action=BUY_YES_15M_UP), fetches each
market from gamma-api, and writes quad_resolutions.csv with win/loss + ROI per
signal. This is what the OOS evaluation needs (ledger alone never resolves).
Math (standard Polymarket YES): entry price p, shares = notional/p.
win -> payout shares*$1, ROI = (1-p)/p
loss -> payout 0, ROI = -1.0
outcomes = ["Up","Down"]; outcomePrices[0] is Up (the side we buy).
Idempotent: skips markets already in the resolutions file.
"""
import csv
import json
import os
import sys
import urllib.request
from datetime import datetime, timezone
LEDGER = os.environ.get("LEDGER_PATH", "/home/neo/quad_ledger.csv")
RESOLUTIONS = os.environ.get("RESOLUTIONS_PATH", "/home/neo/quad_resolutions.csv")
FIELDS = ["ts", "coin", "market_id", "question", "yes_price", "resolve_ts",
"outcome", "win", "roi"]
def now_iso():
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
def parse_yes_price(raw):
"""Ledger stores yes_price as either a JSON array of both outcome prices
(autopilot) or a single YES price float (live-order paper mode)."""
if not raw:
return None
raw = raw.strip()
try:
arr = json.loads(raw)
if isinstance(arr, list) and arr:
return float(arr[0]) # index 0 == Up == our side
except Exception:
pass
try:
return float(raw)
except Exception:
return None
def fetch_market(mid):
url = f"https://gamma-api.polymarket.com/markets/{mid}"
req = urllib.request.Request(url, headers={"User-Agent": "quad-lab/0.1"})
with urllib.request.urlopen(req, timeout=30) as r:
return json.load(r)
def outcome_prices(m):
"""gamma-api returns outcomePrices as a JSON *string* like '["1", "0"]'."""
raw = m.get("outcomePrices")
if isinstance(raw, list):
return [str(x) for x in raw]
if isinstance(raw, str):
try:
arr = json.loads(raw)
return [str(x) for x in arr]
except Exception:
return []
return []
def load_resolved():
ids = set()
if os.path.exists(RESOLUTIONS):
with open(RESOLUTIONS) as f:
for r in csv.DictReader(f):
ids.add(r["market_id"])
return ids
def append_resolution(row):
exists = os.path.exists(RESOLUTIONS)
with open(RESOLUTIONS, "a", newline="") as f:
w = csv.DictWriter(f, fieldnames=FIELDS)
if not exists:
w.writeheader()
w.writerow({k: row.get(k, "") for k in FIELDS})
def main():
if not os.path.exists(LEDGER):
sys.exit(f"ledger not found: {LEDGER}")
rows = []
with open(LEDGER) as f:
rows = [r for r in csv.DictReader(f)]
signals = [r for r in rows if r.get("action") == "BUY_YES_15M_UP"
and r.get("mode") == "paper" and r.get("market_id")]
done = load_resolved()
pending = [r for r in signals if r.get("market_id") not in done]
print(f"signals={len(signals)} already_resolved={len(signals) - len(pending)} pending={len(pending)}")
wins = losses = unresolved = 0
total_roi = 0.0
for r in pending:
mid = r["market_id"]
try:
m = fetch_market(mid)
except Exception as e:
print(f" {mid} {r['coin']}: fetch err {e}")
unresolved += 1
continue
status = m.get("umaResolutionStatus")
prices = outcome_prices(m)
if status != "resolved" or len(prices) < 2 or m.get("closed") is not True:
print(f" {mid} {r['coin']}: not resolved yet (status={status})")
unresolved += 1
continue
p = parse_yes_price(r.get("yes_price"))
if p is None:
print(f" {mid} {r['coin']}: unparsable yes_price={r.get('yes_price')!r}")
unresolved += 1
continue
up_won = prices[0] == "1"
roi = (1 - p) / p if up_won else -1.0
row = {"ts": now_iso(), "coin": r["coin"], "market_id": mid,
"question": r.get("question", ""), "yes_price": str(p),
"resolve_ts": r.get("resolve_ts", ""),
"outcome": "UP" if up_won else "DOWN",
"win": str(up_won), "roi": f"{roi:.4f}"}
append_resolution(row)
wins += 1 if up_won else 0
losses += 0 if up_won else 1
total_roi += roi
print(f" {mid} {r['coin']}: price={p} -> {'WIN' if up_won else 'LOSS'} roi={roi:+.3f}")
if wins + losses:
n = wins + losses
print(f"\nresolved now: {wins}W/{losses}L hit={wins/n:.3f} mean_roi={total_roi/n:+.3f}")
if unresolved:
print(f"unresolved still: {unresolved}")
print("done", now_iso())
if __name__ == "__main__":
main()