// Fetch the liquidation heatmap of a coin and print the largest clusters above and below the mark; optionally save the // whole grid as CSV (one row per column × price bucket with USD at risk). // // BLOCKMETRIC_API_KEY=bm_live_… BLOCKMETRIC_BASE_URL=https:///api/v1 \ // node heatmap.mjs BTC --tf 15m --hours 24 --top 5 --csv btc-heatmap.csv import fs from 'node:fs' import { parseArgs } from 'node:util' import { pathToFileURL } from 'node:url' import { Blockmetric, BlockmetricError, heatmapClusters } from 'blockmetric' const usd = (n) => `$${n >= 1e9 ? `${(n / 1e9).toFixed(2)}B` : n >= 1e6 ? `${(n / 1e6).toFixed(2)}M` : n >= 1e3 ? `${(n / 1e3).toFixed(1)}k` : n.toFixed(0)}` /** Long-form CSV of the grid: time, price, long USD, short USD (cells with zero on both sides are left out). */ export function heatmapCsv(h) { const lines = ['time,price,long_usd,short_usd,mark'] h.times.forEach((t, col) => { if (h.hasSnapshot && h.hasSnapshot[col] === false) return // gap: no snapshot for that column h.prices.forEach((p, row) => { const l = h.longCells[col]?.[row] ?? 0 const s = h.shortCells[col]?.[row] ?? 0 if (l || s) lines.push(`${t},${p},${l},${s},${h.mark[col] ?? ''}`) }) }) return `${lines.join('\n')}\n` } export function report(h, top = 5) { const c = heatmapClusters(h, { top }) if (!c) return `No heatmap snapshots for ${h.coin} in this window.` const line = (x) => ` ${String(x.price).padStart(12)} ${usd(x.usd).padStart(9)} ${((x.distance ?? 0) * 100).toFixed(2).padStart(6)} % ${x.side === 'short' ? 'shorts' : 'longs'}` return [ `${h.coin} liquidation heatmap: ${h.times.length} columns of ${h.tf ?? `${h.stepMinutes} min`} from ${h.times[0]} to ${c.time}`, `mark ${c.mark} · liquidatable now: longs ${usd(c.longUsd)}, shorts ${usd(c.shortUsd)}`, '', 'Above the mark (short liquidations):', ...(c.above.length ? c.above.map(line) : [' -']), 'Below the mark (long liquidations):', ...(c.below.length ? c.below.map(line) : [' -']), ].join('\n') } export async function main(argv = process.argv.slice(2), client = new Blockmetric({ userAgent: 'heatmap-fetch-example/1.0' })) { const { values, positionals } = parseArgs({ args: argv, allowPositionals: true, options: { tf: { type: 'string', default: '15m' }, hours: { type: 'string', default: '24' }, end: { type: 'string' }, top: { type: 'string', default: '5' }, csv: { type: 'string' } }, }) const coin = positionals[0] ?? 'BTC' const res = await client.heatmap(coin, { tf: values.tf, hours: Number(values.hours), end: values.end }) console.log(report(res.data, Number(values.top))) if (values.csv) { fs.writeFileSync(values.csv, heatmapCsv(res.data)) console.log(`\ngrid saved to ${values.csv}`) } // Show the credit next to the data when you display it to others (API Pro display licence). console.log(`\n${res.meta.attribution?.text ?? res.meta.source} · ${res.lastResponse.rateLimit.remaining ?? '?'} requests left this minute`) } if (import.meta.url === pathToFileURL(process.argv[1] ?? '').href) { main().catch((e) => { console.error(e instanceof BlockmetricError ? `${e.code}: ${e.message}${e.param ? ` (parameter ${e.param})` : ''}` : e.message) process.exitCode = 1 }) }