Step 7 of 7

Reports

Three HTML files, each answering a different question. One of them is the file you hand to a human before money is involved — and it is also the slowest to build, for a good reason.

The three deliverables

FileGenerated byQuestion it answers
report.html report_m5.py How did each individual feature set do, and how does the book look?
tail_holdout_report.html report_tail.py What happened on the unseen tail, warm versus cold?
report_m5_performance.html broker_run_report.py The delivery report. Everything needed to judge the strategy, in one file.
report_m5.py         --symbol XAUUSD --tf M5 --port all9
report_tail.py       --symbol XAUUSD --tf M5 --tail 2500
broker_run_report.py --symbol XAUUSD --tf M5 --tail 2500
Every report embeds its own styles and charts They are single-document HTML with inline SVG — no external CSS, no JavaScript libraries, no image files. That means a report is a self-contained artifact: email it, archive it, open it in five years, and it still renders identically.

The per-set report

report_m5.py is the analysis view of the run.

Per-set table
Each feature set's slot metrics — profit factor, net profit, trades, win rate, drawdown, Sharpe.
Equity curves
Per-set and combined, drawn from the equity_*.csv files.
Feature lists
What each set actually models, from the union_features field.
In-sample labelling
Every number here is flagged as an upper bound, because the configurations were chosen on this data.

The --port flag selects which portfolio folder to highlight — pass all9 to centre the nine-slot book.

The tail-holdout report

report_tail.py presents the warm/cold comparison from Step 5.

Warm vs cold side by side
Both modes' metrics, so the gap between them is legible.
Comparison against in-sample
The holdout next to the headline, which is the comparison that decides whether to trade the thing.
Per-slot breakdown
Which slots survived the tail and which did the heavy lifting.
Tail equity curve
The holdout window's combined curve and drawdown.

It reads tail_report.json from <root>/<folder>/tail<tail>_<mode>/, so both modes must have been run for the full comparison.

The delivery report

broker_run_report.py produces the one to hand to a human. It is the slowest to build, and deliberately so.

Sections

Data and cost basis
Which CSV, how many bars, over what period, and — critically — the median spread and the resulting cost assumption. Every P&L figure in the report is shown raw and net of that cost.
Equity and drawdown
The combined curve with its drawdown envelope.
Per-slot performance
Each slot's curve and metrics, so concentration and correlation are visible rather than averaged away.
Best hours
The session windows and the per-hour P&L histogram from Step 4.
24h versus the compiled windows — exact replay
The section that makes this report worth the wait. See below.
Holdout and verdict
The unseen-tail result, next to the in-sample headline, with a single verdict statement.

Why the 24h comparison re-runs the model

Filtering the trade list would give the wrong answer The intuitive way to compare "compiled hours" against "24 hours" is to take the trade list from one run and drop the trades outside the window. That is not equivalent, because the hour gate changes which trades exist at all.

A slot blocked during its off-hours window does not simply skip those trades — it also stays flat through them, which means it is available to take a different trade later. The whole subsequent trade sequence diverges. Filtering a list of trades cannot reproduce a sequence that was never generated.

So the report performs an exact replay: one forward pass per slot for each hour configuration, using the replay machinery in lr_hours_engine.py. It is slower, and it is the honest comparison.

How the exact replay is possible at all

It rests on a structural property of the engine, stated earlier in The LR Engine: the hour filter gates entries only. Training reads its own separate mask, and online learning uses the bar's own forward return rather than position P&L.

The consequence is that the weight trajectory is identical for every hour subset. The model learns the same things regardless of which hours it is allowed to trade. So a single pass that records raw entry signals yields every possible hour-subset result exactly, with no re-training — and that is what makes the comparison both exhaustive and fast enough to include in a report.

Replay details A signal is skipped if the slot is still busy (b < last_exit) or if its hour is not allowed. Exits resolve stop before target intrabar, matching the engine's conservative assumption. An unresolved position closes on the last bar at close with reason "EOData". The replay is validated against real hour-filtered backtests during development — it reports EXACT MATCH when the maximum absolute difference is under $0.01, and *** MISMATCH — do not use *** otherwise.

The Reports page

Generate each report
Individual buttons per report, with the command shown before it runs.
File list
Every generated report with its size and timestamp. Multi-megabyte is normal — these are single-document HTML with inline SVG.
Open in browser
Opens the file directly.
Reveal in folder
For attaching to an email or archiving.
Size warning
Flags files over ~4 MB, which is normal for the delivery report but slow on an old machine.

Which report answers which question

You want to know…Read
Which feature sets contributed most?report.html — per-set table.
Is the book concentrated in one slot?report.html or the delivery report's per-slot section.
Did the strategy survive unseen data?tail_holdout_report.html.
Does the session filter actually help?Delivery report, section 5 — the exact replay.
What cost assumption is baked in?Delivery report, section 1 — median spread and cost basis.
Should I trade this?Delivery report — read section 5 then section 6, in that order.

A suggested reading order for the delivery report

Cost basis first
If the assumed spread is optimistic relative to your account, everything after it is optimistic too. Settle this before reading any performance number.
Then the per-slot curves
Look for nine genuinely different shapes. Identical shapes mean the book is one position at nine times the size.
Then the 24h-vs-window replay
This tells you whether the session filter earns its complexity. If 24-hour performance is materially the same, ship the simpler build.
Then the holdout, last
Read it after you understand the in-sample picture, so you can see how much was curve-fitting.
Regenerate reports after any change Reports are generated from the artefacts on disk. If you re-run the search, or edit the data, an old report describes a run that no longer exists. The pipeline writes them fresh each time; the staleness checks elsewhere in the app exist for exactly this reason.

Checkpoint

End of the pipeline
  • All three reports generated without error.
  • You have read the cost basis and accepted the spread assumption.
  • You have read the 24h-vs-window replay and decided which build to ship.
  • You have read the holdout and it is either positive, or you have consciously accepted a warm-only edge.
  • The delivery report is archived alongside the data it describes.
SymptomCauseFix
Report shows no data / empty sections The upstream artefacts are missing — the report reads summaries, not in-memory state. Confirm the search, build and holdout all ran for this symbol and timeframe.
Report takes a very long time The delivery report's exact replay re-runs one forward pass per slot per hour configuration. Expected. Use the lighter reports while iterating; build the delivery report when you are ready to decide.
Numbers differ from what I saw in the app Either the report is older than the run, or you are comparing in-sample against tail figures. Regenerate, and check which section you are reading.
Excel or the browser shows an old version Aggressive caching of a local file. Hard-refresh (Ctrl+F5), or archive the file with a dated name.

What now?

The pipeline is complete. You have a compiled EA, a holdout result, and reports that describe both. If the holdout is positive in both modes, the next step is forward-testing on a demo account — because a single tail window is one market regime, not a guarantee.