starting to add qm sessions and symbols

This commit is contained in:
Boki 2025-06-16 22:07:40 -04:00
parent e9ff913b7e
commit f05d26d703
13 changed files with 652 additions and 432 deletions

View file

@ -1,182 +1,68 @@
import { getLogger } from '@stock-bot/logger';
import { ProviderConfig } from '../services/provider-registry.service';
import { providerRegistry, type ProviderConfigWithSchedule } from '@stock-bot/queue';
const logger = getLogger('qm-provider');
export const qmProvider: ProviderConfig = {
name: 'qm',
operations: {
'live-data': async (payload: { symbol: string; fields?: string[] }) => {
logger.info('Fetching live data from qm', { symbol: payload.symbol });
// Initialize and register the IB provider
export function initializeQMProvider() {
logger.info('Registering IB provider with scheduled jobs...');
// Simulate qm API call
const mockData = {
symbol: payload.symbol,
price: Math.random() * 1000 + 100,
volume: Math.floor(Math.random() * 1000000),
change: (Math.random() - 0.5) * 20,
changePercent: (Math.random() - 0.5) * 5,
timestamp: new Date().toISOString(),
source: 'qm',
fields: payload.fields || ['price', 'volume', 'change'],
};
const qmProviderConfig: ProviderConfigWithSchedule = {
name: 'qm',
operations: {
'create-sessions': async () => {
logger.info('Creating QM sessions...');
const { createSessions } = await import('./qm.tasks');
await createSessions();
logger.info('QM sessions created successfully');
return { success: true, message: 'QM sessions created successfully' };
},
'search-symbols': async () => {
logger.info('Starting QM symbol search...');
const { fetchSymbols } = await import('./qm.tasks');
const symbols = await fetchSymbols();
// Simulate network delay
await new Promise(resolve => setTimeout(resolve, 100 + Math.random() * 200));
return mockData;
if (symbols && symbols.length > 0) {
logger.info('QM symbol search completed successfully', { count: symbols.length });
return {
success: true,
message: 'QM symbol search completed successfully',
count: symbols.length,
symbols: symbols.slice(0, 10), // Return first 10 symbols as sample
};
} else {
logger.warn('QM symbol search returned no results');
return {
success: false,
message: 'No symbols found',
count: 0,
};
}
},
},
'historical-data': async (payload: {
symbol: string;
from: Date;
to: Date;
interval?: string;
fields?: string[];
}) => {
logger.info('Fetching historical data from qm', {
symbol: payload.symbol,
from: payload.from,
to: payload.to,
interval: payload.interval || '1d',
});
scheduledJobs: [
{
type: 'create-sessions',
operation: 'create-sessions',
payload: {},
cronPattern: '*/15 * * * * *', // Every minute
priority: 7,
immediately: true,
description: 'Create and maintain QM sessions',
},
{
type: 'search-symbols',
operation: 'search-symbols',
payload: {},
cronPattern: '*/1 * * * *', // Every minute
priority: 10,
immediately: false,
description: 'Comprehensive symbol search using QM API',
},
],
};
// Generate mock historical data
const days = Math.ceil(
(payload.to.getTime() - payload.from.getTime()) / (1000 * 60 * 60 * 24)
);
const data = [];
for (let i = 0; i < Math.min(days, 100); i++) {
const date = new Date(payload.from.getTime() + i * 24 * 60 * 60 * 1000);
data.push({
date: date.toISOString().split('T')[0],
open: Math.random() * 1000 + 100,
high: Math.random() * 1000 + 100,
low: Math.random() * 1000 + 100,
close: Math.random() * 1000 + 100,
volume: Math.floor(Math.random() * 1000000),
source: 'qm',
});
}
// Simulate network delay
await new Promise(resolve => setTimeout(resolve, 200 + Math.random() * 300));
return {
symbol: payload.symbol,
interval: payload.interval || '1d',
data,
source: 'qm',
totalRecords: data.length,
};
},
'batch-quotes': async (payload: { symbols: string[]; fields?: string[] }) => {
logger.info('Fetching batch quotes from qm', {
symbols: payload.symbols,
count: payload.symbols.length,
});
const quotes = payload.symbols.map(symbol => ({
symbol,
price: Math.random() * 1000 + 100,
volume: Math.floor(Math.random() * 1000000),
change: (Math.random() - 0.5) * 20,
timestamp: new Date().toISOString(),
source: 'qm',
}));
// Simulate network delay
await new Promise(resolve => setTimeout(resolve, 300 + Math.random() * 200));
return {
quotes,
source: 'qm',
timestamp: new Date().toISOString(),
totalSymbols: payload.symbols.length,
};
},
'company-profile': async (payload: { symbol: string }) => {
logger.info('Fetching company profile from qm', { symbol: payload.symbol });
// Simulate company profile data
const profile = {
symbol: payload.symbol,
companyName: `${payload.symbol} Corporation`,
sector: 'Technology',
industry: 'Software',
description: `${payload.symbol} is a leading technology company.`,
marketCap: Math.floor(Math.random() * 1000000000000),
employees: Math.floor(Math.random() * 100000),
website: `https://www.${payload.symbol.toLowerCase()}.com`,
source: 'qm',
};
await new Promise(resolve => setTimeout(resolve, 150 + Math.random() * 100));
return profile;
},
'options-chain': async (payload: { symbol: string; expiration?: string }) => {
logger.info('Fetching options chain from qm', {
symbol: payload.symbol,
expiration: payload.expiration,
});
// Generate mock options data
const strikes = Array.from({ length: 20 }, (_, i) => 100 + i * 5);
const calls = strikes.map(strike => ({
strike,
bid: Math.random() * 10,
ask: Math.random() * 10 + 0.5,
volume: Math.floor(Math.random() * 1000),
openInterest: Math.floor(Math.random() * 5000),
}));
const puts = strikes.map(strike => ({
strike,
bid: Math.random() * 10,
ask: Math.random() * 10 + 0.5,
volume: Math.floor(Math.random() * 1000),
openInterest: Math.floor(Math.random() * 5000),
}));
await new Promise(resolve => setTimeout(resolve, 400 + Math.random() * 300));
return {
symbol: payload.symbol,
expiration:
payload.expiration ||
new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
calls,
puts,
source: 'qm',
};
},
},
scheduledJobs: [
// {
// type: 'qm-premium-refresh',
// operation: 'batch-quotes',
// payload: { symbols: ['AAPL', 'GOOGL', 'MSFT'] },
// cronPattern: '*/2 * * * *', // Every 2 minutes
// priority: 7,
// description: 'Refresh premium quotes with detailed market data'
// },
// {
// type: 'qm-options-update',
// operation: 'options-chain',
// payload: { symbol: 'SPY' },
// cronPattern: '*/10 * * * *', // Every 10 minutes
// priority: 5,
// description: 'Update options chain data for SPY ETF'
// },
// {
// type: 'qm-profiles',
// operation: 'company-profile',
// payload: { symbol: 'AAPL' },
// cronPattern: '0 9 * * 1-5', // Weekdays at 9 AM
// priority: 3,
// description: 'Update company profile data'
// }
],
};
providerRegistry.registerWithSchedule(qmProviderConfig);
logger.info('IB provider registered successfully with scheduled jobs');
}