edgekit.report
Self-contained HTML reports. Everything is inlined — the CSS in a <style> block, every chart a data:image/png;base64, URI — so the file opens anywhere with no network. Pure-Python f-strings; matplotlib is only ever reached through edgekit.viz.
What's inside. The chainable Report builder (every method returns self), the three_report_suite convenience that emits a linked Challenge / Live / Realistic trio, and three module-level HTML helpers (build_css, card_html, month_table_html) for when you want fragments rather than a whole page.
Module helpers#
build_css(theme: str = "dark") -> str # inline stylesheet for a theme
card_html(label, val, sub: str = "") -> str # one KPI card (HTML-escaped)
month_table_html(monthly, theme: str = "dark") -> str # year x month grid table with row totalsbuild_css— the inline stylesheet string for a theme ("dark"or"light").card_html— one KPI card as an HTML fragment (label, value, optional sub-line; all HTML-escaped).month_table_html— a year × month grid table (with row totals) from a monthly series.
Report (chainable builder)#
Assembles one self-contained HTML page. Every method returns self for chaining.
Report(title: str, meta: str = "", theme: str = "dark") # theme in {"dark","light"} or ValueError| Param | Type | Default | Meaning |
|---|---|---|---|
title | str | — | Page title (the top headline). |
meta | str | "" | Sub-line under the title (e.g. instruments · account size). |
theme | str | "dark" | Palette: "dark" or "light" (else ValueError). |
| Method | Purpose |
|---|---|
header(title, meta="") | Reset the page title / meta. |
nav(links) | Row of (label, href) cross-links. |
section(title) | An <h2> heading. |
hero(label, value, sub="") | The big highlighted headline number. |
kpi_row(cards) | Flex row of KPI cards; each card is (label, val[, sub]) or a dict. |
card(label, val, sub="") | A single KPI card. |
table(headers, rows) | A plain table (headers list, rows list of cell-lists). |
month_table(monthly) | Year x month grid with totals. |
chart(fig, dpi=95) | Embed a matplotlib figure as an inline base64 PNG (closes it). |
caveat(text) | A muted caveat box. |
html_text(raw) | Append a raw HTML fragment verbatim. |
grid(figs, cols=2, dpi=95) | Several figures in a responsive CSS grid (each inlined). |
metrics_table(stats, title=None) | A metrics dict rendered as a key/value table. |
image(src) | Embed an EXTERNAL PNG (path or bytes) as an inline data-URI. |
gauntlet_panel(...) | A standardized Validation section (perm_p / stress / beta / dsr / pbo). |
render() -> str | Assemble the full <!doctype html> string. |
write(path) -> Path | Render and write to path (returns the Path). |
from edgekit.report import Report
from edgekit import viz
monthly = viz.trades_to_monthly(trades)
fig = viz.monthly_heatmap(monthly)
path = (Report("ORB — US100 M1", meta="Nasdaq intraday · $100k", theme="dark")
.hero("Expectancy", "−0.214R", "net of costs")
.kpi_row([("PF", "0.71"), ("Win", "39%"), {"label": "Trades", "val": "2,666"}])
.section("Equity")
.chart(fig)
.month_table(monthly)
.table(["Year", "Total R"], [[2023, -12.4], [2024, -9.1]])
.caveat("Net-negative after costs; the gauntlet rejects it.")
.write("orb_report.html"))
print(path) # -> orb_report.html (self-contained, opens offline)http:// or https:// — you can email it, drop it on a share, or open it on a plane and it renders identically.Composite blocks#
Four higher-level blocks build on the primitives above. Each still returns self.
grid#
Embed several matplotlib figures in a responsive CSS grid, each inlined as its own base64 PNG. Handy for a panel of diagnostics under one heading.
grid(figs, cols: int = 2, dpi: int = 95) -> Reportfigs— an iterable of matplotlib figures (each is closed after encoding).cols— grid columns (default2).dpi— render resolution per figure (default95).
metrics_table#
Render a metrics dict (e.g. trade_stats / equity_stats) as a key/value table, with sensible int/float/bool/str formatting. An optional title emits an <h3> above it.
metrics_table(stats: dict, title: str | None = None) -> Reportimage#
Embed an external PNG — a filesystem path or raw bytes — as an inline data-URI. Use this for a chart produced outside viz (e.g. one saved earlier with viz.save_png). Contrast with chart(fig), which takes a live matplotlib figure.
image(src) -> Reportsrc— a path (str/Path) read from disk, or rawbytes.
gauntlet_panel#
A standardized Validation section that renders whichever results you pass. KPI cards for the scalar checks and a cost-stress table for stress, plus the survivor rule (PF > 1 at 2x and 3x cost).
gauntlet_panel(perm_p=None, stress=None, beta=None, dsr=None, pbo=None) -> Report| Param | Renders | Verdict rule |
|---|---|---|
perm_p | Permutation p card | PASS if < 0.01 |
stress | Cost-stress PF / EV-R table | PF > 1 at 2x and 3x |
beta | Is-it-beta (β) card | alpha if |β| < 0.2 |
dsr | Deflated Sharpe card | PASS if > 0.95 |
pbo | PBO card | PASS if < 0.5 |
(Report("ORB — validation", theme="dark")
.gauntlet_panel(perm_p=0.040,
stress={1: {"pf": 0.71, "ev_r": -0.214},
2: {"pf": 0.52, "ev_r": -0.402},
3: {"pf": 0.38, "ev_r": -0.560}},
beta=0.05, pbo=0.71)
.write("orb_validation.html"))
# perm_p 0.040 -> FAIL vs 0.01; PF < 1 at every cost multiple.tear_sheet#
The one-call full report. Assembles a complete standard tear sheet from a trade frame: a hero + KPI row from trade_stats, an equity+drawdown panel, a monthly-return heatmap, a return distribution and R histogram (in a two-up grid), and a metrics table. If account (dollars-per-R) is given it adds a dollar monthly heatmap too.
tear_sheet(trades: pd.DataFrame, account: float | None = None,
title: str = "Strategy tear sheet", theme: str = "dark", out=None)| Param | Type | Default | Meaning |
|---|---|---|---|
trades | pd.DataFrame | — | Canonical trade frame (date + r). |
account | float | None | None | Dollars-per-R; if given, adds a dollar monthly heatmap. |
title | str | "Strategy tear sheet" | Page title. |
theme | str | "dark" | Palette: "dark" or "light". |
out | str | Path | None | None | If given, writes there and returns the Path; else returns the HTML string. |
from edgekit.report import tear_sheet
# one call: trade frame in, self-contained HTML out
path = tear_sheet(trades, account=500.0,
title="ORB — US100 M1", theme="dark", out="orb_tearsheet.html")
print(path) # -> orb_tearsheet.html
# omit out= to get the HTML string back instead of writing
html = tear_sheet(trades)tear_sheet is the fast path — one call covers equity, drawdown, distribution and the metrics table. Drop down to the Report builder when you need a custom layout, cross-linked pages, or a gauntlet_panel.three_report_suite#
Emit the linked Challenge / Live / Realistic trio in one call. Sizes the trade R-stream to dd_budget of account, then renders three cross-linked self-contained pages: an aggressive challenge view, a live-ceiling view, and a haircut-adjusted realistic view.
three_report_suite(trades: pd.DataFrame, account: float, out_dir,
title_prefix: str = "Strategy", theme: str = "dark",
dd_budget: float = 0.095, haircut: float = 0.85, split: float = 0.90) -> list[Path]| Param | Type | Default | Meaning |
|---|---|---|---|
trades | pd.DataFrame | — | Canonical trade frame (date + r). |
account | float | — | Account size in dollars. |
out_dir | str | Path | — | Directory the three HTML files are written into. |
title_prefix | str | "Strategy" | Prefix for the three page titles. |
theme | str | "dark" | Palette for all three pages. |
dd_budget | float | 0.095 | Drawdown budget the R-stream is sized to. |
haircut | float | 0.85 | Multiplier applied for the realistic page (the honest haircut). |
split | float | 0.90 | IS/OOS split fraction used inside the suite. |
Returns a list[Path] — the three written file paths.
from edgekit.report import three_report_suite
paths = three_report_suite(trades, account=100_000, out_dir="reports/", title_prefix="ORB")
# -> [reports/orb_challenge.html, reports/orb_live.html, reports/orb_realistic.html]haircut-adjusted realistic page precisely so the number you plan around is the one you can actually trade — not the aggressive challenge headline.See also#
- edgekit.viz — the figures
.chart()embeds. - edgekit.challenge — the pass-rate numbers that headline the challenge page.
- edgekit.sizing — the sizing the suite applies before rendering.