@fin.cx/opendata 3.0.0 → 3.2.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.baseproviderservice.d.ts +129 -0
- package/dist_ts/stocks/classes.baseproviderservice.js +204 -0
- package/dist_ts/stocks/classes.fundamentalsservice.d.ts +104 -0
- package/dist_ts/stocks/classes.fundamentalsservice.js +298 -0
- package/dist_ts/stocks/classes.stockdataservice.d.ts +121 -0
- package/dist_ts/stocks/classes.stockdataservice.js +495 -0
- package/dist_ts/stocks/index.d.ts +6 -0
- package/dist_ts/stocks/index.js +9 -2
- package/dist_ts/stocks/interfaces/fundamentals.d.ts +82 -0
- package/dist_ts/stocks/interfaces/fundamentals.js +6 -0
- package/dist_ts/stocks/interfaces/stockdata.d.ts +57 -0
- package/dist_ts/stocks/interfaces/stockdata.js +2 -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 +453 -403
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/stocks/classes.baseproviderservice.ts +296 -0
- package/ts/stocks/classes.fundamentalsservice.ts +404 -0
- package/ts/stocks/classes.stockdataservice.ts +647 -0
- package/ts/stocks/index.ts +10 -2
- package/ts/stocks/interfaces/fundamentals.ts +102 -0
- package/ts/stocks/interfaces/stockdata.ts +65 -0
- package/ts/stocks/providers/provider.secedgar.ts +429 -0
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
/**
|
|
3
|
+
* Unified service for managing both stock prices and fundamentals
|
|
4
|
+
* Provides automatic enrichment and convenient combined data access
|
|
5
|
+
*/
|
|
6
|
+
export class StockDataService {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.priceProviders = new Map();
|
|
9
|
+
this.fundamentalsProviders = new Map();
|
|
10
|
+
this.priceCache = new Map();
|
|
11
|
+
this.fundamentalsCache = new Map();
|
|
12
|
+
this.logger = console;
|
|
13
|
+
this.config = {
|
|
14
|
+
cache: {
|
|
15
|
+
priceTTL: 24 * 60 * 60 * 1000, // 24 hours
|
|
16
|
+
fundamentalsTTL: 90 * 24 * 60 * 60 * 1000, // 90 days
|
|
17
|
+
maxEntries: 10000
|
|
18
|
+
},
|
|
19
|
+
timeout: {
|
|
20
|
+
price: 10000, // 10 seconds
|
|
21
|
+
fundamentals: 30000 // 30 seconds
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
if (config) {
|
|
25
|
+
this.config = {
|
|
26
|
+
cache: { ...this.config.cache, ...config.cache },
|
|
27
|
+
timeout: { ...this.config.timeout, ...config.timeout }
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// ========== Provider Management ==========
|
|
32
|
+
/**
|
|
33
|
+
* Register a price provider
|
|
34
|
+
*/
|
|
35
|
+
registerPriceProvider(provider, config) {
|
|
36
|
+
const defaultConfig = {
|
|
37
|
+
enabled: true,
|
|
38
|
+
priority: provider.priority,
|
|
39
|
+
timeout: this.config.timeout.price,
|
|
40
|
+
retryAttempts: 2,
|
|
41
|
+
retryDelay: 1000
|
|
42
|
+
};
|
|
43
|
+
const mergedConfig = { ...defaultConfig, ...config };
|
|
44
|
+
this.priceProviders.set(provider.name, {
|
|
45
|
+
provider,
|
|
46
|
+
config: mergedConfig,
|
|
47
|
+
successCount: 0,
|
|
48
|
+
errorCount: 0
|
|
49
|
+
});
|
|
50
|
+
console.log(`Registered price provider: ${provider.name}`);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Register a fundamentals provider
|
|
54
|
+
*/
|
|
55
|
+
registerFundamentalsProvider(provider, config) {
|
|
56
|
+
const defaultConfig = {
|
|
57
|
+
enabled: true,
|
|
58
|
+
priority: provider.priority,
|
|
59
|
+
timeout: this.config.timeout.fundamentals,
|
|
60
|
+
retryAttempts: 2,
|
|
61
|
+
retryDelay: 1000,
|
|
62
|
+
cacheTTL: this.config.cache.fundamentalsTTL
|
|
63
|
+
};
|
|
64
|
+
const mergedConfig = { ...defaultConfig, ...config };
|
|
65
|
+
this.fundamentalsProviders.set(provider.name, {
|
|
66
|
+
provider,
|
|
67
|
+
config: mergedConfig,
|
|
68
|
+
successCount: 0,
|
|
69
|
+
errorCount: 0
|
|
70
|
+
});
|
|
71
|
+
console.log(`Registered fundamentals provider: ${provider.name}`);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Unregister a price provider
|
|
75
|
+
*/
|
|
76
|
+
unregisterPriceProvider(providerName) {
|
|
77
|
+
this.priceProviders.delete(providerName);
|
|
78
|
+
console.log(`Unregistered price provider: ${providerName}`);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Unregister a fundamentals provider
|
|
82
|
+
*/
|
|
83
|
+
unregisterFundamentalsProvider(providerName) {
|
|
84
|
+
this.fundamentalsProviders.delete(providerName);
|
|
85
|
+
console.log(`Unregistered fundamentals provider: ${providerName}`);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get all registered price providers
|
|
89
|
+
*/
|
|
90
|
+
getPriceProviders() {
|
|
91
|
+
return Array.from(this.priceProviders.values()).map(entry => entry.provider);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get all registered fundamentals providers
|
|
95
|
+
*/
|
|
96
|
+
getFundamentalsProviders() {
|
|
97
|
+
return Array.from(this.fundamentalsProviders.values()).map(entry => entry.provider);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get enabled price providers sorted by priority
|
|
101
|
+
*/
|
|
102
|
+
getEnabledPriceProviders() {
|
|
103
|
+
return Array.from(this.priceProviders.values())
|
|
104
|
+
.filter(entry => entry.config.enabled)
|
|
105
|
+
.sort((a, b) => (b.config.priority || 0) - (a.config.priority || 0))
|
|
106
|
+
.map(entry => entry.provider);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get enabled fundamentals providers sorted by priority
|
|
110
|
+
*/
|
|
111
|
+
getEnabledFundamentalsProviders() {
|
|
112
|
+
return Array.from(this.fundamentalsProviders.values())
|
|
113
|
+
.filter(entry => entry.config.enabled)
|
|
114
|
+
.sort((a, b) => (b.config.priority || 0) - (a.config.priority || 0))
|
|
115
|
+
.map(entry => entry.provider);
|
|
116
|
+
}
|
|
117
|
+
// ========== Data Fetching Methods ==========
|
|
118
|
+
/**
|
|
119
|
+
* Get current price for a single ticker
|
|
120
|
+
*/
|
|
121
|
+
async getPrice(ticker) {
|
|
122
|
+
const cacheKey = `price:${ticker}`;
|
|
123
|
+
const cached = this.getFromCache(this.priceCache, cacheKey);
|
|
124
|
+
if (cached) {
|
|
125
|
+
console.log(`Cache hit for price: ${ticker}`);
|
|
126
|
+
return cached;
|
|
127
|
+
}
|
|
128
|
+
const providers = this.getEnabledPriceProviders();
|
|
129
|
+
if (providers.length === 0) {
|
|
130
|
+
throw new Error('No price providers available');
|
|
131
|
+
}
|
|
132
|
+
let lastError;
|
|
133
|
+
for (const provider of providers) {
|
|
134
|
+
const entry = this.priceProviders.get(provider.name);
|
|
135
|
+
try {
|
|
136
|
+
const result = await this.fetchWithRetry(() => provider.fetchData({ type: 'current', ticker }), entry.config);
|
|
137
|
+
entry.successCount++;
|
|
138
|
+
const price = result;
|
|
139
|
+
this.addToCache(this.priceCache, cacheKey, price, this.config.cache.priceTTL);
|
|
140
|
+
console.log(`Successfully fetched price for ${ticker} from ${provider.name}`);
|
|
141
|
+
return price;
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
entry.errorCount++;
|
|
145
|
+
entry.lastError = error;
|
|
146
|
+
entry.lastErrorTime = new Date();
|
|
147
|
+
lastError = error;
|
|
148
|
+
console.warn(`Provider ${provider.name} failed for ${ticker}: ${error.message}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
throw new Error(`Failed to fetch price for ${ticker} from all providers. Last error: ${lastError?.message}`);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Get current prices for multiple tickers
|
|
155
|
+
*/
|
|
156
|
+
async getPrices(tickers) {
|
|
157
|
+
const cacheKey = `prices:${tickers.sort().join(',')}`;
|
|
158
|
+
const cached = this.getFromCache(this.priceCache, cacheKey);
|
|
159
|
+
if (cached) {
|
|
160
|
+
console.log(`Cache hit for prices: ${tickers.length} tickers`);
|
|
161
|
+
return cached;
|
|
162
|
+
}
|
|
163
|
+
const providers = this.getEnabledPriceProviders();
|
|
164
|
+
if (providers.length === 0) {
|
|
165
|
+
throw new Error('No price providers available');
|
|
166
|
+
}
|
|
167
|
+
let lastError;
|
|
168
|
+
for (const provider of providers) {
|
|
169
|
+
const entry = this.priceProviders.get(provider.name);
|
|
170
|
+
try {
|
|
171
|
+
const result = await this.fetchWithRetry(() => provider.fetchData({ type: 'batch', tickers }), entry.config);
|
|
172
|
+
entry.successCount++;
|
|
173
|
+
const prices = result;
|
|
174
|
+
this.addToCache(this.priceCache, cacheKey, prices, this.config.cache.priceTTL);
|
|
175
|
+
console.log(`Successfully fetched ${prices.length} prices from ${provider.name}`);
|
|
176
|
+
return prices;
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
entry.errorCount++;
|
|
180
|
+
entry.lastError = error;
|
|
181
|
+
entry.lastErrorTime = new Date();
|
|
182
|
+
lastError = error;
|
|
183
|
+
console.warn(`Provider ${provider.name} failed for batch prices: ${error.message}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
throw new Error(`Failed to fetch prices for ${tickers.length} tickers from all providers. Last error: ${lastError?.message}`);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get fundamentals for a single ticker
|
|
190
|
+
*/
|
|
191
|
+
async getFundamentals(ticker) {
|
|
192
|
+
const cacheKey = `fundamentals:${ticker}`;
|
|
193
|
+
const cached = this.getFromCache(this.fundamentalsCache, cacheKey);
|
|
194
|
+
if (cached) {
|
|
195
|
+
console.log(`Cache hit for fundamentals: ${ticker}`);
|
|
196
|
+
return cached;
|
|
197
|
+
}
|
|
198
|
+
const providers = this.getEnabledFundamentalsProviders();
|
|
199
|
+
if (providers.length === 0) {
|
|
200
|
+
throw new Error('No fundamentals providers available');
|
|
201
|
+
}
|
|
202
|
+
let lastError;
|
|
203
|
+
for (const provider of providers) {
|
|
204
|
+
const entry = this.fundamentalsProviders.get(provider.name);
|
|
205
|
+
try {
|
|
206
|
+
const result = await this.fetchWithRetry(() => provider.fetchData({ type: 'fundamentals-current', ticker }), entry.config);
|
|
207
|
+
entry.successCount++;
|
|
208
|
+
const fundamentals = result;
|
|
209
|
+
const ttl = entry.config.cacheTTL || this.config.cache.fundamentalsTTL;
|
|
210
|
+
this.addToCache(this.fundamentalsCache, cacheKey, fundamentals, ttl);
|
|
211
|
+
console.log(`Successfully fetched fundamentals for ${ticker} from ${provider.name}`);
|
|
212
|
+
return fundamentals;
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
entry.errorCount++;
|
|
216
|
+
entry.lastError = error;
|
|
217
|
+
entry.lastErrorTime = new Date();
|
|
218
|
+
lastError = error;
|
|
219
|
+
console.warn(`Provider ${provider.name} failed for ${ticker} fundamentals: ${error.message}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
throw new Error(`Failed to fetch fundamentals for ${ticker} from all providers. Last error: ${lastError?.message}`);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Get fundamentals for multiple tickers
|
|
226
|
+
*/
|
|
227
|
+
async getBatchFundamentals(tickers) {
|
|
228
|
+
const cacheKey = `fundamentals-batch:${tickers.sort().join(',')}`;
|
|
229
|
+
const cached = this.getFromCache(this.fundamentalsCache, cacheKey);
|
|
230
|
+
if (cached) {
|
|
231
|
+
console.log(`Cache hit for batch fundamentals: ${tickers.length} tickers`);
|
|
232
|
+
return cached;
|
|
233
|
+
}
|
|
234
|
+
const providers = this.getEnabledFundamentalsProviders();
|
|
235
|
+
if (providers.length === 0) {
|
|
236
|
+
throw new Error('No fundamentals providers available');
|
|
237
|
+
}
|
|
238
|
+
let lastError;
|
|
239
|
+
for (const provider of providers) {
|
|
240
|
+
const entry = this.fundamentalsProviders.get(provider.name);
|
|
241
|
+
try {
|
|
242
|
+
const result = await this.fetchWithRetry(() => provider.fetchData({ type: 'fundamentals-batch', tickers }), entry.config);
|
|
243
|
+
entry.successCount++;
|
|
244
|
+
const fundamentals = result;
|
|
245
|
+
const ttl = entry.config.cacheTTL || this.config.cache.fundamentalsTTL;
|
|
246
|
+
this.addToCache(this.fundamentalsCache, cacheKey, fundamentals, ttl);
|
|
247
|
+
console.log(`Successfully fetched ${fundamentals.length} fundamentals from ${provider.name}`);
|
|
248
|
+
return fundamentals;
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
entry.errorCount++;
|
|
252
|
+
entry.lastError = error;
|
|
253
|
+
entry.lastErrorTime = new Date();
|
|
254
|
+
lastError = error;
|
|
255
|
+
console.warn(`Provider ${provider.name} failed for batch fundamentals: ${error.message}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
throw new Error(`Failed to fetch fundamentals for ${tickers.length} tickers from all providers. Last error: ${lastError?.message}`);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* ✨ Get complete stock data (price + fundamentals) with automatic enrichment
|
|
262
|
+
*/
|
|
263
|
+
async getStockData(request) {
|
|
264
|
+
const normalizedRequest = typeof request === 'string'
|
|
265
|
+
? { ticker: request, includeFundamentals: true, enrichFundamentals: true }
|
|
266
|
+
: { includeFundamentals: true, enrichFundamentals: true, ...request };
|
|
267
|
+
const price = await this.getPrice(normalizedRequest.ticker);
|
|
268
|
+
let fundamentals;
|
|
269
|
+
if (normalizedRequest.includeFundamentals) {
|
|
270
|
+
try {
|
|
271
|
+
fundamentals = await this.getFundamentals(normalizedRequest.ticker);
|
|
272
|
+
// Enrich fundamentals with price calculations
|
|
273
|
+
if (normalizedRequest.enrichFundamentals && fundamentals) {
|
|
274
|
+
fundamentals = this.enrichWithPrice(fundamentals, price.price);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
console.warn(`Failed to fetch fundamentals for ${normalizedRequest.ticker}: ${error.message}`);
|
|
279
|
+
// Continue without fundamentals
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
ticker: normalizedRequest.ticker,
|
|
284
|
+
price,
|
|
285
|
+
fundamentals,
|
|
286
|
+
fetchedAt: new Date()
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* ✨ Get complete stock data for multiple tickers with automatic enrichment
|
|
291
|
+
*/
|
|
292
|
+
async getBatchStockData(request) {
|
|
293
|
+
const normalizedRequest = Array.isArray(request)
|
|
294
|
+
? { tickers: request, includeFundamentals: true, enrichFundamentals: true }
|
|
295
|
+
: { includeFundamentals: true, enrichFundamentals: true, ...request };
|
|
296
|
+
const prices = await this.getPrices(normalizedRequest.tickers);
|
|
297
|
+
const priceMap = new Map(prices.map(p => [p.ticker, p]));
|
|
298
|
+
let fundamentalsMap = new Map();
|
|
299
|
+
if (normalizedRequest.includeFundamentals) {
|
|
300
|
+
try {
|
|
301
|
+
const fundamentals = await this.getBatchFundamentals(normalizedRequest.tickers);
|
|
302
|
+
// Enrich with prices if requested
|
|
303
|
+
if (normalizedRequest.enrichFundamentals) {
|
|
304
|
+
for (const fund of fundamentals) {
|
|
305
|
+
const price = priceMap.get(fund.ticker);
|
|
306
|
+
if (price) {
|
|
307
|
+
fundamentalsMap.set(fund.ticker, this.enrichWithPrice(fund, price.price));
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
fundamentalsMap.set(fund.ticker, fund);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
fundamentalsMap = new Map(fundamentals.map(f => [f.ticker, f]));
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
console.warn(`Failed to fetch batch fundamentals: ${error.message}`);
|
|
320
|
+
// Continue without fundamentals
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return normalizedRequest.tickers.map(ticker => ({
|
|
324
|
+
ticker,
|
|
325
|
+
price: priceMap.get(ticker),
|
|
326
|
+
fundamentals: fundamentalsMap.get(ticker),
|
|
327
|
+
fetchedAt: new Date()
|
|
328
|
+
}));
|
|
329
|
+
}
|
|
330
|
+
// ========== Helper Methods ==========
|
|
331
|
+
/**
|
|
332
|
+
* Enrich fundamentals with calculated metrics using current price
|
|
333
|
+
*/
|
|
334
|
+
enrichWithPrice(fundamentals, price) {
|
|
335
|
+
const enriched = { ...fundamentals };
|
|
336
|
+
// Calculate market cap: price × shares outstanding
|
|
337
|
+
if (fundamentals.sharesOutstanding) {
|
|
338
|
+
enriched.marketCap = price * fundamentals.sharesOutstanding;
|
|
339
|
+
}
|
|
340
|
+
// Calculate P/E ratio: price / EPS
|
|
341
|
+
if (fundamentals.earningsPerShareDiluted && fundamentals.earningsPerShareDiluted > 0) {
|
|
342
|
+
enriched.priceToEarnings = price / fundamentals.earningsPerShareDiluted;
|
|
343
|
+
}
|
|
344
|
+
// Calculate price-to-book: market cap / stockholders equity
|
|
345
|
+
if (enriched.marketCap && fundamentals.stockholdersEquity && fundamentals.stockholdersEquity > 0) {
|
|
346
|
+
enriched.priceToBook = enriched.marketCap / fundamentals.stockholdersEquity;
|
|
347
|
+
}
|
|
348
|
+
return enriched;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Fetch with retry logic
|
|
352
|
+
*/
|
|
353
|
+
async fetchWithRetry(fetchFn, config) {
|
|
354
|
+
const maxAttempts = config.retryAttempts || 1;
|
|
355
|
+
let lastError;
|
|
356
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
357
|
+
try {
|
|
358
|
+
return await fetchFn();
|
|
359
|
+
}
|
|
360
|
+
catch (error) {
|
|
361
|
+
lastError = error;
|
|
362
|
+
if (attempt < maxAttempts) {
|
|
363
|
+
const delay = (config.retryDelay || 1000) * attempt;
|
|
364
|
+
console.log(`Retry attempt ${attempt} after ${delay}ms`);
|
|
365
|
+
await plugins.smartdelay.delayFor(delay);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
throw lastError || new Error('Unknown error during fetch');
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Get from cache if not expired
|
|
373
|
+
*/
|
|
374
|
+
getFromCache(cache, key) {
|
|
375
|
+
const entry = cache.get(key);
|
|
376
|
+
if (!entry) {
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
// Check if cache entry has expired
|
|
380
|
+
const age = Date.now() - entry.timestamp.getTime();
|
|
381
|
+
if (entry.ttl !== Infinity && age > entry.ttl) {
|
|
382
|
+
cache.delete(key);
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
return entry.data;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Add to cache with TTL
|
|
389
|
+
*/
|
|
390
|
+
addToCache(cache, key, data, ttl) {
|
|
391
|
+
// Enforce max entries limit
|
|
392
|
+
if (cache.size >= this.config.cache.maxEntries) {
|
|
393
|
+
// Remove oldest entry
|
|
394
|
+
const oldestKey = cache.keys().next().value;
|
|
395
|
+
if (oldestKey) {
|
|
396
|
+
cache.delete(oldestKey);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
cache.set(key, {
|
|
400
|
+
data,
|
|
401
|
+
timestamp: new Date(),
|
|
402
|
+
ttl
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
// ========== Health & Statistics ==========
|
|
406
|
+
/**
|
|
407
|
+
* Check health of all providers (both price and fundamentals)
|
|
408
|
+
*/
|
|
409
|
+
async checkProvidersHealth() {
|
|
410
|
+
const health = new Map();
|
|
411
|
+
// Check price providers
|
|
412
|
+
for (const [name, entry] of this.priceProviders) {
|
|
413
|
+
if (!entry.config.enabled) {
|
|
414
|
+
health.set(`${name} (price)`, false);
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
try {
|
|
418
|
+
const isAvailable = await entry.provider.isAvailable();
|
|
419
|
+
health.set(`${name} (price)`, isAvailable);
|
|
420
|
+
}
|
|
421
|
+
catch (error) {
|
|
422
|
+
health.set(`${name} (price)`, false);
|
|
423
|
+
console.error(`Health check failed for ${name}:`, error);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
// Check fundamentals providers
|
|
427
|
+
for (const [name, entry] of this.fundamentalsProviders) {
|
|
428
|
+
if (!entry.config.enabled) {
|
|
429
|
+
health.set(`${name} (fundamentals)`, false);
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
try {
|
|
433
|
+
const isAvailable = await entry.provider.isAvailable();
|
|
434
|
+
health.set(`${name} (fundamentals)`, isAvailable);
|
|
435
|
+
}
|
|
436
|
+
catch (error) {
|
|
437
|
+
health.set(`${name} (fundamentals)`, false);
|
|
438
|
+
console.error(`Health check failed for ${name}:`, error);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return health;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Get statistics for all providers
|
|
445
|
+
*/
|
|
446
|
+
getProviderStats() {
|
|
447
|
+
const stats = new Map();
|
|
448
|
+
// Price provider stats
|
|
449
|
+
for (const [name, entry] of this.priceProviders) {
|
|
450
|
+
stats.set(name, {
|
|
451
|
+
type: 'price',
|
|
452
|
+
successCount: entry.successCount,
|
|
453
|
+
errorCount: entry.errorCount,
|
|
454
|
+
lastError: entry.lastError?.message,
|
|
455
|
+
lastErrorTime: entry.lastErrorTime
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
// Fundamentals provider stats
|
|
459
|
+
for (const [name, entry] of this.fundamentalsProviders) {
|
|
460
|
+
stats.set(name, {
|
|
461
|
+
type: 'fundamentals',
|
|
462
|
+
successCount: entry.successCount,
|
|
463
|
+
errorCount: entry.errorCount,
|
|
464
|
+
lastError: entry.lastError?.message,
|
|
465
|
+
lastErrorTime: entry.lastErrorTime
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
return stats;
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Clear all caches
|
|
472
|
+
*/
|
|
473
|
+
clearCache() {
|
|
474
|
+
this.priceCache.clear();
|
|
475
|
+
this.fundamentalsCache.clear();
|
|
476
|
+
console.log('All caches cleared');
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Get cache statistics
|
|
480
|
+
*/
|
|
481
|
+
getCacheStats() {
|
|
482
|
+
return {
|
|
483
|
+
priceCache: {
|
|
484
|
+
size: this.priceCache.size,
|
|
485
|
+
ttl: this.config.cache.priceTTL
|
|
486
|
+
},
|
|
487
|
+
fundamentalsCache: {
|
|
488
|
+
size: this.fundamentalsCache.size,
|
|
489
|
+
ttl: this.config.cache.fundamentalsTTL
|
|
490
|
+
},
|
|
491
|
+
maxEntries: this.config.cache.maxEntries
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5zdG9ja2RhdGFzZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvc3RvY2tzL2NsYXNzZXMuc3RvY2tkYXRhc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGVBQWUsQ0FBQztBQXFCekM7OztHQUdHO0FBQ0gsTUFBTSxPQUFPLGdCQUFnQjtJQXFCM0IsWUFBWSxNQUFnQztRQXBCcEMsbUJBQWMsR0FBRyxJQUFJLEdBQUcsRUFBMEMsQ0FBQztRQUNuRSwwQkFBcUIsR0FBRyxJQUFJLEdBQUcsRUFBaUQsQ0FBQztRQUVqRixlQUFVLEdBQUcsSUFBSSxHQUFHLEVBQW9ELENBQUM7UUFDekUsc0JBQWlCLEdBQUcsSUFBSSxHQUFHLEVBQWtFLENBQUM7UUFFOUYsV0FBTSxHQUFHLE9BQU8sQ0FBQztRQUVqQixXQUFNLEdBQXNDO1lBQ2xELEtBQUssRUFBRTtnQkFDTCxRQUFRLEVBQUUsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxFQUFFLFdBQVc7Z0JBQzFDLGVBQWUsRUFBRSxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxFQUFFLFVBQVU7Z0JBQ3JELFVBQVUsRUFBRSxLQUFLO2FBQ2xCO1lBQ0QsT0FBTyxFQUFFO2dCQUNQLEtBQUssRUFBRSxLQUFLLEVBQUUsYUFBYTtnQkFDM0IsWUFBWSxFQUFFLEtBQUssQ0FBQyxhQUFhO2FBQ2xDO1NBQ0YsQ0FBQztRQUdBLElBQUksTUFBTSxFQUFFLENBQUM7WUFDWCxJQUFJLENBQUMsTUFBTSxHQUFHO2dCQUNaLEtBQUssRUFBRSxFQUFFLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsR0FBRyxNQUFNLENBQUMsS0FBSyxFQUFFO2dCQUNoRCxPQUFPLEVBQUUsRUFBRSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLEdBQUcsTUFB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
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
export * from './interfaces/stockprice.js';
|
|
2
2
|
export * from './interfaces/provider.js';
|
|
3
|
+
export * from './interfaces/fundamentals.js';
|
|
4
|
+
export * from './interfaces/stockdata.js';
|
|
3
5
|
export * from './classes.stockservice.js';
|
|
6
|
+
export * from './classes.fundamentalsservice.js';
|
|
7
|
+
export * from './classes.stockdataservice.js';
|
|
8
|
+
export * from './classes.baseproviderservice.js';
|
|
4
9
|
export * from './providers/provider.yahoo.js';
|
|
5
10
|
export * from './providers/provider.marketstack.js';
|
|
11
|
+
export * from './providers/provider.secedgar.js';
|
package/dist_ts/stocks/index.js
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
// Export all interfaces
|
|
2
2
|
export * from './interfaces/stockprice.js';
|
|
3
3
|
export * from './interfaces/provider.js';
|
|
4
|
-
|
|
4
|
+
export * from './interfaces/fundamentals.js';
|
|
5
|
+
export * from './interfaces/stockdata.js';
|
|
6
|
+
// Export main services
|
|
5
7
|
export * from './classes.stockservice.js';
|
|
8
|
+
export * from './classes.fundamentalsservice.js';
|
|
9
|
+
export * from './classes.stockdataservice.js'; // ✨ New unified service
|
|
10
|
+
// Export base service (for advanced use cases)
|
|
11
|
+
export * from './classes.baseproviderservice.js';
|
|
6
12
|
// Export providers
|
|
7
13
|
export * from './providers/provider.yahoo.js';
|
|
8
14
|
export * from './providers/provider.marketstack.js';
|
|
9
|
-
|
|
15
|
+
export * from './providers/provider.secedgar.js';
|
|
16
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9zdG9ja3MvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsd0JBQXdCO0FBQ3hCLGNBQWMsNEJBQTRCLENBQUM7QUFDM0MsY0FBYywwQkFBMEIsQ0FBQztBQUN6QyxjQUFjLDhCQUE4QixDQUFDO0FBQzdDLGNBQWMsMkJBQTJCLENBQUM7QUFFMUMsdUJBQXVCO0FBQ3ZCLGNBQWMsMkJBQTJCLENBQUM7QUFDMUMsY0FBYyxrQ0FBa0MsQ0FBQztBQUNqRCxjQUFjLCtCQUErQixDQUFDLENBQUMsd0JBQXdCO0FBRXZFLCtDQUErQztBQUMvQyxjQUFjLGtDQUFrQyxDQUFDO0FBRWpELG1CQUFtQjtBQUNuQixjQUFjLCtCQUErQixDQUFDO0FBQzlDLGNBQWMscUNBQXFDLENBQUM7QUFDcEQsY0FBYyxrQ0FBa0MsQ0FBQyJ9
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interfaces for stock fundamental data (financials from SEC filings)
|
|
3
|
+
* Separate from stock price data (OHLCV) to maintain clean architecture
|
|
4
|
+
*/
|
|
5
|
+
export interface IFundamentalsCurrentRequest {
|
|
6
|
+
type: 'fundamentals-current';
|
|
7
|
+
ticker: string;
|
|
8
|
+
}
|
|
9
|
+
export interface IFundamentalsBatchRequest {
|
|
10
|
+
type: 'fundamentals-batch';
|
|
11
|
+
tickers: string[];
|
|
12
|
+
}
|
|
13
|
+
export type IFundamentalsRequest = IFundamentalsCurrentRequest | IFundamentalsBatchRequest;
|
|
14
|
+
/**
|
|
15
|
+
* Stock fundamental data from SEC filings (10-K, 10-Q)
|
|
16
|
+
* Contains financial metrics like EPS, Revenue, Assets, etc.
|
|
17
|
+
*/
|
|
18
|
+
export interface IStockFundamentals {
|
|
19
|
+
ticker: string;
|
|
20
|
+
cik: string;
|
|
21
|
+
companyName: string;
|
|
22
|
+
provider: string;
|
|
23
|
+
timestamp: Date;
|
|
24
|
+
fetchedAt: Date;
|
|
25
|
+
earningsPerShareBasic?: number;
|
|
26
|
+
earningsPerShareDiluted?: number;
|
|
27
|
+
sharesOutstanding?: number;
|
|
28
|
+
weightedAverageSharesOutstanding?: number;
|
|
29
|
+
revenue?: number;
|
|
30
|
+
netIncome?: number;
|
|
31
|
+
operatingIncome?: number;
|
|
32
|
+
grossProfit?: number;
|
|
33
|
+
costOfRevenue?: number;
|
|
34
|
+
assets?: number;
|
|
35
|
+
liabilities?: number;
|
|
36
|
+
stockholdersEquity?: number;
|
|
37
|
+
cash?: number;
|
|
38
|
+
propertyPlantEquipment?: number;
|
|
39
|
+
marketCap?: number;
|
|
40
|
+
priceToEarnings?: number;
|
|
41
|
+
priceToBook?: number;
|
|
42
|
+
fiscalYear?: string;
|
|
43
|
+
fiscalQuarter?: string;
|
|
44
|
+
filingDate?: Date;
|
|
45
|
+
form?: '10-K' | '10-Q' | string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Provider interface for fetching fundamental data
|
|
49
|
+
* Parallel to IStockProvider but for fundamentals instead of prices
|
|
50
|
+
*/
|
|
51
|
+
export interface IFundamentalsProvider {
|
|
52
|
+
name: string;
|
|
53
|
+
priority: number;
|
|
54
|
+
fetchData(request: IFundamentalsRequest): Promise<IStockFundamentals | IStockFundamentals[]>;
|
|
55
|
+
isAvailable(): Promise<boolean>;
|
|
56
|
+
readonly requiresAuth: boolean;
|
|
57
|
+
readonly rateLimit?: {
|
|
58
|
+
requestsPerMinute: number;
|
|
59
|
+
requestsPerDay?: number;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Configuration for fundamentals providers
|
|
64
|
+
*/
|
|
65
|
+
export interface IFundamentalsProviderConfig {
|
|
66
|
+
enabled: boolean;
|
|
67
|
+
priority?: number;
|
|
68
|
+
timeout?: number;
|
|
69
|
+
retryAttempts?: number;
|
|
70
|
+
retryDelay?: number;
|
|
71
|
+
cacheTTL?: number;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Registry for managing fundamental data providers
|
|
75
|
+
*/
|
|
76
|
+
export interface IFundamentalsProviderRegistry {
|
|
77
|
+
register(provider: IFundamentalsProvider, config?: IFundamentalsProviderConfig): void;
|
|
78
|
+
unregister(providerName: string): void;
|
|
79
|
+
getProvider(name: string): IFundamentalsProvider | undefined;
|
|
80
|
+
getAllProviders(): IFundamentalsProvider[];
|
|
81
|
+
getEnabledProviders(): IFundamentalsProvider[];
|
|
82
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interfaces for stock fundamental data (financials from SEC filings)
|
|
3
|
+
* Separate from stock price data (OHLCV) to maintain clean architecture
|
|
4
|
+
*/
|
|
5
|
+
export {};
|
|
6
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZnVuZGFtZW50YWxzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvc3RvY2tzL2ludGVyZmFjZXMvZnVuZGFtZW50YWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRyJ9
|