feat(mse): whale trade circles on chart via aggTrade canvas overlay
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0411e43c08
commit
b3975789fa
1 changed files with 87 additions and 0 deletions
|
|
@ -104,6 +104,7 @@
|
|||
#center-pane { display: flex; flex-direction: column; overflow: hidden; border-right: 1px solid var(--border); }
|
||||
#chart-container { flex: 6; min-height: 0; position: relative; }
|
||||
#chart { width: 100%; height: 100%; }
|
||||
#whale-canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 2; }
|
||||
#log-section { flex: 3; display: flex; flex-direction: column; min-height: 0; border-top: 1px solid var(--border); }
|
||||
#log-terminal {
|
||||
flex: 1; background: #0a0a0e; padding: 8px 10px; overflow-y: auto;
|
||||
|
|
@ -239,6 +240,7 @@
|
|||
<div id="center-pane">
|
||||
<div id="chart-container">
|
||||
<div id="chart"></div>
|
||||
<canvas id="whale-canvas"></canvas>
|
||||
</div>
|
||||
<div id="log-section">
|
||||
<div class="pane-hdr">
|
||||
|
|
@ -504,6 +506,8 @@ function initChart() {
|
|||
}).observe(container);
|
||||
loadKlines();
|
||||
startKlineWs();
|
||||
initWhaleCanvas();
|
||||
startAggTradeWs();
|
||||
}
|
||||
|
||||
async function loadKlines() {
|
||||
|
|
@ -610,6 +614,89 @@ function drawSupportResistance(candles) {
|
|||
});
|
||||
}
|
||||
|
||||
// ── Whale trades (aggTrade canvas overlay) ────────
|
||||
const WHALE_THRESHOLD = 10000; // USDT
|
||||
const WHALE_MAX_AGE = 120; // saniye, ekranda kalma süresi
|
||||
const WHALE_MAX_R = 28; // maks çember yarıçapı (px)
|
||||
const WHALE_MIN_R = 6; // min çember yarıçapı (px)
|
||||
const WHALE_SCALE_AT = 500000; // bu hacimde max boyuta ulaşır
|
||||
|
||||
let whaleCanvas = null;
|
||||
let whaleCtx = null;
|
||||
let aggTradeWs = null;
|
||||
let whaleTrades = []; // { price, qty, isBuy, usdtVal, ts }
|
||||
let whaleRafId = null;
|
||||
|
||||
function startAggTradeWs() {
|
||||
if (aggTradeWs) aggTradeWs.close();
|
||||
const sym = bot.symbol.toLowerCase();
|
||||
aggTradeWs = new WebSocket(`wss://stream.binance.com:9443/ws/${sym}@aggTrade`);
|
||||
aggTradeWs.onmessage = e => {
|
||||
const t = JSON.parse(e.data);
|
||||
const price = parseFloat(t.p);
|
||||
const qty = parseFloat(t.q);
|
||||
const usdtVal = price * qty;
|
||||
if (usdtVal < WHALE_THRESHOLD) return;
|
||||
whaleTrades.push({ price, qty, isBuy: !t.m, usdtVal, ts: Date.now() / 1000 });
|
||||
if (whaleTrades.length > 500) whaleTrades.shift();
|
||||
};
|
||||
aggTradeWs.onerror = () => {};
|
||||
aggTradeWs.onclose = () => setTimeout(startAggTradeWs, 5000);
|
||||
}
|
||||
|
||||
function initWhaleCanvas() {
|
||||
whaleCanvas = document.getElementById('whale-canvas');
|
||||
whaleCtx = whaleCanvas.getContext('2d');
|
||||
const container = document.getElementById('chart-container');
|
||||
function resize() {
|
||||
whaleCanvas.width = container.clientWidth;
|
||||
whaleCanvas.height = container.clientHeight;
|
||||
}
|
||||
resize();
|
||||
new ResizeObserver(resize).observe(container);
|
||||
renderWhaleTrades();
|
||||
}
|
||||
|
||||
function renderWhaleTrades() {
|
||||
whaleRafId = requestAnimationFrame(renderWhaleTrades);
|
||||
if (!whaleCtx || !chart || !candleSeries) return;
|
||||
|
||||
const W = whaleCanvas.width;
|
||||
const H = whaleCanvas.height;
|
||||
whaleCtx.clearRect(0, 0, W, H);
|
||||
|
||||
const now = Date.now() / 1000;
|
||||
// eski trade'leri temizle
|
||||
whaleTrades = whaleTrades.filter(t => now - t.ts < WHALE_MAX_AGE);
|
||||
|
||||
for (const t of whaleTrades) {
|
||||
const x = chart.timeScale().timeToCoordinate(Math.floor(t.ts));
|
||||
const y = candleSeries.priceToCoordinate(t.price);
|
||||
if (x === null || y === null) continue;
|
||||
|
||||
const age = now - t.ts;
|
||||
const fade = Math.max(0, 1 - age / WHALE_MAX_AGE);
|
||||
const ratio = Math.min(t.usdtVal / WHALE_SCALE_AT, 1);
|
||||
const r = WHALE_MIN_R + (WHALE_MAX_R - WHALE_MIN_R) * ratio;
|
||||
const alpha = fade * (t.isBuy ? 0.75 : 0.65);
|
||||
|
||||
const color = t.isBuy
|
||||
? `rgba(46,204,113,${alpha})`
|
||||
: `rgba(231,76,60,${alpha})`;
|
||||
const stroke = t.isBuy
|
||||
? `rgba(46,204,113,${Math.min(1, alpha * 1.4)})`
|
||||
: `rgba(231,76,60,${Math.min(1, alpha * 1.4)})`;
|
||||
|
||||
whaleCtx.beginPath();
|
||||
whaleCtx.arc(x, y, r, 0, Math.PI * 2);
|
||||
whaleCtx.fillStyle = color;
|
||||
whaleCtx.fill();
|
||||
whaleCtx.strokeStyle = stroke;
|
||||
whaleCtx.lineWidth = 1.5;
|
||||
whaleCtx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
let klineWs = null;
|
||||
function startKlineWs() {
|
||||
if (klineWs) klineWs.close();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue