import { TradovateClient } from './tradovate-class'; import { getFirms, getInstruments } from './db'; import { resolveContracts } from './contract-resolver'; import { startReporter } from './reporter'; // Use global to persist the client pool across HMR reloads in dev mode const g = global as typeof globalThis & { __tradovateClients?: Map; __tradovateClientsInitialized?: boolean; __contractResolverTimer?: ReturnType; }; function ensureMap(): Map { if (!g.__tradovateClients) { g.__tradovateClients = new Map(); } return g.__tradovateClients; } export function initClient(id: number, username: string, password: string, firmName: string): TradovateClient { const map = ensureMap(); const client = new TradovateClient(username, password, () => { console.log(`[${firmName}] sync complete — ${client.accountList.length} account(s)`); }); map.set(id, client); return client; } export function removeClient(id: number): void { const client = ensureMap().get(id); client?.disconnect(); ensureMap().delete(id); } /** Disconnect all clients and clear the pool so they are recreated on next getClients() call. */ export function resetClients(): void { const map = g.__tradovateClients; if (map) { for (const client of map.values()) { try { client.disconnect(); } catch { /* ignore */ } } } g.__tradovateClients = undefined; g.__tradovateClientsInitialized = false; } export function getClients(): Map { const map = ensureMap(); if (!g.__tradovateClientsInitialized) { g.__tradovateClientsInitialized = true; try { const firms = getFirms(); for (const firm of firms) { initClient(firm.id, firm.username, firm.password, firm.name); } console.log(`[clients] Initialized ${firms.length} Tradovate client(s)`); // Auto-resolve contracts after clients have time to authenticate setTimeout(() => triggerContractResolve(), 15_000); // Schedule daily resolve at midnight scheduleDailyResolve(); // Start master dashboard reporter startReporter(); } catch (err) { console.error('[clients] Failed to initialize clients', err); } } return map; } // ── Contract auto-resolve ──────────────────────────────────────────────────── function triggerContractResolve(): void { const map = ensureMap(); // Find first client with a valid access token let accessToken: string | null = null; for (const [, c] of map) { const token = (c as any).accessInfo?.accessToken; if (token) { accessToken = token; break; } } if (!accessToken) { console.log('[contract-resolver] No authenticated client yet — skipping resolve'); return; } const symbols = getInstruments().filter((i) => i.enabled).map((i) => i.symbol); console.log(`[contract-resolver] Resolving ${symbols.length} instruments...`); resolveContracts(symbols, accessToken) .then((results) => { const rolled = Object.entries(results).filter(([, v]) => v?.alternative && v.rolledVolume && v.frontVolume && v.rolledVolume > v.frontVolume); console.log(`[contract-resolver] Done — ${rolled.length} contract(s) rolled to higher-volume month`); }) .catch((err) => console.error('[contract-resolver] Resolve failed:', err)); } function scheduleDailyResolve(): void { // Clear any existing timer (HMR safety) if (g.__contractResolverTimer) clearInterval(g.__contractResolverTimer); // Check every minute if it's midnight (00:00) g.__contractResolverTimer = setInterval(() => { const now = new Date(); if (now.getHours() === 0 && now.getMinutes() === 0) { console.log('[contract-resolver] Midnight resolve triggered'); triggerContractResolve(); } }, 60_000); }