diff --git a/app/api/state/route.ts b/app/api/state/route.ts index 5a28525..68caf08 100644 --- a/app/api/state/route.ts +++ b/app/api/state/route.ts @@ -98,7 +98,7 @@ export async function GET() { fundTransactions: displayFundTxns, }; }); - return { firm: f.name, connected: true, accounts, perContractFees: client.perContractFees }; + return { firm: f.name, connected: true, accounts }; }); return NextResponse.json(state); diff --git a/lib/auto-trade.ts b/lib/auto-trade.ts index 1ab8901..fecbf91 100644 --- a/lib/auto-trade.ts +++ b/lib/auto-trade.ts @@ -7,7 +7,7 @@ * signal but have since exited and are now eligible. */ -import { getFirms, isSymbolBanned, getInstruments } from './db'; +import { getFirms, isSymbolBanned, getInstruments, getBannedSymbols } from './db'; import { getClients } from './clients'; import { computeDailyTarget, resolveEffectiveConfig, POINT_VALUES } from './trading-logic'; import { getSetting } from './db'; @@ -33,6 +33,7 @@ function mapFirmConfig(firm: FirmWithAccounts): FirmConfig { firm: firm.name, username: firm.username, password: firm.password, + bannedSymbols: getBannedSymbols(firm.id), accounts: firm.accounts.map((a) => ({ prefix: a.prefix, profitTarget: a.profit_target, diff --git a/lib/clients.ts b/lib/clients.ts index 10387c1..9ce17ba 100644 --- a/lib/clients.ts +++ b/lib/clients.ts @@ -19,7 +19,7 @@ function ensureMap(): Map { export function initClient(id: number, username: string, password: string, firmName: string): TradovateClient { const map = ensureMap(); - const client = new TradovateClient(username, password, () => { + const client = new TradovateClient(username, password, async () => { console.log(`[${firmName}] sync complete — ${client.accountList.length} account(s)`); }); map.set(id, client); diff --git a/lib/tradovate-class.ts b/lib/tradovate-class.ts index 0694f7c..bd47495 100644 --- a/lib/tradovate-class.ts +++ b/lib/tradovate-class.ts @@ -7,10 +7,27 @@ import { POINT_VALUES } from './trading-logic'; import { getCachedContract, resolveContracts } from './contract-resolver'; import { saveDailyPnL, loadDailyPnL, saveAccountMeta, loadAccountMeta, saveFundTransactions, loadFundTransactions } from './db'; +/** + * Entity types Tradovate sends over the user-sync socket. Keep this as the + * single source of truth: the inline socket-payload type and the indirect + * callback list previously declared it separately and fell out of sync, which + * made the 'position' and 'cashBalance' handlers unreachable to the compiler. + */ +type TradovateEntityType = + | 'order' + | 'orderVersion' + | 'auditUserAction' + | 'command' + | 'commandReport' + | 'fill' + | 'executionReport' + | 'position' + | 'cashBalance'; + export class TradovateClient { private name: string; private password: string; - private accessInfo: AuthLoginResponse; + private accessInfo!: AuthLoginResponse; private deviceId = randomUUIDV4(); public accountList: AccountItem[] = []; @@ -70,7 +87,7 @@ export class TradovateClient { public syncComplete = false; - private ws: WebSocket; + private ws!: WebSocket; private callbackOnSyncRequest: () => Promise; /** Incrementing ID for outgoing WebSocket messages — ensures concurrent orders don't clobber each other's callbacks. */ @@ -82,15 +99,7 @@ export class TradovateClient { [id: number]: (response: any) => void; } = {}; private indirectEventCallbacks: { - entityType: - | 'order' - | 'orderVersion' - | 'auditUserAction' - | 'command' - | 'commandReport' - | 'fill' - | 'executionReport' - | 'cashBalance'; + entityType: TradovateEntityType; eventType: 'Created' | 'Updated'; // Since the entity is not always the same, we need a validator to check if the response is the one we are looking for validator: (response: any) => boolean; @@ -167,12 +176,7 @@ export class TradovateClient { | { e?: string; d?: { - entityType: - | 'order' - | 'orderVersion' - | 'auditUserAction' - | 'command' - | 'commandReport'; + entityType: TradovateEntityType; eventType: 'Created' | 'Updated'; entity: any; };