Session open: a random 0–180 minute delay, kept across restarts

The day's delay was 1–20 minutes. It is now 0–180, so the evening open
lands anywhere from 5:05 to 8:05 PM CT. The day's roll is saved as a
setting: with a three-hour window, a restart during the wait (an update, a
crash) would otherwise roll again and could start the session early.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-09-27 17:30:01 -05:00
co-authored by Claude Opus 5.5
parent 7866d67676
commit ff97a59b49
3 changed files with 31 additions and 7 deletions
+17 -5
View File
@@ -705,18 +705,30 @@ function evaluateAccountForTrade(client: any, acc: { id: number; name: string; a
// ── helpers ───────────────────────────────────────────────────────────────────
// Random 1–20 minute delay added to the session open, picked once per Central
/** The longest random delay added to the session open, in minutes. */
const MAX_OPEN_DELAY_MIN = 180;
// Random 0–180 minute delay added to the session open, picked once per Central
// calendar day. Re-rolling on every check would move the open boundary between
// polls, letting a trade slip through a moment the next check would reject.
// Held in runtime() so every bundle of this module agrees on the day's roll.
// Held in runtime() so every bundle of this module agrees on the day's roll,
// and saved as a setting so a restart during the wait (an update, a crash)
// keeps the day's delay instead of rolling a new one.
function openJitterMinutes(centralDay: string): number {
const sessionOpenJitter = runtime().sessionOpenJitter;
let jitter = sessionOpenJitter.get(centralDay);
if (jitter === undefined) {
jitter = 1 + Math.floor(Math.random() * 20); // 1–20 inclusive
const [savedDay, savedMin] = (getSetting('session_open_delay') ?? '').split('|');
const saved = savedDay === centralDay ? Number(savedMin) : NaN;
if (Number.isInteger(saved) && saved >= 0 && saved <= MAX_OPEN_DELAY_MIN) {
jitter = saved;
} else {
jitter = Math.floor(Math.random() * (MAX_OPEN_DELAY_MIN + 1)); // 0–180 inclusive
setSetting('session_open_delay', `${centralDay}|${jitter}`);
console.log(`[trading-hours] session open delayed ${jitter} min today`);
}
sessionOpenJitter.clear(); // only today's entry is ever needed
sessionOpenJitter.set(centralDay, jitter);
console.log(`[trading-hours] session open delayed ${jitter} min today`);
}
return jitter;
}
@@ -740,7 +752,7 @@ function isAfterSessionClose(firm: { tradesEveryDay?: boolean }, now: Date = new
/**
* Whether the firm takes no new trades right now (see isNoTradeTime). CME
* firms follow the trading_hours setting — "full_cme" or "equity_hours" — with
* the open pushed back by the day's 1–20 minute jitter (openJitterMinutes), so
* the open pushed back by the day's 0–180 minute delay (openJitterMinutes), so
* sessions never start at the exact same time. Every-day firms ignore the
* setting and only stand down around the daily roll.
*/