/** * IB Symbols Operations - Fetching symbol data from IB API */ import { getMongoDBClient } from '@stock-bot/mongodb-client'; import { OperationContext } from '@stock-bot/utils'; import { IB_CONFIG } from '../shared/config'; // Fetch symbols from IB using the session headers export async function fetchSymbols(sessionHeaders: Record): Promise { const ctx = OperationContext.create('ib', 'symbols'); try { ctx.logger.info('🔍 Fetching symbols with session headers...'); // Prepare headers - include all session headers plus any additional ones const requestHeaders = { ...sessionHeaders, Accept: 'application/json, text/plain, */*', 'Accept-Language': 'en-US,en;q=0.9', 'Cache-Control': 'no-cache', Pragma: 'no-cache', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Site': 'same-origin', 'X-Requested-With': 'XMLHttpRequest', }; const requestBody = { domain: 'com', newProduct: 'all', pageNumber: 1, pageSize: 100, productCountry: IB_CONFIG.PRODUCT_COUNTRIES, productSymbol: '', productType: IB_CONFIG.PRODUCT_TYPES, sortDirection: 'asc', sortField: 'symbol', }; // Get Summary const summaryResponse = await fetch( IB_CONFIG.BASE_URL + IB_CONFIG.SUMMARY_API, { method: 'POST', headers: requestHeaders, proxy: IB_CONFIG.DEFAULT_PROXY, body: JSON.stringify(requestBody), } ); if (!summaryResponse.ok) { ctx.logger.error('❌ Summary API request failed', { status: summaryResponse.status, statusText: summaryResponse.statusText, }); return null; } const summaryData = await summaryResponse.json(); ctx.logger.info('✅ IB Summary data fetched successfully', { totalCount: summaryData[0].totalCount, }); const symbols = []; requestBody.pageSize = IB_CONFIG.PAGE_SIZE; const pageCount = Math.ceil(summaryData[0].totalCount / IB_CONFIG.PAGE_SIZE) || 0; ctx.logger.info('Fetching Symbols for IB', { pageCount }); const symbolPromises = []; for (let page = 1; page <= pageCount; page++) { requestBody.pageNumber = page; // Fetch symbols for the current page const symbolsResponse = fetch( IB_CONFIG.BASE_URL + IB_CONFIG.PRODUCTS_API, { method: 'POST', headers: requestHeaders, proxy: IB_CONFIG.DEFAULT_PROXY, body: JSON.stringify(requestBody), } ); symbolPromises.push(symbolsResponse); } const responses = await Promise.all(symbolPromises); for (const response of responses) { if (!response.ok) { ctx.logger.error('❌ Symbols API request failed', { status: response.status, statusText: response.statusText, }); return null; } const data = await response.json(); const symJson = data?.products || []; if (symJson && symJson.length > 0) { symbols.push(...symJson); } else { ctx.logger.warn('⚠️ No symbols found in response'); continue; } } if (symbols.length === 0) { ctx.logger.warn('⚠️ No symbols fetched from IB'); return null; } ctx.logger.info('✅ IB symbols fetched successfully, saving to DB...', { totalSymbols: symbols.length, }); const client = getMongoDBClient(); await client.batchUpsert('ib_symbols', symbols, ['symbol', 'exchangeId']); ctx.logger.info('Saved IB symbols to DB', { totalSymbols: symbols.length, }); return symbols; } catch (error) { ctx.logger.error('❌ Failed to fetch symbols', { error }); return null; } }