@fin.cx/opendata 2.1.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.
Files changed (30) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/stocks/classes.fundamentalsservice.d.ts +104 -0
  3. package/dist_ts/stocks/classes.fundamentalsservice.js +298 -0
  4. package/dist_ts/stocks/classes.stockservice.d.ts +16 -7
  5. package/dist_ts/stocks/classes.stockservice.js +17 -98
  6. package/dist_ts/stocks/index.d.ts +3 -0
  7. package/dist_ts/stocks/index.js +5 -2
  8. package/dist_ts/stocks/interfaces/fundamentals.d.ts +82 -0
  9. package/dist_ts/stocks/interfaces/fundamentals.js +6 -0
  10. package/dist_ts/stocks/interfaces/provider.d.ts +2 -3
  11. package/dist_ts/stocks/interfaces/stockprice.d.ts +2 -10
  12. package/dist_ts/stocks/interfaces/stockprice.js +2 -2
  13. package/dist_ts/stocks/providers/provider.marketstack.d.ts +6 -11
  14. package/dist_ts/stocks/providers/provider.marketstack.js +32 -24
  15. package/dist_ts/stocks/providers/provider.secedgar.d.ts +102 -0
  16. package/dist_ts/stocks/providers/provider.secedgar.js +343 -0
  17. package/dist_ts/stocks/providers/provider.yahoo.d.ts +13 -3
  18. package/dist_ts/stocks/providers/provider.yahoo.js +27 -4
  19. package/package.json +1 -1
  20. package/readme.md +383 -388
  21. package/ts/00_commitinfo_data.ts +1 -1
  22. package/ts/stocks/classes.fundamentalsservice.ts +404 -0
  23. package/ts/stocks/classes.stockservice.ts +28 -140
  24. package/ts/stocks/index.ts +5 -2
  25. package/ts/stocks/interfaces/fundamentals.ts +102 -0
  26. package/ts/stocks/interfaces/provider.ts +5 -6
  27. package/ts/stocks/interfaces/stockprice.ts +4 -19
  28. package/ts/stocks/providers/provider.marketstack.ts +39 -30
  29. package/ts/stocks/providers/provider.secedgar.ts +429 -0
  30. package/ts/stocks/providers/provider.yahoo.ts +33 -4
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,69 +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
35
 
97
- // Get historical data (1 year of daily prices)
98
- const history = await stockService.getData({
99
- type: 'historical',
100
- ticker: 'AAPL',
101
- from: new Date('2024-01-01'),
102
- to: new Date('2024-12-31'),
103
- sort: 'DESC' // Newest first
104
- });
105
- console.log(`Fetched ${history.length} trading days`);
36
+ console.log(`${apple.companyFullName}: $${apple.price}`);
37
+ // Output: "Apple Inc (NASDAQ:AAPL): $270.37"
38
+ ```
39
+
40
+ ### 💰 Fundamental Financial Data
41
+
42
+ Access comprehensive financial metrics from SEC filings - completely FREE:
106
43
 
107
- // Exchange-specific data (London vs NYSE)
108
- const vodLondon = await stockService.getData({
109
- type: 'current',
110
- ticker: 'VOD',
111
- exchange: 'XLON' // London 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'
112
50
  });
113
51
 
114
- const vodNYSE = await stockService.getData({
115
- type: 'current',
116
- ticker: 'VOD',
117
- exchange: 'XNYS' // New York Stock Exchange
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
118
63
  });
119
64
 
120
- // Batch current prices
121
- const prices = await stockService.getData({
122
- type: 'batch',
123
- tickers: ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'TSLA']
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);
68
+
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)
124
73
  });
74
+ // Output: { marketCap: "$2.65T", peRatio: "28.42", pbRatio: "45.12" }
125
75
  ```
126
76
 
127
- ### đŸĸ German Business Data
77
+ ### đŸĸ German Business Intelligence
128
78
 
129
- Access comprehensive data on German companies:
79
+ Access comprehensive German company data:
130
80
 
