@fintekkers/ledger-models 0.1.113 → 0.1.115

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.
Files changed (36) hide show
  1. package/node/wrappers/models/security/BondSecurity.d.ts +12 -4
  2. package/node/wrappers/models/security/BondSecurity.js +46 -2
  3. package/node/wrappers/models/security/BondSecurity.js.map +1 -1
  4. package/node/wrappers/models/security/BondSecurity.test.d.ts +1 -0
  5. package/node/wrappers/models/security/BondSecurity.test.js +117 -0
  6. package/node/wrappers/models/security/BondSecurity.test.js.map +1 -0
  7. package/node/wrappers/models/security/BondSecurity.test.ts +132 -0
  8. package/node/wrappers/models/security/BondSecurity.ts +53 -6
  9. package/node/wrappers/models/security/coupon_frequency.d.ts +16 -0
  10. package/node/wrappers/models/security/coupon_frequency.js +30 -0
  11. package/node/wrappers/models/security/coupon_frequency.js.map +1 -0
  12. package/node/wrappers/models/security/coupon_frequency.test.d.ts +1 -0
  13. package/node/wrappers/models/security/coupon_frequency.test.js +15 -0
  14. package/node/wrappers/models/security/coupon_frequency.test.js.map +1 -0
  15. package/node/wrappers/models/security/coupon_frequency.test.ts +16 -0
  16. package/node/wrappers/models/security/coupon_frequency.ts +33 -0
  17. package/node/wrappers/models/security/coupon_type.d.ts +16 -0
  18. package/node/wrappers/models/security/coupon_type.js +30 -0
  19. package/node/wrappers/models/security/coupon_type.js.map +1 -0
  20. package/node/wrappers/models/security/coupon_type.test.d.ts +1 -0
  21. package/node/wrappers/models/security/coupon_type.test.js +13 -0
  22. package/node/wrappers/models/security/coupon_type.test.js.map +1 -0
  23. package/node/wrappers/models/security/coupon_type.test.ts +13 -0
  24. package/node/wrappers/models/security/coupon_type.ts +33 -0
  25. package/node/wrappers/models/security/security.js +3 -4
  26. package/node/wrappers/models/security/security.js.map +1 -1
  27. package/node/wrappers/models/security/security.ts +6 -4
  28. package/node/wrappers/models/security/term.d.ts +50 -0
  29. package/node/wrappers/models/security/term.js +157 -0
  30. package/node/wrappers/models/security/term.js.map +1 -0
  31. package/node/wrappers/models/security/term.test.d.ts +1 -0
  32. package/node/wrappers/models/security/term.test.js +253 -0
  33. package/node/wrappers/models/security/term.test.js.map +1 -0
  34. package/node/wrappers/models/security/term.test.ts +302 -0
  35. package/node/wrappers/models/security/term.ts +186 -0
  36. package/package.json +1 -1
