72 lines
2.5 KiB
JavaScript
72 lines
2.5 KiB
JavaScript
// Generated, do not edit. sha256:48f77fc8fc8efc987ebeae10a9b33669b28691941eb865e07c2c66413d1ef0cf
|
|
import { getPref, setPref } from '../data/preferences.js';
|
|
|
|
export const MOMENTUM = Object.freeze({
|
|
SIZE: 60,
|
|
MAX_AGE_MS: 6 * 3600000,
|
|
});
|
|
|
|
const PREF_SCOPE = 'momentum';
|
|
|
|
const recent = (moves, nowMs) => moves.filter(m => nowMs - m.at <= MOMENTUM.MAX_AGE_MS);
|
|
|
|
export function addPrice(buffer, price, nowMs) {
|
|
if (!Number.isFinite(price) || price <= 0) return buffer;
|
|
const prev = buffer || { last: null, lastAt: 0, moves: [] };
|
|
const fresh = prev.last != null && nowMs - prev.lastAt <= MOMENTUM.MAX_AGE_MS;
|
|
if (fresh && price === prev.last) return prev;
|
|
const kept = recent(prev.moves, nowMs);
|
|
const moves = fresh ? [...kept, { at: nowMs, dir: price > prev.last ? 1 : -1 }].slice(-MOMENTUM.SIZE) : kept;
|
|
return { last: price, lastAt: nowMs, moves };
|
|
}
|
|
|
|
export function restore(saved, nowMs) {
|
|
if (!saved || !Number.isFinite(saved.last) || !Number.isFinite(saved.lastAt) || !Array.isArray(saved.moves)) return null;
|
|
const moves = saved.moves
|
|
.filter(m => m && Number.isFinite(m.at) && (m.dir === 1 || m.dir === -1))
|
|
.slice(-MOMENTUM.SIZE);
|
|
return { last: saved.last, lastAt: saved.lastAt, moves: recent(moves, nowMs) };
|
|
}
|
|
|
|
export function tally(buffer, nowMs) {
|
|
const moves = recent(buffer?.moves || [], nowMs);
|
|
let balance = 0, tone = null;
|
|
for (const m of moves) {
|
|
balance += m.dir;
|
|
if (balance !== 0) tone = balance > 0 ? 'up' : 'down';
|
|
}
|
|
const up = moves.filter(m => m.dir > 0).length;
|
|
return { up, down: moves.length - up, tone };
|
|
}
|
|
|
|
const buffers = new Map();
|
|
const loads = new Map();
|
|
|
|
export function loadMomentum(assetId, nowMs = Date.now()) {
|
|
if (assetId == null) return Promise.resolve();
|
|
if (!loads.has(assetId)) {
|
|
loads.set(assetId, (async () => {
|
|
try { buffers.set(assetId, restore(await getPref(PREF_SCOPE, String(assetId), null), nowMs)); }
|
|
catch { buffers.set(assetId, null); }
|
|
})());
|
|
}
|
|
return loads.get(assetId);
|
|
}
|
|
|
|
export async function recordSpot(assetId, price, nowMs = Date.now()) {
|
|
if (assetId == null) return;
|
|
await loadMomentum(assetId, nowMs);
|
|
const prev = buffers.get(assetId) || null;
|
|
const next = addPrice(prev, price, nowMs);
|
|
if (next === prev) return;
|
|
buffers.set(assetId, next);
|
|
try { await setPref(PREF_SCOPE, String(assetId), next); }
|
|
catch { }
|
|
}
|
|
|
|
export function momentumOf(assetId, nowMs = Date.now()) {
|
|
return tally(buffers.get(assetId) || null, nowMs);
|
|
}
|
|
|
|
export function clearMomentum() { buffers.clear(); loads.clear(); }
|