Spaces:
Sleeping
Sleeping
Download static/index.html from callernode/xauusd: direct link, hf CLI and curl.
- Browser
- Download file 7.35 kB
-
https://huggingface.co/spaces/callernode/xauusd/resolve/main/static/index.html
- Command line
-
hf download hf://spaces/callernode/xauusd/static/index.html
-
curl -L -o index.html https://huggingface.co/spaces/callernode/xauusd/resolve/main/static/index.html
7.35 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <title>XAUUSD · Live Gold Spot</title> | |
| <style> | |
| :root { --bg:#0b1120; --card:#131c31; --line:#1f2b45; --txt:#e2e8f0; --dim:#8b9ab5; --up:#22c55e; --dn:#ef4444; --gold:#f5c451; } | |
| * { box-sizing:border-box; } | |
| body { margin:0; min-height:100vh; background:var(--bg); color:var(--txt); | |
| font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; | |
| padding:28px 20px 40px; } | |
| .wrap { max-width:980px; margin:0 auto; } | |
| header { display:flex; flex-wrap:wrap; align-items:center; gap:12px; margin-bottom:22px; } | |
| h1 { font-size:20px; margin:0; letter-spacing:.3px; } | |
| h1 span { color:var(--gold); } | |
| .badge { font-size:11px; padding:4px 10px; border-radius:999px; border:1px solid var(--line); | |
| color:var(--dim); text-transform:uppercase; letter-spacing:.08em; } | |
| .badge.live { color:var(--up); border-color:#14532d; } | |
| .badge.err { color:var(--dn); border-color:#7f1d1d; } | |
| button { margin-left:auto; background:#1c2740; color:var(--txt); border:1px solid var(--line); | |
| border-radius:8px; padding:8px 14px; font-size:13px; cursor:pointer; } | |
| button:hover { background:#243050; } | |
| .price-card { background:linear-gradient(160deg,#16203a,#0f1728); border:1px solid var(--line); | |
| border-radius:16px; padding:22px 24px; } | |
| .label { font-size:11px; text-transform:uppercase; letter-spacing:.1em; color:var(--dim); } | |
| .price { font-size:52px; font-weight:650; line-height:1.1; margin:6px 0 2px; font-variant-numeric:tabular-nums; } | |
| .price small { font-size:20px; color:var(--dim); font-weight:400; margin-left:6px; } | |
| .delta { font-size:14px; font-variant-numeric:tabular-nums; } | |
| .delta.up { color:var(--up); } .delta.dn { color:var(--dn); } .delta.flat { color:var(--dim); } | |
| .spark { width:100%; height:170px; margin-top:14px; display:block; } | |
| .grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(160px,1fr)); gap:12px; margin-top:16px; } | |
| .stat { background:var(--card); border:1px solid var(--line); border-radius:12px; padding:14px 16px; } | |
| .stat .v { font-size:19px; margin-top:5px; font-variant-numeric:tabular-nums; } | |
| footer { margin-top:20px; font-size:12px; color:var(--dim); line-height:1.7; } | |
| a { color:#7dd3fc; text-decoration:none; } | |
| .warn { color:var(--gold); font-size:12px; margin-top:8px; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="wrap"> | |
| <header> | |
| <h1>XAU<span>/USD</span> · Live Gold Spot</h1> | |
| <span class="badge live" id="badge">live</span> | |
| <button id="refresh">Refresh</button> | |
| </header> | |
| <section class="price-card"> | |
| <div class="label">Spot price · USD per troy ounce</div> | |
| <div class="price" id="price">—<small id="unit"></small></div> | |
| <div class="delta flat" id="delta">waiting for data…</div> | |
| <svg class="spark" id="spark" viewBox="0 0 600 150" preserveAspectRatio="none"></svg> | |
| <div class="warn" id="warn" hidden></div> | |
| </section> | |
| <section class="grid"> | |
| <div class="stat"><div class="label">Per gram</div><div class="v" id="gram">—</div></div> | |
| <div class="stat"><div class="label">Per kg</div><div class="v" id="kg">—</div></div> | |
| <div class="stat"><div class="label">Silver XAG/oz</div><div class="v" id="silver">—</div></div> | |
| <div class="stat"><div class="label">Gold / Silver ratio</div><div class="v" id="ratio">—</div></div> | |
| <div class="stat"><div class="label">XAUT premium</div><div class="v" id="xaut">—</div></div> | |
| <div class="stat"><div class="label">PAXG premium</div><div class="v" id="paxg">—</div></div> | |
| <div class="stat"><div class="label">BTC in gold (oz)</div><div class="v" id="btc">—</div></div> | |
| <div class="stat"><div class="label">Samples this session</div><div class="v" id="n">0</div></div> | |
| </section> | |
| <footer> | |
| <div>Source: <span id="src">—</span> · upstream updated <span id="upd">—</span> · fetched <span id="fet">—</span></div> | |
| <div>Auto-refresh every 30s (server caches upstream for 30s). Prices are indicative spot references, delayed, and for informational purposes only — not investment advice.</div> | |
| </footer> | |
| </div> | |
| <script> | |
| const $ = id => document.getElementById(id); | |
| const fmt = (n, d = 2) => (n === null || n === undefined || isNaN(n)) | |
| ? '—' : Number(n).toLocaleString('en-US', { minimumFractionDigits: d, maximumFractionDigits: d }); | |
| function drawSpark(values) { | |
| const svg = $('spark'), W = 600, H = 150, pad = 10; | |
| if (!values || values.length < 2) { | |
| svg.innerHTML = '<text x="300" y="78" text-anchor="middle" fill="#8b9ab5" font-size="13">collecting data…</text>'; | |
| return; | |
| } | |
| const min = Math.min(...values), max = Math.max(...values), span = (max - min) || 1; | |
| const X = i => pad + (W - 2 * pad) * i / (values.length - 1); | |
| const Y = v => H - pad - (H - 2 * pad) * (v - min) / span; | |
| const pts = values.map((v, i) => `${X(i).toFixed(1)},${Y(v).toFixed(1)}`).join(' '); | |
| const up = values[values.length - 1] >= values[0]; | |
| const stroke = up ? '#22c55e' : '#ef4444'; | |
| svg.innerHTML = | |
| `<polygon points="${pad},${H - pad} ${pts} ${W - pad},${H - pad}" fill="${stroke}22" />` + | |
| `<polyline points="${pts}" fill="none" stroke="${stroke}" stroke-width="2.5" ` + | |
| `stroke-linejoin="round" stroke-linecap="round" />`; | |
| } | |
| function setBadge(cls, text) { const b = $('badge'); b.className = 'badge ' + cls; b.textContent = text; } | |
| async function load() { | |
| try { | |
| const res = await fetch('/api/price', { cache: 'no-store' }); | |
| const d = await res.json(); | |
| if (!res.ok || !d.ok) throw new Error(d.error || ('HTTP ' + res.status)); | |
| $('price').innerHTML = fmt(d.price) + '<small>USD</small>'; | |
| const history = d.history || []; | |
| if (history.length > 1) { | |
| const diff = d.price - history[0]; | |
| const pct = (diff / history[0]) * 100; | |
| const cls = diff > 0 ? 'up' : diff < 0 ? 'dn' : 'flat'; | |
| const sign = diff > 0 ? '+' : ''; | |
| $('delta').className = 'delta ' + cls; | |
| $('delta').textContent = `${sign}${fmt(diff)} (${sign}${fmt(pct)}%) this session`; | |
| } else { | |
| $('delta').className = 'delta flat'; | |
| $('delta').textContent = 'session baseline set'; | |
| } | |
| drawSpark(history); | |
| $('gram').textContent = fmt(d.per_gram) + ' $'; | |
| $('kg').textContent = fmt(d.per_kg, 0) + ' $'; | |
| $('silver').textContent = fmt(d.silver); | |
| $('ratio').textContent = fmt(d.gold_silver_ratio); | |
| $('xaut').textContent = d.xaut_ratio === null ? '—' : fmt(d.xaut_ratio, 4) + '×'; | |
| $('paxg').textContent = d.paxg_ratio === null ? '—' : fmt(d.paxg_ratio, 4) + '×'; | |
| $('btc').textContent = fmt(d.btc_gold_oz); | |
| $('n').textContent = history.length; | |
| $('src').textContent = d.source || '—'; | |
| $('upd').textContent = d.upstream_updated_at || '—'; | |
| $('fet').textContent = d.fetched_at || '—'; | |
| const w = $('warn'); | |
| w.hidden = !d.warning; w.textContent = d.warning || ''; | |
| setBadge(d.warning ? 'err' : 'live', d.warning ? 'stale' : 'live'); | |
| } catch (e) { | |
| setBadge('err', 'error'); | |
| $('delta').className = 'delta dn'; | |
| $('delta').textContent = String(e.message || e); | |
| } | |
| } | |
| $('refresh').addEventListener('click', load); | |
| load(); | |
| setInterval(load, 30000); | |
| </script> | |
| </body> | |
| </html> |