From 2f5eaef19c7af456740498c17c24b06935d9fe02 Mon Sep 17 00:00:00 2001 From: Boki Date: Sun, 29 Jun 2025 12:25:38 -0400 Subject: [PATCH] work on prices --- .../src/handlers/qm/actions/prices.action.ts | 75 +++++++++++-------- .../src/handlers/qm/shared/config.ts | 3 + 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/apps/stock/data-ingestion/src/handlers/qm/actions/prices.action.ts b/apps/stock/data-ingestion/src/handlers/qm/actions/prices.action.ts index 7b67788..a4ce443 100644 --- a/apps/stock/data-ingestion/src/handlers/qm/actions/prices.action.ts +++ b/apps/stock/data-ingestion/src/handlers/qm/actions/prices.action.ts @@ -28,26 +28,25 @@ async function getOperationTracker(handler: QMHandler): Promise { - const { symbol, symbolId, qmSearchCode } = input; + const { qmSearchCode, lastRecordDate } = input; - this.logger.info('Fetching daily prices', { symbol, symbolId }); + this.logger.info(`Fetching daily prices ${qmSearchCode}`, { qmSearchCode }); const sessionManager = QMSessionManager.getInstance(); sessionManager.initialize(this.cache, this.logger); // Get a session - you'll need to add the appropriate session ID for prices - const sessionId = QM_SESSION_IDS.LOOKUP; // TODO: Update with correct session ID + const sessionId = QM_SESSION_IDS.PRICES; const session = await sessionManager.getSession(sessionId); if (!session || !session.uuid) { @@ -57,15 +56,20 @@ export async function updatePrices( try { // Build API request for daily prices const searchParams = new URLSearchParams({ - symbol: symbol, - symbolId: symbolId.toString(), - qmodTool: 'DailyPrices', - webmasterId: '500', - days: '30' // Get last 30 days + zeroTradeDays: 'false', + start: lastRecordDate?.toISOString().split('T')[0] ?? '1960-01-01', + interval: '1', + marketSession: 'mkt', + freq: 'day', + adjusted: 'false', + adjustmentType: 'none', + unadjusted: 'true', + datatype: 'eod', + symbol: qmSearchCode, }); - + // https://app.quotemedia.com/datatool/getEnhancedChartData.json?zeroTradeDays=false&start=2025-06-22&interval=1&marketSession=mkt&freq=day&adjusted=true&adjustmentType=none&unadjusted=false&datatype=int&symbol=AAPL // TODO: Update with correct prices endpoint - const apiUrl = `${QM_CONFIG.BASE_URL}/datatool/prices.json?${searchParams.toString()}`; + const apiUrl = `${QM_CONFIG.PRICES_URL}?${searchParams.toString()}`; const response = await fetch(apiUrl, { method: 'GET', @@ -77,31 +81,40 @@ export async function updatePrices( throw new Error(`QM API request failed: ${response.status} ${response.statusText}`); } - const priceData = await response.json(); - + const responseData = await response.json(); + // Update session success stats await sessionManager.incrementSuccessfulCalls(sessionId, session.uuid); + const priceData = responseData.results?.history[0].eoddata || []; + + if(!priceData || priceData.length === 0) { + this.logger.warn(`No price data found for symbol ${qmSearchCode}`); + return { + success: false, + qmSearchCode, + message: `No price data found for symbol ${qmSearchCode}` + }; + } + // Process and store price data if (priceData && priceData.length > 0) { // Store prices in a separate collection const processedPrices = priceData.map((price: any) => ({ ...price, - symbol, - symbolId, - date: new Date(price.date), - updated_at: new Date() + qmSearchCode, + dateTime: new Date(price.date), })); await this.mongodb.batchUpsert( 'qmPrices', processedPrices, - ['symbol', 'date'] // Unique keys + ['qmSearchCode', 'date'] // Unique keys ); // Find the latest price date const latestDate = processedPrices.reduce((latest: Date, price: any) => - price.date > latest ? price.date : latest, + price.dateTime > latest ? price.dateTime : latest, new Date(0) ); @@ -114,26 +127,26 @@ export async function updatePrices( }); this.logger.info('Prices updated successfully', { - symbol, + qmSearchCode, priceCount: priceData.length, latestDate }); return { success: true, - symbol, - message: `Prices updated for ${symbol}`, + qmSearchCode, + message: `Prices updated for ${qmSearchCode}`, data: { count: priceData.length, latestDate } }; } else { - this.logger.warn('No price data returned from API', { symbol }); + this.logger.warn('No price data returned from API', { qmSearchCode }); return { success: false, - symbol, - message: `No price data found for symbol ${symbol}` + qmSearchCode, + message: `No price data found for qmSearchCode ${qmSearchCode}` }; } @@ -144,7 +157,7 @@ export async function updatePrices( } this.logger.error('Error fetching prices', { - symbol, + qmSearchCode, error: error instanceof Error ? error.message : 'Unknown error' }); @@ -156,7 +169,7 @@ export async function updatePrices( return { success: false, - symbol, + qmSearchCode, message: `Failed to fetch prices: ${error instanceof Error ? error.message : 'Unknown error'}` }; } @@ -177,7 +190,7 @@ export async function schedulePriceUpdates( symbolsQueued: number; errors: number; }> { - const { limit = 100, forceUpdate = false } = input; + const { limit = 1, forceUpdate = false } = input; const tracker = await getOperationTracker(this); this.logger.info('Scheduling price updates', { limit, forceUpdate }); @@ -219,8 +232,6 @@ export async function schedulePriceUpdates( } await this.scheduleOperation('update-prices', { - symbol: doc.symbol, - symbolId: doc.symbolId, qmSearchCode: doc.qmSearchCode }, { priority: 7, // High priority for price data diff --git a/apps/stock/data-ingestion/src/handlers/qm/shared/config.ts b/apps/stock/data-ingestion/src/handlers/qm/shared/config.ts index e9b11f8..00c9864 100644 --- a/apps/stock/data-ingestion/src/handlers/qm/shared/config.ts +++ b/apps/stock/data-ingestion/src/handlers/qm/shared/config.ts @@ -8,11 +8,13 @@ import { getRandomUserAgent } from "@stock-bot/utils"; export const QM_SESSION_IDS = { LOOKUP: 'dc8c9930437f65d30f6597768800957017bac203a0a50342932757c8dfa158d6', // lookup endpoint SYMBOL: '1e1d7cb1de1fd2fe52684abdea41a446919a5fe12776dfab88615ac1ce1ec2f6', // getProfiles + PRICES: '5ad521e05faf5778d567f6d0012ec34d6cdbaeb2462f41568f66558bc7b4ced9', // getEnhancedChartData // EDS: '', // // FILINGS: '', // // PRICES: '', // // FINANCIALS: '', // // INTRADAY: '', // + // '5ad521e05faf5778d567f6d0012ec34d6cdbaeb2462f41568f66558bc7b4ced9' // getEhnachedChartData // '5ad521e05faf5778d567f6d0012ec34d6cdbaeb2462f41568f66558bc7b4ced9': [], //4488d072b // cc1cbdaf040f76db8f4c94f7d156b9b9b716e1a7509ec9c74a48a47f6b6b9f87: [], //97ff00cf3 // getQuotes // '74963ff42f1db2320d051762b5d3950ff9eab23f9d5c5b592551b4ca0441d086': [], //32ca24e394b // getSplitsBySymbol getBrokerRatingsBySymbol getDividendsBySymbol getEarningsSurprisesBySymbol getEarningsEventsBySymbol @@ -35,6 +37,7 @@ export const QM_CONFIG = { SESSION_PATH: '/auth/g/authenticate/dataTool/v0/500', LOOKUP_URL: 'https://app.quotemedia.com/datatool/lookup.json', SYMBOL_URL: 'https://app.quotemedia.com/datatool/getProfiles.json', + PRICES_URL: 'https://app.quotemedia.com/datatool/getEnhancedChartData.json', } as const; // Session management settings