Add TopstepX platform support, drain withdrawal stages, and perfect-path calendar projection

- TopstepXClient (ProjectX Gateway): API-key auth, SignalR user hub,
  Trade/search daily P&L, emulated OCO exits, books-invariant
  withdrawal detection for $0-start accounts
- Platform-aware client pool, credentials (api_key column), and
  credential form; $0-size account types match by prefix alone
- Drain stages: min-day ($5-$15 randomized) MNQ legs for the
  configured days, then wait for the 50% withdrawal; stage pills
  show 'Stage N – Drain'
- Account calendar projects the perfect path: future days book their
  planned targets, simulated 50% withdrawals, payout marker

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-09-12 16:25:52 -05:00
co-authored by Claude Fable 5
parent 1bca441247
commit 50a5eb4713
21 changed files with 1854 additions and 142 deletions
+12 -9
View File
@@ -99,17 +99,20 @@ export function getCatalogMeta(): {
};
}
/** Firms this machine can actually trade: in the catalog AND holding
* credentials. Everything that starts a Tradovate client goes through here. */
export function getTradableFirms(): (CatalogFirm & { username: string; password: string })[] {
const result: (CatalogFirm & { username: string; password: string })[] = [];
/** Firms this machine can actually trade: in the catalog AND holding the
* credentials their platform needs — a Tradovate username + password, or a
* TopstepX API key. Everything that starts a platform client goes through
* here. */
export function getTradableFirms(): (CatalogFirm & { username: string; password: string; apiKey: string })[] {
const result: (CatalogFirm & { username: string; password: string; apiKey: string })[] = [];
for (const firm of getCachedCatalog()) {
// Only Tradovate has a client. A TopstepX firm in the catalog is
// config-only for now; handing it to the Tradovate login would just
// burn attempts against the wrong platform.
if (firm.platform !== 'tradovate') continue;
const creds = getCredentials(firm.id);
if (creds) result.push({ ...firm, ...creds });
if (!creds) continue;
if (firm.platform === 'topstepx') {
if (creds.apiKey) result.push({ ...firm, ...creds });
} else {
if (creds.username && creds.password) result.push({ ...firm, ...creds });
}
}
return result;
}