Hold a payout that isn't booked yet, instead of re-earning it

On 2026-10-06 two Topstep Express accounts sat finished, waiting for their
payouts. The hub moved their balances the moment the payouts landed, but
reconcileFunds books a withdrawal only in the next sync. 28 seconds later
the plan read the drop as lost profit, reopened the cycle as stage 1 and
fired a full 3xNQ leg with no stop, on what should have been a drain day.
Both were liquidated against the $0 floor a payout leaves.

- TopstepX: unbookedBalanceChange holds an account while its balance is
  $200+ off its books (today's realized read fresh off the trades, so the
  5 PM roll doesn't trip it); the sync books it as before.
- Every platform: a finished cycle marks its books in account_meta; a later
  drop of $200+ with no fund row booked since holds the account until the
  payout books and moves it to its next stage. Relative to the mark, so
  history missing from before the cache cancels out; a wipe clears it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-10-07 12:13:48 -05:00
co-authored by Claude Opus 5.5
parent 0c3a36845a
commit c3b58c8d8b
4 changed files with 191 additions and 31 deletions
+5
View File
@@ -104,6 +104,11 @@ export interface PlatformClient {
* new hold, no baseline moved. Only where historyReady() has side effects
* (Tradovate's balance-drift check); elsewhere historyReady() is the read. */
peekHistoryReady?(accountId: number): boolean;
/** Dollars of balance the account's books don't explain yet, or null when
* they do. Only where balances arrive live but fund events are booked
* later (TopstepX: the hub, then the next sync); the engine holds the
* account until they're booked, or a payout plans as lost profit. */
unbookedBalanceChange?(accountId: number): number | null;
/** What the broker says about the account beyond active (DXTrade: its
* accountStatus and registration time, epoch ms or NaN). */
brokerAccountMeta?(accountId: number): { status: string; registrationTime: number } | undefined;