@fin.cx/opendata 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/readme.md CHANGED
@@ -1,64 +1,8 @@
1
1
  # @fin.cx/opendata
2
2
 
3
- 🚀 **Real-time financial data and German business intelligence in one powerful TypeScript library**
3
+ 🚀 **Complete financial intelligence toolkit for TypeScript**
4
4
 
5
- Access live stock prices, cryptocurrencies, forex, commodities AND comprehensive German company data - all through a single, unified API.
6
-
7
- ## âš ī¸ Breaking Changes
8
-
9
- ### v2.1 - Enhanced Stock Market API (Current)
10
-
11
- **The stock market API has been significantly enhanced with a new unified request system.**
12
-
13
- **What Changed:**
14
- - New discriminated union request types (`IStockDataRequest`)
15
- - Enhanced `IStockPrice` interface with OHLCV data and metadata
16
- - New `getData()` method replaces legacy `getPrice()` and `getPrices()`
17
- - Historical data support with date ranges
18
- - Exchange filtering via MIC codes
19
- - Smart caching with data-type aware TTL
20
-
21
- **Migration:**
22
- ```typescript
23
- // OLD (v2.0 and earlier)
24
- const price = await service.getPrice({ ticker: 'AAPL' });
25
- const prices = await service.getPrices({ tickers: ['AAPL', 'MSFT'] });
26
-
27
- // NEW (v2.1+) - Unified API
28
- const price = await service.getData({ type: 'current', ticker: 'AAPL' });
29
- const prices = await service.getData({ type: 'batch', tickers: ['AAPL', 'MSFT'] });
30
-
31
- // NEW - Historical data
32
- const history = await service.getData({
33
- type: 'historical',
34
- ticker: 'AAPL',
35
- from: new Date('2024-01-01'),
36
- to: new Date('2024-12-31')
37
- });
38
-
39
- // NEW - Exchange filtering
40
- const price = await service.getData({
41
- type: 'current',
42
- ticker: 'VOD',
43
- exchange: 'XLON' // London Stock Exchange
44
- });
45
- ```
46
-
47
- **Note:** Legacy `getPrice()` and `getPrices()` methods still work but are deprecated.
48
-
49
- ### v2.0 - Directory Configuration
50
-
51
- **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`:
52
-
53
- ```typescript
54
- const openData = new OpenData({
55
- nogitDir: '/path/to/your/data',
56
- downloadDir: '/path/to/your/data/downloads',
57
- germanBusinessDataDir: '/path/to/your/data/germanbusinessdata'
58
- });
59
- ```
60
-
61
- This change enables the package to work in read-only filesystems (like Deno compiled binaries) and gives you full control over where data is stored.
5
+ Access real-time stock prices, fundamental financial data, and comprehensive German company information - all through a single, unified API.
62
6
 
63
7
  ## Installation
64
8
 
@@ -70,85 +14,75 @@ pnpm add @fin.cx/opendata
70
14
 
71
15
  ## Quick Start
72
16
 
73
- ### 📈 Stock Market Data (v2.1+ Enhanced)
17
+ ### 📈 Stock Market Data
74
18
 
75
- Get comprehensive market data with EOD, historical, and OHLCV data:
19
+ Get real-time prices with company information included automatically:
76
20
 
77
21
  ```typescript
78
22
  import { StockPriceService, MarketstackProvider } from '@fin.cx/opendata';
79
23
 
80
- // Initialize the service with smart caching
24
+ // Initialize service with smart caching
81
25
  const stockService = new StockPriceService({
82
- ttl: 60000, // Default cache TTL (historical data cached forever)
83
- maxEntries: 10000 // Increased for historical data
26
+ ttl: 60000, // Cache TTL (historical cached forever)
27
+ maxEntries: 10000
84
28
  });
85
29
 
86
- // Register Marketstack provider with API key
87
- stockService.register(new MarketstackProvider('YOUR_API_KEY'), {
88
- priority: 100,
89
- retryAttempts: 3
90
- });
30
+ // Register provider with API key
31
+ stockService.register(new MarketstackProvider('YOUR_API_KEY'));
91
32
 
92
- // Get current price (new unified API)
33
+ // Get current price with company name (zero extra API calls!)
93
34
  const apple = await stockService.getData({ type: 'current', ticker: 'AAPL' });
94
- console.log(`Apple: $${apple.price} (${apple.changePercent.toFixed(2)}%)`);
95
- console.log(`OHLCV: O=${apple.open} H=${apple.high} L=${apple.low} V=${apple.volume}`);
96
- console.log(`Company: ${apple.companyName}`); // "Apple Inc"
97
- console.log(`Full: ${apple.companyFullName}`); // "Apple Inc (NASDAQ:AAPL)"
98
35
 
99
- // Get historical data (1 year of daily prices)
100
- const history = await stockService.getData({
101
- type: 'historical',
102
- ticker: 'AAPL',
103
- from: new Date('2024-01-01'),
104
- to: new Date('2024-12-31'),
105
- sort: 'DESC' // Newest first
106
- });
107
- console.log(`Fetched ${history.length} trading days`);
36
+ console.log(`${apple.companyFullName}: $${apple.price}`);
37
+ // Output: "Apple Inc (NASDAQ:AAPL): $270.37"
38
+ ```
108
39
 
109
- // Exchange-specific data (London vs NYSE)
110
- const vodLondon = await stockService.getData({
111
- type: 'current',
112
- ticker: 'VOD',
113
- exchange: 'XLON' // London Stock Exchange
114
- });
40
+ ### 💰 Fundamental Financial Data
41
+
42
+ Access comprehensive financial metrics from SEC filings - completely FREE:
115
43
 
116
- const vodNYSE = await stockService.getData({
117
- type: 'current',
118
- ticker: 'VOD',
119
- exchange: 'XNYS' // New York Stock Exchange
44
+ ```typescript
45
+ import { SecEdgarProvider, FundamentalsService } from '@fin.cx/opendata';
46
+
47
+ // Setup SEC EDGAR provider (no API key required!)
48
+ const secEdgar = new SecEdgarProvider({
49
+ userAgent: 'YourCompany youremail@example.com'
120
50
  });
