finished symbol info

This commit is contained in:
Boki 2025-06-29 10:41:12 -04:00
parent 5640444c47
commit 77bba31456
4 changed files with 73 additions and 54 deletions

View file

@ -106,6 +106,14 @@ export async function updateIntradayBars(
['symbol', 'timestamp'] // Unique keys ['symbol', 'timestamp'] // Unique keys
); );
// Update operation tracking
const tracker = await getOperationTracker(this);
await tracker.updateSymbolOperation(qmSearchCode, 'intraday_bars', {
status: 'success',
lastRecordDate: targetDate,
recordCount: barsData.length
});
this.logger.info('Intraday bars updated successfully', { this.logger.info('Intraday bars updated successfully', {
symbol, symbol,
date: targetDate, date: targetDate,
@ -124,6 +132,15 @@ export async function updateIntradayBars(
} else { } else {
// No data for this date (weekend, holiday, or no trading) // No data for this date (weekend, holiday, or no trading)
this.logger.info('No intraday data for date', { symbol, date: targetDate }); this.logger.info('No intraday data for date', { symbol, date: targetDate });
// Still update operation tracking as successful (no data is a valid result)
const tracker = await getOperationTracker(this);
await tracker.updateSymbolOperation(qmSearchCode, 'intraday_bars', {
status: 'success',
lastRecordDate: targetDate,
recordCount: 0
});
return { return {
success: true, success: true,
symbol, symbol,
@ -146,6 +163,12 @@ export async function updateIntradayBars(
error: error instanceof Error ? error.message : 'Unknown error' error: error instanceof Error ? error.message : 'Unknown error'
}); });
// Update operation tracking for failure
const tracker = await getOperationTracker(this);
await tracker.updateSymbolOperation(qmSearchCode, 'intraday_bars', {
status: 'failure'
});
return { return {
success: false, success: false,
symbol, symbol,
@ -256,14 +279,7 @@ export async function scheduleIntradayUpdates(
jobsQueued++; jobsQueued++;
} }
// Update crawl state // Note: Crawl state will be updated when the actual jobs run
await tracker.updateSymbolOperation(doc.qmSearchCode, 'intraday_bars', {
status: 'partial',
crawlState: {
finished: false,
oldestDateReached: new Date(startDate.getTime() - daysToFetch * 24 * 60 * 60 * 1000),
}
});
} else { } else {
// For update mode, just fetch today's data // For update mode, just fetch today's data
await this.scheduleOperation('update-intraday-bars', { await this.scheduleOperation('update-intraday-bars', {

View file

@ -29,25 +29,24 @@ async function getOperationTracker(handler: QMHandler): Promise<QMOperationTrack
export async function updateSymbolInfo( export async function updateSymbolInfo(
this: QMHandler, this: QMHandler,
input: { input: {
symbol: string;
qmSearchCode: string; qmSearchCode: string;
}, },
_context?: ExecutionContext _context?: ExecutionContext
): Promise<{ ): Promise<{
success: boolean; success: boolean;
symbol: string; qmSearchCode: string;
message: string; message: string;
data?: any; data?: any;
}> { }> {
const { symbol, qmSearchCode } = input; const { qmSearchCode } = input;
this.logger.info('Fetching symbol info', { symbol, qmSearchCode }); this.logger.info(`Fetching symbol info ${qmSearchCode}`, { qmSearchCode });
const sessionManager = QMSessionManager.getInstance(); const sessionManager = QMSessionManager.getInstance();
sessionManager.initialize(this.cache, this.logger); sessionManager.initialize(this.cache, this.logger);
// Get a session // Get a session
const sessionId = QM_SESSION_IDS.LOOKUP; const sessionId = QM_SESSION_IDS.SYMBOL;
const session = await sessionManager.getSession(sessionId); const session = await sessionManager.getSession(sessionId);
if (!session || !session.uuid) { if (!session || !session.uuid) {
@ -57,13 +56,15 @@ export async function updateSymbolInfo(
try { try {
// Build API request for symbol info // Build API request for symbol info
const searchParams = new URLSearchParams({ const searchParams = new URLSearchParams({
q: qmSearchCode || symbol, fullDescription: 'true',
qmodTool: 'SymbolInfo', qmodTool: 'CompanyProfile',
symbols: qmSearchCode,
lang: 'en',
pathName: '/demo/portal/company-summary.php',
webmasterId: '500', webmasterId: '500',
includeExtended: 'true' } as Record<string, string>);
});
const apiUrl = `${QM_CONFIG.LOOKUP_URL}?${searchParams.toString()}`; const apiUrl = `${QM_CONFIG.SYMBOL_URL}?${searchParams.toString()}`;
const response = await fetch(apiUrl, { const response = await fetch(apiUrl, {
method: 'GET', method: 'GET',
@ -81,43 +82,44 @@ export async function updateSymbolInfo(
await sessionManager.incrementSuccessfulCalls(sessionId, session.uuid); await sessionManager.incrementSuccessfulCalls(sessionId, session.uuid);
// Process and store symbol info // Process and store symbol info
if (symbolData && (Array.isArray(symbolData) ? symbolData.length > 0 : true)) { if (symbolData && Array.isArray(symbolData?.results?.company) && symbolData?.results?.company.length > 0) {
const symbolInfo = Array.isArray(symbolData) ? symbolData[0] : symbolData;
// Update symbol in database with new metadata // Update symbol in database with new metadata
const updateData = { const updateData = {
...symbolInfo, qmSearchCode: qmSearchCode,
symbol: symbol, profile: symbolData?.results?.company[0].profile,
qmSearchCode: qmSearchCode || symbolInfo.symbol, symbolInfo: symbolData?.results?.company[0].symbolinfo,
lastInfoUpdate: new Date(), }
updated_at: new Date()
};
await this.mongodb.updateOne( await this.mongodb.updateOne(
'qmSymbols', 'qmSymbols',
{ symbol }, { qmSearchCode, },
{ $set: updateData }, { $set: updateData },
{ upsert: true } { upsert: true }
); );
this.logger.info('Symbol info updated successfully', { // Update operation tracking
symbol, const tracker = await getOperationTracker(this);
name: symbolInfo.name, await tracker.updateSymbolOperation(qmSearchCode, 'symbol_info', {
exchange: symbolInfo.exchange status: 'success',
lastRecordDate: new Date()
});
this.logger.info(`Symbol info updated successfully ${qmSearchCode}`, {
qmSearchCode,
}); });
return { return {
success: true, success: true,
symbol, qmSearchCode,
message: `Symbol info updated for ${symbol}`, message: `Symbol info updated for ${qmSearchCode}`,
data: symbolInfo
}; };
} else { } else {
this.logger.warn('No symbol data returned from API', { symbol }); this.logger.warn('No symbol data returned from API', { qmSearchCode });
return { return {
success: false, success: false,
symbol, qmSearchCode,
message: `No data found for symbol ${symbol}` message: `No data found for symbol ${qmSearchCode}`
}; };
} }
@ -128,13 +130,19 @@ export async function updateSymbolInfo(
} }
this.logger.error('Error fetching symbol info', { this.logger.error('Error fetching symbol info', {
symbol, qmSearchCode,
error: error instanceof Error ? error.message : 'Unknown error' error: error instanceof Error ? error.message : 'Unknown error'
}); });
// Update operation tracking for failure
const tracker = await getOperationTracker(this);
await tracker.updateSymbolOperation(qmSearchCode, 'symbol_info', {
status: 'failure'
});
return { return {
success: false, success: false,
symbol, qmSearchCode,
message: `Failed to fetch symbol info: ${error instanceof Error ? error.message : 'Unknown error'}` message: `Failed to fetch symbol info: ${error instanceof Error ? error.message : 'Unknown error'}`
}; };
} }
@ -156,7 +164,7 @@ export async function scheduleSymbolInfoUpdates(
symbolsQueued: number; symbolsQueued: number;
errors: number; errors: number;
}> { }> {
const { limit = 100, forceUpdate = false } = input; const { limit = 1, forceUpdate = false } = input;
const tracker = await getOperationTracker(this); const tracker = await getOperationTracker(this);
this.logger.info('Scheduling symbol info updates', { limit, forceUpdate }); this.logger.info('Scheduling symbol info updates', { limit, forceUpdate });
@ -181,9 +189,9 @@ export async function scheduleSymbolInfoUpdates(
// Get full symbol data to include qmSearchCode // Get full symbol data to include qmSearchCode
const symbolDocs = await this.mongodb.find('qmSymbols', { const symbolDocs = await this.mongodb.find('qmSymbols', {
symbol: { $in: staleSymbols } qmSearchCode: { $in: staleSymbols }
}, { }, {
projection: { symbol: 1, qmSearchCode: 1 } projection: { qmSearchCode: 1 }
}); });
let queued = 0; let queued = 0;
@ -193,22 +201,16 @@ export async function scheduleSymbolInfoUpdates(
for (const doc of symbolDocs) { for (const doc of symbolDocs) {
try { try {
await this.scheduleOperation('update-symbol-info', { await this.scheduleOperation('update-symbol-info', {
symbol: doc.symbol, qmSearchCode: doc.qmSearchCode
qmSearchCode: doc.qmSearchCode || doc.symbol
}, { }, {
// priority: 3, // priority: 3,
// Add some delay to avoid overwhelming the API // Add some delay to avoid overwhelming the API
// delay: queued * 1000 // 1 second between jobs // delay: queued * 1000 // 1 second between jobs
}); });
// Track that we've scheduled this symbol
await tracker.updateSymbolOperation(doc.qmSearchCode, 'symbol_info', {
status: 'success'
});
queued++; queued++;
} catch (error) { } catch (error) {
this.logger.error(`Failed to schedule update for ${doc.symbol}`, { error }); this.logger.error(`Failed to schedule update for ${doc.qmSearchCode}`, { error });
errors++; errors++;
} }
} }

View file

@ -69,7 +69,7 @@ export class QMHandler extends BaseHandler<DataIngestionServices> {
@Operation('update-symbol-info') @Operation('update-symbol-info')
updateSymbolInfo = updateSymbolInfo; updateSymbolInfo = updateSymbolInfo;
@Disabled() // @Disabled()
@ScheduledOperation('schedule-symbol-info-updates', '0 */6 * * *', { @ScheduledOperation('schedule-symbol-info-updates', '0 */6 * * *', {
priority: 7, priority: 7,
immediately: false, immediately: false,

View file

@ -34,12 +34,13 @@ export const QM_CONFIG = {
BASE_URL: 'https://app.quotemedia.com', BASE_URL: 'https://app.quotemedia.com',
SESSION_PATH: '/auth/g/authenticate/dataTool/v0/500', SESSION_PATH: '/auth/g/authenticate/dataTool/v0/500',
LOOKUP_URL: 'https://app.quotemedia.com/datatool/lookup.json', LOOKUP_URL: 'https://app.quotemedia.com/datatool/lookup.json',
SYMBOL_URL: 'https://app.quotemedia.com/datatool/getProfiles.json',
} as const; } as const;
// Session management settings // Session management settings
export const SESSION_CONFIG = { export const SESSION_CONFIG = {
MIN_SESSIONS: 10, MIN_SESSIONS: 2,
MAX_SESSIONS: 10, MAX_SESSIONS: 2,
MAX_FAILED_CALLS: 3, MAX_FAILED_CALLS: 3,
SESSION_TIMEOUT: 5000, // 10 seconds SESSION_TIMEOUT: 5000, // 10 seconds
API_TIMEOUT: 30000, // 15 seconds API_TIMEOUT: 30000, // 15 seconds