From 7b27de6d61d95409d4cfb11725d3ec7c82d1faca Mon Sep 17 00:00:00 2001 From: Brandon Li Date: Mon, 27 Apr 2026 00:41:07 -0500 Subject: [PATCH] Speed up copyTrade symbol resolution + add entry/progress logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The symbol resolution loop iterated every firm × every enabled symbol calling findFrontMonthContract (Tradovate + Yahoo APIs), causing the endpoint to hang for 30+ seconds with no logs. Now only checks the client that actually holds the position. Added logs at function entry and after each major resolution step so hangs are visible in stdout. Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/auto-trade.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/auto-trade.ts b/lib/auto-trade.ts index 763ef12..1ab8901 100644 --- a/lib/auto-trade.ts +++ b/lib/auto-trade.ts @@ -333,6 +333,7 @@ export async function runTrade(action: 'Buy' | 'Sell' | 'Auto', symbol: string) * orders for eligible accounts that haven't traded yet. */ export async function copyTrade() { + console.log('[copy-trade] called'); if (isInNoTradeWindow()) { console.log('[copy-trade] outside trading hours — skipping'); return []; @@ -346,6 +347,8 @@ export async function copyTrade() { let resolvedAction: 'Buy' | 'Sell' | null = null; let positionContractId: number | null = null; let positionedCount = 0; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let positionedClient: any = null; for (const firm of firms) { const client = clients.get(firm.id); @@ -357,6 +360,7 @@ export async function copyTrade() { if (!resolvedAction) { resolvedAction = pos.netPos > 0 ? 'Buy' : 'Sell'; positionContractId = pos.contractId; + positionedClient = client; } } } @@ -365,25 +369,24 @@ export async function copyTrade() { console.log('[copy-trade] no open positions to copy from'); return []; } + console.log(`[copy-trade] found ${positionedCount} open position(s), action=${resolvedAction}, contractId=${positionContractId}`); - // Resolve symbol from the positioned contract's contractId + // Resolve symbol from the positioned contract's contractId — only check the client that + // actually has the position. Iterating every firm × every symbol can take 30+ seconds. let resolvedSymbol: string | null = null; const instruments = getInstruments(); const enabledSymbols = instruments.filter(i => i.enabled).map(i => i.symbol); - // Try each enabled symbol to find which one matches the positioned contractId - for (const firm of firms) { - const client = clients.get(firm.id); - if (!client) continue; + if (positionedClient) { for (const sym of enabledSymbols) { - const contract = await client.findFrontMonthContract(sym); + const contract = await positionedClient.findFrontMonthContract(sym); if (contract && contract.id === positionContractId) { resolvedSymbol = sym; break; } } - if (resolvedSymbol) break; } + console.log(`[copy-trade] resolved symbol: ${resolvedSymbol ?? '(none, using fallback)'}`); // Fall back to scheduler symbol or first enabled if (!resolvedSymbol) {