/** * Market data routes */ import { Hono } from 'hono'; import type { IServiceContainer } from '@stock-bot/handlers'; import { getLogger } from '@stock-bot/logger'; import { processItems } from '@stock-bot/queue'; const logger = getLogger('market-data-routes'); export function createMarketDataRoutes(container: IServiceContainer) { const marketDataRoutes = new Hono(); // Market data endpoints marketDataRoutes.get('/api/live/:symbol', async c => { const symbol = c.req.param('symbol'); logger.info('Live data request', { symbol }); try { // Queue job for live data using Yahoo provider const queueManager = container.queue; if (!queueManager) { return c.json({ status: 'error', message: 'Queue manager not available' }, 503); } const queue = queueManager.getQueue('yahoo-finance'); const job = await queue.add('live-data', { handler: 'yahoo-finance', operation: 'live-data', payload: { symbol }, }); return c.json({ status: 'success', message: 'Live data job queued', jobId: job.id, symbol, }); } catch (error) { logger.error('Failed to queue live data job', { symbol, error }); return c.json({ status: 'error', message: 'Failed to queue live data job' }, 500); } }); marketDataRoutes.get('/api/historical/:symbol', async c => { const symbol = c.req.param('symbol'); const from = c.req.query('from'); const to = c.req.query('to'); logger.info('Historical data request', { symbol, from, to }); try { const fromDate = from ? new Date(from) : new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); // 30 days ago const toDate = to ? new Date(to) : new Date(); // Now // Queue job for historical data using Yahoo provider const queueManager = container.queue; if (!queueManager) { return c.json({ status: 'error', message: 'Queue manager not available' }, 503); } const queue = queueManager.getQueue('yahoo-finance'); const job = await queue.add('historical-data', { handler: 'yahoo-finance', operation: 'historical-data', payload: { symbol, from: fromDate.toISOString(), to: toDate.toISOString(), }, }); return c.json({ status: 'success', message: 'Historical data job queued', jobId: job.id, symbol, from: fromDate, to: toDate, }); } catch (error) { logger.error('Failed to queue historical data job', { symbol, from, to, error }); return c.json({ status: 'error', message: 'Failed to queue historical data job' }, 500); } }); // Batch processing endpoint using new queue system marketDataRoutes.post('/api/process-symbols', async c => { try { const { symbols, provider = 'ib', operation = 'fetch-session', useBatching = true, totalDelayHours = 0.0083, // ~30 seconds (30/3600 hours) batchSize = 10, } = await c.req.json(); if (!symbols || !Array.isArray(symbols) || symbols.length === 0) { return c.json({ status: 'error', message: 'Invalid symbols array' }, 400); } logger.info('Batch processing symbols', { count: symbols.length, provider, operation, useBatching, }); const queueManager = container.queue; if (!queueManager) { return c.json({ status: 'error', message: 'Queue manager not available' }, 503); } const result = await processItems( symbols, provider, { handler: provider, operation, totalDelayHours, useBatching, batchSize, priority: 2, retries: 2, removeOnComplete: 5, removeOnFail: 10, }, queueManager ); return c.json({ status: 'success', message: 'Batch processing initiated', result, symbols: symbols.length, }); } catch (error) { logger.error('Failed to process symbols batch', { error }); return c.json({ status: 'error', message: 'Failed to process symbols batch' }, 500); } }); return marketDataRoutes; } // Legacy export for backward compatibility export const marketDataRoutes = createMarketDataRoutes({} as IServiceContainer);