@fin.cx/opendata 3.5.1 โ 3.6.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/{npmextra.json โ .smartconfig.json} +12 -6
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.businessrecord.js +86 -43
- package/dist_ts/classes.handelsregister.d.ts +2 -0
- package/dist_ts/classes.handelsregister.js +126 -107
- package/dist_ts/classes.jsonldata.js +9 -6
- package/dist_ts/classes.main.opendata.d.ts +2 -1
- package/dist_ts/classes.main.opendata.js +59 -26
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +2 -1
- package/dist_ts/laws/classes.lawrecord.d.ts +24 -0
- package/dist_ts/laws/classes.lawrecord.js +181 -0
- package/dist_ts/laws/classes.lawservice.d.ts +52 -0
- package/dist_ts/laws/classes.lawservice.js +740 -0
- package/dist_ts/laws/index.d.ts +3 -0
- package/dist_ts/laws/index.js +4 -0
- package/dist_ts/laws/interfaces.law.d.ts +35 -0
- package/dist_ts/laws/interfaces.law.js +2 -0
- package/dist_ts/plugins.d.ts +8 -2
- package/dist_ts/plugins.js +14 -3
- package/dist_ts/stocks/classes.baseproviderservice.js +8 -8
- package/dist_ts/stocks/classes.fundamentalsservice.js +9 -9
- package/dist_ts/stocks/classes.stockdataservice.js +23 -23
- package/dist_ts/stocks/classes.stockservice.js +9 -9
- package/dist_ts/stocks/providers/provider.coingecko.d.ts +1 -1
- package/dist_ts/stocks/providers/provider.coingecko.js +45 -39
- package/dist_ts/stocks/providers/provider.marketstack.d.ts +3 -3
- package/dist_ts/stocks/providers/provider.marketstack.js +20 -16
- package/dist_ts/stocks/providers/provider.secedgar.d.ts +1 -1
- package/dist_ts/stocks/providers/provider.secedgar.js +22 -22
- package/dist_ts/stocks/providers/provider.yahoo.d.ts +2 -2
- package/dist_ts/stocks/providers/provider.yahoo.js +14 -13
- package/package.json +22 -19
- package/readme.md +280 -662
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.businessrecord.ts +1 -1
- package/ts/classes.handelsregister.ts +45 -20
- package/ts/classes.jsonldata.ts +7 -6
- package/ts/classes.main.opendata.ts +63 -30
- package/ts/index.ts +1 -0
- package/ts/laws/classes.lawrecord.ts +81 -0
- package/ts/laws/classes.lawservice.ts +1010 -0
- package/ts/laws/index.ts +3 -0
- package/ts/laws/interfaces.law.ts +48 -0
- package/ts/plugins.ts +19 -0
- package/ts/stocks/classes.fundamentalsservice.ts +1 -1
- package/ts/stocks/classes.stockdataservice.ts +17 -7
- package/ts/stocks/classes.stockservice.ts +2 -2
- package/ts/stocks/providers/provider.marketstack.ts +9 -7
- package/ts/stocks/providers/provider.secedgar.ts +2 -2
- package/ts/stocks/providers/provider.yahoo.ts +3 -3
- package/dist_ts/classes.germanbusinessdata.d.ts +0 -9
- package/dist_ts/classes.germanbusinessdata.js +0 -78
- package/readme.plan.md +0 -193
package/readme.md
CHANGED
|
@@ -1,787 +1,405 @@
|
|
|
1
1
|
# @fin.cx/opendata
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`@fin.cx/opendata` is a TypeScript toolbox for serious data plumbing: market data, SEC fundamentals, German company registry workflows, and locally synced law corpora. It is built for programmers who want scriptable APIs, local persistence, and real workflows instead of a thin wrapper around one remote endpoint.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
It currently gives you four practical lanes in one package:
|
|
6
|
+
|
|
7
|
+
- `StockPriceService` for stock and crypto prices with caching, retries, failover, historical ranges, and intraday support
|
|
8
|
+
- `FundamentalsService` and `StockDataService` for SEC fundamentals and combined price + fundamentals payloads
|
|
9
|
+
- `OpenData` for German company ingestion plus browser-driven Handelsregister lookup and document download
|
|
10
|
+
- `LawService` for syncing and searching German, EU, and US law texts in a local embedded database
|
|
11
|
+
|
|
12
|
+
## Issue Reporting and Security
|
|
13
|
+
|
|
14
|
+
For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
|
|
6
15
|
|
|
7
16
|
## Installation
|
|
8
17
|
|
|
9
18
|
```bash
|
|
10
|
-
npm install @fin.cx/opendata
|
|
11
|
-
# or
|
|
12
19
|
pnpm add @fin.cx/opendata
|
|
13
20
|
```
|
|
14
21
|
|
|
15
|
-
##
|
|
22
|
+
## What You Get
|
|
16
23
|
|
|
17
|
-
###
|
|
24
|
+
### Market Data
|
|
18
25
|
|
|
19
|
-
|
|
26
|
+
- `StockPriceService`
|
|
27
|
+
Fetches current, batch, historical, and intraday price data.
|
|
28
|
+
- `FundamentalsService`
|
|
29
|
+
Fetches SEC EDGAR fundamentals for US-listed companies.
|
|
30
|
+
- `StockDataService`
|
|
31
|
+
Combines price + fundamentals and enriches fundamentals with metrics like `marketCap`, `priceToEarnings`, and `priceToBook`.
|
|
32
|
+
- `MarketstackProvider`
|
|
33
|
+
Authenticated stock market provider with current, batch, historical, and intraday support.
|
|
34
|
+
- `CoinGeckoProvider`
|
|
35
|
+
Crypto market provider behind the same stock-price interface.
|
|
36
|
+
- `SecEdgarProvider`
|
|
37
|
+
Fundamentals provider backed by SEC EDGAR company facts.
|
|
20
38
|
|
|
21
|
-
|
|
22
|
-
import { StockDataService, SecEdgarProvider } from '@fin.cx/opendata';
|
|
39
|
+
### German Company Data
|
|
23
40
|
|
|
24
|
-
|
|
25
|
-
|
|
41
|
+
- `OpenData`
|
|
42
|
+
Starts a local embedded database, imports the public German company JSONL dump, and exposes a `handelsregister` helper for company lookup and file download.
|
|
26
43
|
|
|
27
|
-
|
|
28
|
-
stockData.registerFundamentalsProvider(new SecEdgarProvider({
|
|
29
|
-
userAgent: 'YourCompany youremail@example.com'
|
|
30
|
-
}));
|
|
44
|
+
### Laws
|
|
31
45
|
|
|
32
|
-
|
|
33
|
-
|
|
46
|
+
- `LawService`
|
|
47
|
+
Syncs laws into a local embedded database and lets you search them later.
|
|
48
|
+
- `LawRecord`
|
|
49
|
+
The stored law document model returned by sync and search operations.
|
|
34
50
|
|
|
35
|
-
|
|
36
|
-
company: apple.fundamentals.companyName, // "Apple Inc."
|
|
37
|
-
price: apple.price.price, // $270.37
|
|
38
|
-
marketCap: apple.fundamentals.marketCap, // $4.13T (auto-calculated!)
|
|
39
|
-
peRatio: apple.fundamentals.priceToEarnings, // 28.42 (auto-calculated!)
|
|
40
|
-
pbRatio: apple.fundamentals.priceToBook, // 45.12 (auto-calculated!)
|
|
41
|
-
eps: apple.fundamentals.earningsPerShareDiluted, // $6.13
|
|
42
|
-
revenue: apple.fundamentals.revenue // $385.6B
|
|
43
|
-
});
|
|
51
|
+
## Before You Start
|
|
44
52
|
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
- No stock or fundamentals providers are pre-registered. You must register them before calling the services.
|
|
54
|
+
- `MarketstackProvider` requires an API key.
|
|
55
|
+
- `SecEdgarProvider` requires a valid `User-Agent` string in the format `Company Name email@example.com`.
|
|
56
|
+
- `CoinGeckoProvider` works without an API key, but a key gives you better rate limits.
|
|
57
|
+
- `OpenData` requires `nogitDir`, `downloadDir`, and `germanBusinessDataDir`.
|
|
58
|
+
- `OpenData.start()` is required before using `OpenData.handelsregister` or `buildInitialDb()`.
|
|
59
|
+
- `LawService` stores data locally. It auto-starts on demand, but explicit `start()` and `stop()` keep lifecycle handling cleaner.
|
|
60
|
+
- EU law syncing and Handelsregister access use a headless browser under the hood.
|
|
61
|
+
- `StockDataService` defaults to a 24 hour price cache. If you want fresher quote caching, pass `cache.priceTTL` explicitly or use `StockPriceService` directly.
|
|
47
62
|
|
|
48
|
-
|
|
49
|
-
console.log(`${stock.ticker}: $${stock.price.price.toFixed(2)}, ` +
|
|
50
|
-
`P/E ${stock.fundamentals?.priceToEarnings?.toFixed(2)}`);
|
|
51
|
-
});
|
|
52
|
-
// Output:
|
|
53
|
-
// AAPL: $270.37, P/E 28.42
|
|
54
|
-
// MSFT: $425.50, P/E 34.21
|
|
55
|
-
// GOOGL: $142.15, P/E 25.63
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
**Why use the unified API?**
|
|
59
|
-
- โ
**Single service** for both prices and fundamentals
|
|
60
|
-
- โ
**Automatic enrichment** - Market cap, P/E, P/B calculated automatically
|
|
61
|
-
- โ
**One method call** - No manual `enrichWithPrice()` calls
|
|
62
|
-
- โ
**Simplified code** - Less boilerplate, more readable
|
|
63
|
-
- โ
**Type-safe** - Full TypeScript support
|
|
63
|
+
## Quick Start
|
|
64
64
|
|
|
65
|
-
###
|
|
65
|
+
### Combined Stock Data
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
Use `StockDataService` when you want one call that returns both the latest price and SEC fundamentals.
|
|
68
68
|
|
|
69
|
-
```
|
|
70
|
-
import {
|
|
69
|
+
```ts
|
|
70
|
+
import {
|
|
71
|
+
MarketstackProvider,
|
|
72
|
+
SecEdgarProvider,
|
|
73
|
+
StockDataService,
|
|
74
|
+
} from '@fin.cx/opendata';
|
|
71
75
|
|
|
72
|
-
const
|
|
73
|
-
|
|
76
|
+
const stocks = new StockDataService({
|
|
77
|
+
cache: {
|
|
78
|
+
priceTTL: 60_000,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
74
81
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
82
|
+
stocks.registerPriceProvider(
|
|
83
|
+
new MarketstackProvider(process.env.MARKETSTACK_API_KEY!)
|
|
84
|
+
);
|
|
78
85
|
|
|
79
|
-
|
|
80
|
-
|
|
86
|
+
stocks.registerFundamentalsProvider(
|
|
87
|
+
new SecEdgarProvider({
|
|
88
|
+
userAgent: 'Acme Inc. dev@acme.dev',
|
|
89
|
+
})
|
|
90
|
+
);
|
|
81
91
|
|
|
82
|
-
const
|
|
83
|
-
stockService.register(new MarketstackProvider('YOUR_API_KEY'));
|
|
92
|
+
const apple = await stocks.getStockData('AAPL');
|
|
84
93
|
|
|
85
|
-
|
|
86
|
-
|
|
94
|
+
console.log({
|
|
95
|
+
ticker: apple.ticker,
|
|
96
|
+
price: apple.price.price,
|
|
97
|
+
provider: apple.price.provider,
|
|
98
|
+
companyName: apple.fundamentals?.companyName,
|
|
99
|
+
marketCap: apple.fundamentals?.marketCap,
|
|
100
|
+
priceToEarnings: apple.fundamentals?.priceToEarnings,
|
|
101
|
+
fetchedAt: apple.fetchedAt,
|
|
102
|
+
});
|
|
87
103
|
```
|
|
88
104
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
Get real-time crypto prices with the CoinGecko provider:
|
|
105
|
+
You can also batch this:
|
|
92
106
|
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const stockService = new StockPriceService({ ttl: 30000 });
|
|
97
|
-
|
|
98
|
-
// Optional: Pass API key for higher rate limits
|
|
99
|
-
// const provider = new CoinGeckoProvider('your-api-key');
|
|
100
|
-
const provider = new CoinGeckoProvider();
|
|
107
|
+
```ts
|
|
108
|
+
const batch = await stocks.getBatchStockData(['AAPL', 'MSFT', 'NVDA']);
|
|
109
|
+
```
|
|
101
110
|
|
|
102
|
-
|
|
111
|
+
### Price-Only Stocks And Intraday Data
|
|
103
112
|
|
|
104
|
-
|
|
105
|
-
const btc = await stockService.getPrice({ ticker: 'BTC' });
|
|
106
|
-
console.log(`${btc.ticker}: $${btc.price.toLocaleString()}`);
|
|
107
|
-
console.log(`24h Change: ${btc.changePercent.toFixed(2)}%`);
|
|
108
|
-
console.log(`24h Volume: $${btc.volume?.toLocaleString()}`);
|
|
113
|
+
Use `StockPriceService` if you want smarter per-request caching and direct access to current, historical, and intraday request types.
|
|
109
114
|
|
|
110
|
-
|
|
111
|
-
|
|
115
|
+
```ts
|
|
116
|
+
import { MarketstackProvider, StockPriceService } from '@fin.cx/opendata';
|
|
112
117
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
tickers: ['BTC', 'ETH', 'USDT', 'BNB', 'SOL']
|
|
116
|
-
});
|
|
118
|
+
const prices = new StockPriceService();
|
|
119
|
+
prices.register(new MarketstackProvider(process.env.MARKETSTACK_API_KEY!));
|
|
117
120
|
|
|
118
|
-
|
|
119
|
-
console.log(`${crypto.ticker}: $${crypto.price.toFixed(2)} (${crypto.changePercent >= 0 ? '+' : ''}${crypto.changePercent.toFixed(2)}%)`);
|
|
120
|
-
});
|
|
121
|
+
const current = await prices.getPrice({ ticker: 'AAPL' });
|
|
121
122
|
|
|
122
|
-
|
|
123
|
-
const history = await stockService.getData({
|
|
123
|
+
const historical = await prices.getData({
|
|
124
124
|
type: 'historical',
|
|
125
|
-
ticker: '
|
|
125
|
+
ticker: 'AAPL',
|
|
126
126
|
from: new Date('2025-01-01'),
|
|
127
|
-
to: new Date('2025-01-31')
|
|
127
|
+
to: new Date('2025-01-31'),
|
|
128
|
+
sort: 'DESC',
|
|
128
129
|
});
|
|
129
130
|
|
|
130
|
-
|
|
131
|
-
const intraday = await stockService.getData({
|
|
131
|
+
const intraday = await prices.getData({
|
|
132
132
|
type: 'intraday',
|
|
133
|
-
ticker: '
|
|
133
|
+
ticker: 'AAPL',
|
|
134
134
|
interval: '1hour',
|
|
135
|
-
limit: 24
|
|
135
|
+
limit: 24,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
console.log({
|
|
139
|
+
live: current.price,
|
|
140
|
+
candles: intraday.length,
|
|
141
|
+
historicalRows: historical.length,
|
|
136
142
|
});
|
|
137
143
|
```
|
|
138
144
|
|
|
139
|
-
|
|
145
|
+
Supported stock request shapes:
|
|
140
146
|
|
|
141
|
-
|
|
147
|
+
- `{ type: 'current', ticker, exchange? }`
|
|
148
|
+
- `{ type: 'batch', tickers, exchange? }`
|
|
149
|
+
- `{ type: 'historical', ticker, from, to, exchange?, sort?, limit?, offset? }`
|
|
150
|
+
- `{ type: 'intraday', ticker, interval, exchange?, limit?, date? }`
|
|
142
151
|
|
|
143
|
-
|
|
144
|
-
import { StockDataService, SecEdgarProvider } from '@fin.cx/opendata';
|
|
152
|
+
`StockPriceService` also exposes operational helpers like `checkProvidersHealth()`, `getProviderStats()`, `clearCache()`, and `getCacheStats()`.
|
|
145
153
|
|
|
146
|
-
|
|
147
|
-
stockData.registerFundamentalsProvider(new SecEdgarProvider({
|
|
148
|
-
userAgent: 'YourCompany youremail@example.com'
|
|
149
|
-
}));
|
|
154
|
+
### Crypto Prices With The Same Interface
|
|
150
155
|
|
|
151
|
-
|
|
152
|
-
const fundamentals = await stockData.getFundamentals('AAPL');
|
|
156
|
+
`CoinGeckoProvider` plugs into `StockPriceService`, so crypto lookups use the same request model as stock lookups.
|
|
153
157
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
eps: fundamentals.earningsPerShareDiluted,
|
|
157
|
-
revenue: fundamentals.revenue,
|
|
158
|
-
sharesOutstanding: fundamentals.sharesOutstanding
|
|
159
|
-
});
|
|
158
|
+
```ts
|
|
159
|
+
import { CoinGeckoProvider, StockPriceService } from '@fin.cx/opendata';
|
|
160
160
|
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
const prices = new StockPriceService({ ttl: 30_000 });
|
|
162
|
+
prices.register(new CoinGeckoProvider(process.env.COINGECKO_API_KEY));
|
|
163
163
|
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
userAgent: 'YourCompany youremail@example.com'
|
|
167
|
-
}));
|
|
164
|
+
const btc = await prices.getPrice({ ticker: 'BTC' });
|
|
165
|
+
const top = await prices.getPrices({ tickers: ['BTC', 'ETH', 'SOL'] });
|
|
168
166
|
|
|
169
|
-
|
|
167
|
+
console.log(btc.price, top.map((item) => item.ticker));
|
|
170
168
|
```
|
|
171
169
|
|
|
172
|
-
|
|
170
|
+
You can also pass CoinGecko ids like `bitcoin` or `ethereum` instead of ticker symbols.
|
|
173
171
|
|
|
174
|
-
|
|
172
|
+
### Fundamentals Only
|
|
175
173
|
|
|
176
|
-
|
|
177
|
-
import { OpenData } from '@fin.cx/opendata';
|
|
178
|
-
import * as path from 'path';
|
|
174
|
+
Use `FundamentalsService` when you only care about SEC filing data.
|
|
179
175
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
nogitDir: path.join(process.cwd(), '.nogit'),
|
|
183
|
-
downloadDir: path.join(process.cwd(), '.nogit', 'downloads'),
|
|
184
|
-
germanBusinessDataDir: path.join(process.cwd(), '.nogit', 'germanbusinessdata')
|
|
185
|
-
});
|
|
186
|
-
await openData.start();
|
|
176
|
+
```ts
|
|
177
|
+
import { FundamentalsService, SecEdgarProvider } from '@fin.cx/opendata';
|
|
187
178
|
|
|
188
|
-
|
|
189
|
-
|
|
179
|
+
const fundamentals = new FundamentalsService();
|
|
180
|
+
fundamentals.register(
|
|
181
|
+
new SecEdgarProvider({
|
|
182
|
+
userAgent: 'Acme Inc. dev@acme.dev',
|
|
183
|
+
})
|
|
184
|
+
);
|
|
190
185
|
|
|
191
|
-
|
|
192
|
-
const details = await openData.handelsregister.getSpecificCompany({
|
|
193
|
-
court: "Munich",
|
|
194
|
-
type: "HRB",
|
|
195
|
-
number: "6684"
|
|
196
|
-
});
|
|
197
|
-
```
|
|
186
|
+
const apple = await fundamentals.getFundamentals('AAPL');
|
|
198
187
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
- **Company Names** - Automatic company name extraction (e.g., "Apple Inc (NASDAQ:AAPL)")
|
|
206
|
-
- **Historical Data** - Up to 15 years of daily EOD prices with pagination
|
|
207
|
-
- **OHLCV Data** - Open, High, Low, Close, Volume for technical analysis
|
|
208
|
-
- **Exchange Filtering** - Query specific exchanges via MIC codes (XNAS, XLON, XNYS)
|
|
209
|
-
- **Smart Caching** - Data-type aware TTL (historical cached forever, EOD 24h, live 30s)
|
|
210
|
-
- **Batch Operations** - Fetch 100+ symbols in one request
|
|
211
|
-
- **Type-Safe API** - Full TypeScript support with discriminated unions
|
|
212
|
-
- **Multi-Provider** - Automatic fallback between providers
|
|
213
|
-
|
|
214
|
-
### ๐ฐ Fundamental Data Module
|
|
215
|
-
|
|
216
|
-
- **SEC EDGAR Integration** - FREE fundamental data directly from SEC filings
|
|
217
|
-
- **Comprehensive Metrics** - EPS, Revenue, Assets, Liabilities, Cash Flow, and more
|
|
218
|
-
- **All US Public Companies** - Complete coverage of SEC-registered companies
|
|
219
|
-
- **Historical Filings** - Data back to ~2009
|
|
220
|
-
- **CIK Lookup** - Automatic ticker-to-CIK mapping with smart caching
|
|
221
|
-
- **Calculated Ratios** - Market Cap, P/E, P/B ratios when combined with prices
|
|
222
|
-
- **No API Key Required** - Direct access to SEC's public API
|
|
223
|
-
- **Rate Limit Management** - Built-in 10 req/sec rate limiting
|
|
224
|
-
|
|
225
|
-
### ๐ฉ๐ช German Business Intelligence
|
|
226
|
-
|
|
227
|
-
- **MongoDB Integration** - Scalable data storage for millions of records
|
|
228
|
-
- **Bulk JSONL Import** - Process multi-GB datasets efficiently
|
|
229
|
-
- **Handelsregister Automation** - Automated document retrieval
|
|
230
|
-
- **CRUD Operations** - Full database management with validation
|
|
231
|
-
- **Streaming Processing** - Handle large datasets without memory issues
|
|
232
|
-
|
|
233
|
-
## Advanced Examples
|
|
234
|
-
|
|
235
|
-
### Combined Market Analysis (Unified API)
|
|
236
|
-
|
|
237
|
-
Analyze multiple companies with automatic enrichment:
|
|
238
|
-
|
|
239
|
-
```typescript
|
|
240
|
-
import { StockDataService, MarketstackProvider, SecEdgarProvider } from '@fin.cx/opendata';
|
|
241
|
-
|
|
242
|
-
// Setup unified service
|
|
243
|
-
const stockData = new StockDataService();
|
|
244
|
-
stockData.registerPriceProvider(new MarketstackProvider('YOUR_API_KEY'));
|
|
245
|
-
stockData.registerFundamentalsProvider(new SecEdgarProvider({
|
|
246
|
-
userAgent: 'YourCompany youremail@example.com'
|
|
247
|
-
}));
|
|
248
|
-
|
|
249
|
-
// Analyze multiple companies with ONE call
|
|
250
|
-
const tickers = ['AAPL', 'MSFT', 'GOOGL', 'AMZN'];
|
|
251
|
-
const stocks = await stockData.getBatchStockData(tickers);
|
|
252
|
-
|
|
253
|
-
// All metrics are automatically calculated!
|
|
254
|
-
stocks.forEach(stock => {
|
|
255
|
-
if (stock.fundamentals) {
|
|
256
|
-
console.log(`\n${stock.fundamentals.companyName} (${stock.ticker})`);
|
|
257
|
-
console.log(` Price: $${stock.price.price.toFixed(2)}`);
|
|
258
|
-
console.log(` Market Cap: $${(stock.fundamentals.marketCap! / 1e9).toFixed(2)}B`);
|
|
259
|
-
console.log(` P/E Ratio: ${stock.fundamentals.priceToEarnings!.toFixed(2)}`);
|
|
260
|
-
console.log(` Revenue: $${(stock.fundamentals.revenue! / 1e9).toFixed(2)}B`);
|
|
261
|
-
console.log(` EPS: $${stock.fundamentals.earningsPerShareDiluted!.toFixed(2)}`);
|
|
262
|
-
}
|
|
188
|
+
console.log({
|
|
189
|
+
companyName: apple.companyName,
|
|
190
|
+
revenue: apple.revenue,
|
|
191
|
+
netIncome: apple.netIncome,
|
|
192
|
+
earningsPerShareDiluted: apple.earningsPerShareDiluted,
|
|
193
|
+
filingDate: apple.filingDate,
|
|
263
194
|
});
|
|
264
|
-
|
|
265
|
-
// Or analyze one-by-one with automatic enrichment
|
|
266
|
-
for (const ticker of tickers) {
|
|
267
|
-
const stock = await stockData.getStockData(ticker);
|
|
268
|
-
|
|
269
|
-
// Everything is already enriched - no manual calculations needed!
|
|
270
|
-
console.log(`${ticker}: P/E ${stock.fundamentals?.priceToEarnings?.toFixed(2)}`);
|
|
271
|
-
}
|
|
272
195
|
```
|
|
273
196
|
|
|
274
|
-
###
|
|
197
|
+
### German Company Data And Handelsregister Automation
|
|
275
198
|
|
|
276
|
-
|
|
199
|
+
`OpenData` is the local workflow layer for German company data. It creates its directories, boots an embedded SmartDB-backed database, and starts a browser automation session for Handelsregister access.
|
|
277
200
|
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
const stocks = await stockData.getBatchStockData(tickers);
|
|
201
|
+
```ts
|
|
202
|
+
import { OpenData } from '@fin.cx/opendata';
|
|
203
|
+
import * as path from 'node:path';
|
|
282
204
|
|
|
283
|
-
|
|
284
|
-
const valueStocks = stocks.filter(stock => {
|
|
285
|
-
const f = stock.fundamentals;
|
|
286
|
-
return f &&
|
|
287
|
-
f.priceToEarnings! < 30 && // P/E under 30
|
|
288
|
-
f.priceToBook! < 10 && // P/B under 10
|
|
289
|
-
f.revenue! > 100_000_000_000; // Revenue > $100B
|
|
290
|
-
});
|
|
205
|
+
const nogitDir = path.join(process.cwd(), '.nogit');
|
|
291
206
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
207
|
+
const openData = new OpenData({
|
|
208
|
+
nogitDir,
|
|
209
|
+
downloadDir: path.join(nogitDir, 'downloads'),
|
|
210
|
+
germanBusinessDataDir: path.join(nogitDir, 'germanbusinessdata'),
|
|
296
211
|
});
|
|
297
|
-
```
|
|
298
212
|
|
|
299
|
-
|
|
213
|
+
await openData.start();
|
|
300
214
|
|
|
301
|
-
|
|
215
|
+
const matches = await openData.handelsregister.searchCompany('Siemens AG', 20);
|
|
302
216
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
ticker: 'AAPL',
|
|
308
|
-
from: new Date('2024-01-01'),
|
|
309
|
-
to: new Date('2024-12-31'),
|
|
310
|
-
sort: 'DESC' // Newest first
|
|
217
|
+
const company = await openData.handelsregister.getSpecificCompany({
|
|
218
|
+
court: 'Munich',
|
|
219
|
+
type: 'HRB',
|
|
220
|
+
number: '6684',
|
|
311
221
|
});
|
|
312
222
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const low52Week = Math.min(...prices);
|
|
317
|
-
const avgPrice = prices.reduce((a, b) => a + b) / prices.length;
|
|
318
|
-
|
|
319
|
-
console.log(`52-Week Analysis for AAPL:`);
|
|
320
|
-
console.log(` High: $${high52Week.toFixed(2)}`);
|
|
321
|
-
console.log(` Low: $${low52Week.toFixed(2)}`);
|
|
322
|
-
console.log(` Average: $${avgPrice.toFixed(2)}`);
|
|
323
|
-
console.log(` Days: ${history.length}`);
|
|
324
|
-
|
|
325
|
-
// Calculate Simple Moving Average
|
|
326
|
-
const calculateSMA = (data: IStockPrice[], period: number) => {
|
|
327
|
-
const sma: number[] = [];
|
|
328
|
-
for (let i = period - 1; i < data.length; i++) {
|
|
329
|
-
const sum = data.slice(i - period + 1, i + 1)
|
|
330
|
-
.reduce((acc, p) => acc + p.price, 0);
|
|
331
|
-
sma.push(sum / period);
|
|
332
|
-
}
|
|
333
|
-
return sma;
|
|
334
|
-
};
|
|
335
|
-
|
|
336
|
-
const sma20 = calculateSMA(history, 20);
|
|
337
|
-
const sma50 = calculateSMA(history, 50);
|
|
338
|
-
|
|
339
|
-
console.log(`\nMoving Averages:`);
|
|
340
|
-
console.log(` 20-day SMA: $${sma20[0].toFixed(2)}`);
|
|
341
|
-
console.log(` 50-day SMA: $${sma50[0].toFixed(2)}`);
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
### OHLCV Technical Analysis
|
|
345
|
-
|
|
346
|
-
Use OHLCV data for technical indicators:
|
|
347
|
-
|
|
348
|
-
```typescript
|
|
349
|
-
const history = await stockService.getData({
|
|
350
|
-
type: 'historical',
|
|
351
|
-
ticker: 'TSLA',
|
|
352
|
-
from: new Date('2024-11-01'),
|
|
353
|
-
to: new Date('2024-11-30')
|
|
223
|
+
console.log({
|
|
224
|
+
searchHits: matches.length,
|
|
225
|
+
downloadedFiles: company.files.map((file) => file.path),
|
|
354
226
|
});
|
|
355
227
|
|
|
356
|
-
|
|
357
|
-
for (const day of history) {
|
|
358
|
-
const range = day.high! - day.low!;
|
|
359
|
-
const rangePercent = (range / day.low!) * 100;
|
|
360
|
-
|
|
361
|
-
console.log(`${day.timestamp.toISOString().split('T')[0]}:`);
|
|
362
|
-
console.log(` Open: $${day.open}`);
|
|
363
|
-
console.log(` High: $${day.high}`);
|
|
364
|
-
console.log(` Low: $${day.low}`);
|
|
365
|
-
console.log(` Close: $${day.price}`);
|
|
366
|
-
console.log(` Volume: ${day.volume?.toLocaleString()}`);
|
|
367
|
-
console.log(` Range: $${range.toFixed(2)} (${rangePercent.toFixed(2)}%)`);
|
|
368
|
-
}
|
|
228
|
+
await openData.stop();
|
|
369
229
|
```
|
|
370
230
|
|
|
371
|
-
|
|
231
|
+
Useful `OpenData` workflows today:
|
|
372
232
|
|
|
373
|
-
|
|
233
|
+
- `await openData.start()`
|
|
234
|
+
- `await openData.buildInitialDb()` to ingest the public German company JSONL dataset
|
|
235
|
+
- `await openData.handelsregister.searchCompany(name, limit)`
|
|
236
|
+
- `await openData.handelsregister.getSpecificCompany({ court, type, number })`
|
|
237
|
+
- `await openData.handelsregister.getSpecificCompanyByName(name)`
|
|
374
238
|
|
|
375
|
-
|
|
376
|
-
const tickers = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'TSLA', 'META', 'NVDA'];
|
|
239
|
+
The download workflow currently fetches the Handelsregister files into a temporary folder and returns the files to you as `SmartFile` objects.
|
|
377
240
|
|
|
378
|
-
|
|
379
|
-
const allFundamentals = await fundamentalsService.getBatchFundamentals(tickers);
|
|
241
|
+
### Law Syncing And Search
|
|
380
242
|
|
|
381
|
-
|
|
382
|
-
const prices = await stockService.getData({
|
|
383
|
-
type: 'batch',
|
|
384
|
-
tickers: tickers
|
|
385
|
-
});
|
|
243
|
+
`LawService` stores synced laws in a local embedded database and supports three jurisdictions:
|
|
386
244
|
|
|
387
|
-
|
|
388
|
-
|
|
245
|
+
- Germany via `gesetze-im-internet`
|
|
246
|
+
- EU via `EUR-Lex`
|
|
247
|
+
- US via GovInfo and Cornell LII
|
|
389
248
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
allFundamentals,
|
|
393
|
-
priceMap
|
|
394
|
-
);
|
|
249
|
+
```ts
|
|
250
|
+
import { LawService } from '@fin.cx/opendata';
|
|
395
251
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
);
|
|
401
|
-
|
|
402
|
-
console.log('Value Stocks:');
|
|
403
|
-
valueStocks.forEach(stock => {
|
|
404
|
-
console.log(`\n${stock.companyName} (${stock.ticker})`);
|
|
405
|
-
console.log(` P/E Ratio: ${stock.priceToEarnings!.toFixed(2)}`);
|
|
406
|
-
console.log(` P/B Ratio: ${stock.priceToBook!.toFixed(2)}`);
|
|
407
|
-
console.log(` Market Cap: $${(stock.marketCap! / 1e9).toFixed(2)}B`);
|
|
252
|
+
const laws = new LawService({
|
|
253
|
+
dbFolderPath: '.nogit/law-smartdb',
|
|
254
|
+
dbName: 'laws',
|
|
255
|
+
govInfoApiKey: process.env.GOVINFO_API_KEY,
|
|
408
256
|
});
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
### Market Dashboard
|
|
412
257
|
|
|
413
|
-
|
|
258
|
+
await laws.start();
|
|
414
259
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
{ ticker: 'MSFT', name: 'Microsoft' },
|
|
419
|
-
{ ticker: 'GOOGL', name: 'Alphabet' },
|
|
420
|
-
{ ticker: 'AMZN', name: 'Amazon' },
|
|
421
|
-
{ ticker: 'TSLA', name: 'Tesla' }
|
|
422
|
-
];
|
|
423
|
-
|
|
424
|
-
const prices = await stockService.getData({
|
|
425
|
-
type: 'batch',
|
|
426
|
-
tickers: indicators.map(i => i.ticker)
|
|
260
|
+
const germanLaw = await laws.syncLaw({
|
|
261
|
+
jurisdiction: 'de',
|
|
262
|
+
identifier: 'aeg',
|
|
427
263
|
});
|
|
428
264
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
const color = price.change >= 0 ? '\x1b[32m' : '\x1b[31m';
|
|
434
|
-
|
|
435
|
-
console.log(
|
|
436
|
-
`${price.companyName!.padEnd(25)} $${price.price.toFixed(2).padStart(8)} ` +
|
|
437
|
-
`${color}${arrow} ${price.changePercent.toFixed(2)}%\x1b[0m`
|
|
438
|
-
);
|
|
265
|
+
const euLaw = await laws.syncLaw({
|
|
266
|
+
jurisdiction: 'eu',
|
|
267
|
+
identifier: '32024R1689',
|
|
268
|
+
language: 'EN',
|
|
439
269
|
});
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
### Exchange-Specific Trading
|
|
443
|
-
|
|
444
|
-
Compare prices across different exchanges:
|
|
445
|
-
|
|
446
|
-
```typescript
|
|
447
|
-
// Vodafone trades on both London and NYSE
|
|
448
|
-
const exchanges = [
|
|
449
|
-
{ mic: 'XLON', name: 'London Stock Exchange' },
|
|
450
|
-
{ mic: 'XNYS', name: 'New York Stock Exchange' }
|
|
451
|
-
];
|
|
452
|
-
|
|
453
|
-
for (const exchange of exchanges) {
|
|
454
|
-
try {
|
|
455
|
-
const price = await stockService.getData({
|
|
456
|
-
type: 'current',
|
|
457
|
-
ticker: 'VOD',
|
|
458
|
-
exchange: exchange.mic
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
console.log(`${exchange.name}:`);
|
|
462
|
-
console.log(` Price: ${price.price} ${price.currency}`);
|
|
463
|
-
console.log(` Volume: ${price.volume?.toLocaleString()}`);
|
|
464
|
-
console.log(` Exchange: ${price.exchangeName}`);
|
|
465
|
-
} catch (error) {
|
|
466
|
-
console.log(`${exchange.name}: Not available`);
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
## Configuration
|
|
472
270
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
const stockService = new StockPriceService({
|
|
477
|
-
ttl: 60000, // Default cache TTL in ms
|
|
478
|
-
maxEntries: 10000 // Max cached entries
|
|
271
|
+
const usCode = await laws.syncLaw({
|
|
272
|
+
jurisdiction: 'us',
|
|
273
|
+
identifier: '8 U.S.C. ยง 1226',
|
|
479
274
|
});
|
|
480
275
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
timeout: 10000,
|
|
486
|
-
retryAttempts: 3,
|
|
487
|
-
retryDelay: 1000
|
|
276
|
+
const results = await laws.searchLaws({
|
|
277
|
+
jurisdiction: 'eu',
|
|
278
|
+
query: 'Artificial Intelligence Act',
|
|
279
|
+
limit: 5,
|
|
488
280
|
});
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
### Fundamentals Service Options
|
|
492
281
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
282
|
+
console.log({
|
|
283
|
+
germany: germanLaw.title,
|
|
284
|
+
eu: euLaw.title,
|
|
285
|
+
us: usCode.title,
|
|
286
|
+
hits: results.length,
|
|
497
287
|
});
|
|
498
288
|
|
|
499
|
-
|
|
500
|
-
fundamentalsService.register(new SecEdgarProvider({
|
|
501
|
-
userAgent: 'YourCompany youremail@example.com',
|
|
502
|
-
cikCacheTTL: 30 * 24 * 60 * 60 * 1000, // 30 days
|
|
503
|
-
fundamentalsCacheTTL: 90 * 24 * 60 * 60 * 1000, // 90 days
|
|
504
|
-
timeout: 30000
|
|
505
|
-
}));
|
|
289
|
+
await laws.stop();
|
|
506
290
|
```
|
|
507
291
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
All directory paths are mandatory when using German business data features:
|
|
511
|
-
|
|
512
|
-
```typescript
|
|
513
|
-
import { OpenData } from '@fin.cx/opendata';
|
|
514
|
-
import * as path from 'path';
|
|
515
|
-
|
|
516
|
-
// Development
|
|
517
|
-
const openData = new OpenData({
|
|
518
|
-
nogitDir: path.join(process.cwd(), '.nogit'),
|
|
519
|
-
downloadDir: path.join(process.cwd(), '.nogit', 'downloads'),
|
|
520
|
-
germanBusinessDataDir: path.join(process.cwd(), '.nogit', 'germanbusinessdata')
|
|
521
|
-
});
|
|
292
|
+
`LawService` also supports bulk sync:
|
|
522
293
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
294
|
+
```ts
|
|
295
|
+
await laws.syncLaws({
|
|
296
|
+
jurisdiction: 'us',
|
|
297
|
+
usCollection: 'PLAW',
|
|
298
|
+
limit: 25,
|
|
299
|
+
since: new Date('2025-01-01'),
|
|
528
300
|
});
|
|
529
301
|
```
|
|
530
302
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
cash?: number;
|
|
606
|
-
propertyPlantEquipment?: number;
|
|
607
|
-
|
|
608
|
-
// Calculated metrics (requires price)
|
|
609
|
-
marketCap?: number; // price ร sharesOutstanding
|
|
610
|
-
priceToEarnings?: number; // price / EPS
|
|
611
|
-
priceToBook?: number; // marketCap / stockholdersEquity
|
|
612
|
-
|
|
613
|
-
// Metadata
|
|
614
|
-
fiscalYear?: string;
|
|
615
|
-
fiscalQuarter?: string;
|
|
616
|
-
filingDate?: Date;
|
|
617
|
-
form?: '10-K' | '10-Q' | string;
|
|
618
|
-
}
|
|
619
|
-
```
|
|
620
|
-
|
|
621
|
-
### Key Methods
|
|
622
|
-
|
|
623
|
-
**StockPriceService**
|
|
624
|
-
- `getData(request)` - Unified method for all stock data (current, historical, batch)
|
|
625
|
-
- `getPrice(request)` - Convenience method for single current price
|
|
626
|
-
- `getPrices(request)` - Convenience method for batch current prices
|
|
627
|
-
- `register(provider, config)` - Add data provider with priority and retry config
|
|
628
|
-
- `checkProvidersHealth()` - Test all providers and return health status
|
|
629
|
-
- `getProviderStats()` - Get success/error statistics for each provider
|
|
630
|
-
- `clearCache()` - Clear price cache
|
|
631
|
-
- `setCacheTTL(ttl)` - Update cache TTL dynamically
|
|
632
|
-
|
|
633
|
-
**FundamentalsService**
|
|
634
|
-
- `getFundamentals(ticker)` - Get fundamental data for single ticker
|
|
635
|
-
- `getBatchFundamentals(tickers)` - Get fundamentals for multiple tickers
|
|
636
|
-
- `enrichWithPrice(fundamentals, price)` - Calculate market cap, P/E, P/B ratios
|
|
637
|
-
- `enrichBatchWithPrices(fundamentals, priceMap)` - Batch enrich with prices
|
|
638
|
-
- `register(provider, config)` - Add fundamentals provider
|
|
639
|
-
- `checkProvidersHealth()` - Test all providers
|
|
640
|
-
- `getProviderStats()` - Get success/error statistics
|
|
641
|
-
- `clearCache()` - Clear fundamentals cache
|
|
642
|
-
|
|
643
|
-
**SecEdgarProvider**
|
|
644
|
-
- โ
FREE - No API key required
|
|
645
|
-
- โ
All US public companies
|
|
646
|
-
- โ
Comprehensive US GAAP financial metrics
|
|
647
|
-
- โ
Historical data back to ~2009
|
|
648
|
-
- โ
Direct access to SEC filings (10-K, 10-Q)
|
|
649
|
-
- โ
Smart caching (CIK: 30 days, Fundamentals: 90 days)
|
|
650
|
-
- โ
Rate limiting (10 requests/second)
|
|
651
|
-
- โน๏ธ Requires User-Agent header in format: "Company Name Email"
|
|
652
|
-
|
|
653
|
-
**MarketstackProvider**
|
|
654
|
-
- โ
End-of-Day (EOD) stock prices
|
|
655
|
-
- โ
500,000+ tickers across 72+ exchanges worldwide
|
|
656
|
-
- โ
Historical data with pagination
|
|
657
|
-
- โ
Batch fetching support
|
|
658
|
-
- โ
OHLCV data (Open, High, Low, Close, Volume)
|
|
659
|
-
- โ
Company names included automatically
|
|
660
|
-
- โ ๏ธ Requires API key (free tier: 100 requests/month)
|
|
661
|
-
|
|
662
|
-
**CoinGeckoProvider**
|
|
663
|
-
- โ
Cryptocurrency prices (Bitcoin, Ethereum, 13M+ tokens)
|
|
664
|
-
- โ
Current, historical, and intraday data
|
|
665
|
-
- โ
24/7 market data (crypto never closes)
|
|
666
|
-
- โ
OHLCV data with market cap and volume
|
|
667
|
-
- โ
Supports both ticker symbols (BTC, ETH) and CoinGecko IDs (bitcoin, ethereum)
|
|
668
|
-
- โ
240+ networks, 1600+ exchanges
|
|
669
|
-
- โน๏ธ Optional API key (free tier: 30 requests/min, 10K/month)
|
|
670
|
-
|
|
671
|
-
**OpenData**
|
|
672
|
-
- `start()` - Initialize MongoDB connection
|
|
673
|
-
- `buildInitialDb()` - Import bulk data
|
|
674
|
-
- `CBusinessRecord` - Business record class
|
|
675
|
-
- `handelsregister` - German registry automation
|
|
676
|
-
|
|
677
|
-
## Provider Architecture
|
|
678
|
-
|
|
679
|
-
Add custom data providers easily:
|
|
680
|
-
|
|
681
|
-
```typescript
|
|
682
|
-
class MyCustomProvider implements IStockProvider {
|
|
683
|
-
name = 'My Provider';
|
|
684
|
-
priority = 50;
|
|
685
|
-
requiresAuth = true;
|
|
686
|
-
rateLimit = { requestsPerMinute: 60 };
|
|
687
|
-
|
|
688
|
-
async fetchData(request: IStockDataRequest): Promise<IStockPrice | IStockPrice[]> {
|
|
689
|
-
// Implement unified data fetching
|
|
690
|
-
switch (request.type) {
|
|
691
|
-
case 'current':
|
|
692
|
-
return this.fetchCurrentPrice(request);
|
|
693
|
-
case 'batch':
|
|
694
|
-
return this.fetchBatchPrices(request);
|
|
695
|
-
case 'historical':
|
|
696
|
-
return this.fetchHistoricalPrices(request);
|
|
697
|
-
default:
|
|
698
|
-
throw new Error(`Unsupported request type`);
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
async isAvailable(): Promise<boolean> {
|
|
703
|
-
// Health check
|
|
704
|
-
return true;
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
supportsMarket(market: string): boolean {
|
|
708
|
-
return ['US', 'UK', 'DE'].includes(market);
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
supportsTicker(ticker: string): boolean {
|
|
712
|
-
return /^[A-Z]{1,5}$/.test(ticker);
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
stockService.register(new MyCustomProvider());
|
|
717
|
-
```
|
|
718
|
-
|
|
719
|
-
## Performance
|
|
720
|
-
|
|
721
|
-
- **Batch Fetching**: Get 100+ prices in one API request
|
|
722
|
-
- **Smart Caching**: Data-type aware TTL (historical cached forever, EOD 24h, live 30s)
|
|
723
|
-
- **Rate Limit Management**: Automatic retry logic for API limits
|
|
724
|
-
- **Concurrent Processing**: Handle 1000+ records/second
|
|
725
|
-
- **Streaming**: Process GB-sized datasets without memory issues
|
|
726
|
-
- **Provider Fallback**: Automatic failover between data sources
|
|
303
|
+
Useful request patterns:
|
|
304
|
+
|
|
305
|
+
- Germany: `identifier: 'aeg'`
|
|
306
|
+
- EU: `identifier: '32024R1689'`, `language: 'EN'`
|
|
307
|
+
- US public law package: `identifier: 'PLAW-119publ1'`, `usCollection: 'PLAW'`
|
|
308
|
+
- US Code citation: `identifier: '8 U.S.C. ยง 1226'`
|
|
309
|
+
|
|
310
|
+
## API At A Glance
|
|
311
|
+
|
|
312
|
+
### `StockPriceService`
|
|
313
|
+
|
|
314
|
+
- `register(provider, config?)`
|
|
315
|
+
- `unregister(providerName)`
|
|
316
|
+
- `getPrice({ ticker })`
|
|
317
|
+
- `getPrices({ tickers })`
|
|
318
|
+
- `getData(request)`
|
|
319
|
+
- `checkProvidersHealth()`
|
|
320
|
+
- `getProviderStats()`
|
|
321
|
+
- `clearCache()`
|
|
322
|
+
- `getCacheStats()`
|
|
323
|
+
|
|
324
|
+
### `FundamentalsService`
|
|
325
|
+
|
|
326
|
+
- `register(provider, config?)`
|
|
327
|
+
- `getFundamentals(ticker)`
|
|
328
|
+
- `getBatchFundamentals(tickers)`
|
|
329
|
+
- `getData(request)`
|
|
330
|
+
- `enrichWithPrice(fundamentals, price)`
|
|
331
|
+
- `checkProvidersHealth()`
|
|
332
|
+
- `getProviderStats()`
|
|
333
|
+
- `clearCache()`
|
|
334
|
+
|
|
335
|
+
### `StockDataService`
|
|
336
|
+
|
|
337
|
+
- `registerPriceProvider(provider, config?)`
|
|
338
|
+
- `registerFundamentalsProvider(provider, config?)`
|
|
339
|
+
- `getPrice(ticker)`
|
|
340
|
+
- `getPrices(tickers)`
|
|
341
|
+
- `getFundamentals(ticker)`
|
|
342
|
+
- `getBatchFundamentals(tickers)`
|
|
343
|
+
- `getStockData(ticker | request)`
|
|
344
|
+
- `getBatchStockData(tickers | request)`
|
|
345
|
+
- `checkProvidersHealth()`
|
|
346
|
+
- `getProviderStats()`
|
|
347
|
+
- `clearCache()`
|
|
348
|
+
|
|
349
|
+
### `OpenData`
|
|
350
|
+
|
|
351
|
+
- `start()`
|
|
352
|
+
- `stop()`
|
|
353
|
+
- `buildInitialDb()`
|
|
354
|
+
- `handelsregister.searchCompany(name, limit?)`
|
|
355
|
+
- `handelsregister.getSpecificCompany({ court, type, number })`
|
|
356
|
+
- `handelsregister.getSpecificCompanyByName(name)`
|
|
357
|
+
|
|
358
|
+
### `LawService`
|
|
359
|
+
|
|
360
|
+
- `start()`
|
|
361
|
+
- `stop()`
|
|
362
|
+
- `getLaw(request)`
|
|
363
|
+
- `syncLaw(request)`
|
|
364
|
+
- `syncLaws(request)`
|
|
365
|
+
- `searchLaws({ query, jurisdiction?, limit? })`
|
|
366
|
+
|
|
367
|
+
## Behavior And Caveats
|
|
368
|
+
|
|
369
|
+
- Historical market data is cached aggressively because it is treated as immutable.
|
|
370
|
+
- Intraday data uses interval-aware caching and tries incremental refreshes for recent data.
|
|
371
|
+
- `StockDataService` is convenience-first. `StockPriceService` is the better low-level choice if you care about request-shape-specific cache behavior.
|
|
372
|
+
- `OpenData` is a real workflow layer, not just types. It writes local data, launches a browser, and manages downloads.
|
|
373
|
+
- Handelsregister and EUR-Lex integrations depend on current site structure. They are practical automation workflows, not guaranteed stable official APIs.
|
|
374
|
+
- The most useful public `OpenData` features today are startup, JSONL import, and Handelsregister operations. Some extra validation/search helper methods exist in source but are still stubs.
|
|
375
|
+
- `LawService` persists synced laws locally, so repeated searches can stay offline once the data has been synced.
|
|
376
|
+
- There is no hosted backend in this package. Everything is meant to run from your own TypeScript/Node process.
|
|
727
377
|
|
|
728
378
|
## Testing
|
|
729
379
|
|
|
730
|
-
Run the comprehensive test suite:
|
|
731
|
-
|
|
732
380
|
```bash
|
|
733
381
|
pnpm test
|
|
734
382
|
```
|
|
735
383
|
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
```bash
|
|
739
|
-
# Stock price providers
|
|
740
|
-
pnpm tstest test/test.marketstack.node.ts --verbose
|
|
741
|
-
pnpm tstest test/test.stocks.ts --verbose
|
|
742
|
-
|
|
743
|
-
# Fundamental data
|
|
744
|
-
pnpm tstest test/test.secedgar.provider.node.ts --verbose
|
|
745
|
-
pnpm tstest test/test.fundamentals.service.node.ts --verbose
|
|
746
|
-
|
|
747
|
-
# German business data
|
|
748
|
-
pnpm tstest test/test.handelsregister.ts --verbose
|
|
749
|
-
```
|
|
750
|
-
|
|
751
|
-
## Getting API Keys
|
|
752
|
-
|
|
753
|
-
### Marketstack (EOD Stock Data)
|
|
754
|
-
|
|
755
|
-
1. Visit [marketstack.com](https://marketstack.com)
|
|
756
|
-
2. Sign up for a free account (100 requests/month)
|
|
757
|
-
3. Get your API key from the dashboard
|
|
758
|
-
4. Set environment variable: `MARKETSTACK_COM_TOKEN=your_key_here`
|
|
759
|
-
|
|
760
|
-
### SEC EDGAR (Fundamental Data)
|
|
761
|
-
|
|
762
|
-
**No API key required!** SEC EDGAR is completely free and public. Just provide your company name and email in the User-Agent:
|
|
763
|
-
|
|
764
|
-
```typescript
|
|
765
|
-
new SecEdgarProvider({
|
|
766
|
-
userAgent: 'YourCompany youremail@example.com'
|
|
767
|
-
});
|
|
768
|
-
```
|
|
384
|
+
Some tests hit live remote services, so they may depend on network access, optional API credentials, or browser automation support.
|
|
769
385
|
|
|
770
386
|
## License and Legal Information
|
|
771
387
|
|
|
772
|
-
This repository contains open-source code
|
|
388
|
+
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./license) file.
|
|
773
389
|
|
|
774
390
|
**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.
|
|
775
391
|
|
|
776
392
|
### Trademarks
|
|
777
393
|
|
|
778
|
-
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein.
|
|
394
|
+
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
|
|
395
|
+
|
|
396
|
+
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
|
|
779
397
|
|
|
780
398
|
### Company Information
|
|
781
399
|
|
|
782
|
-
Task Venture Capital GmbH
|
|
783
|
-
Registered at District
|
|
400
|
+
Task Venture Capital GmbH
|
|
401
|
+
Registered at District Court Bremen HRB 35230 HB, Germany
|
|
784
402
|
|
|
785
|
-
For any legal inquiries or
|
|
403
|
+
For any legal inquiries or further information, please contact us via email at hello@task.vc.
|
|
786
404
|
|
|
787
405
|
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.
|