Skip to main content

Audit your Polymarket bot's actual on-chain P&L vs what your bot thinks it earned. Slippage-focused fill reconciliation.

Project description

pnl-truthteller

Audit your Polymarket bot's actual on-chain P&L vs what your bot thinks it earned.

pip install pnl-truthteller → run one command → get a report that shows you how much money your fill slippage is actually costing you.

If you run a Polymarket trading bot and you've never compared sum(your_db.profit) to sum(actual on-chain pUSD movement) — you have no idea what you're really making. This tool tells you.

Why this exists

Most Polymarket bots record P&L the moment an order is placed, not when it fills. The CLOB matching engine fills in stages (FOK rejects, partial fills, sweep retries, dust). If your bot writes profit=$3.20 to its DB the moment post_order returns OK, but the actual on-chain fills only retrieve $2.85, you're losing ~11% to slippage and your DB is lying to you about it.

We hit this on our own bot. The DB said +$33.49 lifetime profit. The chain said -$89.01. Difference: -$122.50 of hidden slippage cost across 302 trades.

This package finds that gap on your bot.

Install

pip install pnl-truthteller

Or from source:

git clone https://github.com/LuciferForge/pnl-truthteller
cd pnl-truthteller
pip install -e .

Usage — three input modes

1. Direct from CLOB API (recommended — zero setup)

If you trade through py-clob-client or py-clob-client-v2, your fills are queryable from Polymarket's CLOB API. Just give us your wallet address:

pnl-truthteller --wallet 0xYourProxyAddress --output report.md

This pulls every fill for the wallet, groups them by token+direction, and produces a slippage report.

2. From your bot's SQLite (if you've been logging fills locally)

If your bot stores raw CLOB responses in a SQLite file (column raw_response), point the tool at it:

pnl-truthteller --sqlite ~/bot/trades.db \
                --positions ~/bot/positions.json \
                --output report.md

Schema expected: a live_trades table with columns token_id, side, timestamp, raw_response where raw_response is the JSON string returned by client.post_order(). See docs/data-format.md.

3. From a JSONL file (custom integrations)

If you're on a non-Python stack or have custom logging, dump your trades + fills to JSONL and pass them in:

pnl-truthteller --trades trades.jsonl --fills fills.jsonl --output report.md

Format spec: docs/data-format.md.

What the report looks like

# Slippage Report — 2026-04-28T14:30:00+00:00

## TL;DR
- Closed trades total: 302 (live: 302, paper: 0)
- Lifetime theoretical P&L: +$33.49
- Lifetime actual P&L (on-chain fills): -$89.01
- Total slippage cost: -$122.50 (-365.8% of theoretical)
- Trades with stranded dust on-chain: 31 (total 47.3 shares dust)

## By exit reason
| Reason | n | Theoretical | Actual | Slippage |
|---|---|---|---|---|
| TIMEOUT | 142 | -$18.00 | -$84.50 | -$66.50 |
| TARGET | 71 | +$28.40 | +$22.10 | -$6.30 |
| RECOVERY_TRAILED | 50 | +$15.20 | +$12.40 | -$2.80 |
| STOP | 39 | +$7.89 | +$0.99 | -$6.90 |

## Worst 10 slippage events (per-trade)
[table of the 10 worst trades by slippage]

What this measures, precisely

For each closed trade in your data, the tool:

  1. Finds the actual BUY fills by matching token+timestamp window, deduplicated by orderID.
  2. Finds the actual SELL fills that closed the position (same matching, same dedup).
  3. Computes theoretical P&L = (exit_price - entry_price) × shares (what the bot's DB says).
  4. Computes actual P&L = sum(sell_takingAmount) - sum(buy_makingAmount) (what the chain says).
  5. Slippage = actual - theoretical. Negative = your fill ladder walked the book down.

The dedup-by-orderID step is critical. Sweep retries (where your bot tries 5%, 15%, 25% off ref price) often log the same orderID multiple times if your bot calls post_order from a retry loop without checking idempotency. Without dedup you double-count the proceeds and your slippage looks fine when it isn't.

What it does NOT do

  • It does NOT execute trades. Read-only auditing.
  • It does NOT need your private key. Wallet address only.
  • It does NOT report tax-purpose P&L. Slippage-focused, not gain/loss accounting.
  • It does NOT work for non-Polymarket markets (yet — see roadmap).

When to run it

Frequency Why
Once, now If you've never done a fill-level audit, run it once to find out what your real P&L is.
Daily (cron) If your bot is live, run the report nightly to catch slippage regressions early.
After every parameter change Param changes (entry threshold, exit ladder) shift slippage. Run before/after to measure.

Roadmap

  • v0.2 — Per-market category breakdown (sports vs politics vs crypto have different liquidity profiles)
  • v0.3 — Orderbook-depth-at-time-of-fill reconstruction (what was the actual book when you swept?)
  • v0.4 — Adapter for Kalshi, Polygon Polymarket-style markets
  • v0.5 — Streamlit dashboard (real-time slippage monitoring)

About the author

Built by LuciferForge, a solo operator running a public-audited Polymarket crash-recovery bot (302 closed trades, 79.8% WR). I built this because my own bot's DB was lying to me. Now that it's not, my parameters are honest. Yours can be too.

License

MIT. Audit your bot, audit your friends' bots, audit anyone's bot. The chain is public.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pnl_truthteller-0.1.0.tar.gz (18.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pnl_truthteller-0.1.0-py3-none-any.whl (17.5 kB view details)

Uploaded Python 3

File details

Details for the file pnl_truthteller-0.1.0.tar.gz.

File metadata

  • Download URL: pnl_truthteller-0.1.0.tar.gz
  • Upload date:
  • Size: 18.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.2

File hashes

Hashes for pnl_truthteller-0.1.0.tar.gz
Algorithm Hash digest
SHA256 8d27259020a367deabdf347f03b8a9d593105b30a4b2d24fd80dc2cf2bae67b2
MD5 07e79711ce250c6fffefa9492ef5624c
BLAKE2b-256 02db91357e34dd99d758dd46154bfe8747f24103c3077d5548c19292e4dfa022

See more details on using hashes here.

File details

Details for the file pnl_truthteller-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pnl_truthteller-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 951367d09ba33f1d6d2d0fe10fd2ff0c337116ef26c183d2f7789bd78bf2a8b6
MD5 2b68973b92e63c8dcc2bb006d291c288
BLAKE2b-256 548c0a83a71f9239756c37df30fa7b86a6588a8f63cdea59c7e7ef6626020ef3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page