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 ✓
94 lines
4.3 KiB
HTML
94 lines
4.3 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Reports — MT5 Optimizer</title>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
<style>
|
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
body { background: #0a0d14; color: #e2e8f8; font-family: 'Inter', sans-serif; font-size: 13px; padding: 32px; }
|
|
header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 32px; }
|
|
h1 { font-size: 22px; font-weight: 800; }
|
|
.sub { color: #6b7fa3; font-size: 12px; margin-top: 4px; }
|
|
.back-link { color: #4f8ef7; text-decoration: none; font-size: 12px; }
|
|
.back-link:hover { text-decoration: underline; }
|
|
.empty { color: #3d4f70; text-align: center; padding: 80px 0; font-size: 16px; }
|
|
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 16px; }
|
|
.run-card {
|
|
background: #111621; border: 1px solid #1e2740; border-radius: 12px; padding: 18px 20px;
|
|
transition: border-color 0.2s, transform 0.2s;
|
|
text-decoration: none; color: inherit; display: block;
|
|
}
|
|
.run-card:hover { border-color: #4f8ef7; transform: translateY(-2px); }
|
|
.run-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px; }
|
|
.run-id { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: #6b7fa3; }
|
|
.run-score { font-size: 28px; font-weight: 800; font-family: 'JetBrains Mono', monospace; color: #22d3a5; }
|
|
.run-phase { font-size: 10px; padding: 2px 8px; border-radius: 4px; background: rgba(79,142,247,0.15); color: #4f8ef7; font-weight: 700; text-transform: uppercase; }
|
|
.metrics-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 10px; }
|
|
.m { text-align: center; background: #161c2a; border-radius: 6px; padding: 6px 8px; }
|
|
.m-label { font-size: 9px; color: #6b7fa3; text-transform: uppercase; letter-spacing: 0.5px; }
|
|
.m-val { font-size: 14px; font-weight: 700; font-family: 'JetBrains Mono', monospace; margin-top: 2px; }
|
|
.delta-up { color: #22d3a5; } .delta-dn { color: #f44; }
|
|
.run-ts { font-size: 10px; color: #3d4f70; margin-top: 10px; font-family: 'JetBrains Mono', monospace; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<div>
|
|
<h1>📁 Optimization Reports</h1>
|
|
<div class="sub">{{ runs|length }} run(s) recorded · Click any card to open the full report</div>
|
|
</div>
|
|
<a class="back-link" href="/dashboard">← Back to Dashboard</a>
|
|
</header>
|
|
|
|
{% if not runs %}
|
|
<div class="empty">No reports yet.<br>Start the optimizer to generate your first run report.</div>
|
|
{% else %}
|
|
<div class="cards">
|
|
{% for r in runs %}
|
|
<a class="run-card" href="/reports/{{ r.run_id }}/summary.html" target="_blank">
|
|
<div class="run-header">
|
|
<div>
|
|
<div class="run-id">{{ r.run_id }}</div>
|
|
<div class="run-score">{{ "%.4f"|format(r.score) }}</div>
|
|
</div>
|
|
<span class="run-phase">{{ r.phase }}</span>
|
|
</div>
|
|
<div class="metrics-row">
|
|
<div class="m">
|
|
<div class="m-label">Net Profit</div>
|
|
<div class="m-val {% if r.net_profit >= 0 %}delta-up{% else %}delta-dn{% endif %}">
|
|
${{ "%.0f"|format(r.net_profit) }}
|
|
</div>
|
|
</div>
|
|
<div class="m">
|
|
<div class="m-label">Calmar</div>
|
|
<div class="m-val">{{ "%.2f"|format(r.calmar) }}</div>
|
|
</div>
|
|
<div class="m">
|
|
<div class="m-label">Drawdown</div>
|
|
<div class="m-val delta-dn">{{ "%.1f"|format(r.drawdown_pct) }}%</div>
|
|
</div>
|
|
<div class="m">
|
|
<div class="m-label">PF</div>
|
|
<div class="m-val">{{ "%.2f"|format(r.profit_factor) }}</div>
|
|
</div>
|
|
<div class="m">
|
|
<div class="m-label">Trades</div>
|
|
<div class="m-val">{{ r.total_trades }}</div>
|
|
</div>
|
|
<div class="m">
|
|
<div class="m-label">Score Δ</div>
|
|
<div class="m-val {% if r.score_delta >= 0 %}delta-up{% else %}delta-dn{% endif %}">
|
|
{{ "%+.4f"|format(r.score_delta) }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="run-ts">{{ r.ts[:19].replace("T"," ") }} UTC</div>
|
|
</a>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</body>
|
|
</html>
|