- 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>
85 lines
2.8 KiB
TypeScript
85 lines
2.8 KiB
TypeScript
/**
|
|
* The surface a platform trading client must expose. TradovateClient grew this
|
|
* shape organically; TopstepXClient implements the same one so the scheduler,
|
|
* reporter, state route and resolver can drive either platform without caring
|
|
* which is behind the map entry.
|
|
*
|
|
* Contract ids are `number` on Tradovate and `string` (e.g.
|
|
* "CON.F.US.ENQ.Z25") on TopstepX — everything that compares them only ever
|
|
* compares within one client, so the union is safe.
|
|
*/
|
|
|
|
export interface PlatformAccount {
|
|
id: number;
|
|
name: string;
|
|
active: boolean;
|
|
}
|
|
|
|
export interface PlatformContract {
|
|
id: number | string;
|
|
name: string;
|
|
tickSize: number;
|
|
}
|
|
|
|
export interface PlatformClient {
|
|
accountList: PlatformAccount[];
|
|
accountCashBalances: { [accountId: number]: { amount: number; realizedPnL: number } };
|
|
positions: {
|
|
[accountId: number]: {
|
|
contractId: number | string;
|
|
netPos: number;
|
|
netPrice: number;
|
|
timestamp: Date;
|
|
};
|
|
};
|
|
|
|
daysTraded: { [accountId: number]: number };
|
|
dailyPnL: { [accountId: number]: { date: string; pnl: number }[] };
|
|
fullDailyPnL: { [accountId: number]: { date: string; pnl: number }[] };
|
|
lastFundDates: { [accountId: number]: string | null };
|
|
fundTransactions: { [accountId: number]: { date: string; amount: number }[] };
|
|
priorProfit: { [accountId: number]: number };
|
|
autoLiqThresholds: { [accountId: number]: number };
|
|
|
|
fetchDaysComplete: boolean;
|
|
syncComplete: boolean;
|
|
lastFetchErrors: Record<string, string>;
|
|
lastFetchRaw: Record<string, string>;
|
|
recentEntityEvents: { entityType: string; eventType: string; entity: unknown; ts: number }[];
|
|
products: { id: number; name: string }[];
|
|
|
|
fetchDaysTraded(onlyAccountIds?: number[]): Promise<{ failedAccounts: number }>;
|
|
findFrontMonthContract(productName: string): Promise<PlatformContract | null>;
|
|
fetchInstrumentFees(symbols: string[]): Promise<{ [symbol: string]: number }>;
|
|
|
|
sendOrder(
|
|
accountId: number,
|
|
contractSymbol: string,
|
|
quantity: number,
|
|
action: 'Buy' | 'Sell',
|
|
orderType: 'Market' | 'Limit',
|
|
price?: number
|
|
): Promise<Record<string, unknown>>;
|
|
|
|
placeOrderNoWait(
|
|
accountId: number,
|
|
contractSymbol: string,
|
|
quantity: number,
|
|
action: 'Buy' | 'Sell',
|
|
orderType: 'Market' | 'Limit',
|
|
price?: number
|
|
): Promise<{ orderId?: number }>;
|
|
|
|
placeOcoExit(
|
|
accountId: number,
|
|
contractSymbol: string,
|
|
quantity: number,
|
|
exitAction: 'Buy' | 'Sell',
|
|
limitPrice: number,
|
|
stopPrice: number,
|
|
): Promise<{ orderId?: number }>;
|
|
|
|
disconnect(): void;
|
|
onFill(orderId: number, callback: (fill: Record<string, unknown>) => void): void;
|
|
}
|