UNPKG

skills-ws

Version:

85 agent skills for AI coding assistants — marketing, growth, web3, dev, design & operations. Built for OpenClaw, Claude Code, Cursor, and Codex.

282 lines (212 loc) • 19.1 kB
--- name: polymarket-trading description: "Analyze sports prediction markets on Polymarket: scan bookmaker odds (The Odds API) for a raw shortlist, then de-vig to true probabilities to find positive-EV favorites, and optionally execute trades after confirmation. Use when the user mentions polymarket, prediction markets, sports betting, scan/place bets, NBA/football odds, positions, redeem, or edge." --- # Polymarket Sports Prediction Markets > **⚠️ FINANCIAL & LEGAL RISK DISCLAIMER — READ FIRST** > > This skill moves real money into a speculative prediction market. **Capital is at risk and you can lose 100% of any stake. There are no guaranteed payouts.** Past results never predict future outcomes, and "high-conviction favorites" lose routinely. > > - **This is not financial advice.** It is an analysis-and-execution aid only. You are solely responsible for every trade. > - **Legality varies by jurisdiction and changes fast.** Prediction-market / event-contract access may be restricted, KYC-gated, or outright illegal where you live. As of mid-2026: the international Polymarket exchange is geoblocked for US IPs (2022 CFTC settlement); a separate **Polymarket US** venue (operated by QCX LLC, a CFTC-regulated DCM) requires **full KYC** and USD settlement; and several US states have challenged or banned prediction markets (e.g. Minnesota ban effective 1 Aug 2026). Verify your own eligibility before doing anything: <https://polymarket.com> and <https://help.polymarket.com>. > - **You must comply with KYC, age limits, the platform Terms of Service, sanctions rules, and local gambling law.** Do not attempt to bypass geoblocks or KYC. > - **You are responsible for your own taxes and record-keeping** on any winnings/losses. Consult a qualified professional. > - **Practice responsible gambling.** Set a hard bankroll and loss limit, never stake money you cannot afford to lose, and stop when you hit your limit. (US: 1-800-GAMBLER.) > > Run the read-only scan freely. **Never execute a trade without explicit, per-trade user confirmation** of amount, market, outcome, token ID, chain, max price, and worst-case loss. ## ⚠️ STRATEGY RULES (Non-Negotiable) 1. **Sports markets only by default** — This skill is scoped to sports moneylines. Avoid politics, crypto, and geopolitics markets here: they are harder to price from a clean odds benchmark, can move on non-public information, and have thinner, more reflexive liquidity. This is a scoping choice, not a claim that any specific market is rigged. 2. **De-vig before you judge a favorite** — Raw `1/decimal_odds` includes the bookmaker's margin (vig) and **overstates** probability. Always normalize per bookmaker (see "De-vig math") before applying any probability threshold. Minimum **70% de-vigged consensus probability** to qualify as a favorite. 3. **Require positive expected value (+EV)** — A favorite is only a trade if the executable Polymarket price is **below** the de-vigged true probability *after* fees and slippage. Win rate is **not** EV: a true 70% favorite bought at 0.75 is **negative EV** and must be skipped. See "Edge & EV". 4. **No long shots.** Low-probability / high-payout punts are out of scope. 5. **The best trade is sometimes no trade** — If nothing clears the de-vig + +EV bar, say so. Never lower the threshold to manufacture a pick. 6. **Verify the Polymarket market exists** — Many matches are not listed. Never recommend a bet without confirming the PM market and resolving the correct outcome `token_id`. 7. **Size by bankroll and liquidity, not by conviction** — Bet size is **not** unlimited. It is capped by (a) your pre-set bankroll/loss limit, (b) order-book depth at your price (slippage), and (c) any platform/market limits. See "Position sizing". 8. **Football 3-way markets need care** — Win/Draw/Lose means a "win" outcome is priced against two alternatives. De-vig across all three outcomes per bookmaker; never compare a 2-way PM price to a non-normalized 3-way book number. ## Configuration (placeholders — never hardcode real values) Set these in your environment. **Never commit a real wallet address, private key, API key, or personal account name into a skill, repo, or prompt.** | Variable | Purpose | Example placeholder | |---|---|---| | `$ODDS_API_KEY` | The Odds API key (read-only scan) | `the-odds-api-key` | | `$POLYMARKET_WALLET` | Your Polygon address for positions lookups | `0xYourWalletAddress` | | `$POLYMARKET_PK` | Wallet private key — **execution only**, keep in a secret store | (never in plaintext) | | `$POLYMARKET_API_KEY` / `_SECRET` / `_PASSPHRASE` | CLOB API credentials — **execution only** | (secret store) | | `$KEYCHAIN_ACCOUNT` | macOS Keychain account name, if you use Keychain | `<your-keychain-account>` | - **Chain:** Polygon. **Collateral:** Polymarket migrated its exchange stack on **28 Apr 2026** to a new collateral token, **pUSD** (Polymarket USD, an ERC-20 backed 1:1 by native Circle USDC), moving off bridged `USDC.e`. Do **not** assume `USDC.e` — confirm the current collateral token, decimals, and contract for your account at <https://docs.polymarket.com>. - **Positions API (read-only):** `https://data-api.polymarket.com/positions?user=$POLYMARKET_WALLET` - **Secret storage:** `scripts/scan.mjs` reads `$ODDS_API_KEY` from the environment first, and only falls back to the macOS Keychain (`security find-generic-password -s odds-api-key -a "$(whoami)"`) for local dev. **Never** put secrets in the SKILL or in shell history. Execution keys must live in a secret manager (Keychain, 1Password CLI, Vault, env injected at runtime), never on disk in plaintext. ## Supported Sports | The Odds API Key | Sport | |---|---| | `basketball_nba` | NBA | | `soccer_epl` | English Premier League | | `soccer_spain_la_liga` | La Liga | | `soccer_italy_serie_a` | Serie A | | `soccer_germany_bundesliga` | Bundesliga | | `soccer_france_ligue_one` | Ligue 1 | | `soccer_efl_champ` | EFL Championship | ## Scripts | Script | Status | Purpose | Auth | |---|---|---|---| | `scripts/scan.mjs` | **Ships with this skill** | Scan odds → raw `1/odds` shortlist signal (NOT de-vigged) → match PM market → resolve token ID → live CLOB midpoint. De-vig + EV decision happen later in Step 2 | No (read-only) | | Query helper (e.g. `polymarket.mjs`) | **User-supplied** | Search PM markets, get price/book/spread | No | | Trade client (e.g. `trade.mjs`) | **User-supplied** | Place buy/sell orders on the CLOB | Yes | | Redeem helper (e.g. `redeem.mjs`) | **User-supplied** | Redeem resolved winning positions | Yes | Only `scripts/scan.mjs` is bundled. **The execution/query/redeem clients are NOT shipped** — they move money and must be written and audited by you against the current official SDK. A minimal buy-order template is inlined below in **Step 4: EXECUTE**; the query and redeem helpers are described (with the exact endpoints/contract calls they must make) in **Step 5: TRACK** and **Step 6: REDEEM**. Build all of them on the official client: <https://docs.polymarket.com> (Python `py-clob-client`, TypeScript `@polymarket/clob-client`). --- ## Workflow ### Step 1: SCAN — Fetch Bookmaker Odds for a Raw Shortlist (read-only) ```bash # Scan all sports node scripts/scan.mjs --all-sports # Single sport node scripts/scan.mjs --sport=basketball_nba # Custom probability threshold (applied to the raw `1/odds` shortlist signal) node scripts/scan.mjs --all-sports --min-prob=0.75 ``` `scan.mjs` does the following (read-only — no keys needed beyond `$ODDS_API_KEY`): 1. Fetches odds from The Odds API (`h2h` market, EU region, decimal format, next ~48h). 2. Computes a **raw** implied-probability signal per outcome by averaging `1/decimal_odds` across all listed bookmakers. This is **not** de-vigged — it overstates probability and is only a shortlist signal; de-vig happens in Step 2. 3. Filters to the top outcome per game above the threshold and matches it on Polymarket (Gamma `public-search`), resolving the outcome `token_id`. 4. Pulls the **live CLOB midpoint** for that token and reports `Edge = bookProb − pmPrice`. > **Important caveat about `scan.mjs`'s numbers:** the bundled script reports a *raw* averaged `1/odds` figure and a *midpoint*-based edge. Raw `1/odds` is **not** de-vigged, so it inflates probability; and the midpoint is **not** an executable fill price. Treat scan output as a **shortlist**, then redo the math in Step 2 with proper de-vig and the real order-book ask before trusting any "edge". **Validation gate:** If the scan returns no shortlist, stop. Tell the user "No qualifying bets today." Do not lower the threshold. ### Step 2: REVIEW — De-vig, then compute true Edge & EV For every shortlisted pick, recompute the math correctly before presenting it. **De-vig math (do this per bookmaker, then average):** For a game with outcomes `i`, raw implied prob `qᵢ = 1/decimalOddsᵢ`. The book's overround is `Σqᵢ > 1`. The de-vigged (fair) probability for outcome `i` from that book is: ``` pᵢ = qᵢ / Σⱼ qⱼ # normalize so probabilities sum to 1 ``` Average each outcome's `pᵢ` across all bookmakers to get the **consensus true probability** `p`. For football, sum over all **three** outcomes (Home/Draw/Away). Skipping this step is the single most common way to overstate edge. **Edge & EV (this is the decision rule):** Let `p` = de-vigged consensus probability, `P` = the **executable** Polymarket ask price (top of book you can actually fill at, in dollars, 0–1), `f` = round-trip fees/costs as a fraction. ``` edge = p − P EV per $1 staked ≈ (p / P) − 1 − f # buy YES at P, pays $1 if it resolves true ``` - **`edge > 0` and `EV > 0` → tradeable.** PM is pricing the favorite cheaper than its fair probability. - **`edge ≤ 0` (PM price ≥ true probability) → NEGATIVE expected value → DO NOT STAKE.** A negative or zero edge means you are paying *at or above* fair value; over many such bets you lose money on average. "Better payouts than a bookmaker" does **not** rescue a negative-EV price — skip it. (This corrects the old, incorrect guidance that near-zero/negative edge was "still fine".) - Always use the **ask you can fill at**, not the midpoint. After accounting for slippage and fees, a small positive midpoint "edge" frequently turns negative. **Per-pick checklist:** 1. De-vigged consensus probability `p` ≥ 70% (recomputed, not raw). 2. PM market exists and `token_id` resolved (not `N/A`). 3. `edge = p − P > 0` **and** `EV > 0` after fees/slippage. Otherwise **skip**. 4. Game has not started (kickoff > now). 5. Liquidity: order-book depth at your price supports your size without major slippage (Step "Position sizing"). 6. Football 3-way: probabilities normalized across all three outcomes. **Validation gate:** Drop any pick that is negative/zero EV, has `token_id = N/A`, has already started, or whose book depth can't support your size. ### Step 3: PRESENT — Show Picks to the User ``` 🏀 NBA Picks — <date> | Game | Pick | True Prob (de-vig) | PM Ask | Edge | EV/$1 | Kickoff | |-------------|----------------|--------------------|--------|-------|-------|-----------| | BOS vs WAS | Boston Celtics | 85.0% | 0.83 | +2.0% | +2.4% | 19:00 ET | | LAL vs DET | LA Lakers | 72.0% | 0.74 | -2.0% | SKIP | 21:30 ET | Token IDs: - Boston Celtics: 123456789... (TRADEABLE) - LA Lakers: 987654321... (SKIP — negative EV at this price) ``` Include: sport + date, the de-vigged probability (not raw), executable PM ask, edge, EV, token IDs, and caveats (football 3-way, thin liquidity). Clearly mark negative-EV picks as **SKIP**. **Then ask for explicit per-trade approval before executing anything.** ### Step 4: EXECUTE — Place Trades (user-supplied client, requires keys) There is **no bundled trade script.** Use your own audited client built on the official CLOB SDK. A minimal, safe TypeScript template (do not paste secrets inline — read them from the environment): ```ts // trade.ts — USER-SUPPLIED. Build on the official client and audit before use. // npm i @polymarket/clob-client ethers import { ClobClient, OrderType, Side } from "@polymarket/clob-client"; import { Wallet } from "ethers"; const host = "https://clob.polymarket.com"; const chainId = 137; // Polygon const signer = new Wallet(process.env.POLYMARKET_PK!); // execution key from secret store const creds = { key: process.env.POLYMARKET_API_KEY!, secret: process.env.POLYMARKET_API_SECRET!, passphrase: process.env.POLYMARKET_API_PASSPHRASE!, }; const client = new ClobClient(host, chainId, signer, creds); async function buy(tokenId: string, price: number, sizeUsd: number, dryRun = true) { // Guardrail: confirm executable ask & depth before sending. const book = await client.getOrderBook(tokenId); const bestAsk = book.asks?.length ? Number(book.asks[0].price) : NaN; if (!(price >= bestAsk)) throw new Error(`Limit ${price} below best ask ${bestAsk}; would not fill`); console.log(`worst-case loss if it resolves NO: $${sizeUsd.toFixed(2)} (full stake)`); if (dryRun) { console.log("DRY RUN — not sending", { tokenId, price, sizeUsd }); return; } const order = await client.createOrder({ tokenID: tokenId, price, // limit price you are willing to pay (0–1) side: Side.BUY, size: sizeUsd / price, // number of shares feeRateBps: 0, }); return client.postOrder(order, OrderType.GTC); } // Default to dryRun=true. Flip to false ONLY after explicit user confirmation. ``` **Validation gate:** Always run dry-run first, show the user the exact command, the executable ask, the size in shares, and the **worst-case loss = full stake**. Execute only after explicit confirmation. **Post-execution:** Show the order response. If rejected, explain why (insufficient balance, price moved, market closed, etc.). ### Step 5: TRACK — Monitor Positions (read-only) ```bash # Read-only positions via Data API curl "https://data-api.polymarket.com/positions?user=$POLYMARKET_WALLET" ``` Open orders / balances come from your CLOB client (`client.getOpenOrders()`, balance allowance checks). There is no bundled script for this — it's part of your user-supplied client. ### Step 6: REDEEM — Collect Winnings (user-supplied client, requires keys) After a market resolves, redeem winning positions via the CTF contract on Polygon (`redeemPositions`). There is **no bundled redeem script.** A correct redeem helper must: enumerate redeemable positions for `$POLYMARKET_WALLET`, handle both standard and `negRisk` markets, call the appropriate adapter/CTF `redeemPositions`, and verify the resulting collateral balance. Build it on the official SDK and current contract addresses from <https://docs.polymarket.com>; verify addresses on-chain before signing. --- ## Error Handling | Error | Cause | Fix | |---|---|---| | `security: SecItemCopyMatching` | Keychain access denied | Unlock Keychain, or set `$ODDS_API_KEY` env var directly | | `HTTP 401` from Odds API | Invalid/expired key | Verify the key; if using Keychain: `security find-generic-password -s odds-api-key -a "$KEYCHAIN_ACCOUNT" -w` | | `HTTP 429` from Odds API | Out of monthly credits | Wait; check `x-requests-remaining` header; scan fewer sports | | Token ID `N/A` | PM lacks this market | Skip — common for smaller football matches | | `No results` from PM search | Team-name mismatch | Try alternate names / search the PM UI manually | | Order rejected | Price moved or insufficient collateral | Check balance/allowance, re-check the ask, retry | | `NONCE_TOO_LOW` | Tx nonce conflict | Wait ~30s, retry | | Redeem fails | Polygon gas spike | Retry with a higher max fee; confirm contract address | ### The Odds API Quota The free tier is **500 credits/month** *(as of Jun 2026 — verify current limits and credit costs at <https://the-odds-api.com/#get-access>)*. A simple `h2h`/EU/single-region request costs ~1 credit, but extra regions/markets multiply the cost, so a `--all-sports` scan can consume several credits. To conserve: - Don't scan repeatedly within an hour. - Watch the `x-requests-remaining` response header. - Scan only the sport the user asks about when near the limit. --- ## Examples ### "Scan for bets today" ``` 1. Run: node scripts/scan.mjs --all-sports 2. Re-do de-vig + EV math on the shortlist (Step 2); drop negative-EV picks 3. Present the +EV picks table; mark SKIPs 4. Wait for explicit approval before trading ``` ### "Bet $100 on the Celtics" ``` 1. Search PM for the Celtics game (your query client) and resolve the Celtics outcome token_id 2. Get the executable ask (order book), not just midpoint 3. Pull bookmaker odds, de-vig to a consensus true probability p 4. Compute edge = p − ask and EV; if EV ≤ 0, advise SKIP and explain why 5. If +EV: present "Buy $100 on Celtics at 0.XX (ask), true prob YY%, worst-case loss $100" 6. After explicit approval: run your audited trade client (dry-run first) ``` ### "Check my positions" ``` 1. curl "https://data-api.polymarket.com/positions?user=$POLYMARKET_WALLET" 2. Show open positions with current value 3. If any are redeemable, use your audited redeem helper (Step 6) ``` ### "What are the odds on Real Madrid?" ``` 1. Scan La Liga: node scripts/scan.mjs --sport=soccer_spain_la_liga 2. Find Real Madrid; de-vig across Home/Draw/Away 3. Only call it a "pick" if de-vigged prob ≥ 70% AND PM ask gives +EV 4. Otherwise show the odds and state it's below threshold or negative-EV ``` --- ## Key Concepts - **Raw implied probability:** `1 / decimal_odds`. Includes vig — **overstates** the true chance. Never threshold on this directly. - **De-vigged (fair) probability:** raw probs normalized to sum to 1 per bookmaker, then averaged across books. This is the benchmark `p`. - **Executable price (ask):** the top-of-book price you can actually fill at on PM — use this, not the midpoint, for edge. - **Edge:** `p − ask`. **Positive** = PM cheaper than fair value (necessary condition). - **Expected value (EV):** `(p / ask) − 1 − fees`. **The decision rule.** Positive edge with positive EV after fees/slippage = tradeable; **zero or negative edge = negative EV = do not stake.** Win rate is not EV. - **Position sizing:** bet size is bounded by your pre-set bankroll/loss limit, by order-book depth at your price (to limit slippage), and by any market/platform limits — not by how confident you feel. A common discipline is fractional-Kelly capped to a small % of bankroll; never bet the bankroll on one market. - **Liquidity / slippage:** thin books move against you as you fill. Check depth before sizing; large orders walk the book and erode edge. - **This is not arbitrage:** PM generally tracks fair value closely. Genuine +EV after de-vig and fees is rare and small — and every position can still lose. Stake only within your responsible-gambling limits and only where it is legal for you.