mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-25 07:09:59 -07:00
130 lines
3.5 KiB
JavaScript
130 lines
3.5 KiB
JavaScript
import { throttle } from "../utils/timing.js";
|
|
|
|
/**
|
|
* @param {Object} args
|
|
* @param {IChartApi} args.chart
|
|
* @param {Accessor<Set<AnySeries>>} args.seriesList
|
|
* @param {Colors} args.colors
|
|
* @param {(value: number) => string} args.formatValue
|
|
*/
|
|
export function createMinMaxMarkers({ chart, seriesList, colors, formatValue }) {
|
|
/** @type {Set<AnySeries>} */
|
|
const prevMarkerSeries = new Set();
|
|
|
|
function update() {
|
|
const timeScale = chart.timeScale();
|
|
const width = timeScale.width();
|
|
const range = timeScale.getVisibleRange();
|
|
if (!range) return;
|
|
|
|
const tLeft = timeScale.coordinateToTime(30);
|
|
const tRight = timeScale.coordinateToTime(width - 30);
|
|
const t0 = /** @type {number} */ (tLeft ?? range.from);
|
|
const t1 = /** @type {number} */ (tRight ?? range.to);
|
|
const color = colors.gray();
|
|
|
|
/** @type {Map<number, { minV: number, minT: Time, minS: AnySeries, maxV: number, maxT: Time, maxS: AnySeries }>} */
|
|
const byPane = new Map();
|
|
|
|
for (const series of seriesList()) {
|
|
if (!series.active() || !series.hasData()) continue;
|
|
|
|
const data = series.getData();
|
|
const len = data.length;
|
|
if (!len) continue;
|
|
|
|
// Binary search for start
|
|
let lo = 0, hi = len;
|
|
while (lo < hi) {
|
|
const mid = (lo + hi) >>> 1;
|
|
if (/** @type {number} */ (data[mid].time) < t0) lo = mid + 1;
|
|
else hi = mid;
|
|
}
|
|
if (lo >= len) continue;
|
|
|
|
const paneIndex = series.paneIndex;
|
|
let pane = byPane.get(paneIndex);
|
|
if (!pane) {
|
|
pane = {
|
|
minV: Infinity,
|
|
minT: /** @type {Time} */ (0),
|
|
minS: series,
|
|
maxV: -Infinity,
|
|
maxT: /** @type {Time} */ (0),
|
|
maxS: series,
|
|
};
|
|
byPane.set(paneIndex, pane);
|
|
}
|
|
|
|
for (let i = lo; i < len; i++) {
|
|
const pt = data[i];
|
|
if (/** @type {number} */ (pt.time) > t1) break;
|
|
const v = pt.low ?? pt.value;
|
|
const h = pt.high ?? pt.value;
|
|
if (v && v < pane.minV) {
|
|
pane.minV = v;
|
|
pane.minT = pt.time;
|
|
pane.minS = series;
|
|
}
|
|
if (h && h > pane.maxV) {
|
|
pane.maxV = h;
|
|
pane.maxT = pt.time;
|
|
pane.maxS = series;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set new markers
|
|
/** @type {Set<AnySeries>} */
|
|
const used = new Set();
|
|
for (const { minV, minT, minS, maxV, maxT, maxS } of byPane.values()) {
|
|
if (!Number.isFinite(minV) || !Number.isFinite(maxV) || minT === maxT)
|
|
continue;
|
|
|
|
const minM = /** @type {TimeSeriesMarker} */ ({
|
|
time: minT,
|
|
position: "belowBar",
|
|
shape: "arrowUp",
|
|
color,
|
|
size: 0,
|
|
text: formatValue(minV),
|
|
});
|
|
const maxM = /** @type {TimeSeriesMarker} */ ({
|
|
time: maxT,
|
|
position: "aboveBar",
|
|
shape: "arrowDown",
|
|
color,
|
|
size: 0,
|
|
text: formatValue(maxV),
|
|
});
|
|
|
|
used.add(minS);
|
|
used.add(maxS);
|
|
if (minS === maxS) {
|
|
minS.setMarkers([minM, maxM]);
|
|
} else {
|
|
minS.setMarkers([minM]);
|
|
maxS.setMarkers([maxM]);
|
|
}
|
|
}
|
|
|
|
// Clear stale
|
|
for (const s of prevMarkerSeries) {
|
|
if (!used.has(s)) s.clearMarkers();
|
|
}
|
|
prevMarkerSeries.clear();
|
|
for (const s of used) prevMarkerSeries.add(s);
|
|
}
|
|
|
|
function clear() {
|
|
for (const s of prevMarkerSeries) s.clearMarkers();
|
|
prevMarkerSeries.clear();
|
|
}
|
|
|
|
return {
|
|
update,
|
|
scheduleUpdate: throttle(update, 100),
|
|
clear,
|
|
};
|
|
}
|