diff --git a/apps/ingestor/package.json b/apps/ingestor/package.json index ea938ce..fb74a43 100644 --- a/apps/ingestor/package.json +++ b/apps/ingestor/package.json @@ -15,6 +15,7 @@ "test:cov": "jest --coverage" }, "dependencies": { + "@oracle-stocks/shared": "*", "@nestjs/common": "^10.0.0", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", diff --git a/apps/ingestor/src/controllers/prices.controller.ts b/apps/ingestor/src/controllers/prices.controller.ts index 9a2335c..b01b8f3 100644 --- a/apps/ingestor/src/controllers/prices.controller.ts +++ b/apps/ingestor/src/controllers/prices.controller.ts @@ -1,5 +1,6 @@ import { Controller, Get, Logger } from '@nestjs/common'; -import { PriceFetcherService, RawPrice } from '../services/price-fetcher.service'; +import { RawPrice } from '@oracle-stocks/shared'; +import { PriceFetcherService } from '../services/price-fetcher.service'; @Controller('prices') export class PricesController { diff --git a/apps/ingestor/src/providers/index.ts b/apps/ingestor/src/providers/index.ts new file mode 100644 index 0000000..44050cd --- /dev/null +++ b/apps/ingestor/src/providers/index.ts @@ -0,0 +1,2 @@ +export { PriceProvider } from './provider.interface'; +export { MockProvider } from './mock.provider'; diff --git a/apps/ingestor/src/providers/mock.provider.ts b/apps/ingestor/src/providers/mock.provider.ts new file mode 100644 index 0000000..985620e --- /dev/null +++ b/apps/ingestor/src/providers/mock.provider.ts @@ -0,0 +1,19 @@ +import { RawPrice } from '@oracle-stocks/shared'; +import { PriceProvider } from './provider.interface'; + +export class MockProvider implements PriceProvider { + readonly name = 'MockProvider'; + + async fetchPrices(symbols: string[]): Promise { + return symbols.map(symbol => ({ + symbol, + price: this.generateMockPrice(), + timestamp: Date.now(), + source: this.name, + })); + } + + private generateMockPrice(): number { + return parseFloat((Math.random() * 1000 + 50).toFixed(2)); + } +} diff --git a/apps/ingestor/src/providers/provider.interface.ts b/apps/ingestor/src/providers/provider.interface.ts new file mode 100644 index 0000000..d2f2ef1 --- /dev/null +++ b/apps/ingestor/src/providers/provider.interface.ts @@ -0,0 +1,6 @@ +import { RawPrice } from '@oracle-stocks/shared'; + +export interface PriceProvider { + readonly name: string; + fetchPrices(symbols: string[]): Promise; +} diff --git a/apps/ingestor/src/services/price-fetcher.service.ts b/apps/ingestor/src/services/price-fetcher.service.ts index 0c5553d..96220ab 100644 --- a/apps/ingestor/src/services/price-fetcher.service.ts +++ b/apps/ingestor/src/services/price-fetcher.service.ts @@ -1,31 +1,25 @@ import { Injectable, Logger } from '@nestjs/common'; - -export interface RawPrice { - symbol: string; - price: number; - timestamp: number; - source: string; -} +import { RawPrice } from '@oracle-stocks/shared'; +import { PriceProvider, MockProvider } from '../providers'; @Injectable() export class PriceFetcherService { private readonly logger = new Logger(PriceFetcherService.name); private rawPrices: RawPrice[] = []; + private readonly providers: PriceProvider[] = []; + + constructor() { + this.providers.push(new MockProvider()); + } async fetchRawPrices(): Promise { const symbols = ['AAPL', 'GOOGL', 'MSFT', 'TSLA']; - const sources = ['AlphaVantage', 'YahooFinance', 'Finnhub']; - this.rawPrices = symbols.flatMap(symbol => - sources.map(source => ({ - symbol, - price: this.generateMockPrice(), - timestamp: Date.now(), - source, - })) - ); + const pricePromises = this.providers.map(provider => provider.fetchPrices(symbols)); + const results = await Promise.all(pricePromises); + this.rawPrices = results.flat(); - this.logger.log(`Fetched ${this.rawPrices.length} raw prices from external APIs`); + this.logger.log(`Fetched ${this.rawPrices.length} raw prices from ${this.providers.length} provider(s)`); this.rawPrices.forEach(price => { this.logger.debug( `${price.source} - ${price.symbol}: $${price.price.toFixed(2)} at ${new Date(price.timestamp).toISOString()}` @@ -38,8 +32,4 @@ export class PriceFetcherService { getRawPrices(): RawPrice[] { return this.rawPrices; } - - private generateMockPrice(): number { - return parseFloat((Math.random() * 1000 + 50).toFixed(2)); - } } diff --git a/package-lock.json b/package-lock.json index f8cfa18..73768ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1902,6 +1902,7 @@ "@nestjs/common": "^10.0.0", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", + "@oracle-stocks/shared": "*", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1" }, @@ -8484,24 +8485,6 @@ } } }, - "node_modules/eslint-config-prettier": { - "version": "10.1.8", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", - "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "funding": { - "url": "https://opencollective.com/eslint-config-prettier" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, "node_modules/eslint-import-resolver-node": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index d53c8fb..dfc68ce 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -1,3 +1,4 @@ // Main entry point for the Shared package -// Shared utilities and types will be exported here +// Type exports +export * from './types'; diff --git a/packages/shared/src/types/index.ts b/packages/shared/src/types/index.ts new file mode 100644 index 0000000..4837cae --- /dev/null +++ b/packages/shared/src/types/index.ts @@ -0,0 +1 @@ +export { RawPrice } from './raw-price'; diff --git a/packages/shared/src/types/raw-price.ts b/packages/shared/src/types/raw-price.ts new file mode 100644 index 0000000..065ae14 --- /dev/null +++ b/packages/shared/src/types/raw-price.ts @@ -0,0 +1,17 @@ +/** + * Normalized price data from any provider + * This is the canonical format for all ingested price data + */ +export interface RawPrice { + /** Stock ticker symbol (e.g., 'AAPL', 'GOOGL') */ + symbol: string; + + /** Price value in USD */ + price: number; + + /** Unix timestamp in milliseconds when price was recorded */ + timestamp: number; + + /** Data source identifier (e.g., 'AlphaVantage', 'YahooFinance') */ + source: string; +}