Scan multi-outcome Polymarket markets for mathematical arbitrage. When the sum of all outcome prices falls below $1.00, buy all sides simultaneously via batc...
---
name: polymarket-negrisk-arb
description: Scan multi-outcome Polymarket markets for mathematical arbitrage. When the sum of all outcome prices falls below $1.00, buy all sides simultaneously via batch trade to lock in risk-free profit. No signals. No AI. Just math.
metadata:
author: "r2creatorstudios"
version: "1.0.2"
displayName: "Polymarket NegRisk Arbitrage"
difficulty: "intermediate"
---
# Polymarket NegRisk Arbitrage Trader
> **This is a template.** The default signal is NegRisk price sum deviation from $1.00 —
> remix it with tighter thresholds, different min-profit floors, or additional liquidity filters.
> The skill handles all the plumbing (market discovery, grouping, batch execution, safeguards).
> Your agent provides the edge by tuning when and how aggressively to enter.
Scan multi-outcome markets for mathematical arbitrage opportunities. When the sum of all outcome prices deviates from $1.00, buy the mispriced side across all buckets simultaneously using Simmer's batch trade endpoint.
No signals. No AI. Just math.
## Strategy
Polymarket NegRisk markets have multiple mutually exclusive outcomes (e.g., election candidates, tweet count ranges). Exactly one outcome resolves YES = $1.00.
**The Math:**
In an efficient market:
```
sum(all YES prices) = $1.00
sum(all NO prices) = $1.00
```
When markets are inefficient:
```
sum(YES) < $0.95 → Buy ALL YES → guaranteed $1.00 payout, cost < $0.95 → profit
sum(NO) < $0.95 → Buy ALL NO → guaranteed $1.00 payout, cost < $0.95 → profit
```
**Example:**
```
Election market — 4 candidates:
Candidate A: YES = $0.45
Candidate B: YES = $0.22
Candidate C: YES = $0.18
Candidate D: YES = $0.08
Sum = $0.93 (< $0.95 threshold)
→ Buy ALL YES for $0.93 total
→ One candidate wins → collect $1.00
→ Profit: $0.07 (7.5% return, risk-free)
```
## When to Use This Skill
- Multi-outcome Polymarket events (elections, tweet counts, sports brackets)
- When market makers are slow to rebalance after news
- High-volatility periods where prices shift quickly
- Events with many outcomes (more outcomes = more pricing errors)
## Setup
```bash
pip install simmer-sdk
export SIMMER_API_KEY="your-key-here"
```
## Quick Start
```bash
# Scan for opportunities (dry run, no trades)
python negrisk_arb.py
# Execute real trades
python negrisk_arb.py --live
# Only show YES arbitrage
python negrisk_arb.py --side yes
# Only show NO arbitrage
python negrisk_arb.py --side no
# Set minimum profit threshold
python negrisk_arb.py --min-profit 0.05
# JSON output
python negrisk_arb.py --json
# Quiet mode for cron
python negrisk_arb.py --live --quiet
# Show stats
python negrisk_arb.py --stats
# Show config
python negrisk_arb.py --config
# Update config
python negrisk_arb.py --set max_position_usd=10.00
```
## Configuration
| Setting | Env Variable | Default | Description |
|---------|-------------|---------|-------------|
| max_yes_sum | SIMMER_NEGRISK_MAX_YES_SUM | 0.95 | Max sum of YES prices to trigger buy |
| max_no_sum | SIMMER_NEGRISK_MAX_NO_SUM | 0.95 | Max sum of NO prices to trigger buy |
| min_profit_pct | SIMMER_NEGRISK_MIN_PROFIT | 0.03 | Min profit % after fees (3%) |
| max_position_usd | SIMMER_NEGRISK_MAX_POSITION | 10.00 | Max USD per outcome bucket |
| max_total_usd | SIMMER_NEGRISK_MAX_TOTAL | 50.00 | Max total USD per event |
| max_trades_per_run | SIMMER_NEGRISK_MAX_TRADES | 2 | Max arbitrage events per cycle |
| daily_budget | SIMMER_NEGRISK_DAILY_BUDGET | 50.00 | Daily spend limit |
| min_outcomes | SIMMER_NEGRISK_MIN_OUTCOMES | 3 | Min outcomes per event (skip binary) |
| max_outcomes | SIMMER_NEGRISK_MAX_OUTCOMES | 15 | Max outcomes (too many = illiquid) |
| slippage_max_pct | SIMMER_NEGRISK_SLIPPAGE | 0.05 | Max slippage tolerance (5%) |
| fee_filter | SIMMER_NEGRISK_FEE_FILTER | true | Skip markets with fees |
| require_simultaneous | SIMMER_NEGRISK_SIMULTANEOUS | true | Use batch trades for simultaneous execution |
## How It Works
Each cycle the script:
1. **Fetch active markets** — GET /api/sdk/markets (all active, sorted by opportunity)
2. **Group by event** — Group markets sharing the same Polymarket event
3. **Calculate sums** — Sum YES prices and NO prices for each event group
4. **Filter opportunities** — Find events where sum < threshold
5. **Validate edge** — Check fee rates, slippage estimates, min profit after costs
6. **Execute batch** — POST /api/sdk/trades/batch with all buckets simultaneously
7. **Track positions** — Tag all trades with source: "sdk:negrisk-arb"
## Safeguards
- **Fee check** — Skips any event where any outcome has taker fees
- **Slippage check** — Estimates slippage via context API, skips if too high
- **Simultaneous execution** — Uses batch endpoint to minimize timing risk
- **Position check** — Skips events where you already hold positions
- **Daily budget** — Hard stop when daily limit is reached
- **Min outcomes** — Skips binary markets (YES + NO always = $1.00 by design)
- **Liquidity check** — Skips outcomes with very low volume (< $100 24h)
## Fee-Accurate Profit Calculation
```
yes_cost = sum of all YES prices in cluster
payout = $1.00 (exactly one resolves YES)
fee_cost = sum of (price × fee_rate) for each outcome
net_profit = payout - yes_cost - fee_cost
profit_pct = net_profit / yes_cost
```
Only trades when `profit_pct >= min_profit_pct`.
## Simultaneous Execution (Critical)
Standard sequential trades are risky:
```
Buy outcome A → price moves → Buy outcome B → sum now > $1 → no longer profitable
```
This skill uses Simmer's batch endpoint:
```python
POST /api/sdk/trades/batch
{
"trades": [
{"market_id": "uuid-A", "side": "yes", "amount": 5.0},
{"market_id": "uuid-B", "side": "yes", "amount": 5.0},
{"market_id": "uuid-C", "side": "yes", "amount": 5.0}
],
"venue": "polymarket",
"source": "sdk:negrisk-arb"
}
```
All trades execute in parallel — minimizes timing risk.
## Example Output
```
🔢 NegRisk Arbitrage Scanner
==================================================
⚙️ Configuration:
Max YES sum: $0.95
Max NO sum: $0.95
Min profit: 3.0%
Max position: $10.00/bucket
Daily budget: $50.00
🔍 Scanning 847 active markets...
Grouped into 203 multi-outcome events
Evaluating 203 events for arbitrage...
🎯 Opportunities Found: 2
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Event: "Who wins the 2026 German election?"
Side: YES arbitrage
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CDU/CSU: $0.38
SPD: $0.27
Greens: $0.15
AfD: $0.09
FDP: $0.04
──────────────
Sum: $0.93 (< $0.95 threshold) ✅
Profit calculation:
Cost: $0.93
Payout: $1.00
Net: +$0.07 (7.5%) ✅
Fees: $0.00 (zero-fee market) ✅
Slippage: ~1.2% (within limit) ✅
Executing batch trade...
✅ CDU/CSU YES — 13.2 shares @ $0.38
✅ SPD YES — 18.5 shares @ $0.27
✅ Greens YES — 33.3 shares @ $0.15
✅ AfD YES — 55.6 shares @ $0.09
✅ FDP YES — 125.0 shares @ $0.04
Total spent: $4.65
Expected gain: $0.35 (7.5%)
📊 Summary:
Events scanned: 203
Opportunities: 2
Trades executed: 8 (batch)
Total invested: $9.30
Expected profit: $0.70
```
## Win Rate Tracking
The skill tracks every arbitrage in `negrisk_ledger.json`:
- Entry prices and total cost
- Winning outcome after resolution
- Actual profit vs expected
- Slippage impact
Run `--stats` after 20+ events to see real performance vs theoretical edge.
## Troubleshooting
**"No arbitrage opportunities found"**
- Markets may be efficiently priced right now
- Try lowering `min_profit_pct` to 0.02
- Check during high-volatility periods (election night, major events)
**"All events skipped for fees"**
- Only zero-fee markets are traded
- Set `fee_filter=false` to override (not recommended)
**"Batch trade partially filled"**
- Some outcomes may have thin liquidity
- Reduce `max_position_usd` to reduce market impact
- Increase `slippage_max_pct` slightly
**"Sum check passed but trade rejected"**
- Prices moved between scan and execution
- Normal in fast-moving markets
- Skill will retry on next cycle
**"Event has too many outcomes"**
- Increase `max_outcomes` setting
- Or skip — very fragmented markets often have liquidity issues
## API Endpoints Used
- `GET /api/sdk/markets` — Market list with prices and divergence
- `GET /api/sdk/context/{market_id}` — Fee rates and slippage estimates
- `POST /api/sdk/trades/batch` — Simultaneous multi-outcome execution
- `GET /api/sdk/positions` — Current portfolio positions
- `GET /api/sdk/portfolio` — Balance and daily spend tracking
## Cron / Automaton Setup
```json
"automaton": {
"managed": true,
"entrypoint": "negrisk_arb.py --live --quiet",
"cron": "0 */2 * * *"
}
```
Runs every 2 hours. Arbitrage windows can be short — more frequent scanning catches more opportunities.
Or via OpenClaw native cron:
```bash
openclaw cron add \
--name "NegRisk Arbitrage" \
--cron "0 */2 * * *" \
--session isolated \
--message "Run NegRisk arbitrage scan: cd /path/to/skill && python negrisk_arb.py --live --quiet. Show summary." \
--announce
```
## Important Notes
- This is mathematical arbitrage, not prediction — you don't need to know who wins
- Works best on events with 4+ outcomes (more outcomes = more pricing errors)
- Elon Tweet markets are a special case of this strategy (you're already doing it!)
- Opportunities are rare but high-confidence when found
- Always verify the batch filled completely before assuming profit is locked
don't have the plugin yet? install it then click "run inline in claude" again.