Problem
The bot never compares the current time (Date.now()) against scheduledStartTime to determine game phase. It relies entirely on Shipp's status field (scheduled → live → completed), which means:
-
No detection of stale scheduled times — If Shipp reports a game as scheduled but the start time has passed (API lag, outage, bug), the bot will poll indefinitely with no timeout or fallback.
-
No autonomous phase transitions — The bot can't independently determine "this game should be live by now" or "this game should be over." It's fully dependent on an external service for phase awareness.
-
No timezone handling — Times are displayed via .toLocaleString() using the host machine's local timezone. No explicit timezone management exists.
Where it matters
| Location |
What happens |
src/trading/loop.ts:135-154 |
Pre-match wait loop polls DB for status change with no time-based timeout |
src/adapters/shipp.ts:171-184 |
getLiveEvents() checks status field, never checks if scheduledStartTime has passed |
src/adapters/shipp.ts:227-229 |
Status transitions to live only when events arrive from Shipp, not based on time |
src/adapters/kalshi.ts:174-180 |
scheduledStartTime is used for ±24h market window, but never compared to Date.now() |
How scheduledStartTime is actually used today
- Display: converting to human-readable strings for console output
- Market filtering: ±24h window for Kalshi market search
- Storage: persisted in
games table but never read for phase logic
Proposed Solution
Add time-aware game phase detection throughout the bot.
1. Time-based phase inference
function inferGamePhase(game: Game): 'prematch' | 'near-start' | 'should-be-live' | 'should-be-over' {
const now = Date.now()
const start = (game.scheduledStartTime ?? 0) * 1000
if (now < start - 60 * 60 * 1000) return 'prematch' // >1hr before start
if (now < start) return 'near-start' // within 1hr of start
if (now < start + 3.5 * 60 * 60 * 1000) return 'should-be-live' // 0-3.5hrs after start
return 'should-be-over' // >3.5hrs after start
}
Use this alongside Shipp's status — trust Shipp when available, but use time as a fallback and sanity check.
2. Timeout on pre-match wait loop
If the bot is waiting for a game to go live and scheduledStartTime + buffer has passed without a status change, it should:
- Log a warning
- Attempt to refresh the schedule from Shipp
- After N retries, either proceed with live polling or exit with an error
3. Auto-refresh stale games
When status === 'scheduled' but Date.now() > scheduledStartTime, proactively re-fetch the schedule from Shipp to check if the status has changed upstream.
4. Sport-aware game duration estimates
Different sports have different expected durations. Use these for "should be over" detection:
| Sport |
Typical duration |
| NBA |
~2.5 hours |
| NFL |
~3.5 hours |
| MLB |
~3 hours |
| Soccer |
~2 hours |
| NCAAFB |
~3.5 hours |
5. Explicit timezone handling (optional)
Consider storing and displaying times in UTC internally, with configurable display timezone. This prevents issues when deploying the bot on servers in different timezones.
Impact
Without time awareness, the bot is fragile to any delay or gap in Shipp's status updates. A single slow API response can cause the bot to miss an entire game. Time-based inference provides a safety net that makes the bot resilient to upstream issues.
Related
Problem
The bot never compares the current time (
Date.now()) againstscheduledStartTimeto determine game phase. It relies entirely on Shipp'sstatusfield (scheduled→live→completed), which means:No detection of stale scheduled times — If Shipp reports a game as
scheduledbut the start time has passed (API lag, outage, bug), the bot will poll indefinitely with no timeout or fallback.No autonomous phase transitions — The bot can't independently determine "this game should be live by now" or "this game should be over." It's fully dependent on an external service for phase awareness.
No timezone handling — Times are displayed via
.toLocaleString()using the host machine's local timezone. No explicit timezone management exists.Where it matters
src/trading/loop.ts:135-154src/adapters/shipp.ts:171-184getLiveEvents()checksstatusfield, never checks ifscheduledStartTimehas passedsrc/adapters/shipp.ts:227-229liveonly when events arrive from Shipp, not based on timesrc/adapters/kalshi.ts:174-180scheduledStartTimeis used for ±24h market window, but never compared toDate.now()How
scheduledStartTimeis actually used todaygamestable but never read for phase logicProposed Solution
Add time-aware game phase detection throughout the bot.
1. Time-based phase inference
Use this alongside Shipp's status — trust Shipp when available, but use time as a fallback and sanity check.
2. Timeout on pre-match wait loop
If the bot is waiting for a game to go live and
scheduledStartTime + bufferhas passed without a status change, it should:3. Auto-refresh stale games
When
status === 'scheduled'butDate.now() > scheduledStartTime, proactively re-fetch the schedule from Shipp to check if the status has changed upstream.4. Sport-aware game duration estimates
Different sports have different expected durations. Use these for "should be over" detection:
5. Explicit timezone handling (optional)
Consider storing and displaying times in UTC internally, with configurable display timezone. This prevents issues when deploying the bot on servers in different timezones.
Impact
Without time awareness, the bot is fragile to any delay or gap in Shipp's status updates. A single slow API response can cause the bot to miss an entire game. Time-based inference provides a safety net that makes the bot resilient to upstream issues.
Related