ACIL FM
Dark
Refresh
Current DIR:
/home/benbot/.trash/market_ranking5
/
home
benbot
.trash
market_ranking5
Upload
Zip Selected
Delete Selected
Pilih semua
Nama
Ukuran
Permission
Aksi
static
-
chmod
Open
Rename
Delete
templates
-
chmod
Open
Rename
Delete
app.py
4.19 MB
chmod
View
DL
Edit
Rename
Delete
index.html
13.7 MB
chmod
View
DL
Edit
Rename
Delete
main.js
14.13 MB
chmod
View
DL
Edit
Rename
Delete
Edit file: /home/benbot/.trash/market_ranking5/main.js
// ============ Part 3 / 3 : static/main.js ============ // Drives the 9:16 board: clock, BGM, Spot/Futures boards (bar+marquee), // Longest Spread (24h) tables, Price Comparison (24h) tables. // Uses Flask proxy endpoints: // - /api/bitget/spot_tickers // - /api/bitget/futures_tickers?productType=USDT-FUTURES // - /api/bitget/mix/history_candles?symbol=XXX&granularity=5m&limit=288 /* ========================= 0) DOM + UTILITIES ========================= */ const $ = (q, ctx = document) => ctx.querySelector(q); const $$ = (q, ctx = document) => Array.from(ctx.querySelectorAll(q)); const num = (v) => { const n = Number(v); return Number.isFinite(n) ? n : NaN; }; const fmtNum = (v, d = 6) => Number.isFinite(v) ? Number(v).toFixed(d) : ''; const fmtPct = (v) => Number.isFinite(v) ? (v >= 0 ? `+${v.toFixed(2)}` : v.toFixed(2)) : ''; const clsPct = (v) => (v > 0 ? 'pos' : v < 0 ? 'neg' : ''); const isUSDT = (s) => /USDT$/i.test(s || ''); const toLA = (ts) => new Date(ts).toLocaleString('en-US', { timeZone: 'America/Los_Angeles' }); /* ========================= 1) CLOCK (Los Angeles, HH:mm:ss) + BGM ========================= */ (function clock() { const clk = $('#laClock'); const tzEl = $('#laTz'); function tickLA() { const now = new Date(); const t = new Intl.DateTimeFormat('en-US', { timeZone: 'America/Los_Angeles', hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' }).format(now); const tz = new Intl.DateTimeFormat('en-US', { timeZone: 'America/Los_Angeles', timeZoneName: 'short' }).format(now).split(' ').pop(); if (clk) clk.textContent = t; if (tzEl) tzEl.textContent = `Los Angeles (${tz})`; } tickLA(); setInterval(tickLA, 1000); })(); (function bgm() { const a = $('#bgm'); if (!a) return; a.volume = .25; const tryPlay = () => a.play().catch(() => {}); tryPlay(); ['click', 'touchstart', 'keydown'].forEach(ev => window.addEventListener(ev, () => { a.muted = false; tryPlay(); }, { once: true }) ); })(); /* ========================= 2) FETCH HELPERS (≤ 0.5s loop with graceful backoff) ========================= */ const API = { spotTickers: '/api/bitget/spot_tickers', futTickers: (productType = 'USDT-FUTURES') => `/api/bitget/futures_tickers?productType=${encodeURIComponent(productType)}`, history: (symbol, granularity = '5m', limit = 288) => `/api/bitget/mix/history_candles?symbol=${encodeURIComponent(symbol)}&granularity=${encodeURIComponent(granularity)}&limit=${encodeURIComponent(limit)}` }; async function getJSON(url) { const r = await fetch(url, { cache: 'no-store' }); if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); } let baseMs = 500, backoff = 1; const schedule = (fn) => setTimeout(fn, Math.min(4000, Math.max(500, baseMs * backoff))); /* ========================= 3) BOARDS: Breadth bar + Marquee (Spot/Futures) ========================= */ function ensureRails(el) { if (el._dup) return; const a = document.createElement('div'), b = document.createElement('div'); a.className = 'rail'; b.className = 'rail'; el.append(a, b); el._a = a; el._b = b; el._dup = true; el._x = 0; } function renderMarquee(el, items) { ensureRails(el); const html = items.map(x => `<span class="chip ${x.pct > 0 ? 'up' : x.pct < 0 ? 'down' : ''}"> <strong>${x.symbol}</strong> <span>${fmtNum(x.last, x.last > 100 ? 2 : 6)}</span> <span class="pct">${fmtPct(x.pct)}%</span> </span>`).join(''); el._a.innerHTML = html; el._b.innerHTML = html; cancelAnimationFrame(el._raf); const speed = 40; // px/s const step = (t) => { const dt = (el._t ? (t - el._t) : 16) / 1000; el._t = t; el._x -= speed * dt; const w = el._a.getBoundingClientRect().width || 1; if (el._x <= -w) el._x += w; el._a.style.transform = `translateX(${el._x}px)`; el._b.style.transform = `translateX(${el._x + w}px)`; el._raf = requestAnimationFrame(step); }; el._x = 0; el._t = undefined; el._raf = requestAnimationFrame(step); } /* Breadth computation using rolling 5-minute baseline */ const spotHist = new Map(); // symbol -> [{ts, price}] const futHist = new Map(); const windowMin = 5; function trimHistory(q, keepMin) { const cutoff = Date.now() - keepMin * 60 * 1000; while (q.length && q[0].ts < cutoff) q.shift(); } function boundaryStart(min) { const tf = min * 60 * 1000; const now = Date.now(); const al = Math.floor(now / tf) * tf; return al - tf; } function baseAt(q, t0) { if (!q || !q.length) return null; for (let i = 0; i < q.length; i++) if (q[i].ts >= t0) return q[i].price; // fallback: oldest return q[0].price; } function breadthCounts(mapHist, min) { const t0 = boundaryStart(min); let up = 0, dn = 0, flat = 0, total = 0; for (const [, q] of mapHist.entries()) { if (!q || q.length < 2) continue; const base = baseAt(q, t0); const last = q[q.length - 1].price; if (!Number.isFinite(base) || !Number.isFinite(last)) continue; total++; if (last > base) up++; else if (last < base) dn++; else flat++; } return { up, dn, flat, total }; } function drawBreadth(prefix, counts) { const { up, dn, flat, total } = counts; const upPct = total ? Math.round((up / total) * 100) : 0; const midPct = total ? Math.round((flat / total) * 100) : 0; const dnPct = total ? Math.round((dn / total) * 100) : 0; $(`#${prefix}UpBar`).style.width = upPct + '%'; $(`#${prefix}MidBar`).style.width = midPct + '%'; $(`#${prefix}MidBar`).style.left = upPct + '%'; $(`#${prefix}DnBar`).style.width = dnPct + '%'; $(`#${prefix}DnBar`).style.left = (upPct + midPct) + '%'; $(`#${prefix}UpPct`).textContent = upPct; $(`#${prefix}MidPct`).textContent = midPct; $(`#${prefix}DnPct`).textContent = dnPct; $(`#${prefix}UpCnt`).textContent = up; $(`#${prefix}MidCnt`).textContent = flat; $(`#${prefix}DnCnt`).textContent = dn; $(`#${prefix}Total`).textContent = total; } /* ========================= 4) DATA LOOPS: Spot/Futures boards ========================= */ async function loopBoards() { try { const [spot, fut] = await Promise.all([ getJSON(API.spotTickers), getJSON(API.futTickers('USDT-FUTURES')) ]); // Spot const sArr = (Array.isArray(spot?.data) ? spot.data : []) .filter(t => isUSDT(String(t.symbol))) .map(t => { const sym = String(t.symbol); const last = num(t.lastPr ?? t.last ?? t.close ?? t.lastPrice); let p = num(t.change ?? t.change24h); if (Number.isFinite(p) && Math.abs(p) < 1) p *= 100; if (!Number.isFinite(p)) { const o = num(t.open24h ?? t.openPr ?? t.open ?? t.openPrice); p = (Number.isFinite(last) && Number.isFinite(o) && o) ? ((last - o) / o * 100) : NaN; } if (!spotHist.has(sym)) spotHist.set(sym, []); const q = spotHist.get(sym); q.push({ ts: Date.now(), price: last }); trimHistory(q, 20); return { symbol: sym, last, pct: p }; }); renderMarquee($('#spotRail'), sArr); drawBreadth('spot', breadthCounts(spotHist, windowMin)); // Futures const fArr = (Array.isArray(fut?.data) ? fut.data : []) .filter(t => isUSDT(String(t.symbol))) .map(t => { const sym = String(t.symbol); const last = num(t.lastPr ?? t.last ?? t.close ?? t.lastPrice); let p = num(t.change24h ?? t.change); if (Number.isFinite(p) && Math.abs(p) < 1) p *= 100; if (!Number.isFinite(p)) { const o = num(t.open24h ?? t.openPr ?? t.open ?? t.openPrice); p = (Number.isFinite(last) && Number.isFinite(o) && o) ? ((last - o) / o * 100) : NaN; } if (!futHist.has(sym)) futHist.set(sym, []); const q = futHist.get(sym); q.push({ ts: Date.now(), price: last }); trimHistory(q, 20); return { symbol: sym, last, pct: p }; }); renderMarquee($('#futRail'), fArr); drawBreadth('fut', breadthCounts(futHist, windowMin)); backoff = 1; } catch (e) { backoff = Math.min(8, backoff * 1.5); console.error('Boards loop error:', e); } finally { schedule(loopBoards); } } /* ========================= 5) RANKINGS: Spread(24h) & Price Comparison(24h) ========================= */ const spotPctMap = new Map(); // Symbol -> spot % change(24h) async function loadSpotPctMap() { try { const spot = await getJSON(API.spotTickers); const arr = Array.isArray(spot?.data) ? spot.data : []; spotPctMap.clear(); for (const t of arr) { const sym = String(t.symbol || '').toUpperCase(); if (!isUSDT(sym)) continue; const last = num(t.lastPr ?? t.last ?? t.close ?? t.lastPrice); let p = num(t.change ?? t.change24h); if (Number.isFinite(p) && Math.abs(p) < 1) p *= 100; if (!Number.isFinite(p)) { const o = num(t.open24h ?? t.openPr ?? t.open ?? t.openPrice); p = (Number.isFinite(last) && Number.isFinite(o) && o) ? ((last - o) / o * 100) : NaN; } if (Number.isFinite(p)) spotPctMap.set(sym, p); } } catch (e) { console.warn('loadSpotPctMap failed:', e.message); } } /* Candle cache for 24h high/low timestamp (5m granularity, 288 bars) */ const candleCache = new Map(); // symbol -> {asOf, high, highT, low, lowT} async function ensureCandleInfo(symbol) { const cached = candleCache.get(symbol); if (cached && Date.now() - cached.asOf < 60_000) return cached; // refresh each minute try { const j = await getJSON(API.history(symbol, '5m', 288)); const bars = Array.isArray(j?.data) ? j.data : []; // Bitget returns arrays; assume [ts, open, high, low, close, vol] order variant let hi = -Infinity, hiT = null, lo = Infinity, loT = null; for (const k of bars) { const ts = +k[0]; const h = num(k[2]); const l = num(k[3]); if (h > hi) { hi = h; hiT = ts; } if (l < lo) { lo = l; loT = ts; } } const info = { asOf: Date.now(), high: hi, highT: hiT, low: lo, lowT: loT }; candleCache.set(symbol, info); return info; } catch (e) { console.warn('ensureCandleInfo failed for', symbol, e.message); const info = { asOf: Date.now(), high: NaN, highT: null, low: NaN, lowT: null }; candleCache.set(symbol, info); return info; } } function trHTML(rank, group, d, info, futPct, spotPct) { return `<tr> <td class="left">${rank}</td> <td class="left">${group}</td> <td class="left">${d.symbol}</td> <td class="num">${fmtNum(d.last, d.last > 100 ? 2 : 6)}</td> <td class="num ${clsPct(futPct)}">${Number.isFinite(futPct) ? futPct.toFixed(2) : ''}</td> <td class="num ${clsPct(spotPct)}">${Number.isFinite(spotPct) ? spotPct.toFixed(2) : ''}</td> <td class="num">${(Number(d.volUSDT) || 0).toLocaleString('en-US')}</td> <td class="num">${fmtNum(info?.high, d.last > 100 ? 2 : 6)}</td> <td class="left">${info?.highT ? toLA(info.highT) : ''}</td> <td class="num">${fmtNum(info?.low, d.last > 100 ? 2 : 6)}</td> <td class="left">${info?.lowT ? toLA(info.lowT) : ''}</td> </tr>`; } async function loopRanks() { try { // Keep spot % map fresh (for Spot% column) await loadSpotPctMap(); const fut = await getJSON(API.futTickers('USDT-FUTURES')); const arr = (Array.isArray(fut?.data) ? fut.data : []) .filter(t => isUSDT(String(t.symbol))); // Normalize futures rows const futRows = arr.map(t => { const symbol = String(t.symbol).toUpperCase(); const last = num(t.lastPr ?? t.last ?? t.close ?? t.lastPrice); const open24 = num(t.open24h ?? t.openPr ?? t.open ?? t.openPrice); const futPct = (Number.isFinite(last) && Number.isFinite(open24) && open24) ? ((last - open24) / open24 * 100) : num(t.change24h ?? t.change); const volUSDT = num(t.usdtVolume ?? t.quoteVolume ?? t.baseVolume); const high24 = num(t.high24h ?? t.high ?? t.highestPrice); const low24 = num(t.low24h ?? t.low ?? t.lowestPrice); const spread = (Number.isFinite(high24) && Number.isFinite(low24)) ? (high24 - low24) : NaN; const dir = Number.isFinite(futPct) ? futPct : (Number.isFinite(last) && Number.isFinite(open24) ? (last - open24) : 0); return { symbol, last, futPct, volUSDT, spread, dir }; }).filter(r => Number.isFinite(r.last)); // Spread sections (Top 5 ↑ / ↓ by spread, split by direction) const spreadUp = [...futRows].filter(r => r.dir >= 0).sort((a, b) => (b.spread || 0) - (a.spread || 0)).slice(0, 5); const spreadDn = [...futRows].filter(r => r.dir < 0).sort((a, b) => (b.spread || 0) - (a.spread || 0)).slice(0, 5); // Price Comparison (24h) sections (Top 5 gainers / losers by futPct) const gainers = [...futRows].filter(r => Number.isFinite(r.futPct)).sort((a, b) => b.futPct - a.futPct).slice(0, 5); const losers = [...futRows].filter(r => Number.isFinite(r.futPct)).sort((a, b) => a.futPct - b.futPct).slice(0, 5); // Render four tables with candle info (24h high/low timestamps) async function fillTable(rows, tbodyId, groupLabel, startRank = 1) { const tbody = document.getElementById(tbodyId); if (!tbody) return; const htmlParts = []; for (let i = 0; i < rows.length; i++) { const d = rows[i]; const info = await ensureCandleInfo(d.symbol); const spotPct = spotPctMap.get(d.symbol); htmlParts.push(trHTML(startRank + i, groupLabel, d, info, d.futPct, spotPct)); } tbody.innerHTML = htmlParts.join(''); } await Promise.all([ fillTable(spreadUp, 'spreadUpTbody', 'Gainers(Spread)'), fillTable(spreadDn, 'spreadDnTbody', 'Losers(Spread)'), fillTable(gainers, 'pcUpTbody', 'Gainers(24h)'), fillTable(losers, 'pcDnTbody', 'Losers(24h)'), ]); backoff = 1; } catch (e) { backoff = Math.min(8, backoff * 1.5); console.error('Ranks loop error:', e); } finally { schedule(loopRanks); } } /* ========================= 6) START ALL LOOPS ========================= */ loopBoards(); loopRanks(); // ========== / Part 3 / 3 ==========
Simpan
Batal
Isi Zip:
Unzip
Create
Buat Folder
Buat File
Terminal / Execute
Run
Chmod Bulk
All File
All Folder
All File dan Folder
Apply