Last weekend I built tilt-check for my partner, who trades micro futures at night from Korea. Before a trade she'd type it ("short 2 MNQ"), and it would show what her own history said about trades like that. I wrote it up here.
She read the post and said, more or less: the trades I'd need to type for are exactly the ones I don't stop to type for.
She's right. Nobody pauses to describe the trade they're about to take in a hurry. So the check now needs nothing typed. Here's how that went.
If you'd rather watch it: the same story as a 3-minute cartoon, with October 1 replayed through the tool.
The feed: a NinjaTrader add-on that only listens
NinjaTrader 8 add-ons are C#. Mine subscribes to ExecutionUpdate and OrderUpdate on every account and appends one line per event to two CSV files in Documents\tilt-check:
-
fills.csv: time, account, instrument, signed quantity (+2 for a buy of two, -1 for a sell of one), price, execution id -
orders.csv: order id, action, type, state, quantity, stop and limit prices
A timer adds a third file, snapshots.csv, with unrealized P&L, open positions and cash value: every 2 seconds while a position is open, every 60 seconds when flat.
That's it. It never calls Submit, Change or Cancel. That was the first thing we agreed on when I built the original, and it matters more now that the thing runs by itself. Her prop firm also bans fully automated trading, so "a file writer" is as far as this goes on the NinjaTrader side.
Honest status: I compiled it against stub types with csc.exe because the trading PC isn't mine. It has not run inside a real NinjaTrader yet. If you've shipped an NT8 add-on and see something that will break on first launch, I'd like to know.
The watcher: tail the files, rebuild the position, check her rules
The Python side (python -m tiltcheck watch) tails the three files and keeps a running position per instrument from the fills: average price, size, when it opened. Every fill goes through a few checks against a rules.json:
- max trades per day, max contracts (micros and minis separately), daily loss limit in dollars or as a percent of her drawdown
- a pause after a losing exit, with a longer pause after several losses in a row
- adding to a losing position, with a minimum distance so a scale-in at breakeven doesn't fire
- size more than N times her usual size for that instrument
- entries in sessions her own history says are weak
- a stop check: a few seconds after an entry, if there's no stop order in
orders.csvfor that position, it says so
When something fires, it pops a Windows toast (through PowerShell, no extra packages) and beeps if it's urgent. When nothing fires, it still shows one quiet line at each entry: her median winner and median loser for that instrument and side, as prices. Her own numbers, not a prediction.
The rules file is optional. The pause rule exists because her history says so: re-entering within 15 minutes of a losing exit made -$178.65 over 50 trades; waiting longer made +$1,395.92 over 42. She didn't have a written rule for that before. Now the tool just tells her when the 15 minutes are up.
Replaying a real day through it
There's a --replay-day flag that takes a past day from her history and feeds the trades through the watcher as fills, in order, with only the history before that day loaded. Here's October 1 on her account (she agreed to share this one), trimmed:
12:07:06 fill +1 MGC @ 4194.60
[tilt-check: rule] Loss closed ($-28.34). Your pause: next entry after 12:22.
12:10:52 fill -2 MGC @ 4195.40
[tilt-check: check this one]
RULE: 4 min after a losing exit, your pause is 15 min.
12:21:31 fill -10 MGC @ 4194.10
[tilt-check: check this one]
RULE: 10 min after a losing exit, your pause is 15 min.
SIZE: 10 contracts, your usual MGC size is 1.
14:03:37 fill -1 MNQ @ 30995.00
[tilt-check: check this one]
Adding to a losing short: average 30964.75, now 30995 (-30.25 pts, 16 min in).
14:15:04 fill +20 MNQ @ 31020.50
[tilt-check: check this one]
RULE: 9 min after a losing exit, your pause is 15 min.
SIZE: 20 contracts, your usual MNQ size is 1.
14:29:00 fill -1 MNQ @ 31061.75
[tilt-check: check this one]
Adding to a losing short: average 31028.5, now 31061.75 (-33.25 pts, 9 min in).
Both adds to a losing MNQ short got flagged. All four of those contracts closed at a loss, -$273.16 together. The 10-lot and the 20-lot were in and out within a minute each; she calls those warm-up trades, and the tool doesn't know that, so it flagged the size. That's fine. A flag is a question, not a verdict.
What I got wrong first
Position from rows, not fills. NinjaTrader's trade export has one row per exit, so a scaled-out entry becomes several rows and trades overlap. Rebuilding the position from signed fills fixed that, and it's also why the add-on logs raw executions instead of "trades".
PC clock vs. market clock. On a Korean PC, 13:00 looks like lunchtime. It's midnight in New York. Every session label is in New York time now, with daylight saving handled, and the PC hour shown next to it.
Alerting on everything. The first version fired on every entry. She'd have muted it in an hour. Now there's a mute list for rules she already knows about (her first few trades of the day are weak, and she knows), and the quiet line at entry is just two prices.
Chart lines, from closed bars only
She watches EMAs and VWAP bands, so the watcher now reads minute bars too (--bars, an export for now) and adds one line of facts at each entry, computed only from bars that had closed when she clicked:
CHART: 15m 200 EMA 30695.00, against the trade; VWAP +1.5 sd, fading the stretch.
Three optional rules sit on top: warn when an entry is against the 15-minute 200 EMA, warn when chasing more than N sd past VWAP, warn on a fade inside the first band (her own record says that's where she's weakest). Replaying October 1 on her own bars with those on, 4 of her 12 entries drew the trend warning.
What's next
Live bars. The add-on should write closed 1-minute bars to a file the same way it writes fills, so the chart line works without an export. The replay module already refuses to look ahead; the live one has to as well.
If you trade and want to run your own history through this, the repo has a report command that works on a plain NinjaTrader trade export, no add-on needed:
ssap-pa
/
tilt-check
Pre-trade check for futures traders: TabPFN learns from your own NinjaTrader history, Gemma explains it locally. Never places orders.
tilt-check
A pre-trade check for futures traders that learns from your own NinjaTrader history.
TabPFN (open weights) reads your past trades. Gemma (open weights, through Ollama) tells you in plain language what your own numbers say about the trade you're about to take. Everything runs on your machine. It never places an order.
I built it for my partner, who trades micro futures (MNQ, MES, MGC) on a prop-firm account and kept asking the same question after a bad session: is this one of my good trades or one of my bad ones?
Want this run on your own export and written up? Your Trading History, Audited: a written report within 48 hours, late means a full refund. The sample is one real account.
What it said about her account
Her numbers, shared with her permission. One account, Sep 1 to Oct 2, 2026:
- 293 entries over 23…
There's also a new audit-report command that writes the whole thing up as one document: sessions in market time, first trades of the day, re-entries after a loss, size, exits with the bracket replay, and your rules checked against the chart. Here's what it produced for her account, rendered.
If you'd rather I run it on your export and write it up, the details are here: https://ssap-pa.github.io/tilt-check/

Top comments (0)