Run report¶
fastmm report turns what a run left behind into one HTML file: equity and inventory over time, where the PnL came from, markouts, fill quality, the counts, and the configuration that produced them. Code: python/fastmm/report.py.
The file is self-contained: inline CSS, charts as inline SVG and CSS bars, no JavaScript, no network access. It opens from a file:// URL, follows the system dark mode and prints.
Write one¶
fastmm report runs/backtest # writes runs/backtest/report.html
fastmm report runs/20260101-101500/session.fmj # writes into the same directory
fastmm report runs/backtest -o /tmp/run.html
python3 tools/report.py runs/backtest # from a checkout, nothing installed
The command prints the path it wrote and nothing else.
| Input | Where it comes from |
|---|---|
| a backtest output directory | fastmm-backtest --out <dir> or BacktestResult::write_all: summary.json, equity.csv, fills.csv |
a session journal (.fmj) |
fastmm-live --journal <path>, scripts/run-sim.sh |
a BacktestResult in memory |
fastmm.write_report(result, "report.html") |
fastmm is the console script of the Python package (Python); python -m fastmm report is the same command. tools/report.py loads the same module out of the source tree and needs only the standard library, which is how scripts/run-sim.sh writes its report.
The report names the instrument's assets and lists the strategy parameters when it can find the configuration: the one embedded in a session journal, a .toml file in the run directory, or --config <file.toml>.
What it shows¶
| Panel | What is in it |
|---|---|
| header | strategy, kind of run, instrument, window, and the net PnL in the quote currency |
| key figures | fills and their maker/taker split, traded notional, fees paid, quote uptime, max drawdown, Sharpe per bar |
| equity and inventory | equity (realized + unrealized, less fees) and the net position on one time axis, with the worst drawdown marked, the inventory limit drawn as a threshold, and a strip showing when both sides were quoted |
| where the PnL came from | the pnl_decomposition of summary.json: gross spread capture, mid drift after the fills, fees, rebates, and what is left unexplained (Economics) |
| markouts | the venue mid at each horizon against the fill price, in bps of notional, per horizon and split buy/sell, with the fills each horizon excluded (Backtesting) |
| fill quality | at the touch / inside it / behind it / through it, time from quote to fill (p50, p90, p99), realized spread, quotes placed and filled, queue position at the fill |
| quotes, rejects and fills | orders, cancels, replaces, rejects, outbound messages, market-data events, tick-to-order latency, and when the fills arrived |
| configuration | the strategy parameters, the run settings (seed, window, markout horizons, outbound hash) and the effective configuration |
Every number is one the run reported. A missing input removes its panel.
A session report is smaller¶
A live session journal records the fills, not the venue's book at each fill, so a session report has no markouts, no spread capture and no fill quality, and says so. It marks inventory at the last book ticker the venue published (or the last trade print), and splits the PnL into realized, unrealized and fees only.
For the full set of panels, backtest the journal (fastmm-backtest --data session.fmj --out runs/replayed) and report on that. Those numbers are a simulation over the session's market data, not the session (Backtesting).
From Python¶
import fastmm
result = fastmm.run_backtest(cfg, data="synthetic", strategy=MyMM)
print(fastmm.write_report(result, "report.html"))
fastmm.write_report(source, out=None, config=None) takes a result, a run directory or a journal path (Python API).