Balance-delta PnL — end balance minus start balance — can look healthy for months while someone faster quietly takes the other side of every quote. Fillgrade grades each fill on your own machine: markout, spread capture, adverse selection, and a paper→live promotion gate that decides with auditable reasons.
no install, sample data: npx fillgrade demo
Markout is what the price did right after your trade — the cleanest test of whether your fills are good or you're being picked off.
Fillgrade checks the mid price at 1 second, 5 seconds, 15 seconds, 30 seconds, 1 minute, and 5 minutes after every fill. A markout of −0.8 bps means you lose about $0.08 per $1,000 traded. Invisible per fill. Decisive at size.
$ fillgrade demo Seeding the demo dataset (2 venues, 3 strategies, ~1,000 fills)… 1040 fills, 8320 mid prices, 602 guardrail events $ fillgrade gates check [PASS] mm-btc-tight: promoted -> promoted (hold) [FAIL] mm-sol-wide: dry-run -> dry-run (hold) - meanMarkoutBps: -0.85 < 0.5 - guardrailBlockRate: 0.24 > 0.2 [FAIL] momo-eth: eligible -> eligible (hold) - minLiveMarkoutEvidence: 26 live fills with markout < 50 1/3 strategies promotable $ echo $? 1 # an exit code. your deploy pipeline already speaks this language
The dashboard home: a verdict first, the math one click deeper. Local web dashboard (zero-build, works offline).
Between free bot frameworks and sales-led enterprise TCA (commonly cited in the thousands per month) there has been nothing. Time-series vendors publish DIY markout cookbooks — QuestDB in its docs, OneTick on Medium — because no product covers this. Fillgrade is that product.
Declare your promotion policy in a JSON config (fillgrade.gates.json): minimum markout, spread capture floor, adverse-fill ceiling, sample size. Fillgrade evaluates paper or canary fills against it and emits a promote/demote decision with explicit block reasons — every decision recorded locally, queryable via fillgrade gates history. Exit code 0 promotes, 1 blocks. It drops into the same pipeline that deploys your bot. The dashboard turns those decisions into a timeline:
One screen, one verdict, every morning: fillgrade report --daily. Day-over-day markout deltas, gates moved, fills graded — with a healthy / watch / act chip on top. Conclusions first; "show the math" one click deeper.
Mid-price markout at standard horizons per strategy, venue, and symbol. See whether your fills are genuinely good or merely early — before the position tells you the expensive way.
The slice of the bid-ask spread you actually earned on each fill — the single most honest number a market-making strategy has. Split maker/taker.
Flags trades where the market immediately moved against you — someone likely knew more than your bot. Sliced by venue, hour of day, and symbol. The patterns are usually concentrated, and findable.
Realized PnL split into edge captured vs. fees, slippage, and exit cost. A surprising number of "profitable" strategies are net payers to trade. This is where they get caught.
CCXT and Hummingbot CSV exports, Hyperliquid, Solana trade exports, plus a generic CSV mapper. Everything normalizes into a single local fills table.
Zero to your first gate decision in one sitting. Runs on the box your bot already runs on.
One npm package. Node 20+. Or try it on sample data without installing anything.
$ npm install -g fillgrade
# or, no commitment:
$ npx fillgrade demo
Point it at your exports. Fillgrade normalizes everything into one local fills table.
$ fillgrade ingest hummingbot \
--file fills.csv
$ fillgrade ingest csv \
--file other_venue.csv
Open the dashboard, get the daily verdict, let the gate make the promote/demote call in CI.
$ fillgrade serve # localhost:4178
$ fillgrade report --daily
$ fillgrade gates check
Something misbehaving? fillgrade doctor diagnoses config, data, and license issues — and tells you the fix, not the stack trace.
You're trusting this with capital decisions. You should know exactly what it won't do.
Paid plans are license keys. The software and your data stay on your machine either way — keys validate online with a 7-day offline grace period.
Monthly billing, cancel anytime. One losing strategy demoted a week earlier pays for years of any tier.
fillgrade.gates.json) — e.g. "meanMarkoutBps": -1, "adverseSelectionRate": 0.12, "minFills": 5000, plus custom expressions over the full metric namespace ("spreadCaptureBps >= 0.5"). fillgrade gates check evaluates a strategy's recent paper or canary fills against the config and exits 0 (promote) or 1 (blocked), printing each pass/fail with the measured value and recording the decision locally (queryable via fillgrade gates history). Because the interface is an exit code, it slots into GitHub Actions, cron, or whatever deploys your bot today.npx fillgrade demo shows every feature, gates included, on sample data. Gates on your real fills start at Pro. If Pro doesn't pay for itself in your first month of decisions, email us and we'll refund it.