Spaces:
Runtime error
Download README.md from Noaleetz/worldcup-predictor-hack: direct link, hf CLI and curl.
- Browser
- Download file 13.4 kB
-
https://huggingface.co/spaces/Noaleetz/worldcup-predictor-hack/resolve/main/README.md
- Command line
-
hf download hf://spaces/Noaleetz/worldcup-predictor-hack/README.md
-
curl -L -o README.md https://huggingface.co/spaces/Noaleetz/worldcup-predictor-hack/resolve/main/README.md
title: World Cup Predictions
emoji: ⚽
colorFrom: green
colorTo: blue
sdk: docker
app_port: 7860
pinned: false
World Cup Prediction Agent
https://huggingface.co/spaces/Noaleetz/worldcup-predictor-hack
A self-improving match prediction agent for the 2026 FIFA World Cup. Before each match it autonomously purchases real-time sports data via x402 micropayments, reasons over that data with Gemini, records the result afterward, and updates its own strategy document so future predictions get better.
Live dashboard: deployed on Hugging Face Spaces (Docker).
How it works
Each match goes through two automated phases:
1. Predict (predict.py) — runs 30 minutes before kickoff
python predict.py HOME AWAY [KICKOFF_UTC]
# e.g. python predict.py NED JPN
# optional kickoff override: python predict.py NED JPN 2026-06-14T20:00Z
Kickoff is read from schedule.json by default; pass KICKOFF_UTC only for retroactive re-runs.
Stage 1 — Analyst: Gemini lists 8–12 specific questions grouped by category (FORM, PLAYERS, TACTICS, H2H, CONTEXT, MARKET, NEWS, VENUE). No data services are shown yet — this is pure information-needs planning.
Stage 2 — Shopper: A second Gemini call receives those needs, the current strategy, and the full x402 service directory. It maps each need to concrete endpoints and prices, and flags any research gaps (needs with no available service).
Baseline searches (always run): Six mandatory Tavily/Brave search calls cover match preview, squad news, both teams' form, head-to-head, and group context. They cost $0.056 (2× Tavily @ $0.012 + 4× Brave @ $0.008) and are always included in the plan first; the shopper stages additional calls against whatever remains of the per-match budget.
Data fetch: The agent calls each planned endpoint through an x402-aware requests session. When a server responds with HTTP 402, the client automatically signs and submits an on-chain USDC micropayment on Base, then retries. Responses are cleaned before being passed to the predictor — skim402 nav junk is stripped, and Tavily/Brave/Swerver JSON is condensed to readable snippets.
Fallback research: If most calls fail, return low-quality data, or no search results were obtained, a third Gemini call plans up to five alternative endpoints (preferring Tavily/Brave) within the remaining budget.
Stage 3 — Synthesizer: Gemini extracts structured evidence from purchased data plus free context — past learnings, group-stage neighbors, and ESPN tournament results — and flags data quality, contradictions, and key factors.
Stage 4 — Stress-test: A separate Gemini call challenges the synthesis: blind spots, underweighted factors, draw risk, and a confidence_cap (1–10) based on evidence gaps.
Stage 5 — Predictor: A final Gemini call produces calibrated win probabilities and a pick:
HOME_PCT/DRAW_PCT/AWAY_PCT: integers summing to 100PICK: home / away / drawCONFIDENCE: 1–10 (respects the stress-test cap — not defaulted to 5)CONFIDENCE_REASON: one sentence explaining the confidence levelREASONING: one-sentence justification for the pick
There is no bet sizing — the agent predicts outcomes only, not stake amounts. The dashboard shows pick, win probabilities, confidence (with hover tooltip for the reason), and research cost. Older entries in results.json may still have a legacy bet field from earlier runs.
Results are saved to results.json, a Discord notification is sent, and the commit is pushed to GitHub and the HF Space.
2. Reflect (reflect.py) — runs 2.5 hours after kickoff
python reflect.py HOME AWAY
# e.g. python reflect.py NED JPN
Result fetch: Polls the ESPN public scoreboard API to get the final score. Retries over the last 3 days to handle timezone offsets.
Evaluator: An independent Gemini call receives the prediction, actual result, and purchased endpoints. It writes a terse post-match note (≤120 words): what went wrong (if applicable), which data sources were useful vs useless, and no filler or markdown formatting.
Reflector: A second Gemini call rewrites strategy.md with exactly one new rule, keeping still-valid existing rules. The document has two sections — Rules (one sentence per match-informed rule) and Data sources (use: / skip: entries tagged with the match). Max 250 words.
Commit: Updates results.json and strategy.md, pushes to GitHub, pushes to the HF Space (so the live dashboard updates), and sends a Discord outcome notification.
Research pipeline detail
x402 data sources
The agent pulls from two layers:
Service directory — fetched live from x402-list.com, filtered to online services on the configured network (Base mainnet or Sepolia). OpenAPI specs are fetched per service to discover real endpoint paths and prices.
Supplemental services — verified working endpoints not yet in the directory:
| Service | Endpoint | Cost | Use |
|---|---|---|---|
| SignalFuse Tavily | POST /v1/gateway/search/tavily |
$0.012 | AI-ranked news/previews, injury reports |
| SignalFuse Brave | GET /v1/gateway/search/brave |
$0.008 | Structured web search |
| Swerver Search | POST /search |
$0.010 | Fast headless browser search |
| Swerver Scrape | POST /scrape |
$0.010 | Scrape any URL when skim402 fails |
| Skim402 | GET /api/v2/read?url=... |
~$0.002 | Read verified team/tournament pages |
| GDELT | GET /news/recent?topic=... |
varies | Recent news by topic |
Verified team URLs
For skim402 page reads, predict.py maintains a lookup table (TEAM_DATA) of empirically verified URLs per team — Transfermarkt squad/results pages, national-football-teams.com, Soccerway, and Guardian team pages. Sites known to fail (Wikipedia, FBref, ESPN, etc.) are excluded.
Budget allocation
MATCH_RESEARCH_BUDGET defaults to $0.50 per match (set in .env). All spending — baseline, agent-planned, and fallback — must stay within that cap.
MATCH_RESEARCH_BUDGET ($0.50 default)
├── Baseline searches ($0.056 — always merged first)
├── Agent-chosen calls (Skim402, GDELT, extra Tavily/Brave, etc.)
│ └── Shopper plans against ~$0.44 remaining after baseline
└── Fallback calls (if data quality is poor)
└── Uses whatever is left: BUDGET − total_spent so far
Wallet checks: predict.py aborts if on-chain USDC balance is below $0.10. Recommended starting balance is $2.00 (~4 matches at default budget). Actual spend per match varies — baseline alone is $0.056; with agent and fallback calls, recent matches typically land around $0.08–$0.12.
Self-improving strategy
strategy.md is the agent's accumulated knowledge — a terse document (≤250 words) with Rules and Data sources sections. It grows one rule at a time, each tagged with the match that informed it. The predictor reads the full strategy before every match; the reflector updates it after every result.
The Data sources section tracks which x402 endpoints have proven useful vs not across past matches, so the agent learns to allocate its budget more efficiently over time.
To regenerate terse evaluations and rebuild strategy from scratch for all past reflections:
python backfill_learnings.py
x402 payment system
The agent pays for data using USDC on Base (mainnet or testnet). Key pieces:
| File | Role |
|---|---|
setup_wallet.py |
One-time keypair generation; prints .env values to add |
wallet.py |
Balance check via Web3 + USDC contract; x402 session factory |
Balance check: At startup, predict.py reads the on-chain USDC balance by calling balanceOf on the USDC contract. If it's below $0.10, the run aborts.
x402 session: wallet.get_x402_client() returns a requests.Session wired with ExactEvmScheme for the configured chain. Any request that returns 402 Payment Required is handled automatically: the client signs a payment transaction with the wallet's private key, submits it on-chain, and retries — all transparent to the calling code. Both GET and POST endpoints are supported.
Cost tracking: research_cost, wallet_before, and wallet_after are saved per match in results.json. wallet_after is an estimate (balance − total_spent); the on-chain balance is not re-fetched mid-run.
Web dashboard (app.py)
FastAPI app deployed on HF Spaces. Renders HTML directly (inline templates, no Jinja2). Routes:
| Route | Content |
|---|---|
/ |
Pipeline dashboard: awaiting prediction → predicted → awaiting reflection → reflected, with win/loss record, pick + win probabilities + confidence, x402 spend, strategy snippet |
/match/HOME_AWAY |
Full detail: analyst needs, purchased endpoints with raw data, research gaps, win model, full Gemini reasoning, evaluation, strategy at prediction time |
/strategy |
Current strategy (rendered markdown) + snapshot history across all matches |
/learnings |
Strategy evolution per reflection — line diffs and terse post-match evaluations |
/api/results |
Raw results.json as JSON |
/api/schedule |
Raw schedule.json as JSON |
Research gaps (intelligence needs with no matching x402 service) are surfaced as a demand signal for the x402 ecosystem.
Setup
1. Dependencies
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
2. Environment variables
Create a .env file:
# AI
GEMINI_API_KEY=...
# Wallet (generate with setup_wallet.py)
WALLET_ADDRESS=0x...
WALLET_PRIVATE_KEY=0x...
# Network: base-mainnet (real USDC) or base-sepolia (testnet)
NETWORK=base-mainnet
# Budget per match in USD (default 0.50)
MATCH_RESEARCH_BUDGET=0.50
# Optional: Discord bot for notifications
DISCORD_BOT_TOKEN=...
3. Wallet setup
# Generate a new keypair and print .env values
python setup_wallet.py
# For testnet: set NETWORK=base-sepolia, then fund from faucets:
# ETH (gas): coinbase.com/faucets/base-ethereum-goerli-faucet
# USDC: faucet.circle.com (select Base Sepolia)
# For mainnet: transfer USDC to the printed address on Base
# Recommended minimum: $2.00
Scheduling
Matches are scheduled automatically with cron.
# Fetch all WC 2026 fixtures from ESPN and schedule upcoming matches
python setup_schedule.py
# Preview without writing cron entries
python setup_schedule.py --dry-run
# Schedule a single match manually
python schedule_match.py NED JPN 2026-06-14T20:00:00Z
setup_schedule.py fetches fixtures from the ESPN public API, writes schedule.json, adds cron entries for all upcoming matches, and pushes the schedule to GitHub. Predict runs 30 minutes before kickoff; reflect runs 2 hours 30 minutes after kickoff. Logs go to logs/HOME_AWAY.log.
Manual run
# Predict (kickoff read from schedule.json; pass timestamp for retroactive mode)
python predict.py NED JPN
python predict.py NED JPN 2026-06-14T20:00Z # retroactive override
# Reflect (after the match is complete)
python reflect.py NED JPN
# Run the dashboard locally
uvicorn app:app --reload --port 7860
Retroactive mode is automatically detected when the kickoff timestamp is in the past, and constrains research to pre-match sources only (no live scoreboards or post-match reports).
File structure
predict.py # Pre-match: analyst → shopper → fetch → fallback → synthesize → stress-test → predict
prediction_intel.py # Stages 3–5: evidence synthesis, stress-test, calibrated prediction + parsing
reflect.py # Post-match: result + terse evaluation + strategy update
backfill_learnings.py # Regenerate evaluations and rebuild strategy for past reflections
app.py # FastAPI web dashboard (inline HTML)
wallet.py # USDC balance check + x402 payment session
gemini.py # Gemini REST API wrapper
discord_notify.py # Discord bot notifications
setup_wallet.py # One-time wallet keypair generation
schedule_match.py # Add cron entries for a single match
setup_schedule.py # Fetch ESPN fixtures + bulk-load cron + push schedule.json
make_thumbnail.py # Generate HF Space thumbnail (1280×720)
results.json # All predictions and outcomes (includes probabilities, synthesis, stress_test)
strategy.md # Agent's accumulated strategy (Rules + Data sources, rewritten each match)
schedule.json # Fixture list with kickoff times (from ESPN)
logs/ # Per-match cron output
Dependencies
- Gemini Flash — multi-stage reasoning (analyst → shopper → synthesizer → stress-test → predictor; separate evaluator + reflector)
- x402 — HTTP 402 micropayment protocol for real-time sports data and search APIs
- Web3 / eth-account — Base chain interaction for balance checks and payment signing
- FastAPI / uvicorn — web dashboard
- markdown — server-side strategy rendering
- ESPN public API — free fixture list, tournament form, and post-match result fetching
- Pillow — HF Space thumbnail generation