Unknown entries settle only on a sync started after them; filled-after-all and working orders seen

Sixth review round (8e66631):

- An entry of unknown outcome is judged against a good sync that STARTED
  at least 90s after it was declared unknown (lastSyncStartedAt: TopstepX's
  pass start, Tradovate's request time), not one that merely ended then —
  a sequential pass can read the account before a late fill.
- Once the broker shows its fill (a position, or realized P&L moved), the
  entry becomes an ordinary filled one instead of holding the account for
  the rest of the day.
- It never settles while the broker still shows a working order on the
  account: Tradovate now reads working orders from the sync snapshot, and
  its fill-timeout cancel checks Tradovate's answer instead of assuming it.
- A second wipe (after a restart) keeps the pending wipe's pre-wipe fund
  signature instead of overwriting it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-09-24 21:38:57 -05:00
co-authored by Claude Opus 5.5
parent 8e66631b0e
commit 57835c01f0
4 changed files with 61 additions and 10 deletions
+3
View File
@@ -242,6 +242,8 @@ export class TopstepXClient implements PlatformClient {
* fetched without it (after a wipe took the fund rows) would plan the
* account as stage 1 until the next sync. */
private reconcilePendingIds = new Set<number>();
/** See PlatformClient: when the last good pass started reading. */
public lastSyncStartedAt = 0;
/** Settles when the sync in flight ends (requestSync hands it to a caller
* that arrives mid-pass). */
private syncRunning: Promise<void> = Promise.resolve();
@@ -925,6 +927,7 @@ export class TopstepXClient implements PlatformClient {
console.warn(`[topstepx] sync incomplete — not refreshed: ${staleAccounts.join(', ')}; not counted as a good sync`);
} else if (this.historyLoaded) {
this.lastSyncAt = Date.now();
this.lastSyncStartedAt = startedAt;
}
// The resolver runs off this; its failure is not a sync failure.
try {