@@ -0,0 +1,302 @@
1
+ import assert = require('assert');
2
+ import { Tenor, Period } from './term';
3
+ import { TenorTypeProto } from '../../../fintekkers/models/security/tenor_type_pb';
4
+
5
+ test('test Tenor.UNKNOWN_TENOR has correct type', () => {
6
+ testUnknownTenor();
7
+ });
8
+
9
+ test('test Tenor constructor with type only', () => {
10
+ testTenorConstructorTypeOnly();
11
+ });
12
+
13
+ test('test Tenor constructor with type and string term', () => {
14
+ testTenorConstructorWithString();
15
+ });
16
+
17
+ test('test Tenor constructor with type and Period', () => {
18
+ testTenorConstructorWithPeriod();
19
+ });
20
+
21
+ test('test Tenor constructor throws error for non-TERM type with Period', () => {
22
+ testTenorConstructorError();
23
+ });
24
+
25
+ test('test Tenor.getType() returns correct type', () => {
26
+ testGetType();
27
+ });
28
+
29
+ test('test Tenor.getTenor() returns correct period', () => {
30
+ testGetTenor();
31
+ });
32
+
33
+ test('test Tenor.getTenorDescription() returns correct string', () => {
34
+ testGetTenorDescription();
35
+ });
36
+
37
+ test('test Tenor.toString() returns correct representation', () => {
38
+ testToString();
39
+ });
40
+
41
+ test('test Tenor.fromTenorDescription() parses various formats', () => {
42
+ testFromTenorDescription();
43
+ });
44
+
45
+ test('test Tenor.periodToString() converts period to string', () => {
46
+ testPeriodToString();
47
+ });
48
+
49
+ test('test Tenor.parsePeriod() parses period strings', () => {
50
+ testParsePeriod();
51
+ });
52
+
53
+ test('test Tenor.parsePeriod() handles edge cases', () => {
54
+ testParsePeriodEdgeCases();
55
+ });
56
+
57
+ test('test Tenor.parsePeriod() throws error for invalid input', () => {
58
+ testParsePeriodErrors();
59
+ });
60
+
61
+ function testUnknownTenor(): void {
62
+ const unknownTenor = Tenor.UNKNOWN_TENOR;
63
+ assert(unknownTenor.getType() === TenorTypeProto.UNKNOWN_TENOR_TYPE, 'UNKNOWN_TENOR should have UNKNOWN_TENOR_TYPE');
64
+ assert(unknownTenor.getTenor() === null, 'UNKNOWN_TENOR should have null tenor');
65
+ }
66
+
67
+ function testTenorConstructorTypeOnly(): void {
68
+ const perpetualTenor = new Tenor(TenorTypeProto.PERPETUAL);
69
+ assert(perpetualTenor.getType() === TenorTypeProto.PERPETUAL, 'Should create PERPETUAL tenor');
70
+ assert(perpetualTenor.getTenor() === null, 'Should have null tenor when only type is provided');
71
+ }
72
+
73
+ function testTenorConstructorWithString(): void {
74
+ const termTenor = new Tenor(TenorTypeProto.TERM, '2Y3M');
75
+ assert(termTenor.getType() === TenorTypeProto.TERM, 'Should create TERM tenor');
76
+ const period = termTenor.getTenor();
77
+ assert(period !== null, 'Should have a period');
78
+ if (period) {
79
+ assert(period.years === 2, 'Should parse 2 years');
80
+ assert(period.months === 3, 'Should parse 3 months');
81
+ }
82
+ }
83
+
84
+ function testTenorConstructorWithPeriod(): void {
85
+ const period: Period = { years: 5, months: 6, days: 14 };
86
+ const termTenor = new Tenor(TenorTypeProto.TERM, period);
87
+ assert(termTenor.getType() === TenorTypeProto.TERM, 'Should create TERM tenor');
88
+ const returnedPeriod = termTenor.getTenor();
89
+ assert(returnedPeriod !== null, 'Should have a period');
90
+ if (returnedPeriod) {
91
+ assert(returnedPeriod.years === 5, 'Should have 5 years');
92
+ assert(returnedPeriod.months === 6, 'Should have 6 months');
93
+ assert(returnedPeriod.days === 14, 'Should have 14 days');
94
+ }
95
+ }
96
+
97
+ function testTenorConstructorError(): void {
98
+ const period: Period = { years: 1, months: 0, days: 0 };
99
+ try {
100
+ new Tenor(TenorTypeProto.PERPETUAL, period);
101
+ assert(false, 'Should have thrown an error');
102
+ } catch (error) {
103
+ assert(error instanceof Error, 'Should throw Error');
104
+ assert(error.message.includes('TERM'), 'Error message should mention TERM');
105
+ }
106
+ }
107
+
108
+ function testGetType(): void {
109
+ const tenor1 = new Tenor(TenorTypeProto.UNKNOWN_TENOR_TYPE);
110
+ assert(tenor1.getType() === TenorTypeProto.UNKNOWN_TENOR_TYPE, 'Should return UNKNOWN_TENOR_TYPE');
111
+
112
+ const tenor2 = new Tenor(TenorTypeProto.PERPETUAL);
113
+ assert(tenor2.getType() === TenorTypeProto.PERPETUAL, 'Should return PERPETUAL');
114
+
115
+ const tenor3 = new Tenor(TenorTypeProto.TERM, '1Y');
116
+ assert(tenor3.getType() === TenorTypeProto.TERM, 'Should return TERM');
117
+ }
118
+
119
+ function testGetTenor(): void {
120
+ const tenor1 = new Tenor(TenorTypeProto.PERPETUAL);
121
+ assert(tenor1.getTenor() === null, 'PERPETUAL should have null tenor');
122
+
123
+ const period: Period = { years: 3, months: 4, days: 21 };
124
+ const tenor2 = new Tenor(TenorTypeProto.TERM, period);
125
+ const returnedPeriod = tenor2.getTenor();
126
+ assert(returnedPeriod !== null, 'TERM should have a period');
127
+ if (returnedPeriod) {
128
+ assert(returnedPeriod.years === 3, 'Should return correct years');
129
+ assert(returnedPeriod.months === 4, 'Should return correct months');
130
+ assert(returnedPeriod.days === 21, 'Should return correct days');
131
+ }
132
+ }
133
+
134
+ function testGetTenorDescription(): void {
135
+ const tenor1 = new Tenor(TenorTypeProto.PERPETUAL);
136
+ assert(tenor1.getTenorDescription() === '', 'PERPETUAL should return empty string');
137
+
138
+ const tenor2 = new Tenor(TenorTypeProto.TERM, '2Y3M');
139
+ assert(tenor2.getTenorDescription() === '2Y3M', 'Should return "2Y3M"');
140
+
141
+ const tenor3 = new Tenor(TenorTypeProto.TERM, '1Y6M2W5D');
142
+ assert(tenor3.getTenorDescription() === '1Y6M2W5D', 'Should return "1Y6M2W5D"');
143
+ }
144
+
145
+ function testToString(): void {
146
+ const tenor1 = Tenor.UNKNOWN_TENOR;
147
+ assert(tenor1.toString() === 'UNKNOWN_TENOR_TYPE', 'UNKNOWN should return "UNKNOWN_TENOR_TYPE"');
148
+
149
+ const tenor2 = new Tenor(TenorTypeProto.PERPETUAL);
150
+ assert(tenor2.toString() === 'PERPETUAL', 'PERPETUAL should return "PERPETUAL"');
151
+
152
+ const tenor3 = new Tenor(TenorTypeProto.TERM, '2Y3M');
153
+ assert(tenor3.toString() === 'TERM: 2Y3M', 'TERM should return "TERM: 2Y3M"');
154
+ }
155
+
156
+ function testFromTenorDescription(): void {
157
+ // Test empty string
158
+ const period1 = Tenor.fromTenorDescription('');
159
+ assert(period1 === null, 'Empty string should return null');
160
+
161
+ // Test whitespace
162
+ const period2 = Tenor.fromTenorDescription(' ');
163
+ assert(period2 === null, 'Whitespace should return null');
164
+
165
+ // Test various formats
166
+ const period3 = Tenor.fromTenorDescription('2Y');
167
+ assert(period3 !== null, 'Should parse "2Y"');
168
+ if (period3) {
169
+ assert(period3.years === 2, 'Should have 2 years');
170
+ assert(period3.months === 0, 'Should have 0 months');
171
+ assert(period3.days === 0, 'Should have 0 days');
172
+ }
173
+
174
+ const period4 = Tenor.fromTenorDescription('3M');
175
+ assert(period4 !== null, 'Should parse "3M"');
176
+ if (period4) {
177
+ assert(period4.years === 0, 'Should have 0 years');
178
+ assert(period4.months === 3, 'Should have 3 months');
179
+ }
180
+
181
+ const period5 = Tenor.fromTenorDescription('2W');
182
+ assert(period5 !== null, 'Should parse "2W"');
183
+ if (period5) {
184
+ assert(period5.days === 14, 'Should convert 2 weeks to 14 days');
185
+ }
186
+
187
+ const period6 = Tenor.fromTenorDescription('5D');
188
+ assert(period6 !== null, 'Should parse "5D"');
189
+ if (period6) {
190
+ assert(period6.days === 5, 'Should have 5 days');
191
+ }
192
+ }
193
+
194
+ function testPeriodToString(): void {
195
+ // Test simple periods
196
+ const period1: Period = { years: 2, months: 3, days: 0 };
197
+ assert(Tenor.periodToString(period1) === '2Y3M', 'Should return "2Y3M"');
198
+
199
+ const period2: Period = { years: 0, months: 6, days: 14 };
200
+ assert(Tenor.periodToString(period2) === '6M2W', 'Should convert 14 days to 2W');
201
+
202
+ const period3: Period = { years: 1, months: 0, days: 21 };
203
+ assert(Tenor.periodToString(period3) === '1Y3W', 'Should convert 21 days to 3W');
204
+
205
+ const period4: Period = { years: 0, months: 0, days: 10 };
206
+ assert(Tenor.periodToString(period4) === '1W3D', 'Should convert 10 days to 1W3D');
207
+
208
+ // Test negative period
209
+ const period5: Period = { years: -1, months: 0, days: 0 };
210
+ assert(Tenor.periodToString(period5) === '', 'Negative period should return empty string');
211
+
212
+ // Test zero period
213
+ const period6: Period = { years: 0, months: 0, days: 0 };
214
+ assert(Tenor.periodToString(period6) === '', 'Zero period should return empty string');
215
+ }
216
+
217
+ function testParsePeriod(): void {
218
+ // Test years only
219
+ const period1 = Tenor.parsePeriod('2Y');
220
+ assert(period1.years === 2, 'Should parse 2 years');
221
+ assert(period1.months === 0, 'Should have 0 months');
222
+ assert(period1.days === 0, 'Should have 0 days');
223
+
224
+ // Test months only
225
+ const period2 = Tenor.parsePeriod('3M');
226
+ assert(period2.years === 0, 'Should have 0 years');
227
+ assert(period2.months === 3, 'Should parse 3 months');
228
+ assert(period2.days === 0, 'Should have 0 days');
229
+
230
+ // Test weeks only
231
+ const period3 = Tenor.parsePeriod('2W');
232
+ assert(period3.years === 0, 'Should have 0 years');
233
+ assert(period3.months === 0, 'Should have 0 months');
234
+ assert(period3.days === 14, 'Should convert 2 weeks to 14 days');
235
+
236
+ // Test days only
237
+ const period4 = Tenor.parsePeriod('5D');
238
+ assert(period4.years === 0, 'Should have 0 years');
239
+ assert(period4.months === 0, 'Should have 0 months');
240
+ assert(period4.days === 5, 'Should parse 5 days');
241
+
242
+ // Test combined
243
+ const period5 = Tenor.parsePeriod('1Y6M2W5D');
244
+ assert(period5.years === 1, 'Should parse 1 year');
245
+ assert(period5.months === 6, 'Should parse 6 months');
246
+ assert(period5.days === 19, 'Should convert 2W + 5D to 19 days');
247
+
248
+ // Test MW ambiguity (MW should be interpreted as months + weeks, not just months)
249
+ const period6 = Tenor.parsePeriod('2MW');
250
+ assert(period6.months === 0, 'MW should be interpreted as weeks, not months');
251
+ assert(period6.days === 14, 'MW should convert 2 weeks to 14 days');
252
+ }
253
+
254
+ function testParsePeriodEdgeCases(): void {
255
+ // Test large numbers
256
+ const period1 = Tenor.parsePeriod('10Y12M');
257
+ assert(period1.years === 10, 'Should parse 10 years');
258
+ assert(period1.months === 12, 'Should parse 12 months');
259
+
260
+ // Test single digit
261
+ const period2 = Tenor.parsePeriod('1Y1M1W1D');
262
+ assert(period2.years === 1, 'Should parse 1 year');
263
+ assert(period2.months === 1, 'Should parse 1 month');
264
+ assert(period2.days === 8, 'Should convert 1W + 1D to 8 days');
265
+
266
+ // Test multiple weeks
267
+ const period3 = Tenor.parsePeriod('4W');
268
+ assert(period3.days === 28, 'Should convert 4 weeks to 28 days');
269
+
270
+ // Test weeks and days
271
+ const period4 = Tenor.parsePeriod('2W3D');
272
+ assert(period4.days === 17, 'Should convert 2W + 3D to 17 days');
273
+ }
274
+
275
+ function testParsePeriodErrors(): void {
276
+ // Test invalid character
277
+ try {
278
+ Tenor.parsePeriod('2X');
279
+ assert(false, 'Should have thrown an error for invalid character');
280
+ } catch (error) {
281
+ assert(error instanceof Error, 'Should throw Error');
282
+ assert(error.message.includes('Invalid character'), 'Error message should mention invalid character');
283
+ }
284
+
285
+ // Test missing number before unit
286
+ try {
287
+ Tenor.parsePeriod('Y');
288
+ assert(false, 'Should have thrown an error for missing number');
289
+ } catch (error) {
290
+ assert(error instanceof Error, 'Should throw Error');
291
+ assert(error.message.includes('expected number'), 'Error message should mention expected number');
292
+ }
293
+
294
+ // Test invalid format
295
+ try {
296
+ Tenor.parsePeriod('2YM');
297
+ assert(false, 'Should have thrown an error for invalid format');
298
+ } catch (error) {
299
+ assert(error instanceof Error, 'Should throw Error');
300
+ }
301
+ }
302
+
@@ -0,0 +1,186 @@
1
+ import { TenorTypeProto } from '../../../fintekkers/models/security/tenor_type_pb';
2
+
3
+ /**
4
+ * Represents a period of time with years, months, and total days.
5
+ * Similar to Java's Period class. Days includes weeks converted to days.
6
+ */
7
+ export interface Period {
8
+ years: number;
9
+ months: number;
10
+ days: number; // Total days (includes weeks converted to days)
11
+ }
12
+
13
+ /**
14
+ * Tenor class representing the maturity or term of a security.
15
+ * Can be UNKNOWN, PERPETUAL, or TERM (with a specific period).
16
+ */
17
+ export class Tenor {
18
+ public static readonly UNKNOWN_TENOR = new Tenor(TenorTypeProto.UNKNOWN_TENOR_TYPE);
19
+
20
+ static tenorTypeEnumMap: Map<number, string>;
21
+
22
+ static {
23
+ Tenor.tenorTypeEnumMap = new Map<number, string>();
24
+
25
+ Object.keys(TenorTypeProto).forEach(key => {
26
+ Tenor.tenorTypeEnumMap.set(TenorTypeProto[key as keyof typeof TenorTypeProto], key);
27
+ });
28
+ }
29
+
30
+ private type: TenorTypeProto;
31
+ private tenor: Period | null;
32
+
33
+ constructor(type: TenorTypeProto);
34
+ constructor(type: TenorTypeProto, term: string);
35
+ constructor(type: TenorTypeProto, tenor: Period);
36
+ constructor(type: TenorTypeProto, termOrTenor?: string | Period) {
37
+ this.type = type;
38
+
39
+ if (termOrTenor === undefined) {
40
+ this.tenor = null;
41
+ } else if (typeof termOrTenor === 'string') {
42
+ this.tenor = Tenor.fromTenorDescription(termOrTenor);
43
+ } else {
44
+ if (type !== TenorTypeProto.TERM) {
45
+ throw new Error('Cannot instantiate a tenor with a term unless the TenorType is TERM');
46
+ }
47
+ this.tenor = termOrTenor;
48
+ }
49
+ }
50
+
51
+ getType(): TenorTypeProto {
52
+ return this.type;
53
+ }
54
+
55
+ getTenor(): Period | null {
56
+ return this.tenor;
57
+ }
58
+
59
+ getTenorDescription(): string {
60
+ const tenor = this.getTenor();
61
+ if (!tenor) {
62
+ return '';
63
+ }
64
+ return Tenor.periodToString(tenor);
65
+ }
66
+
67
+ toString(): string {
68
+ let str = Tenor.tenorTypeEnumMap.get(this.type) ?? 'UNKNOWN';
69
+
70
+ if (this.type === TenorTypeProto.TERM) {
71
+ str += ': ';
72
+ str += this.getTenorDescription();
73
+ }
74
+
75
+ return str;
76
+ }
77
+
78
+ /**
79
+ * Parses a tenor description string (e.g., "2Y3M") into a Period object.
80
+ * @param tenorDescription - String like "2Y3M", "1Y6M2W", "5D", etc.
81
+ * @returns Period object or null if the description is empty
82
+ */
83
+ public static fromTenorDescription(tenorDescription: string): Period | null {
84
+ if (!tenorDescription || tenorDescription.trim() === '') {
85
+ return null;
86
+ }
87
+
88
+ return Tenor.parsePeriod(tenorDescription);
89
+ }
90
+
91
+ /**
92
+ * Converts a Period object to a string representation (e.g., "2Y3M").
93
+ * @param period - Period object to convert
94
+ * @returns String representation like "2Y3M" or empty string if period is negative
95
+ */
96
+ public static periodToString(period: Period): string {
97
+ if (Tenor.isPeriodNegative(period)) {
98
+ return '';
99
+ }
100
+
101
+ const years = period.years;
102
+ const months = period.months;
103
+ // Extract weeks and remaining days from total days (matching Java behavior)
104
+ const weeks = Math.floor(period.days / 7);
105
+ const days = period.days % 7;
106
+
107
+ const parts: string[] = [];
108
+ if (years > 0) {
109
+ parts.push(`${years}Y`);
110
+ }
111
+ if (months > 0) {
112
+ parts.push(`${months}M`);
113
+ }
114
+ if (weeks > 0) {
115
+ parts.push(`${weeks}W`);
116
+ }
117
+ if (days > 0) {
118
+ parts.push(`${days}D`);
119
+ }
120
+
121
+ return parts.join('');
122
+ }
123
+
124
+ /**
125
+ * Checks if a period is negative (has any negative component).
126
+ */
127
+ private static isPeriodNegative(period: Period): boolean {
128
+ return period.years < 0 || period.months < 0 || period.days < 0;
129
+ }
130
+
131
+ /**
132
+ * Parses a period string (e.g., "2Y3M", "1Y6M2W5D") into a Period object.
133
+ * @param periodString - String like "2Y3M", "1Y6M2W", "5D", etc.
134
+ * @returns Period object
135
+ * @throws Error if the period string contains invalid characters
136
+ */
137
+ public static parsePeriod(periodString: string): Period {
138
+ let years = 0;
139
+ let months = 0;
140
+ let weeks = 0;
141
+ let days = 0;
142
+
143
+ let numberString = '';
144
+ for (let i = 0; i < periodString.length; i++) {
145
+ const c = periodString.charAt(i);
146
+ if (/\d/.test(c)) {
147
+ numberString += c;
148
+ } else {
149
+ if (numberString === '') {
150
+ throw new Error(`Invalid period string: expected number before '${c}'`);
151
+ }
152
+ const number = parseInt(numberString, 10);
153
+ switch (c) {
154
+ case 'Y':
155
+ years = number;
156
+ break;
157
+ case 'M':
158
+ // Check if next character is 'W' (for months-weeks ambiguity)
159
+ if (i < periodString.length - 1 && periodString.charAt(i + 1) === 'W') {
160
+ weeks = number;
161
+ i++; // Skip next character (W)
162
+ } else {
163
+ months = number;
164
+ }
165
+ break;
166
+ case 'W':
167
+ weeks = number;
168
+ break;
169
+ case 'D':
170
+ days = number;
171
+ break;
172
+ default:
173
+ throw new Error(`Invalid character in period string: ${c}`);
174
+ }
175
+ numberString = '';
176
+ }
177
+ }
178
+
179
+ // Convert weeks to days and combine with days (matching Java Period.of behavior)
180
+ return {
181
+ years,
182
+ months,
183
+ days: days + weeks * 7
184
+ };
185
+ }
186
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fintekkers/ledger-models",
3
3
  "todo": "Replace the version with build script version number",
4
- "version": "0.1.113",
4
+ "version": "0.1.115",
5
5
  "description": "ledger model protos ",
6
6
  "authors": [
7
7
  "David Doherty",