Restore missing fills for filled orders (the June 23-30 capture gap)
POST/api/v1/reconciliation/backfill-missing-fills
Restores fills that were never captured for an order the broker reports as filled (#1349, the inverse repair of #1348): the Order row exists, but UTM's linked executions fall short of the broker's filledQuantity, or the order's local status contradicts the broker's terminal state (for example, locally Expired while the broker now reports Filled). This action finds every order in a terminal local status (scoped to the caller's accounts, optionally narrowed to one account), confirms each against a fresh broker read, and for a confirmed gap repulls the broker's per-fill activity, matches each fill against the order's existing executions through the shared fill-identity rule (#1348), and ingests whichever fills are genuinely missing through ingestBrokerEvents alongside the broker's fresh order summary - the one write path, never a direct Order or Execution write. When the broker's status differs from what UTM held, the trade is corrected through the existing terminal-propagation path (propagateOrderTerminalToTrade), never a direct trade status write.
Most candidate orders are not a real gap (the broker agrees nothing more filled than UTM already holds) and are silently excluded from the report, counted only in ordersScanned. A confirmed gap where every broker fill already matches an existing execution (via the fill-identity rule, possibly under a different broker execution id) classifies as already_present: nothing is ingested, though the order's status may still be corrected. A confirmed gap whose broker state cannot be read, or whose fill-activity feed has no rows in the lookback window, classifies as unresolvable and is left untouched for operator dismissal.
TradeStation is excluded by construction: its synthesised fills always carry an orderId and are ingested in the same batch as the order event reporting them Filled, so a TradeStation order can never sit Filled-at-broker with fewer linked executions than the broker reports.
Defaults to a dry run, which confirms and reports without writing. A live run (dryRun false) restores each confirmed gap and writes one AuditLog row per restored order (action "reconciliation.backfill_missing_fills", entityType "Order") plus one summary row for the run. Backs the "Backfill missing fills" action on the reconcile Broker data tab. The CLI script (apps/api/src/scripts/backfill-missing-fills.ts) is a break-glass wrapper over the same service function.
Every broker read this action makes is throttled and retried with exponential backoff on HTTP 429 (#1367); a candidate that still cannot be confirmed once the retry budget is exhausted is reported in its own rateLimited bucket, distinct from unresolvable - re-run later rather than treating it as a data problem. Orders confirmed to have a fill-capture gap are resolved with a batched broker fill-activity sweep (one request can cover many orders) rather than one request per order, falling back to a per-order read only for a candidate the sweep did not cover. Supplying from and to narrows both the candidate scan and the broker sweep to that window - use it to target a known era (e.g. a post-cutover incident window) instead of scanning the account's full history. The report also breaks every found candidate down by the UTC year-month of its own timestamp (candidatesByMonth), so a fresh cluster stands out from long-tail historical noise in the same run.
Request
Responses
- 200
- 400
- 401
- 404
- 500
Confirmation report (dry run) or restore result (live run)
Validation error (including from/to supplied without the other, or from after to)
Authentication required
Account not found or access denied
Server error