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:
co-authored by
Claude Fable 5
parent
1bca441247
commit
50a5eb4713
@@ -49,6 +49,15 @@ db.exec(`
|
||||
// Tradovate credentials, and a silent DROP would be unrecoverable if this
|
||||
// change had to be rolled back. Safe to drop by hand once it has stuck.
|
||||
|
||||
// Migration: TopstepX firms authenticate with an API key instead of a
|
||||
// password. Kept in the same table so re-keying and reconciliation treat both
|
||||
// platforms alike; a Tradovate row simply leaves it blank.
|
||||
try {
|
||||
db.exec("ALTER TABLE firm_credentials ADD COLUMN api_key TEXT NOT NULL DEFAULT ''");
|
||||
} catch {
|
||||
// Column already exists
|
||||
}
|
||||
|
||||
// ── Firm credentials ─────────────────────────────────────────────────────────
|
||||
|
||||
export interface FirmCredentialRow {
|
||||
@@ -56,13 +65,15 @@ export interface FirmCredentialRow {
|
||||
firm_name: string;
|
||||
username: string;
|
||||
password: string;
|
||||
api_key: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export function getCredentials(firmId: string): { username: string; password: string } | undefined {
|
||||
const row = db.prepare('SELECT username, password FROM firm_credentials WHERE firm_id = ?').get(firmId) as
|
||||
{ username: string; password: string } | undefined;
|
||||
return row;
|
||||
export function getCredentials(firmId: string): { username: string; password: string; apiKey: string } | undefined {
|
||||
const row = db.prepare('SELECT username, password, api_key FROM firm_credentials WHERE firm_id = ?').get(firmId) as
|
||||
{ username: string; password: string; api_key: string } | undefined;
|
||||
if (!row) return undefined;
|
||||
return { username: row.username, password: row.password, apiKey: row.api_key };
|
||||
}
|
||||
|
||||
export function listCredentials(): FirmCredentialRow[] {
|
||||
@@ -72,16 +83,17 @@ export function listCredentials(): FirmCredentialRow[] {
|
||||
/** firm_name is stored alongside the id so credentials can be re-keyed by name
|
||||
* if the master's database is ever rebuilt and the firms come back with new
|
||||
* ids. See rekeyCredentialsByName in lib/firm-catalog.ts. */
|
||||
export function setCredentials(firmId: string, firmName: string, username: string, password: string): void {
|
||||
export function setCredentials(firmId: string, firmName: string, username: string, password: string, apiKey = ''): void {
|
||||
db.prepare(`
|
||||
INSERT INTO firm_credentials (firm_id, firm_name, username, password, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
INSERT INTO firm_credentials (firm_id, firm_name, username, password, api_key, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(firm_id) DO UPDATE SET
|
||||
firm_name = excluded.firm_name,
|
||||
username = excluded.username,
|
||||
password = excluded.password,
|
||||
api_key = excluded.api_key,
|
||||
updated_at = excluded.updated_at
|
||||
`).run(firmId, firmName, username, password, new Date().toISOString());
|
||||
`).run(firmId, firmName, username, password, apiKey, new Date().toISOString());
|
||||
}
|
||||
|
||||
export function clearCredentials(firmId: string): boolean {
|
||||
|
||||
Reference in New Issue
Block a user