@fintekkers/ledger-models 0.1.113 → 0.1.114

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 (30) hide show
  1. package/node/wrappers/models/security/BondSecurity.d.ts +12 -4
  2. package/node/wrappers/models/security/BondSecurity.test.d.ts +1 -0
  3. package/node/wrappers/models/security/BondSecurity.test.js +117 -0
  4. package/node/wrappers/models/security/BondSecurity.test.js.map +1 -0
  5. package/node/wrappers/models/security/BondSecurity.test.ts +132 -0
  6. package/node/wrappers/models/security/BondSecurity.ts +53 -6
  7. package/node/wrappers/models/security/coupon_frequency.d.ts +16 -0
  8. package/node/wrappers/models/security/coupon_frequency.js +30 -0
  9. package/node/wrappers/models/security/coupon_frequency.js.map +1 -0
  10. package/node/wrappers/models/security/coupon_frequency.test.d.ts +1 -0
  11. package/node/wrappers/models/security/coupon_frequency.test.js +15 -0
  12. package/node/wrappers/models/security/coupon_frequency.test.js.map +1 -0
  13. package/node/wrappers/models/security/coupon_frequency.test.ts +16 -0
  14. package/node/wrappers/models/security/coupon_frequency.ts +33 -0
  15. package/node/wrappers/models/security/coupon_type.d.ts +16 -0
  16. package/node/wrappers/models/security/coupon_type.js +30 -0
  17. package/node/wrappers/models/security/coupon_type.js.map +1 -0
  18. package/node/wrappers/models/security/coupon_type.test.d.ts +1 -0
  19. package/node/wrappers/models/security/coupon_type.test.js +13 -0
  20. package/node/wrappers/models/security/coupon_type.test.js.map +1 -0
  21. package/node/wrappers/models/security/coupon_type.test.ts +13 -0
  22. package/node/wrappers/models/security/coupon_type.ts +33 -0
  23. package/node/wrappers/models/security/security.js +3 -4
  24. package/node/wrappers/models/security/security.js.map +1 -1
  25. package/node/wrappers/models/security/security.ts +6 -4
  26. package/node/wrappers/models/security/term.test.ts +302 -0
  27. package/node/wrappers/models/security/term.ts +186 -0
  28. package/package.json +1 -1
  29. package/node/wrappers/models/security/BondSecurity.js +0 -43
  30. package/node/wrappers/models/security/BondSecurity.js.map +0 -1
@@ -1,16 +1,24 @@
1
1
  import Security from './security';
2
2
  import { SecurityProto } from '../../../fintekkers/models/security/security_pb';
3
3
  import { DecimalValueProto } from '../../../fintekkers/models/util/decimal_value_pb';
4
- import { CouponTypeProto } from '../../../fintekkers/models/security/coupon_type_pb';
5
- import { CouponFrequencyProto } from '../../../fintekkers/models/security/coupon_frequency_pb';
6
4
  import { LocalDate } from '../utils/date';
7
5
  import { IssuanceProto } from '../../../fintekkers/models/security/bond/issuance_pb';
