Skip to content

Repository files navigation

What Is This Project?

Polywinner is a toolkit for analyzing, learning from, and potentially replicating the trading strategies of successful traders on Polymarket (a prediction market platform).

The core insight: Some traders consistently make money through a technique called "Gabagool" (hedging arbitrage) — buying BOTH sides of a prediction market (YES and NO) when the combined cost is less than $1.00. Since one side must pay out $1.00 at resolution, this guarantees profit.

Your goal: Build an ML model that learns WHEN these successful traders choose to trade, so you can eventually automate similar decisions.


The High-Level Pipeline

YOUR ML DATA PIPELINE

STEP 1: EXTRACT TRADE DATA pmx extract --user @wallet --out ./data/wallet

  • Pulls historical trades from Polymarket's APIs
  • Stores: trades, positions, market metadata (activity dataset is opt-in via --with-activity)
  • Prefer windowed runs (--start/--end) for large wallets; omit dates only when a full-history pull is intentional.
  • Default trade mode is strict (single-market fetches; most reliable)
  • Optional alternative: --trade-mode packed (volume-target chunking; uses cached per-market trade counts and activity-sliced preseed on cold windows; can be disabled via --no-trade-activity-preseed)
  • Optional verification: --verify-trades to sample-check and auto-promote to strict on gaps
  • For windowed runs, use --condition-enum-strategy goldsky-orderbook for fuller market coverage
  • Tables are Parquet by default; JSON tables are written only with --emit-json.
  • Raw trades are compressed by default (raw/trades.ndjson.zst); keep uncompressed raw with --keep-raw-uncompressed.
  • Auto-compact is enabled for >=2h windows; disable with --no-compact-after or adjust with --compact-after-hours.

STEP 1B: MERGE WINDOWED EXTRACTS (optional but recommended for ML) pmx merge --out ./data/merged ./data/window-*

  • Merges multiple windowed extracts into a single, ordered dataset (raw trades + recomputed derived tables)
  • Writes new output directory; never mutates inputs
  • Add --verify to validate sortedness, dedupe, and parity
  • Merge accepts raw/trades.ndjson, raw/trades.ndjson.zst, or raw/trades.ndjson.gz.
  • Runbook: docs/merge/merge-runbook.md

STEP 2: DISCOVER THE BEST TRADERS pmx discover:gabagool --data ./data --out ./wallets.json

  • Analyzes all extracted wallets
  • Ranks them by "Gabagool Score" (cost efficiency, volume, profit, timing)
  • Outputs top N wallets + their market IDs

STEP 3: FETCH ORDERBOOK HISTORY pmx dome:backfill --all --data ./data --out ./clob --trade-bounded --resume

  • Uses Dome API to get historical orderbook snapshots (1 QPS, slow but resumable)
  • Defaults to L1-only (best bid/ask, mid, spread, depth1). Use --full-book to persist full depth.
  • Use --max-runtime-minutes for short validation slices that still write checkpoints + reports.
  • This is the "market context" when traders made decisions
  • Decoupled from extraction so you can run it later or in the background
  • Coverage check: node scripts/report-clob-coverage.js --trades ./data --clob ./clob --report ./data/clob-coverage.json

STEP 4: GENERATE ML DATASET pmx ml:generate-dataset --trades ./data --clob ./clob --out ./dataset

  • Aligns trades with orderbook state at trade time
  • Extracts 50 total features per decision point (49 numeric + 1 categorical)
  • Default labels: EPISODE_ACTIVE, NO_TRADE (optional --label-mode 3class for BUY_YES/BUY_NO/NO_TRADE)
  • Splits into train/val/test (chronological, no data leakage)
  • Writes Parquet part files by default (use --output-format ndjson for NDJSON)

STEP 5: TRAIN MODEL (Python) python3 python/train_baseline.py ./dataset

  • Loads the generated dataset
  • Trains classifier (RandomForest, etc.)
  • Evaluates on held-out test data

Key Concepts

The "Gabagool" Strategy

On Polymarket, you can buy YES or NO shares. If the event resolves YES, YES shares pay $1.00. If NO, NO shares pay $1.00.

The arbitrage: If you can buy YES at $0.48 and NO at $0.48, you pay $0.96 total. When the market resolves, you get $1.00 back no matter what. That's a guaranteed $0.04 profit (4.2% return).

The best practitioners:

  • Find markets where combined cost < $1.00
  • Trade in the final minutes before resolution (when they're most confident)
  • Execute quickly on both sides

The 50 ML Features (49 Numeric + 1 Categorical)

Category What It Captures
Lifecycle (6) Time to resolution features + calendar timing
Prices (9) Best bid/ask, midprice, spread for YES and NO
Depth L1 (6) Top-of-book liquidity
Depth L_n (10) Multi-level depth, VWAP, and imbalance
Arb Geometry (6) Combined cost, edge, pressure between YES/NO books
Dynamics (5) Recent trade volume, momentum, time since last trade
Inventory (8) Wallet's current position, P&L, cost basis

The Labels

  • Default (episode mode): EPISODE_ACTIVE, NO_TRADE
  • Optional (3class mode): BUY_YES, BUY_NO, NO_TRADE

ML Model Details (Baseline)

What the model predicts

  • Default: a binary classifier for each decision point: EPISODE_ACTIVE (1), NO_TRADE (0)
  • Optional: a 3-class action classifier: BUY_YES (0), BUY_NO (1), NO_TRADE (2)
  • Each row is a trade-aligned snapshot with 50 total features in TS; the Python baseline loads the 49 numeric features (resolutionBucket is categorical and not used unless you encode it).

How training works

  • Data loading: python/load_dataset.py uses pandas to load train/val/test splits
  • Feature prep: numeric columns only, coerced to floats, NaNs filled
  • Scaling: StandardScaler (fit on train, applied to val/test)
  • Class imbalance: class weights computed from training labels (balanced)
  • Metrics: accuracy + macro precision/recall/F1, per-class metrics, confusion matrix

Algorithms used right now

  • RandomForestClassifier (scikit-learn) [default]
    • n_estimators=200, max_depth=15, min_samples_leaf=10, min_samples_split=20
    • class_weight=balanced, n_jobs=-1, random_state=42
  • LogisticRegression (scikit-learn) [optional]
    • class_weight=balanced, max_iter=500, n_jobs=-1, random_state=42

Artifacts

  • Saved to <dataset_dir>/artifacts/:
    • model.pkl (model + scaler + model_type)
    • feature_list.json
    • metrics.json

Libraries in use

  • pandas, numpy (data handling)
  • scikit-learn (models, scaling, metrics)
  • pyarrow (optional: Parquet read/write support)
  • apache-arrow, parquet-wasm (TS Parquet output)

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages