@fin.cx/opendata 1.7.0 → 2.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/classes.handelsregister.d.ts +2 -1
- package/dist_ts/classes.handelsregister.js +5 -5
- package/dist_ts/classes.jsonldata.d.ts +2 -1
- package/dist_ts/classes.jsonldata.js +5 -5
- package/dist_ts/classes.main.opendata.d.ts +7 -0
- package/dist_ts/classes.main.opendata.js +16 -5
- package/dist_ts/paths.d.ts +0 -3
- package/dist_ts/paths.js +1 -6
- package/dist_ts/stocks/classes.stockservice.d.ts +28 -1
- package/dist_ts/stocks/classes.stockservice.js +147 -9
- package/dist_ts/stocks/interfaces/stockprice.d.ts +51 -0
- package/dist_ts/stocks/providers/provider.marketstack.d.ts +43 -9
- package/dist_ts/stocks/providers/provider.marketstack.js +159 -22
- package/dist_ts/stocks/providers/provider.yahoo.js +7 -3
- package/package.json +1 -1
- package/readme.md +320 -22
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.handelsregister.ts +6 -4
- package/ts/classes.jsonldata.ts +6 -4
- package/ts/classes.main.opendata.ts +39 -13
- package/ts/paths.ts +1 -10
- package/ts/stocks/classes.stockservice.ts +204 -28
- package/ts/stocks/interfaces/stockprice.ts +73 -0
- package/ts/stocks/providers/provider.marketstack.ts +189 -22
- package/ts/stocks/providers/provider.yahoo.ts +6 -2
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
2
|
import type { IStockProvider, IProviderConfig, IProviderRegistry } from './interfaces/provider.js';
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
IStockPrice,
|
|
5
|
+
IStockQuoteRequest,
|
|
6
|
+
IStockBatchQuoteRequest,
|
|
7
|
+
IStockPriceError,
|
|
8
|
+
IStockDataRequest,
|
|
9
|
+
IStockCurrentRequest,
|
|
10
|
+
IStockHistoricalRequest,
|
|
11
|
+
IStockIntradayRequest,
|
|
12
|
+
IStockBatchCurrentRequest,
|
|
13
|
+
TIntervalType
|
|
14
|
+
} from './interfaces/stockprice.js';
|
|
4
15
|
|
|
5
16
|
interface IProviderEntry {
|
|
6
17
|
provider: IStockProvider;
|
|
@@ -12,18 +23,19 @@ interface IProviderEntry {
|
|
|
12
23
|
}
|
|
13
24
|
|
|
14
25
|
interface ICacheEntry {
|
|
15
|
-
price: IStockPrice;
|
|
26
|
+
price: IStockPrice | IStockPrice[];
|
|
16
27
|
timestamp: Date;
|
|
28
|
+
ttl: number; // Specific TTL for this entry
|
|
17
29
|
}
|
|
18
30
|
|
|
19
31
|
export class StockPriceService implements IProviderRegistry {
|
|
20
32
|
private providers = new Map<string, IProviderEntry>();
|
|
21
33
|
private cache = new Map<string, ICacheEntry>();
|
|
22
34
|
private logger = console;
|
|
23
|
-
|
|
35
|
+
|
|
24
36
|
private cacheConfig = {
|
|
25
|
-
ttl: 60000, // 60 seconds default
|
|
26
|
-
maxEntries:
|
|
37
|
+
ttl: 60000, // 60 seconds default (for backward compatibility)
|
|
38
|
+
maxEntries: 10000 // Increased for historical data
|
|
27
39
|
};
|
|
28
40
|
|
|
29
41
|
constructor(cacheConfig?: { ttl?: number; maxEntries?: number }) {
|
|
@@ -32,6 +44,43 @@ export class StockPriceService implements IProviderRegistry {
|
|
|
32
44
|
}
|
|
33
45
|
}
|
|
34
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Get data-type aware TTL for smart caching
|
|
49
|
+
*/
|
|
50
|
+
private getCacheTTL(dataType: 'eod' | 'historical' | 'intraday' | 'live', interval?: TIntervalType): number {
|
|
51
|
+
switch (dataType) {
|
|
52
|
+
case 'historical':
|
|
53
|
+
return Infinity; // Historical data never changes
|
|
54
|
+
case 'eod':
|
|
55
|
+
return 24 * 60 * 60 * 1000; // 24 hours (EOD is static after market close)
|
|
56
|
+
case 'intraday':
|
|
57
|
+
// Match cache TTL to interval
|
|
58
|
+
return this.getIntervalMs(interval);
|
|
59
|
+
case 'live':
|
|
60
|
+
return 30 * 1000; // 30 seconds for live data
|
|
61
|
+
default:
|
|
62
|
+
return this.cacheConfig.ttl; // Fallback to default
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Convert interval to milliseconds
|
|
68
|
+
*/
|
|
69
|
+
private getIntervalMs(interval?: TIntervalType): number {
|
|
70
|
+
if (!interval) return 60 * 1000; // Default 1 minute
|
|
71
|
+
|
|
72
|
+
const intervalMap: Record<TIntervalType, number> = {
|
|
73
|
+
'1min': 60 * 1000,
|
|
74
|
+
'5min': 5 * 60 * 1000,
|
|
75
|
+
'10min': 10 * 60 * 1000,
|
|
76
|
+
'15min': 15 * 60 * 1000,
|
|
77
|
+
'30min': 30 * 60 * 1000,
|
|
78
|
+
'1hour': 60 * 60 * 1000
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
return intervalMap[interval] || 60 * 1000;
|
|
82
|
+
}
|
|
83
|
+
|
|
35
84
|
public register(provider: IStockProvider, config?: IProviderConfig): void {
|
|
36
85
|
const defaultConfig: IProviderConfig = {
|
|
37
86
|
enabled: true,
|
|
@@ -75,8 +124,8 @@ export class StockPriceService implements IProviderRegistry {
|
|
|
75
124
|
|
|
76
125
|
public async getPrice(request: IStockQuoteRequest): Promise<IStockPrice> {
|
|
77
126
|
const cacheKey = this.getCacheKey(request);
|
|
78
|
-
const cached = this.getFromCache(cacheKey);
|
|
79
|
-
|
|
127
|
+
const cached = this.getFromCache(cacheKey) as IStockPrice | null;
|
|
128
|
+
|
|
80
129
|
if (cached) {
|
|
81
130
|
console.log(`Cache hit for ${request.ticker}`);
|
|
82
131
|
return cached;
|
|
@@ -91,7 +140,7 @@ export class StockPriceService implements IProviderRegistry {
|
|
|
91
140
|
|
|
92
141
|
for (const provider of providers) {
|
|
93
142
|
const entry = this.providers.get(provider.name)!;
|
|
94
|
-
|
|
143
|
+
|
|
95
144
|
try {
|
|
96
145
|
const price = await this.fetchWithRetry(
|
|
97
146
|
() => provider.fetchPrice(request),
|
|
@@ -99,7 +148,11 @@ export class StockPriceService implements IProviderRegistry {
|
|
|
99
148
|
);
|
|
100
149
|
|
|
101
150
|
entry.successCount++;
|
|
102
|
-
|
|
151
|
+
|
|
152
|
+
// Use smart TTL based on data type
|
|
153
|
+
const ttl = this.getCacheTTL(price.dataType);
|
|
154
|
+
this.addToCache(cacheKey, price, ttl);
|
|
155
|
+
|
|
103
156
|
console.log(`Successfully fetched ${request.ticker} from ${provider.name}`);
|
|
104
157
|
return price;
|
|
105
158
|
} catch (error) {
|
|
@@ -107,7 +160,7 @@ export class StockPriceService implements IProviderRegistry {
|
|
|
107
160
|
entry.lastError = error as Error;
|
|
108
161
|
entry.lastErrorTime = new Date();
|
|
109
162
|
lastError = error as Error;
|
|
110
|
-
|
|
163
|
+
|
|
111
164
|
console.warn(
|
|
112
165
|
`Provider ${provider.name} failed for ${request.ticker}: ${error.message}`
|
|
113
166
|
);
|
|
@@ -126,8 +179,8 @@ export class StockPriceService implements IProviderRegistry {
|
|
|
126
179
|
// Check cache for each ticker
|
|
127
180
|
for (const ticker of request.tickers) {
|
|
128
181
|
const cacheKey = this.getCacheKey({ ticker, includeExtendedHours: request.includeExtendedHours });
|
|
129
|
-
const cached = this.getFromCache(cacheKey);
|
|
130
|
-
|
|
182
|
+
const cached = this.getFromCache(cacheKey) as IStockPrice | null;
|
|
183
|
+
|
|
131
184
|
if (cached) {
|
|
132
185
|
cachedPrices.push(cached);
|
|
133
186
|
} else {
|
|
@@ -150,25 +203,26 @@ export class StockPriceService implements IProviderRegistry {
|
|
|
150
203
|
|
|
151
204
|
for (const provider of providers) {
|
|
152
205
|
const entry = this.providers.get(provider.name)!;
|
|
153
|
-
|
|
206
|
+
|
|
154
207
|
try {
|
|
155
208
|
fetchedPrices = await this.fetchWithRetry(
|
|
156
|
-
() => provider.fetchPrices({
|
|
157
|
-
tickers: tickersToFetch,
|
|
158
|
-
includeExtendedHours: request.includeExtendedHours
|
|
209
|
+
() => provider.fetchPrices({
|
|
210
|
+
tickers: tickersToFetch,
|
|
211
|
+
includeExtendedHours: request.includeExtendedHours
|
|
159
212
|
}),
|
|
160
213
|
entry.config
|
|
161
214
|
);
|
|
162
215
|
|
|
163
216
|
entry.successCount++;
|
|
164
|
-
|
|
165
|
-
// Cache the fetched prices
|
|
217
|
+
|
|
218
|
+
// Cache the fetched prices with smart TTL
|
|
166
219
|
for (const price of fetchedPrices) {
|
|
167
|
-
const cacheKey = this.getCacheKey({
|
|
168
|
-
ticker: price.ticker,
|
|
169
|
-
includeExtendedHours: request.includeExtendedHours
|
|
220
|
+
const cacheKey = this.getCacheKey({
|
|
221
|
+
ticker: price.ticker,
|
|
222
|
+
includeExtendedHours: request.includeExtendedHours
|
|
170
223
|
});
|
|
171
|
-
this.
|
|
224
|
+
const ttl = this.getCacheTTL(price.dataType);
|
|
225
|
+
this.addToCache(cacheKey, price, ttl);
|
|
172
226
|
}
|
|
173
227
|
|
|
174
228
|
console.log(
|
|
@@ -180,7 +234,7 @@ export class StockPriceService implements IProviderRegistry {
|
|
|
180
234
|
entry.lastError = error as Error;
|
|
181
235
|
entry.lastErrorTime = new Date();
|
|
182
236
|
lastError = error as Error;
|
|
183
|
-
|
|
237
|
+
|
|
184
238
|
console.warn(
|
|
185
239
|
`Provider ${provider.name} failed for batch request: ${error.message}`
|
|
186
240
|
);
|
|
@@ -196,6 +250,101 @@ export class StockPriceService implements IProviderRegistry {
|
|
|
196
250
|
return [...cachedPrices, ...fetchedPrices];
|
|
197
251
|
}
|
|
198
252
|
|
|
253
|
+
/**
|
|
254
|
+
* New unified data fetching method supporting all request types
|
|
255
|
+
*/
|
|
256
|
+
public async getData(request: IStockDataRequest): Promise<IStockPrice | IStockPrice[]> {
|
|
257
|
+
const cacheKey = this.getDataCacheKey(request);
|
|
258
|
+
const cached = this.getFromCache(cacheKey);
|
|
259
|
+
|
|
260
|
+
if (cached) {
|
|
261
|
+
console.log(`Cache hit for ${this.getRequestDescription(request)}`);
|
|
262
|
+
return cached;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const providers = this.getEnabledProviders();
|
|
266
|
+
if (providers.length === 0) {
|
|
267
|
+
throw new Error('No stock price providers available');
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
let lastError: Error | undefined;
|
|
271
|
+
|
|
272
|
+
for (const provider of providers) {
|
|
273
|
+
const entry = this.providers.get(provider.name)!;
|
|
274
|
+
|
|
275
|
+
// Check if provider supports the new fetchData method
|
|
276
|
+
if (typeof (provider as any).fetchData !== 'function') {
|
|
277
|
+
console.warn(`Provider ${provider.name} does not support new API, skipping`);
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
try {
|
|
282
|
+
const result = await this.fetchWithRetry(
|
|
283
|
+
() => (provider as any).fetchData(request),
|
|
284
|
+
entry.config
|
|
285
|
+
) as IStockPrice | IStockPrice[];
|
|
286
|
+
|
|
287
|
+
entry.successCount++;
|
|
288
|
+
|
|
289
|
+
// Determine TTL based on request type
|
|
290
|
+
const ttl = this.getRequestTTL(request, result);
|
|
291
|
+
this.addToCache(cacheKey, result, ttl);
|
|
292
|
+
|
|
293
|
+
console.log(`Successfully fetched ${this.getRequestDescription(request)} from ${provider.name}`);
|
|
294
|
+
return result;
|
|
295
|
+
} catch (error) {
|
|
296
|
+
entry.errorCount++;
|
|
297
|
+
entry.lastError = error as Error;
|
|
298
|
+
entry.lastErrorTime = new Date();
|
|
299
|
+
lastError = error as Error;
|
|
300
|
+
|
|
301
|
+
console.warn(
|
|
302
|
+
`Provider ${provider.name} failed for ${this.getRequestDescription(request)}: ${error.message}`
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
throw new Error(
|
|
308
|
+
`Failed to fetch ${this.getRequestDescription(request)} from all providers. Last error: ${lastError?.message}`
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Get TTL based on request type and result
|
|
314
|
+
*/
|
|
315
|
+
private getRequestTTL(request: IStockDataRequest, result: IStockPrice | IStockPrice[]): number {
|
|
316
|
+
switch (request.type) {
|
|
317
|
+
case 'historical':
|
|
318
|
+
return Infinity; // Historical data never changes
|
|
319
|
+
case 'current':
|
|
320
|
+
return this.getCacheTTL('eod');
|
|
321
|
+
case 'batch':
|
|
322
|
+
return this.getCacheTTL('eod');
|
|
323
|
+
case 'intraday':
|
|
324
|
+
return this.getCacheTTL('intraday', request.interval);
|
|
325
|
+
default:
|
|
326
|
+
return this.cacheConfig.ttl;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Get human-readable description of request
|
|
332
|
+
*/
|
|
333
|
+
private getRequestDescription(request: IStockDataRequest): string {
|
|
334
|
+
switch (request.type) {
|
|
335
|
+
case 'current':
|
|
336
|
+
return `current price for ${request.ticker}${request.exchange ? ` on ${request.exchange}` : ''}`;
|
|
337
|
+
case 'historical':
|
|
338
|
+
return `historical prices for ${request.ticker} from ${request.from.toISOString().split('T')[0]} to ${request.to.toISOString().split('T')[0]}`;
|
|
339
|
+
case 'intraday':
|
|
340
|
+
return `intraday ${request.interval} prices for ${request.ticker}`;
|
|
341
|
+
case 'batch':
|
|
342
|
+
return `batch prices for ${request.tickers.length} tickers`;
|
|
343
|
+
default:
|
|
344
|
+
return 'data';
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
199
348
|
public async checkProvidersHealth(): Promise<Map<string, boolean>> {
|
|
200
349
|
const health = new Map<string, boolean>();
|
|
201
350
|
|
|
@@ -271,19 +420,45 @@ export class StockPriceService implements IProviderRegistry {
|
|
|
271
420
|
throw lastError || new Error('Unknown error during fetch');
|
|
272
421
|
}
|
|
273
422
|
|
|
423
|
+
/**
|
|
424
|
+
* Legacy cache key generation
|
|
425
|
+
*/
|
|
274
426
|
private getCacheKey(request: IStockQuoteRequest): string {
|
|
275
427
|
return `${request.ticker}:${request.includeExtendedHours || false}`;
|
|
276
428
|
}
|
|
277
429
|
|
|
278
|
-
|
|
430
|
+
/**
|
|
431
|
+
* New cache key generation for discriminated union requests
|
|
432
|
+
*/
|
|
433
|
+
private getDataCacheKey(request: IStockDataRequest): string {
|
|
434
|
+
switch (request.type) {
|
|
435
|
+
case 'current':
|
|
436
|
+
return `current:${request.ticker}${request.exchange ? `:${request.exchange}` : ''}`;
|
|
437
|
+
case 'historical':
|
|
438
|
+
const fromStr = request.from.toISOString().split('T')[0];
|
|
439
|
+
const toStr = request.to.toISOString().split('T')[0];
|
|
440
|
+
return `historical:${request.ticker}:${fromStr}:${toStr}${request.exchange ? `:${request.exchange}` : ''}`;
|
|
441
|
+
case 'intraday':
|
|
442
|
+
const dateStr = request.date ? request.date.toISOString().split('T')[0] : 'latest';
|
|
443
|
+
return `intraday:${request.ticker}:${request.interval}:${dateStr}${request.exchange ? `:${request.exchange}` : ''}`;
|
|
444
|
+
case 'batch':
|
|
445
|
+
const tickers = request.tickers.sort().join(',');
|
|
446
|
+
return `batch:${tickers}${request.exchange ? `:${request.exchange}` : ''}`;
|
|
447
|
+
default:
|
|
448
|
+
return `unknown:${JSON.stringify(request)}`;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
private getFromCache(key: string): IStockPrice | IStockPrice[] | null {
|
|
279
453
|
const entry = this.cache.get(key);
|
|
280
|
-
|
|
454
|
+
|
|
281
455
|
if (!entry) {
|
|
282
456
|
return null;
|
|
283
457
|
}
|
|
284
458
|
|
|
459
|
+
// Check if cache entry has expired
|
|
285
460
|
const age = Date.now() - entry.timestamp.getTime();
|
|
286
|
-
if (age >
|
|
461
|
+
if (entry.ttl !== Infinity && age > entry.ttl) {
|
|
287
462
|
this.cache.delete(key);
|
|
288
463
|
return null;
|
|
289
464
|
}
|
|
@@ -291,7 +466,7 @@ export class StockPriceService implements IProviderRegistry {
|
|
|
291
466
|
return entry.price;
|
|
292
467
|
}
|
|
293
468
|
|
|
294
|
-
private addToCache(key: string, price: IStockPrice): void {
|
|
469
|
+
private addToCache(key: string, price: IStockPrice | IStockPrice[], ttl?: number): void {
|
|
295
470
|
// Enforce max entries limit
|
|
296
471
|
if (this.cache.size >= this.cacheConfig.maxEntries) {
|
|
297
472
|
// Remove oldest entry
|
|
@@ -303,7 +478,8 @@ export class StockPriceService implements IProviderRegistry {
|
|
|
303
478
|
|
|
304
479
|
this.cache.set(key, {
|
|
305
480
|
price,
|
|
306
|
-
timestamp: new Date()
|
|
481
|
+
timestamp: new Date(),
|
|
482
|
+
ttl: ttl || this.cacheConfig.ttl
|
|
307
483
|
});
|
|
308
484
|
}
|
|
309
485
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
2
|
|
|
3
|
+
// Enhanced stock price interface with additional OHLCV data
|
|
3
4
|
export interface IStockPrice {
|
|
4
5
|
ticker: string;
|
|
5
6
|
price: number;
|
|
@@ -12,6 +13,15 @@ export interface IStockPrice {
|
|
|
12
13
|
marketState: 'PRE' | 'REGULAR' | 'POST' | 'CLOSED';
|
|
13
14
|
exchange?: string;
|
|
14
15
|
exchangeName?: string;
|
|
16
|
+
|
|
17
|
+
// Phase 1 enhancements
|
|
18
|
+
volume?: number; // Trading volume
|
|
19
|
+
open?: number; // Opening price
|
|
20
|
+
high?: number; // Day high
|
|
21
|
+
low?: number; // Day low
|
|
22
|
+
adjusted?: boolean; // If price is split/dividend adjusted
|
|
23
|
+
dataType: 'eod' | 'intraday' | 'live'; // What kind of data this is
|
|
24
|
+
fetchedAt: Date; // When we fetched (vs data timestamp)
|
|
15
25
|
}
|
|
16
26
|
type CheckStockPrice = plugins.tsclass.typeFest.IsEqual<
|
|
17
27
|
IStockPrice,
|
|
@@ -25,11 +35,74 @@ export interface IStockPriceError {
|
|
|
25
35
|
timestamp: Date;
|
|
26
36
|
}
|
|
27
37
|
|
|
38
|
+
// Pagination support for large datasets
|
|
39
|
+
export interface IPaginatedResponse<T> {
|
|
40
|
+
data: T[];
|
|
41
|
+
pagination: {
|
|
42
|
+
currentPage: number;
|
|
43
|
+
totalPages: number;
|
|
44
|
+
totalRecords: number;
|
|
45
|
+
hasMore: boolean;
|
|
46
|
+
limit: number;
|
|
47
|
+
offset: number;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Phase 1: Discriminated union types for different request types
|
|
52
|
+
export type TIntervalType = '1min' | '5min' | '10min' | '15min' | '30min' | '1hour';
|
|
53
|
+
export type TSortOrder = 'ASC' | 'DESC';
|
|
54
|
+
|
|
55
|
+
// Current price request (latest EOD or live)
|
|
56
|
+
export interface IStockCurrentRequest {
|
|
57
|
+
type: 'current';
|
|
58
|
+
ticker: string;
|
|
59
|
+
exchange?: string; // MIC code like 'XNAS', 'XNYS', 'XLON'
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Historical price request (date range)
|
|
63
|
+
export interface IStockHistoricalRequest {
|
|
64
|
+
type: 'historical';
|
|
65
|
+
ticker: string;
|
|
66
|
+
from: Date;
|
|
67
|
+
to: Date;
|
|
68
|
+
exchange?: string;
|
|
69
|
+
sort?: TSortOrder;
|
|
70
|
+
limit?: number; // Max results per page (default 1000)
|
|
71
|
+
offset?: number; // For pagination
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Intraday price request (real-time intervals)
|
|
75
|
+
export interface IStockIntradayRequest {
|
|
76
|
+
type: 'intraday';
|
|
77
|
+
ticker: string;
|
|
78
|
+
interval: TIntervalType;
|
|
79
|
+
exchange?: string;
|
|
80
|
+
limit?: number; // Number of bars to return
|
|
81
|
+
date?: Date; // Specific date for historical intraday
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Batch current prices request
|
|
85
|
+
export interface IStockBatchCurrentRequest {
|
|
86
|
+
type: 'batch';
|
|
87
|
+
tickers: string[];
|
|
88
|
+
exchange?: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Union type for all stock data requests
|
|
92
|
+
export type IStockDataRequest =
|
|
93
|
+
| IStockCurrentRequest
|
|
94
|
+
| IStockHistoricalRequest
|
|
95
|
+
| IStockIntradayRequest
|
|
96
|
+
| IStockBatchCurrentRequest;
|
|
97
|
+
|
|
98
|
+
// Legacy interfaces (for backward compatibility during migration)
|
|
99
|
+
/** @deprecated Use IStockDataRequest with type: 'current' instead */
|
|
28
100
|
export interface IStockQuoteRequest {
|
|
29
101
|
ticker: string;
|
|
30
102
|
includeExtendedHours?: boolean;
|
|
31
103
|
}
|
|
32
104
|
|
|
105
|
+
/** @deprecated Use IStockDataRequest with type: 'batch' instead */
|
|
33
106
|
export interface IStockBatchQuoteRequest {
|
|
34
107
|
tickers: string[];
|
|
35
108
|
includeExtendedHours?: boolean;
|