131
81
  ```typescript
132
82
  import { OpenData } from '@fin.cx/opendata';
133
83
  import * as path from 'path';
134
84
 
135
- // REQUIRED: Configure directory paths
85
+ // Configure directory paths
136
86
  const openData = new OpenData({
137
87
  nogitDir: path.join(process.cwd(), '.nogit'),
138
88
  downloadDir: path.join(process.cwd(), '.nogit', 'downloads'),
@@ -140,54 +90,93 @@ const openData = new OpenData({
140
90
  });
141
91
  await openData.start();
142
92
 
143
- // Create a business record
144
- const company = new openData.CBusinessRecord();
145
- company.data = {
146
- name: "TechStart GmbH",
147
- city: "Berlin",
148
- registrationId: "HRB 123456",
149
- // ... more fields
150
- };
151
- await company.save();
152
-
153
- // Search companies by city
154
- const berlinCompanies = await openData.db
155
- .collection('businessrecords')
156
- .find({ city: "Berlin" })
157
- .toArray();
93
+ // Search for companies
94
+ const results = await openData.handelsregister.searchCompany("Siemens AG");
158
95
 
159
- // Import bulk data from official sources
160
- 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
+ });
161
102
  ```
162
103
 
163
104
  ## Features
164
105
 
165
- ### đŸŽ¯ Stock Market Module (v2.1 Enhanced)
106
+ ### 📊 Stock Market Module
166
107
 
167
- - **Historical Data** - Up to 15 years of daily EOD prices with automatic pagination
168
- - **Exchange Filtering** - Query specific exchanges via MIC codes (XNAS, XLON, XNYS, etc.)
169
- - **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)
170
113
  - **Smart Caching** - Data-type aware TTL (historical cached forever, EOD 24h, live 30s)
171
- - **Marketstack API** - 500,000+ tickers across 72+ exchanges worldwide
172
114
  - **Batch Operations** - Fetch 100+ symbols in one request
173
- - **Unified API** - Discriminated union types for type-safe requests
174
- - **Extensible Providers** - Easy to add new data sources
175
- - **Retry Logic** - Configurable attempts and delays
176
- - **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
177
128
 
178
129
  ### 🇩đŸ‡Ē German Business Intelligence
179
130
 
180
- - **MongoDB integration** for scalable data storage
181
- - **Bulk JSONL import** from official German data sources
182
- - **Handelsregister automation** - automated document retrieval
183
- - **CRUD operations** with validation
184
- - **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
185
136
 
186
137
  ## Advanced Examples
187
138
 
188
- ### 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
+ ```
176
+
177
+ ### Historical Data Analysis
189
178
 
190
- Fetch and analyze historical price data:
179
+ Fetch and analyze historical price trends:
191
180
 
192
181
  ```typescript
193
182
  // Get 1 year of historical data
@@ -196,7 +185,7 @@ const history = await stockService.getData({
196
185
  ticker: 'AAPL',
197
186
  from: new Date('2024-01-01'),
198
187
  to: new Date('2024-12-31'),
199
- sort: 'DESC' // Newest first (default)
188
+ sort: 'DESC' // Newest first
200
189
  });
201
190
 
202
191
  // Calculate statistics
@@ -206,50 +195,31 @@ const low52Week = Math.min(...prices);
206
195
  const avgPrice = prices.reduce((a, b) => a + b) / prices.length;
207
196
 
208
197
  console.log(`52-Week Analysis for AAPL:`);
209
- console.log(`High: $${high52Week.toFixed(2)}`);
210
- console.log(`Low: $${low52Week.toFixed(2)}`);
211
- console.log(`Average: $${avgPrice.toFixed(2)}`);
212
- console.log(`Days: ${history.length}`);
213
-
214
- // Calculate daily returns
215
- for (let i = 0; i < history.length - 1; i++) {
216
- const todayPrice = history[i].price;
217
- const yesterdayPrice = history[i + 1].price;
218
- const dailyReturn = ((todayPrice - yesterdayPrice) / yesterdayPrice) * 100;
219
- console.log(`${history[i].timestamp.toISOString().split('T')[0]}: ${dailyReturn.toFixed(2)}%`);
220
- }
221
- ```
222
-
223
- ### Phase 1: Exchange-Specific Trading
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}`);
224
202
 