6
+ import { CouponFrequency } from './coupon_frequency';
7
+ import { CouponType } from './coupon_type';
8
+ import { Tenor } from './term';
8
9
  declare class BondSecurity extends Security {
9
10
  constructor(proto: SecurityProto);
11
+ /** Returns the tenor (term) of the bond as a Tenor object */
12
+ getTenor(): Tenor;
13
+ /**
14
+ * Calculates the period between two dates in years, months, and days.
15
+ * This method handles month and year boundaries correctly.
16
+ */
17
+ private calculatePeriod;
10
18
  getCouponRate(): DecimalValueProto;
11
19
  getFaceValue(): DecimalValueProto;
12
- getCouponType(): CouponTypeProto;
13
- getCouponFrequency(): CouponFrequencyProto;
20
+ getCouponType(): CouponType;
21
+ getCouponFrequency(): CouponFrequency;
14
22
  getDatedDate(): LocalDate | undefined;
15
23
  getIssuanceInfo(): IssuanceProto[];
16
24
  }
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const assert = require("assert");
7
+ const security_1 = __importDefault(require("./security"));
8
+ const BondSecurity_1 = __importDefault(require("./BondSecurity"));
9
+ const security_pb_1 = require("../../../fintekkers/models/security/security_pb");
10
+ const security_type_pb_1 = require("../../../fintekkers/models/security/security_type_pb");
11
+ const coupon_type_pb_1 = require("../../../fintekkers/models/security/coupon_type_pb");
12
+ const coupon_frequency_pb_1 = require("../../../fintekkers/models/security/coupon_frequency_pb");
13
+ const local_date_pb_1 = require("../../../fintekkers/models/util/local_date_pb");
14
+ const decimal_value_pb_1 = require("../../../fintekkers/models/util/decimal_value_pb");
15
+ const uuid_1 = require("../utils/uuid");
16
+ const tenor_type_pb_1 = require("../../../fintekkers/models/security/tenor_type_pb");
17
+ test('test Security.create returns BondSecurity with correct coupon type and frequency', () => {
18
+ testBondSecurityCreation();
19
+ });
20
+ test('test BondSecurity.getTenor() returns correct Tenor for 10-year bond', () => {
21
+ testBondSecurityTenor();
22
+ });
23
+ function testBondSecurityCreation() {
24
+ // Create a SecurityProto with BOND_SECURITY type, coupon type, and coupon frequency
25
+ const securityProto = new security_pb_1.SecurityProto();
26
+ securityProto.setObjectClass('Security');
27
+ securityProto.setVersion('0.0.1');
28
+ securityProto.setUuid(uuid_1.UUID.random().toUUIDProto());
29
+ securityProto.setSecurityType(security_type_pb_1.SecurityTypeProto.BOND_SECURITY);
30
+ securityProto.setAssetClass('FixedIncome');
31
+ securityProto.setIssuerName('Test Issuer');
32
+ securityProto.setCouponType(coupon_type_pb_1.CouponTypeProto.FIXED);
33
+ securityProto.setCouponFrequency(coupon_frequency_pb_1.CouponFrequencyProto.SEMIANNUALLY);
34
+ securityProto.setCouponRate(new decimal_value_pb_1.DecimalValueProto().setArbitraryPrecisionValue('0.05'));
35
+ securityProto.setFaceValue(new decimal_value_pb_1.DecimalValueProto().setArbitraryPrecisionValue('1000.00'));
36
+ securityProto.setIssueDate(new local_date_pb_1.LocalDateProto().setYear(2021).setMonth(1).setDay(1));
37
+ securityProto.setMaturityDate(new local_date_pb_1.LocalDateProto().setYear(2031).setMonth(1).setDay(1));
38
+ securityProto.setDescription('Test Bond Security');
39
+ // Call Security.create and validate it returns a BondSecurity
40
+ const security = security_1.default.create(securityProto);
41
+ assert(security instanceof BondSecurity_1.default, 'Security.create should return a BondSecurity instance');
42
+ // Cast to BondSecurity for type safety
43
+ const bondSecurity = security;
44
+ // Validate coupon type
45
+ const couponType = bondSecurity.getCouponType();
46
+ assert(couponType.name() === 'FIXED', `Expected coupon type FIXED, got ${couponType.name()}`);
47
+ // Validate coupon frequency
48
+ const couponFrequency = bondSecurity.getCouponFrequency();
49
+ assert(couponFrequency.toString() === 'SEMIANNUALLY', `Expected coupon frequency SEMIANNUALLY, got ${couponFrequency.toString()}`);
50
+ }
51
+ function testBondSecurityTenor() {
52
+ // Create a SecurityProto with BOND_SECURITY type and dates for a 10-year bond
53
+ const securityProto = new security_pb_1.SecurityProto();
54
+ securityProto.setObjectClass('Security');
55
+ securityProto.setVersion('0.0.1');
56
+ securityProto.setUuid(uuid_1.UUID.random().toUUIDProto());
57
+ securityProto.setSecurityType(security_type_pb_1.SecurityTypeProto.BOND_SECURITY);
58
+ securityProto.setAssetClass('FixedIncome');
59
+ securityProto.setIssuerName('Test Issuer');
60
+ securityProto.setCouponType(coupon_type_pb_1.CouponTypeProto.FIXED);
61
+ securityProto.setCouponFrequency(coupon_frequency_pb_1.CouponFrequencyProto.SEMIANNUALLY);
62
+ securityProto.setCouponRate(new decimal_value_pb_1.DecimalValueProto().setArbitraryPrecisionValue('0.05'));
63
+ securityProto.setFaceValue(new decimal_value_pb_1.DecimalValueProto().setArbitraryPrecisionValue('1000.00'));
64
+ // Set issue date: January 1, 2021
65
+ securityProto.setIssueDate(new local_date_pb_1.LocalDateProto().setYear(2021).setMonth(1).setDay(1));
66
+ // Set maturity date: January 1, 2031 (exactly 10 years later)
67
+ securityProto.setMaturityDate(new local_date_pb_1.LocalDateProto().setYear(2031).setMonth(1).setDay(1));
68
+ securityProto.setDescription('Test 10-Year Bond Security');
69
+ // Create BondSecurity
70
+ const security = security_1.default.create(securityProto);
71
+ assert(security instanceof BondSecurity_1.default, 'Security.create should return a BondSecurity instance');
72
+ const bondSecurity = security;
73
+ // Test getTenor()
74
+ const tenor = bondSecurity.getTenor();
75
+ assert(tenor !== null, 'Tenor should not be null');
76
+ assert(tenor.getType() === tenor_type_pb_1.TenorTypeProto.TERM, 'Tenor type should be TERM');
77
+ const period = tenor.getTenor();
78
+ assert(period !== null, 'Period should not be null');
79
+ if (period) {
80
+ assert(period.years === 10, `Expected 10 years, got ${period.years}`);
81
+ assert(period.months === 0, `Expected 0 months, got ${period.months}`);
82
+ assert(period.days === 0, `Expected 0 days, got ${period.days}`);
83
+ }
84
+ // Test tenor description
85
+ const tenorDescription = tenor.getTenorDescription();
86
+ assert(tenorDescription === '10Y', `Expected tenor description "10Y", got "${tenorDescription}"`);
87
+ // Test toString()
88
+ const tenorString = tenor.toString();
89
+ assert(tenorString === 'TERM: 10Y', `Expected "TERM: 10Y", got "${tenorString}"`);
90
+ // Test with a bond that has months and days
91
+ const securityProto2 = new security_pb_1.SecurityProto();
92
+ securityProto2.setObjectClass('Security');
93
+ securityProto2.setVersion('0.0.1');
94
+ securityProto2.setUuid(uuid_1.UUID.random().toUUIDProto());
95
+ securityProto2.setSecurityType(security_type_pb_1.SecurityTypeProto.BOND_SECURITY);
96
+ securityProto2.setAssetClass('FixedIncome');
97
+ securityProto2.setIssuerName('Test Issuer');
98
+ securityProto2.setCouponType(coupon_type_pb_1.CouponTypeProto.FIXED);
99
+ securityProto2.setCouponFrequency(coupon_frequency_pb_1.CouponFrequencyProto.SEMIANNUALLY);
100
+ securityProto2.setCouponRate(new decimal_value_pb_1.DecimalValueProto().setArbitraryPrecisionValue('0.05'));
101
+ securityProto2.setFaceValue(new decimal_value_pb_1.DecimalValueProto().setArbitraryPrecisionValue('1000.00'));
102
+ // Set issue date: January 15, 2021
103
+ securityProto2.setIssueDate(new local_date_pb_1.LocalDateProto().setYear(2021).setMonth(1).setDay(15));
104
+ // Set maturity date: July 20, 2023 (2 years, 6 months, 5 days)
105
+ securityProto2.setMaturityDate(new local_date_pb_1.LocalDateProto().setYear(2023).setMonth(7).setDay(20));
106
+ securityProto2.setDescription('Test Bond with Months and Days');
107
+ const bondSecurity2 = security_1.default.create(securityProto2);
108
+ const tenor2 = bondSecurity2.getTenor();
109
+ const period2 = tenor2.getTenor();
110
+ if (period2) {
111
+ assert(period2.years === 2, `Expected 2 years, got ${period2.years}`);
112
+ assert(period2.months === 6, `Expected 6 months, got ${period2.months}`);
113
+ assert(period2.days === 5, `Expected 5 days, got ${period2.days}`);
114
+ }
115
+ assert(tenor2.toString() === 'TERM: 2Y6M5D', `Expected "TERM: 2Y6M5D", got "${tenor2.toString()}"`);
116
+ }
117
+ //# sourceMappingURL=BondSecurity.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BondSecurity.test.js","sourceRoot":"","sources":["BondSecurity.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,132 @@
1
+ import assert = require('assert');
2
+ import Security from './security';
3
+ import BondSecurity from './BondSecurity';
4
+ import { SecurityProto } from '../../../fintekkers/models/security/security_pb';
5
+ import { SecurityTypeProto } from '../../../fintekkers/models/security/security_type_pb';
6
+ import { CouponTypeProto } from '../../../fintekkers/models/security/coupon_type_pb';
7
+ import { CouponFrequencyProto } from '../../../fintekkers/models/security/coupon_frequency_pb';
8
+ import { LocalDateProto } from '../../../fintekkers/models/util/local_date_pb';
9
+ import { DecimalValueProto } from '../../../fintekkers/models/util/decimal_value_pb';
10
+ import { UUID } from '../utils/uuid';
11
+ import { CouponType } from './coupon_type';
12
+ import { TenorTypeProto } from '../../../fintekkers/models/security/tenor_type_pb';
13
+
14
+ test('test Security.create returns BondSecurity with correct coupon type and frequency', () => {
15
+ testBondSecurityCreation();
16
+ });
17
+
18
+ test('test BondSecurity.getTenor() returns correct Tenor for 10-year bond', () => {
19
+ testBondSecurityTenor();
20
+ });
21
+
22
+ function testBondSecurityCreation(): void {
23
+ // Create a SecurityProto with BOND_SECURITY type, coupon type, and coupon frequency
24
+ const securityProto = new SecurityProto();
25
+ securityProto.setObjectClass('Security');
26
+ securityProto.setVersion('0.0.1');
27
+ securityProto.setUuid(UUID.random().toUUIDProto());
28
+ securityProto.setSecurityType(SecurityTypeProto.BOND_SECURITY);
29
+ securityProto.setAssetClass('FixedIncome');
30
+ securityProto.setIssuerName('Test Issuer');
31
+ securityProto.setCouponType(CouponTypeProto.FIXED);
32
+ securityProto.setCouponFrequency(CouponFrequencyProto.SEMIANNUALLY);
33
+ securityProto.setCouponRate(new DecimalValueProto().setArbitraryPrecisionValue('0.05'));
34
+ securityProto.setFaceValue(new DecimalValueProto().setArbitraryPrecisionValue('1000.00'));
35
+ securityProto.setIssueDate(new LocalDateProto().setYear(2021).setMonth(1).setDay(1));
36
+ securityProto.setMaturityDate(new LocalDateProto().setYear(2031).setMonth(1).setDay(1));
37
+ securityProto.setDescription('Test Bond Security');
38
+
39
+ // Call Security.create and validate it returns a BondSecurity
40
+ const security = Security.create(securityProto);
41
+ assert(security instanceof BondSecurity, 'Security.create should return a BondSecurity instance');
42
+
43
+ // Cast to BondSecurity for type safety
44
+ const bondSecurity = security as BondSecurity;
45
+
46
+ // Validate coupon type
47
+ const couponType = bondSecurity.getCouponType();
48
+ assert(couponType.name() === 'FIXED', `Expected coupon type FIXED, got ${couponType.name()}`);
49
+
50
+ // Validate coupon frequency
51
+ const couponFrequency = bondSecurity.getCouponFrequency();
52
+ assert(couponFrequency.toString() === 'SEMIANNUALLY', `Expected coupon frequency SEMIANNUALLY, got ${couponFrequency.toString()}`);
53
+ }
54
+
55
+ function testBondSecurityTenor(): void {
56
+ // Create a SecurityProto with BOND_SECURITY type and dates for a 10-year bond
57
+ const securityProto = new SecurityProto();
58
+ securityProto.setObjectClass('Security');
59
+ securityProto.setVersion('0.0.1');
60
+ securityProto.setUuid(UUID.random().toUUIDProto());
61
+ securityProto.setSecurityType(SecurityTypeProto.BOND_SECURITY);
62
+ securityProto.setAssetClass('FixedIncome');
63
+ securityProto.setIssuerName('Test Issuer');
64
+ securityProto.setCouponType(CouponTypeProto.FIXED);
65
+ securityProto.setCouponFrequency(CouponFrequencyProto.SEMIANNUALLY);
66
+ securityProto.setCouponRate(new DecimalValueProto().setArbitraryPrecisionValue('0.05'));
67
+ securityProto.setFaceValue(new DecimalValueProto().setArbitraryPrecisionValue('1000.00'));
68
+
69
+ // Set issue date: January 1, 2021
70
+ securityProto.setIssueDate(new LocalDateProto().setYear(2021).setMonth(1).setDay(1));
71
+ // Set maturity date: January 1, 2031 (exactly 10 years later)
72
+ securityProto.setMaturityDate(new LocalDateProto().setYear(2031).setMonth(1).setDay(1));
73
+ securityProto.setDescription('Test 10-Year Bond Security');
74
+
75
+ // Create BondSecurity
76
+ const security = Security.create(securityProto);
77
+ assert(security instanceof BondSecurity, 'Security.create should return a BondSecurity instance');
78
+ const bondSecurity = security as BondSecurity;
79
+
80
+ // Test getTenor()
81
+ const tenor = bondSecurity.getTenor();
82
+ assert(tenor !== null, 'Tenor should not be null');
83
+ assert(tenor.getType() === TenorTypeProto.TERM, 'Tenor type should be TERM');
84
+
85
+ const period = tenor.getTenor();
86
+ assert(period !== null, 'Period should not be null');
87
+ if (period) {
88
+ assert(period.years === 10, `Expected 10 years, got ${period.years}`);
89
+ assert(period.months === 0, `Expected 0 months, got ${period.months}`);
90
+ assert(period.days === 0, `Expected 0 days, got ${period.days}`);
91
+ }
92
+
93
+ // Test tenor description
94
+ const tenorDescription = tenor.getTenorDescription();
95
+ assert(tenorDescription === '10Y', `Expected tenor description "10Y", got "${tenorDescription}"`);
96
+
97
+ // Test toString()
98
+ const tenorString = tenor.toString();
99
+ assert(tenorString === 'TERM: 10Y', `Expected "TERM: 10Y", got "${tenorString}"`);
100
+
101
+ // Test with a bond that has months and days
102
+ const securityProto2 = new SecurityProto();
103
+ securityProto2.setObjectClass('Security');
104
+ securityProto2.setVersion('0.0.1');
105
+ securityProto2.setUuid(UUID.random().toUUIDProto());
106
+ securityProto2.setSecurityType(SecurityTypeProto.BOND_SECURITY);
107
+ securityProto2.setAssetClass('FixedIncome');
108
+ securityProto2.setIssuerName('Test Issuer');
109
+ securityProto2.setCouponType(CouponTypeProto.FIXED);
110
+ securityProto2.setCouponFrequency(CouponFrequencyProto.SEMIANNUALLY);
111
+ securityProto2.setCouponRate(new DecimalValueProto().setArbitraryPrecisionValue('0.05'));
112
+ securityProto2.setFaceValue(new DecimalValueProto().setArbitraryPrecisionValue('1000.00'));
113
+
114
+ // Set issue date: January 15, 2021
115
+ securityProto2.setIssueDate(new LocalDateProto().setYear(2021).setMonth(1).setDay(15));
116
+ // Set maturity date: July 20, 2023 (2 years, 6 months, 5 days)
117
+ securityProto2.setMaturityDate(new LocalDateProto().setYear(2023).setMonth(7).setDay(20));
118
+ securityProto2.setDescription('Test Bond with Months and Days');
119
+
120
+ const bondSecurity2 = Security.create(securityProto2) as BondSecurity;
121
+ const tenor2 = bondSecurity2.getTenor();
122
+ const period2 = tenor2.getTenor();
123
+
124
+ if (period2) {
125
+ assert(period2.years === 2, `Expected 2 years, got ${period2.years}`);
126
+ assert(period2.months === 6, `Expected 6 months, got ${period2.months}`);
127
+ assert(period2.days === 5, `Expected 5 days, got ${period2.days}`);
128
+ }
129
+
130
+ assert(tenor2.toString() === 'TERM: 2Y6M5D', `Expected "TERM: 2Y6M5D", got "${tenor2.toString()}"`);
131
+ }
132
+
@@ -2,10 +2,12 @@ import Security from './security';
2
2
  import { SecurityProto } from '../../../fintekkers/models/security/security_pb';
