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:
Kev
2026-07-11 00:51:00 -04:00
parent 80481d6f6a
commit 9ae64516e6
7 changed files with 453 additions and 3 deletions
+23
View File
@@ -318,6 +318,28 @@ async function settleLedger(sport, opts = {}) {
return { settled, pending };
}
/**
* Phase 2.5 (Session 60) — a PUBLIC grade revision. The intraday refresh
* re-graded a prop whose line moved ≥1.0 against the graded side and the
* grade dropped: update the locked row's grade and set revised_from_grade
* ONCE (the original letter is preserved forever — revisions are public,
* never silent). Only unsettled public rows for today are touched.
*/
async function applyRevision(sport, { playerKey, stat, line, side, newGrade, fromGrade }, opts = {}) {
if (!opts.sb && !isConfigured()) return { skipped: 'supabase not configured' };
const sb = opts.sb || defaultClient();
const { error } = await sb.from('ledger_entries')
.update({ grade: newGrade, revised_from_grade: fromGrade })
.is('user_id', null)
.eq('sport', String(sport).toLowerCase())
.eq('player_key', playerKey)
.eq('stat', stat)
.eq('line', line)
.eq('side', side)
.is('outcome', null);
return error ? { error: error.message } : { revised: true };
}
async function settleAllLedgers(opts = {}) {
const sports = opts.sports || ['mlb', 'nba', 'wnba', 'soccer'];
const results = [];
@@ -400,6 +422,7 @@ module.exports = {
captureClosing,
settleLedger,
settleAllLedgers,
applyRevision,
getModelAggregate,
MIN_AGG_SAMPLE,
__internals: {