@fin.cx/opendata 1.6.1 → 2.0.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/index.d.ts +1 -0
- package/dist_ts/stocks/index.js +2 -1
- package/dist_ts/stocks/providers/provider.marketstack.d.ts +57 -0
- package/dist_ts/stocks/providers/provider.marketstack.js +173 -0
- package/package.json +2 -2
- package/readme.md +209 -59
- 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/index.ts +2 -1
- package/ts/stocks/providers/provider.marketstack.ts +200 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import type { IStockProvider, IProviderConfig } from '../interfaces/provider.js';
|
|
3
|
+
import type { IStockPrice, IStockQuoteRequest, IStockBatchQuoteRequest } from '../interfaces/stockprice.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Marketstack API v2 Provider
|
|
7
|
+
* Documentation: https://marketstack.com/documentation_v2
|
|
8
|
+
*
|
|
9
|
+
* Features:
|
|
10
|
+
* - End-of-Day (EOD) stock prices
|
|
11
|
+
* - Supports 125,000+ tickers across 72+ exchanges worldwide
|
|
12
|
+
* - Requires API key authentication
|
|
13
|
+
*
|
|
14
|
+
* Rate Limits:
|
|
15
|
+
* - Free Plan: 100 requests/month (EOD only)
|
|
16
|
+
* - Basic Plan: 10,000 requests/month
|
|
17
|
+
* - Professional Plan: 100,000 requests/month
|
|
18
|
+
*
|
|
19
|
+
* Note: This provider returns EOD data, not real-time prices
|
|
20
|
+
*/
|
|
21
|
+
export class MarketstackProvider implements IStockProvider {
|
|
22
|
+
public name = 'Marketstack';
|
|
23
|
+
public priority = 80; // Lower than Yahoo (100) due to rate limits and EOD-only data
|
|
24
|
+
public readonly requiresAuth = true;
|
|
25
|
+
public readonly rateLimit = {
|
|
26
|
+
requestsPerMinute: undefined, // No per-minute limit specified
|
|
27
|
+
requestsPerDay: undefined // Varies by plan
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
private logger = console;
|
|
31
|
+
private baseUrl = 'https://api.marketstack.com/v2';
|
|
32
|
+
private apiKey: string;
|
|
33
|
+
|
|
34
|
+
constructor(apiKey: string, private config?: IProviderConfig) {
|
|
35
|
+
if (!apiKey) {
|
|
36
|
+
throw new Error('API key is required for Marketstack provider');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this.apiKey = apiKey;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Fetch latest EOD price for a single ticker
|
|
44
|
+
*/
|
|
45
|
+
public async fetchPrice(request: IStockQuoteRequest): Promise<IStockPrice> {
|
|
46
|
+
try {
|
|
47
|
+
const url = `${this.baseUrl}/tickers/${request.ticker}/eod/latest?access_key=${this.apiKey}`;
|
|
48
|
+
|
|
49
|
+
const response = await plugins.smartrequest.SmartRequest.create()
|
|
50
|
+
.url(url)
|
|
51
|
+
.timeout(this.config?.timeout || 10000)
|
|
52
|
+
.get();
|
|
53
|
+
|
|
54
|
+
const responseData = await response.json() as any;
|
|
55
|
+
|
|
56
|
+
// Check for API errors
|
|
57
|
+
if (responseData.error) {
|
|
58
|
+
throw new Error(`Marketstack API error: ${responseData.error.message || JSON.stringify(responseData.error)}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// For single ticker endpoint, response is direct object (not wrapped in data field)
|
|
62
|
+
if (!responseData || !responseData.close) {
|
|
63
|
+
throw new Error(`No data found for ticker ${request.ticker}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return this.mapToStockPrice(responseData);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
this.logger.error(`Failed to fetch price for ${request.ticker}:`, error);
|
|
69
|
+
throw new Error(`Marketstack: Failed to fetch price for ${request.ticker}: ${error.message}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Fetch latest EOD prices for multiple tickers
|
|
75
|
+
*/
|
|
76
|
+
public async fetchPrices(request: IStockBatchQuoteRequest): Promise<IStockPrice[]> {
|
|
77
|
+
try {
|
|
78
|
+
const symbols = request.tickers.join(',');
|
|
79
|
+
const url = `${this.baseUrl}/eod/latest?access_key=${this.apiKey}&symbols=${symbols}`;
|
|
80
|
+
|
|
81
|
+
const response = await plugins.smartrequest.SmartRequest.create()
|
|
82
|
+
.url(url)
|
|
83
|
+
.timeout(this.config?.timeout || 15000)
|
|
84
|
+
.get();
|
|
85
|
+
|
|
86
|
+
const responseData = await response.json() as any;
|
|
87
|
+
|
|
88
|
+
// Check for API errors
|
|
89
|
+
if (responseData.error) {
|
|
90
|
+
throw new Error(`Marketstack API error: ${responseData.error.message || JSON.stringify(responseData.error)}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!responseData?.data || !Array.isArray(responseData.data)) {
|
|
94
|
+
throw new Error('Invalid response format from Marketstack API');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const prices: IStockPrice[] = [];
|
|
98
|
+
|
|
99
|
+
for (const data of responseData.data) {
|
|
100
|
+
try {
|
|
101
|
+
prices.push(this.mapToStockPrice(data));
|
|
102
|
+
} catch (error) {
|
|
103
|
+
this.logger.warn(`Failed to parse data for ${data.symbol}:`, error);
|
|
104
|
+
// Continue processing other tickers
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (prices.length === 0) {
|
|
109
|
+
throw new Error('No valid price data received from batch request');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return prices;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
this.logger.error(`Failed to fetch batch prices:`, error);
|
|
115
|
+
throw new Error(`Marketstack: Failed to fetch batch prices: ${error.message}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Check if the Marketstack API is available and accessible
|
|
121
|
+
*/
|
|
122
|
+
public async isAvailable(): Promise<boolean> {
|
|
123
|
+
try {
|
|
124
|
+
// Test with a well-known ticker
|
|
125
|
+
const url = `${this.baseUrl}/tickers/AAPL/eod/latest?access_key=${this.apiKey}`;
|
|
126
|
+
|
|
127
|
+
const response = await plugins.smartrequest.SmartRequest.create()
|
|
128
|
+
.url(url)
|
|
129
|
+
.timeout(5000)
|
|
130
|
+
.get();
|
|
131
|
+
|
|
132
|
+
const responseData = await response.json() as any;
|
|
133
|
+
|
|
134
|
+
// Check if we got valid data (not an error)
|
|
135
|
+
// Single ticker endpoint returns direct object, not wrapped in data field
|
|
136
|
+
return !responseData.error && responseData.close !== undefined;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
this.logger.warn('Marketstack provider is not available:', error);
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Check if a market is supported
|
|
145
|
+
* Marketstack supports 72+ exchanges worldwide
|
|
146
|
+
*/
|
|
147
|
+
public supportsMarket(market: string): boolean {
|
|
148
|
+
// Marketstack has broad international coverage including:
|
|
149
|
+
// US, UK, DE, FR, JP, CN, HK, AU, CA, IN, etc.
|
|
150
|
+
const supportedMarkets = [
|
|
151
|
+
'US', 'UK', 'GB', 'DE', 'FR', 'JP', 'CN', 'HK', 'AU', 'CA',
|
|
152
|
+
'IN', 'BR', 'MX', 'IT', 'ES', 'NL', 'SE', 'CH', 'NO', 'DK'
|
|
153
|
+
];
|
|
154
|
+
return supportedMarkets.includes(market.toUpperCase());
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Check if a ticker format is supported
|
|
159
|
+
*/
|
|
160
|
+
public supportsTicker(ticker: string): boolean {
|
|
161
|
+
// Basic validation - Marketstack supports most standard ticker formats
|
|
162
|
+
return /^[A-Z0-9\.\-]{1,10}$/.test(ticker.toUpperCase());
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Map Marketstack API response to IStockPrice interface
|
|
167
|
+
*/
|
|
168
|
+
private mapToStockPrice(data: any): IStockPrice {
|
|
169
|
+
if (!data.close) {
|
|
170
|
+
throw new Error('Missing required price data');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Calculate change and change percent
|
|
174
|
+
// EOD data: previous close is typically open price of the same day
|
|
175
|
+
// For better accuracy, we'd need previous day's close, but that requires another API call
|
|
176
|
+
const currentPrice = data.close;
|
|
177
|
+
const previousClose = data.open;
|
|
178
|
+
const change = currentPrice - previousClose;
|
|
179
|
+
const changePercent = previousClose !== 0 ? (change / previousClose) * 100 : 0;
|
|
180
|
+
|
|
181
|
+
// Parse timestamp
|
|
182
|
+
const timestamp = data.date ? new Date(data.date) : new Date();
|
|
183
|
+
|
|
184
|
+
const stockPrice: IStockPrice = {
|
|
185
|
+
ticker: data.symbol.toUpperCase(),
|
|
186
|
+
price: currentPrice,
|
|
187
|
+
currency: data.price_currency || 'USD',
|
|
188
|
+
change: change,
|
|
189
|
+
changePercent: changePercent,
|
|
190
|
+
previousClose: previousClose,
|
|
191
|
+
timestamp: timestamp,
|
|
192
|
+
provider: this.name,
|
|
193
|
+
marketState: 'CLOSED', // EOD data is always for closed markets
|
|
194
|
+
exchange: data.exchange,
|
|
195
|
+
exchangeName: data.exchange_code || data.name
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
return stockPrice;
|
|
199
|
+
}
|
|
200
|
+
}
|