Spaces:
Running
Running
| import { state } from './state.js'; | |
| // --- FOOTPRINT / DELTA LOGIC --- | |
| let volBuy = 0; | |
| let volSell = 0; | |
| export function updateDelta(qty, isMaker) { | |
| // Binance API: If isMaker (m) = true, it means Taker SOLD. | |
| if(isMaker) volSell += parseFloat(qty); | |
| else volBuy += parseFloat(qty); | |
| } | |
| export function initFootprint() { | |
| const box = document.getElementById('footprint-box'); | |
| // Every 3 seconds, push a new bar | |
| setInterval(() => { | |
| if (volBuy === 0 && volSell === 0) return; // No data, do nothing | |
| const total = volBuy + volSell; | |
| const buyPct = (volBuy / total) * 100; | |
| const sellPct = (volSell / total) * 100; | |
| const bar = document.createElement('div'); | |
| // Tailwind classes for a vertical stacked bar | |
| bar.className = "w-2 h-full border-r border-gray-300 shrink-0 flex flex-col-reverse"; | |
| bar.innerHTML = ` | |
| <div style="height:${buyPct}%" class="bg-[#bef264]" title="Buy: ${volBuy.toFixed(2)}"></div> | |
| <div style="height:${sellPct}%" class="bg-[#fb7185]" title="Sell: ${volSell.toFixed(2)}"></div> | |
| `; | |
| box.appendChild(bar); | |
| // Keep list clean (remove old bars) | |
| if(box.children.length > 50) { | |
| box.removeChild(box.firstChild); | |
| } | |
| // Reset counters for next 3s interval | |
| volBuy = 0; | |
| volSell = 0; | |
| }, 3000); | |
| } | |
| // --- EXISTING UI FUNCTIONS (Keep these) --- | |
| export function updatePrice(price) { | |
| const el = document.getElementById('price-ticker'); | |
| if(el) el.innerText = parseFloat(price).toFixed(2); | |
| } | |
| export function detectTimezone() { | |
| const el = document.getElementById('timezone-label'); | |
| if(el) el.innerText = Intl.DateTimeFormat().resolvedOptions().timeZone; | |
| } | |
| export function setStatus(text, type) { | |
| const el = document.getElementById('status-text'); | |
| if(!el) return; | |
| el.innerText = text; | |
| el.className = type === 'live' ? "text-green-600 blink" : "text-orange-500 font-bold"; | |
| } | |
| export function renderDom(bids, asks) { | |
| const domBox = document.getElementById('dom-box'); | |
| if(!domBox) return; | |
| const cleanAsks = asks.slice(0, 8).reverse(); | |
| const cleanBids = bids.slice(0, 8); | |
| let rows = ''; | |
| cleanAsks.forEach(a => rows += makeRow(a[0], a[1], 'ask')); | |
| rows += `<div class="h-6 flex items-center justify-center bg-gray-100 border-y border-black font-bold text-[10px]">SPREAD</div>`; | |
| cleanBids.forEach(b => rows += makeRow(b[0], b[1], 'bid')); | |
| domBox.innerHTML = rows; | |
| } | |
| function makeRow(price, qty, type) { | |
| const color = type === 'bid' ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'; | |
| const align = type === 'bid' ? 'left-0' : 'right-0'; | |
| const w = Math.min(parseFloat(qty)*20, 100); | |
| return `<div class="flex items-center relative h-5 overflow-hidden hover:bg-gray-200"> | |
| <div class="absolute ${align} top-0 h-full ${color.split(' ')[0]}" style="width:${w}%"></div> | |
| <span class="w-1/3 z-10 pl-2 text-left">${type === 'bid' ? qty : ''}</span> | |
| <span class="w-1/3 z-10 text-center font-bold ${color.split(' ')[1]}">${parseFloat(price).toFixed(2)}</span> | |
| <span class="w-1/3 z-10 pr-2 text-right">${type === 'ask' ? qty : ''}</span> | |
| </div>`; | |
| } | |
| export function spawnBubble(qty, isMaker) { | |
| const box = document.getElementById('bubble-box'); | |
| if(!box) return; | |
| const b = document.createElement('div'); | |
| b.className = 'bubble'; | |
| const size = Math.min(qty * 50 + 20, 100); | |
| b.style.width = size + 'px'; b.style.height = size + 'px'; | |
| b.style.left = Math.random() * 80 + '%'; | |
| b.style.backgroundColor = isMaker ? '#fb7185' : '#bef264'; | |
| b.innerHTML = `<span>${parseFloat(qty).toFixed(3)}</span>`; | |
| box.appendChild(b); | |
| setTimeout(() => b.remove(), 4000); | |
| } |