Step by step

The Full Process

Seven stages, strictly ordered. This page is the map: what each stage consumes, what it produces, the exact command it runs, and what to check before moving on.

Order is not a suggestion

Each stage reads the previous stage's artefacts. That is why the chain stops at the first non-zero exit rather than pressing on: continuing after a failed search would build a portfolio out of whatever configs were already on disk — probably yesterday's — and every subsequent number would be plausible and meaningless.

DataCSV in data/
Search11 × summary.json
Portfolioall9/summary.json
Hoursbest_hours.json
Holdouttail_report.json
EA.mq5 + .ex5
ReportsHTML

The seven stages

Data — get bars in, and prove they are usable

A broker-exported or API-downloaded OHLC CSV, validated against the engine's assumptions before any CPU is spent on it.

Consumes: nothing (the terminal, or a file you already have).
Produces: data/<SYMBOL>_<TF>.csv.

check_ohlc_csv.py data/XAUUSD_M5.csv

Full detail →

Feature search — the CPU-heavy stage

A two-stage grid search per feature set: 64 trade-geometry combinations, then 243 training-hyper-parameter combinations, then the top 40 candidates re-verified at the full 2,000-iteration budget. Survivors are filtered by an equity-correlation test down to 5 slots per set.

Consumes: the CSV.
Produces: strategies_lr_m5/<set>/ with summary.json, configs.json, slots.txt, equity_<magic>.csv.

feature_search.py --engine lr --symbol XAUUSD --tf M5 --set all --cores 14
--set accepts a single set name or the literal all. all9 is not valid and fails with Unknown set. Group selections are expanded by the GUI into one process per set.

Full detail →

Portfolio — combine, and check for concentration

Takes the best slot from each selected feature set, re-simulates all of them at the full verification budget, and sums their equity onto a shared bar grid.

Consumes: every <set>/summary.json.
Produces: strategies_lr_m5/all9/ with the combined curve and each slot's equity.

build_allstar.py --engine lr --symbol XAUUSD --tf M5 --all9 --cores 14
The interesting output is not the headline net — it is the composition. Nine variations of one edge is not diversification.

Full detail →

Trading hours — the session filter

For each feature set, finds the contiguous cyclic block of server hours that maximised in-sample P&L, then re-scores that winner on the half of the data it was not chosen on.

Consumes: each set's best slot.
Produces: strategies_lr_m5/best_hours.json plus per-set trades.csv.

best_hours.py --engine lr --symbol XAUUSD --tf M5 --cores 14
A window with a big total and a negative out-of-sample half is an artefact of the search space, not a session edge. Only windows positive in both halves are worth compiling.

Full detail →

Tail holdout — the honest number

Re-simulates the frozen portfolio on the last N bars only, where nothing was ever selected against. Run twice: warm (model carried its learned state into the tail) and cold (model started from scratch at the boundary).

Consumes: all9/summary.json.
Produces: all9/tail2500_warm/ and …_cold/ with tail_report.json and per-slot trades.

holdout_tail.py --symbol XAUUSD --tf M5 --folder all9 --mode warm --tail 2500 --cores 14
holdout_tail.py --symbol XAUUSD --tf M5 --folder all9 --mode cold --tail 2500 --cores 14
This is the only number in the system that was not used to choose anything. If both modes are negative, stop — do not compile.

Full detail →

EA — generate MQL5, compile, deploy

Emits compile-ready MQL5 that mirrors the engine exactly — the same feature expressions, the same lam = 0.001, the same login per slot — then drives MetaEditor and copies the binary into the terminal.

Consumes: the portfolio's summary.json + configs.json, and best_hours.json.
Produces: FxMath_AST_allstar_ALL9_M5.mq5 and its compiled .ex5.

gen_ea.py --engine lr --symbol XAUUSD --tf M5 --all9

# the 24-hour variant, tagged so it cannot overwrite the above
gen_ea.py --engine lr --symbol XAUUSD --tf M5 --all9 --no-hours --tag 24X
MetaEditor returns exit code 1 even on a clean compile. Success is judged from the .ex5 plus 0 errors in the log — never from the exit code.

Full detail →

Reports — the deliverables

Three self-contained HTML files, each answering a different question. The delivery report is the one to hand to a human.

Consumes: everything above.
Produces: report.html, tail_holdout_report.html, report_m5_performance.html.

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

Full detail →

The whole thing as one command

Full pipeline → Run everything builds the same ordered task list. These are the exact command lines it produces, in order:

# 1  feature search (one process, loops all 11 sets itself)
feature_search.py --engine lr --symbol XAUUSD --tf M5 --set all --cores N

# 2  portfolio
build_allstar.py --engine lr --symbol XAUUSD --tf M5 --all9 --cores N

# 3  trading hours
best_hours.py --engine lr --symbol XAUUSD --tf M5 --cores N

