Download admin.html from Almaatla/MPTrading: direct link, hf CLI and curl.
- Browser
- Download file 12.3 kB
-
https://huggingface.co/spaces/Almaatla/MPTrading/resolve/main/admin.html
- Command line
-
hf download hf://spaces/Almaatla/MPTrading/admin.html
-
curl -L -o admin.html https://huggingface.co/spaces/Almaatla/MPTrading/resolve/main/admin.html
12.3 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width,initial-scale=1" /> | |
| <title>MPTrading Admin</title> | |
| <style> | |
| :root{ | |
| --bg:#0b1020; | |
| --panel:#111a33; | |
| --panel2:#0f1730; | |
| --text:#e7eaf3; | |
| --muted:#aab2d5; | |
| --line:#233055; | |
| --green:#22c55e; | |
| --red:#ef4444; | |
| --blue:#60a5fa; | |
| } | |
| *{ box-sizing:border-box; } | |
| body{ | |
| margin:0; | |
| font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif; | |
| background:var(--bg); | |
| color:var(--text); | |
| } | |
| header{ | |
| height:52px; | |
| display:flex; | |
| align-items:center; | |
| justify-content:space-between; | |
| padding:0 14px; | |
| border-bottom:1px solid var(--line); | |
| background:rgba(0,0,0,0.15); | |
| gap:12px; | |
| } | |
| header .title{ | |
| font-weight:700; | |
| letter-spacing:0.3px; | |
| white-space:nowrap; | |
| } | |
| .small{ font-size:12px; color:var(--muted); } | |
| code{ color:#c7d2fe; } | |
| main{ | |
| padding:12px; | |
| display:grid; | |
| grid-template-columns: 1.2fr 1fr; | |
| gap:12px; | |
| height: calc(100vh - 52px); | |
| } | |
| @media (max-width: 1000px){ | |
| main{ grid-template-columns: 1fr; height:auto; } | |
| } | |
| .card{ | |
| background:var(--panel); | |
| border:1px solid var(--line); | |
| border-radius:10px; | |
| overflow:hidden; | |
| display:flex; | |
| flex-direction:column; | |
| min-height:0; | |
| } | |
| .card h2{ | |
| margin:0; | |
| padding:10px 12px; | |
| font-size:13px; | |
| color:var(--muted); | |
| text-transform:uppercase; | |
| letter-spacing:0.08em; | |
| border-bottom:1px solid var(--line); | |
| background:var(--panel2); | |
| } | |
| .content{ | |
| padding:12px; | |
| overflow:auto; | |
| min-height:0; | |
| } | |
| label{ | |
| display:block; | |
| font-size:12px; | |
| color:var(--muted); | |
| margin:10px 0 6px; | |
| } | |
| input, textarea, button{ | |
| font:inherit; | |
| border-radius:10px; | |
| border:1px solid var(--line); | |
| background:#0c1430; | |
| color:var(--text); | |
| padding:10px 12px; | |
| } | |
| textarea{ | |
| width:100%; | |
| min-height:260px; | |
| resize:vertical; | |
| font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; | |
| font-size:12px; | |
| line-height:1.35; | |
| } | |
| input{ width:100%; } | |
| .row{ display:grid; grid-template-columns: 1fr 1fr; gap:10px; } | |
| .row3{ display:grid; grid-template-columns: 1fr 1fr 1fr; gap:10px; } | |
| .actions{ | |
| display:flex; | |
| gap:10px; | |
| flex-wrap:wrap; | |
| margin-top:10px; | |
| align-items:center; | |
| } | |
| button{ cursor:pointer; } | |
| button.primary:hover{ background:rgba(96,165,250,0.12); } | |
| button.danger{ border-color:rgba(239,68,68,0.5); } | |
| button.danger:hover{ background:rgba(239,68,68,0.12); } | |
| table{ | |
| width:100%; | |
| border-collapse:collapse; | |
| font-size:13px; | |
| } | |
| th, td{ | |
| text-align:left; | |
| padding:8px 6px; | |
| border-bottom:1px solid rgba(35,48,85,0.6); | |
| vertical-align:top; | |
| } | |
| th{ color:var(--muted); font-weight:600; } | |
| td.mono{ | |
| font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; | |
| } | |
| .ok{ color:var(--green); } | |
| .err{ color:var(--red); } | |
| .hint{ | |
| font-size:12px; | |
| color:var(--muted); | |
| margin-top:6px; | |
| line-height:1.35; | |
| } | |
| .pill{ | |
| display:inline-block; | |
| padding:2px 8px; | |
| border-radius:999px; | |
| border:1px solid var(--line); | |
| font-size:12px; | |
| color:var(--muted); | |
| background:rgba(255,255,255,0.02); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <div class="title">MPTrading Admin</div> | |
| <div class="small"> | |
| Endpoints: | |
| <code>/admin/state</code>, <code>/admin/load_scenario</code>, <code>/admin/add_event</code>, <code>/admin/clear_events</code> | |
| </div> | |
| </header> | |
| <main> | |
| <!-- LEFT: Scenario loader --> | |
| <section class="card"> | |
| <h2>Scenario loader</h2> | |
| <div class="content"> | |
| <label>Admin token (sent as <code>X-ADMIN-TOKEN</code>)</label> | |
| <input id="token" type="password" placeholder="Enter ADMIN_TOKEN" autocomplete="off" /> | |
| <div class="row"> | |
| <div> | |
| <label>Market length override (optional)</label> | |
| <input id="marketLength" type="number" min="600" step="1" placeholder="Leave blank = auto" /> | |
| <div class="hint"> | |
| Blank = auto-size to at least <span class="pill">600</span> and at least the highest scenario day + 1. | |
| Set a number to force a specific length (minimum 600). | |
| </div> | |
| </div> | |
| <div> | |
| <label>Server state</label> | |
| <div class="small"> | |
| Day: <span class="mono" id="curDay">--</span><br/> | |
| Vol: <span class="mono" id="curVol">--</span><br/> | |
| Market length: <span class="mono" id="curMktLen">--</span><br/> | |
| Tick rate: <span class="mono" id="curTickRate">--</span> | |
| </div> | |
| </div> | |
| </div> | |
| <label>Scenario JSON</label> | |
| <textarea id="scenario" spellcheck="false"></textarea> | |
| <div class="actions"> | |
| <button class="primary" id="loadBtn">Load scenario</button> | |
| <button class="danger" id="clearBtn">Clear events</button> | |
| <button id="refreshBtn">Refresh state</button> | |
| <span id="msg" class="small"></span> | |
| </div> | |
| <div class="small" style="margin-top:12px;"> | |
| Scenario format: | |
| <pre class="small" style="white-space:pre-wrap;margin:8px 0 0;color:var(--muted);"> | |
| { | |
| "name": "FOMC week", | |
| "startDay": 0, | |
| "basePrice": 100.0, | |
| "defaultVolatility": 0.8, | |
| "events": [ | |
| {"day": 20, "shockPct": 5.0, "volatility": 1.4, "news": "Rumor of rate cut"} | |
| ] | |
| } | |
| </pre> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- RIGHT: Add event schedule --> | |
| <section class="card"> | |
| <h2>Add event schedule</h2> | |
| <div class="content"> | |
| <div class="row3"> | |
| <div> | |
| <label>Offset ticks ahead</label> | |
| <input id="offset" type="number" value="10" min="0" step="1" /> | |
| </div> | |
| <div> | |
| <label>Shock % (e.g. 5 or -3)</label> | |
| <input id="shockPct" type="number" value="5" step="0.1" /> | |
| </div> | |
| <div> | |
| <label>Volatility (optional)</label> | |
| <input id="vol" type="number" placeholder="leave blank" step="0.1" /> | |
| </div> | |
| </div> | |
| <label>News (optional)</label> | |
| <input id="news" type="text" placeholder="Headline to broadcast at that tick" /> | |
| <div class="actions"> | |
| <button class="primary" id="addBtn">Add event</button> | |
| </div> | |
| <div style="height:12px;"></div> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Day</th> | |
| <th>Shock</th> | |
| <th>Vol</th> | |
| <th>News</th> | |
| </tr> | |
| </thead> | |
| <tbody id="eventsBody"> | |
| <tr><td colspan="4" class="small">No events loaded.</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </section> | |
| </main> | |
| <script> | |
| const id = (x) => document.getElementById(x); | |
| const token = id("token"); | |
| const scenario = id("scenario"); | |
| const marketLength = id("marketLength"); | |
| const curDay = id("curDay"); | |
| const curVol = id("curVol"); | |
| const curMktLen = id("curMktLen"); | |
| const curTickRate = id("curTickRate"); | |
| const msg = id("msg"); | |
| const eventsBody = id("eventsBody"); | |
| const loadBtn = id("loadBtn"); | |
| const clearBtn = id("clearBtn"); | |
| const refreshBtn = id("refreshBtn"); | |
| const addBtn = id("addBtn"); | |
| const offset = id("offset"); | |
| const shockPct = id("shockPct"); | |
| const vol = id("vol"); | |
| const news = id("news"); | |
| function setMsg(text, ok = true){ | |
| msg.textContent = text; | |
| msg.className = ok ? "small ok" : "small err"; | |
| } | |
| function headers(){ | |
| const t = token.value; | |
| return { | |
| "Content-Type": "application/json", | |
| "X-ADMIN-TOKEN": t | |
| }; | |
| } | |
| async function apiGet(path){ | |
| const r = await fetch(path, { method: "GET", headers: headers() }); | |
| const txt = await r.text(); | |
| let data = null; | |
| try { data = JSON.parse(txt); } catch { data = { raw: txt }; } | |
| if (!r.ok) throw new Error((data && data.detail) ? data.detail : ("HTTP " + r.status)); | |
| return data; | |
| } | |
| async function apiPost(path, body){ | |
| const r = await fetch(path, { method: "POST", headers: headers(), body: JSON.stringify(body) }); | |
| const txt = await r.text(); | |
| let data = null; | |
| try { data = JSON.parse(txt); } catch { data = { raw: txt }; } | |
| if (!r.ok) throw new Error((data && data.detail) ? data.detail : ("HTTP " + r.status)); | |
| return data; | |
| } | |
| function escapeHtml(s){ | |
| return String(s).replace(/[&<>"']/g, (c) => ({ | |
| "&":"&","<":"<",">":">",'"':""","'":"'" | |
| }[c])); | |
| } | |
| function renderEvents(events){ | |
| const tb = eventsBody; | |
| tb.innerHTML = ""; | |
| if (!events || !events.length){ | |
| tb.innerHTML = '<tr><td colspan="4" class="small">No events loaded.</td></tr>'; | |
| return; | |
| } | |
| for (const e of events){ | |
| const tr = document.createElement("tr"); | |
| const shock = Number(e.shockPct || 0).toFixed(2); | |
| const v = (e.volatility === null || e.volatility === undefined) ? "" : Number(e.volatility).toFixed(2); | |
| tr.innerHTML = ` | |
| <td class="mono">${Number(e.day)}</td> | |
| <td class="mono">${shock}</td> | |
| <td class="mono">${v}</td> | |
| <td>${escapeHtml(e.news || "")}</td> | |
| `; | |
| tb.appendChild(tr); | |
| } | |
| } | |
| async function refresh(){ | |
| const st = await apiGet("/admin/state"); | |
| curDay.textContent = st.day; | |
| curVol.textContent = st.currentVolatility; | |
| curMktLen.textContent = st.marketLength; | |
| curTickRate.textContent = st.tickRate; | |
| renderEvents(st.events); | |
| } | |
| // Default scenario template | |
| scenario.value = JSON.stringify({ | |
| name: "Example scenario", | |
| startDay: 0, | |
| basePrice: 100.0, | |
| defaultVolatility: 0.8, | |
| events: [ | |
| { day: 20, shockPct: 5.0, volatility: 1.4, news: "Rumor of rate cut" }, | |
| { day: 30, shockPct: -3.0, volatility: 2.0, news: "Unexpected hike" } | |
| ] | |
| }, null, 2); | |
| refreshBtn.addEventListener("click", async () => { | |
| try{ | |
| await refresh(); | |
| setMsg("State refreshed."); | |
| }catch(e){ | |
| setMsg(e.message, false); | |
| } | |
| }); | |
| clearBtn.addEventListener("click", async () => { | |
| try{ | |
| await apiPost("/admin/clear_events", {}); | |
| await refresh(); | |
| setMsg("Events cleared."); | |
| }catch(e){ | |
| setMsg(e.message, false); | |
| } | |
| }); | |
| loadBtn.addEventListener("click", async () => { | |
| try{ | |
| const obj = JSON.parse(scenario.value); | |
| const ml = marketLength.value.trim(); | |
| if (ml){ | |
| obj.marketLength = Number(ml); | |
| } else { | |
| delete obj.marketLength; // ensure auto mode | |
| } | |
| const res = await apiPost("/admin/load_scenario", obj); | |
| await refresh(); | |
| setMsg(`Scenario loaded. startDay=${res.startDay}, eventsLoaded=${res.eventsLoaded}, marketLength=${res.marketLength} (${res.marketLengthMode})`); | |
| }catch(e){ | |
| setMsg(e.message, false); | |
| } | |
| }); | |
| addBtn.addEventListener("click", async () => { | |
| try{ | |
| const off = Number(offset.value); | |
| const sh = Number(shockPct.value); | |
| const volRaw = vol.value.trim(); | |
| const volatility = volRaw ? Number(volRaw) : null; | |
| const n = news.value.trim(); | |
| const body = { offset: off, shockPct: sh }; | |
| if (volatility !== null && Number.isFinite(volatility)) body.volatility = volatility; | |
| if (n) body.news = n; | |
| await apiPost("/admin/add_event", body); | |
| await refresh(); | |
| setMsg("Event added."); | |
| }catch(e){ | |
| setMsg(e.message, false); | |
| } | |
| }); | |
| // Initial refresh; if token missing it will fail silently | |
| refresh().catch(() => {}); | |
| </script> | |
| </body> | |
| </html> | |