NEO-TRADE / js /socket.js
diamond-in's picture
Update js/socket.js
f450b99 verified
import { state } from './state.js';
import { getSeries, generateSmartMarkers } from './chart.js';
import { updatePrice, renderDom, spawnBubble, setStatus, updateDelta } from './ui.js';
// Main Socket Controller
export function startSocket() {
if (state.isReplay) return;
// Close existing connection if switching pairs
stopSocket();
const symbol = state.symbol.toUpperCase();
const exchange = state.exchange;
setStatus(`CONNECTING ${exchange}...`, 'warn');
if (exchange === 'BINANCE') {
startBinanceStream(symbol);
} else if (exchange === 'COINBASE') {
startCoinbaseStream(symbol);
}
}
export function stopSocket() {
if (state.socket) {
state.socket.close();
state.socket = null;
}
}
/* ==============================================
1. BINANCE LOGIC (USDT Pairs)
============================================== */
function startBinanceStream(symbol) {
// Normalize pair (e.g., BTC -> BTCUSDT)
const pair = `${symbol}USDT`.toLowerCase();
const url = `wss://stream.binance.com:9443/ws/${pair}@kline_1m/${pair}@aggTrade/${pair}@depth10`;
state.socket = new WebSocket(url);
state.socket.onopen = () => {
setStatus('● BINANCE LIVE', 'live');
};
state.socket.onmessage = (e) => {
if (state.isReplay) return;
const data = JSON.parse(e.data);
const type = data.e;
// --- A. KLINE (CANDLES + CHART MARKERS) ---
if (type === 'kline') {
handleBinanceKline(data.k);
}
// --- B. TRADES (SIDEBAR BUBBLES & FOOTPRINT) ---
if (type === 'aggTrade') {
handleBinanceTrade(data);
}
// --- C. DEPTH (ORDER BOOK) ---
if (data.lastUpdateId) {
renderDom(data.bids, data.asks);
}
};
state.socket.onclose = () => setStatus('DISCONNECTED', 'warn');
}
function handleBinanceKline(k) {
const c = {
time: k.t / 1000,
open: parseFloat(k.o),
high: parseFloat(k.h),
low: parseFloat(k.l),
close: parseFloat(k.c)
};
// 1. Update the Chart Series
getSeries().update(c);
updatePrice(c.close);
// 2. Sync Global State (Vital for markers)
// We replace the last candle in history if timestamps match, or push if new.
const lastIdx = state.candles.length - 1;
if (lastIdx >= 0 && state.candles[lastIdx].time === c.time) {
state.candles[lastIdx] = c;
} else {
state.candles.push(c);
}
// 3. REAL-TIME MARKER LOGIC (The new Feature)
// "x": true means this candle just closed. That's the perfect time to check if it was a whale candle.
if (k.x) {
// Recalculate markers based on updated history
generateSmartMarkers(state.candles);
}
}
function handleBinanceTrade(data) {
const qty = parseFloat(data.q);
const isMaker = data.m; // Binance: maker=true implies SELL pressure
// Sidebar Bubbles
if (qty > 0.02) spawnBubble(qty, isMaker);
// Delta / Footprint strip
updateDelta(qty, isMaker);
}
/* ==============================================
2. COINBASE LOGIC (USD Pairs)
============================================== */
function startCoinbaseStream(symbol) {
const pair = `${symbol}-USD`;
state.socket = new WebSocket('wss://ws-feed.exchange.coinbase.com');
state.socket.onopen = () => {
setStatus('● COINBASE LIVE', 'live');
const msg = {
type: "subscribe",
product_ids: [pair],
channels: ["level2", "matches", "ticker"]
};
state.socket.send(JSON.stringify(msg));
};
state.socket.onmessage = (e) => {
if (state.isReplay) return;
const data = JSON.parse(e.data);
// Price & Synthetic Candle
if (data.type === 'ticker') {
handleCoinbaseTicker(data);
}
// Trade Bubbles
if (data.type === 'match') {
handleCoinbaseTrade(data);
}
// DOM (Ignoring detailed L2 updates for this simple prototype)
};
}
let cbLastCandleTime = 0;
function handleCoinbaseTicker(data) {
const price = parseFloat(data.price);
const now = Math.floor(Date.now() / 1000);
const time = now - (now % 60); // Snap to minute start
updatePrice(price);
// Synthesize Candle logic
const lastIdx = state.candles.length - 1;
if (state.candles.length > 0 && state.candles[lastIdx].time === time) {
// Update Existing Current Minute
const c = state.candles[lastIdx];
c.close = price;
c.high = Math.max(c.high, price);
c.low = Math.min(c.low, price);
getSeries().update(c);
} else {
// Create New Minute Candle
const c = { time: time, open: price, high: price, low: price, close: price };
state.candles.push(c);
getSeries().update(c);
// Previous candle definitively "closed" because time shifted. Update markers now.
if (cbLastCandleTime !== 0 && cbLastCandleTime !== time) {
generateSmartMarkers(state.candles);
}
cbLastCandleTime = time;
}
}
function handleCoinbaseTrade(data) {
const qty = parseFloat(data.size);
const isSell = data.side === 'sell'; // Coinbase is straightforward
if (qty > 0.01) spawnBubble(qty, isSell);
updateDelta(qty, isSell);
}