initial data-service started
This commit is contained in:
parent
8681c34529
commit
f68e620c76
6 changed files with 531 additions and 178 deletions
179
apps/data-service/src/providers/market-data.provider.ts
Normal file
179
apps/data-service/src/providers/market-data.provider.ts
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
import { Logger } from '@stock-bot/logger';
|
||||
import { HttpClient } from '@stock-bot/http';
|
||||
import createCache, { type CacheProvider } from '@stock-bot/cache';
|
||||
|
||||
export interface MarketDataResponse {
|
||||
symbol: string;
|
||||
price: number;
|
||||
timestamp: Date;
|
||||
volume?: number;
|
||||
change?: number;
|
||||
open?: number;
|
||||
high?: number;
|
||||
low?: number;
|
||||
close?: number;
|
||||
}
|
||||
|
||||
export class MarketDataProvider {
|
||||
private logger = new Logger('market-data-provider');
|
||||
private httpClient: HttpClient;
|
||||
private cache: CacheProvider = createCache('hybrid');
|
||||
private readonly CACHE_TTL = 60; // 1 minute
|
||||
|
||||
constructor() {
|
||||
this.httpClient = new HttpClient({
|
||||
timeout: 10000,
|
||||
}, this.logger);
|
||||
}
|
||||
async getLiveData(symbol: string): Promise<MarketDataResponse> {
|
||||
const cacheKey = `market-data:${symbol}`;
|
||||
|
||||
try {
|
||||
// Check cache first
|
||||
const cached = await this.cache.get(cacheKey) as MarketDataResponse | null;
|
||||
if (cached) {
|
||||
this.logger.debug('Returning cached market data', { symbol });
|
||||
return cached;
|
||||
}
|
||||
|
||||
// Generate simulated data for demo
|
||||
const data = this.generateSimulatedData(symbol);
|
||||
|
||||
// Cache the result
|
||||
await this.cache.set(cacheKey, data, this.CACHE_TTL);
|
||||
|
||||
this.logger.info('Generated live market data', { symbol, price: data.price });
|
||||
return data;
|
||||
} catch (error) {
|
||||
this.logger.error('Error fetching market data', { symbol, error });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async getHistoricalData(symbol: string, from: Date, to: Date, interval: string = '1m'): Promise<MarketDataResponse[]> {
|
||||
const cacheKey = `historical:${symbol}:${from.toISOString()}:${to.toISOString()}:${interval}`;
|
||||
|
||||
try {
|
||||
const cached = await this.cache.get(cacheKey) as MarketDataResponse[] | null;
|
||||
if (cached) {
|
||||
this.logger.debug('Returning cached historical data', { symbol, from, to });
|
||||
return cached;
|
||||
}
|
||||
|
||||
// Generate simulated historical data
|
||||
const data = this.generateHistoricalData(symbol, from, to, interval);
|
||||
|
||||
// Cache for longer time (1 hour)
|
||||
await this.cache.set(cacheKey, data, 3600);
|
||||
|
||||
this.logger.info('Generated historical market data', { symbol, from, to, count: data.length });
|
||||
return data;
|
||||
} catch (error) {
|
||||
this.logger.error('Error fetching historical data', { symbol, error });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private generateSimulatedData(symbol: string): MarketDataResponse {
|
||||
// Base prices for different symbols
|
||||
const basePrices: { [key: string]: number } = {
|
||||
'AAPL': 150,
|
||||
'GOOGL': 2500,
|
||||
'MSFT': 300,
|
||||
'TSLA': 200,
|
||||
'AMZN': 3000,
|
||||
'NVDA': 400,
|
||||
'META': 250,
|
||||
'NFLX': 400
|
||||
};
|
||||
|
||||
const basePrice = basePrices[symbol] || 100;
|
||||
|
||||
// Add some randomness (+/- 2%)
|
||||
const variation = (Math.random() - 0.5) * 0.04; // ±2%
|
||||
const price = basePrice * (1 + variation);
|
||||
const change = variation * basePrice;
|
||||
|
||||
return {
|
||||
symbol,
|
||||
price: Math.round(price * 100) / 100,
|
||||
timestamp: new Date(),
|
||||
volume: Math.floor(Math.random() * 1000000) + 500000,
|
||||
change: Math.round(change * 100) / 100,
|
||||
open: Math.round((price - change) * 100) / 100,
|
||||
high: Math.round((price + Math.abs(change * 0.5)) * 100) / 100,
|
||||
low: Math.round((price - Math.abs(change * 0.5)) * 100) / 100,
|
||||
close: Math.round(price * 100) / 100
|
||||
};
|
||||
}
|
||||
|
||||
private generateHistoricalData(symbol: string, from: Date, to: Date, interval: string): MarketDataResponse[] {
|
||||
const data: MarketDataResponse[] = [];
|
||||
const intervalMs = this.parseInterval(interval);
|
||||
|
||||
let currentTime = new Date(from);
|
||||
const endTime = new Date(to);
|
||||
|
||||
// Base price for the symbol
|
||||
const basePrices: { [key: string]: number } = {
|
||||
'AAPL': 150,
|
||||
'GOOGL': 2500,
|
||||
'MSFT': 300,
|
||||
'TSLA': 200,
|
||||
'AMZN': 3000,
|
||||
'NVDA': 400,
|
||||
'META': 250,
|
||||
'NFLX': 400
|
||||
};
|
||||
|
||||
let basePrice = basePrices[symbol] || 100;
|
||||
|
||||
while (currentTime <= endTime) {
|
||||
// Add some trend and randomness
|
||||
const trend = (Math.random() - 0.5) * 0.01; // Small trend
|
||||
const variation = (Math.random() - 0.5) * 0.02; // Random variation
|
||||
|
||||
basePrice = basePrice * (1 + trend + variation);
|
||||
const change = basePrice * variation;
|
||||
|
||||
data.push({
|
||||
symbol,
|
||||
price: Math.round(basePrice * 100) / 100,
|
||||
timestamp: new Date(currentTime),
|
||||
volume: Math.floor(Math.random() * 1000000) + 500000,
|
||||
change: Math.round(change * 100) / 100,
|
||||
open: Math.round((basePrice - change) * 100) / 100,
|
||||
high: Math.round((basePrice + Math.abs(change * 0.5)) * 100) / 100,
|
||||
low: Math.round((basePrice - Math.abs(change * 0.5)) * 100) / 100,
|
||||
close: Math.round(basePrice * 100) / 100
|
||||
});
|
||||
|
||||
currentTime = new Date(currentTime.getTime() + intervalMs);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private parseInterval(interval: string): number {
|
||||
const value = parseInt(interval.slice(0, -1));
|
||||
const unit = interval.slice(-1).toLowerCase();
|
||||
|
||||
switch (unit) {
|
||||
case 's': return value * 1000;
|
||||
case 'm': return value * 60 * 1000;
|
||||
case 'h': return value * 60 * 60 * 1000;
|
||||
case 'd': return value * 24 * 60 * 60 * 1000;
|
||||
default: return 60 * 1000; // Default to 1 minute
|
||||
}
|
||||
}
|
||||
|
||||
async clearCache(): Promise<void> {
|
||||
this.logger.info('Clearing market data cache');
|
||||
// Note: Cache provider limitations - would need proper key tracking
|
||||
}
|
||||
|
||||
async shutdown(): Promise<void> {
|
||||
this.logger.info('Shutting down MarketDataProvider');
|
||||
}
|
||||
}
|
||||
|
||||
export const marketDataProvider = new MarketDataProvider();
|
||||
Loading…
Add table
Add a link
Reference in a new issue