225
- Compare prices across different exchanges:
226
-
227
- ```typescript
228
- // Vodafone trades on both London and NYSE
229
- const exchanges = [
230
- { mic: 'XLON', name: 'London Stock Exchange' },
231
- { mic: 'XNYS', name: 'New York Stock Exchange' }
232
- ];
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
+ };
233
213
 
234
- for (const exchange of exchanges) {
235
- try {
236
- const price = await stockService.getData({
237
- type: 'current',
238
- ticker: 'VOD',
239
- exchange: exchange.mic
240
- });
214
+ const sma20 = calculateSMA(history, 20);
215
+ const sma50 = calculateSMA(history, 50);
241
216
 
242
- console.log(`${exchange.name}:`);
243
- console.log(` Price: ${price.price} ${price.currency}`);
244
- console.log(` Volume: ${price.volume?.toLocaleString()}`);
245
- console.log(` Exchange: ${price.exchangeName}`);
246
- } catch (error) {
247
- console.log(`${exchange.name}: Not available`);
248
- }
249
- }
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)}`);
250
220
  ```
251
221
 
252
- ### Phase 1: OHLCV Technical Analysis
222
+ ### OHLCV Technical Analysis
253
223
 
254
224
  Use OHLCV data for technical indicators:
255
225
 
@@ -263,8 +233,8 @@ const history = await stockService.getData({
263
233
 
264
234
  // Calculate daily trading range
265
235
  for (const day of history) {
266
- const range = day.high - day.low;
267
- const rangePercent = (range / day.low) * 100;
236
+ const range = day.high! - day.low!;
237
+ const rangePercent = (range / day.low!) * 100;
268
238
 
269
239
  console.log(`${day.timestamp.toISOString().split('T')[0]}:`);
270
240
  console.log(` Open: $${day.open}`);
@@ -274,73 +244,54 @@ for (const day of history) {
274
244
  console.log(` Volume: ${day.volume?.toLocaleString()}`);
275
245
  console.log(` Range: $${range.toFixed(2)} (${rangePercent.toFixed(2)}%)`);
276
246
  }
277
-
278
- // Calculate Simple Moving Average (SMA)
279
- const calculateSMA = (data: IStockPrice[], period: number) => {
280
- const sma: number[] = [];
281
- for (let i = period - 1; i < data.length; i++) {
282
- const sum = data.slice(i - period + 1, i + 1)
283
- .reduce((acc, p) => acc + p.price, 0);
284
- sma.push(sum / period);
285
- }
286
- return sma;
287
- };
288
-
289
- const sma20 = calculateSMA(history, 20);
290
- const sma50 = calculateSMA(history, 50);
291
-
292
- console.log(`20-day SMA: $${sma20[0].toFixed(2)}`);
293
- console.log(`50-day SMA: $${sma50[0].toFixed(2)}`);
294
247
  ```
295
248
 
296
- ### Phase 1: Smart Caching Performance
249
+ ### Fundamental Data Screening
297
250
 
298
- Leverage smart caching for efficiency:
251
+ Screen stocks based on fundamental metrics:
299
252
 
300
253
  ```typescript
301
- // Historical data is cached FOREVER (never changes)
302
- console.time('First historical fetch');
303
- const history1 = await stockService.getData({
304
- type: 'historical',
305
- ticker: 'AAPL',
306
- from: new Date('2024-01-01'),
307
- to: new Date('2024-12-31')
308
- });
309
- console.timeEnd('First historical fetch');
310
- // Output: First historical fetch: 2341ms
254
+ const tickers = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'TSLA', 'META', 'NVDA'];
311
255
 
312
- console.time('Second historical fetch (cached)');
313
- const history2 = await stockService.getData({
314
- type: 'historical',
315
- ticker: 'AAPL',
316
- from: new Date('2024-01-01'),
317
- to: new Date('2024-12-31')
318
- });
319
- console.timeEnd('Second historical fetch (cached)');
320
- // Output: Second historical fetch (cached): 2ms (1000x faster!)
256
+ // Fetch fundamentals for all tickers
257
+ const allFundamentals = await fundamentalsService.getBatchFundamentals(tickers);
321
258
 
322
- // EOD data cached for 24 hours
323
- const currentPrice = await stockService.getData({
324
- type: 'current',
325
- ticker: 'MSFT'
259
+ // Get current prices
260
+ const prices = await stockService.getData({
261
+ type: 'batch',
262
+ tickers: tickers
326
263
  });
327
- // Subsequent calls within 24h served from cache
328
264
 
329
- // Cache statistics
330
- 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
+ });
331
287
  ```
332
288
 
