Roll window instead of a roll-time base; transient outages keep payout holds

Third review round (088886e):

- Tradovate: a record still on the ended trade date waits only inside
  17:00-17:45 CT (every account rolled 17:17-17:21 on 2026-09-24); nothing
  trades on it there, so no pre-roll fill can be lost or double-counted by
  the broker's roll. Past the window an unrolled record (an idle account)
  counts from its value when first read, so nothing waits indefinitely —
  across restarts, stale bases or accounts the broker rolls only on a fill.
- One-and-done accounts also stop once this process fired a leg today.
- A balance hold is released unchecked only when Cash History is refused
  outright (4xx / errorText, e.g. a passed account's 404s); a 5xx, timeout
  or queue outage keeps the payout held until it recovers.
- A Cash History is judged against the baseline as it stood when requested,
  so a fold observed while it was in flight is not taken for a payout.
- TopstepX: a cache fallback for an account not loaded before waits for a
  funds check; accounts with no account type (not ours) aren't left
  pending; a fetch awaits a sync already running and syncs again if its
  accounts still wait, so wipe routes report the real outcome.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-09-24 19:37:14 -05:00
co-authored by Claude Opus 5.5
parent 088886ed73
commit 52ed9c75e4
3 changed files with 86 additions and 32 deletions
+5
View File
@@ -479,6 +479,11 @@ function evaluateAccountForTrade(client: any, acc: { id: number; name: string; a
if (combined.realizedPnL !== 0) {
return { ...evaluated, eligible: false, reason: `already traded today (realizedPnL=${combined.realizedPnL})` };
}
// And any leg this process fired today, whatever the broker's
// realized P&L says (a record replaced under it, a fill not booked yet).
if (legsToday(acc.id, tradeDate) > 0) {
return { ...evaluated, eligible: false, reason: 'already traded today (leg fired)' };
}
if (!isExtraDay && plan.remainingToday <= 0) {
const reason = effective.drain && plan.dayTarget === 0
? 'drain days booked — waiting for withdrawal'