121
51
 
122
- // Batch current prices with company names
123
- const prices = await stockService.getData({
124
- type: 'batch',
125
- tickers: ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'TSLA']
52
+ const fundamentalsService = new FundamentalsService();
53
+ fundamentalsService.register(secEdgar);
54
+
55
+ // Fetch fundamentals for Apple
56
+ const fundamentals = await fundamentalsService.getFundamentals('AAPL');
57
+
58
+ console.log({
59
+ company: fundamentals.companyName, // "Apple Inc."
60
+ eps: fundamentals.earningsPerShareDiluted, // $6.13
61
+ revenue: fundamentals.revenue, // $385.6B
62
+ sharesOutstanding: fundamentals.sharesOutstanding // 15.3B
126
63
  });
127
64
 
128
- // Display with company names (automatically included - zero extra API calls!)
129
- for (const stock of prices) {
130
- console.log(`${stock.companyName}: $${stock.price}`);
131
- // Output:
132
- // Apple Inc: $271.40
133
- // Microsoft Corporation: $525.76
134
- // Alphabet Inc - Class A: $281.48
135
- // Amazon.com Inc: $222.86
136
- // Tesla Inc: $440.10
137
- }
65
+ // Calculate market cap and P/E ratio
66
+ const price = await stockService.getData({ type: 'current', ticker: 'AAPL' });
67
+ const enriched = await fundamentalsService.enrichWithPrice(fundamentals, price.price);
138
68
 
139
- // Use companyFullName for richer context
140
- console.log(prices[0].companyFullName); // "Apple Inc (NASDAQ:AAPL)"
69
+ console.log({
70
+ marketCap: `$${(enriched.marketCap! / 1_000_000_000_000).toFixed(2)}T`,
71
+ peRatio: enriched.priceToEarnings!.toFixed(2),
72
+ pbRatio: enriched.priceToBook?.toFixed(2)
73
+ });
74
+ // Output: { marketCap: "$2.65T", peRatio: "28.42", pbRatio: "45.12" }
141
75
  ```
142
76
 
143
- ### đŸĸ German Business Data
77
+ ### đŸĸ German Business Intelligence
144
78
 
145
- Access comprehensive data on German companies:
79
+ Access comprehensive German company data:
146
80
 
147
81
  ```typescript
148
82
  import { OpenData } from '@fin.cx/opendata';
149
83
  import * as path from 'path';
150
84
 
151
- // REQUIRED: Configure directory paths
85
+ // Configure directory paths
152
86
  const openData = new OpenData({
153
87
  nogitDir: path.join(process.cwd(), '.nogit'),
154
88
  downloadDir: path.join(process.cwd(), '.nogit', 'downloads'),
@@ -156,55 +90,93 @@ const openData = new OpenData({
156
90
  });
157
91
  await openData.start();
158
92
 
159
- // Create a business record
160
- const company = new openData.CBusinessRecord();
161
- company.data = {
162
- name: "TechStart GmbH",
163
- city: "Berlin",
164
- registrationId: "HRB 123456",
165
- // ... more fields
166
- };
167
- await company.save();
168
-
169
- // Search companies by city
170
- const berlinCompanies = await openData.db
171
- .collection('businessrecords')
172
- .find({ city: "Berlin" })
173
- .toArray();
93
+ // Search for companies
94
+ const results = await openData.handelsregister.searchCompany("Siemens AG");
174
95
 
175
- // Import bulk data from official sources
176
- await openData.buildInitialDb();
96
+ // Get detailed information with documents
97
+ const details = await openData.handelsregister.getSpecificCompany({
98
+ court: "Munich",
99
+ type: "HRB",
100
+ number: "6684"
101
+ });
177
102
  ```
178
103
 
179
104
  ## Features
180
105
 
181
- ### đŸŽ¯ Stock Market Module (v2.1 Enhanced)
106
+ ### 📊 Stock Market Module
182
107
 
183
- - **Company Names** - Automatic company name extraction with zero extra API calls (e.g., "Apple Inc (NASDAQ:AAPL)")
184
- - **Historical Data** - Up to 15 years of daily EOD prices with automatic pagination
185
- - **Exchange Filtering** - Query specific exchanges via MIC codes (XNAS, XLON, XNYS, etc.)
186
- - **OHLCV Data** - Open, High, Low, Close, Volume for comprehensive analysis
108
+ - **Real-Time Prices** - Live and EOD stock prices from Yahoo Finance and Marketstack
109
+ - **Company Names** - Automatic company name extraction (e.g., "Apple Inc (NASDAQ:AAPL)")
110
+ - **Historical Data** - Up to 15 years of daily EOD prices with pagination
111
+ - **OHLCV Data** - Open, High, Low, Close, Volume for technical analysis
112
+ - **Exchange Filtering** - Query specific exchanges via MIC codes (XNAS, XLON, XNYS)
187
113
  - **Smart Caching** - Data-type aware TTL (historical cached forever, EOD 24h, live 30s)
188
- - **Marketstack API** - 500,000+ tickers across 72+ exchanges worldwide
189
114
  - **Batch Operations** - Fetch 100+ symbols in one request
190
- - **Unified API** - Discriminated union types for type-safe requests
191
- - **Extensible Providers** - Easy to add new data sources
192
- - **Retry Logic** - Configurable attempts and delays
193
- - **Type-Safe** - Full TypeScript support with detailed interfaces
115
+ - **Type-Safe API** - Full TypeScript support with discriminated unions
116
+ - **Multi-Provider** - Automatic fallback between providers
117
+
118
+ ### 💰 Fundamental Data Module
119
+
120
+ - **SEC EDGAR Integration** - FREE fundamental data directly from SEC filings
121
+ - **Comprehensive Metrics** - EPS, Revenue, Assets, Liabilities, Cash Flow, and more
122
+ - **All US Public Companies** - Complete coverage of SEC-registered companies
123
+ - **Historical Filings** - Data back to ~2009
124
+ - **CIK Lookup** - Automatic ticker-to-CIK mapping with smart caching
125
+ - **Calculated Ratios** - Market Cap, P/E, P/B ratios when combined with prices
126
+ - **No API Key Required** - Direct access to SEC's public API
127
+ - **Rate Limit Management** - Built-in 10 req/sec rate limiting
194
128
 
195
129
  ### 🇩đŸ‡Ē German Business Intelligence
196
130
 
197
- - **MongoDB integration** for scalable data storage
198
- - **Bulk JSONL import** from official German data sources
199
- - **Handelsregister automation** - automated document retrieval
200
- - **CRUD operations** with validation
201
- - **Streaming processing** for multi-GB datasets
131
+ - **MongoDB Integration** - Scalable data storage for millions of records
132
+ - **Bulk JSONL Import** - Process multi-GB datasets efficiently
133
+ - **Handelsregister Automation** - Automated document retrieval
134
+ - **CRUD Operations** - Full database management with validation
135
+ - **Streaming Processing** - Handle large datasets without memory issues
202
136
 
203
137
  ## Advanced Examples
204
138
 
205
- ### Phase 1: Historical Data Analysis
139
+ ### Combined Market Analysis
140
+
141
+ Combine price data with fundamentals for comprehensive analysis:
142
+
143
+ ```typescript
144
+ import { StockPriceService, MarketstackProvider, SecEdgarProvider, FundamentalsService } from '@fin.cx/opendata';
145
+
146
+ // Setup services
147
+ const stockService = new StockPriceService({ ttl: 60000 });
148
+ stockService.register(new MarketstackProvider('YOUR_API_KEY'));
149
+
150
+ const fundamentalsService = new FundamentalsService();
151
+ fundamentalsService.register(new SecEdgarProvider({
152
+ userAgent: 'YourCompany youremail@example.com'
153
+ }));
154
+
155
+ // Analyze multiple companies
156
+ const tickers = ['AAPL', 'MSFT', 'GOOGL', 'AMZN'];
157
+
158
+ for (const ticker of tickers) {
159
+ // Get price and fundamentals in parallel
160
+ const [price, fundamentals] = await Promise.all([
161
+ stockService.getData({ type: 'current', ticker }),
162
+ fundamentalsService.getFundamentals(ticker)
163
+ ]);
164
+
165
+ // Calculate metrics
166
+ const enriched = await fundamentalsService.enrichWithPrice(fundamentals, price.price);
167
+
168
+ console.log(`\n${fundamentals.companyName} (${ticker})`);
169
+ console.log(` Price: $${price.price.toFixed(2)}`);
170
+ console.log(` Market Cap: $${(enriched.marketCap! / 1e9).toFixed(2)}B`);
171
+ console.log(` P/E Ratio: ${enriched.priceToEarnings!.toFixed(2)}`);
172
+ console.log(` Revenue: $${(fundamentals.revenue! / 1e9).toFixed(2)}B`);
173
+ console.log(` EPS: $${fundamentals.earningsPerShareDiluted!.toFixed(2)}`);
174
+ }
175
+ ```
206
176
 
207
- Fetch and analyze historical price data:
177
+ ### Historical Data Analysis
178
+
179
+ Fetch and analyze historical price trends:
208
180
 
209
181
  ```typescript
210
182
  // Get 1 year of historical data
@@ -213,7 +185,7 @@ const history = await stockService.getData({
213
185
  ticker: 'AAPL',
214
186
  from: new Date('2024-01-01'),
215
187
  to: new Date('2024-12-31'),
216
- sort: 'DESC' // Newest first (default)
188
+ sort: 'DESC' // Newest first
217
189
  });
218
190
 
219
191
  // Calculate statistics
@@ -223,50 +195,31 @@ const low52Week = Math.min(...prices);
223
195
  const avgPrice = prices.reduce((a, b) => a + b) / prices.length;
224
196
 
225
197
  console.log(`52-Week Analysis for AAPL:`);
226
- console.log(`High: $${high52Week.toFixed(2)}`);
227
- console.log(`Low: $${low52Week.toFixed(2)}`);
228
- console.log(`Average: $${avgPrice.toFixed(2)}`);
229
- console.log(`Days: ${history.length}`);
230
-
231
- // Calculate daily returns
232
- for (let i = 0; i < history.length - 1; i++) {
233
- const todayPrice = history[i].price;
234
- const yesterdayPrice = history[i + 1].price;
235
- const dailyReturn = ((todayPrice - yesterdayPrice) / yesterdayPrice) * 100;
236
- console.log(`${history[i].timestamp.toISOString().split('T')[0]}: ${dailyReturn.toFixed(2)}%`);
237
- }
238
- ```
239
-
240
- ### Phase 1: Exchange-Specific Trading
241
-
242
- Compare prices across different exchanges:
198
+ console.log(` High: $${high52Week.toFixed(2)}`);
199
+ console.log(` Low: $${low52Week.toFixed(2)}`);
200
+ console.log(` Average: $${avgPrice.toFixed(2)}`);
201
+ console.log(` Days: ${history.length}`);
243
202
 
244
- ```typescript
245
- // Vodafone trades on both London and NYSE
246
- const exchanges = [
247
- { mic: 'XLON', name: 'London Stock Exchange' },
248
- { mic: 'XNYS', name: 'New York Stock Exchange' }
249
- ];
203
+ // Calculate Simple Moving Average
204
+ const calculateSMA = (data: IStockPrice[], period: number) => {
205
+ const sma: number[] = [];
206
+ for (let i = period - 1; i < data.length; i++) {
207
+ const sum = data.slice(i - period + 1, i + 1)
208
+ .reduce((acc, p) => acc + p.price, 0);
209
+ sma.push(sum / period);
210
+ }
211
+ return sma;
212
+ };
250
213
 
251
- for (const exchange of exchanges) {
252
- try {
253
- const price = await stockService.getData({
254
- type: 'current',
255
- ticker: 'VOD',
256
- exchange: exchange.mic
257
- });
214
+ const sma20 = calculateSMA(history, 20);
215
+ const sma50 = calculateSMA(history, 50);
258
216
 
259
- console.log(`${exchange.name}:`);
260
- console.log(` Price: ${price.price} ${price.currency}`);
261
- console.log(` Volume: ${price.volume?.toLocaleString()}`);
262
- console.log(` Exchange: ${price.exchangeName}`);
263
- } catch (error) {
264
- console.log(`${exchange.name}: Not available`);
265
- }
266
- }
217
+ console.log(`\nMoving Averages:`);
218
+ console.log(` 20-day SMA: $${sma20[0].toFixed(2)}`);
219
+ console.log(` 50-day SMA: $${sma50[0].toFixed(2)}`);
267
220
  ```
268
221
 
269
- ### Phase 1: OHLCV Technical Analysis
222
+ ### OHLCV Technical Analysis
270
223
 
271
224
  Use OHLCV data for technical indicators:
272
225
 
@@ -280,8 +233,8 @@ const history = await stockService.getData({
280
233
 
281
234
  // Calculate daily trading range
282
235
  for (const day of history) {
283
- const range = day.high - day.low;
284
- const rangePercent = (range / day.low) * 100;
236
+ const range = day.high! - day.low!;
237
+ const rangePercent = (range / day.low!) * 100;
285
238
 
286
239
  console.log(`${day.timestamp.toISOString().split('T')[0]}:`);
287
240
  console.log(` Open: $${day.open}`);
@@ -291,73 +244,54 @@ for (const day of history) {
291
244
  console.log(` Volume: ${day.volume?.toLocaleString()}`);
292
245
  console.log(` Range: $${range.toFixed(2)} (${rangePercent.toFixed(2)}%)`);
293
246
  }
294
-
295
- // Calculate Simple Moving Average (SMA)
296
- const calculateSMA = (data: IStockPrice[], period: number) => {
297
- const sma: number[] = [];
298
- for (let i = period - 1; i < data.length; i++) {
299
- const sum = data.slice(i - period + 1, i + 1)
300
- .reduce((acc, p) => acc + p.price, 0);
301
- sma.push(sum / period);
302
- }
303
- return sma;
304
- };
305
-
306
- const sma20 = calculateSMA(history, 20);
307
- const sma50 = calculateSMA(history, 50);
308
-
309
- console.log(`20-day SMA: $${sma20[0].toFixed(2)}`);
310
- console.log(`50-day SMA: $${sma50[0].toFixed(2)}`);
311
247
  ```
312
248
 
313
- ### Phase 1: Smart Caching Performance
249
+ ### Fundamental Data Screening
314
250
 
315
- Leverage smart caching for efficiency:
251
+ Screen stocks based on fundamental metrics:
316
252
 
317
253
  ```typescript
318
- // Historical data is cached FOREVER (never changes)
319
- console.time('First historical fetch');
320
- const history1 = await stockService.getData({
321
- type: 'historical',
322
- ticker: 'AAPL',
323
- from: new Date('2024-01-01'),
324
- to: new Date('2024-12-31')
325
- });
326
- console.timeEnd('First historical fetch');
327
- // Output: First historical fetch: 2341ms
254
+ const tickers = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'TSLA', 'META', 'NVDA'];
328
255
 
329
- console.time('Second historical fetch (cached)');
330
- const history2 = await stockService.getData({
331
- type: 'historical',
332
- ticker: 'AAPL',
333
- from: new Date('2024-01-01'),
334
- to: new Date('2024-12-31')
335
- });
336
- console.timeEnd('Second historical fetch (cached)');
337
- // Output: Second historical fetch (cached): 2ms (1000x faster!)
256
+ // Fetch fundamentals for all tickers
257
+ const allFundamentals = await fundamentalsService.getBatchFundamentals(tickers);
338
258
 
339
- // EOD data cached for 24 hours
340
- const currentPrice = await stockService.getData({
341
- type: 'current',
342
- ticker: 'MSFT'
259
+ // Get current prices
260
+ const prices = await stockService.getData({
261
+ type: 'batch',
262
+ tickers: tickers
343
263
  });
344
- // Subsequent calls within 24h served from cache
345
264
 
346
- // Cache statistics
347
- console.log(`Cache size: ${stockService['cache'].size} entries`);
265
+ // Create price map
266
+ const priceMap = new Map(prices.map(p => [p.ticker, p.price]));
267
+
268
+ // Enrich with prices
269
+ const enriched = await fundamentalsService.enrichBatchWithPrices(
270
+ allFundamentals,
271
+ priceMap
272
+ );
273
+
274
+ // Screen for value stocks (P/E < 25, P/B < 5)
275
+ const valueStocks = enriched.filter(f =>
276
+ f.priceToEarnings && f.priceToEarnings < 25 &&
277
+ f.priceToBook && f.priceToBook < 5
278
+ );
279
+
280
+ console.log('Value Stocks:');
281
+ valueStocks.forEach(stock => {
282
+ console.log(`\n${stock.companyName} (${stock.ticker})`);
283
+ console.log(` P/E Ratio: ${stock.priceToEarnings!.toFixed(2)}`);
284
+ console.log(` P/B Ratio: ${stock.priceToBook!.toFixed(2)}`);
285
+ console.log(` Market Cap: $${(stock.marketCap! / 1e9).toFixed(2)}B`);
286
+ });
348
287
  ```
349
288
 
350
289
  ### Market Dashboard
351
290
 
352
- Create an EOD market overview:
291
+ Create a comprehensive market overview:
353
292
 
354
293
  ```typescript
355
294
  const indicators = [
356
- // Indices
357
- { ticker: '^GSPC', name: 'S&P 500' },
358
- { ticker: '^DJI', name: 'DOW Jones' },
359
-
360
- // Tech Giants
361
295
  { ticker: 'AAPL', name: 'Apple' },
362
296
  { ticker: 'MSFT', name: 'Microsoft' },
363
297
  { ticker: 'GOOGL', name: 'Alphabet' },
@@ -365,7 +299,8 @@ const indicators = [
365
299
  { ticker: 'TSLA', name: 'Tesla' }
366
300
  ];
367
301
 
368
- const prices = await stockService.getPrices({
302
+ const prices = await stockService.getData({
303
+ type: 'batch',
369
304
  tickers: indicators.map(i => i.ticker)
370
305
  });
371
306
 
@@ -376,197 +311,125 @@ prices.forEach(price => {
376
311
  const color = price.change >= 0 ? '\x1b[32m' : '\x1b[31m';
377
312
 
378
313
  console.log(
379
- `${indicator.name.padEnd(15)} ${price.price.toFixed(2).padStart(10)} ` +
314
+ `${price.companyName!.padEnd(25)} $${price.price.toFixed(2).padStart(8)} ` +
380
315
  `${color}${arrow} ${price.changePercent.toFixed(2)}%\x1b[0m`
381
316
  );
382
317
  });
383
318
  ```
384
319
 
385
- ### Provider Health and Statistics
320
+ ### Exchange-Specific Trading
386
321
 
387
- Monitor your provider health and track usage:
322
+ Compare prices across different exchanges:
388
323
 
389
324
  ```typescript
390
- // Check provider health
391
- const health = await stockService.checkProvidersHealth();
392
- console.log(`Marketstack: ${health.get('Marketstack') ? '✅' : '❌'}`);
393
-
394
- // Get provider statistics
395
- const stats = stockService.getProviderStats();
396
- const marketstackStats = stats.get('Marketstack');
397
- console.log('Marketstack Stats:', {
398
- successCount: marketstackStats.successCount,
399
- errorCount: marketstackStats.errorCount,
400
- lastError: marketstackStats.lastError
401
- });
325
+ // Vodafone trades on both London and NYSE
326
+ const exchanges = [
327
+ { mic: 'XLON', name: 'London Stock Exchange' },
328
+ { mic: 'XNYS', name: 'New York Stock Exchange' }
329
+ ];
330
+
331
+ for (const exchange of exchanges) {
332
+ try {
333
+ const price = await stockService.getData({
334
+ type: 'current',
335
+ ticker: 'VOD',
336
+ exchange: exchange.mic
337
+ });
338
+
339
+ console.log(`${exchange.name}:`);
340
+ console.log(` Price: ${price.price} ${price.currency}`);
341
+ console.log(` Volume: ${price.volume?.toLocaleString()}`);
342
+ console.log(` Exchange: ${price.exchangeName}`);
343
+ } catch (error) {
344
+ console.log(`${exchange.name}: Not available`);
345
+ }
346
+ }
402
347
  ```
403
348
 
404
- ### Handelsregister Integration
349
+ ## Configuration
405
350
 
406
- Automate German company data retrieval:
351
+ ### Stock Service Options
407
352
 
408
353
  ```typescript
409
- import { OpenData } from '@fin.cx/opendata';
410
- import * as path from 'path';
411
-
412
- // Configure paths first
413
- const openData = new OpenData({
414
- nogitDir: path.join(process.cwd(), '.nogit'),
415
- downloadDir: path.join(process.cwd(), '.nogit', 'downloads'),
416
- germanBusinessDataDir: path.join(process.cwd(), '.nogit', 'germanbusinessdata')
354
+ const stockService = new StockPriceService({
355
+ ttl: 60000, // Default cache TTL in ms
356
+ maxEntries: 10000 // Max cached entries
417
357
  });
418
- await openData.start();
419
-
420
- // Search for a company
421
- const results = await openData.handelsregister.searchCompany("Siemens AG");
422
358
 
423
- // Get detailed information and documents
424
- const details = await openData.handelsregister.getSpecificCompany({
425
- court: "Munich",
426
- type: "HRB",
427
- number: "6684"
359
+ // Marketstack - EOD data (requires API key)
360
+ stockService.register(new MarketstackProvider('YOUR_API_KEY'), {
361
+ enabled: true,
362
+ priority: 100,
363
+ timeout: 10000,
364
+ retryAttempts: 3,
365
+ retryDelay: 1000
428
366
  });
429
367
 
430
- // Downloaded files include:
431
- // - XML data (SI files)
432
- // - PDF documents (AD files)
433
- for (const file of details.files) {
434
- await file.writeToDir('./downloads');
435
- }
368
+ // Yahoo Finance - Real-time data (no API key)
369
+ stockService.register(new YahooFinanceProvider(), {
370
+ enabled: true,
371
+ priority: 50
372
+ });
436
373
  ```
437
374
 
438
- ### Combined Data Analysis
439
-
440
- Merge financial and business data:
375
+ ### Fundamentals Service Options
441
376
 
442
377
  ```typescript
443
- import { OpenData, StockPriceService, MarketstackProvider } from '@fin.cx/opendata';
444
- import * as path from 'path';
445
-
446
- // Configure OpenData with paths
447
- const openData = new OpenData({
448
- nogitDir: path.join(process.cwd(), '.nogit'),
449
- downloadDir: path.join(process.cwd(), '.nogit', 'downloads'),
450
- germanBusinessDataDir: path.join(process.cwd(), '.nogit', 'germanbusinessdata')
378
+ const fundamentalsService = new FundamentalsService({
379
+ ttl: 90 * 24 * 60 * 60 * 1000, // 90 days (quarterly refresh)
380
+ maxEntries: 10000
451
381
  });
452
- await openData.start();
453
-
454
- // Setup stock service
455
- const stockService = new StockPriceService({ ttl: 60000, maxEntries: 1000 });
456
- stockService.register(new MarketstackProvider('YOUR_API_KEY'));
457
-
458
- // Find all public German companies (AG)
459
- const publicCompanies = await openData.db
460
- .collection('businessrecords')
461
- .find({ legalForm: 'AG' })
462
- .toArray();
463
-
464
- // Enrich with stock data
465
- for (const company of publicCompanies) {
466
- try {
467
- // Map company to ticker (custom logic needed)
468
- const ticker = mapCompanyToTicker(company.data.name);
469
382
 
470
- if (ticker) {
471
- const stock = await stockService.getPrice({ ticker });
472
-
473
- // Add financial metrics
474
- company.data.stockPrice = stock.price;
475
- company.data.marketCap = stock.price * getSharesOutstanding(ticker);
476
- company.data.priceChange = stock.changePercent;
477
-
478
- await company.save();
479
- }
480
- } catch (error) {
481
- // Handle missing tickers gracefully
482
- }
483
- }
383
+ // SEC EDGAR provider (FREE - no API key!)
384
+ fundamentalsService.register(new SecEdgarProvider({
385
+ userAgent: 'YourCompany youremail@example.com',
386
+ cikCacheTTL: 30 * 24 * 60 * 60 * 1000, // 30 days
387
+ fundamentalsCacheTTL: 90 * 24 * 60 * 60 * 1000, // 90 days
388
+ timeout: 30000
389
+ }));
484
390
  ```
485
391
 
486
- ## Configuration
392
+ ### Directory Configuration (German Business Data)
487
393
 
488
- ### Directory Configuration (Required for German Business Data)
394
+ All directory paths are mandatory when using German business data features:
489
395
 
490
- **All directory paths are mandatory when using `OpenData`.** Here are examples for different environments:
491
-
492
- #### Development Environment
493
396
  ```typescript
494
397
  import { OpenData } from '@fin.cx/opendata';
495
398
  import * as path from 'path';
496
399
 
400
+ // Development
497
401
  const openData = new OpenData({
498
402
  nogitDir: path.join(process.cwd(), '.nogit'),
499
403
  downloadDir: path.join(process.cwd(), '.nogit', 'downloads'),
500
404
  germanBusinessDataDir: path.join(process.cwd(), '.nogit', 'germanbusinessdata')
501
405
  });
502
- ```
503
-
504
- #### Production Environment
505
- ```typescript
506
- import { OpenData } from '@fin.cx/opendata';
507
- import * as path from 'path';
508
406
 
509
- const openData = new OpenData({
407
+ // Production
408
+ const openDataProd = new OpenData({
510
409
  nogitDir: '/var/lib/myapp/data',
511
410
  downloadDir: '/var/lib/myapp/data/downloads',
512
411
  germanBusinessDataDir: '/var/lib/myapp/data/germanbusinessdata'
513
412
  });
514
413
  ```
515
414
 
516
- #### Deno Compiled Binaries (or other read-only filesystems)
517
- ```typescript
518
- import { OpenData } from '@fin.cx/opendata';
519
-
520
- // Use OS temp directory or user data directory
521
- const dataDir = Deno.env.get('HOME') + '/.myapp/data';
522
-
523
- const openData = new OpenData({
524
- nogitDir: dataDir,
525
- downloadDir: dataDir + '/downloads',
526
- germanBusinessDataDir: dataDir + '/germanbusinessdata'
527
- });
528
- ```
529
-
530
- ### Stock Service Options
531
-
532
- ```typescript
533
- const stockService = new StockPriceService({
534
- ttl: 60000, // Cache for 1 minute
535
- maxEntries: 1000 // Max cached symbols
536
- });
537
-
538
- // Marketstack - EOD data, requires API key
539
- stockService.register(new MarketstackProvider('YOUR_API_KEY'), {
540
- enabled: true,
541
- priority: 100,
542
- timeout: 10000,
543
- retryAttempts: 3,
544
- retryDelay: 1000
545
- });
546
- ```
547
-
548
- ### MongoDB Setup
415
+ ### Environment Variables
549
416
 
550
- Set environment variables for German business data:
417
+ Set environment variables for API keys and database:
551
418
 
552
419
  ```env
420
+ # Marketstack API (for EOD stock data)
421
+ MARKETSTACK_COM_TOKEN=your_api_key_here
422
+
423
+ # MongoDB (for German business data)
553
424
  MONGODB_URL=mongodb://localhost:27017
554
425
  MONGODB_NAME=opendata
555
426
  MONGODB_USER=myuser
556
427
  MONGODB_PASS=mypass
557
428
  ```
558
429
 
559
- ### Marketstack API Key
560
-
561
- Get your free API key at [marketstack.com](https://marketstack.com) and set it in your environment:
562
-
563
- ```env
564
- MARKETSTACK_COM_TOKEN=your_api_key_here
565
- ```
566
-
567
430
  ## API Reference
568
431
 
569
- ### Stock Types
432
+ ### Stock Price Interfaces
570
433
 
571
434
  ```typescript
572
435
  interface IStockPrice {
@@ -581,62 +444,154 @@ interface IStockPrice {
581
444
  marketState: 'PRE' | 'REGULAR' | 'POST' | 'CLOSED';
582
445
  exchange?: string;
583
446
  exchangeName?: string;
447
+
448
+ // OHLCV data
449
+ volume?: number;
450
+ open?: number;
451
+ high?: number;
452
+ low?: number;
453
+ adjusted?: boolean;
454
+ dataType: 'eod' | 'intraday' | 'live';
455
+ fetchedAt: Date;
456
+
457
+ // Company identification
458
+ companyName?: string; // "Apple Inc"
459
+ companyFullName?: string; // "Apple Inc (NASDAQ:AAPL)"
460
+ }
461
+ ```
462
+
463
+ ### Fundamental Data Interfaces
464
+
465
+ ```typescript
466
+ interface IStockFundamentals {
467
+ ticker: string;
468
+ cik: string;
469
+ companyName: string;
470
+ provider: string;
471
+ timestamp: Date;
472
+ fetchedAt: Date;
473
+
474
+ // Per-share metrics
475
+ earningsPerShareBasic?: number;
476
+ earningsPerShareDiluted?: number;
477
+ sharesOutstanding?: number;
478
+
479
+ // Income statement (annual USD)
480
+ revenue?: number;
481
+ netIncome?: number;
482
+ operatingIncome?: number;
483
+ grossProfit?: number;
484
+
485
+ // Balance sheet (annual USD)
486
+ assets?: number;
487
+ liabilities?: number;
488
+ stockholdersEquity?: number;
489
+ cash?: number;
490
+ propertyPlantEquipment?: number;
491
+
492
+ // Calculated metrics (requires price)
493
+ marketCap?: number; // price × sharesOutstanding
494
+ priceToEarnings?: number; // price / EPS
495
+ priceToBook?: number; // marketCap / stockholdersEquity
496
+
497
+ // Metadata
498
+ fiscalYear?: string;
499
+ fiscalQuarter?: string;
500
+ filingDate?: Date;
501
+ form?: '10-K' | '10-Q' | string;
584
502
  }
585
503
  ```
586
504
 
587
505
  ### Key Methods
588
506
 
589
507
  **StockPriceService**
590
- - `getPrice(request)` - Single stock price with automatic provider selection
591
- - `getPrices(request)` - Batch prices (100+ symbols in one request)
508
+ - `getData(request)` - Unified method for all stock data (current, historical, batch)
509
+ - `getPrice(request)` - Convenience method for single current price
510
+ - `getPrices(request)` - Convenience method for batch current prices
592
511
  - `register(provider, config)` - Add data provider with priority and retry config
593
512
  - `checkProvidersHealth()` - Test all providers and return health status
594
513
  - `getProviderStats()` - Get success/error statistics for each provider
595
514
  - `clearCache()` - Clear price cache
596
515
  - `setCacheTTL(ttl)` - Update cache TTL dynamically
597
516
 
517
+ **FundamentalsService**
518
+ - `getFundamentals(ticker)` - Get fundamental data for single ticker
519
+ - `getBatchFundamentals(tickers)` - Get fundamentals for multiple tickers
520
+ - `enrichWithPrice(fundamentals, price)` - Calculate market cap, P/E, P/B ratios
521
+ - `enrichBatchWithPrices(fundamentals, priceMap)` - Batch enrich with prices
522
+ - `register(provider, config)` - Add fundamentals provider
523
+ - `checkProvidersHealth()` - Test all providers
524
+ - `getProviderStats()` - Get success/error statistics
525
+ - `clearCache()` - Clear fundamentals cache
526
+
527
+ **SecEdgarProvider**
528
+ - ✅ FREE - No API key required
529
+ - ✅ All US public companies
530
+ - ✅ Comprehensive US GAAP financial metrics
531
+ - ✅ Historical data back to ~2009
532
+ - ✅ Direct access to SEC filings (10-K, 10-Q)
533
+ - ✅ Smart caching (CIK: 30 days, Fundamentals: 90 days)
534
+ - ✅ Rate limiting (10 requests/second)
535
+ - â„šī¸ Requires User-Agent header in format: "Company Name Email"
536
+
598
537
  **MarketstackProvider**
599
- - ✅ End-of-Day (EOD) data
600
- - ✅ 125,000+ tickers across 72+ exchanges worldwide
601
- - ✅ Batch fetching support (multiple symbols in one request)
602
- - ✅ Comprehensive data: open, high, low, close, volume, splits, dividends
538
+ - ✅ End-of-Day (EOD) stock prices
539
+ - ✅ 500,000+ tickers across 72+ exchanges worldwide
540
+ - ✅ Historical data with pagination
541
+ - ✅ Batch fetching support
542
+ - ✅ OHLCV data (Open, High, Low, Close, Volume)
543
+ - ✅ Company names included automatically
603
544
  - âš ī¸ Requires API key (free tier: 100 requests/month)
604
- - âš ī¸ EOD data only (not real-time)
545
+
546
+ **YahooFinanceProvider**
547
+ - ✅ Real-time stock prices
548
+ - ✅ No API key required
549
+ - ✅ Global coverage
550
+ - ✅ Company names included
551
+ - âš ī¸ Rate limits may apply
605
552
 
606
553
  **OpenData**
607
554
  - `start()` - Initialize MongoDB connection
608
555
  - `buildInitialDb()` - Import bulk data
609
556
  - `CBusinessRecord` - Business record class
610
- - `handelsregister` - Registry automation
557
+ - `handelsregister` - German registry automation
611
558
 
612
559
  ## Provider Architecture
613
560
 
614
- The library uses a flexible provider system that makes it easy to add new data sources:
561
+ Add custom data providers easily:
615
562
 
616
563
  ```typescript
617
564
  class MyCustomProvider implements IStockProvider {
618
565
  name = 'My Provider';
619
566
  priority = 50;
620
567
  requiresAuth = true;
621
-
622
- async fetchPrice(request: IStockQuoteRequest): Promise<IStockPrice> {
623
- // Your implementation
624
- }
625
-
626
- async fetchPrices(request: IStockBatchQuoteRequest): Promise<IStockPrice[]> {
627
- // Batch implementation
568
+ rateLimit = { requestsPerMinute: 60 };
569
+
570
+ async fetchData(request: IStockDataRequest): Promise<IStockPrice | IStockPrice[]> {
571
+ // Implement unified data fetching
572
+ switch (request.type) {
573
+ case 'current':
574
+ return this.fetchCurrentPrice(request);
575
+ case 'batch':
576
+ return this.fetchBatchPrices(request);
577
+ case 'historical':
578
+ return this.fetchHistoricalPrices(request);
579
+ default:
580
+ throw new Error(`Unsupported request type`);
581
+ }
628
582
  }
629
583
 
630
584
  async isAvailable(): Promise<boolean> {
631
585
  // Health check
586
+ return true;
632
587
  }
633
588
 
634
589
  supportsMarket(market: string): boolean {
635
- // Market validation
590
+ return ['US', 'UK', 'DE'].includes(market);
636
591
  }
637
592
 
638
593
  supportsTicker(ticker: string): boolean {
639
- // Ticker validation
594
+ return /^[A-Z]{1,5}$/.test(ticker);
640
595
  }
641
596
  }
642
597
 
@@ -645,30 +600,53 @@ stockService.register(new MyCustomProvider());
645
600
 
646
601
  ## Performance
647
602
 
648
- - **Batch fetching**: Get 100+ EOD prices in one API request
649
- - **Smart caching**: Instant repeated queries with configurable TTL
650
- - **Rate limit aware**: Automatic retry logic for API limits
651
- - **Concurrent processing**: Handle 1000+ business records/second
603
+ - **Batch Fetching**: Get 100+ prices in one API request
604
+ - **Smart Caching**: Data-type aware TTL (historical cached forever, EOD 24h, live 30s)
605
+ - **Rate Limit Management**: Automatic retry logic for API limits
606
+ - **Concurrent Processing**: Handle 1000+ records/second
652
607
  - **Streaming**: Process GB-sized datasets without memory issues
608
+ - **Provider Fallback**: Automatic failover between data sources
653
609
 
654
610
  ## Testing
655
611
 
656
612
  Run the comprehensive test suite:
657
613
 
658
614
  ```bash
659
- npm test
615
+ pnpm test
660
616
  ```
661
617
 
662
- Test stock provider:
618
+ Test specific modules:
663
619
 
664
620
  ```bash
665
- npx tstest test/test.marketstack.node.ts --verbose
621
+ # Stock price providers
622
+ pnpm tstest test/test.marketstack.node.ts --verbose
623
+ pnpm tstest test/test.stocks.ts --verbose
624
+
625
+ # Fundamental data
626
+ pnpm tstest test/test.secedgar.provider.node.ts --verbose
627
+ pnpm tstest test/test.fundamentals.service.node.ts --verbose
628
+
629
+ # German business data
630
+ pnpm tstest test/test.handelsregister.ts --verbose
666
631
  ```
667
632
 
668
- Test German business data:
633
+ ## Getting API Keys
669
634
 
670
- ```bash
671
- npx tstest test/test.handelsregister.ts --verbose
635
+ ### Marketstack (EOD Stock Data)
636
+
637
+ 1. Visit [marketstack.com](https://marketstack.com)
638
+ 2. Sign up for a free account (100 requests/month)
639
+ 3. Get your API key from the dashboard
640
+ 4. Set environment variable: `MARKETSTACK_COM_TOKEN=your_key_here`
641
+
642
+ ### SEC EDGAR (Fundamental Data)
643
+
644
+ **No API key required!** SEC EDGAR is completely free and public. Just provide your company name and email in the User-Agent:
645
+
646
+ ```typescript
647
+ new SecEdgarProvider({
648
+ userAgent: 'YourCompany youremail@example.com'
649
+ });
672
650
  ```
673
651
 
674
652
  ## License and Legal Information