333
289
  ### Market Dashboard
334
290
 
335
- Create an EOD market overview:
291
+ Create a comprehensive market overview:
336
292
 
337
293
  ```typescript
338
294
  const indicators = [
339
- // Indices
340
- { ticker: '^GSPC', name: 'S&P 500' },
341
- { ticker: '^DJI', name: 'DOW Jones' },
342
-
343
- // Tech Giants
344
295
  { ticker: 'AAPL', name: 'Apple' },
345
296
  { ticker: 'MSFT', name: 'Microsoft' },
346
297
  { ticker: 'GOOGL', name: 'Alphabet' },
@@ -348,7 +299,8 @@ const indicators = [
348
299
  { ticker: 'TSLA', name: 'Tesla' }
349
300
  ];
350
301
 
351
- const prices = await stockService.getPrices({
302
+ const prices = await stockService.getData({
303
+ type: 'batch',
352
304
  tickers: indicators.map(i => i.ticker)
353
305
  });
354
306
 
@@ -359,197 +311,125 @@ prices.forEach(price => {
359
311
  const color = price.change >= 0 ? '\x1b[32m' : '\x1b[31m';
360
312
 
361
313
  console.log(
362
- `${indicator.name.padEnd(15)} ${price.price.toFixed(2).padStart(10)} ` +
314
+ `${price.companyName!.padEnd(25)} $${price.price.toFixed(2).padStart(8)} ` +
363
315
  `${color}${arrow} ${price.changePercent.toFixed(2)}%\x1b[0m`
364
316
  );
365
317
  });
366
318
  ```
367
319
 
368
- ### Provider Health and Statistics
320
+ ### Exchange-Specific Trading
369
321
 
370
- Monitor your provider health and track usage:
322
+ Compare prices across different exchanges:
371
323
 
372
324
  ```typescript
373
- // Check provider health
374
- const health = await stockService.checkProvidersHealth();
375
- console.log(`Marketstack: ${health.get('Marketstack') ? '✅' : '❌'}`);
376
-
377
- // Get provider statistics
378
- const stats = stockService.getProviderStats();
379
- const marketstackStats = stats.get('Marketstack');
380
- console.log('Marketstack Stats:', {
381
- successCount: marketstackStats.successCount,
382
- errorCount: marketstackStats.errorCount,
383
- lastError: marketstackStats.lastError
384
- });
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
+ }
385
347
  ```
386
348
 
387
- ### Handelsregister Integration
349
+ ## Configuration
388
350
 
389
- Automate German company data retrieval:
351
+ ### Stock Service Options
390
352
 
391
353
  ```typescript
392
- import { OpenData } from '@fin.cx/opendata';
393
- import * as path from 'path';
394
-
395
- // Configure paths first
396
- const openData = new OpenData({
397
- nogitDir: path.join(process.cwd(), '.nogit'),
398
- downloadDir: path.join(process.cwd(), '.nogit', 'downloads'),
399
- 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
400
357
  });
401
- await openData.start();
402
-
403
- // Search for a company
404
- const results = await openData.handelsregister.searchCompany("Siemens AG");
405
358
 
406
- // Get detailed information and documents
407
- const details = await openData.handelsregister.getSpecificCompany({
408
- court: "Munich",
409
- type: "HRB",
410
- 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
411
366
  });
412
367
 
413
- // Downloaded files include:
414
- // - XML data (SI files)
415
- // - PDF documents (AD files)
416
- for (const file of details.files) {
417
- await file.writeToDir('./downloads');
418
- }
368
+ // Yahoo Finance - Real-time data (no API key)
369
+ stockService.register(new YahooFinanceProvider(), {
370
+ enabled: true,
371
+ priority: 50
372
+ });
419
373
  ```
420
374
 
421
- ### Combined Data Analysis
422
-
423
- Merge financial and business data:
375
+ ### Fundamentals Service Options
424
376
 
425
377
  ```typescript
426
- import { OpenData, StockPriceService, MarketstackProvider } from '@fin.cx/opendata';
427
- import * as path from 'path';
428
-
429
- // Configure OpenData with paths
430
- const openData = new OpenData({
431
- nogitDir: path.join(process.cwd(), '.nogit'),
432
- downloadDir: path.join(process.cwd(), '.nogit', 'downloads'),
433
- 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
434
381
  });
435
- await openData.start();
436
-
437
- // Setup stock service
438
- const stockService = new StockPriceService({ ttl: 60000, maxEntries: 1000 });
439
- stockService.register(new MarketstackProvider('YOUR_API_KEY'));
440
-
441
- // Find all public German companies (AG)
442
- const publicCompanies = await openData.db
443
- .collection('businessrecords')
444
- .find({ legalForm: 'AG' })
445
- .toArray();
446
-
447
- // Enrich with stock data
448
- for (const company of publicCompanies) {
449
- try {
450
- // Map company to ticker (custom logic needed)
451
- const ticker = mapCompanyToTicker(company.data.name);
452
382
 
453
- if (ticker) {
454
- const stock = await stockService.getPrice({ ticker });
455
-
456
- // Add financial metrics
457
- company.data.stockPrice = stock.price;
458
- company.data.marketCap = stock.price * getSharesOutstanding(ticker);
459
- company.data.priceChange = stock.changePercent;
460
-
461
- await company.save();
462
- }
463
- } catch (error) {
464
- // Handle missing tickers gracefully
465
- }
466
- }
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
+ }));
467
390
  ```
468
391
 
469
- ## Configuration
392
+ ### Directory Configuration (German Business Data)
470
393
 
471
- ### Directory Configuration (Required for German Business Data)
394
+ All directory paths are mandatory when using German business data features:
472
395
 
473
- **All directory paths are mandatory when using `OpenData`.** Here are examples for different environments:
474
-
475
- #### Development Environment
476
396
  ```typescript
477
397
  import { OpenData } from '@fin.cx/opendata';
478
398
  import * as path from 'path';
479
399
 
400
+ // Development
480
401
  const openData = new OpenData({
481
402
  nogitDir: path.join(process.cwd(), '.nogit'),
482
403
  downloadDir: path.join(process.cwd(), '.nogit', 'downloads'),
483
404
  germanBusinessDataDir: path.join(process.cwd(), '.nogit', 'germanbusinessdata')
484
405
  });
485
- ```
486
-
487
- #### Production Environment
488
- ```typescript
489
- import { OpenData } from '@fin.cx/opendata';
490
- import * as path from 'path';
491
406
 
492
- const openData = new OpenData({
407
+ // Production
408
+ const openDataProd = new OpenData({
493
409
  nogitDir: '/var/lib/myapp/data',
494
410
  downloadDir: '/var/lib/myapp/data/downloads',
495
411
  germanBusinessDataDir: '/var/lib/myapp/data/germanbusinessdata'
496
412
  });
497
413
  ```
498
414
 
499
- #### Deno Compiled Binaries (or other read-only filesystems)
500
- ```typescript
501
- import { OpenData } from '@fin.cx/opendata';
502
-
503
- // Use OS temp directory or user data directory
504
- const dataDir = Deno.env.get('HOME') + '/.myapp/data';
505
-
506
- const openData = new OpenData({
507
- nogitDir: dataDir,
508
- downloadDir: dataDir + '/downloads',
509
- germanBusinessDataDir: dataDir + '/germanbusinessdata'
510
- });
511
- ```
512
-
513
- ### Stock Service Options
514
-
515
- ```typescript
516
- const stockService = new StockPriceService({
517
- ttl: 60000, // Cache for 1 minute
518
- maxEntries: 1000 // Max cached symbols
519
- });
520
-
521
- // Marketstack - EOD data, requires API key
522
- stockService.register(new MarketstackProvider('YOUR_API_KEY'), {
523
- enabled: true,
524
- priority: 100,
525
- timeout: 10000,
526
- retryAttempts: 3,
527
- retryDelay: 1000
528
- });
529
- ```
530
-
531
- ### MongoDB Setup
415
+ ### Environment Variables
532
416
 
533
- Set environment variables for German business data:
417
+ Set environment variables for API keys and database:
534
418
 
535
419
  ```env
420
+ # Marketstack API (for EOD stock data)
421
+ MARKETSTACK_COM_TOKEN=your_api_key_here
422
+
423
+ # MongoDB (for German business data)
536
424
  MONGODB_URL=mongodb://localhost:27017
537
425
  MONGODB_NAME=opendata
538
426
  MONGODB_USER=myuser
539
427
  MONGODB_PASS=mypass
540
428
  ```
541
429
 
542
- ### Marketstack API Key
543
-
544
- Get your free API key at [marketstack.com](https://marketstack.com) and set it in your environment:
545
-
546
- ```env
547
- MARKETSTACK_COM_TOKEN=your_api_key_here
548
- ```
549
-
550
430
  ## API Reference
551
431
 
552
- ### Stock Types
432
+ ### Stock Price Interfaces
553
433
 
554
434
  ```typescript
555
435
  interface IStockPrice {
@@ -564,62 +444,154 @@ interface IStockPrice {
564
444
  marketState: 'PRE' | 'REGULAR' | 'POST' | 'CLOSED';
565
445
  exchange?: string;
566
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;
567
502
  }
568
503
  ```
569
504
 
570
505
  ### Key Methods
571
506
 
572
507
  **StockPriceService**
573
- - `getPrice(request)` - Single stock price with automatic provider selection
574
- - `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
575
511
  - `register(provider, config)` - Add data provider with priority and retry config
576
512
  - `checkProvidersHealth()` - Test all providers and return health status
577
513
  - `getProviderStats()` - Get success/error statistics for each provider
578
514
  - `clearCache()` - Clear price cache
579
515
  - `setCacheTTL(ttl)` - Update cache TTL dynamically
580
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
+
581
537
  **MarketstackProvider**
582
- - ✅ End-of-Day (EOD) data
583
- - ✅ 125,000+ tickers across 72+ exchanges worldwide
584
- - ✅ Batch fetching support (multiple symbols in one request)
585
- - ✅ 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
586
544
  - âš ī¸ Requires API key (free tier: 100 requests/month)
587
- - âš ī¸ 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
588
552
 
589
553
  **OpenData**
590
554
  - `start()` - Initialize MongoDB connection
591
555
  - `buildInitialDb()` - Import bulk data
592
556
  - `CBusinessRecord` - Business record class
593
- - `handelsregister` - Registry automation
557
+ - `handelsregister` - German registry automation
594
558
 
595
559
  ## Provider Architecture
596
560
 
597
- The library uses a flexible provider system that makes it easy to add new data sources:
561
+ Add custom data providers easily:
598
562
 
599
563
  ```typescript
600
564
  class MyCustomProvider implements IStockProvider {
601
565
  name = 'My Provider';
602
566
  priority = 50;
603
567
  requiresAuth = true;
604
-
605
- async fetchPrice(request: IStockQuoteRequest): Promise<IStockPrice> {
606
- // Your implementation
607
- }
608
-
609
- async fetchPrices(request: IStockBatchQuoteRequest): Promise<IStockPrice[]> {
610
- // 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
+ }
611
582
  }
612
583
 
613
584
  async isAvailable(): Promise<boolean> {
614
585
  // Health check
586
+ return true;
615
587
  }
616
588
 
617
589
  supportsMarket(market: string): boolean {
618
- // Market validation
590
+ return ['US', 'UK', 'DE'].includes(market);
619
591
  }
620
592
 
621
593
  supportsTicker(ticker: string): boolean {
622
- // Ticker validation
594
+ return /^[A-Z]{1,5}$/.test(ticker);
623
595
  }
624
596
  }
625
597
 
@@ -628,30 +600,53 @@ stockService.register(new MyCustomProvider());
628
600
 
629
601
  ## Performance
630
602
 
631
- - **Batch fetching**: Get 100+ EOD prices in one API request
632
- - **Smart caching**: Instant repeated queries with configurable TTL
633
- - **Rate limit aware**: Automatic retry logic for API limits
634
- - **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
635
607
  - **Streaming**: Process GB-sized datasets without memory issues
608
+ - **Provider Fallback**: Automatic failover between data sources
636
609
 
637
610
  ## Testing
638
611
 
639
612
  Run the comprehensive test suite:
640
613
 
641
614
  ```bash
642
- npm test
615
+ pnpm test
643
616
  ```
644
617
 
645
- Test stock provider:
618
+ Test specific modules:
646
619
 
647
620
  ```bash
648
- 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
649
631
  ```
650
632
 
651
- Test German business data:
633
+ ## Getting API Keys
652
634
 
653
- ```bash
654
- 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
+ });
655
650
  ```
656
651
 
657
652
  ## License and Legal Information