PROBLEM: Old system repeated identical params, all scores flat at 0.2500,
user had zero control over symbol/TF/dates. Not smart, not dynamic.
NEW ARCHITECTURE:
optimizer/ (NEW package)
├── __init__.py
├── session_config.py User choices (EA, symbol, TF, dates, budget, objective)
├── lhs_sampler.py Latin Hypercube Sampling — diverse exploration
├── result_ranker.py Relative scoring (best in session=1.0, worst=0.0)
├── budget.py Time budget tracker
└── pipeline.py 3-phase orchestrator
Phase 1 — Broad Discovery (LHS, 20-26 runs):
Samples FULL parameter space, not just defaults±tiny step
LHS guarantees coverage: all 17 optimizable LEGSTECH params explored
Relative ranking: profitable configs float top, losers score 0
Phase 2 — Refinement (9 runs):
Neighbor search around top 3 configs at ±20% range (not ±0.5 step)
Keeps best of Phase1 vs Phase2 — never regresses
Phase 3 — Validation (5 runs):
OOS backtest on unseen data period
Sensitivity test: nudge params ±20%, detect fragility
Verdict: RECOMMENDED / RISKY / NOT_RELIABLE
Output: Clean downloadable .set file via /download_set/<run_id>
UI REDESIGN:
ui/templates/landing.html New / homepage (was old dashboard)
ui/templates/setup.html New /setup — EA, symbol, TF, dates, budget, objective
ui/templates/dashboard.html Updated /dashboard with:
- 5-step phase indicator
- Real progress bar per run
- Phase 1 results table (top 5 after phase1)
- Verdict banner with download button
- No-profitable-config warning
ui/static/js/dashboard.js Handles 8 new pipeline SocketIO events
app.py New routes: /, /setup, /dashboard
/api/start accepts full SessionConfig JSON
/download_set/<id> serves optimized .set
ea/registry.py +list_all() for setup page dropdown
ui/templates/reports_index.html Back to Dashboard → /dashboard (was /)
ui/static/css/style.css +dot-warn, dot-done, profit-pos/neg, aliases
FIXES:
Score no longer flat 0.2500 (was: absolute thresholds on losing EA)
User now controls: symbol, timeframe, dates, budget, objective
Parameters now span full range (was: tiny step from defaults)
Verdict is actionable: RECOMMENDED / RISKY / NOT_RELIABLE with reason
TESTED:
8/8 pre-flight checks pass
Browser test: landing ✓, setup form ✓, /dashboard ✓,
phase indicator active ✓, /reports ✓, back link ✓
Root cause: Removing ea: from config.yaml broke 4 call sites in
optimizer_loop.py (_execute_run, _build_components) and main.py that
still read cfg['ea']. Server started but MT5 never launched.
Fixes:
config.yaml - Restored ea: block as bridge for legacy callers
optimizer_loop.py - _build_components: loads EARegistry, creates
IniBuilder(schema=schema) instead of manifest path
_execute_run: uses self._profile.{name,symbol,tf}
instead of cfg['ea']; passes profile to runner.run()
mt5/runner.py - Validation + TradeLog now use EAProfile data when
profile is provided (legacy fallback preserved)
Tested:
7/7 pre-flight checks pass (config, registry, schema, IniBuilder, runner,
MutationEngine, OptimizerLoop._build_components)
Live browser test: Start clicked → MT5 launched → logs appeared → Reports
page loaded (9 cards) → Back to dashboard navigated in-tab
System fully operational
1. CRITICAL FIX - Reports 500 Internal Server Error:
Root cause: {{}} in onclick was Jinja2 template expression
Fix: Changed to single {} in reports_index.html onclick
2. Score history chart always empty:
Root cause: score_update only emitted inside wfv.passed block
Fix: Emit score_update after every hypothesis run with {promoted:false}
Fix: Baseline run also pushes to chart via run_complete handler
3. IS gate too strict - optimizer produces 0 candidates forever:
Root cause: min_calmar=0.35 but best EA has calmar=0.165
Fix: Two-tier IS check - passes if composite_score improves vs baseline
(absolute thresholds still apply as alternative pass condition)
4. Findings emitted twice (double UI display):
Root cause: baseline analyzed once after run, then re-analyzed in iter 1
Fix: Cache baseline findings, reuse in iteration 1 without re-emitting
5. Chart labels improved:
'Baseline' for first point, 'It1·h1' for hypothesis runs
Calmar normalized -0.5..2.0 -> 0..1 for chart display
6. Best score header only updates on actual promotion (not per-hypothesis)
Reports 500 (Internal Server Error):
- app.py: Replace NaN/Infinity->null before json.loads() in both
/reports and /api/runs routes. Old summary.json files with invalid
NaN are now handled transparently.
- reports/writer.py: Added _safe_val() helper using math.isfinite().
All summary.json values now use safe rounding (NaN->0, Inf->0).
No new hypotheses (optimizer stops after 1 iter):
- optimizer_loop.py: Changed dedup from DB-wide history to
session-scoped (self.session_tested_deltas). Previous runs from
old sessions no longer block new hypothesis proposals.
- Each tested hypothesis is tracked within the session only.
Result: optimizer now runs multiple iterations, scores are valid
numbers, reports page loads without 500.
- NaN composite score: dropna().mean() on empty series returns NaN,
nan is truthy so (nan or 0)=nan. Fixed: use None when no MFE data
so scorer uses 0.5 fallback instead of propagating NaN.
- Analysis stopping: gate WFV was importing from main.execute_run
(old CLI code that uses broken runner). Fixed: gate.run_walk_forward
now accepts an executor callable from optimizer_loop.
- Back to Dashboard opened blank tab: Reports button used
window.open(_blank). Fixed: same-tab navigation + history.back().
- NaN in reports card: downstream of composite_score NaN above.
- Added GitHub URL
- Documented all 6 critical bugs found and fixed:
* UTF-16 LE report encoding
* Report location (appdata root not /reports/)
* INI Period string format (H1 not 16385)
* INI Report relative path
* MQL5 reference syntax
* Stale report detection
- Added full SocketIO events table
- Added baseline run results (597 trades confirmed parsed)
- Updated file structure with all Phase 2 files
- Updated next steps to Phase 3 (PyInstaller packaging)