Session D (night2): Phase 2.5 — intraday refresh + directional movement
Odds-only refresh every 20 min during slate hours (noon–midnight ET), in-process (INTRADAY_REFRESH=0 kill switch; skips full-snapshot slots): - signed delta RELATIVE TO THE GRADED SIDE; direction is the signal. - WITH the grade → STEAM badge, never a re-grade. - AGAINST >=1.0 → re-grade THAT PROP ONLY at the real current line: holds/refuses → VALUE (better entry, same read); drops → PUBLIC revision (grade updates, revised_from_grade preserves the ORIGINAL forever, UI strikethrough on slate strips + ledger cards). - Every run recaptures closing_line/odds → the close is now refresh- fidelity; SYNC goes live by dropping SNAPSHOT_EXPECTED_INTERVAL to 1200. - Ticker MOVE events feed from the refresh (>=1.0 moves). - QUOTA MATH: 36 runs/day/sport x 4 sports <= 144 PropLine calls/day vs 9,000/day free capacity (3 keys x 3,000). Re-grades are internal compute. - POST /api/internal/refresh/all for manual runs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -115,10 +115,39 @@ function startSnapshotScheduler(opts = {}) {
|
||||
}
|
||||
};
|
||||
|
||||
const interval = setInterval(tick, 60_000);
|
||||
// Session 60 (night2/D) — Phase 2.5 intraday refresh. Every
|
||||
// REFRESH_MINUTES during slate hours (noon–midnight ET), odds-only:
|
||||
// STEAM/VALUE badges, ≥1.0-against re-grades with public revisions,
|
||||
// refresh-fidelity closing capture, higher-frequency ticker MOVEs.
|
||||
// Skips the top of scheduled snapshot hours (the full run owns those).
|
||||
// Kill switch: INTRADAY_REFRESH=0.
|
||||
const REFRESH_MINUTES = Math.max(5, parseInt(process.env.INTRADAY_REFRESH_MINUTES || '20', 10) || 20);
|
||||
const refreshAll = opts.runAllIntradayRefreshes || require('./services/intradayRefreshService').runAllIntradayRefreshes;
|
||||
const inSlateHours = opts.inSlateHours || require('./services/intradayRefreshService').inSlateHours;
|
||||
let lastRefreshSlot = null;
|
||||
const refreshTick = async () => {
|
||||
if (process.env.INTRADAY_REFRESH === '0') return;
|
||||
const d = now();
|
||||
if (!inSlateHours(d)) return;
|
||||
if (d.getUTCMinutes() % REFRESH_MINUTES !== 0) return;
|
||||
if (d.getUTCMinutes() === 0 && HOURS_UTC.includes(d.getUTCHours())) return; // full snapshot owns this slot
|
||||
const slot = `${d.toISOString().slice(0, 13)}-${d.getUTCMinutes()}`;
|
||||
if (slot === lastRefreshSlot) return;
|
||||
lastRefreshSlot = slot;
|
||||
try {
|
||||
const results = await refreshAll();
|
||||
const touched = results.filter((r) => r.status === 'ok');
|
||||
const revised = results.reduce((n, r) => n + (r.revised || 0), 0);
|
||||
if (touched.length > 0) console.log(`[intraday] refresh — ${touched.length} sports, ${revised} public revisions`);
|
||||
} catch (e) {
|
||||
console.warn('[intraday] refresh failed:', e.message);
|
||||
}
|
||||
};
|
||||
|
||||
const interval = setInterval(() => { void tick(); void refreshTick(); }, 60_000);
|
||||
if (interval.unref) interval.unref();
|
||||
console.log(`[snapshotScheduler] armed — SNAPSHOT_CRON=${process.env.SNAPSHOT_CRON}, hours=${HOURS_UTC.join(',')} UTC`);
|
||||
return { interval, tick };
|
||||
console.log(`[snapshotScheduler] armed — SNAPSHOT_CRON=${process.env.SNAPSHOT_CRON}, hours=${HOURS_UTC.join(',')} UTC, intraday=${process.env.INTRADAY_REFRESH === '0' ? 'off' : `${REFRESH_MINUTES}m (slate hours)`}`);
|
||||
return { interval, tick, refreshTick };
|
||||
}
|
||||
|
||||
module.exports = { startSnapshotScheduler, HOURS_UTC, mostRecentExpectedSlot, isSnapshotOverdue };
|
||||
|
||||
Reference in New Issue
Block a user