Skip to main content

pnl-truthteller

PyPI Python License

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

pip install pnl-truthteller
pnl-truthteller --wallet 0xYourPolymarketProxy --output report.md

That's the entire workflow. Wallet address only. No API keys. Read-only. You get a markdown report showing how much your fill slippage is actually costing you.

What we found

We built this because our own crash-recovery bot looked profitable in its SQLite (+$34) but felt like it was bleeding capital. We were right:

Source Trades DB-reported P&L On-chain P&L Hidden slippage
Our bot 320 $+34.31 $-90.72 $-125.03
Random stranger's wallet (0x1417...) 65 $+32.36 $-30.29 $-62.66

The gap generalises. We tested the tool on a random Polymarket trader's wallet pulled from the public CLOB feed — same pattern. Their DB-equivalent showed +$32 over 65 trades; the chain says -$30; that's $62 of hidden slippage they don't know about.

Both samples are in examples/.

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.

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: 308 (live: 308, 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 and other prediction-market venues
  • 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 (308 closed trades, 80.2% WR). I built this because my own bot's DB was lying to me — found -$122.50 of hidden slippage cost on 308 trades. Now that the math is honest, the parameters can be too. Yours can be too.

Other tools in the LuciferForge stack:

License

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

Release files for pnl-truthteller 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pnl-truthteller 0.1.1
File Size Uploaded
pnl_truthteller-0.1.1.tar.gz 19.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pnl-truthteller 0.1.1
File Interpreter ABI Platform
pnl_truthteller-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 37.7 kB

Release files / pnl_truthteller-0.1.1.tar.gz

Download URL pnl_truthteller-0.1.1.tar.gz
Size 19.5 kB
Tags Source
SHA-256 checksum
How to use checksums
59d87074b7480b07a28b5df563fc0c847b1cffc5c3d451b92a2a3554fec60335
BLAKE2b-256 checksum
How to use checksums
bf396894e6a538c3fe3b50e6feda87a154d39c422656e81657e7dad40a15745f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.8.9

Release files / pnl_truthteller-0.1.1-py3-none-any.whl

Download URL pnl_truthteller-0.1.1-py3-none-any.whl
Size 18.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9e59fcc586af474c36e60d5e3aaed8e8ab95c7fd0c0bac67f8892947b0c0b73d
BLAKE2b-256 checksum
How to use checksums
8bce8d44155640e415ba83cadb92b129adf8d45fd5407d5cc05829f4953501ed
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.8.9

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page