@fintekkers/ledger-models 0.4.24 → 0.4.25
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/hierarchy.json +31 -4
- package/node/wrappers/models/security/asset_class_vocabulary.test.d.ts +1 -0
- package/node/wrappers/models/security/asset_class_vocabulary.test.js +146 -0
- package/node/wrappers/models/security/asset_class_vocabulary.test.js.map +1 -0
- package/node/wrappers/models/security/asset_class_vocabulary.test.ts +152 -0
- package/node/wrappers/models/security/product_hierarchy.d.ts +45 -0
- package/node/wrappers/models/security/product_hierarchy.js +126 -1
- package/node/wrappers/models/security/product_hierarchy.js.map +1 -1
- package/node/wrappers/models/security/product_hierarchy.ts +128 -0
- package/package.json +1 -1
package/hierarchy.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"schema_version": "1.
|
|
3
|
-
"_doc": "Single source of truth for the FinTekkers product hierarchy. See ../docs/product-hierarchy.md for narrative description, hierarchy-examples.md for worked examples, and registry-versioning.md for compatibility rules. Each leaf product_type carries (asset_class, instrument_type). Abstract nodes carry only hierarchy. status=active entries must have a matching ProductTypeProto enum value (CI-enforced). status=planned entries are aspirational and not yet wired into the proto enum. NOTE: M1 ships a 3-dimensional registry (productType + asset_class + instrument_type). The 4th dimension (index_type — single_name vs index) is intentionally deferred and will be added in a future milestone. Option strategy leaves (butterfly, vertical spread, calendar spread, condor, straddle, ...) are also intentionally absent — strategies are derived from the legs field on SecurityProto, not from a productType enum value.",
|
|
2
|
+
"schema_version": "1.1",
|
|
3
|
+
"_doc": "Single source of truth for the FinTekkers product hierarchy. See ../docs/product-hierarchy.md for narrative description, hierarchy-examples.md for worked examples, and registry-versioning.md for compatibility rules. Each leaf product_type carries (asset_class, instrument_type). Abstract nodes carry only hierarchy. status=active entries must have a matching ProductTypeProto enum value (CI-enforced). status=planned entries are aspirational and not yet wired into the proto enum. NOTE: M1 ships a 3-dimensional registry (productType + asset_class + instrument_type). The 4th dimension (index_type — single_name vs index) is intentionally deferred and will be added in a future milestone. Option strategy leaves (butterfly, vertical spread, calendar spread, condor, straddle, ...) are also intentionally absent — strategies are derived from the legs field on SecurityProto, not from a productType enum value. asset_classes is the one canonical asset-class vocabulary (codes and tree); an entry's optional aliases list names legacy values that resolve to it. enum_labels holds display labels (and identifier-type input placeholders) for proto enum values with no entry elsewhere, keyed by proto enum name then value name. See ../docs/adr/asset_class_vocabulary.md.",
|
|
4
4
|
|
|
5
5
|
"product_types": {
|
|
6
6
|
"BOND": { "parent": null, "abstract": true, "label": "Bond" },
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
"CREDIT": { "parent": "FIXED_INCOME", "label": "Credit" },
|
|
117
117
|
"EQUITY": { "parent": null, "label": "Equity" },
|
|
118
118
|
"VOLATILITY": { "parent": null, "label": "Volatility" },
|
|
119
|
-
"CASH": { "parent": null, "label": "Cash" },
|
|
119
|
+
"CASH": { "parent": null, "label": "Cash", "aliases": ["CASH_ASSET_CLASS"] },
|
|
120
120
|
"FX": { "parent": null, "label": "Foreign Exchange" },
|
|
121
121
|
"CRYPTO": { "parent": null, "label": "Crypto" },
|
|
122
122
|
"COMMODITY": { "parent": null, "label": "Commodity" },
|
|
@@ -127,5 +127,32 @@
|
|
|
127
127
|
"ALTERNATIVE": { "parent": null, "label": "Alternative" }
|
|
128
128
|
},
|
|
129
129
|
|
|
130
|
-
"instrument_types": ["CASH", "DERIVATIVE", "REFERENCE_INDEX"]
|
|
130
|
+
"instrument_types": ["CASH", "DERIVATIVE", "REFERENCE_INDEX"],
|
|
131
|
+
|
|
132
|
+
"enum_labels": {
|
|
133
|
+
"IdentifierTypeProto": {
|
|
134
|
+
"UNKNOWN_IDENTIFIER_TYPE": { "label": "Unknown", "placeholder": "Identifier" },
|
|
135
|
+
"EXCH_TICKER": { "label": "Exchange Ticker", "placeholder": "e.g. AAPL" },
|
|
136
|
+
"ISIN": { "label": "ISIN", "placeholder": "e.g. US0378331005" },
|
|
137
|
+
"CUSIP": { "label": "CUSIP", "placeholder": "e.g. 037833100" },
|
|
138
|
+
"OSI": { "label": "OSI Option Symbol", "placeholder": "e.g. AAPL 250117C00150000" },
|
|
139
|
+
"FIGI": { "label": "FIGI", "placeholder": "e.g. BBG000B9XRY4" },
|
|
140
|
+
"SERIES_ID": { "label": "Series ID", "placeholder": "e.g. CPIAUCSL" },
|
|
141
|
+
"INDEX_NAME": { "label": "Index Name", "placeholder": "e.g. SPX" },
|
|
142
|
+
"CASH": { "label": "Currency Code", "placeholder": "e.g. USD" }
|
|
143
|
+
},
|
|
144
|
+
"InstrumentTypeProto": {
|
|
145
|
+
"INSTRUMENT_TYPE_UNKNOWN": { "label": "Unknown" },
|
|
146
|
+
"INSTRUMENT_TYPE_CASH": { "label": "Cash" },
|
|
147
|
+
"INSTRUMENT_TYPE_DERIVATIVE": { "label": "Derivative" },
|
|
148
|
+
"INSTRUMENT_TYPE_REFERENCE_INDEX": { "label": "Reference Index" }
|
|
149
|
+
},
|
|
150
|
+
"ProductTypeProto": {
|
|
151
|
+
"PRODUCT_TYPE_UNKNOWN": { "label": "Unknown" }
|
|
152
|
+
},
|
|
153
|
+
"AssetClassProto": {
|
|
154
|
+
"UNKNOWN_ASSET_CLASS": { "label": "Unknown" },
|
|
155
|
+
"INDEX": { "label": "Index" }
|
|
156
|
+
}
|
|
157
|
+
}
|
|
131
158
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
/**
|
|
27
|
+
* LM-275: one asset-class vocabulary (hierarchy.json), a shared match helper
|
|
28
|
+
* and labels for every enum the UI shows.
|
|
29
|
+
*
|
|
30
|
+
* The match cases live in ledger-models-protos/fixtures/asset_class_matches.json,
|
|
31
|
+
* shared with the Java and Python tests.
|
|
32
|
+
*/
|
|
33
|
+
const fs = __importStar(require("fs"));
|
|
34
|
+
const path = __importStar(require("path"));
|
|
35
|
+
const product_hierarchy_1 = require("./product_hierarchy");
|
|
36
|
+
const asset_class_pb_1 = require("../../../fintekkers/models/security/asset_class_pb");
|
|
37
|
+
const identifier_type_pb_1 = require("../../../fintekkers/models/security/identifier/identifier_type_pb");
|
|
38
|
+
const instrument_type_pb_1 = require("../../../fintekkers/models/security/instrument_type_pb");
|
|
39
|
+
const product_type_pb_1 = require("../../../fintekkers/models/security/product_type_pb");
|
|
40
|
+
const PROTOS = path.resolve(__dirname, '../../../../../ledger-models-protos');
|
|
41
|
+
const rows = JSON.parse(fs.readFileSync(path.join(PROTOS, 'fixtures/asset_class_matches.json'), 'utf-8')).cases;
|
|
42
|
+
const hierarchy = JSON.parse(fs.readFileSync(path.join(PROTOS, 'hierarchy.json'), 'utf-8'));
|
|
43
|
+
describe('assetClassMatches shared fixture', () => {
|
|
44
|
+
test.each(rows)('assetClassMatches($filter, $stored) === $expected', ({ filter, stored, expected }) => {
|
|
45
|
+
expect((0, product_hierarchy_1.assetClassMatches)(filter, stored)).toBe(expected);
|
|
46
|
+
});
|
|
47
|
+
test('fixture has the required rows', () => {
|
|
48
|
+
expect(rows.length).toBeGreaterThanOrEqual(7);
|
|
49
|
+
const found = new Map(rows.map((r) => [`${r.filter}|${r.stored}`, r.expected]));
|
|
50
|
+
const required = [
|
|
51
|
+
['FIXED_INCOME|RATES', true],
|
|
52
|
+
['FIXED_INCOME|CREDIT', true],
|
|
53
|
+
['FIXED_INCOME|Fixed Income', true],
|
|
54
|
+
['EQUITY|Equity', true],
|
|
55
|
+
['CASH|Cash', true],
|
|
56
|
+
['EQUITY|RATES', false],
|
|
57
|
+
['EQUITY|NOT_AN_ASSET_CLASS', false],
|
|
58
|
+
];
|
|
59
|
+
for (const [key, expected] of required) {
|
|
60
|
+
expect([key, found.get(key)]).toEqual([key, expected]);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
describe('the tree comes from hierarchy.json', () => {
|
|
65
|
+
test('every asset_classes entry matches its parent, label and aliases', () => {
|
|
66
|
+
var _a;
|
|
67
|
+
const entries = Object.entries(hierarchy.asset_classes);
|
|
68
|
+
expect(entries.length).toBeGreaterThan(0);
|
|
69
|
+
for (const [code, entry] of entries) {
|
|
70
|
+
if (entry.parent)
|
|
71
|
+
expect([entry.parent, code, (0, product_hierarchy_1.assetClassMatches)(entry.parent, code)]).toEqual([entry.parent, code, true]);
|
|
72
|
+
expect([code, entry.label, (0, product_hierarchy_1.assetClassMatches)(code, entry.label)]).toEqual([code, entry.label, true]);
|
|
73
|
+
for (const alias of (_a = entry.aliases) !== null && _a !== void 0 ? _a : []) {
|
|
74
|
+
expect([code, alias, (0, product_hierarchy_1.assetClassMatches)(code, alias)]).toEqual([code, alias, true]);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
test('an ambiguous label fails at load', () => {
|
|
79
|
+
expect(() => (0, product_hierarchy_1.buildAssetClassLookup)({ A: { parent: null, label: 'Shared' }, B: { parent: null, label: 'shared' } })).toThrow();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
describe('labels for every enum value', () => {
|
|
83
|
+
const enums = [
|
|
84
|
+
['IdentifierTypeProto', identifier_type_pb_1.IdentifierTypeProto, product_hierarchy_1.identifierTypeLabelOf],
|
|
85
|
+
['InstrumentTypeProto', instrument_type_pb_1.InstrumentTypeProto, product_hierarchy_1.instrumentTypeLabelOf],
|
|
86
|
+
['ProductTypeProto', product_type_pb_1.ProductTypeProto, product_hierarchy_1.productTypeLabelOf],
|
|
87
|
+
['AssetClassProto', asset_class_pb_1.AssetClassProto, product_hierarchy_1.assetClassProtoLabelOf],
|
|
88
|
+
];
|
|
89
|
+
test.each(enums)('every %s value has a non-empty label', (name, enumObj, labelOf) => {
|
|
90
|
+
var _a;
|
|
91
|
+
const values = Object.entries(enumObj);
|
|
92
|
+
expect(values.length).toBeGreaterThan(0);
|
|
93
|
+
let checked = 0;
|
|
94
|
+
for (const [valueName, v] of values) {
|
|
95
|
+
expect([`${name}.${valueName}`, ((_a = labelOf(v)) !== null && _a !== void 0 ? _a : '').trim() !== '']).toEqual([`${name}.${valueName}`, true]);
|
|
96
|
+
checked++;
|
|
97
|
+
}
|
|
98
|
+
expect(checked).toBe(values.length);
|
|
99
|
+
});
|
|
100
|
+
test('every IdentifierTypeProto value has a non-empty placeholder', () => {
|
|
101
|
+
var _a;
|
|
102
|
+
const values = Object.entries(identifier_type_pb_1.IdentifierTypeProto);
|
|
103
|
+
for (const [valueName, v] of values) {
|
|
104
|
+
expect([valueName, ((_a = (0, product_hierarchy_1.identifierTypePlaceholderOf)(v)) !== null && _a !== void 0 ? _a : '').trim() !== '']).toEqual([valueName, true]);
|
|
105
|
+
}
|
|
106
|
+
expect(values.length).toBeGreaterThan(0);
|
|
107
|
+
});
|
|
108
|
+
test('CASH_ASSET_CLASS is labelled Cash', () => {
|
|
109
|
+
expect((0, product_hierarchy_1.assetClassProtoLabelOf)(asset_class_pb_1.AssetClassProto.CASH_ASSET_CLASS)).toBe('Cash');
|
|
110
|
+
});
|
|
111
|
+
test('out-of-range numbers return null', () => {
|
|
112
|
+
expect((0, product_hierarchy_1.identifierTypeLabelOf)(9999)).toBeNull();
|
|
113
|
+
expect((0, product_hierarchy_1.identifierTypePlaceholderOf)(9999)).toBeNull();
|
|
114
|
+
expect((0, product_hierarchy_1.instrumentTypeLabelOf)(9999)).toBeNull();
|
|
115
|
+
expect((0, product_hierarchy_1.productTypeLabelOf)(9999)).toBeNull();
|
|
116
|
+
expect((0, product_hierarchy_1.assetClassProtoLabelOf)(9999)).toBeNull();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
describe('the published (compiled) product_hierarchy.js', () => {
|
|
120
|
+
// npm publish runs no tsc: the tracked .js is what consumers get.
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
122
|
+
const compiled = require('./product_hierarchy.js');
|
|
123
|
+
test('exports the asset-class match helper', () => {
|
|
124
|
+
expect(typeof compiled.assetClassMatches).toBe('function');
|
|
125
|
+
expect(typeof compiled.resolveAssetClass).toBe('function');
|
|
126
|
+
expect(compiled.assetClassMatches('FIXED_INCOME', 'RATES')).toBe(true);
|
|
127
|
+
expect(compiled.assetClassMatches('EQUITY', 'RATES')).toBe(false);
|
|
128
|
+
});
|
|
129
|
+
test('exports the label and placeholder helpers', () => {
|
|
130
|
+
for (const fn of [
|
|
131
|
+
'identifierTypeLabelOf',
|
|
132
|
+
'identifierTypePlaceholderOf',
|
|
133
|
+
'instrumentTypeLabelOf',
|
|
134
|
+
'productTypeLabelOf',
|
|
135
|
+
'assetClassProtoLabelOf',
|
|
136
|
+
]) {
|
|
137
|
+
expect([fn, typeof compiled[fn]]).toEqual([fn, 'function']);
|
|
138
|
+
}
|
|
139
|
+
expect(compiled.identifierTypeLabelOf(identifier_type_pb_1.IdentifierTypeProto.ISIN)).toBe('ISIN');
|
|
140
|
+
expect(compiled.identifierTypePlaceholderOf(identifier_type_pb_1.IdentifierTypeProto.ISIN)).toBeTruthy();
|
|
141
|
+
expect(compiled.instrumentTypeLabelOf(instrument_type_pb_1.InstrumentTypeProto.INSTRUMENT_TYPE_CASH)).toBeTruthy();
|
|
142
|
+
expect(compiled.productTypeLabelOf(product_type_pb_1.ProductTypeProto.TBILL)).toBe('Treasury Bill');
|
|
143
|
+
expect(compiled.assetClassProtoLabelOf(asset_class_pb_1.AssetClassProto.CASH_ASSET_CLASS)).toBe('Cash');
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
//# sourceMappingURL=asset_class_vocabulary.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset_class_vocabulary.test.js","sourceRoot":"","sources":["asset_class_vocabulary.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LM-275: one asset-class vocabulary (hierarchy.json), a shared match helper
|
|
3
|
+
* and labels for every enum the UI shows.
|
|
4
|
+
*
|
|
5
|
+
* The match cases live in ledger-models-protos/fixtures/asset_class_matches.json,
|
|
6
|
+
* shared with the Java and Python tests.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
assetClassMatches,
|
|
13
|
+
assetClassProtoLabelOf,
|
|
14
|
+
buildAssetClassLookup,
|
|
15
|
+
identifierTypeLabelOf,
|
|
16
|
+
identifierTypePlaceholderOf,
|
|
17
|
+
instrumentTypeLabelOf,
|
|
18
|
+
productTypeLabelOf,
|
|
19
|
+
} from './product_hierarchy';
|
|
20
|
+
import { AssetClassProto } from '../../../fintekkers/models/security/asset_class_pb';
|
|
21
|
+
import { IdentifierTypeProto } from '../../../fintekkers/models/security/identifier/identifier_type_pb';
|
|
22
|
+
import { InstrumentTypeProto } from '../../../fintekkers/models/security/instrument_type_pb';
|
|
23
|
+
import { ProductTypeProto } from '../../../fintekkers/models/security/product_type_pb';
|
|
24
|
+
|
|
25
|
+
const PROTOS = path.resolve(__dirname, '../../../../../ledger-models-protos');
|
|
26
|
+
|
|
27
|
+
interface FixtureRow {
|
|
28
|
+
filter: string | null;
|
|
29
|
+
stored: string | null;
|
|
30
|
+
expected: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const rows: FixtureRow[] = JSON.parse(
|
|
34
|
+
fs.readFileSync(path.join(PROTOS, 'fixtures/asset_class_matches.json'), 'utf-8'),
|
|
35
|
+
).cases;
|
|
36
|
+
|
|
37
|
+
const hierarchy = JSON.parse(fs.readFileSync(path.join(PROTOS, 'hierarchy.json'), 'utf-8'));
|
|
38
|
+
|
|
39
|
+
describe('assetClassMatches shared fixture', () => {
|
|
40
|
+
test.each(rows)('assetClassMatches($filter, $stored) === $expected', ({ filter, stored, expected }) => {
|
|
41
|
+
expect(assetClassMatches(filter, stored)).toBe(expected);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('fixture has the required rows', () => {
|
|
45
|
+
expect(rows.length).toBeGreaterThanOrEqual(7);
|
|
46
|
+
const found = new Map(rows.map((r) => [`${r.filter}|${r.stored}`, r.expected]));
|
|
47
|
+
const required: Array<[string, boolean]> = [
|
|
48
|
+
['FIXED_INCOME|RATES', true],
|
|
49
|
+
['FIXED_INCOME|CREDIT', true],
|
|
50
|
+
['FIXED_INCOME|Fixed Income', true],
|
|
51
|
+
['EQUITY|Equity', true],
|
|
52
|
+
['CASH|Cash', true],
|
|
53
|
+
['EQUITY|RATES', false],
|
|
54
|
+
['EQUITY|NOT_AN_ASSET_CLASS', false],
|
|
55
|
+
];
|
|
56
|
+
for (const [key, expected] of required) {
|
|
57
|
+
expect([key, found.get(key)]).toEqual([key, expected]);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('the tree comes from hierarchy.json', () => {
|
|
63
|
+
test('every asset_classes entry matches its parent, label and aliases', () => {
|
|
64
|
+
const entries = Object.entries<{ parent: string | null; label: string; aliases?: string[] }>(
|
|
65
|
+
hierarchy.asset_classes,
|
|
66
|
+
);
|
|
67
|
+
expect(entries.length).toBeGreaterThan(0);
|
|
68
|
+
for (const [code, entry] of entries) {
|
|
69
|
+
if (entry.parent) expect([entry.parent, code, assetClassMatches(entry.parent, code)]).toEqual([entry.parent, code, true]);
|
|
70
|
+
expect([code, entry.label, assetClassMatches(code, entry.label)]).toEqual([code, entry.label, true]);
|
|
71
|
+
for (const alias of entry.aliases ?? []) {
|
|
72
|
+
expect([code, alias, assetClassMatches(code, alias)]).toEqual([code, alias, true]);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('an ambiguous label fails at load', () => {
|
|
78
|
+
expect(() =>
|
|
79
|
+
buildAssetClassLookup({ A: { parent: null, label: 'Shared' }, B: { parent: null, label: 'shared' } }),
|
|
80
|
+
).toThrow();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('labels for every enum value', () => {
|
|
85
|
+
const enums: Array<[string, object, (v: number) => string | null]> = [
|
|
86
|
+
['IdentifierTypeProto', IdentifierTypeProto, identifierTypeLabelOf],
|
|
87
|
+
['InstrumentTypeProto', InstrumentTypeProto, instrumentTypeLabelOf],
|
|
88
|
+
['ProductTypeProto', ProductTypeProto, productTypeLabelOf],
|
|
89
|
+
['AssetClassProto', AssetClassProto, assetClassProtoLabelOf],
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
test.each(enums)('every %s value has a non-empty label', (name, enumObj, labelOf) => {
|
|
93
|
+
const values = Object.entries(enumObj);
|
|
94
|
+
expect(values.length).toBeGreaterThan(0);
|
|
95
|
+
let checked = 0;
|
|
96
|
+
for (const [valueName, v] of values) {
|
|
97
|
+
expect([`${name}.${valueName}`, (labelOf(v) ?? '').trim() !== '']).toEqual([`${name}.${valueName}`, true]);
|
|
98
|
+
checked++;
|
|
99
|
+
}
|
|
100
|
+
expect(checked).toBe(values.length);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('every IdentifierTypeProto value has a non-empty placeholder', () => {
|
|
104
|
+
const values = Object.entries(IdentifierTypeProto);
|
|
105
|
+
for (const [valueName, v] of values) {
|
|
106
|
+
expect([valueName, (identifierTypePlaceholderOf(v as IdentifierTypeProto) ?? '').trim() !== '']).toEqual([valueName, true]);
|
|
107
|
+
}
|
|
108
|
+
expect(values.length).toBeGreaterThan(0);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('CASH_ASSET_CLASS is labelled Cash', () => {
|
|
112
|
+
expect(assetClassProtoLabelOf(AssetClassProto.CASH_ASSET_CLASS)).toBe('Cash');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('out-of-range numbers return null', () => {
|
|
116
|
+
expect(identifierTypeLabelOf(9999 as IdentifierTypeProto)).toBeNull();
|
|
117
|
+
expect(identifierTypePlaceholderOf(9999 as IdentifierTypeProto)).toBeNull();
|
|
118
|
+
expect(instrumentTypeLabelOf(9999 as InstrumentTypeProto)).toBeNull();
|
|
119
|
+
expect(productTypeLabelOf(9999 as ProductTypeProto)).toBeNull();
|
|
120
|
+
expect(assetClassProtoLabelOf(9999 as AssetClassProto)).toBeNull();
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('the published (compiled) product_hierarchy.js', () => {
|
|
125
|
+
// npm publish runs no tsc: the tracked .js is what consumers get.
|
|
126
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
127
|
+
const compiled = require('./product_hierarchy.js');
|
|
128
|
+
|
|
129
|
+
test('exports the asset-class match helper', () => {
|
|
130
|
+
expect(typeof compiled.assetClassMatches).toBe('function');
|
|
131
|
+
expect(typeof compiled.resolveAssetClass).toBe('function');
|
|
132
|
+
expect(compiled.assetClassMatches('FIXED_INCOME', 'RATES')).toBe(true);
|
|
133
|
+
expect(compiled.assetClassMatches('EQUITY', 'RATES')).toBe(false);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('exports the label and placeholder helpers', () => {
|
|
137
|
+
for (const fn of [
|
|
138
|
+
'identifierTypeLabelOf',
|
|
139
|
+
'identifierTypePlaceholderOf',
|
|
140
|
+
'instrumentTypeLabelOf',
|
|
141
|
+
'productTypeLabelOf',
|
|
142
|
+
'assetClassProtoLabelOf',
|
|
143
|
+
]) {
|
|
144
|
+
expect([fn, typeof compiled[fn]]).toEqual([fn, 'function']);
|
|
145
|
+
}
|
|
146
|
+
expect(compiled.identifierTypeLabelOf(IdentifierTypeProto.ISIN)).toBe('ISIN');
|
|
147
|
+
expect(compiled.identifierTypePlaceholderOf(IdentifierTypeProto.ISIN)).toBeTruthy();
|
|
148
|
+
expect(compiled.instrumentTypeLabelOf(InstrumentTypeProto.INSTRUMENT_TYPE_CASH)).toBeTruthy();
|
|
149
|
+
expect(compiled.productTypeLabelOf(ProductTypeProto.TBILL)).toBe('Treasury Bill');
|
|
150
|
+
expect(compiled.assetClassProtoLabelOf(AssetClassProto.CASH_ASSET_CLASS)).toBe('Cash');
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -13,9 +13,25 @@
|
|
|
13
13
|
* Plus per-leaf classification lookups: assetClassOf, instrumentTypeOf,
|
|
14
14
|
* labelOf.
|
|
15
15
|
*
|
|
16
|
+
* asset_classes is the one canonical asset-class vocabulary.
|
|
17
|
+
* assetClassMatches filters stored asset-class values (codes, labels such as
|
|
18
|
+
* "Fixed Income", or aliases such as "CASH_ASSET_CLASS") against a code,
|
|
19
|
+
* walking the tree. The *LabelOf / identifierTypePlaceholderOf helpers give
|
|
20
|
+
* display strings for every value of the enums the UI shows. See
|
|
21
|
+
* docs/adr/asset_class_vocabulary.md.
|
|
22
|
+
*
|
|
16
23
|
* `indexTypeOf` is intentionally absent — that dimension is deferred per
|
|
17
24
|
* the M1 descope.
|
|
18
25
|
*/
|
|
26
|
+
import { AssetClassProto } from '../../../fintekkers/models/security/asset_class_pb';
|
|
27
|
+
import { IdentifierTypeProto } from '../../../fintekkers/models/security/identifier/identifier_type_pb';
|
|
28
|
+
import { InstrumentTypeProto } from '../../../fintekkers/models/security/instrument_type_pb';
|
|
29
|
+
import { ProductTypeProto } from '../../../fintekkers/models/security/product_type_pb';
|
|
30
|
+
interface AssetClassEntry {
|
|
31
|
+
parent?: string | null;
|
|
32
|
+
label?: string;
|
|
33
|
+
aliases?: string[];
|
|
34
|
+
}
|
|
19
35
|
/** Parent product_type node (abstract or leaf). null for top-level nodes;
|
|
20
36
|
* null for unknown nodes. */
|
|
21
37
|
export declare function parentOf(node: string): string | null;
|
|
@@ -36,7 +52,36 @@ export declare function assetClassParentOf(node: string): string | null;
|
|
|
36
52
|
export declare function assetClassDescendantsOf(ancestor: string): string[];
|
|
37
53
|
export declare function isAssetClassDescendantOf(node: string, ancestor: string): boolean;
|
|
38
54
|
export declare function assetClassLabelOf(node: string): string | null;
|
|
55
|
+
/** Normalised code / label / alias -> code. Throws if two codes claim the
|
|
56
|
+
* same key, so an ambiguous label or alias fails at load time. Exported for
|
|
57
|
+
* tests only. */
|
|
58
|
+
export declare function buildAssetClassLookup(classes: Record<string, AssetClassEntry>): Map<string, string>;
|
|
59
|
+
/** Resolve a stored or user-supplied asset-class value to its hierarchy.json
|
|
60
|
+
* code. An exact code wins; otherwise the value is normalised (trimmed,
|
|
61
|
+
* upper-cased, runs of whitespace or hyphens turned into `_`) and matched
|
|
62
|
+
* against every code, label and alias. null for null, blank or unknown. */
|
|
63
|
+
export declare function resolveAssetClass(value: string | null | undefined): string | null;
|
|
64
|
+
/** True iff `storedValue` falls under `filterCode`: both resolve (see
|
|
65
|
+
* resolveAssetClass) and the stored code equals the filter code or descends
|
|
66
|
+
* from it. FIXED_INCOME matches RATES, CREDIT and "Fixed Income"; EQUITY
|
|
67
|
+
* does not match RATES; unknown values match nothing. */
|
|
68
|
+
export declare function assetClassMatches(filterCode: string | null | undefined, storedValue: string | null | undefined): boolean;
|
|
69
|
+
/** Display label for an IdentifierTypeProto value, or null if unknown. */
|
|
70
|
+
export declare function identifierTypeLabelOf(v: IdentifierTypeProto): string | null;
|
|
71
|
+
/** Input placeholder (e.g. "e.g. US0378331005") for an IdentifierTypeProto
|
|
72
|
+
* value, or null if unknown. */
|
|
73
|
+
export declare function identifierTypePlaceholderOf(v: IdentifierTypeProto): string | null;
|
|
74
|
+
/** Display label for an InstrumentTypeProto value, or null if unknown. */
|
|
75
|
+
export declare function instrumentTypeLabelOf(v: InstrumentTypeProto): string | null;
|
|
76
|
+
/** Display label for a ProductTypeProto value: the product_types label, else
|
|
77
|
+
* enum_labels (e.g. PRODUCT_TYPE_UNKNOWN). null if unknown. */
|
|
78
|
+
export declare function productTypeLabelOf(v: ProductTypeProto): string | null;
|
|
79
|
+
/** Display label for an AssetClassProto value: the label of the hierarchy.json
|
|
80
|
+
* code it resolves to (CASH_ASSET_CLASS -> "Cash"), else enum_labels
|
|
81
|
+
* (e.g. INDEX). null if unknown. */
|
|
82
|
+
export declare function assetClassProtoLabelOf(v: AssetClassProto): string | null;
|
|
39
83
|
export declare function allProductTypes(): string[];
|
|
40
84
|
export declare function activeProductTypes(): string[];
|
|
41
85
|
export declare function allAssetClasses(): string[];
|
|
42
86
|
export declare function allInstrumentTypes(): string[];
|
|
87
|
+
export {};
|
|
@@ -14,6 +14,13 @@
|
|
|
14
14
|
* Plus per-leaf classification lookups: assetClassOf, instrumentTypeOf,
|
|
15
15
|
* labelOf.
|
|
16
16
|
*
|
|
17
|
+
* asset_classes is the one canonical asset-class vocabulary.
|
|
18
|
+
* assetClassMatches filters stored asset-class values (codes, labels such as
|
|
19
|
+
* "Fixed Income", or aliases such as "CASH_ASSET_CLASS") against a code,
|
|
20
|
+
* walking the tree. The *LabelOf / identifierTypePlaceholderOf helpers give
|
|
21
|
+
* display strings for every value of the enums the UI shows. See
|
|
22
|
+
* docs/adr/asset_class_vocabulary.md.
|
|
23
|
+
*
|
|
17
24
|
* `indexTypeOf` is intentionally absent — that dimension is deferred per
|
|
18
25
|
* the M1 descope.
|
|
19
26
|
*/
|
|
@@ -21,12 +28,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
21
28
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
29
|
};
|
|
23
30
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
-
exports.allInstrumentTypes = exports.allAssetClasses = exports.activeProductTypes = exports.allProductTypes = exports.assetClassLabelOf = exports.isAssetClassDescendantOf = exports.assetClassDescendantsOf = exports.assetClassParentOf = exports.instrumentTypeOf = exports.assetClassOf = exports.labelOf = exports.isDescendantOf = exports.descendantsOf = exports.parentOf = void 0;
|
|
31
|
+
exports.allInstrumentTypes = exports.allAssetClasses = exports.activeProductTypes = exports.allProductTypes = exports.assetClassProtoLabelOf = exports.productTypeLabelOf = exports.instrumentTypeLabelOf = exports.identifierTypePlaceholderOf = exports.identifierTypeLabelOf = exports.assetClassMatches = exports.resolveAssetClass = exports.buildAssetClassLookup = exports.assetClassLabelOf = exports.isAssetClassDescendantOf = exports.assetClassDescendantsOf = exports.assetClassParentOf = exports.instrumentTypeOf = exports.assetClassOf = exports.labelOf = exports.isDescendantOf = exports.descendantsOf = exports.parentOf = void 0;
|
|
25
32
|
// hierarchy.json is bundled into the npm package via the package's `files`
|
|
26
33
|
// list (see package.json). Bundlers handle this `import` at build time
|
|
27
34
|
// (TypeScript's resolveJsonModule is enabled).
|
|
28
35
|
const hierarchy_json_1 = __importDefault(require("../../../../hierarchy.json"));
|
|
36
|
+
const asset_class_pb_1 = require("../../../fintekkers/models/security/asset_class_pb");
|
|
37
|
+
const identifier_type_pb_1 = require("../../../fintekkers/models/security/identifier/identifier_type_pb");
|
|
38
|
+
const instrument_type_pb_1 = require("../../../fintekkers/models/security/instrument_type_pb");
|
|
39
|
+
const product_type_pb_1 = require("../../../fintekkers/models/security/product_type_pb");
|
|
29
40
|
const registry = hierarchy_json_1.default;
|
|
41
|
+
function hasOwn(o, key) {
|
|
42
|
+
return o !== undefined && Object.prototype.hasOwnProperty.call(o, key);
|
|
43
|
+
}
|
|
30
44
|
// ---------- product_type tree ----------
|
|
31
45
|
/** Parent product_type node (abstract or leaf). null for top-level nodes;
|
|
32
46
|
* null for unknown nodes. */
|
|
@@ -134,6 +148,117 @@ function assetClassLabelOf(node) {
|
|
|
134
148
|
return (_b = (_a = registry.asset_classes[node]) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : null;
|
|
135
149
|
}
|
|
136
150
|
exports.assetClassLabelOf = assetClassLabelOf;
|
|
151
|
+
function normaliseAssetClass(value) {
|
|
152
|
+
return value.trim().toUpperCase().replace(/[\s-]+/g, '_');
|
|
153
|
+
}
|
|
154
|
+
/** Normalised code / label / alias -> code. Throws if two codes claim the
|
|
155
|
+
* same key, so an ambiguous label or alias fails at load time. Exported for
|
|
156
|
+
* tests only. */
|
|
157
|
+
function buildAssetClassLookup(classes) {
|
|
158
|
+
var _a;
|
|
159
|
+
const lookup = new Map();
|
|
160
|
+
for (const [code, entry] of Object.entries(classes)) {
|
|
161
|
+
const keys = [code, ...(entry.label ? [entry.label] : []), ...((_a = entry.aliases) !== null && _a !== void 0 ? _a : [])];
|
|
162
|
+
for (const key of keys) {
|
|
163
|
+
const n = normaliseAssetClass(key);
|
|
164
|
+
if (!n)
|
|
165
|
+
continue;
|
|
166
|
+
const prev = lookup.get(n);
|
|
167
|
+
if (prev !== undefined && prev !== code) {
|
|
168
|
+
throw new Error(`hierarchy.json asset_classes: '${key}' resolves to both ${prev} and ${code}`);
|
|
169
|
+
}
|
|
170
|
+
lookup.set(n, code);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return lookup;
|
|
174
|
+
}
|
|
175
|
+
exports.buildAssetClassLookup = buildAssetClassLookup;
|
|
176
|
+
const assetClassLookup = buildAssetClassLookup(registry.asset_classes);
|
|
177
|
+
/** Resolve a stored or user-supplied asset-class value to its hierarchy.json
|
|
178
|
+
* code. An exact code wins; otherwise the value is normalised (trimmed,
|
|
179
|
+
* upper-cased, runs of whitespace or hyphens turned into `_`) and matched
|
|
180
|
+
* against every code, label and alias. null for null, blank or unknown. */
|
|
181
|
+
function resolveAssetClass(value) {
|
|
182
|
+
var _a;
|
|
183
|
+
if (value === null || value === undefined)
|
|
184
|
+
return null;
|
|
185
|
+
if (hasOwn(registry.asset_classes, value))
|
|
186
|
+
return value;
|
|
187
|
+
return (_a = assetClassLookup.get(normaliseAssetClass(value))) !== null && _a !== void 0 ? _a : null;
|
|
188
|
+
}
|
|
189
|
+
exports.resolveAssetClass = resolveAssetClass;
|
|
190
|
+
/** True iff `storedValue` falls under `filterCode`: both resolve (see
|
|
191
|
+
* resolveAssetClass) and the stored code equals the filter code or descends
|
|
192
|
+
* from it. FIXED_INCOME matches RATES, CREDIT and "Fixed Income"; EQUITY
|
|
193
|
+
* does not match RATES; unknown values match nothing. */
|
|
194
|
+
function assetClassMatches(filterCode, storedValue) {
|
|
195
|
+
const f = resolveAssetClass(filterCode);
|
|
196
|
+
const s = resolveAssetClass(storedValue);
|
|
197
|
+
if (f === null || s === null)
|
|
198
|
+
return false;
|
|
199
|
+
return f === s || isAssetClassDescendantOf(s, f);
|
|
200
|
+
}
|
|
201
|
+
exports.assetClassMatches = assetClassMatches;
|
|
202
|
+
// ---------- enum labels ----------
|
|
203
|
+
/** Name of numeric enum value `v` in a generated *_pb enum object, or null. */
|
|
204
|
+
function enumValueName(enumObj, v) {
|
|
205
|
+
for (const [name, n] of Object.entries(enumObj)) {
|
|
206
|
+
if (n === v)
|
|
207
|
+
return name;
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
function enumEntry(enumName, valueName) {
|
|
212
|
+
var _a;
|
|
213
|
+
if (valueName === null)
|
|
214
|
+
return null;
|
|
215
|
+
const values = (_a = registry.enum_labels) === null || _a === void 0 ? void 0 : _a[enumName];
|
|
216
|
+
return values && hasOwn(values, valueName) ? values[valueName] : null;
|
|
217
|
+
}
|
|
218
|
+
/** Display label for an IdentifierTypeProto value, or null if unknown. */
|
|
219
|
+
function identifierTypeLabelOf(v) {
|
|
220
|
+
var _a, _b;
|
|
221
|
+
return (_b = (_a = enumEntry('IdentifierTypeProto', enumValueName(identifier_type_pb_1.IdentifierTypeProto, v))) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : null;
|
|
222
|
+
}
|
|
223
|
+
exports.identifierTypeLabelOf = identifierTypeLabelOf;
|
|
224
|
+
/** Input placeholder (e.g. "e.g. US0378331005") for an IdentifierTypeProto
|
|
225
|
+
* value, or null if unknown. */
|
|
226
|
+
function identifierTypePlaceholderOf(v) {
|
|
227
|
+
var _a, _b;
|
|
228
|
+
return (_b = (_a = enumEntry('IdentifierTypeProto', enumValueName(identifier_type_pb_1.IdentifierTypeProto, v))) === null || _a === void 0 ? void 0 : _a.placeholder) !== null && _b !== void 0 ? _b : null;
|
|
229
|
+
}
|
|
230
|
+
exports.identifierTypePlaceholderOf = identifierTypePlaceholderOf;
|
|
231
|
+
/** Display label for an InstrumentTypeProto value, or null if unknown. */
|
|
232
|
+
function instrumentTypeLabelOf(v) {
|
|
233
|
+
var _a, _b;
|
|
234
|
+
return (_b = (_a = enumEntry('InstrumentTypeProto', enumValueName(instrument_type_pb_1.InstrumentTypeProto, v))) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : null;
|
|
235
|
+
}
|
|
236
|
+
exports.instrumentTypeLabelOf = instrumentTypeLabelOf;
|
|
237
|
+
/** Display label for a ProductTypeProto value: the product_types label, else
|
|
238
|
+
* enum_labels (e.g. PRODUCT_TYPE_UNKNOWN). null if unknown. */
|
|
239
|
+
function productTypeLabelOf(v) {
|
|
240
|
+
var _a, _b, _c;
|
|
241
|
+
const name = enumValueName(product_type_pb_1.ProductTypeProto, v);
|
|
242
|
+
if (name === null)
|
|
243
|
+
return null;
|
|
244
|
+
const entry = hasOwn(registry.product_types, name) ? registry.product_types[name] : undefined;
|
|
245
|
+
return (_c = (_a = entry === null || entry === void 0 ? void 0 : entry.label) !== null && _a !== void 0 ? _a : (_b = enumEntry('ProductTypeProto', name)) === null || _b === void 0 ? void 0 : _b.label) !== null && _c !== void 0 ? _c : null;
|
|
246
|
+
}
|
|
247
|
+
exports.productTypeLabelOf = productTypeLabelOf;
|
|
248
|
+
/** Display label for an AssetClassProto value: the label of the hierarchy.json
|
|
249
|
+
* code it resolves to (CASH_ASSET_CLASS -> "Cash"), else enum_labels
|
|
250
|
+
* (e.g. INDEX). null if unknown. */
|
|
251
|
+
function assetClassProtoLabelOf(v) {
|
|
252
|
+
var _a, _b;
|
|
253
|
+
const name = enumValueName(asset_class_pb_1.AssetClassProto, v);
|
|
254
|
+
if (name === null)
|
|
255
|
+
return null;
|
|
256
|
+
const code = resolveAssetClass(name);
|
|
257
|
+
if (code !== null)
|
|
258
|
+
return assetClassLabelOf(code);
|
|
259
|
+
return (_b = (_a = enumEntry('AssetClassProto', name)) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : null;
|
|
260
|
+
}
|
|
261
|
+
exports.assetClassProtoLabelOf = assetClassProtoLabelOf;
|
|
137
262
|
// ---------- introspection ----------
|
|
138
263
|
function allProductTypes() {
|
|
139
264
|
return Object.keys(registry.product_types).sort();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"product_hierarchy.js","sourceRoot":"","sources":["product_hierarchy.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"product_hierarchy.js","sourceRoot":"","sources":["product_hierarchy.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,6 +13,13 @@
|
|
|
13
13
|
* Plus per-leaf classification lookups: assetClassOf, instrumentTypeOf,
|
|
14
14
|
* labelOf.
|
|
15
15
|
*
|
|
16
|
+
* asset_classes is the one canonical asset-class vocabulary.
|
|
17
|
+
* assetClassMatches filters stored asset-class values (codes, labels such as
|
|
18
|
+
* "Fixed Income", or aliases such as "CASH_ASSET_CLASS") against a code,
|
|
19
|
+
* walking the tree. The *LabelOf / identifierTypePlaceholderOf helpers give
|
|
20
|
+
* display strings for every value of the enums the UI shows. See
|
|
21
|
+
* docs/adr/asset_class_vocabulary.md.
|
|
22
|
+
*
|
|
16
23
|
* `indexTypeOf` is intentionally absent — that dimension is deferred per
|
|
17
24
|
* the M1 descope.
|
|
18
25
|
*/
|
|
@@ -21,6 +28,10 @@
|
|
|
21
28
|
// list (see package.json). Bundlers handle this `import` at build time
|
|
22
29
|
// (TypeScript's resolveJsonModule is enabled).
|
|
23
30
|
import hierarchy from '../../../../hierarchy.json';
|
|
31
|
+
import { AssetClassProto } from '../../../fintekkers/models/security/asset_class_pb';
|
|
32
|
+
import { IdentifierTypeProto } from '../../../fintekkers/models/security/identifier/identifier_type_pb';
|
|
33
|
+
import { InstrumentTypeProto } from '../../../fintekkers/models/security/instrument_type_pb';
|
|
34
|
+
import { ProductTypeProto } from '../../../fintekkers/models/security/product_type_pb';
|
|
24
35
|
|
|
25
36
|
interface ProductTypeEntry {
|
|
26
37
|
parent?: string | null;
|
|
@@ -34,16 +45,27 @@ interface ProductTypeEntry {
|
|
|
34
45
|
interface AssetClassEntry {
|
|
35
46
|
parent?: string | null;
|
|
36
47
|
label?: string;
|
|
48
|
+
aliases?: string[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface EnumLabelEntry {
|
|
52
|
+
label?: string;
|
|
53
|
+
placeholder?: string;
|
|
37
54
|
}
|
|
38
55
|
|
|
39
56
|
interface Registry {
|
|
40
57
|
product_types: Record<string, ProductTypeEntry>;
|
|
41
58
|
asset_classes: Record<string, AssetClassEntry>;
|
|
42
59
|
instrument_types: string[];
|
|
60
|
+
enum_labels?: Record<string, Record<string, EnumLabelEntry>>;
|
|
43
61
|
}
|
|
44
62
|
|
|
45
63
|
const registry: Registry = hierarchy as Registry;
|
|
46
64
|
|
|
65
|
+
function hasOwn(o: object | undefined, key: string): boolean {
|
|
66
|
+
return o !== undefined && Object.prototype.hasOwnProperty.call(o, key);
|
|
67
|
+
}
|
|
68
|
+
|
|
47
69
|
// ---------- product_type tree ----------
|
|
48
70
|
|
|
49
71
|
/** Parent product_type node (abstract or leaf). null for top-level nodes;
|
|
@@ -137,6 +159,112 @@ export function assetClassLabelOf(node: string): string | null {
|
|
|
137
159
|
return registry.asset_classes[node]?.label ?? null;
|
|
138
160
|
}
|
|
139
161
|
|
|
162
|
+
function normaliseAssetClass(value: string): string {
|
|
163
|
+
return value.trim().toUpperCase().replace(/[\s-]+/g, '_');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Normalised code / label / alias -> code. Throws if two codes claim the
|
|
167
|
+
* same key, so an ambiguous label or alias fails at load time. Exported for
|
|
168
|
+
* tests only. */
|
|
169
|
+
export function buildAssetClassLookup(
|
|
170
|
+
classes: Record<string, AssetClassEntry>,
|
|
171
|
+
): Map<string, string> {
|
|
172
|
+
const lookup = new Map<string, string>();
|
|
173
|
+
for (const [code, entry] of Object.entries(classes)) {
|
|
174
|
+
const keys = [code, ...(entry.label ? [entry.label] : []), ...(entry.aliases ?? [])];
|
|
175
|
+
for (const key of keys) {
|
|
176
|
+
const n = normaliseAssetClass(key);
|
|
177
|
+
if (!n) continue;
|
|
178
|
+
const prev = lookup.get(n);
|
|
179
|
+
if (prev !== undefined && prev !== code) {
|
|
180
|
+
throw new Error(
|
|
181
|
+
`hierarchy.json asset_classes: '${key}' resolves to both ${prev} and ${code}`,
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
lookup.set(n, code);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return lookup;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const assetClassLookup = buildAssetClassLookup(registry.asset_classes);
|
|
191
|
+
|
|
192
|
+
/** Resolve a stored or user-supplied asset-class value to its hierarchy.json
|
|
193
|
+
* code. An exact code wins; otherwise the value is normalised (trimmed,
|
|
194
|
+
* upper-cased, runs of whitespace or hyphens turned into `_`) and matched
|
|
195
|
+
* against every code, label and alias. null for null, blank or unknown. */
|
|
196
|
+
export function resolveAssetClass(value: string | null | undefined): string | null {
|
|
197
|
+
if (value === null || value === undefined) return null;
|
|
198
|
+
if (hasOwn(registry.asset_classes, value)) return value;
|
|
199
|
+
return assetClassLookup.get(normaliseAssetClass(value)) ?? null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** True iff `storedValue` falls under `filterCode`: both resolve (see
|
|
203
|
+
* resolveAssetClass) and the stored code equals the filter code or descends
|
|
204
|
+
* from it. FIXED_INCOME matches RATES, CREDIT and "Fixed Income"; EQUITY
|
|
205
|
+
* does not match RATES; unknown values match nothing. */
|
|
206
|
+
export function assetClassMatches(
|
|
207
|
+
filterCode: string | null | undefined,
|
|
208
|
+
storedValue: string | null | undefined,
|
|
209
|
+
): boolean {
|
|
210
|
+
const f = resolveAssetClass(filterCode);
|
|
211
|
+
const s = resolveAssetClass(storedValue);
|
|
212
|
+
if (f === null || s === null) return false;
|
|
213
|
+
return f === s || isAssetClassDescendantOf(s, f);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ---------- enum labels ----------
|
|
217
|
+
|
|
218
|
+
/** Name of numeric enum value `v` in a generated *_pb enum object, or null. */
|
|
219
|
+
function enumValueName(enumObj: object, v: number): string | null {
|
|
220
|
+
for (const [name, n] of Object.entries(enumObj)) {
|
|
221
|
+
if (n === v) return name;
|
|
222
|
+
}
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function enumEntry(enumName: string, valueName: string | null): EnumLabelEntry | null {
|
|
227
|
+
if (valueName === null) return null;
|
|
228
|
+
const values = registry.enum_labels?.[enumName];
|
|
229
|
+
return values && hasOwn(values, valueName) ? values[valueName] : null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** Display label for an IdentifierTypeProto value, or null if unknown. */
|
|
233
|
+
export function identifierTypeLabelOf(v: IdentifierTypeProto): string | null {
|
|
234
|
+
return enumEntry('IdentifierTypeProto', enumValueName(IdentifierTypeProto, v))?.label ?? null;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Input placeholder (e.g. "e.g. US0378331005") for an IdentifierTypeProto
|
|
238
|
+
* value, or null if unknown. */
|
|
239
|
+
export function identifierTypePlaceholderOf(v: IdentifierTypeProto): string | null {
|
|
240
|
+
return enumEntry('IdentifierTypeProto', enumValueName(IdentifierTypeProto, v))?.placeholder ?? null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Display label for an InstrumentTypeProto value, or null if unknown. */
|
|
244
|
+
export function instrumentTypeLabelOf(v: InstrumentTypeProto): string | null {
|
|
245
|
+
return enumEntry('InstrumentTypeProto', enumValueName(InstrumentTypeProto, v))?.label ?? null;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Display label for a ProductTypeProto value: the product_types label, else
|
|
249
|
+
* enum_labels (e.g. PRODUCT_TYPE_UNKNOWN). null if unknown. */
|
|
250
|
+
export function productTypeLabelOf(v: ProductTypeProto): string | null {
|
|
251
|
+
const name = enumValueName(ProductTypeProto, v);
|
|
252
|
+
if (name === null) return null;
|
|
253
|
+
const entry = hasOwn(registry.product_types, name) ? registry.product_types[name] : undefined;
|
|
254
|
+
return entry?.label ?? enumEntry('ProductTypeProto', name)?.label ?? null;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/** Display label for an AssetClassProto value: the label of the hierarchy.json
|
|
258
|
+
* code it resolves to (CASH_ASSET_CLASS -> "Cash"), else enum_labels
|
|
259
|
+
* (e.g. INDEX). null if unknown. */
|
|
260
|
+
export function assetClassProtoLabelOf(v: AssetClassProto): string | null {
|
|
261
|
+
const name = enumValueName(AssetClassProto, v);
|
|
262
|
+
if (name === null) return null;
|
|
263
|
+
const code = resolveAssetClass(name);
|
|
264
|
+
if (code !== null) return assetClassLabelOf(code);
|
|
265
|
+
return enumEntry('AssetClassProto', name)?.label ?? null;
|
|
266
|
+
}
|
|
267
|
+
|
|
140
268
|
// ---------- introspection ----------
|
|
141
269
|
|
|
142
270
|
export function allProductTypes(): string[] {
|