# 4  holdout, warm then cold
holdout_tail.py --symbol XAUUSD --tf M5 --folder all9 --mode warm --tail 2500 --cores N
holdout_tail.py --symbol XAUUSD --tf M5 --folder all9 --mode cold --tail 2500 --cores N

# 5  generate the EA
gen_ea.py --engine lr --symbol XAUUSD --tf M5 --all9

# 6  reports
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 --json logs/m5_broker_run.json

Note that the search is one process here, not eleven. The script loops all eleven sets itself, which is both the cheapest option and what the progress bar tracks. Group selections such as all9 or core6 are the ones that expand into one process per set.

Data is deliberately not part of the chain Fetching bars can require interaction with a terminal or credentials, so the chain starts at the search. Validate your CSV on the Data page first — the chain assumes data/<SYMBOL>_<TF>.csv already exists and is good.

Where everything lands

StageArtefactPath (relative to the repo root)
DataOHLC CSVdata/XAUUSD_M5.csv
SearchPer-set summarystrategies_lr_m5/<set>/summary.json
SearchSlot parametersstrategies_lr_m5/<set>/configs.json
SearchHuman-readable slotsstrategies_lr_m5/<set>/slots.txt
SearchPer-slot equitystrategies_lr_m5/<set>/equity_<magic>.csv
PortfolioCombined bookstrategies_lr_m5/all9/summary.json
PortfolioCombined curvestrategies_lr_m5/all9/equity_combined.csv
HoursWindowsstrategies_lr_m5/best_hours.json
HoldoutTail resultsstrategies_lr_m5/all9/tail2500_warm/tail_report.json
EAMQL5 sourcestrategies_lr_m5/all9/FxMath_AST_allstar_ALL9_M5.mq5
EACompiled binarystrategies_lr_m5/all9/FxMath_AST_allstar_ALL9_M5.ex5
ReportsDelivery reportstrategies_lr_m5/report_m5_performance.html

Full schemas and naming rules →

Checkpoints between stages

Each stage has one thing worth confirming before you spend time on the next. These are the checks that catch a wasted run early.

After…ConfirmWhy
Data Validator says no problems found; median spread is non-zero A zero median spread makes every report charge $0 cost and every result look better than reality.
Search ALL SETS DONE, and each set reported 5 verified slots A set that found nothing produces an empty portfolio slot later, silently changing the book size.
Portfolio No single slot dominates net or drawdown Nine correlated slots are one slot with nine names.
Hours Windows are positive in both halves, or plan for 24h A negative out-of-sample half means the window is curve-fitted.
Holdout Warm and cold are both positive This is the only out-of-sample evidence you have. If it is negative, the EA has no demonstrated edge.
EA 0 errors, 0 warnings and no STALE badge A stale .ex5 is the single easiest way to backtest the wrong EA.

How long it takes

The search dominates everything else. On a large machine with the full 11 sets at a 2,000-iteration verification, the search is the better part of an hour; the remaining stages together are minutes. Rough shape:

StageRelative costNotes
DataSecondsNetwork-bound, not CPU-bound.
SearchDominant307 configurations per set × 11 sets, then 40 re-verified at full budget each. Runs a process pool.
PortfolioMinutesOne full-budget re-simulation per slot — 9 slots.
HoursMinutesOne full-budget run per set.
HoldoutMinutesTwo runs (warm, cold) over the tail plus warmup.
EASecondsCode generation; MetaEditor compile is fast.
ReportsMinutesThe delivery report is slowest — it re-runs one forward pass per slot for the exact 24h-vs-window replay.

Cores matter almost exclusively for the search. Setting cores to the machine's core count minus one or two is the usual choice — the app defaults to cpu_count − 2 so the UI and OS keep a core.

Reproducing a run by hand

Everything the app does is reproducible from a shell. This is intentional: the Output panel shows each command, and the fresh artefacts land in the same paths. A full manual reproduction:

# from the repository root, with any python that has numpy + pandas
python scripts/feature_search.py --engine lr --symbol XAUUSD --tf M5 --set all --cores 14
python scripts/build_allstar.py  --engine lr --symbol XAUUSD --tf M5 --all9 --cores 14
python scripts/best_hours.py     --engine lr --symbol XAUUSD --tf M5 --cores 14
python scripts/holdout_tail.py   --symbol XAUUSD --tf M5 --folder all9 --mode warm --tail 2500 --cores 14
python scripts/holdout_tail.py   --symbol XAUUSD --tf M5 --folder all9 --mode cold --tail 2500 --cores 14
python scripts/gen_ea.py         --engine lr --symbol XAUUSD --tf M5 --all9
python scripts/report_m5.py      --symbol XAUUSD --tf M5 --port all9
python scripts/report_tail.py    --symbol XAUUSD --tf M5 --tail 2500

The app will pick all of it up on the next refresh, because it reads state from disk rather than from memory.