work on prices

This commit is contained in:
Boki 2025-06-29 12:25:38 -04:00
parent 77bba31456
commit 2f5eaef19c
2 changed files with 46 additions and 32 deletions

View file

@ -28,26 +28,25 @@ async function getOperationTracker(handler: QMHandler): Promise<QMOperationTrack
export async function updatePrices( export async function updatePrices(
this: QMHandler, this: QMHandler,
input: { input: {
symbol: string;
symbolId: number;
qmSearchCode: string; qmSearchCode: string;
lastRecordDate?: Date;
}, },
_context?: ExecutionContext _context?: ExecutionContext
): Promise<{ ): Promise<{
success: boolean; success: boolean;
symbol: string; qmSearchCode: string;
message: string; message: string;
data?: any; data?: any;
}> { }> {
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(); const sessionManager = QMSessionManager.getInstance();
sessionManager.initialize(this.cache, this.logger); sessionManager.initialize(this.cache, this.logger);
// Get a session - you'll need to add the appropriate session ID for prices // 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); const session = await sessionManager.getSession(sessionId);
if (!session || !session.uuid) { if (!session || !session.uuid) {
@ -57,15 +56,20 @@ export async function updatePrices(
try { try {
// Build API request for daily prices // Build API request for daily prices
const searchParams = new URLSearchParams({ const searchParams = new URLSearchParams({
symbol: symbol, zeroTradeDays: 'false',
symbolId: symbolId.toString(), start: lastRecordDate?.toISOString().split('T')[0] ?? '1960-01-01',
qmodTool: 'DailyPrices', interval: '1',
webmasterId: '500', marketSession: 'mkt',
days: '30' // Get last 30 days 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 // 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, { const response = await fetch(apiUrl, {
method: 'GET', method: 'GET',
@ -77,31 +81,40 @@ export async function updatePrices(
throw new Error(`QM API request failed: ${response.status} ${response.statusText}`); 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 // Update session success stats
await sessionManager.incrementSuccessfulCalls(sessionId, session.uuid); 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 // Process and store price data
if (priceData && priceData.length > 0) { if (priceData && priceData.length > 0) {
// Store prices in a separate collection // Store prices in a separate collection
const processedPrices = priceData.map((price: any) => ({ const processedPrices = priceData.map((price: any) => ({
...price, ...price,
symbol, qmSearchCode,
symbolId, dateTime: new Date(price.date),
date: new Date(price.date),
updated_at: new Date()
})); }));
await this.mongodb.batchUpsert( await this.mongodb.batchUpsert(
'qmPrices', 'qmPrices',
processedPrices, processedPrices,
['symbol', 'date'] // Unique keys ['qmSearchCode', 'date'] // Unique keys
); );
// Find the latest price date // Find the latest price date
const latestDate = processedPrices.reduce((latest: Date, price: any) => const latestDate = processedPrices.reduce((latest: Date, price: any) =>
price.date > latest ? price.date : latest, price.dateTime > latest ? price.dateTime : latest,
new Date(0) new Date(0)
); );
@ -114,26 +127,26 @@ export async function updatePrices(
}); });
this.logger.info('Prices updated successfully', { this.logger.info('Prices updated successfully', {
symbol, qmSearchCode,
priceCount: priceData.length, priceCount: priceData.length,
latestDate latestDate
}); });
return { return {
success: true, success: true,
symbol, qmSearchCode,
message: `Prices updated for ${symbol}`, message: `Prices updated for ${qmSearchCode}`,
data: { data: {
count: priceData.length, count: priceData.length,
latestDate latestDate
} }
}; };
} else { } else {
this.logger.warn('No price data returned from API', { symbol }); this.logger.warn('No price data returned from API', { qmSearchCode });
return { return {
success: false, success: false,
symbol, qmSearchCode,
message: `No price data found for symbol ${symbol}` message: `No price data found for qmSearchCode ${qmSearchCode}`
}; };
} }
@ -144,7 +157,7 @@ export async function updatePrices(
} }
this.logger.error('Error fetching prices', { this.logger.error('Error fetching prices', {
symbol, qmSearchCode,
error: error instanceof Error ? error.message : 'Unknown error' error: error instanceof Error ? error.message : 'Unknown error'
}); });
@ -156,7 +169,7 @@ export async function updatePrices(
return { return {
success: false, success: false,
symbol, qmSearchCode,
message: `Failed to fetch prices: ${error instanceof Error ? error.message : 'Unknown error'}` message: `Failed to fetch prices: ${error instanceof Error ? error.message : 'Unknown error'}`
}; };
} }
@ -177,7 +190,7 @@ export async function schedulePriceUpdates(
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 price updates', { limit, forceUpdate }); this.logger.info('Scheduling price updates', { limit, forceUpdate });
@ -219,8 +232,6 @@ export async function schedulePriceUpdates(
} }
await this.scheduleOperation('update-prices', { await this.scheduleOperation('update-prices', {
symbol: doc.symbol,
symbolId: doc.symbolId,
qmSearchCode: doc.qmSearchCode qmSearchCode: doc.qmSearchCode
}, { }, {
priority: 7, // High priority for price data priority: 7, // High priority for price data

View file

@ -8,11 +8,13 @@ import { getRandomUserAgent } from "@stock-bot/utils";
export const QM_SESSION_IDS = { export const QM_SESSION_IDS = {
LOOKUP: 'dc8c9930437f65d30f6597768800957017bac203a0a50342932757c8dfa158d6', // lookup endpoint LOOKUP: 'dc8c9930437f65d30f6597768800957017bac203a0a50342932757c8dfa158d6', // lookup endpoint
SYMBOL: '1e1d7cb1de1fd2fe52684abdea41a446919a5fe12776dfab88615ac1ce1ec2f6', // getProfiles SYMBOL: '1e1d7cb1de1fd2fe52684abdea41a446919a5fe12776dfab88615ac1ce1ec2f6', // getProfiles
PRICES: '5ad521e05faf5778d567f6d0012ec34d6cdbaeb2462f41568f66558bc7b4ced9', // getEnhancedChartData
// EDS: '', // // EDS: '', //
// FILINGS: '', // // FILINGS: '', //
// PRICES: '', // // PRICES: '', //
// FINANCIALS: '', // // FINANCIALS: '', //
// INTRADAY: '', // // INTRADAY: '', //
// '5ad521e05faf5778d567f6d0012ec34d6cdbaeb2462f41568f66558bc7b4ced9' // getEhnachedChartData
// '5ad521e05faf5778d567f6d0012ec34d6cdbaeb2462f41568f66558bc7b4ced9': [], //4488d072b // '5ad521e05faf5778d567f6d0012ec34d6cdbaeb2462f41568f66558bc7b4ced9': [], //4488d072b
// cc1cbdaf040f76db8f4c94f7d156b9b9b716e1a7509ec9c74a48a47f6b6b9f87: [], //97ff00cf3 // getQuotes // cc1cbdaf040f76db8f4c94f7d156b9b9b716e1a7509ec9c74a48a47f6b6b9f87: [], //97ff00cf3 // getQuotes
// '74963ff42f1db2320d051762b5d3950ff9eab23f9d5c5b592551b4ca0441d086': [], //32ca24e394b // getSplitsBySymbol getBrokerRatingsBySymbol getDividendsBySymbol getEarningsSurprisesBySymbol getEarningsEventsBySymbol // '74963ff42f1db2320d051762b5d3950ff9eab23f9d5c5b592551b4ca0441d086': [], //32ca24e394b // getSplitsBySymbol getBrokerRatingsBySymbol getDividendsBySymbol getEarningsSurprisesBySymbol getEarningsEventsBySymbol
@ -35,6 +37,7 @@ export const QM_CONFIG = {
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', SYMBOL_URL: 'https://app.quotemedia.com/datatool/getProfiles.json',
PRICES_URL: 'https://app.quotemedia.com/datatool/getEnhancedChartData.json',
} as const; } as const;
// Session management settings // Session management settings