@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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/stocks/classes.fundamentalsservice.d.ts +104 -0
- package/dist_ts/stocks/classes.fundamentalsservice.js +298 -0
- package/dist_ts/stocks/index.d.ts +3 -0
- package/dist_ts/stocks/index.js +5 -2
- package/dist_ts/stocks/interfaces/fundamentals.d.ts +82 -0
- package/dist_ts/stocks/interfaces/fundamentals.js +6 -0
- package/dist_ts/stocks/providers/provider.secedgar.d.ts +102 -0
- package/dist_ts/stocks/providers/provider.secedgar.js +343 -0
- package/package.json +1 -1
- package/readme.md +382 -404
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/stocks/classes.fundamentalsservice.ts +404 -0
- package/ts/stocks/index.ts +5 -2
- package/ts/stocks/interfaces/fundamentals.ts +102 -0
- package/ts/stocks/providers/provider.secedgar.ts +429 -0
package/readme.md
CHANGED
|
@@ -1,64 +1,8 @@
|
|
|
1
1
|
# @fin.cx/opendata
|
|
2
2
|
|
|
3
|
-
đ **
|
|
3
|
+
đ **Complete financial intelligence toolkit for TypeScript**
|
|
4
4
|
|
|
5
|
-
Access
|
|
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
|
|
17
|
+
### đ Stock Market Data
|
|
74
18
|
|
|
75
|
-
Get
|
|
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
|
|
24
|
+
// Initialize service with smart caching
|
|
81
25
|
const stockService = new StockPriceService({
|
|
82
|
-
ttl: 60000, //
|
|
83
|
-
maxEntries: 10000
|
|
26
|
+
ttl: 60000, // Cache TTL (historical cached forever)
|
|
27
|
+
maxEntries: 10000
|
|
84
28
|
});
|
|
85
29
|
|
|
86
|
-
// Register
|
|
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 (
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
//
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
|
|
140
|
-
|
|
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
|
|
77
|
+
### đĸ German Business Intelligence
|
|
144
78
|
|
|
145
|
-
Access comprehensive
|
|
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
|
-
//
|
|
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
|
-
//
|
|
160
|
-
const
|
|
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
|
-
//
|
|
176
|
-
await openData.
|
|
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
|
-
###
|
|
106
|
+
### đ Stock Market Module
|
|
182
107
|
|
|
183
|
-
- **
|
|
184
|
-
- **
|
|
185
|
-
- **
|
|
186
|
-
- **OHLCV Data** - Open, High, Low, Close, Volume for
|
|
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
|
-
- **
|
|
191
|
-
- **
|
|
192
|
-
|
|
193
|
-
|
|
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
|
|
198
|
-
- **Bulk JSONL
|
|
199
|
-
- **Handelsregister
|
|
200
|
-
- **CRUD
|
|
201
|
-
- **Streaming
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
245
|
-
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
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
|
-
|
|
252
|
-
|
|
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
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
-
###
|
|
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
|
-
###
|
|
249
|
+
### Fundamental Data Screening
|
|
314
250
|
|
|
315
|
-
|
|
251
|
+
Screen stocks based on fundamental metrics:
|
|
316
252
|
|
|
317
253
|
```typescript
|
|
318
|
-
|
|
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
|
-
|
|
330
|
-
const
|
|
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
|
-
//
|
|
340
|
-
const
|
|
341
|
-
type: '
|
|
342
|
-
|
|
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
|
-
//
|
|
347
|
-
|
|
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
|
|
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.
|
|
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
|
-
`${
|
|
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
|
-
###
|
|
320
|
+
### Exchange-Specific Trading
|
|
386
321
|
|
|
387
|
-
|
|
322
|
+
Compare prices across different exchanges:
|
|
388
323
|
|
|
389
324
|
```typescript
|
|
390
|
-
//
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
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
|
-
|
|
349
|
+
## Configuration
|
|
405
350
|
|
|
406
|
-
|
|
351
|
+
### Stock Service Options
|
|
407
352
|
|
|
408
353
|
```typescript
|
|
409
|
-
|
|
410
|
-
|
|
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
|
-
//
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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
|
-
//
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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
|
-
###
|
|
439
|
-
|
|
440
|
-
Merge financial and business data:
|
|
375
|
+
### Fundamentals Service Options
|
|
441
376
|
|
|
442
377
|
```typescript
|
|
443
|
-
|
|
444
|
-
|
|
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
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
//
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
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
|
-
|
|
392
|
+
### Directory Configuration (German Business Data)
|
|
487
393
|
|
|
488
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
- `
|
|
591
|
-
- `
|
|
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)
|
|
600
|
-
- â
|
|
601
|
-
- â
|
|
602
|
-
- â
|
|
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
|
-
|
|
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` -
|
|
557
|
+
- `handelsregister` - German registry automation
|
|
611
558
|
|
|
612
559
|
## Provider Architecture
|
|
613
560
|
|
|
614
|
-
|
|
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
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
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
|
-
|
|
590
|
+
return ['US', 'UK', 'DE'].includes(market);
|
|
636
591
|
}
|
|
637
592
|
|
|
638
593
|
supportsTicker(ticker: string): boolean {
|
|
639
|
-
|
|
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
|
|
649
|
-
- **Smart
|
|
650
|
-
- **Rate
|
|
651
|
-
- **Concurrent
|
|
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
|
-
|
|
615
|
+
pnpm test
|
|
660
616
|
```
|
|
661
617
|
|
|
662
|
-
Test
|
|
618
|
+
Test specific modules:
|
|
663
619
|
|
|
664
620
|
```bash
|
|
665
|
-
|
|
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
|
-
|
|
633
|
+
## Getting API Keys
|
|
669
634
|
|
|
670
|
-
|
|
671
|
-
|
|
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
|