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:
committed by
LEGTECH
co-authored by
Claude Opus 4.7
parent
96680cde63
commit
33e670aa4c
@@ -117,16 +117,30 @@ hit **Start**, and watch the AI think.
|
||||
|
||||
### Demo mode (no MT5 required)
|
||||
|
||||
Don't have MT5 installed? Run the offline demo that feeds synthetic backtest results
|
||||
through the same AI loop and dashboard:
|
||||
Don't have MT5 installed? The offline demo feeds synthetic backtest results
|
||||
through the same AI loop and dashboard. **It auto-starts** — open the link
|
||||
the script prints, sit back, and watch APEX think:
|
||||
|
||||
```bash
|
||||
python -m demo.run_demo
|
||||
```
|
||||
|
||||
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.
|
||||
That's it. The browser opens to the live dashboard, an optimization kicks off
|
||||
automatically, and you'll see all three phases play out over ~3-4 minutes:
|
||||
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
@@ -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:
|
||||
|
||||
- **Static**: open `screenshots/apex_demo.gif` in the repo (6‑frame 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
|
||||
- **With API key**: set `ANTHROPIC_API_KEY` env var to see live Claude reasoning stream into the Thinking Feed
|
||||
- **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 (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
@@ -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,
|
||||
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:
|
||||
python -m demo.run_demo
|
||||
# or
|
||||
python demo/run_demo.py
|
||||
python -m demo.run_demo # auto-running showcase (default, ~3-4 min)
|
||||
python -m demo.run_demo --quick # original fast/manual demo (you click "New Run")
|
||||
python -m demo.run_demo --loop # auto-restart on completion (for unattended recording)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
@@ -83,24 +87,159 @@ def ensure_config() -> None:
|
||||
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
|
||||
print(textwrap.dedent(f"""
|
||||
{bar}
|
||||
APEX -- DEMO MODE (offline / no MT5)
|
||||
{bar}
|
||||
* Backtests are synthetic (deterministic from params + jitter)
|
||||
* The AI loop, validation, and verdict flow are 100% real
|
||||
* Set ANTHROPIC_API_KEY to see live AI reasoning
|
||||
if quick:
|
||||
mode_line = "APEX -- DEMO MODE (quick / manual)"
|
||||
body = (
|
||||
" * Backtests are synthetic; AI reasoning is real if API key is set\n"
|
||||
" * Per-run latency is short for fast UI testing\n\n"
|
||||
' Open http://localhost:5000 in your browser, hit "New Run", and\n'
|
||||
" 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.
|
||||
{bar}
|
||||
"""))
|
||||
|
||||
# ── Showcase auto-start config ──────────────────────────────────────────────
|
||||
# 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:
|
||||
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...")
|
||||
ensure_config()
|
||||
@@ -108,14 +247,35 @@ def main() -> int:
|
||||
|
||||
# Set the demo flag — the pipeline checks this in _execute_run.
|
||||
os.environ["APEX_DEMO_MODE"] = "1"
|
||||
# Per-run latency tunable — keep small so demo feels snappy.
|
||||
os.environ.setdefault("APEX_DEMO_RUN_SECONDS", "1.2")
|
||||
# Per-run latency tunable. Default (auto-running showcase) is slower so
|
||||
# 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:
|
||||
print(" [!] ANTHROPIC_API_KEY not set — AI reasoning will be skipped (synthetic metrics still flow).")
|
||||
# AI reasoner pulls from env var OR config.yaml ai.anthropic_api_key
|
||||
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("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))
|
||||
# Import after env vars are set so the pipeline picks them up.
|
||||
import threading
|
||||
@@ -126,11 +286,22 @@ def main() -> int:
|
||||
import time as _t
|
||||
_t.sleep(1.5)
|
||||
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:
|
||||
pass
|
||||
|
||||
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(
|
||||
flask_app, host="0.0.0.0", port=5000,
|
||||
debug=False, use_reloader=False, allow_unsafe_werkzeug=True,
|
||||
|
||||
+11
-5
@@ -898,11 +898,17 @@ class OptimizationPipeline:
|
||||
delay = float(os.environ.get("APEX_DEMO_RUN_SECONDS", "1.5"))
|
||||
time.sleep(max(0.05, delay))
|
||||
|
||||
# Run AI reasoning if enabled — same flow as live mode
|
||||
try:
|
||||
self._reason_about_run(run_id, metrics, [], params)
|
||||
except Exception:
|
||||
pass
|
||||
# Run AI reasoning if enabled — same flow as live mode.
|
||||
# In showcase mode (APEX_DEMO_SKIP_PHASE1_AI=1) we skip Phase 1's
|
||||
# per-run AI analysis to keep total runtime to ~3-4 min. The AI loop
|
||||
# in Phase 2 — where reasoning actually drives parameter changes — is
|
||||
# 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)
|
||||
|
||||
|
||||
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
@@ -4,6 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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.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">
|
||||
@@ -16,16 +17,19 @@
|
||||
--card-hover: #131d35;
|
||||
--border: rgba(255,255,255,0.07);
|
||||
--border-hover: rgba(255,255,255,0.14);
|
||||
--accent: #4f46e5;
|
||||
--accent2: #7c6dfa;
|
||||
--brand-cyan: #00d4ff;
|
||||
--brand-blue: #4f6cff;
|
||||
--brand-purple: #9333ea;
|
||||
--accent: #4f6cff;
|
||||
--accent2: #8b5cf6;
|
||||
--teal: #00d4aa;
|
||||
--text: #e2e8f0;
|
||||
--muted: #64748b;
|
||||
--green: #10b981;
|
||||
--red: #ef4444;
|
||||
--yellow: #f59e0b;
|
||||
--nav-active-bg: rgba(79,70,229,0.18);
|
||||
--nav-active-border: #4f46e5;
|
||||
--nav-active-bg: rgba(79,108,255,0.18);
|
||||
--nav-active-border: #4f6cff;
|
||||
}
|
||||
|
||||
/* ── Reset ──────────────────────────────────────────────────── */
|
||||
@@ -70,11 +74,17 @@
|
||||
border-bottom: 1px solid var(--border);
|
||||
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 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.12em;
|
||||
background: linear-gradient(135deg, #7c6dfa, #00d4aa);
|
||||
background: linear-gradient(180deg, #ffffff 0%, #d4dae6 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
@@ -82,11 +92,12 @@
|
||||
.logo-tagline {
|
||||
font-size: 0.58rem;
|
||||
color: var(--muted);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.06em;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
display: block;
|
||||
line-height: 1;
|
||||
margin-top: -2px;
|
||||
margin-top: 2px;
|
||||
-webkit-text-fill-color: var(--muted);
|
||||
}
|
||||
|
||||
@@ -217,7 +228,7 @@
|
||||
content: '';
|
||||
position: absolute;
|
||||
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 {
|
||||
display: flex;
|
||||
@@ -234,19 +245,22 @@
|
||||
font-size: 1.05rem;
|
||||
font-weight: 800;
|
||||
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-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
.cmd-tagline {
|
||||
font-size: 0.46rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--muted);
|
||||
font-size: 0.5rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.18em;
|
||||
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 {
|
||||
width: 1px;
|
||||
height: 30px;
|
||||
@@ -310,7 +324,7 @@
|
||||
}
|
||||
.cmd-prog-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #00e5ff, #4f46e5);
|
||||
background: linear-gradient(90deg, #00d4ff, #4f6cff, #9333ea);
|
||||
border-radius: 999px;
|
||||
transition: width 0.5s ease;
|
||||
}
|
||||
@@ -347,8 +361,9 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
.cmd-btn-start {
|
||||
background: linear-gradient(135deg, #4f46e5, #7c6dfa);
|
||||
background: linear-gradient(135deg, #4f6cff 0%, #9333ea 100%);
|
||||
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-pause {
|
||||
@@ -399,7 +414,7 @@
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, var(--accent), var(--accent2));
|
||||
background: linear-gradient(90deg, #00d4ff, #4f6cff, #9333ea);
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
@@ -1629,27 +1644,10 @@
|
||||
|
||||
<!-- Logo -->
|
||||
<div class="logo-wrap">
|
||||
<svg width="36" height="40" viewBox="0 0 40 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<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>
|
||||
<img src="/static/img/apex-mark.png" alt="APEX" class="logo-mark" width="38" height="42">
|
||||
<div>
|
||||
<span class="logo-text">APEX</span>
|
||||
<span class="logo-tagline">AI Optimizer</span>
|
||||
<span class="logo-tagline">AI Powered Optimizer</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1728,27 +1726,15 @@
|
||||
|
||||
<!-- Logo -->
|
||||
<div class="cmd-logo">
|
||||
<svg width="32" height="36" viewBox="0 0 40 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<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>
|
||||
<img src="/static/img/apex-mark.png" alt="APEX" class="cmd-mark" width="34" height="38">
|
||||
<div class="cmd-logo-text">
|
||||
<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>
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<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 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">
|
||||
|
||||
+19
-33
@@ -4,12 +4,16 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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">
|
||||
<style>
|
||||
:root {
|
||||
--bg: #080d1a;
|
||||
--accent: #4f46e5;
|
||||
--accent2: #7c6dfa;
|
||||
--brand-cyan: #00d4ff;
|
||||
--brand-blue: #4f6cff;
|
||||
--brand-purple: #9333ea;
|
||||
--accent: #4f6cff;
|
||||
--accent2: #8b5cf6;
|
||||
--teal: #00d4aa;
|
||||
--text: #e2e8f0;
|
||||
--muted: #64748b;
|
||||
@@ -72,6 +76,15 @@
|
||||
align-items: center;
|
||||
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 {
|
||||
font-size: 1.25rem;
|
||||
@@ -145,7 +158,7 @@
|
||||
font-weight: 900;
|
||||
letter-spacing: -0.04em;
|
||||
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-text-fill-color: transparent;
|
||||
}
|
||||
@@ -239,7 +252,7 @@
|
||||
font-size: 4rem;
|
||||
font-weight: 900;
|
||||
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;
|
||||
opacity: 0.15;
|
||||
pointer-events: none;
|
||||
@@ -307,20 +320,7 @@
|
||||
<!-- Navbar -->
|
||||
<nav class="navbar">
|
||||
<div class="nav-logo">
|
||||
<svg width="32" height="32" viewBox="0 0 36 36" fill="none">
|
||||
<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>
|
||||
<img src="/static/img/apex-mark.png" alt="APEX" class="nav-mark" width="34" height="38">
|
||||
<span class="nav-logo-text">APEX</span>
|
||||
</div>
|
||||
<div class="status-pill" id="status-pill">
|
||||
@@ -335,21 +335,7 @@
|
||||
<div class="hero-badge">✦ Hackathon Edition 2026</div>
|
||||
|
||||
<div class="hero-logo-row">
|
||||
<svg width="72" height="72" viewBox="0 0 72 72" fill="none">
|
||||
<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>
|
||||
<img src="/static/img/apex-mark.png" alt="APEX" class="hero-mark" width="120" height="132">
|
||||
</div>
|
||||
|
||||
<div class="hero-title">APEX</div>
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<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">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<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">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<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 href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
|
||||
Reference in New Issue
Block a user