Auto-claim without the scheduler, and no claim twice

A payout locks in profit and opens nothing, so stopping the trading no
longer stops it: a claim watcher runs the same claims once a minute while
the scheduler is off (its tick still claims while it runs), and the
dashboard's Auto-claim payouts box turns them off.

From two independent reviews of the claim path:
- A claim holds every further claim on the account until Vest's
  transactions show its WITHDRAW. A sync already out when the claim lands
  (by sync generation, not the clock) no longer writes the pre-claim
  balance back, and a fresh sync follows the claim.
- A withdraw that fails in transit (timeout, reset, 5xx) may have gone in:
  it is recorded as of unknown outcome and holds claims the same way,
  until the transactions show it, or an hour of them shows it didn't.
- claimProfit checks Vest's own account: funded (or instant), and no more
  than the balance above its starting capital.
- A claiming account is marked in flight; runClaims sends the amount it
  re-checked under the mutex.
- The watcher starts before the rest of the init can fail; settings are
  validated before any is written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-10-26 20:45:35 -05:00
co-authored by Claude Opus 5.5
parent 016444239e
commit 6f6660fdcc
7 changed files with 210 additions and 31 deletions
+4 -1
View File
@@ -15,7 +15,7 @@ import { applyBrokerHttpDefaults, applyBrokerProxy } from './outbound-proxy';
import axios from './http-client';
import { startMasterLink } from './master-link';
import { startUpdaterWatchdog } from './updater-watchdog';
import { resumeSchedulerIfPersisted } from './auto-trade';
import { resumeSchedulerIfPersisted, startClaimWatcher } from './auto-trade';
import { superviseQuotes, SUPERVISE_INTERVAL_MS, type QuoteClient } from './live-quotes';
// Use global to persist the client pool across HMR reloads in dev mode.
@@ -189,6 +189,9 @@ export function getClients(): Map<string, PlatformClient> {
if (!g.__tradovateClientsInitialized) {
g.__tradovateClientsInitialized = true;
// Payouts at target are claimed whether or not the scheduler runs —
// started first, so a later init step that throws can't leave it off.
startClaimWatcher();
try {
// Before anything dials out: a timeout on every broker HTTP call,
// and the operator's proxy (if any) under the broker clients.