self-hosted · local files · fills stay local

The PnL chart says the bot works.
The fills say you're being picked off.

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
localhost:4178 — fillgrade DAILY EDGE REPORT wed jun 10 · 3 strategies · 2 venues · day 41 monitored HEALTHY · markout +0.3 bps d/d markout vs mid · mm-btc-tight 0 bps — mm-btc-tight (+0.4 bps) --- mm-sol-wide (−0.85 bps) 1s 5s 30s 1m 5m one curve earns the spread and keeps it. the other is exit liquidity. promotion gate · momo-eth dry-run eligible promoted ✓ meanMarkoutBps +0.4 bps ✓ adverseSelectionRate 9.7 % ✓ minFills 1,040 ⚠ minLiveMarkoutEvidence 26 / 50 1 gate from full confidence — needs 24 more live fills gate timeline jun 09 · mm-btc-tight promoted jun 04 · mm-sol-wide blocked (meanMarkoutBps)

The dashboard home: a verdict first, the math one click deeper. Local web dashboard (zero-build, works offline).

$2k+/mo
Commonly-cited entry point for enterprise crypto TCA (Talos / CoinRoutes class — sales-led, annual contracts; exact pricing isn't published)
balance-delta
The only "PnL" most free bot frameworks report. No markout, no spread capture, no fill quality
Jul 1, 2026
MiCA transition ends. Art. 78 best-execution obligations become enforceable for EU crypto firms
$49–999+/mo
Helius's public RPC pricing tiers — what serious operators already spend on infrastructure alone, while flying blind on execution
// what it measures

The middle ground that didn't exist

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.

[flagship] gate engine

Strategies earn live capital. They don't get it for one good week.

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:

promoted  mm-btc-tight — all gates passed over 30d window
1 gate from promotion  momo-eth — minLiveMarkoutEvidence 26 / 50, needs 24 more live fills
blocked  mm-sol-wide — meanMarkoutBps −0.85 below the 0.5 floor
[daily edge]

The Daily Edge Report

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.

[markout]

Markout curves, t+1s → t+5m

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.

[spread]

Spread capture

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.

[toxicity]

Adverse-selection flags

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.

[close-cost]

Close-cost decomposition

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.

[ingest]

One fills table, every venue

CCXT and Hummingbot CSV exports, Hyperliquid, Solana trade exports, plus a generic CSV mapper. Everything normalizes into a single local fills table.

// three commands

Install → ingest → decide

Zero to your first gate decision in one sitting. Runs on the box your bot already runs on.

step 1 / install

Install

One npm package. Node 20+. Or try it on sample data without installing anything.

$ npm install -g fillgrade
# or, no commitment:
$ npx fillgrade demo
step 2 / ingest

Ingest your fills

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
step 3 / decide

Read the verdict

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.

// the edges

Where Fillgrade stops

You're trusting this with capital decisions. You should know exactly what it won't do.

// pricing

Self-hosted at every tier

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.

Free

$0
Verify the numbers on your own fills
  • 1 venue
  • 5,000 fills / month
  • Markout curves & spread capture
  • Adverse-selection flags
  • Close-cost decomposition
  • Local dashboard + CLI
Grade my fills

Desk

$149/mo
Serious operators, many venues
  • Unlimited venues & fills
  • Everything in Pro
  • Webhooks on gate decisions
  • Local REST API
Buy Desk

Quant

$299/mo
Small desks & EU-regulated firms
  • Everything in Desk
  • Multi-seat licensing
  • MiCA Art. 78 best-execution report export
  • Priority support
Buy Quant

Monthly billing, cancel anytime. One losing strategy demoted a week earlier pays for years of any tier.

// questions

FAQ

Does any of my trading data leave my machine?
No. Ingest, analytics, dashboard, and gate decisions all run against local files on your disk (JSON-lines by default; SQLite if you install better-sqlite3). The only outbound calls are optional public market-data fetches (reference mid prices) and a license-key check for paid tiers. No fills, no addresses, no strategy names are ever transmitted. Paid licenses revalidate online with a 7-day offline grace period; after that, paid features degrade to the Free tier until you reconnect — your data and analytics stay intact throughout.
How is this different from the PnL my bot framework already shows?
Most free frameworks report balance-delta PnL: end balance minus start balance. That tells you what happened, not why. Markout, spread capture, and adverse-selection analysis tell you whether each fill was good at the moment it happened — which is what you need to know before scaling a strategy up or shutting it down.
How does the promotion gate actually work?
You declare thresholds in a JSON config (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.
Where is my data stored?
Local files on your machine: JSON-lines by default, zero extra dependencies. Install better-sqlite3 and Fillgrade uses SQLite instead — handy if you want to run your own SQL against the fills table. Either way it's plain files you can query, back up, or delete.
What's the license?
Source-available: you can read the code, audit it, and modify it for your own use; paid tiers unlock features via a license key. It is not (yet) OSI open source — we're a tiny team and this keeps us funded. Perpetual-fallback licensing is on the roadmap: cancel and keep the last version you paid for.
What venues and formats can I ingest?
CCXT-format trade exports (covers most major CEXs), Hummingbot CSV, Hyperliquid, Solana trade exports, and a generic CSV mapper for anything else — if you can get fills into a CSV with timestamp, symbol, side, price, and size, Fillgrade can grade them. Venue limits apply per tier.
I'm an EU operator — what does the MiCA export give me?
MiCA's transition period ends July 1, 2026, after which Article 78 best-execution obligations are enforceable for crypto-asset service providers. The Quant tier exports an execution-quality report (per-venue markout, slippage, and cost statistics over a period) formatted as supporting evidence for best-execution review. It's a documentation aid, not legal advice.
Can I try the gates before paying?
The Free tier includes full markout and spread-capture analytics on one venue, so you can verify the numbers are real on your own fills first — and 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.