Sessions 5-7a: 955 tests, deployment ready

This commit is contained in:
Kev
2026-06-08 18:35:13 -04:00
parent 06b82624a2
commit 1fa04dc776
371 changed files with 49366 additions and 955 deletions
+57
View File
@@ -0,0 +1,57 @@
/**
* Steam detection — flags lines that move 1+ points in <2 hours.
*
* Inputs: a stream of { prop_key, book, line, odds, recorded_at } samples.
* The orchestrator persists samples to `line_history` and calls check() with
* the rolling window for tonight's slate.
*/
const TWO_HOURS_MS = 2 * 60 * 60_000;
const STEAM_THRESHOLD = 1;
/**
* @param {Array<{prop_key:string, book:string, line:number, odds:number|null, recorded_at:string|number}>} samples
* @returns {Array<{prop_key:string, book:string, from_line:number, to_line:number, delta:number, duration_ms:number, started_at:string, ended_at:string}>}
*/
function check(samples) {
if (!Array.isArray(samples) || samples.length === 0) return [];
// Group samples by prop_key + book and sort chronologically.
const buckets = new Map();
for (const s of samples) {
const k = `${s.prop_key}|${s.book}`;
if (!buckets.has(k)) buckets.set(k, []);
buckets.get(k).push({ ...s, t: new Date(s.recorded_at).getTime() });
}
const flags = [];
for (const [key, rows] of buckets.entries()) {
rows.sort((a, b) => a.t - b.t);
for (let i = 0; i < rows.length; i++) {
// Walk forward in time and stop as soon as the gap > window.
const start = rows[i];
for (let j = i + 1; j < rows.length; j++) {
const end = rows[j];
if (end.t - start.t > TWO_HOURS_MS) break;
const delta = end.line - start.line;
if (Math.abs(delta) >= STEAM_THRESHOLD) {
const [propKey, book] = key.split('|');
flags.push({
prop_key: propKey,
book,
from_line: start.line,
to_line: end.line,
delta,
duration_ms: end.t - start.t,
started_at: new Date(start.t).toISOString(),
ended_at: new Date(end.t).toISOString(),
});
break; // one flag per starting sample
}
}
}
}
return flags;
}
module.exports = { check, TWO_HOURS_MS, STEAM_THRESHOLD };