3
3
  import { SecurityTypeProto } from '../../../fintekkers/models/security/security_type_pb';
4
4
  import { DecimalValueProto } from '../../../fintekkers/models/util/decimal_value_pb';
5
- import { CouponTypeProto } from '../../../fintekkers/models/security/coupon_type_pb';
6
- import { CouponFrequencyProto } from '../../../fintekkers/models/security/coupon_frequency_pb';
7
5
  import { LocalDate } from '../utils/date';
8
6
  import { IssuanceProto } from '../../../fintekkers/models/security/bond/issuance_pb';
7
+ import { CouponFrequency } from './coupon_frequency';
8
+ import { CouponType } from './coupon_type';
9
+ import { Tenor, Period } from './term';
10
+ import { TenorTypeProto } from '../../../fintekkers/models/security/tenor_type_pb';
9
11
 
10
12
  class BondSecurity extends Security {
11
13
  constructor(proto: SecurityProto) {
@@ -17,6 +19,47 @@ class BondSecurity extends Security {
17
19
  }
18
20
  }
19
21
 
22
+ /** Returns the tenor (term) of the bond as a Tenor object */
23
+ getTenor(): Tenor {
24
+ const issueDate = this.getIssueDate().toDate();
25
+ const maturityDate = this.getMaturityDate().toDate();
26
+
27
+ // Calculate the period between issue date and maturity date
28
+ const period = this.calculatePeriod(issueDate, maturityDate);
29
+
30
+ return new Tenor(TenorTypeProto.TERM, period);
31
+ }
32
+
33
+ /**
34
+ * Calculates the period between two dates in years, months, and days.
35
+ * This method handles month and year boundaries correctly.
36
+ */
37
+ private calculatePeriod(startDate: Date, endDate: Date): Period {
38
+ let years = endDate.getFullYear() - startDate.getFullYear();
39
+ let months = endDate.getMonth() - startDate.getMonth();
40
+ let days = endDate.getDate() - startDate.getDate();
41
+
42
+ // Adjust for negative days (e.g., if end day is before start day)
43
+ if (days < 0) {
44
+ months--;
45
+ // Get the number of days in the previous month
46
+ const lastDayOfPrevMonth = new Date(endDate.getFullYear(), endDate.getMonth(), 0).getDate();
47
+ days += lastDayOfPrevMonth;
48
+ }
49
+
50
+ // Adjust for negative months
51
+ if (months < 0) {
52
+ years--;
53
+ months += 12;
54
+ }
55
+
56
+ return {
57
+ years,
58
+ months,
59
+ days
60
+ };
61
+ }
62
+
20
63
  getCouponRate(): DecimalValueProto {
21
64
  const rate = this.proto.getCouponRate();
22
65
  if (!rate) throw new Error("Coupon rate is required for bonds");
@@ -29,12 +72,16 @@ class BondSecurity extends Security {
29
72
  return faceValue;
30
73
  }
31
74
 
32
- getCouponType(): CouponTypeProto {
33
- return this.proto.getCouponType();
75
+ getCouponType(): CouponType {
76
+ const couponType = this.proto.getCouponType();
77
+ if (!couponType) throw new Error("Coupon Type is required for bonds");
78
+ return new CouponType(couponType);
34
79
  }
35
80
 
36
- getCouponFrequency(): CouponFrequencyProto {
37
- return this.proto.getCouponFrequency();
81
+ getCouponFrequency(): CouponFrequency {
82
+ const couponFrequency = this.proto.getCouponFrequency();
83
+ if (!couponFrequency) throw new Error("Coupon Frequency is required for bonds");
84
+ return new CouponFrequency(couponFrequency);
38
85
  }
39
86
 
40
87
  getDatedDate(): LocalDate | undefined {
@@ -0,0 +1,16 @@
1
+ import { CouponFrequencyProto } from "../../../fintekkers/models/security/coupon_frequency_pb";
2
+ export declare class CouponFrequency {
3
+ proto: CouponFrequencyProto;
4
+ constructor(proto: CouponFrequencyProto);
5
+ static cfEnumMap: Map<number, string>;
6
+ /**
7
+ * NOTE that this method is not performant and should only be used for debugging purposes,
8
+ * or infrequently. If this is required for a high performance use case, please create a
9
+ * reverse map with the enum ID as the key and the enum descriptor as the value. There is
10
+ * nothing stopping this code from returning a value that does not map exactly to the enum
11
+ * value. E.g. rather than SEMIANNUALLY, you could return Semiannually.
12
+ *
13
+ * @returns CouponFrequency as a string
14
+ */
15
+ toString(): string;
16
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CouponFrequency = void 0;
4
+ const coupon_frequency_pb_1 = require("../../../fintekkers/models/security/coupon_frequency_pb");
5
+ class CouponFrequency {
6
+ constructor(proto) {
7
+ this.proto = proto;
8
+ }
9
+ /**
10
+ * NOTE that this method is not performant and should only be used for debugging purposes,
11
+ * or infrequently. If this is required for a high performance use case, please create a
12
+ * reverse map with the enum ID as the key and the enum descriptor as the value. There is
13
+ * nothing stopping this code from returning a value that does not map exactly to the enum
14
+ * value. E.g. rather than SEMIANNUALLY, you could return Semiannually.
15
+ *
16
+ * @returns CouponFrequency as a string
17
+ */
18
+ toString() {
19
+ var _a;
20
+ return (_a = CouponFrequency.cfEnumMap.get(this.proto)) !== null && _a !== void 0 ? _a : 'UNKNOWN_COUPON_FREQUENCY';
21
+ }
22
+ }
23
+ exports.CouponFrequency = CouponFrequency;
24
+ (() => {
25
+ CouponFrequency.cfEnumMap = new Map();
26
+ Object.keys(coupon_frequency_pb_1.CouponFrequencyProto).forEach(key => {
27
+ CouponFrequency.cfEnumMap.set(coupon_frequency_pb_1.CouponFrequencyProto[key], key);
28
+ });
29
+ })();
30
+ //# sourceMappingURL=coupon_frequency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coupon_frequency.js","sourceRoot":"","sources":["coupon_frequency.ts"],"names":[],"mappings":";;;AAAA,iGAA+F;AAE/F,MAAa,eAAe;IAGxB,YAAY,KAA2B;QACnC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;IAYD;;;;;;;;OAQG;IACH,QAAQ;;QACJ,OAAO,MAAA,eAAe,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,mCAAI,0BAA0B,CAAC;IACnF,CAAC;CACJ;AA7BD,0CA6BC;AApBG;IACI,eAAe,CAAC,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEtD,MAAM,CAAC,IAAI,CAAC,0CAAoB,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;QAC5C,eAAe,CAAC,SAAS,CAAC,GAAG,CAAC,0CAAoB,CAAC,GAAwC,CAAC,EAAE,GAAG,CAAC,CAAC;IACvG,CAAC,CAAC,CAAC;AACP,CAAC,GAAA,CAAA"}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const assert = require("assert");
4
+ const coupon_frequency_1 = require("./coupon_frequency");
5
+ const coupon_frequency_pb_1 = require("../../../fintekkers/models/security/coupon_frequency_pb");
6
+ test('test CouponFrequency.SEMIANNUALLY returns "SEMIANNUALLY"', () => {
7
+ testSemiannuallyCouponFrequency();
8
+ });
9
+ function testSemiannuallyCouponFrequency() {
10
+ let couponFrequency = new coupon_frequency_1.CouponFrequency(coupon_frequency_pb_1.CouponFrequencyProto.SEMIANNUALLY);
11
+ assert(couponFrequency.toString() === 'SEMIANNUALLY');
12
+ couponFrequency = new coupon_frequency_1.CouponFrequency(2);
13
+ assert(couponFrequency.toString() === 'SEMIANNUALLY');
14
+ }
15
+ //# sourceMappingURL=coupon_frequency.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coupon_frequency.test.js","sourceRoot":"","sources":["coupon_frequency.test.ts"],"names":[],"mappings":";;AAAA,iCAAkC;AAClC,yDAAqD;AACrD,iGAA+F;AAE/F,IAAI,CAAC,0DAA0D,EAAE,GAAG,EAAE;IAClE,+BAA+B,EAAE,CAAC;AACtC,CAAC,CAAC,CAAC;AAEH,SAAS,+BAA+B;IACpC,IAAI,eAAe,GAAG,IAAI,kCAAe,CAAC,0CAAoB,CAAC,YAAY,CAAC,CAAC;IAC7E,MAAM,CAAC,eAAe,CAAC,QAAQ,EAAE,KAAK,cAAc,CAAC,CAAC;IAEtD,eAAe,GAAG,IAAI,kCAAe,CAAC,CAAC,CAAC,CAAC;IACzC,MAAM,CAAC,eAAe,CAAC,QAAQ,EAAE,KAAK,cAAc,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,16 @@
1
+ import assert = require('assert');
2
+ import { CouponFrequency } from './coupon_frequency';
3
+ import { CouponFrequencyProto } from '../../../fintekkers/models/security/coupon_frequency_pb';
4
+
5
+ test('test CouponFrequency.SEMIANNUALLY returns "SEMIANNUALLY"', () => {
6
+ testSemiannuallyCouponFrequency();
7
+ });
8
+
9
+ function testSemiannuallyCouponFrequency(): void {
10
+ let couponFrequency = new CouponFrequency(CouponFrequencyProto.SEMIANNUALLY);
11
+ assert(couponFrequency.toString() === 'SEMIANNUALLY');
12
+
13
+ couponFrequency = new CouponFrequency(2);
14
+ assert(couponFrequency.toString() === 'SEMIANNUALLY');
15
+ }
16
+
@@ -0,0 +1,33 @@
1
+ import { CouponFrequencyProto } from "../../../fintekkers/models/security/coupon_frequency_pb";
2
+
3
+ export class CouponFrequency {
4
+ proto: CouponFrequencyProto;
5
+
6
+ constructor(proto: CouponFrequencyProto) {
7
+ this.proto = proto;
8
+ }
9
+
10
+ static cfEnumMap: Map<number, string>;
11
+
12
+ static {
13
+ CouponFrequency.cfEnumMap = new Map<number, string>();
14
+
15
+ Object.keys(CouponFrequencyProto).forEach(key => {
16
+ CouponFrequency.cfEnumMap.set(CouponFrequencyProto[key as keyof typeof CouponFrequencyProto], key);
17
+ });
18
+ }
19
+
20
+ /**
21
+ * NOTE that this method is not performant and should only be used for debugging purposes,
22
+ * or infrequently. If this is required for a high performance use case, please create a
23
+ * reverse map with the enum ID as the key and the enum descriptor as the value. There is
24
+ * nothing stopping this code from returning a value that does not map exactly to the enum
25
+ * value. E.g. rather than SEMIANNUALLY, you could return Semiannually.
26
+ *
27
+ * @returns CouponFrequency as a string
28
+ */
29
+ toString(): string {
30
+ return CouponFrequency.cfEnumMap.get(this.proto) ?? 'UNKNOWN_COUPON_FREQUENCY';
31
+ }
32
+ }
33
+
@@ -0,0 +1,16 @@
1
+ import { CouponTypeProto } from "../../../fintekkers/models/security/coupon_type_pb";
2
+ export declare class CouponType {
3
+ proto: CouponTypeProto;
4
+ constructor(proto: CouponTypeProto);
5
+ static ctEnumMap: Map<number, string>;
6
+ /**
7
+ * NOTE that this method is not performant and should only be used for debugging purposes,
8
+ * or infrequently. If this is required for a high performance use case, please create a
9
+ * reverse map with the enum ID as the key and the enum descriptor as the value. There is
10
+ * nothing stopping this code from returning a value that does not map exactly to the enum
11
+ * value. E.g. rather than FIXED, you could return Fixed.
12
+ *
13
+ * @returns CouponType as a string
14
+ */
15
+ name(): string;
16
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CouponType = void 0;
4
+ const coupon_type_pb_1 = require("../../../fintekkers/models/security/coupon_type_pb");
5
+ class CouponType {
6
+ constructor(proto) {
7
+ this.proto = proto;
8
+ }
9
+ /**
10
+ * NOTE that this method is not performant and should only be used for debugging purposes,
11
+ * or infrequently. If this is required for a high performance use case, please create a
12
+ * reverse map with the enum ID as the key and the enum descriptor as the value. There is
13
+ * nothing stopping this code from returning a value that does not map exactly to the enum
14
+ * value. E.g. rather than FIXED, you could return Fixed.
15
+ *
16
+ * @returns CouponType as a string
17
+ */
18
+ name() {
19
+ var _a;
20
+ return (_a = CouponType.ctEnumMap.get(this.proto)) !== null && _a !== void 0 ? _a : 'UNKNOWN_COUPON_TYPE';
21
+ }
22
+ }
23
+ exports.CouponType = CouponType;
24
+ (() => {
25
+ CouponType.ctEnumMap = new Map();
26
+ Object.keys(coupon_type_pb_1.CouponTypeProto).forEach(key => {
27
+ CouponType.ctEnumMap.set(coupon_type_pb_1.CouponTypeProto[key], key);
28
+ });
29
+ })();
30
+ //# sourceMappingURL=coupon_type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coupon_type.js","sourceRoot":"","sources":["coupon_type.ts"],"names":[],"mappings":";;;AAAA,uFAAqF;AAErF,MAAa,UAAU;IAGnB,YAAY,KAAsB;QAC9B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;IAYD;;;;;;;;OAQG;IACH,IAAI;;QACA,OAAO,MAAA,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,mCAAI,qBAAqB,CAAC;IACzE,CAAC;CACJ;AA7BD,gCA6BC;AApBG;IACI,UAAU,CAAC,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEjD,MAAM,CAAC,IAAI,CAAC,gCAAe,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;QACvC,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,gCAAe,CAAC,GAAmC,CAAC,EAAE,GAAG,CAAC,CAAC;IACxF,CAAC,CAAC,CAAC;AACP,CAAC,GAAA,CAAA"}
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const assert = require("assert");
4
+ const coupon_type_1 = require("./coupon_type");
5
+ const coupon_type_pb_1 = require("../../../fintekkers/models/security/coupon_type_pb");
6
+ test('test CouponType.FIXED returns "FIXED"', () => {
7
+ testFixedCouponType();
8
+ });
9
+ function testFixedCouponType() {
10
+ const couponType = new coupon_type_1.CouponType(coupon_type_pb_1.CouponTypeProto.FIXED);
11
+ assert(couponType.toString() === 'FIXED');
12
+ }
13
+ //# sourceMappingURL=coupon_type.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coupon_type.test.js","sourceRoot":"","sources":["coupon_type.test.ts"],"names":[],"mappings":";;AAAA,iCAAkC;AAClC,+CAA2C;AAC3C,uFAAqF;AAErF,IAAI,CAAC,uCAAuC,EAAE,GAAG,EAAE;IACjD,mBAAmB,EAAE,CAAC;AACxB,CAAC,CAAC,CAAC;AAEH,SAAS,mBAAmB;IAC1B,MAAM,UAAU,GAAG,IAAI,wBAAU,CAAC,gCAAe,CAAC,KAAK,CAAC,CAAC;IACzD,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,OAAO,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,13 @@
1
+ import assert = require('assert');
2
+ import { CouponType } from './coupon_type';
3
+ import { CouponTypeProto } from '../../../fintekkers/models/security/coupon_type_pb';
4
+
5
+ test('test CouponType.FIXED returns "FIXED"', () => {
6
+ testFixedCouponType();
7
+ });
8
+
9
+ function testFixedCouponType(): void {
10
+ const couponType = new CouponType(CouponTypeProto.FIXED);
11
+ assert(couponType.toString() === 'FIXED');
12
+ }
13
+
@@ -0,0 +1,33 @@
1
+ import { CouponTypeProto } from "../../../fintekkers/models/security/coupon_type_pb";
2
+
3
+ export class CouponType {
4
+ proto: CouponTypeProto;
5
+
6
+ constructor(proto: CouponTypeProto) {
7
+ this.proto = proto;
8
+ }
9
+
10
+ static ctEnumMap: Map<number, string>;
11
+
12
+ static {
13
+ CouponType.ctEnumMap = new Map<number, string>();
14
+
15
+ Object.keys(CouponTypeProto).forEach(key => {
16
+ CouponType.ctEnumMap.set(CouponTypeProto[key as keyof typeof CouponTypeProto], key);
17
+ });
18
+ }
19
+
20
+ /**
21
+ * NOTE that this method is not performant and should only be used for debugging purposes,
22
+ * or infrequently. If this is required for a high performance use case, please create a
23
+ * reverse map with the enum ID as the key and the enum descriptor as the value. There is
24
+ * nothing stopping this code from returning a value that does not map exactly to the enum
25
+ * value. E.g. rather than FIXED, you could return Fixed.
26
+ *
27
+ * @returns CouponType as a string
28
+ */
29
+ name(): string {
30
+ return CouponType.ctEnumMap.get(this.proto) ?? 'UNKNOWN_COUPON_TYPE';
31
+ }
32
+ }
33
+
@@ -73,10 +73,9 @@ class Security {
73
73
  throw new Error('Not implemented yet. See Java implementation for reference');
74
74
  }
75
75
  getProductType() {
76
- let securityType = this.proto.getSecurityType();
77
- let securityTypeString = security_type_pb_1.SecurityTypeProto[securityType];
78
- ;
79
- return securityTypeString;
76
+ const securityType = this.proto.getSecurityType();
77
+ const securityTypeString = Object.keys(security_type_pb_1.SecurityTypeProto).find(key => security_type_pb_1.SecurityTypeProto[key] === securityType);
78
+ return securityTypeString || 'UNKNOWN_SECURITY_TYPE';
80
79
  }
81
80
  getSecurityID() {
82
81
  const identifier = this.proto.getIdentifier();
@@ -1 +1 @@
1
- {"version":3,"file":"security.js","sourceRoot":"","sources":["security.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"security.js","sourceRoot":"","sources":["security.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,6 @@ import { IdentifierProto } from "../../../fintekkers/models/security/identifier/
3
3
  import { SecurityProto } from "../../../fintekkers/models/security/security_pb";
4
4
  import { ZonedDateTime } from "../utils/datetime";
5
5
  import { UUID } from "../utils/uuid";
6
- import { ProtoSerializationUtil } from '../utils/serialization';
7
6
  import { LocalDate } from "../utils/date";
8
7
  import { SecurityTypeProto } from "../../../fintekkers/models/security/security_type_pb";
9
8
 
@@ -84,9 +83,12 @@ class Security {
84
83
  }
85
84
 
86
85
  getProductType(): string {
87
- let securityType = this.proto.getSecurityType();
88
- let securityTypeString = SecurityTypeProto[securityType];;
89
- return securityTypeString;
86
+ const securityType = this.proto.getSecurityType();
87
+ const securityTypeString = (Object.keys(SecurityTypeProto) as Array<keyof typeof SecurityTypeProto>).find(
88
+ key => SecurityTypeProto[key] === securityType
89
+ );
90
+
91
+ return securityTypeString || 'UNKNOWN_SECURITY_TYPE';
90
92
  }
91
93
 
92
94
  getSecurityID(): IdentifierProto {
@@ -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.114",
5
5
  "description": "ledger model protos ",
6
6
  "authors": [
7
7
  "David Doherty",
@@ -1,43 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const security_1 = __importDefault(require("./security"));
7
- const security_type_pb_1 = require("../../../fintekkers/models/security/security_type_pb");
8
- const date_1 = require("../utils/date");
9
- class BondSecurity extends security_1.default {
10
- constructor(proto) {
11
- super(proto);
12
- if (proto.getSecurityType() !== security_type_pb_1.SecurityTypeProto.BOND_SECURITY) {
13
- throw new Error(`BondSecurity requires BOND_SECURITY type, got ${security_type_pb_1.SecurityTypeProto[proto.getSecurityType()]}`);
14
- }
15
- }
16
- getCouponRate() {
17
- const rate = this.proto.getCouponRate();
18
- if (!rate)
19
- throw new Error("Coupon rate is required for bonds");
20
- return rate;
21
- }
22
- getFaceValue() {
23
- const faceValue = this.proto.getFaceValue();
24
- if (!faceValue)
25
- throw new Error("Face value is required for bonds");
26
- return faceValue;
27
- }
28
- getCouponType() {
29
- return this.proto.getCouponType();
30
- }
31
- getCouponFrequency() {
32
- return this.proto.getCouponFrequency();
33
- }
34
- getDatedDate() {
35
- const datedDate = this.proto.getDatedDate();
36
- return datedDate ? new date_1.LocalDate(datedDate) : undefined;
37
- }
38
- getIssuanceInfo() {
39
- return this.proto.getIssuanceInfoList();
40
- }
41
- }
42
- exports.default = BondSecurity;
43
- //# sourceMappingURL=BondSecurity.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BondSecurity.js","sourceRoot":"","sources":["BondSecurity.ts"],"names":[],"mappings":";;;;;AAAA,0DAAkC;AAElC,2FAAyF;AAIzF,wCAA0C;AAG1C,MAAM,YAAa,SAAQ,kBAAQ;IACjC,YAAY,KAAoB;QAC9B,KAAK,CAAC,KAAK,CAAC,CAAC;QACb,IAAI,KAAK,CAAC,eAAe,EAAE,KAAK,oCAAiB,CAAC,aAAa,EAAE,CAAC;YAChE,MAAM,IAAI,KAAK,CACb,iDAAiD,oCAAiB,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC,EAAE,CAC9F,CAAC;QACJ,CAAC;IACH,CAAC;IAED,aAAa;QACX,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;QACxC,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;QAChE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,YAAY;QACV,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;QAC5C,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACpE,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,aAAa;QACX,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;IACpC,CAAC;IAED,kBAAkB;QAChB,OAAO,IAAI,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC;IACzC,CAAC;IAED,YAAY;QACV,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;QAC5C,OAAO,SAAS,CAAC,CAAC,CAAC,IAAI,gBAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1D,CAAC;IAED,eAAe;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,mBAAmB,EAAE,CAAC;IAC1C,CAAC;CACF;AAED,kBAAe,YAAY,CAAC"}