work on financials

This commit is contained in:
Boki 2025-06-29 20:40:37 -04:00
parent d3850f9eaf
commit a34ff3ed1b
3 changed files with 38 additions and 28 deletions

View file

@ -29,8 +29,9 @@ export async function updateFinancials(
this: QMHandler, this: QMHandler,
input: { input: {
symbol: string; symbol: string;
symbolId: number; exchange: number;
qmSearchCode: string; qmSearchCode: string;
reportType: 'Q' | 'A'; // Quarterly or Annual
}, },
_context?: ExecutionContext _context?: ExecutionContext
): Promise<{ ): Promise<{
@ -39,15 +40,15 @@ export async function updateFinancials(
message: string; message: string;
data?: any; data?: any;
}> { }> {
const { symbol, symbolId, qmSearchCode } = input; const { symbol, exchange, qmSearchCode, reportType } = input;
this.logger.info('Fetching financials', { symbol, symbolId }); this.logger.info('Fetching financials', { symbol, exchange, qmSearchCode });
const sessionManager = QMSessionManager.getInstance(); const sessionManager = QMSessionManager.getInstance();
await sessionManager.initialize(this.cache, this.logger); await sessionManager.initialize(this.cache, this.logger);
// Get a session - you'll need to add the appropriate session ID for financials // Get a session - you'll need to add the appropriate session ID for financials
const sessionId = QM_SESSION_IDS.LOOKUP; // TODO: Update with correct session ID const sessionId = QM_SESSION_IDS.FINANCIALS; // TODO: Update with correct session ID
const session = await sessionManager.getSession(sessionId); const session = await sessionManager.getSession(sessionId);
if (!session || !session.uuid) { if (!session || !session.uuid) {
@ -57,14 +58,18 @@ export async function updateFinancials(
try { try {
// Build API request for financials // Build API request for financials
const searchParams = new URLSearchParams({ const searchParams = new URLSearchParams({
symbol: symbol, currency: 'true',
symbolId: symbolId.toString(), lang: 'en',
latestfiscaldate: 'true',
numberOfReports: '300',
pathName: '/demo/portal/company-research.php',
qmodTool: 'Financials', qmodTool: 'Financials',
webmasterId: '500' reportType: reportType,
symbol: 'AAPL',
webmasterId: '500',
}); });
// TODO: Update with correct financials endpoint // TODO: Update with correct financials endpoint
const apiUrl = `${QM_CONFIG.BASE_URL}/datatool/financials.json?${searchParams.toString()}`; const apiUrl = `${QM_CONFIG.FINANCIALS_URL}?${searchParams.toString()}`;
const response = await fetch(apiUrl, { const response = await fetch(apiUrl, {
method: 'GET', method: 'GET',
@ -89,7 +94,8 @@ export async function updateFinancials(
financialData.map((statement: any) => ({ financialData.map((statement: any) => ({
...statement, ...statement,
symbol, symbol,
symbolId, exchange,
qmSearchCode,
updated_at: new Date() updated_at: new Date()
})), })),
['symbol', 'period', 'statementType'] // Unique keys ['symbol', 'period', 'statementType'] // Unique keys
@ -170,12 +176,16 @@ export async function scheduleFinancialsUpdates(
try { try {
// Get symbols that need updating // Get symbols that need updating
const staleSymbols = await tracker.getStaleSymbols('financials_update', { const staleSymbolsQ = await tracker.getStaleSymbols('financials_update_quarterly', {
minHoursSinceRun: forceUpdate ? 0 : 24 * 7, // Weekly by default
limit
});
const staleSymbolsA = await tracker.getStaleSymbols('financials_update_annual', {
minHoursSinceRun: forceUpdate ? 0 : 24 * 7, // Weekly by default minHoursSinceRun: forceUpdate ? 0 : 24 * 7, // Weekly by default
limit limit
}); });
if (staleSymbols.length === 0) { if (staleSymbolsQ.length === 0 && staleSymbolsA.length === 0) {
this.logger.info('No symbols need financials updates'); this.logger.info('No symbols need financials updates');
return { return {
message: 'No symbols need financials updates', message: 'No symbols need financials updates',
@ -190,7 +200,7 @@ export async function scheduleFinancialsUpdates(
const symbolDocs = await this.mongodb.find('qmSymbols', { const symbolDocs = await this.mongodb.find('qmSymbols', {
qmSearchCode: { $in: staleSymbols } qmSearchCode: { $in: staleSymbols }
}, { }, {
projection: { symbol: 1, symbolId: 1, qmSearchCode: 1 } projection: { symbol: 1, exchange: 1, qmSearchCode: 1 }
}); });
let queued = 0; let queued = 0;
@ -199,23 +209,18 @@ export async function scheduleFinancialsUpdates(
// Schedule individual update jobs for each symbol // Schedule individual update jobs for each symbol
for (const doc of symbolDocs) { for (const doc of symbolDocs) {
try { try {
if (!doc.symbolId) {
this.logger.warn(`Symbol ${doc.symbol} missing symbolId, skipping`);
continue;
}
await this.scheduleOperation('update-financials', { await this.scheduleOperation('update-financials', {
symbol: doc.symbol, symbol: doc.symbol,
symbolId: doc.symbolId, exchange: doc.exchange,
qmSearchCode: doc.qmSearchCode qmSearchCode: doc.qmSearchCode,
}, { }, {
priority: 4, priority: 4,
delay: queued * 2000 // 2 seconds between jobs delay: queued * 1000 // 2 seconds between jobs
}); });
queued++; queued++;
} catch (error) { } catch (error) {
this.logger.error(`Failed to schedule financials update for ${doc.symbol}`, { error }); this.logger.error(`Failed to schedule financials update for ${doc.qmSearchCode}`, { error });
errors++; errors++;
} }
} }

View file

@ -9,10 +9,8 @@ export const QM_SESSION_IDS = {
LOOKUP: 'dc8c9930437f65d30f6597768800957017bac203a0a50342932757c8dfa158d6', // lookup endpoint LOOKUP: 'dc8c9930437f65d30f6597768800957017bac203a0a50342932757c8dfa158d6', // lookup endpoint
SYMBOL: '1e1d7cb1de1fd2fe52684abdea41a446919a5fe12776dfab88615ac1ce1ec2f6', // getProfiles SYMBOL: '1e1d7cb1de1fd2fe52684abdea41a446919a5fe12776dfab88615ac1ce1ec2f6', // getProfiles
PRICES: '5ad521e05faf5778d567f6d0012ec34d6cdbaeb2462f41568f66558bc7b4ced9', // getEnhancedChartData PRICES: '5ad521e05faf5778d567f6d0012ec34d6cdbaeb2462f41568f66558bc7b4ced9', // getEnhancedChartData
// EDS: '', // FINANCIALS: '4e4f1565fb7c9f2a8b4b32b9aa3137af684f3da8a2ce97799d3a7117b14f07be', // getFinancialsEnhancedBySymbol
// FILINGS: '', // // FILINGS: '', //
// PRICES: '', //
// FINANCIALS: '', //
// INTRADAY: '', // // INTRADAY: '', //
// '5ad521e05faf5778d567f6d0012ec34d6cdbaeb2462f41568f66558bc7b4ced9' // getEhnachedChartData // '5ad521e05faf5778d567f6d0012ec34d6cdbaeb2462f41568f66558bc7b4ced9' // getEhnachedChartData
// '5ad521e05faf5778d567f6d0012ec34d6cdbaeb2462f41568f66558bc7b4ced9': [], //4488d072b // '5ad521e05faf5778d567f6d0012ec34d6cdbaeb2462f41568f66558bc7b4ced9': [], //4488d072b
@ -38,7 +36,8 @@ export const QM_CONFIG = {
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', SYMBOL_URL: 'https://app.quotemedia.com/datatool/getProfiles.json',
PRICES_URL: 'https://app.quotemedia.com/datatool/getEnhancedChartData.json', PRICES_URL: 'https://app.quotemedia.com/datatool/getEnhancedChartData.json',
EVENTS_URL: 'https://app.quotemedia.com/datatool/getIndicatorsBySymbol.json' EVENTS_URL: 'https://app.quotemedia.com/datatool/getIndicatorsBySymbol.json',
FINANCIALS_URL: 'https://app.quotemedia.com/datatool/getFinancialsEnhancedBySymbol.json',
} as const; } as const;
// Session management settings // Session management settings

View file

@ -31,9 +31,15 @@ export const QM_OPERATIONS: QMOperationConfig[] = [
// Fundamental data operations // Fundamental data operations
{ {
name: 'financials_update', name: 'financials_update_quarterly',
type: 'standard', type: 'standard',
description: 'Update financial statements', description: 'Update quarterly financial statements',
defaultStaleHours: 24 * 7 // Weekly
},
{
name: 'financials_update_annual',
type: 'standard',
description: 'Update annual financial statements',
defaultStaleHours: 24 * 7 // Weekly defaultStaleHours: 24 * 7 // Weekly
}, },
// Corporate actions - fetched together in one API call // Corporate actions - fetched together in one API call