feat(stocks): Add unified stock data API (getData) with historical/OHLCV support, smart caching and provider enhancements
This commit is contained in:
254
readme.md
254
readme.md
@@ -4,7 +4,49 @@
|
||||
|
||||
Access live stock prices, cryptocurrencies, forex, commodities AND comprehensive German company data - all through a single, unified API.
|
||||
|
||||
## ⚠️ Breaking Change in v2.0
|
||||
## ⚠️ Breaking Changes
|
||||
|
||||
### v2.1 - Enhanced Stock Market API (Current)
|
||||
|
||||
**The stock market API has been significantly enhanced with a new unified request system.**
|
||||
|
||||
**What Changed:**
|
||||
- New discriminated union request types (`IStockDataRequest`)
|
||||
- Enhanced `IStockPrice` interface with OHLCV data and metadata
|
||||
- New `getData()` method replaces legacy `getPrice()` and `getPrices()`
|
||||
- Historical data support with date ranges
|
||||
- Exchange filtering via MIC codes
|
||||
- Smart caching with data-type aware TTL
|
||||
|
||||
**Migration:**
|
||||
```typescript
|
||||
// OLD (v2.0 and earlier)
|
||||
const price = await service.getPrice({ ticker: 'AAPL' });
|
||||
const prices = await service.getPrices({ tickers: ['AAPL', 'MSFT'] });
|
||||
|
||||
// NEW (v2.1+) - Unified API
|
||||
const price = await service.getData({ type: 'current', ticker: 'AAPL' });
|
||||
const prices = await service.getData({ type: 'batch', tickers: ['AAPL', 'MSFT'] });
|
||||
|
||||
// NEW - Historical data
|
||||
const history = await service.getData({
|
||||
type: 'historical',
|
||||
ticker: 'AAPL',
|
||||
from: new Date('2024-01-01'),
|
||||
to: new Date('2024-12-31')
|
||||
});
|
||||
|
||||
// NEW - Exchange filtering
|
||||
const price = await service.getData({
|
||||
type: 'current',
|
||||
ticker: 'VOD',
|
||||
exchange: 'XLON' // London Stock Exchange
|
||||
});
|
||||
```
|
||||
|
||||
**Note:** Legacy `getPrice()` and `getPrices()` methods still work but are deprecated.
|
||||
|
||||
### v2.0 - Directory Configuration
|
||||
|
||||
**Directory paths are now MANDATORY when using German business data features.** The package no longer creates `.nogit/` directories automatically. You must explicitly configure all directory paths when instantiating `OpenData`:
|
||||
|
||||
@@ -28,17 +70,17 @@ pnpm add @fin.cx/opendata
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 📈 Stock Market Data
|
||||
### 📈 Stock Market Data (v2.1+ Enhanced)
|
||||
|
||||
Get market data with EOD (End-of-Day) pricing:
|
||||
Get comprehensive market data with EOD, historical, and OHLCV data:
|
||||
|
||||
```typescript
|
||||
import { StockPriceService, MarketstackProvider } from '@fin.cx/opendata';
|
||||
|
||||
// Initialize the service with caching
|
||||
// Initialize the service with smart caching
|
||||
const stockService = new StockPriceService({
|
||||
ttl: 60000, // Cache for 1 minute
|
||||
maxEntries: 1000 // Max cached symbols
|
||||
ttl: 60000, // Default cache TTL (historical data cached forever)
|
||||
maxEntries: 10000 // Increased for historical data
|
||||
});
|
||||
|
||||
// Register Marketstack provider with API key
|
||||
@@ -47,18 +89,38 @@ stockService.register(new MarketstackProvider('YOUR_API_KEY'), {
|
||||
retryAttempts: 3
|
||||
});
|
||||
|
||||
// Get single stock price
|
||||
const apple = await stockService.getPrice({ ticker: 'AAPL' });
|
||||
// Get current price (new unified API)
|
||||
const apple = await stockService.getData({ type: 'current', ticker: 'AAPL' });
|
||||
console.log(`Apple: $${apple.price} (${apple.changePercent.toFixed(2)}%)`);
|
||||
console.log(`OHLCV: O=${apple.open} H=${apple.high} L=${apple.low} V=${apple.volume}`);
|
||||
|
||||
// Get multiple prices at once (batch fetching)
|
||||
const prices = await stockService.getPrices({
|
||||
tickers: ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'TSLA']
|
||||
// Get historical data (1 year of daily prices)
|
||||
const history = await stockService.getData({
|
||||
type: 'historical',
|
||||
ticker: 'AAPL',
|
||||
from: new Date('2024-01-01'),
|
||||
to: new Date('2024-12-31'),
|
||||
sort: 'DESC' // Newest first
|
||||
});
|
||||
console.log(`Fetched ${history.length} trading days`);
|
||||
|
||||
// Exchange-specific data (London vs NYSE)
|
||||
const vodLondon = await stockService.getData({
|
||||
type: 'current',
|
||||
ticker: 'VOD',
|
||||
exchange: 'XLON' // London Stock Exchange
|
||||
});
|
||||
|
||||
// 125,000+ tickers across 72+ exchanges worldwide
|
||||
const internationalStocks = await stockService.getPrices({
|
||||
tickers: ['AAPL', 'VOD.LON', 'SAP.DEX', 'TM', 'BABA']
|
||||
const vodNYSE = await stockService.getData({
|
||||
type: 'current',
|
||||
ticker: 'VOD',
|
||||
exchange: 'XNYS' // New York Stock Exchange
|
||||
});
|
||||
|
||||
// Batch current prices
|
||||
const prices = await stockService.getData({
|
||||
type: 'batch',
|
||||
tickers: ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'TSLA']
|
||||
});
|
||||
```
|
||||
|
||||
@@ -100,15 +162,18 @@ await openData.buildInitialDb();
|
||||
|
||||
## Features
|
||||
|
||||
### 🎯 Stock Market Module
|
||||
### 🎯 Stock Market Module (v2.1 Enhanced)
|
||||
|
||||
- **Marketstack API** - End-of-Day (EOD) data for 125,000+ tickers across 72+ exchanges
|
||||
- **Stock prices** for stocks, ETFs, indices, and more
|
||||
- **Batch operations** - fetch 100+ symbols in one request
|
||||
- **Smart caching** - configurable TTL, automatic invalidation
|
||||
- **Extensible provider system** - easily add new data sources
|
||||
- **Retry logic** - configurable retry attempts and delays
|
||||
- **Type-safe** - full TypeScript support with detailed interfaces
|
||||
- **Historical Data** - Up to 15 years of daily EOD prices with automatic pagination
|
||||
- **Exchange Filtering** - Query specific exchanges via MIC codes (XNAS, XLON, XNYS, etc.)
|
||||
- **OHLCV Data** - Open, High, Low, Close, Volume for comprehensive analysis
|
||||
- **Smart Caching** - Data-type aware TTL (historical cached forever, EOD 24h, live 30s)
|
||||
- **Marketstack API** - 500,000+ tickers across 72+ exchanges worldwide
|
||||
- **Batch Operations** - Fetch 100+ symbols in one request
|
||||
- **Unified API** - Discriminated union types for type-safe requests
|
||||
- **Extensible Providers** - Easy to add new data sources
|
||||
- **Retry Logic** - Configurable attempts and delays
|
||||
- **Type-Safe** - Full TypeScript support with detailed interfaces
|
||||
|
||||
### 🇩🇪 German Business Intelligence
|
||||
|
||||
@@ -120,6 +185,151 @@ await openData.buildInitialDb();
|
||||
|
||||
## Advanced Examples
|
||||
|
||||
### Phase 1: Historical Data Analysis
|
||||
|
||||
Fetch and analyze historical price data:
|
||||
|
||||
```typescript
|
||||
// Get 1 year of historical data
|
||||
const history = await stockService.getData({
|
||||
type: 'historical',
|
||||
ticker: 'AAPL',
|
||||
from: new Date('2024-01-01'),
|
||||
to: new Date('2024-12-31'),
|
||||
sort: 'DESC' // Newest first (default)
|
||||
});
|
||||
|
||||
// Calculate statistics
|
||||
const prices = history.map(p => p.price);
|
||||
const high52Week = Math.max(...prices);
|
||||
const low52Week = Math.min(...prices);
|
||||
const avgPrice = prices.reduce((a, b) => a + b) / prices.length;
|
||||
|
||||
console.log(`52-Week Analysis for AAPL:`);
|
||||
console.log(`High: $${high52Week.toFixed(2)}`);
|
||||
console.log(`Low: $${low52Week.toFixed(2)}`);
|
||||
console.log(`Average: $${avgPrice.toFixed(2)}`);
|
||||
console.log(`Days: ${history.length}`);
|
||||
|
||||
// Calculate daily returns
|
||||
for (let i = 0; i < history.length - 1; i++) {
|
||||
const todayPrice = history[i].price;
|
||||
const yesterdayPrice = history[i + 1].price;
|
||||
const dailyReturn = ((todayPrice - yesterdayPrice) / yesterdayPrice) * 100;
|
||||
console.log(`${history[i].timestamp.toISOString().split('T')[0]}: ${dailyReturn.toFixed(2)}%`);
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 1: Exchange-Specific Trading
|
||||
|
||||
Compare prices across different exchanges:
|
||||
|
||||
```typescript
|
||||
// Vodafone trades on both London and NYSE
|
||||
const exchanges = [
|
||||
{ mic: 'XLON', name: 'London Stock Exchange' },
|
||||
{ mic: 'XNYS', name: 'New York Stock Exchange' }
|
||||
];
|
||||
|
||||
for (const exchange of exchanges) {
|
||||
try {
|
||||
const price = await stockService.getData({
|
||||
type: 'current',
|
||||
ticker: 'VOD',
|
||||
exchange: exchange.mic
|
||||
});
|
||||
|
||||
console.log(`${exchange.name}:`);
|
||||
console.log(` Price: ${price.price} ${price.currency}`);
|
||||
console.log(` Volume: ${price.volume?.toLocaleString()}`);
|
||||
console.log(` Exchange: ${price.exchangeName}`);
|
||||
} catch (error) {
|
||||
console.log(`${exchange.name}: Not available`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 1: OHLCV Technical Analysis
|
||||
|
||||
Use OHLCV data for technical indicators:
|
||||
|
||||
```typescript
|
||||
const history = await stockService.getData({
|
||||
type: 'historical',
|
||||
ticker: 'TSLA',
|
||||
from: new Date('2024-11-01'),
|
||||
to: new Date('2024-11-30')
|
||||
});
|
||||
|
||||
// Calculate daily trading range
|
||||
for (const day of history) {
|
||||
const range = day.high - day.low;
|
||||
const rangePercent = (range / day.low) * 100;
|
||||
|
||||
console.log(`${day.timestamp.toISOString().split('T')[0]}:`);
|
||||
console.log(` Open: $${day.open}`);
|
||||
console.log(` High: $${day.high}`);
|
||||
console.log(` Low: $${day.low}`);
|
||||
console.log(` Close: $${day.price}`);
|
||||
console.log(` Volume: ${day.volume?.toLocaleString()}`);
|
||||
console.log(` Range: $${range.toFixed(2)} (${rangePercent.toFixed(2)}%)`);
|
||||
}
|
||||
|
||||
// Calculate Simple Moving Average (SMA)
|
||||
const calculateSMA = (data: IStockPrice[], period: number) => {
|
||||
const sma: number[] = [];
|
||||
for (let i = period - 1; i < data.length; i++) {
|
||||
const sum = data.slice(i - period + 1, i + 1)
|
||||
.reduce((acc, p) => acc + p.price, 0);
|
||||
sma.push(sum / period);
|
||||
}
|
||||
return sma;
|
||||
};
|
||||
|
||||
const sma20 = calculateSMA(history, 20);
|
||||
const sma50 = calculateSMA(history, 50);
|
||||
|
||||
console.log(`20-day SMA: $${sma20[0].toFixed(2)}`);
|
||||
console.log(`50-day SMA: $${sma50[0].toFixed(2)}`);
|
||||
```
|
||||
|
||||
### Phase 1: Smart Caching Performance
|
||||
|
||||
Leverage smart caching for efficiency:
|
||||
|
||||
```typescript
|
||||
// Historical data is cached FOREVER (never changes)
|
||||
console.time('First historical fetch');
|
||||
const history1 = await stockService.getData({
|
||||
type: 'historical',
|
||||
ticker: 'AAPL',
|
||||
from: new Date('2024-01-01'),
|
||||
to: new Date('2024-12-31')
|
||||
});
|
||||
console.timeEnd('First historical fetch');
|
||||
// Output: First historical fetch: 2341ms
|
||||
|
||||
console.time('Second historical fetch (cached)');
|
||||
const history2 = await stockService.getData({
|
||||
type: 'historical',
|
||||
ticker: 'AAPL',
|
||||
from: new Date('2024-01-01'),
|
||||
to: new Date('2024-12-31')
|
||||
});
|
||||
console.timeEnd('Second historical fetch (cached)');
|
||||
// Output: Second historical fetch (cached): 2ms (1000x faster!)
|
||||
|
||||
// EOD data cached for 24 hours
|
||||
const currentPrice = await stockService.getData({
|
||||
type: 'current',
|
||||
ticker: 'MSFT'
|
||||
});
|
||||
// Subsequent calls within 24h served from cache
|
||||
|
||||
// Cache statistics
|
||||
console.log(`Cache size: ${stockService['cache'].size} entries`);
|
||||
```
|
||||
|
||||
### Market Dashboard
|
||||
|
||||
Create an EOD market overview:
|
||||
|
||||
Reference in New Issue
Block a user