Skip to content

HTML Reports — pmlab.reports

generate_report produces a self-contained dark-themed HTML file from your paper trading results. No external JS or CSS dependencies — everything is inline.


Basic Usage

from pmlab import generate_report, PaperBroker

broker = PaperBroker(trades_path="artifacts/ops/trades.json", ...)
trades = broker.load_trades()

output = generate_report(
    trades,
    output_path="reports/may_2026.html",
    title="May 2026 — Buenos Aires Paper Trading",
)
print(f"Report saved to: {output}")

Open the file in any browser — no server required.


With Brier Score

from pmlab import generate_report, brier_decomposition

diag = brier_decomposition(y_true, y_prob)
generate_report(
    trades,
    output_path="reports/session.html",
    brier_score=diag.brier_score,
)

The Brier score appears as an additional summary card.


Report Sections

Summary Cards

Card Description
Total PnL Sum of realized_pnl for all settled trades (green/red)
ROI total_pnl / total_staked * 100
Hit Rate % of settled trades with realized_pnl > 0
Avg Edge Mean edge_after_fee across settled trades
Settled Count of settled trades
Brier Score Optional — shown if passed to generate_report

Equity Curve

SVG polyline of cumulative PnL over settled trades, ordered by recorded_at. A dashed zero-line is drawn if the curve crosses zero. Final PnL is labeled at the endpoint.

Per-Segment Breakdown

Table of trade count, total PnL, and hit rate for each city_or_segment. Sorted alphabetically.

Trade Log

Full table of all trades (settled and open), with columns: Date, Segment, Direction, Outcome, Price, Edge, PnL (open for unsettled).


Trade Dict Format

generate_report works with the output of PaperBroker.load_trades():

{
    "recorded_at": "2026-05-01T10:00:00+00:00",  # ISO timestamp
    "city_or_segment": "Buenos Aires",
    "target_date": "2026-05-02",
    "outcome_label": "above_30",
    "direction": "yes",
    "gamma_price": 0.35,
    "edge_after_fee": 0.08,
    "flat_stake": 5.0,
    "realized_pnl": 9.28,   # None for open trades
}

Fields not present default to empty string or 0 — the report will not crash on partial data.


Automating Weekly Reports

from pathlib import Path
from datetime import date
from pmlab import generate_report, PaperBroker

def generate_weekly_report(trades_path: Path, reports_dir: Path) -> Path:
    broker = PaperBroker(trades_path=trades_path, flat_stake=5.0)
    trades = broker.load_trades()
    week = date.today().strftime("%Y-W%U")
    output = reports_dir / f"report_{week}.html"
    return generate_report(trades, output_path=output, title=f"pmlab Weekly — {week}")