heatmaps: part 9

This commit is contained in:
nym21
2026-05-31 23:35:19 +02:00
parent 7860c5a8bd
commit 3b7734a61a
8 changed files with 321 additions and 32 deletions
+104
View File
@@ -0,0 +1,104 @@
/** @import { PartialHeatmapOption } from "../../scripts/options/types.js" */
/** @import { HeatmapPoints } from "./types.js" */
import { brk } from "../../scripts/utils/client.js";
import { createAverageGrid } from "./grid.js";
import { INFERNO_LUT, logIntensityColor } from "./lut.js";
import { defaultTooltip } from "./tooltip.js";
const BINS = 2400;
const MIN_LOG = -8;
const BINS_PER_DECADE = 200;
const DEBUG = true;
const DEBUG_STARTED_AT = performance.now();
let fetchLogCount = 0;
/**
* @param {string} message
* @param {Record<string, unknown>} [data]
*/
function debug(message, data) {
if (!DEBUG) return;
const elapsed = Math.round(performance.now() - DEBUG_STARTED_AT);
console.log(`[heatmap:oracle +${elapsed}ms] ${message}`, data ?? "");
}
export const oracleRawHeatmapOption = createOracleHeatmapOption("raw", "Raw");
export const oracleEmaHeatmapOption = createOracleHeatmapOption("ema", "EMA");
/**
* @param {"raw" | "ema"} mode
* @param {string} name
* @returns {PartialHeatmapOption}
*/
function createOracleHeatmapOption(mode, name) {
return {
kind: "heatmap",
name,
title: `Oracle ${name} Histogram`,
points: {
fetch: (date, signal) => fetchOraclePoints(mode, date, signal),
},
grid: createAverageGrid({
yStart: MIN_LOG,
yEnd: MIN_LOG + BINS / BINS_PER_DECADE,
nativeRows: BINS,
}),
color: logIntensityColor({ light: INFERNO_LUT, dark: INFERNO_LUT }),
tooltip: defaultTooltip,
};
}
/**
* @param {"raw" | "ema"} mode
* @param {string} date
* @param {AbortSignal} signal
* @returns {Promise<HeatmapPoints>}
*/
async function fetchOraclePoints(mode, date, signal) {
const shouldLog = DEBUG && fetchLogCount < 20;
fetchLogCount += 1;
const startedAt = performance.now();
if (shouldLog) debug("fetch:start", { mode, date });
const values = await firstAvailable((onValue) =>
mode === "raw"
? brk.getOracleHistogramRaw(date, { signal, onValue })
: brk.getOracleHistogramEma(date, { signal, onValue }),
);
if (shouldLog) {
debug("fetch:done", {
mode,
date,
length: values.length,
elapsed: Math.round(performance.now() - startedAt),
});
}
return {
kind: "implicit",
yStart: MIN_LOG,
yStep: 1 / BINS_PER_DECADE,
values,
};
}
/**
* @param {(onValue: (value: number[]) => void) => Promise<number[]>} fetch
* @returns {Promise<number[]>}
*/
function firstAvailable(fetch) {
return new Promise((resolve, reject) => {
let settled = false;
/** @param {number[]} value */
const resolveOnce = (value) => {
if (settled) return;
settled = true;
resolve(value);
};
fetch(resolveOnce).then(resolveOnce, (error) => {
if (!settled) reject(error);
});
});
}