@fin.cx/opendata 1.6.1 → 1.7.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/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 +120 -58
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/stocks/index.ts +2 -1
- package/ts/stocks/providers/provider.marketstack.ts +200 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@fin.cx/opendata',
|
|
6
|
-
version: '1.
|
|
6
|
+
version: '1.7.0',
|
|
7
7
|
description: 'A comprehensive TypeScript library for accessing business data and real-time financial information. Features include German company data management with MongoDB integration, JSONL bulk processing, automated Handelsregister interactions, and real-time stock market data from multiple providers.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxrQkFBa0I7SUFDeEIsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLHVTQUF1UztDQUNyVCxDQUFBIn0=
|
package/dist_ts/stocks/index.js
CHANGED
|
@@ -5,4 +5,5 @@ export * from './interfaces/provider.js';
|
|
|
5
5
|
export * from './classes.stockservice.js';
|
|
6
6
|
// Export providers
|
|
7
7
|
export * from './providers/provider.yahoo.js';
|
|
8
|
-
|
|
8
|
+
export * from './providers/provider.marketstack.js';
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9zdG9ja3MvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsd0JBQXdCO0FBQ3hCLGNBQWMsNEJBQTRCLENBQUM7QUFDM0MsY0FBYywwQkFBMEIsQ0FBQztBQUV6QyxzQkFBc0I7QUFDdEIsY0FBYywyQkFBMkIsQ0FBQztBQUUxQyxtQkFBbUI7QUFDbkIsY0FBYywrQkFBK0IsQ0FBQztBQUM5QyxjQUFjLHFDQUFxQyxDQUFDIn0=
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { IStockProvider, IProviderConfig } from '../interfaces/provider.js';
|
|
2
|
+
import type { IStockPrice, IStockQuoteRequest, IStockBatchQuoteRequest } from '../interfaces/stockprice.js';
|
|
3
|
+
/**
|
|
4
|
+
* Marketstack API v2 Provider
|
|
5
|
+
* Documentation: https://marketstack.com/documentation_v2
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - End-of-Day (EOD) stock prices
|
|
9
|
+
* - Supports 125,000+ tickers across 72+ exchanges worldwide
|
|
10
|
+
* - Requires API key authentication
|
|
11
|
+
*
|
|
12
|
+
* Rate Limits:
|
|
13
|
+
* - Free Plan: 100 requests/month (EOD only)
|
|
14
|
+
* - Basic Plan: 10,000 requests/month
|
|
15
|
+
* - Professional Plan: 100,000 requests/month
|
|
16
|
+
*
|
|
17
|
+
* Note: This provider returns EOD data, not real-time prices
|
|
18
|
+
*/
|
|
19
|
+
export declare class MarketstackProvider implements IStockProvider {
|
|
20
|
+
private config?;
|
|
21
|
+
name: string;
|
|
22
|
+
priority: number;
|
|
23
|
+
readonly requiresAuth = true;
|
|
24
|
+
readonly rateLimit: {
|
|
25
|
+
requestsPerMinute: any;
|
|
26
|
+
requestsPerDay: any;
|
|
27
|
+
};
|
|
28
|
+
private logger;
|
|
29
|
+
private baseUrl;
|
|
30
|
+
private apiKey;
|
|
31
|
+
constructor(apiKey: string, config?: IProviderConfig);
|
|
32
|
+
/**
|
|
33
|
+
* Fetch latest EOD price for a single ticker
|
|
34
|
+
*/
|
|
35
|
+
fetchPrice(request: IStockQuoteRequest): Promise<IStockPrice>;
|
|
36
|
+
/**
|
|
37
|
+
* Fetch latest EOD prices for multiple tickers
|
|
38
|
+
*/
|
|
39
|
+
fetchPrices(request: IStockBatchQuoteRequest): Promise<IStockPrice[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Check if the Marketstack API is available and accessible
|
|
42
|
+
*/
|
|
43
|
+
isAvailable(): Promise<boolean>;
|
|
44
|
+
/**
|
|
45
|
+
* Check if a market is supported
|
|
46
|
+
* Marketstack supports 72+ exchanges worldwide
|
|
47
|
+
*/
|
|
48
|
+
supportsMarket(market: string): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Check if a ticker format is supported
|
|
51
|
+
*/
|
|
52
|
+
supportsTicker(ticker: string): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Map Marketstack API response to IStockPrice interface
|
|
55
|
+
*/
|
|
56
|
+
private mapToStockPrice;
|
|
57
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
/**
|
|
3
|
+
* Marketstack API v2 Provider
|
|
4
|
+
* Documentation: https://marketstack.com/documentation_v2
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - End-of-Day (EOD) stock prices
|
|
8
|
+
* - Supports 125,000+ tickers across 72+ exchanges worldwide
|
|
9
|
+
* - Requires API key authentication
|
|
10
|
+
*
|
|
11
|
+
* Rate Limits:
|
|
12
|
+
* - Free Plan: 100 requests/month (EOD only)
|
|
13
|
+
* - Basic Plan: 10,000 requests/month
|
|
14
|
+
* - Professional Plan: 100,000 requests/month
|
|
15
|
+
*
|
|
16
|
+
* Note: This provider returns EOD data, not real-time prices
|
|
17
|
+
*/
|
|
18
|
+
export class MarketstackProvider {
|
|
19
|
+
constructor(apiKey, config) {
|
|
20
|
+
this.config = config;
|
|
21
|
+
this.name = 'Marketstack';
|
|
22
|
+
this.priority = 80; // Lower than Yahoo (100) due to rate limits and EOD-only data
|
|
23
|
+
this.requiresAuth = true;
|
|
24
|
+
this.rateLimit = {
|
|
25
|
+
requestsPerMinute: undefined, // No per-minute limit specified
|
|
26
|
+
requestsPerDay: undefined // Varies by plan
|
|
27
|
+
};
|
|
28
|
+
this.logger = console;
|
|
29
|
+
this.baseUrl = 'https://api.marketstack.com/v2';
|
|
30
|
+
if (!apiKey) {
|
|
31
|
+
throw new Error('API key is required for Marketstack provider');
|
|
32
|
+
}
|
|
33
|
+
this.apiKey = apiKey;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Fetch latest EOD price for a single ticker
|
|
37
|
+
*/
|
|
38
|
+
async fetchPrice(request) {
|
|
39
|
+
try {
|
|
40
|
+
const url = `${this.baseUrl}/tickers/${request.ticker}/eod/latest?access_key=${this.apiKey}`;
|
|
41
|
+
const response = await plugins.smartrequest.SmartRequest.create()
|
|
42
|
+
.url(url)
|
|
43
|
+
.timeout(this.config?.timeout || 10000)
|
|
44
|
+
.get();
|
|
45
|
+
const responseData = await response.json();
|
|
46
|
+
// Check for API errors
|
|
47
|
+
if (responseData.error) {
|
|
48
|
+
throw new Error(`Marketstack API error: ${responseData.error.message || JSON.stringify(responseData.error)}`);
|
|
49
|
+
}
|
|
50
|
+
// For single ticker endpoint, response is direct object (not wrapped in data field)
|
|
51
|
+
if (!responseData || !responseData.close) {
|
|
52
|
+
throw new Error(`No data found for ticker ${request.ticker}`);
|
|
53
|
+
}
|
|
54
|
+
return this.mapToStockPrice(responseData);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
this.logger.error(`Failed to fetch price for ${request.ticker}:`, error);
|
|
58
|
+
throw new Error(`Marketstack: Failed to fetch price for ${request.ticker}: ${error.message}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Fetch latest EOD prices for multiple tickers
|
|
63
|
+
*/
|
|
64
|
+
async fetchPrices(request) {
|
|
65
|
+
try {
|
|
66
|
+
const symbols = request.tickers.join(',');
|
|
67
|
+
const url = `${this.baseUrl}/eod/latest?access_key=${this.apiKey}&symbols=${symbols}`;
|
|
68
|
+
const response = await plugins.smartrequest.SmartRequest.create()
|
|
69
|
+
.url(url)
|
|
70
|
+
.timeout(this.config?.timeout || 15000)
|
|
71
|
+
.get();
|
|
72
|
+
const responseData = await response.json();
|
|
73
|
+
// Check for API errors
|
|
74
|
+
if (responseData.error) {
|
|
75
|
+
throw new Error(`Marketstack API error: ${responseData.error.message || JSON.stringify(responseData.error)}`);
|
|
76
|
+
}
|
|
77
|
+
if (!responseData?.data || !Array.isArray(responseData.data)) {
|
|
78
|
+
throw new Error('Invalid response format from Marketstack API');
|
|
79
|
+
}
|
|
80
|
+
const prices = [];
|
|
81
|
+
for (const data of responseData.data) {
|
|
82
|
+
try {
|
|
83
|
+
prices.push(this.mapToStockPrice(data));
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
this.logger.warn(`Failed to parse data for ${data.symbol}:`, error);
|
|
87
|
+
// Continue processing other tickers
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (prices.length === 0) {
|
|
91
|
+
throw new Error('No valid price data received from batch request');
|
|
92
|
+
}
|
|
93
|
+
return prices;
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
this.logger.error(`Failed to fetch batch prices:`, error);
|
|
97
|
+
throw new Error(`Marketstack: Failed to fetch batch prices: ${error.message}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Check if the Marketstack API is available and accessible
|
|
102
|
+
*/
|
|
103
|
+
async isAvailable() {
|
|
104
|
+
try {
|
|
105
|
+
// Test with a well-known ticker
|
|
106
|
+
const url = `${this.baseUrl}/tickers/AAPL/eod/latest?access_key=${this.apiKey}`;
|
|
107
|
+
const response = await plugins.smartrequest.SmartRequest.create()
|
|
108
|
+
.url(url)
|
|
109
|
+
.timeout(5000)
|
|
110
|
+
.get();
|
|
111
|
+
const responseData = await response.json();
|
|
112
|
+
// Check if we got valid data (not an error)
|
|
113
|
+
// Single ticker endpoint returns direct object, not wrapped in data field
|
|
114
|
+
return !responseData.error && responseData.close !== undefined;
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
this.logger.warn('Marketstack provider is not available:', error);
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Check if a market is supported
|
|
123
|
+
* Marketstack supports 72+ exchanges worldwide
|
|
124
|
+
*/
|
|
125
|
+
supportsMarket(market) {
|
|
126
|
+
// Marketstack has broad international coverage including:
|
|
127
|
+
// US, UK, DE, FR, JP, CN, HK, AU, CA, IN, etc.
|
|
128
|
+
const supportedMarkets = [
|
|
129
|
+
'US', 'UK', 'GB', 'DE', 'FR', 'JP', 'CN', 'HK', 'AU', 'CA',
|
|
130
|
+
'IN', 'BR', 'MX', 'IT', 'ES', 'NL', 'SE', 'CH', 'NO', 'DK'
|
|
131
|
+
];
|
|
132
|
+
return supportedMarkets.includes(market.toUpperCase());
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Check if a ticker format is supported
|
|
136
|
+
*/
|
|
137
|
+
supportsTicker(ticker) {
|
|
138
|
+
// Basic validation - Marketstack supports most standard ticker formats
|
|
139
|
+
return /^[A-Z0-9\.\-]{1,10}$/.test(ticker.toUpperCase());
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Map Marketstack API response to IStockPrice interface
|
|
143
|
+
*/
|
|
144
|
+
mapToStockPrice(data) {
|
|
145
|
+
if (!data.close) {
|
|
146
|
+
throw new Error('Missing required price data');
|
|
147
|
+
}
|
|
148
|
+
// Calculate change and change percent
|
|
149
|
+
// EOD data: previous close is typically open price of the same day
|
|
150
|
+
// For better accuracy, we'd need previous day's close, but that requires another API call
|
|
151
|
+
const currentPrice = data.close;
|
|
152
|
+
const previousClose = data.open;
|
|
153
|
+
const change = currentPrice - previousClose;
|
|
154
|
+
const changePercent = previousClose !== 0 ? (change / previousClose) * 100 : 0;
|
|
155
|
+
// Parse timestamp
|
|
156
|
+
const timestamp = data.date ? new Date(data.date) : new Date();
|
|
157
|
+
const stockPrice = {
|
|
158
|
+
ticker: data.symbol.toUpperCase(),
|
|
159
|
+
price: currentPrice,
|
|
160
|
+
currency: data.price_currency || 'USD',
|
|
161
|
+
change: change,
|
|
162
|
+
changePercent: changePercent,
|
|
163
|
+
previousClose: previousClose,
|
|
164
|
+
timestamp: timestamp,
|
|
165
|
+
provider: this.name,
|
|
166
|
+
marketState: 'CLOSED', // EOD data is always for closed markets
|
|
167
|
+
exchange: data.exchange,
|
|
168
|
+
exchangeName: data.exchange_code || data.name
|
|
169
|
+
};
|
|
170
|
+
return stockPrice;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvdmlkZXIubWFya2V0c3RhY2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9zdG9ja3MvcHJvdmlkZXJzL3Byb3ZpZGVyLm1hcmtldHN0YWNrLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFJNUM7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBQ0gsTUFBTSxPQUFPLG1CQUFtQjtJQWE5QixZQUFZLE1BQWMsRUFBVSxNQUF3QjtRQUF4QixXQUFNLEdBQU4sTUFBTSxDQUFrQjtRQVpyRCxTQUFJLEdBQUcsYUFBYSxDQUFDO1FBQ3JCLGFBQVEsR0FBRyxFQUFFLENBQUMsQ0FBQyw4REFBOEQ7UUFDcEUsaUJBQVksR0FBRyxJQUFJLENBQUM7UUFDcEIsY0FBUyxHQUFHO1lBQzFCLGlCQUFpQixFQUFFLFNBQVMsRUFBRSxnQ0FBZ0M7WUFDOUQsY0FBYyxFQUFFLFNBQVMsQ0FBQyxpQkFBaUI7U0FDNUMsQ0FBQztRQUVNLFdBQU0sR0FBRyxPQUFPLENBQUM7UUFDakIsWUFBTyxHQUFHLGdDQUFnQyxDQUFDO1FBSWpELElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNaLE1BQU0sSUFBSSxLQUFLLENBQUMsOENBQThDLENBQUMsQ0FBQztRQUNsRSxDQUFDO1FBRUQsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7SUFDdkIsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLFVBQVUsQ0FBQyxPQUEyQjtRQUNqRCxJQUFJLENBQUM7WUFDSCxNQUFNLEdBQUcsR0FBRyxHQUFHLElBQUksQ0FBQyxPQUFPLFlBQVksT0FBTyxDQUFDLE1BQU0sMEJBQTBCLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUU3RixNQUFNLFFBQVEsR0FBRyxNQUFNLE9BQU8sQ0FBQyxZQUFZLENBQUMsWUFBWSxDQUFDLE1BQU0sRUFBRTtpQkFDOUQsR0FBRyxDQUFDLEdBQUcsQ0FB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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fin.cx/opendata",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A comprehensive TypeScript library for accessing business data and real-time financial information. Features include German company data management with MongoDB integration, JSONL bulk processing, automated Handelsregister interactions, and real-time stock market data from multiple providers.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"@git.zone/tsbuild": "^2.6.8",
|
|
13
13
|
"@git.zone/tsbundle": "^2.5.1",
|
|
14
14
|
"@git.zone/tsrun": "^1.3.3",
|
|
15
|
-
"@git.zone/tstest": "^2.
|
|
15
|
+
"@git.zone/tstest": "^2.4.2",
|
|
16
16
|
"@types/node": "^22.14.0"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
package/readme.md
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# @fin.cx/opendata
|
|
2
|
+
|
|
2
3
|
🚀 **Real-time financial data and German business intelligence in one powerful TypeScript library**
|
|
3
4
|
|
|
4
5
|
Access live stock prices, cryptocurrencies, forex, commodities AND comprehensive German company data - all through a single, unified API.
|
|
@@ -8,34 +9,42 @@ Access live stock prices, cryptocurrencies, forex, commodities AND comprehensive
|
|
|
8
9
|
```bash
|
|
9
10
|
npm install @fin.cx/opendata
|
|
10
11
|
# or
|
|
11
|
-
|
|
12
|
+
pnpm add @fin.cx/opendata
|
|
12
13
|
```
|
|
13
14
|
|
|
14
15
|
## Quick Start
|
|
15
16
|
|
|
16
|
-
### 📈
|
|
17
|
+
### 📈 Stock Market Data
|
|
17
18
|
|
|
18
|
-
Get
|
|
19
|
+
Get market data with EOD (End-of-Day) pricing:
|
|
19
20
|
|
|
20
21
|
```typescript
|
|
21
|
-
import { StockPriceService,
|
|
22
|
+
import { StockPriceService, MarketstackProvider } from '@fin.cx/opendata';
|
|
23
|
+
|
|
24
|
+
// Initialize the service with caching
|
|
25
|
+
const stockService = new StockPriceService({
|
|
26
|
+
ttl: 60000, // Cache for 1 minute
|
|
27
|
+
maxEntries: 1000 // Max cached symbols
|
|
28
|
+
});
|
|
22
29
|
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
30
|
+
// Register Marketstack provider with API key
|
|
31
|
+
stockService.register(new MarketstackProvider('YOUR_API_KEY'), {
|
|
32
|
+
priority: 100,
|
|
33
|
+
retryAttempts: 3
|
|
34
|
+
});
|
|
26
35
|
|
|
27
36
|
// Get single stock price
|
|
28
37
|
const apple = await stockService.getPrice({ ticker: 'AAPL' });
|
|
29
38
|
console.log(`Apple: $${apple.price} (${apple.changePercent.toFixed(2)}%)`);
|
|
30
39
|
|
|
31
|
-
// Get multiple prices at once
|
|
32
|
-
const prices = await stockService.getPrices({
|
|
33
|
-
tickers: ['AAPL', 'MSFT', 'GOOGL', '
|
|
40
|
+
// Get multiple prices at once (batch fetching)
|
|
41
|
+
const prices = await stockService.getPrices({
|
|
42
|
+
tickers: ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'TSLA']
|
|
34
43
|
});
|
|
35
44
|
|
|
36
|
-
//
|
|
37
|
-
const
|
|
38
|
-
tickers: ['
|
|
45
|
+
// 125,000+ tickers across 72+ exchanges worldwide
|
|
46
|
+
const internationalStocks = await stockService.getPrices({
|
|
47
|
+
tickers: ['AAPL', 'VOD.LON', 'SAP.DEX', 'TM', 'BABA']
|
|
39
48
|
});
|
|
40
49
|
```
|
|
41
50
|
|
|
@@ -73,11 +82,12 @@ await openData.buildInitialDb();
|
|
|
73
82
|
|
|
74
83
|
### 🎯 Stock Market Module
|
|
75
84
|
|
|
76
|
-
- **
|
|
85
|
+
- **Marketstack API** - End-of-Day (EOD) data for 125,000+ tickers across 72+ exchanges
|
|
86
|
+
- **Stock prices** for stocks, ETFs, indices, and more
|
|
77
87
|
- **Batch operations** - fetch 100+ symbols in one request
|
|
78
88
|
- **Smart caching** - configurable TTL, automatic invalidation
|
|
79
|
-
- **
|
|
80
|
-
- **
|
|
89
|
+
- **Extensible provider system** - easily add new data sources
|
|
90
|
+
- **Retry logic** - configurable retry attempts and delays
|
|
81
91
|
- **Type-safe** - full TypeScript support with detailed interfaces
|
|
82
92
|
|
|
83
93
|
### 🇩🇪 German Business Intelligence
|
|
@@ -92,29 +102,24 @@ await openData.buildInitialDb();
|
|
|
92
102
|
|
|
93
103
|
### Market Dashboard
|
|
94
104
|
|
|
95
|
-
Create
|
|
105
|
+
Create an EOD market overview:
|
|
96
106
|
|
|
97
107
|
```typescript
|
|
98
108
|
const indicators = [
|
|
99
109
|
// Indices
|
|
100
110
|
{ ticker: '^GSPC', name: 'S&P 500' },
|
|
101
|
-
{ ticker: '^
|
|
102
|
-
|
|
111
|
+
{ ticker: '^DJI', name: 'DOW Jones' },
|
|
112
|
+
|
|
103
113
|
// Tech Giants
|
|
104
114
|
{ ticker: 'AAPL', name: 'Apple' },
|
|
105
115
|
{ ticker: 'MSFT', name: 'Microsoft' },
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
{ ticker: '
|
|
109
|
-
{ ticker: 'ETH-USD', name: 'Ethereum' },
|
|
110
|
-
|
|
111
|
-
// Commodities
|
|
112
|
-
{ ticker: 'GC=F', name: 'Gold' },
|
|
113
|
-
{ ticker: 'CL=F', name: 'Oil' }
|
|
116
|
+
{ ticker: 'GOOGL', name: 'Alphabet' },
|
|
117
|
+
{ ticker: 'AMZN', name: 'Amazon' },
|
|
118
|
+
{ ticker: 'TSLA', name: 'Tesla' }
|
|
114
119
|
];
|
|
115
120
|
|
|
116
|
-
const prices = await stockService.getPrices({
|
|
117
|
-
tickers: indicators.map(i => i.ticker)
|
|
121
|
+
const prices = await stockService.getPrices({
|
|
122
|
+
tickers: indicators.map(i => i.ticker)
|
|
118
123
|
});
|
|
119
124
|
|
|
120
125
|
// Display with color-coded changes
|
|
@@ -122,7 +127,7 @@ prices.forEach(price => {
|
|
|
122
127
|
const indicator = indicators.find(i => i.ticker === price.ticker);
|
|
123
128
|
const arrow = price.change >= 0 ? '↑' : '↓';
|
|
124
129
|
const color = price.change >= 0 ? '\x1b[32m' : '\x1b[31m';
|
|
125
|
-
|
|
130
|
+
|
|
126
131
|
console.log(
|
|
127
132
|
`${indicator.name.padEnd(15)} ${price.price.toFixed(2).padStart(10)} ` +
|
|
128
133
|
`${color}${arrow} ${price.changePercent.toFixed(2)}%\x1b[0m`
|
|
@@ -130,6 +135,25 @@ prices.forEach(price => {
|
|
|
130
135
|
});
|
|
131
136
|
```
|
|
132
137
|
|
|
138
|
+
### Provider Health and Statistics
|
|
139
|
+
|
|
140
|
+
Monitor your provider health and track usage:
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
// Check provider health
|
|
144
|
+
const health = await stockService.checkProvidersHealth();
|
|
145
|
+
console.log(`Marketstack: ${health.get('Marketstack') ? '✅' : '❌'}`);
|
|
146
|
+
|
|
147
|
+
// Get provider statistics
|
|
148
|
+
const stats = stockService.getProviderStats();
|
|
149
|
+
const marketstackStats = stats.get('Marketstack');
|
|
150
|
+
console.log('Marketstack Stats:', {
|
|
151
|
+
successCount: marketstackStats.successCount,
|
|
152
|
+
errorCount: marketstackStats.errorCount,
|
|
153
|
+
lastError: marketstackStats.lastError
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
133
157
|
### Handelsregister Integration
|
|
134
158
|
|
|
135
159
|
Automate German company data retrieval:
|
|
@@ -169,15 +193,15 @@ for (const company of publicCompanies) {
|
|
|
169
193
|
try {
|
|
170
194
|
// Map company to ticker (custom logic needed)
|
|
171
195
|
const ticker = mapCompanyToTicker(company.data.name);
|
|
172
|
-
|
|
196
|
+
|
|
173
197
|
if (ticker) {
|
|
174
198
|
const stock = await stockService.getPrice({ ticker });
|
|
175
|
-
|
|
199
|
+
|
|
176
200
|
// Add financial metrics
|
|
177
201
|
company.data.stockPrice = stock.price;
|
|
178
202
|
company.data.marketCap = stock.price * getSharesOutstanding(ticker);
|
|
179
203
|
company.data.priceChange = stock.changePercent;
|
|
180
|
-
|
|
204
|
+
|
|
181
205
|
await company.save();
|
|
182
206
|
}
|
|
183
207
|
} catch (error) {
|
|
@@ -196,8 +220,8 @@ const stockService = new StockPriceService({
|
|
|
196
220
|
maxEntries: 1000 // Max cached symbols
|
|
197
221
|
});
|
|
198
222
|
|
|
199
|
-
//
|
|
200
|
-
stockService.register(new
|
|
223
|
+
// Marketstack - EOD data, requires API key
|
|
224
|
+
stockService.register(new MarketstackProvider('YOUR_API_KEY'), {
|
|
201
225
|
enabled: true,
|
|
202
226
|
priority: 100,
|
|
203
227
|
timeout: 10000,
|
|
@@ -208,7 +232,7 @@ stockService.register(new YahooFinanceProvider(), {
|
|
|
208
232
|
|
|
209
233
|
### MongoDB Setup
|
|
210
234
|
|
|
211
|
-
Set environment variables:
|
|
235
|
+
Set environment variables for German business data:
|
|
212
236
|
|
|
213
237
|
```env
|
|
214
238
|
MONGODB_URL=mongodb://localhost:27017
|
|
@@ -217,6 +241,14 @@ MONGODB_USER=myuser
|
|
|
217
241
|
MONGODB_PASS=mypass
|
|
218
242
|
```
|
|
219
243
|
|
|
244
|
+
### Marketstack API Key
|
|
245
|
+
|
|
246
|
+
Get your free API key at [marketstack.com](https://marketstack.com) and set it in your environment:
|
|
247
|
+
|
|
248
|
+
```env
|
|
249
|
+
MARKETSTACK_COM_TOKEN=your_api_key_here
|
|
250
|
+
```
|
|
251
|
+
|
|
220
252
|
## API Reference
|
|
221
253
|
|
|
222
254
|
### Stock Types
|
|
@@ -240,10 +272,21 @@ interface IStockPrice {
|
|
|
240
272
|
### Key Methods
|
|
241
273
|
|
|
242
274
|
**StockPriceService**
|
|
243
|
-
- `getPrice(request)` - Single stock price
|
|
244
|
-
- `getPrices(request)` - Batch prices
|
|
245
|
-
- `register(provider)` - Add data provider
|
|
275
|
+
- `getPrice(request)` - Single stock price with automatic provider selection
|
|
276
|
+
- `getPrices(request)` - Batch prices (100+ symbols in one request)
|
|
277
|
+
- `register(provider, config)` - Add data provider with priority and retry config
|
|
278
|
+
- `checkProvidersHealth()` - Test all providers and return health status
|
|
279
|
+
- `getProviderStats()` - Get success/error statistics for each provider
|
|
246
280
|
- `clearCache()` - Clear price cache
|
|
281
|
+
- `setCacheTTL(ttl)` - Update cache TTL dynamically
|
|
282
|
+
|
|
283
|
+
**MarketstackProvider**
|
|
284
|
+
- ✅ End-of-Day (EOD) data
|
|
285
|
+
- ✅ 125,000+ tickers across 72+ exchanges worldwide
|
|
286
|
+
- ✅ Batch fetching support (multiple symbols in one request)
|
|
287
|
+
- ✅ Comprehensive data: open, high, low, close, volume, splits, dividends
|
|
288
|
+
- ⚠️ Requires API key (free tier: 100 requests/month)
|
|
289
|
+
- ⚠️ EOD data only (not real-time)
|
|
247
290
|
|
|
248
291
|
**OpenData**
|
|
249
292
|
- `start()` - Initialize MongoDB connection
|
|
@@ -251,31 +294,48 @@ interface IStockPrice {
|
|
|
251
294
|
- `CBusinessRecord` - Business record class
|
|
252
295
|
- `handelsregister` - Registry automation
|
|
253
296
|
|
|
254
|
-
##
|
|
255
|
-
|
|
256
|
-
- **Batch fetching**: Get 100+ prices in <500ms
|
|
257
|
-
- **Caching**: Instant repeated queries
|
|
258
|
-
- **Concurrent processing**: Handle 1000+ records/second
|
|
259
|
-
- **Streaming**: Process GB-sized datasets without memory issues
|
|
260
|
-
|
|
261
|
-
## Extensibility
|
|
297
|
+
## Provider Architecture
|
|
262
298
|
|
|
263
|
-
The provider
|
|
299
|
+
The library uses a flexible provider system that makes it easy to add new data sources:
|
|
264
300
|
|
|
265
301
|
```typescript
|
|
266
302
|
class MyCustomProvider implements IStockProvider {
|
|
267
303
|
name = 'My Provider';
|
|
268
|
-
|
|
304
|
+
priority = 50;
|
|
305
|
+
requiresAuth = true;
|
|
306
|
+
|
|
269
307
|
async fetchPrice(request: IStockQuoteRequest): Promise<IStockPrice> {
|
|
270
308
|
// Your implementation
|
|
271
309
|
}
|
|
272
|
-
|
|
273
|
-
|
|
310
|
+
|
|
311
|
+
async fetchPrices(request: IStockBatchQuoteRequest): Promise<IStockPrice[]> {
|
|
312
|
+
// Batch implementation
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async isAvailable(): Promise<boolean> {
|
|
316
|
+
// Health check
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
supportsMarket(market: string): boolean {
|
|
320
|
+
// Market validation
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
supportsTicker(ticker: string): boolean {
|
|
324
|
+
// Ticker validation
|
|
325
|
+
}
|
|
274
326
|
}
|
|
275
327
|
|
|
276
328
|
stockService.register(new MyCustomProvider());
|
|
277
329
|
```
|
|
278
330
|
|
|
331
|
+
## Performance
|
|
332
|
+
|
|
333
|
+
- **Batch fetching**: Get 100+ EOD prices in one API request
|
|
334
|
+
- **Smart caching**: Instant repeated queries with configurable TTL
|
|
335
|
+
- **Rate limit aware**: Automatic retry logic for API limits
|
|
336
|
+
- **Concurrent processing**: Handle 1000+ business records/second
|
|
337
|
+
- **Streaming**: Process GB-sized datasets without memory issues
|
|
338
|
+
|
|
279
339
|
## Testing
|
|
280
340
|
|
|
281
341
|
Run the comprehensive test suite:
|
|
@@ -284,19 +344,21 @@ Run the comprehensive test suite:
|
|
|
284
344
|
npm test
|
|
285
345
|
```
|
|
286
346
|
|
|
287
|
-
|
|
347
|
+
Test stock provider:
|
|
288
348
|
|
|
289
349
|
```bash
|
|
290
|
-
|
|
350
|
+
npx tstest test/test.marketstack.node.ts --verbose
|
|
291
351
|
```
|
|
292
352
|
|
|
293
|
-
|
|
353
|
+
Test German business data:
|
|
294
354
|
|
|
295
|
-
|
|
355
|
+
```bash
|
|
356
|
+
npx tstest test/test.handelsregister.ts --verbose
|
|
357
|
+
```
|
|
296
358
|
|
|
297
359
|
## License and Legal Information
|
|
298
360
|
|
|
299
|
-
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
|
361
|
+
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
|
300
362
|
|
|
301
363
|
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
|
302
364
|
|
|
@@ -306,9 +368,9 @@ This project is owned and maintained by Task Venture Capital GmbH. The names and
|
|
|
306
368
|
|
|
307
369
|
### Company Information
|
|
308
370
|
|
|
309
|
-
Task Venture Capital GmbH
|
|
371
|
+
Task Venture Capital GmbH
|
|
310
372
|
Registered at District court Bremen HRB 35230 HB, Germany
|
|
311
373
|
|
|
312
374
|
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
|
|
313
375
|
|
|
314
|
-
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
|
376
|
+
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@fin.cx/opendata',
|
|
6
|
-
version: '1.
|
|
6
|
+
version: '1.7.0',
|
|
7
7
|
description: 'A comprehensive TypeScript library for accessing business data and real-time financial information. Features include German company data management with MongoDB integration, JSONL bulk processing, automated Handelsregister interactions, and real-time stock market data from multiple providers.'
|
|
8
8
|
}
|
package/ts/stocks/index.ts
CHANGED
|
@@ -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
|
+
}
|