feat: brand UI with real APEX logo + auto-running showcase demo

UI:
- Embed the real APEX logo PNG (transparent mark) in dashboard sidebar,
  cmd-header, landing nav, and landing hero (replaces SVG approximations).
- Add favicon.png across all six templates.
- Nudge color palette to brand cyan→blue→purple gradient and silver-white
  wordmark; word-by-word coloring on the cmd-header tagline.
- Drop-shadow glow on logo marks tuned to the brand-blue.

Demo:
- python -m demo.run_demo now defaults to an auto-running showcase: opens
  the dashboard, kicks off a ~3-4 min optimization that exercises every
  phase, and lands on a verdict modal — no manual setup needed.
- Tight per-iteration targets so Phase 2 actually iterates (visible AI loop).
- Skip per-run AI analysis during Phase 1 exploration via APEX_DEMO_SKIP_PHASE1_AI=1
  to keep total runtime down without losing the headline AI loop in Phase 2.
- --quick flag opts back to the original fast/manual flow.
- --loop auto-restarts for unattended screen recording.

Docs:
- README + SUBMISSION updated to describe the new auto-running default.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
LEGSTECH Optimizer
2026-04-25 23:34:25 +00:00
committed by LEGTECH
co-authored by Claude Opus 4.7
parent 96680cde63
commit 33e670aa4c
14 changed files with 295 additions and 127 deletions
+19 -5
View File
@@ -117,16 +117,30 @@ hit **Start**, and watch the AI think.
### Demo mode (no MT5 required) ### Demo mode (no MT5 required)
Don't have MT5 installed? Run the offline demo that feeds synthetic backtest results Don't have MT5 installed? The offline demo feeds synthetic backtest results
through the same AI loop and dashboard: through the same AI loop and dashboard. **It auto-starts** — open the link
the script prints, sit back, and watch APEX think:
```bash ```bash
python -m demo.run_demo python -m demo.run_demo
``` ```
This is the path to use if you're a hackathon judge — you'll see the full thinking feed, That's it. The browser opens to the live dashboard, an optimization kicks off
parameterchange panel, validation phase, and verdict screen without needing a Windows automatically, and you'll see all three phases play out over ~3-4 minutes:
machine with MT5. exploration → AI iteration with reasoning streaming live → out-of-sample +
sensitivity validation → final verdict. Every panel populates so you can see
exactly what the system does.
If you'd rather drive it yourself (configure your own EA, dates, targets), use:
```bash
python -m demo.run_demo --quick # boots the server, you click "New Run"
python -m demo.run_demo --loop # auto-restart between runs (unattended recording)
```
This is the path to use if you're a hackathon judge — you'll see the full
thinking feed, parameter-change panel, validation phase, and verdict screen
without needing a Windows machine with MT5.
--- ---
+3 -2
View File
@@ -81,8 +81,9 @@ https://github.com/tonnylegacy/MT5_Optimizer
No hosted demo (the app runs locally to drive a local MT5 install). For judges: No hosted demo (the app runs locally to drive a local MT5 install). For judges:
- **Static**: open `screenshots/apex_demo.gif` in the repo (6frame timelapse of one autonomous run) - **Static**: open `screenshots/apex_demo.gif` in the repo (6frame timelapse of one autonomous run)
- **Run it**: `git clone … && pip install -r requirements.txt && python -m demo.run_demo`opens at `http://localhost:5000` with synthetic backtests, no MT5 required - **Run it**: `git clone … && pip install -r requirements.txt && python -m demo.run_demo`the browser opens to the dashboard, a ~3-4 minute optimization auto-starts, and every phase (exploration → AI iteration → validation → verdict) plays out without you touching anything
- **With API key**: set `ANTHROPIC_API_KEY` env var to see live Claude reasoning stream into the Thinking Feed - **With API key**: set `ANTHROPIC_API_KEY` env var (or fill `ai.anthropic_api_key` in `config.yaml`) to see live Claude reasoning stream into the Thinking Feed
- **Faster / your-own-config**: `python -m demo.run_demo --quick` to skip the auto-run and drive the demo from `/setup` yourself
--- ---
+194 -23
View File
@@ -7,16 +7,20 @@ generates synthetic backtest results instead of calling MT5. Lets judges
without a Windows + MT5 install see the full live AI loop, validation, without a Windows + MT5 install see the full live AI loop, validation,
and verdict flow. and verdict flow.
By default, the demo **auto-runs**: it opens the dashboard in your browser
and immediately starts a ~3-4 minute optimization showcasing every phase
(exploration AI iteration validation verdict). No manual setup needed.
Usage: Usage:
python -m demo.run_demo python -m demo.run_demo # auto-running showcase (default, ~3-4 min)
# or python -m demo.run_demo --quick # original fast/manual demo (you click "New Run")
python demo/run_demo.py python -m demo.run_demo --loop # auto-restart on completion (for unattended recording)
""" """
from __future__ import annotations from __future__ import annotations
import argparse
import os import os
import sys import sys
import textwrap
from pathlib import Path from pathlib import Path
import yaml import yaml
@@ -83,24 +87,159 @@ def ensure_config() -> None:
print(f" [!] No config.yaml or config.example.yaml — app may fail to start") print(f" [!] No config.yaml or config.example.yaml — app may fail to start")
def banner() -> None: def banner(quick: bool = False, loop: bool = False) -> None:
bar = "=" * 72 bar = "=" * 72
print(textwrap.dedent(f""" if quick:
{bar} mode_line = "APEX -- DEMO MODE (quick / manual)"
APEX -- DEMO MODE (offline / no MT5) body = (
{bar} " * Backtests are synthetic; AI reasoning is real if API key is set\n"
* Backtests are synthetic (deterministic from params + jitter) " * Per-run latency is short for fast UI testing\n\n"
* The AI loop, validation, and verdict flow are 100% real ' Open http://localhost:5000 in your browser, hit "New Run", and\n'
* Set ANTHROPIC_API_KEY to see live AI reasoning " configure your own optimization."
)
else:
suffix = " [LOOP]" if loop else ""
mode_line = f"APEX -- DEMO MODE (auto-running showcase){suffix}"
body = (
" * Auto-starts a ~3-4 min optimization showing every phase\n"
" * AI reasoning streams live (API key loaded from env or config.yaml)\n"
" * The dashboard opens itself; just sit back or hit your recorder hotkey\n"
" * Heads up: AI API calls add ~12-15s each — actual run can stretch\n"
" to ~5 min depending on Claude latency.\n"
" * To skip the auto-start and configure your own run: --quick"
)
print(f"\n{bar}\n{mode_line.center(72)}\n{bar}\n{body}\n{bar}\n")
Open http://localhost:5000 in your browser, hit "New Run", and
watch the AI think. # ── Showcase auto-start config ──────────────────────────────────────────────
{bar} # Tuned for ~2.5-3 min runtime that actually shows the AI loop iterating:
""")) # * Phase 1 (10 LHS samples × ~2.5s, no AI) ≈ 25-30s
# * Phase 2 (5 AI iterations × ~15-18s incl. analyze + suggest_next_params)
# ≈ 75-90s — targets are deliberately STIFF so the loop can't early-exit
# at iteration 0; Phase 1's best typically lands around PF≈1.9 / Calmar≈1
# which is below these targets, so the AI gets to actually do its job.
# * Phase 3 (1 OOS + 3 sensitivity, each with AI analyze) ≈ 50-60s
# AI call latency dominates the math; the synthetic backtest delay is short.
CINEMATIC_PAYLOAD = {
"ea_name": "APEX_DEMO_EA",
"symbol": "XAUUSD",
"timeframe": "H1",
"train_start": "2022.01.01",
"train_end": "2023.12.31",
"val_start": "2024.01.01",
"val_end": "2024.06.30",
"objective": "balanced",
"budget_minutes": 15,
"autonomous_mode": True,
"autonomous_max_iterations": 5,
"target_profit_factor": 2.5,
"target_max_drawdown_pct": 5.0,
"target_min_calmar": 1.5,
"selected_params": [],
}
def _autostart_cinematic_run(loop: bool = False) -> None:
"""
Background thread: waits for the server to be up, then POSTs /api/start
with cinematic settings. If --loop, polls /api/status and re-triggers
when the pipeline goes idle (so an unattended recording keeps producing
fresh footage).
"""
import json
import time
import urllib.error
import urllib.request
base = "http://127.0.0.1:5000"
def _server_ready() -> bool:
try:
with urllib.request.urlopen(f"{base}/api/status", timeout=2) as r:
return r.status == 200
except Exception:
return False
def _is_running() -> bool:
try:
with urllib.request.urlopen(f"{base}/api/status", timeout=2) as r:
data = json.loads(r.read())
state = (data.get("state") or "").lower()
return state in ("running", "starting")
except Exception:
return False
def _post_start() -> bool:
body = json.dumps(CINEMATIC_PAYLOAD).encode("utf-8")
req = urllib.request.Request(
f"{base}/api/start", data=body,
headers={"Content-Type": "application/json"}, method="POST",
)
try:
with urllib.request.urlopen(req, timeout=5) as r:
resp = json.loads(r.read())
return bool(resp.get("ok"))
except Exception as e:
print(f" [cinematic] /api/start failed: {e}")
return False
# Wait for server to come up (~10 seconds max)
for _ in range(40):
if _server_ready():
break
time.sleep(0.25)
else:
print(" [cinematic] server didn't become ready — aborting auto-start")
return
# Small grace period so the dashboard tab finishes connecting via SocketIO
time.sleep(2.0)
while True:
print(" [cinematic] starting optimization run …")
if not _post_start():
print(" [cinematic] failed to start — retrying in 10s")
time.sleep(10)
continue
# Wait for run to complete (poll until idle for 3 consecutive checks)
idle_streak = 0
while idle_streak < 3:
time.sleep(4)
if _is_running():
idle_streak = 0
else:
idle_streak += 1
if not loop:
print(" [cinematic] run complete — staying on verdict screen")
return
print(" [cinematic] run complete — restarting in 12s for next loop iteration")
time.sleep(12)
def main() -> int: def main() -> int:
banner() parser = argparse.ArgumentParser(
prog="demo.run_demo",
description="APEX offline demo runner.",
)
parser.add_argument(
"--quick", action="store_true",
help="Skip the auto-start showcase. Server boots with fast per-run latency "
"and you drive the demo manually from /setup.",
)
parser.add_argument(
"--loop", action="store_true",
help="Auto-restart a fresh showcase run when each one completes (for unattended "
"screen recording). Ignored with --quick.",
)
parser.add_argument(
"--per-run-seconds", type=float, default=None,
help="Override APEX_DEMO_RUN_SECONDS (default: 5.0 showcase / 1.2 --quick).",
)
args = parser.parse_args()
banner(quick=args.quick, loop=args.loop)
print("Bootstrapping demo environment...") print("Bootstrapping demo environment...")
ensure_config() ensure_config()
@@ -108,14 +247,35 @@ def main() -> int:
# Set the demo flag — the pipeline checks this in _execute_run. # Set the demo flag — the pipeline checks this in _execute_run.
os.environ["APEX_DEMO_MODE"] = "1" os.environ["APEX_DEMO_MODE"] = "1"
# Per-run latency tunable — keep small so demo feels snappy. # Per-run latency tunable. Default (auto-running showcase) is slower so
os.environ.setdefault("APEX_DEMO_RUN_SECONDS", "1.2") # each phase is visible long enough to film and narrate over.
if args.per_run_seconds is not None:
os.environ["APEX_DEMO_RUN_SECONDS"] = str(args.per_run_seconds)
elif args.quick:
os.environ.setdefault("APEX_DEMO_RUN_SECONDS", "1.2")
else:
# 2.5s synthetic delay; AI call latency dominates total runtime anyway
os.environ.setdefault("APEX_DEMO_RUN_SECONDS", "2.5")
# Skip per-run AI analysis during Phase 1 exploration so the demo
# finishes in ~3-4 min. Phase 2's autonomous AI loop (the headline
# feature) still streams reasoning live.
os.environ.setdefault("APEX_DEMO_SKIP_PHASE1_AI", "1")
if "ANTHROPIC_API_KEY" not in os.environ: # AI reasoner pulls from env var OR config.yaml ai.anthropic_api_key
print(" [!] ANTHROPIC_API_KEY not set — AI reasoning will be skipped (synthetic metrics still flow).") cfg_has_key = False
try:
cfg = yaml.safe_load(CONFIG.read_text()) if CONFIG.exists() else {}
cfg_has_key = bool(((cfg or {}).get("ai") or {}).get("anthropic_api_key", "").strip())
except Exception:
pass
if not (os.environ.get("ANTHROPIC_API_KEY", "").strip() or cfg_has_key):
print(" [!] No API key in env (ANTHROPIC_API_KEY) or config.yaml — AI reasoning will be skipped.")
else:
src = "config.yaml" if cfg_has_key else "env var"
print(f" [ok] API key found in {src} — AI reasoning enabled.")
print() print()
print("Launching APEX server at http://localhost:5000 ...") print(f"Launching APEX server at http://localhost:5000 (per-run: {os.environ['APEX_DEMO_RUN_SECONDS']}s) ...")
sys.path.insert(0, str(ROOT)) sys.path.insert(0, str(ROOT))
# Import after env vars are set so the pipeline picks them up. # Import after env vars are set so the pipeline picks them up.
import threading import threading
@@ -126,11 +286,22 @@ def main() -> int:
import time as _t import time as _t
_t.sleep(1.5) _t.sleep(1.5)
try: try:
webbrowser.open("http://localhost:5000") # In auto-running mode jump straight to the dashboard so the user sees
# the live run unfold; in --quick mode land on the landing page.
url = "http://localhost:5000" if args.quick else "http://localhost:5000/dashboard"
webbrowser.open(url)
except Exception: except Exception:
pass pass
threading.Thread(target=_open_browser, daemon=True).start() threading.Thread(target=_open_browser, daemon=True).start()
if not args.quick:
threading.Thread(
target=_autostart_cinematic_run,
kwargs={"loop": args.loop},
daemon=True,
).start()
socketio.run( socketio.run(
flask_app, host="0.0.0.0", port=5000, flask_app, host="0.0.0.0", port=5000,
debug=False, use_reloader=False, allow_unsafe_werkzeug=True, debug=False, use_reloader=False, allow_unsafe_werkzeug=True,
+11 -5
View File
@@ -898,11 +898,17 @@ class OptimizationPipeline:
delay = float(os.environ.get("APEX_DEMO_RUN_SECONDS", "1.5")) delay = float(os.environ.get("APEX_DEMO_RUN_SECONDS", "1.5"))
time.sleep(max(0.05, delay)) time.sleep(max(0.05, delay))
# Run AI reasoning if enabled — same flow as live mode # Run AI reasoning if enabled — same flow as live mode.
try: # In showcase mode (APEX_DEMO_SKIP_PHASE1_AI=1) we skip Phase 1's
self._reason_about_run(run_id, metrics, [], params) # per-run AI analysis to keep total runtime to ~3-4 min. The AI loop
except Exception: # in Phase 2 — where reasoning actually drives parameter changes — is
pass # untouched, so the headline feature is still visible.
skip_p1_ai = os.environ.get("APEX_DEMO_SKIP_PHASE1_AI", "").strip() in ("1", "true", "yes")
if not (skip_p1_ai and phase.startswith("phase1")):
try:
self._reason_about_run(run_id, metrics, [], params)
except Exception:
pass
return ranker.make_result(run_id, params, phase, metrics) return ranker.make_result(run_id, params, phase, metrics)
Binary file not shown.

After

Width:  |  Height:  |  Size: 1008 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 KiB

+42 -56
View File
@@ -4,6 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>APEX — AI-Powered EA Optimizer</title> <title>APEX — AI-Powered EA Optimizer</title>
<link rel="icon" type="image/png" href="/static/img/favicon.png">
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
@@ -16,16 +17,19 @@
--card-hover: #131d35; --card-hover: #131d35;
--border: rgba(255,255,255,0.07); --border: rgba(255,255,255,0.07);
--border-hover: rgba(255,255,255,0.14); --border-hover: rgba(255,255,255,0.14);
--accent: #4f46e5; --brand-cyan: #00d4ff;
--accent2: #7c6dfa; --brand-blue: #4f6cff;
--brand-purple: #9333ea;
--accent: #4f6cff;
--accent2: #8b5cf6;
--teal: #00d4aa; --teal: #00d4aa;
--text: #e2e8f0; --text: #e2e8f0;
--muted: #64748b; --muted: #64748b;
--green: #10b981; --green: #10b981;
--red: #ef4444; --red: #ef4444;
--yellow: #f59e0b; --yellow: #f59e0b;
--nav-active-bg: rgba(79,70,229,0.18); --nav-active-bg: rgba(79,108,255,0.18);
--nav-active-border: #4f46e5; --nav-active-border: #4f6cff;
} }
/* ── Reset ──────────────────────────────────────────────────── */ /* ── Reset ──────────────────────────────────────────────────── */
@@ -70,11 +74,17 @@
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
} }
.logo-mark,
.cmd-mark {
display: block;
flex-shrink: 0;
filter: drop-shadow(0 0 10px rgba(79,108,255,0.3));
}
.logo-text { .logo-text {
font-size: 1.25rem; font-size: 1.25rem;
font-weight: 800; font-weight: 800;
letter-spacing: 0.12em; letter-spacing: 0.12em;
background: linear-gradient(135deg, #7c6dfa, #00d4aa); background: linear-gradient(180deg, #ffffff 0%, #d4dae6 100%);
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
background-clip: text; background-clip: text;
@@ -82,11 +92,12 @@
.logo-tagline { .logo-tagline {
font-size: 0.58rem; font-size: 0.58rem;
color: var(--muted); color: var(--muted);
font-weight: 400; font-weight: 600;
letter-spacing: 0.06em; letter-spacing: 0.08em;
text-transform: uppercase;
display: block; display: block;
line-height: 1; line-height: 1;
margin-top: -2px; margin-top: 2px;
-webkit-text-fill-color: var(--muted); -webkit-text-fill-color: var(--muted);
} }
@@ -217,7 +228,7 @@
content: ''; content: '';
position: absolute; position: absolute;
top: 0; left: 0; right: 0; height: 1px; top: 0; left: 0; right: 0; height: 1px;
background: linear-gradient(90deg, rgba(0,229,255,0.45), rgba(79,70,229,0.55), rgba(124,58,237,0.45)); background: linear-gradient(90deg, rgba(0,212,255,0.55), rgba(79,108,255,0.65), rgba(147,51,234,0.55));
} }
.cmd-logo { .cmd-logo {
display: flex; display: flex;
@@ -234,19 +245,22 @@
font-size: 1.05rem; font-size: 1.05rem;
font-weight: 800; font-weight: 800;
letter-spacing: 0.14em; letter-spacing: 0.14em;
background: linear-gradient(135deg, #00e5ff 0%, #4f46e5 50%, #7c3aed 100%); background: linear-gradient(180deg, #ffffff 0%, #d4dae6 100%);
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
background-clip: text; background-clip: text;
} }
.cmd-tagline { .cmd-tagline {
font-size: 0.46rem; font-size: 0.5rem;
font-weight: 600; font-weight: 700;
letter-spacing: 0.1em; letter-spacing: 0.18em;
color: var(--muted);
text-transform: uppercase; text-transform: uppercase;
-webkit-text-fill-color: var(--muted); display: inline-flex;
gap: 0.35em;
} }
.cmd-tagline .tag-cyan { color: #00d4ff; }
.cmd-tagline .tag-purple { color: #b06bff; }
.cmd-tagline .tag-white { color: #cfd5e0; }
.cmd-sep { .cmd-sep {
width: 1px; width: 1px;
height: 30px; height: 30px;
@@ -310,7 +324,7 @@
} }
.cmd-prog-fill { .cmd-prog-fill {
height: 100%; height: 100%;
background: linear-gradient(90deg, #00e5ff, #4f46e5); background: linear-gradient(90deg, #00d4ff, #4f6cff, #9333ea);
border-radius: 999px; border-radius: 999px;
transition: width 0.5s ease; transition: width 0.5s ease;
} }
@@ -347,8 +361,9 @@
white-space: nowrap; white-space: nowrap;
} }
.cmd-btn-start { .cmd-btn-start {
background: linear-gradient(135deg, #4f46e5, #7c6dfa); background: linear-gradient(135deg, #4f6cff 0%, #9333ea 100%);
color: white; color: white;
box-shadow: 0 0 0 1px rgba(147,51,234,0.4), 0 4px 14px rgba(79,108,255,0.25);
} }
.cmd-btn-start:hover { opacity: 0.88; } .cmd-btn-start:hover { opacity: 0.88; }
.cmd-btn-pause { .cmd-btn-pause {
@@ -399,7 +414,7 @@
position: absolute; position: absolute;
top: 0; left: 0; right: 0; top: 0; left: 0; right: 0;
height: 2px; height: 2px;
background: linear-gradient(90deg, var(--accent), var(--accent2)); background: linear-gradient(90deg, #00d4ff, #4f6cff, #9333ea);
opacity: 0; opacity: 0;
transition: opacity 0.2s; transition: opacity 0.2s;
} }
@@ -1629,27 +1644,10 @@
<!-- Logo --> <!-- Logo -->
<div class="logo-wrap"> <div class="logo-wrap">
<svg width="36" height="40" viewBox="0 0 40 44" fill="none" xmlns="http://www.w3.org/2000/svg"> <img src="/static/img/apex-mark.png" alt="APEX" class="logo-mark" width="38" height="42">
<defs>
<linearGradient id="logoGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#00e5ff"/>
<stop offset="45%" stop-color="#4f46e5"/>
<stop offset="100%" stop-color="#7c3aed"/>
</linearGradient>
<clipPath id="sbTriClip">
<polygon points="20,2 39,40 1,40"/>
</clipPath>
</defs>
<polygon points="20,2 39,40 1,40" fill="rgba(79,70,229,0.08)" stroke="url(#logoGrad)" stroke-width="2" stroke-linejoin="round"/>
<line x1="10" y1="30" x2="30" y2="30" stroke="url(#logoGrad)" stroke-width="1.5"/>
<rect x="11" y="31" width="4" height="8" rx="0.8" fill="url(#logoGrad)" opacity="0.5" clip-path="url(#sbTriClip)"/>
<rect x="17" y="24" width="4" height="15" rx="0.8" fill="url(#logoGrad)" opacity="0.75" clip-path="url(#sbTriClip)"/>
<rect x="23" y="18" width="4" height="21" rx="0.8" fill="url(#logoGrad)" clip-path="url(#sbTriClip)"/>
<polyline points="8,38 17,27 22,30 33,16" stroke="#00e5ff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" clip-path="url(#sbTriClip)" opacity="0.9"/>
</svg>
<div> <div>
<span class="logo-text">APEX</span> <span class="logo-text">APEX</span>
<span class="logo-tagline">AI Optimizer</span> <span class="logo-tagline">AI Powered Optimizer</span>
</div> </div>
</div> </div>
@@ -1728,27 +1726,15 @@
<!-- Logo --> <!-- Logo -->
<div class="cmd-logo"> <div class="cmd-logo">
<svg width="32" height="36" viewBox="0 0 40 44" fill="none" xmlns="http://www.w3.org/2000/svg"> <img src="/static/img/apex-mark.png" alt="APEX" class="cmd-mark" width="34" height="38">
<defs>
<linearGradient id="hdrLogoGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#00e5ff"/>
<stop offset="45%" stop-color="#4f46e5"/>
<stop offset="100%" stop-color="#7c3aed"/>
</linearGradient>
<clipPath id="hdrTriClip">
<polygon points="20,2 39,40 1,40"/>
</clipPath>
</defs>
<polygon points="20,2 39,40 1,40" fill="rgba(79,70,229,0.09)" stroke="url(#hdrLogoGrad)" stroke-width="1.8" stroke-linejoin="round"/>
<line x1="10" y1="30" x2="30" y2="30" stroke="url(#hdrLogoGrad)" stroke-width="1.4"/>
<rect x="11" y="31" width="4" height="8" rx="0.8" fill="url(#hdrLogoGrad)" opacity="0.5" clip-path="url(#hdrTriClip)"/>
<rect x="17" y="24" width="4" height="15" rx="0.8" fill="url(#hdrLogoGrad)" opacity="0.75" clip-path="url(#hdrTriClip)"/>
<rect x="23" y="18" width="4" height="21" rx="0.8" fill="url(#hdrLogoGrad)" clip-path="url(#hdrTriClip)"/>
<polyline points="8,38 17,27 22,30 33,16" stroke="#00e5ff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" clip-path="url(#hdrTriClip)" opacity="0.9"/>
</svg>
<div class="cmd-logo-text"> <div class="cmd-logo-text">
<span class="cmd-apex">APEX</span> <span class="cmd-apex">APEX</span>
<span class="cmd-tagline">AI Powered EA Optimizer</span> <span class="cmd-tagline">
<span class="tag-cyan">AI</span>
<span class="tag-white">POWERED</span>
<span class="tag-purple">EA</span>
<span class="tag-white">OPTIMIZER</span>
</span>
</div> </div>
</div> </div>
+2 -1
View File
@@ -3,7 +3,8 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MT5 EA Optimizer — Live Dashboard</title> <title>APEX — Live Dashboard</title>
<link rel="icon" type="image/png" href="/static/img/favicon.png">
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/static/css/style.css"> <link rel="stylesheet" href="/static/css/style.css">
+19 -33
View File
@@ -4,12 +4,16 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>APEX — AI-Powered EA Optimizer</title> <title>APEX — AI-Powered EA Optimizer</title>
<link rel="icon" type="image/png" href="/static/img/favicon.png">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<style> <style>
:root { :root {
--bg: #080d1a; --bg: #080d1a;
--accent: #4f46e5; --brand-cyan: #00d4ff;
--accent2: #7c6dfa; --brand-blue: #4f6cff;
--brand-purple: #9333ea;
--accent: #4f6cff;
--accent2: #8b5cf6;
--teal: #00d4aa; --teal: #00d4aa;
--text: #e2e8f0; --text: #e2e8f0;
--muted: #64748b; --muted: #64748b;
@@ -72,6 +76,15 @@
align-items: center; align-items: center;
gap: 0.75rem; gap: 0.75rem;
} }
.nav-mark,
.hero-mark {
display: block;
flex-shrink: 0;
filter: drop-shadow(0 0 14px rgba(79,108,255,0.35));
}
.hero-mark {
filter: drop-shadow(0 0 28px rgba(79,108,255,0.45));
}
.nav-logo-text { .nav-logo-text {
font-size: 1.25rem; font-size: 1.25rem;
@@ -145,7 +158,7 @@
font-weight: 900; font-weight: 900;
letter-spacing: -0.04em; letter-spacing: -0.04em;
line-height: 1; line-height: 1;
background: linear-gradient(135deg, #ffffff 0%, #c7d2fe 50%, #00d4aa 100%); background: linear-gradient(135deg, #ffffff 0%, #c7d2fe 50%, #b06bff 100%);
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
} }
@@ -239,7 +252,7 @@
font-size: 4rem; font-size: 4rem;
font-weight: 900; font-weight: 900;
letter-spacing: 0.1em; letter-spacing: 0.1em;
background: linear-gradient(135deg, #4f46e5, #00d4aa); background: linear-gradient(135deg, #00d4ff, #4f6cff, #9333ea);
-webkit-background-clip: text; -webkit-text-fill-color: transparent; -webkit-background-clip: text; -webkit-text-fill-color: transparent;
opacity: 0.15; opacity: 0.15;
pointer-events: none; pointer-events: none;
@@ -307,20 +320,7 @@
<!-- Navbar --> <!-- Navbar -->
<nav class="navbar"> <nav class="navbar">
<div class="nav-logo"> <div class="nav-logo">
<svg width="32" height="32" viewBox="0 0 36 36" fill="none"> <img src="/static/img/apex-mark.png" alt="APEX" class="nav-mark" width="34" height="38">
<polygon points="18,2 34,32 2,32" fill="url(#logoGradL)" opacity="0.15"/>
<polygon points="18,8 30,30 6,30" fill="url(#logoGradL)" opacity="0.3"/>
<path d="M18 6L22 14H14L18 6Z" fill="url(#logoGradL)"/>
<rect x="11" y="22" width="3" height="8" rx="1" fill="#00d4aa"/>
<rect x="16" y="18" width="3" height="12" rx="1" fill="#00d4aa" opacity="0.8"/>
<rect x="21" y="14" width="3" height="16" rx="1" fill="#00d4aa" opacity="0.6"/>
<defs>
<linearGradient id="logoGradL" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#4f46e5"/>
<stop offset="100%" stop-color="#00d4aa"/>
</linearGradient>
</defs>
</svg>
<span class="nav-logo-text">APEX</span> <span class="nav-logo-text">APEX</span>
</div> </div>
<div class="status-pill" id="status-pill"> <div class="status-pill" id="status-pill">
@@ -335,21 +335,7 @@
<div class="hero-badge">&#10022; Hackathon Edition 2026</div> <div class="hero-badge">&#10022; Hackathon Edition 2026</div>
<div class="hero-logo-row"> <div class="hero-logo-row">
<svg width="72" height="72" viewBox="0 0 72 72" fill="none"> <img src="/static/img/apex-mark.png" alt="APEX" class="hero-mark" width="120" height="132">
<polygon points="36,4 68,64 4,64" fill="url(#heroGrad)" opacity="0.12"/>
<polygon points="36,16 60,60 12,60" fill="url(#heroGrad)" opacity="0.25"/>
<path d="M36 12L44 28H28L36 12Z" fill="url(#heroGrad)"/>
<path d="M33 20L36 13L39 20" stroke="#00d4aa" stroke-width="1.5" fill="none" stroke-linecap="round"/>
<rect x="22" y="44" width="6" height="16" rx="2" fill="#00d4aa" opacity="0.9"/>
<rect x="33" y="36" width="6" height="24" rx="2" fill="#00d4aa" opacity="0.7"/>
<rect x="44" y="28" width="6" height="32" rx="2" fill="#00d4aa" opacity="0.5"/>
<defs>
<linearGradient id="heroGrad" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#4f46e5"/>
<stop offset="100%" stop-color="#7c6dfa"/>
</linearGradient>
</defs>
</svg>
</div> </div>
<div class="hero-title">APEX</div> <div class="hero-title">APEX</div>
+2 -1
View File
@@ -2,7 +2,8 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Run {{ run_id }} — MT5 Optimizer Report</title> <title>Run {{ run_id }} — APEX Report</title>
<link rel="icon" type="image/png" href="/static/img/favicon.png">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=JetBrains+Mono&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=JetBrains+Mono&display=swap" rel="stylesheet">
<style> <style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
+1
View File
@@ -3,6 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Reports — APEX MT5 Optimizer</title> <title>Reports — APEX MT5 Optimizer</title>
<link rel="icon" type="image/png" href="/static/img/favicon.png">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style> <style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
+2 -1
View File
@@ -3,7 +3,8 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>New Optimization — MT5 Smart Optimizer</title> <title>New Optimization — APEX</title>
<link rel="icon" type="image/png" href="/static/img/favicon.png">
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<style> <style>