Files
AtreidesAssetsFree/client/price/momentum.js
T

71 lines
2.5 KiB
JavaScript
Raw Permalink Normal View History

2026-10-29 11:57:29 +01:00
// 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(); }