History holds: only a booked fund transaction lifts them; pace, persist, and cover wipes
Follow-up to77d8c2efrom its adversarial review: - Stop-after-all no longer shuts down before an open while histories are still loading (a regression in77d8c2e): a pending account counts as in play everywhere but the weekday 2:55-5:00 PM CT stretch after the close. - Tradovate balance holds lift only when a Cash History books a fund transaction the account didn't have when the hold began; one that never does is released after 3 clean reports over 15 minutes, with a warning. The baseline comes from the balance when the report was requested, so a payout landing mid-report is held for the next one. Held accounts refetch every 5 minutes, not every sync. - The Cash History baseline is kept in account_meta, so a restart onto a failing report server still catches a payout from the DB cache. - A wipe holds the account until a Cash History rebuilds its fund transactions (the Fills report and the cache can't). - A report still queued after its polls, or answered without data, is a failure, not an empty history. - TopstepX: a newly fetched account (new, wiped, failed) waits for a sync that reconciles its funds, and the fetch starts that sync right away. - Accounts a fetch is already working through aren't fetched again by the targeted pass; the TopstepX REST fill search spans ±15 minutes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e49640498a
commit
9b2d677662
+18
-3
@@ -528,6 +528,19 @@ export function todaysSessionOpenDelay(): number {
|
||||
return openJitterMinutes(centralDay);
|
||||
}
|
||||
|
||||
/** Weekdays 2:55–5:00 PM CT: the trade date's session has closed and the 5 PM
|
||||
* roll hasn't opened the next one. The rest of the no-trade window — the
|
||||
* roll to the open, and weekends — is waiting on a session to come. */
|
||||
function isAfterSessionClose(now: Date = new Date()): boolean {
|
||||
const parts = new Intl.DateTimeFormat('en-US', {
|
||||
weekday: 'short', hour: 'numeric', minute: 'numeric', hour12: false, timeZone: 'America/Chicago',
|
||||
}).formatToParts(now);
|
||||
const get = (type: string) => parts.find((p) => p.type === type)?.value ?? '';
|
||||
if (get('weekday') === 'Sat' || get('weekday') === 'Sun') return false;
|
||||
const minutes = (parseInt(get('hour'), 10) % 24) * 60 + parseInt(get('minute'), 10);
|
||||
return minutes >= 14 * 60 + 55 && minutes < 17 * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when trading is not allowed based on the trading_hours setting.
|
||||
*
|
||||
@@ -1197,9 +1210,11 @@ function hasRemainingConfiguredAccounts(): boolean {
|
||||
|
||||
const ev = evaluateAccountForTrade(client, acc, tradeDate);
|
||||
if (ev?.eligible) return true;
|
||||
// Waiting on its history, not done: it may still owe today's trade.
|
||||
// Only while the session is open, like the unready firms above.
|
||||
if (ev?.reason === HISTORY_PENDING && !isInNoTradeWindow()) return true;
|
||||
// Waiting on its history, not done: it may still owe today's trade —
|
||||
// during the session and before it opens alike (a restart between
|
||||
// the 5 PM roll and the open loads histories right then). Only
|
||||
// once the session has closed is a still-loading account let go.
|
||||
if (ev?.reason === HISTORY_PENDING && !isAfterSessionClose()) return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user