@fin.cx/opendata 4.4.0 → 4.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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/cpi/classes.bundesbankcpiclient.js +1 -1
- package/dist_ts/cpi/classes.cpierror.js +1 -1
- package/dist_ts/cpi/constants.d.ts +2 -2
- package/dist_ts/cpi/index.d.ts +1 -1
- package/dist_ts/cpi/index.js +2 -2
- package/dist_ts/cpi/indexmath.d.ts +12 -0
- package/dist_ts/cpi/indexmath.js +48 -13
- package/dist_ts/handelsregister/classes.handelsregisterclient.js +1 -1
- package/dist_ts/handelsregister/classes.handelsregistererror.js +1 -1
- package/dist_ts/handelsregister/classes.portalsession.js +1 -1
- package/dist_ts/handelsregister/classes.slidingwindowgate.js +1 -1
- package/dist_ts/handelsregister/parse.searchresults.js +1 -1
- package/dist_ts/handelsregister/references.js +1 -1
- package/dist_ts/holidays/calendar.d.ts +13 -1
- package/dist_ts/holidays/calendar.js +52 -1
- package/dist_ts/holidays/classes.holidaycalendarerror.d.ts +4 -1
- package/dist_ts/holidays/classes.holidaycalendarerror.js +1 -1
- package/dist_ts/holidays/data.holidays.js +1 -1
- package/dist_ts/holidays/index.d.ts +1 -1
- package/dist_ts/holidays/index.js +2 -2
- package/dist_ts/holidays/interfaces.d.ts +2 -0
- package/dist_ts/paths.js +1 -1
- package/dist_ts/stocks/providers/provider.coingecko.js +1 -1
- package/dist_ts/stocks/providers/provider.marketstack.js +1 -1
- package/dist_ts/stocks/providers/provider.yahoo.js +1 -1
- package/dist_ts/vatid/classes.evatrclient.js +1 -1
- package/dist_ts/vatid/classes.viesclient.js +1 -1
- package/dist_ts/vatid/constants.d.ts +2 -2
- package/dist_ts/vatid/request.d.ts +1 -1
- package/package.json +3 -3
- package/readme.md +32 -5
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/cpi/index.ts +1 -1
- package/ts/cpi/indexmath.ts +62 -14
- package/ts/holidays/calendar.ts +73 -0
- package/ts/holidays/classes.holidaycalendarerror.ts +10 -1
- package/ts/holidays/index.ts +2 -0
- package/ts/holidays/interfaces.ts +3 -0
|
@@ -115,4 +115,4 @@ export class ViesClient {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
const optionalMatch = (keyArg, matchArg) => (matchArg ? { [keyArg]: matchArg } : {});
|
|
118
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
118
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy52aWVzY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvdmF0aWQvY2xhc3Nlcy52aWVzY2xpZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQXdCLE1BQU0seUJBQXlCLENBQUM7QUFDM0UsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3BELE9BQU8sRUFBRSxXQUFXLEVBQUUsYUFBYSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFNUQsT0FBTyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDbkQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUV4Qyx3R0FBd0c7QUFDeEcsTUFBTSxRQUFRLEdBQThDO0lBQzFELGFBQWEsRUFBRSxlQUFlO0lBQzlCLHNCQUFzQixFQUFFLG1CQUFtQjtJQUMzQyxtQkFBbUIsRUFBRSxhQUFhO0lBQ2xDLGNBQWMsRUFBRSwwQkFBMEI7SUFDMUMsT0FBTyxFQUFFLDBCQUEwQjtJQUNuQyxXQUFXLEVBQUUsU0FBUztJQUN0QixVQUFVLEVBQUUsU0FBUztJQUNyQix5QkFBeUIsRUFBRSxNQUFNO0lBQ2pDLDhCQUE4QixFQUFFLE1BQU07SUFDdEMscUJBQXFCLEVBQUUsTUFBTTtJQUM3QiwwQkFBMEIsRUFBRSxNQUFNO0NBQ25DLENBQUM7QUFFRixNQUFNLE9BQU8sR0FBMEIsQ0FBQyxPQUFPLEVBQUUsU0FBUyxFQUFFLGVBQWUsQ0FBQyxDQUFDO0FBRTdFLE1BQU0sT0FBTyxHQUFHLENBQUMsT0FBZ0IsRUFBRSxNQUFjLEVBQTBCLEVBQUU7SUFDM0UsTUFBTSxLQUFLLEdBQUcsU0FBUyxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztJQUN6QyxPQUFPLEtBQUssS0FBSyxTQUFTLElBQUssT0FBNkIsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FB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
|
|
@@ -19,10 +19,10 @@ export declare const defaultVatIdTimeoutMs = 30000;
|
|
|
19
19
|
* states gives them (`/v1/info/eu_mitgliedstaaten`, read on 2026-09-27): the 27 member states,
|
|
20
20
|
* Greece as `EL`, and `XI` for Northern Ireland.
|
|
21
21
|
*/
|
|
22
|
-
export declare const vatIdCountryCodes: readonly [
|
|
22
|
+
export declare const vatIdCountryCodes: readonly ['AT', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'EL', 'ES', 'FI', 'FR', 'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'PL', 'PT', 'RO', 'SE', 'SI', 'SK', 'XI'];
|
|
23
23
|
/**
|
|
24
24
|
* Every status code of the eVatR API with the HTTP status its list of messages gives it
|
|
25
25
|
* (`/v1/info/statusmeldungen`, read on 2026-09-27). The OpenAPI document lists `evatr-0008` under
|
|
26
26
|
* 400 and the list under 403, so the code decides, never the HTTP status.
|
|
27
27
|
*/
|
|
28
|
-
export declare const evatrStatusCodes: readonly [
|
|
28
|
+
export declare const evatrStatusCodes: readonly ['evatr-0000', 'evatr-0001', 'evatr-0002', 'evatr-0003', 'evatr-0004', 'evatr-0005', 'evatr-0006', 'evatr-0007', 'evatr-0008', 'evatr-0011', 'evatr-0012', 'evatr-0013', 'evatr-1001', 'evatr-1002', 'evatr-1003', 'evatr-1004', 'evatr-2001', 'evatr-2002', 'evatr-2003', 'evatr-2004', 'evatr-2005', 'evatr-2006', 'evatr-2007', 'evatr-2008', 'evatr-2011', 'evatr-3011'];
|
|
@@ -11,6 +11,6 @@ export interface IJsonAnswer {
|
|
|
11
11
|
* ends it as `aborted`, the time as `timeout`, and a request that could not be sent or answered as
|
|
12
12
|
* `unreachable`; nothing is retried. The timer ends with the request, answered or not.
|
|
13
13
|
*/
|
|
14
|
-
export declare const postJson: (optionsArg: IVatIdClientOptions, serviceArg: IVatIdErrorDetails[
|
|
14
|
+
export declare const postJson: (optionsArg: IVatIdClientOptions, serviceArg: IVatIdErrorDetails['service'], urlArg: string, bodyArg: unknown, signalArg: AbortSignal | undefined) => Promise<IJsonAnswer>;
|
|
15
15
|
/** A string field of an answer, where it is one and not empty. */
|
|
16
16
|
export declare const textField: (bodyArg: unknown, keyArg: string) => string | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fin.cx/opendata",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Open business and financial data for TypeScript: register lookups on the German register portal (handelsregister.de) without storage, the statutory public holidays of the German federal states, the base rate of § 247 BGB, the German consumer price index with the arithmetic of index clauses, and stock and crypto prices and fundamentals from several providers.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"author": "Task Venture Capital GmbH",
|
|
37
37
|
"license": "MIT",
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@git.zone/tsbuild": "^
|
|
39
|
+
"@git.zone/tsbuild": "^6.0.2",
|
|
40
40
|
"@git.zone/tsrun": "^3.0.2",
|
|
41
|
-
"@git.zone/tstest": "^7.
|
|
41
|
+
"@git.zone/tstest": "^7.2.0",
|
|
42
42
|
"@push.rocks/qenv": "^6.1.3",
|
|
43
43
|
"@types/node": "^26.6.3"
|
|
44
44
|
},
|
package/readme.md
CHANGED
|
@@ -7,7 +7,7 @@ It gives you seven practical lanes in one package:
|
|
|
7
7
|
- `StockPriceService` for stock and crypto prices with caching, retries, failover, historical ranges, and intraday support
|
|
8
8
|
- `FundamentalsService` and `StockDataService` for SEC fundamentals and combined price + fundamentals payloads
|
|
9
9
|
- `HandelsregisterClient`, from `@fin.cx/opendata/handelsregister`, for single lookups in the commercial, cooperative, partnership, association and civil-law partnership registers over plain HTTP, without any storage
|
|
10
|
-
- the holiday calendar, from `@fin.cx/opendata/holidays`, for the statutory public holidays of each German federal state
|
|
10
|
+
- the holiday calendar, from `@fin.cx/opendata/holidays`, for the statutory public holidays of each German federal state, the working days of § 193 BGB and the bank working days the social insurance counts its due dates in, computed per year without data files or requests
|
|
11
11
|
- the base rate, from `@fin.cx/opendata/baserate`, for the Basiszinssatz of § 247 BGB per half-year as the Deutsche Bundesbank publishes it, for default interest under § 288 BGB
|
|
12
12
|
- VAT ID confirmation, from `@fin.cx/opendata/vatid`, over the BZSt's eVatR REST API (the confirmation of § 18e UStG, simple or qualified) and the European Commission's VIES REST API, without any storage
|
|
13
13
|
- the consumer price index, from `@fin.cx/opendata/cpi`, for the Verbraucherpreisindex für Deutschland of the Statistisches Bundesamt as the Deutsche Bundesbank republishes it, with the arithmetic of index clauses (Wertsicherungsklauseln, § 557b BGB), without any storage
|
|
@@ -60,6 +60,8 @@ Everything here comes from `@fin.cx/opendata/holidays`. That entry point imports
|
|
|
60
60
|
The statutory public holidays of a federal state in a year or on a date, each with its legal basis.
|
|
61
61
|
- `isPublicHoliday()`, `isWorkingDay()`, `nextWorkingDay()`, `workingDayOnOrAfter()`
|
|
62
62
|
Holidays and working days in the sense of § 193 BGB: neither a Saturday, nor a Sunday, nor a statutory public holiday.
|
|
63
|
+
- `isBankWorkingDay()`, `nthLastBankWorkingDay()`
|
|
64
|
+
Bank working days (Bankarbeitstage), which also leave out 24 and 31 December, when the Deutsche Bundesbank is closed: the days German social insurance due dates are counted in.
|
|
63
65
|
- `easterSunday(year)`, `germanStates`, `findGermanState()`
|
|
64
66
|
|
|
65
67
|
### The Base Rate Of § 247 BGB
|
|
@@ -116,6 +118,8 @@ Everything here comes from `@fin.cx/opendata/cpi`. That entry point imports noth
|
|
|
116
118
|
The change in percent as the Statistisches Bundesamt computes it, ((new / old) × 100) − 100, rounded to one decimal; and the change in index points.
|
|
117
119
|
- `cpiThresholds(reference, percent)`, `cpiFirstReaching(values, reference, percent, { direction, reach })`
|
|
118
120
|
The thresholds of a percent rule (reference × (1 ± percent / 100), rounded to the reference's digits), and the first month after the reference month that reaches one. Whether the threshold itself counts (`reached`) or only a value beyond it (`exceeded`) is the clause's to say.
|
|
121
|
+
- `cpiFirstReachingPoints(values, reference, points, { direction, reach })`
|
|
122
|
+
The same for a points rule: the first month after the reference month whose change in index points reaches the threshold, at or above reference + points (`up`), at or below reference − points (`down`), or whichever comes first (`either`). The points are a decimal from 0 upward (`'5'`, `'2.5'`); the comparison is exact and nothing is rounded.
|
|
119
123
|
- `cpiScaleAmount(amountCents, old, new, { basis? })`
|
|
120
124
|
An amount in cents scaled from one index value to another: by the index ratio (amount × new / old, the default) or, with `basis: 'rounded_percent'`, by the change in percent rounded to one decimal.
|
|
121
125
|
- `CpiError` with the codes `invalid_input`, `before_first_period`, `not_published`, `base_mismatch`, `series_withdrawn`, `busy`, `unavailable`, `unreachable`, `timeout`, `aborted` and `unexpected_answer`
|
|
@@ -123,7 +127,7 @@ Everything here comes from `@fin.cx/opendata/cpi`. That entry point imports noth
|
|
|
123
127
|
The client takes a `fetch` (for example `createPublicOnlyFetch` of `@push.rocks/smartrequest`), a `baseUrl` for a test server, a `timeoutMs` (30 seconds by default; its timer ends with the request), a `userAgent` and a caller's `signal`. Nothing is retried.
|
|
124
128
|
|
|
125
129
|
```typescript
|
|
126
|
-
import { BundesbankCpiClient, CpiError, cpiChangePercent, cpiFirstReaching, cpiScaleAmount } from '@fin.cx/opendata/cpi';
|
|
130
|
+
import { BundesbankCpiClient, CpiError, cpiChangePercent, cpiFirstReaching, cpiFirstReachingPoints, cpiScaleAmount } from '@fin.cx/opendata/cpi';
|
|
127
131
|
|
|
128
132
|
const client = new BundesbankCpiClient();
|
|
129
133
|
const series = await client.getMonthly({ from: '2020-01' });
|
|
@@ -140,6 +144,7 @@ cpiFirstReaching(series.values, series.values.find((value) => value.period === '
|
|
|
140
144
|
direction: 'up',
|
|
141
145
|
reach: 'reached',
|
|
142
146
|
}); // the value of 2021-12 (104.7)
|
|
147
|
+
cpiFirstReachingPoints(series.values, october2022, '5', { direction: 'up', reach: 'reached' }); // the value of 2024-03 (118.6); 5 % first in 2024-04
|
|
143
148
|
|
|
144
149
|
try {
|
|
145
150
|
await client.getMonth('2026-09');
|
|
@@ -160,7 +165,7 @@ The Statistisches Bundesamt moves the index to a new base year about every five
|
|
|
160
165
|
|
|
161
166
|
- every value carries its base, and the calculations refuse values of different bases, indices or frequencies with `base_mismatch`; nothing is converted;
|
|
162
167
|
- for a percent rule the Statistisches Bundesamt calculates with the values of the current base for both months, even where the contract names an earlier base, so read both again on the current base;
|
|
163
|
-
- points depend on the base. `cpiChangePoints()`
|
|
168
|
+
- points depend on the base. `cpiChangePoints()` and `cpiFirstReachingPoints()` give them within one base only; a points rule on an earlier base is the contract's matter;
|
|
164
169
|
- the client leaves the base open in its request, and the Bundesbank answers with the series of the current base. The key of the former base 2015=100 answers 404 today; an answer of 404 ends in `series_withdrawn`, and an answer that holds two bases in `unexpected_answer`.
|
|
165
170
|
|
|
166
171
|
#### Attribution
|
|
@@ -465,6 +470,24 @@ workingDayOnOrAfter('2026-05-14', 'DE-BY', { regionalHolidays: 'ignore' }); // '
|
|
|
465
470
|
|
|
466
471
|
States are ISO 3166-2 codes (`DE-BY`); `findGermanState('Bayern')` and `findGermanState('BY')` find one. Dates are plain calendar dates written `YYYY-MM-DD`, so no time zone can move them.
|
|
467
472
|
|
|
473
|
+
#### Bank working days
|
|
474
|
+
|
|
475
|
+
A bank working day (Bankarbeitstag) is neither a Saturday, nor a Sunday, nor a statutory public holiday, nor 24 or 31 December. On those two days the Deutsche Bundesbank is closed although they are no statutory holidays; its list of holidays names them as its additional closing days. German social insurance counts its due dates in these days, with the holidays of the state where the collecting agency (Einzugsstelle) has its seat: contributions are due on the third-last bank working day of the month, and the contribution statement has to be with the collecting agency at the start of the fifth-last.
|
|
476
|
+
|
|
477
|
+
```ts
|
|
478
|
+
import { isBankWorkingDay, nthLastBankWorkingDay } from '@fin.cx/opendata/holidays';
|
|
479
|
+
|
|
480
|
+
isBankWorkingDay('2026-12-24', 'DE-HH', { regionalHolidays: 'ignore' }); // false: the Bundesbank is closed
|
|
481
|
+
nthLastBankWorkingDay('2026-12', 3, 'DE-HH', { regionalHolidays: 'ignore' }); // '2026-12-28'
|
|
482
|
+
nthLastBankWorkingDay('2026-12', 5, 'DE-HH', { regionalHolidays: 'ignore' }); // '2026-12-22'
|
|
483
|
+
|
|
484
|
+
// Reformationstag 2025 was a Friday: a holiday in Niedersachsen, a working day in Bayern
|
|
485
|
+
nthLastBankWorkingDay('2025-10', 3, 'DE-NI', { regionalHolidays: 'ignore' }); // '2025-10-28'
|
|
486
|
+
nthLastBankWorkingDay('2025-10', 3, 'DE-BY', { regionalHolidays: 'ignore' }); // '2025-10-29'
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
Months are written `YYYY-MM`. The position counts from the end of the month, 1 being the last bank working day; a position the month does not have ends in `invalid_position`. The tests hold the results against the due dates the collecting agencies published for 2025 and 2026.
|
|
490
|
+
|
|
468
491
|
#### Regional holidays
|
|
469
492
|
|
|
470
493
|
Four holidays apply only in part of a state:
|
|
@@ -560,10 +583,12 @@ Four holidays apply only in part of a state:
|
|
|
560
583
|
- `isWorkingDay(date, state, { regionalHolidays })`
|
|
561
584
|
- `nextWorkingDay(date, state, { regionalHolidays })`
|
|
562
585
|
- `workingDayOnOrAfter(date, state, { regionalHolidays })`
|
|
586
|
+
- `isBankWorkingDay(date, state, { regionalHolidays })`
|
|
587
|
+
- `nthLastBankWorkingDay(month, position, state, { regionalHolidays })`
|
|
563
588
|
- `easterSunday(year)`
|
|
564
589
|
- `germanStates`, `findGermanState(codeOrName)`
|
|
565
590
|
- `holidayCalendarFirstYear`, `holidayLawsReadOn`
|
|
566
|
-
- `HolidayCalendarError` with the codes `invalid_date`, `unsupported_year`, `unknown_state` and `invalid_options`
|
|
591
|
+
- `HolidayCalendarError` with the codes `invalid_date`, `invalid_month`, `invalid_position`, `unsupported_year`, `unknown_state` and `invalid_options`
|
|
567
592
|
|
|
568
593
|
### VAT ID (`@fin.cx/opendata/vatid`)
|
|
569
594
|
|
|
@@ -581,7 +606,7 @@ Four holidays apply only in part of a state:
|
|
|
581
606
|
### Consumer price index (`@fin.cx/opendata/cpi`)
|
|
582
607
|
|
|
583
608
|
- `BundesbankCpiClient` with `getMonthly({ from, to? })`, `getAnnual({ from, to? })` and `getMonth(month)`
|
|
584
|
-
- `cpiChangePercent(old, new)`, `cpiChangePoints(old, new)`, `cpiThresholds(reference, percent)`, `cpiFirstReaching(values, reference, percent, options)`, `cpiScaleAmount(amountCents, old, new, options?)`
|
|
609
|
+
- `cpiChangePercent(old, new)`, `cpiChangePoints(old, new)`, `cpiThresholds(reference, percent)`, `cpiFirstReaching(values, reference, percent, options)`, `cpiFirstReachingPoints(values, reference, points, options)`, `cpiScaleAmount(amountCents, old, new, options?)`
|
|
585
610
|
- `bundesbankCpiBaseUrl`, `bundesbankVpiMonthlyKey`, `bundesbankVpiAnnualKey`, `bundesbankVpiFirstMonth`, `bundesbankVpiFirstYear`, `destatisLicence`, `defaultCpiTimeoutMs`
|
|
586
611
|
- `CpiError` with the codes `invalid_input`, `before_first_period`, `not_published`, `base_mismatch`, `series_withdrawn`, `busy`, `unavailable`, `unreachable`, `timeout`, `aborted` and `unexpected_answer`
|
|
587
612
|
|
|
@@ -632,6 +657,8 @@ The holiday calendar is derived from the holiday laws of the federal states, rea
|
|
|
632
657
|
| Thüringen | [Thüringer Feier- und Gedenktagsgesetz (ThürFGtG)](https://landesrecht.thueringen.de/perma?j=FeiertG_TH_!_2) | § 2, § 10 Abs. 1 |
|
|
633
658
|
| all | [Bürgerliches Gesetzbuch](https://www.gesetze-im-internet.de/bgb/__193.html) | § 193 (Sonntag, Feiertag, Sonnabend) |
|
|
634
659
|
|
|
660
|
+
The bank working days follow the Deutsche Bundesbank's list "Feiertage in Deutschland im Jahr 2026", read on 2026-10-08, which names 24 December (Heiligabend) and 31 December (Silvester) as its additional closing days ("Zusätzliche geschäftsfreie Tage der Deutschen Bundesbank"), and the collecting agencies' statement that these two days are no bank working days for the due dates of contributions, that a holiday counts by the seat of the collecting agency, and their published due dates of 2025 (AOK, with separate October dates for the states with and without Reformationstag) and 2026 (TK, AOK and KKH). The calendar takes the two closing days as fixed; a change of the Bundesbank's closing days needs a new version.
|
|
661
|
+
|
|
635
662
|
The base rate of `@fin.cx/opendata/baserate` (`ts/baserate/data.baserate.ts`) is the table "Basiszinssatz nach § 247 BGB" of the Deutsche Bundesbank (https://www.bundesbank.de/de/bundesbank/organisation/agb-und-regelungen/basiszinssatz-607820), which the Bundesbank publishes under § 247 Abs. 2 BGB, read on 2026-09-27 and reproduced unaltered: every half-year from 1 July 2002, the first row it lists, to the half-year from 1 July 2026 (1,52 %). The rate changes on 1 January and 1 July (§ 247 Abs. 1 Satz 2 BGB); a day after 31 December 2026 answers `not_published` until a release carries the next row.
|
|
636
663
|
|
|
637
664
|
The VAT ID clients of `@fin.cx/opendata/vatid` ask two official services at run time, one request at a time, read on 2026-09-27:
|
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: '4.
|
|
6
|
+
version: '4.6.0',
|
|
7
7
|
description: 'Open business and financial data for TypeScript: register lookups on the German register portal (handelsregister.de) without storage, the statutory public holidays of the German federal states, the base rate of § 247 BGB, the German consumer price index with the arithmetic of index clauses, and stock and crypto prices and fundamentals from several providers.'
|
|
8
8
|
}
|
package/ts/cpi/index.ts
CHANGED
|
@@ -12,5 +12,5 @@ export {
|
|
|
12
12
|
defaultCpiTimeoutMs,
|
|
13
13
|
destatisLicence,
|
|
14
14
|
} from './constants.js';
|
|
15
|
-
export { cpiChangePercent, cpiChangePoints, cpiFirstReaching, cpiScaleAmount, cpiThresholds } from './indexmath.js';
|
|
15
|
+
export { cpiChangePercent, cpiChangePoints, cpiFirstReaching, cpiFirstReachingPoints, cpiScaleAmount, cpiThresholds } from './indexmath.js';
|
|
16
16
|
export { BundesbankCpiClient } from './classes.bundesbankcpiclient.js';
|
package/ts/cpi/indexmath.ts
CHANGED
|
@@ -102,23 +102,25 @@ export const cpiThresholds = (referenceArg: ICpiValue, percentArg: string): ICpi
|
|
|
102
102
|
};
|
|
103
103
|
};
|
|
104
104
|
|
|
105
|
+
/** The reach options as the clause gives them; anything else is refused. */
|
|
106
|
+
const requireReachOptions = (optionsArg: ICpiReachOptions): void => {
|
|
107
|
+
if (!['up', 'down', 'either'].includes(optionsArg.direction) || !['reached', 'exceeded'].includes(optionsArg.reach)) {
|
|
108
|
+
throw new CpiError('invalid_input', 'the direction is up, down or either, and the reach is reached or exceeded.');
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
105
112
|
/**
|
|
106
|
-
* The first value after the reference period
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* given twice is refused.
|
|
113
|
+
* The first value after the reference period at or beyond the upper threshold or at or below the lower
|
|
114
|
+
* one, as the options say. The thresholds are units of 10^-scale; the lower one may be zero or less, which
|
|
115
|
+
* no index value reaches. All values must be published values of the reference value's series and base;
|
|
116
|
+
* their order does not matter, a period given twice is refused.
|
|
111
117
|
*/
|
|
112
|
-
|
|
118
|
+
const firstReachingUnits = (
|
|
113
119
|
valuesArg: readonly ICpiValue[],
|
|
114
120
|
referenceArg: ICpiValue,
|
|
115
|
-
|
|
121
|
+
thresholdsArg: { upper: bigint; lower: bigint; scale: number },
|
|
116
122
|
optionsArg: ICpiReachOptions,
|
|
117
123
|
): ICpiValue | undefined => {
|
|
118
|
-
if (!['up', 'down', 'either'].includes(optionsArg.direction) || !['reached', 'exceeded'].includes(optionsArg.reach)) {
|
|
119
|
-
throw new CpiError('invalid_input', 'the direction is up, down or either, and the reach is reached or exceeded.');
|
|
120
|
-
}
|
|
121
|
-
const thresholds = thresholdUnitsOf(referenceArg, percentArg);
|
|
122
124
|
const periods = new Set<string>();
|
|
123
125
|
const candidates: { value: ICpiValue; decimal: IScaledDecimal }[] = [];
|
|
124
126
|
for (const value of valuesArg) {
|
|
@@ -135,10 +137,10 @@ export const cpiFirstReaching = (
|
|
|
135
137
|
candidates.sort((leftArg, rightArg) => leftArg.value.period.localeCompare(rightArg.value.period));
|
|
136
138
|
const exceeding = optionsArg.reach === 'exceeded';
|
|
137
139
|
for (const { value, decimal } of candidates) {
|
|
138
|
-
const scale = Math.max(decimal.scale,
|
|
140
|
+
const scale = Math.max(decimal.scale, thresholdsArg.scale);
|
|
139
141
|
const units = atScale(decimal, scale);
|
|
140
|
-
const upper =
|
|
141
|
-
const lower =
|
|
142
|
+
const upper = thresholdsArg.upper * powerOfTen(scale - thresholdsArg.scale);
|
|
143
|
+
const lower = thresholdsArg.lower * powerOfTen(scale - thresholdsArg.scale);
|
|
142
144
|
const up = exceeding ? units > upper : units >= upper;
|
|
143
145
|
const down = exceeding ? units < lower : units <= lower;
|
|
144
146
|
if ((optionsArg.direction !== 'down' && up) || (optionsArg.direction !== 'up' && down)) {
|
|
@@ -148,6 +150,52 @@ export const cpiFirstReaching = (
|
|
|
148
150
|
return undefined;
|
|
149
151
|
};
|
|
150
152
|
|
|
153
|
+
/**
|
|
154
|
+
* The first value after the reference period that reaches a threshold of a percent rule
|
|
155
|
+
* (`cpiThresholds`), or `undefined` while none does. Whether the threshold itself counts (`reached`) or
|
|
156
|
+
* only a value beyond it (`exceeded`), and which direction, is the clause's to say. All values must be
|
|
157
|
+
* published values of the reference value's series and base; their order does not matter, a period
|
|
158
|
+
* given twice is refused.
|
|
159
|
+
*/
|
|
160
|
+
export const cpiFirstReaching = (
|
|
161
|
+
valuesArg: readonly ICpiValue[],
|
|
162
|
+
referenceArg: ICpiValue,
|
|
163
|
+
percentArg: string,
|
|
164
|
+
optionsArg: ICpiReachOptions,
|
|
165
|
+
): ICpiValue | undefined => {
|
|
166
|
+
requireReachOptions(optionsArg);
|
|
167
|
+
return firstReachingUnits(valuesArg, referenceArg, thresholdUnitsOf(referenceArg, percentArg), optionsArg);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* The first value after the reference period whose change from the reference in index points
|
|
172
|
+
* (`cpiChangePoints`) reaches a points rule's threshold, or `undefined` while none does: a value at or
|
|
173
|
+
* above reference + points (`up`), at or below reference − points (`down`), or whichever comes first
|
|
174
|
+
* (`either`). The points are a decimal written with digits and a point, from 0 upward, with any number
|
|
175
|
+
* of digits (`'5'`, `'2.5'`); nothing is rounded, so a threshold with more digits than the values is
|
|
176
|
+
* compared exactly. Whether the threshold itself counts (`reached`) or only a value beyond it
|
|
177
|
+
* (`exceeded`) is the clause's to say. Points depend on the base, so all values must be published
|
|
178
|
+
* values of the reference value's series and base (`base_mismatch` otherwise); their order does not
|
|
179
|
+
* matter, a period given twice is refused.
|
|
180
|
+
*/
|
|
181
|
+
export const cpiFirstReachingPoints = (
|
|
182
|
+
valuesArg: readonly ICpiValue[],
|
|
183
|
+
referenceArg: ICpiValue,
|
|
184
|
+
pointsArg: string,
|
|
185
|
+
optionsArg: ICpiReachOptions,
|
|
186
|
+
): ICpiValue | undefined => {
|
|
187
|
+
requireReachOptions(optionsArg);
|
|
188
|
+
const reference = publishedValueOf(referenceArg);
|
|
189
|
+
const points = parseDecimal(pointsArg);
|
|
190
|
+
if (!points) {
|
|
191
|
+
throw new CpiError('invalid_input', `${JSON.stringify(pointsArg)} is no number of index points from 0 upward, written with digits and a point.`);
|
|
192
|
+
}
|
|
193
|
+
const scale = Math.max(reference.scale, points.scale);
|
|
194
|
+
const referenceUnits = atScale(reference, scale);
|
|
195
|
+
const pointsUnits = atScale(points, scale);
|
|
196
|
+
return firstReachingUnits(valuesArg, referenceArg, { upper: referenceUnits + pointsUnits, lower: referenceUnits - pointsUnits, scale }, optionsArg);
|
|
197
|
+
};
|
|
198
|
+
|
|
151
199
|
/**
|
|
152
200
|
* An amount in cents scaled from one index value to another, rounded half away from zero to whole cents:
|
|
153
201
|
* by the index ratio (amount × new / old, the default, as the Statistisches Bundesamt writes it) or by
|
package/ts/holidays/calendar.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
IWorkingDayOptions,
|
|
8
8
|
TGermanStateCode,
|
|
9
9
|
TIsoDate,
|
|
10
|
+
TIsoMonth,
|
|
10
11
|
} from './interfaces.js';
|
|
11
12
|
|
|
12
13
|
const dayMs = 86_400_000;
|
|
@@ -57,6 +58,17 @@ const parseIsoDate = (dateArg: TIsoDate): ICalendarDay => {
|
|
|
57
58
|
return { year, utc };
|
|
58
59
|
};
|
|
59
60
|
|
|
61
|
+
const parseIsoMonth = (monthArg: TIsoMonth): { year: number; month: number } => {
|
|
62
|
+
const match = typeof monthArg === 'string' ? /^(\d{4})-(\d{2})$/.exec(monthArg) : null;
|
|
63
|
+
const month = match ? Number(match[2]) : 0;
|
|
64
|
+
if (!match || month < 1 || month > 12) {
|
|
65
|
+
throw new HolidayCalendarError('invalid_month', `A month is written YYYY-MM; ${JSON.stringify(monthArg)} is not.`);
|
|
66
|
+
}
|
|
67
|
+
const year = Number(match[1]);
|
|
68
|
+
checkYear(year);
|
|
69
|
+
return { year, month };
|
|
70
|
+
};
|
|
71
|
+
|
|
60
72
|
const checkState = (stateArg: TGermanStateCode): void => {
|
|
61
73
|
if (!germanStates.some((stateEntry) => stateEntry.code === stateArg)) {
|
|
62
74
|
throw new HolidayCalendarError(
|
|
@@ -189,6 +201,15 @@ const isWorkingDayUtc = (utcArg: number, stateArg: TGermanStateCode, optionsArg:
|
|
|
189
201
|
return weekday !== 0 && weekday !== 6 && !isHolidayUtc(utcArg, stateArg, optionsArg);
|
|
190
202
|
};
|
|
191
203
|
|
|
204
|
+
/** 24 and 31 December, the days the Deutsche Bundesbank closes besides the statutory holidays */
|
|
205
|
+
const isBundesbankClosingDayUtc = (utcArg: number): boolean => {
|
|
206
|
+
const date = new Date(utcArg);
|
|
207
|
+
return date.getUTCMonth() === 11 && (date.getUTCDate() === 24 || date.getUTCDate() === 31);
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const isBankWorkingDayUtc = (utcArg: number, stateArg: TGermanStateCode, optionsArg: IWorkingDayOptions): boolean =>
|
|
211
|
+
isWorkingDayUtc(utcArg, stateArg, optionsArg) && !isBundesbankClosingDayUtc(utcArg);
|
|
212
|
+
|
|
192
213
|
/** Whether a date is a statutory public holiday in a state. */
|
|
193
214
|
export const isPublicHoliday = (
|
|
194
215
|
dateArg: TIsoDate,
|
|
@@ -212,6 +233,58 @@ export const isWorkingDay = (dateArg: TIsoDate, stateArg: TGermanStateCode, opti
|
|
|
212
233
|
return isWorkingDayUtc(day.utc, stateArg, optionsArg);
|
|
213
234
|
};
|
|
214
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Whether a date is a bank working day (Bankarbeitstag) in a state: neither a Saturday, nor a
|
|
238
|
+
* Sunday, nor a statutory public holiday, nor 24 or 31 December, on which the Deutsche Bundesbank
|
|
239
|
+
* is closed. These are the days the German social insurance counts the due dates of contributions
|
|
240
|
+
* in, with the holidays of the state where the collecting agency has its seat.
|
|
241
|
+
*/
|
|
242
|
+
export const isBankWorkingDay = (
|
|
243
|
+
dateArg: TIsoDate,
|
|
244
|
+
stateArg: TGermanStateCode,
|
|
245
|
+
optionsArg: IWorkingDayOptions,
|
|
246
|
+
): boolean => {
|
|
247
|
+
const day = parseIsoDate(dateArg);
|
|
248
|
+
checkState(stateArg);
|
|
249
|
+
checkOptions(optionsArg);
|
|
250
|
+
return isBankWorkingDayUtc(day.utc, stateArg, optionsArg);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* The bank working day of a month at a position counted from the month's end: 1 is the last bank
|
|
255
|
+
* working day of the month, 3 the third-last.
|
|
256
|
+
*/
|
|
257
|
+
export const nthLastBankWorkingDay = (
|
|
258
|
+
monthArg: TIsoMonth,
|
|
259
|
+
positionArg: number,
|
|
260
|
+
stateArg: TGermanStateCode,
|
|
261
|
+
optionsArg: IWorkingDayOptions,
|
|
262
|
+
): TIsoDate => {
|
|
263
|
+
const { year, month } = parseIsoMonth(monthArg);
|
|
264
|
+
checkState(stateArg);
|
|
265
|
+
checkOptions(optionsArg);
|
|
266
|
+
if (!Number.isInteger(positionArg) || positionArg < 1) {
|
|
267
|
+
throw new HolidayCalendarError(
|
|
268
|
+
'invalid_position',
|
|
269
|
+
`A position counted from the end of a month is a whole number from 1 on, not ${String(positionArg)}.`,
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
const first = utcOf(year, month, 1);
|
|
273
|
+
let found = 0;
|
|
274
|
+
for (let utc = utcOf(year, month + 1, 0); utc >= first; utc -= dayMs) {
|
|
275
|
+
if (isBankWorkingDayUtc(utc, stateArg, optionsArg)) {
|
|
276
|
+
found += 1;
|
|
277
|
+
if (found === positionArg) {
|
|
278
|
+
return toIsoDate(utc);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
throw new HolidayCalendarError(
|
|
283
|
+
'invalid_position',
|
|
284
|
+
`${monthArg} has ${found} bank working days in ${stateArg}, fewer than ${positionArg}.`,
|
|
285
|
+
);
|
|
286
|
+
};
|
|
287
|
+
|
|
215
288
|
/** The first working day after a date (§ 193 BGB: not a Saturday, Sunday or public holiday). */
|
|
216
289
|
export const nextWorkingDay = (dateArg: TIsoDate, stateArg: TGermanStateCode, optionsArg: IWorkingDayOptions): TIsoDate => {
|
|
217
290
|
const day = parseIsoDate(dateArg);
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Why a question to the holiday calendar was refused:
|
|
3
3
|
* - `invalid_date`: the value is not a calendar date written `YYYY-MM-DD`;
|
|
4
|
+
* - `invalid_month`: the value is not a calendar month written `YYYY-MM`;
|
|
5
|
+
* - `invalid_position`: the position of a bank working day counted from the end of a month is not
|
|
6
|
+
* a whole number from 1 up to the number of bank working days the month has;
|
|
4
7
|
* - `unsupported_year`: the year lies before the first year the calendar answers for (2018) or
|
|
5
8
|
* after 9999;
|
|
6
9
|
* - `unknown_state`: the value is not the code of a German federal state;
|
|
7
10
|
* - `invalid_options`: the options do not say how to treat regional holidays.
|
|
8
11
|
*/
|
|
9
|
-
export type THolidayCalendarErrorCode =
|
|
12
|
+
export type THolidayCalendarErrorCode =
|
|
13
|
+
| 'invalid_date'
|
|
14
|
+
| 'invalid_month'
|
|
15
|
+
| 'invalid_position'
|
|
16
|
+
| 'unsupported_year'
|
|
17
|
+
| 'unknown_state'
|
|
18
|
+
| 'invalid_options';
|
|
10
19
|
|
|
11
20
|
/** The typed error of the holiday calendar. */
|
|
12
21
|
export class HolidayCalendarError extends Error {
|
package/ts/holidays/index.ts
CHANGED
|
@@ -27,6 +27,9 @@ export interface IGermanState {
|
|
|
27
27
|
/** A plain calendar date, `YYYY-MM-DD`. */
|
|
28
28
|
export type TIsoDate = string;
|
|
29
29
|
|
|
30
|
+
/** A calendar month, `YYYY-MM`. */
|
|
31
|
+
export type TIsoMonth = string;
|
|
32
|
+
|
|
30
33
|
/** The statutory public holidays the calendar knows. */
|
|
31
34
|
export type TPublicHolidayId =
|
|
32
35
|
| 'neujahr'
|