polymarket-quant-trader
Professional-grade Polymarket prediction market trading system. Includes Kelly Criterion position sizing, EV calculator, Bayesian probability updater, cross-platform arbitrage detector (Polymarket vs 1WIN), and autoresearch loop that self-improves strategy overnight via Brier score optimisation. Use when: user wants to trade prediction markets, find arbitrage opportunities, build a trading bot, or improve prediction accuracy. Triggers: polymarket, prediction markets, kelly criterion, EV trading, arb detector, brier score, prediction market bot, market making, quant trading, sports betting math, cross-platform arbitrage.
安装 / 下载方式
TotalClaw CLI推荐
totalclaw install github:LeoYeAI~openclaw-master-skills~polymarket-quant-tradercURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/github%3ALeoYeAI~openclaw-master-skills~polymarket-quant-trader/file -o polymarket-quant-trader.md# Polymarket Quant Trader
A professional quant trading system for Polymarket prediction markets, built and battle-tested in production. Three alpha streams. One integrated system.
---
## Overview
This skill gives you a complete quantitative trading system for Polymarket with three independent alpha streams:
1. **EV-Based Signal Trading** — Kelly Criterion position sizing + Bayesian probability updating. Find edges, size them correctly, update beliefs as evidence arrives.
2. **Self-Improving Strategy (Autoresearch Loop)** — An autonomous hill-climbing optimizer that tunes your strategy parameters overnight using Brier score as the objective function. Wake up to a better strategy.
3. **Cross-Platform Arbitrage (PM x 1WIN)** — Detect spread discrepancies between Polymarket and 1WIN bookmaker. Fuzzy title matching, confidence tiering, Kelly-sized positions.
Each stream works independently or together. The system ships with TypeScript source, npm scripts for every workflow, and a backtester to validate before going live.
**Current production performance:** Brier score 0.18 (meaningful edge territory — baseline random is 0.25, professional is sub-0.12).
---
## Stream 1: EV-Based Signal Trading
### How It Works
The core loop: estimate a probability, compare it to the market price, calculate expected value, size the position with Kelly Criterion, and update beliefs as new evidence arrives.
### Kelly Criterion Position Sizing
Kelly answers: "Given my edge, what fraction of my bankroll should I bet?"
**The formula:**
```
f* = (p * b - q) / b
where:
f* = optimal fraction of bankroll to wager
p = probability of winning (your estimate, 0-1)
b = net odds multiplier (payout per $1 risked)
q = 1 - p (probability of losing)
```
In prediction markets, odds derive from the market price:
```
b = (1 - marketYesPrice) / marketYesPrice
```
If YES trades at $0.40, then b = 0.60/0.40 = 1.5 (you risk $0.40 to win $0.60).
**Implementation:**
```typescript
// kelly-criterion.ts
export function kelly(p: number, b: number, q?: number): number {
const qVal = q ?? 1 - p;
return (p * b - qVal) / b;
}
export function quarterKelly(p: number, b: number): number {
return 0.25 * kelly(p, b);
}
export function kellySizing(
bankroll: number,
p: number,
b: number,
mode: 'full' | 'half' | 'quarter' = 'quarter'
): number {
const fraction = mode === 'full' ? kelly(p, b)
: mode === 'half' ? 0.5 * kelly(p, b)
: quarterKelly(p, b);
return Math.max(0, bankroll * Math.min(fraction, 0.15));
}
```
**Why quarter Kelly?** Full Kelly maximizes long-run growth rate but produces brutal drawdowns (50%+ swings). Quarter Kelly captures ~75% of the growth rate with dramatically lower variance. Every serious quant fund uses fractional Kelly.
### EV Calculator
Expected value quantifies your edge per dollar risked:
```typescript
// ev-calculator.ts
export interface MarketEV {
marketId: string;
ourP: number; // Your estimated probability
marketP: number; // Market-implied probability (= YES price)
b: number; // Net odds: (1 - marketP) / marketP
ev: number; // Expected value per dollar risked
edgePct: number; // Edge as percentage of market price
kellyFraction: number; // Quarter Kelly optimal fraction
recommend: boolean; // Worth trading? (ev > 0 && edgePct >= 2%)
}
export function calcEV(ourProbability: number, marketYesPrice: number) {
const b = (1 - marketYesPrice) / marketYesPrice;
const ev = ourProbability * b - (1 - ourProbability);
const edgePct = (ev / marketYesPrice) * 100;
return { ev, edgePct, b };
}
export function scoreMarket(market: any, ourP: number): MarketEV {
const { ev, edgePct, b } = calcEV(ourP, market.yesPrice);
const kellyFraction = quarterKelly(ourP, b);
return {
marketId: market.id,
ourP,
marketP: market.yesPrice,
b,
ev,
edgePct,
kellyFraction,
recommend: ev > 0 && edgePct >= 2,
};
}
export function rankByEV(markets: MarketEV[]): MarketEV[] {
return [...markets].sort((a, b) => b.ev - a.ev);
}
```
**Reading the output:** An `edgePct` of 5% means your model thinks the market is mispriced by 5%. The `recommend` flag fires when EV is positive AND edge exceeds 2% (below that, transaction costs eat your edge).
### Bayesian Probability Updater
Update your probability estimates as new evidence arrives:
```typescript
// bayesian-updater.ts
export interface BayesianState {
marketId: string;
priorP: number;
currentP: number;
evidence: Evidence[];
lastUpdated: Date;
}
export interface Evidence {
description: string;
likelihoodRatio: number; // > 1 supports YES, < 1 supports NO
timestamp: Date;
}
export function bayesUpdate(prior: number, likelihoodRatio: number): number {
const posterior = (prior * likelihoodRatio) /
(prior * likelihoodRatio + (1 - prior));
return Math.max(0.001, Math.min(0.999, posterior));
}
export function addEvidence(
state: BayesianState,
evidence: Evidence
): BayesianState {
const newP = bayesUpdate(state.currentP, evidence.likelihoodRatio);
return {
...state,
currentP: newP,
evidence: [...state.evidence, evidence],
lastUpdated: evidence.timestamp,
};
}
export function getRecommendation(
state: BayesianState,
marketPrice: number
): { action: 'buy' | 'sell' | 'hold'; confidence: number; reason: string } {
const diff = state.currentP - marketPrice;
if (Math.abs(diff) < 0.02) return { action: 'hold', confidence: 0, reason: 'Within noise' };
if (diff > 0) return { action: 'buy', confidence: diff, reason: `Model ${(diff*100).toFixed(1)}% above market` };
return { action: 'sell', confidence: -diff, reason: `Model ${(-diff*100).toFixed(1)}% below market` };
}
```
**Likelihood ratios:** A ratio of 2.0 means "this evidence is twice as likely if YES is true." A ratio of 0.5 means "this evidence is twice as likely if NO is true." The Bayesian updater chains multiple evidence items — each update feeds the next as a new prior.
### Market Scorer (Composite Ranking)
Combines all signals into a single score for market selection:
```typescript
// market-scorer.ts — Weighted scoring model
// EV Score: 40% weight — edge percentage
// Kelly Fraction: 30% weight — optimal sizing (higher = more confident)
// Expiry Window: 20% weight — sweet spot 6-72 hours
// Volume Score: 10% weight — log-normalized liquidity
```
Markets scoring highest get traded first. The expiry window filter avoids two failure modes: too-short expiry (can't exit if wrong) and too-long expiry (capital locked up, edge decays).
---
## Stream 2: Self-Improving Strategy (Autoresearch Loop)
### The Brier Score Metric
Brier score measures prediction calibration — how close your probability estimates are to actual outcomes:
```
brierScore = mean((predictedProbability - actualOutcome)^2)
where actualOutcome = 1 if resolved YES, 0 if resolved NO
```
**Interpretation scale:**
| Score | Level | Meaning |
|-------|-------|---------|
| 0.25 | Random | Coin-flip predictions |
| 0.22 | Weak edge | Slightly better than random |
| 0.18 | Meaningful edge | Consistent alpha |
| 0.12 | Professional | Elite forecaster territory |
| < 0.10 | Superforecaster | Top 1% calibration |
Lower is better. The system tracks Brier score as the primary optimization objective.
### Strategy Configuration
The strategy is defined by tunable parameters:
```typescript
// research/strategy.ts
export interface StrategyConfig {
minVolume: number; // Minimum market volume ($)
minEdgePct: number; // Minimum edge to trade (%)
kellyMode: "full"|"half"|"quarter";
maxKellyFraction: number; // Cap on position size
expiryMinHours: number; // Earliest expiry to consider
expiryMaxHours: number; // Latest expiry to consider
}
export const DEFAULT_CONFIG: StrategyConfig = {
minVolume: 10000,
minEdgePct: 3.0,
kellyMode: "quarter",
maxKellyFraction: 0.15,
e