jg-bignumber 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0
4
+
5
+ The first release of `jg-bignumber`, which replaces `bignumber-utils` 1.1.0.
6
+
7
+ ### Added
8
+
9
+ - `BigNumber`, the package's own bignumber.js constructor.
10
+ - `ERRORS`, the codes of every error the package throws.
11
+
12
+ ### Changed
13
+
14
+ - `decimalPlaces` applies to every rounding mode, including `*_CEIL` and `*_FLOOR`.
15
+ - `NaN` and `±Infinity` are rejected as `INVALID_VALUE`.
16
+ - `calculateWeightedEntry` returns 0 when the amounts add up to 0.
17
+ - The package no longer changes the global bignumber.js configuration, and it uses bignumber.js 11.
18
+ - Errors are `Exception`s from `jg-exception`.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jesus Graterol
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,68 @@
1
- # Temporary Holding Version
1
+ # jg-bignumber
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Exact decimal math with bignumber.js: rounding, formatting, and sum, mean, percentage, and exchange calculations.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install jg-bignumber
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ Every calculation is exact, then rounded to 2 decimal places with `ROUND_HALF_UP` and returned as a number, unless the configuration asks otherwise:
14
+
15
+ ```ts
16
+ import { calculateSum, processValue, prettifyValue } from 'jg-bignumber';
17
+
18
+ calculateSum([0.1, '0.2', 0.3]); // 0.6, not 0.6000000000000001
19
+ processValue(512.155); // 512.16
20
+ processValue(19.991, { roundingMode: 'ROUND_CEIL' }); // 20
21
+ processValue('0.000001', { decimalPlaces: 6, type: 'string' }); // '0.000001'
22
+ prettifyValue(1234.5, { format: { prefix: '$' } }); // '$1,234.5'
23
+ ```
24
+
25
+ Percentages and money:
26
+
27
+ ```ts
28
+ import {
29
+ adjustByPercentage,
30
+ calculateExchange,
31
+ calculatePercentageChange,
32
+ calculateWeightedEntry,
33
+ } from 'jg-bignumber';
34
+
35
+ calculatePercentageChange(100, 125); // 25
36
+ adjustByPercentage(100, -10); // 90
37
+ calculateExchange(100, 65000, { decimalPlaces: 8 }); // 0.00153846
38
+ calculateWeightedEntry([
39
+ [100, 2],
40
+ [130, 1],
41
+ ]); // 110, the average price weighted by amount
42
+ ```
43
+
44
+ `BigNumber` is the package's own bignumber.js constructor, so creating values with it never changes the global bignumber.js configuration.
45
+
46
+ ## Coming from `bignumber-utils`
47
+
48
+ | `bignumber-utils` | `jg-bignumber` |
49
+ | ------------------------------------------------------------------------- | ------------------------------------------ |
50
+ | `import … from 'bignumber-utils'` | `import … from 'jg-bignumber'` |
51
+ | `import { BigNumber } from 'bignumber.js'` for values the package accepts | `import { BigNumber } from 'jg-bignumber'` |
52
+
53
+ Changed behavior:
54
+
55
+ - `decimalPlaces` applies to every rounding mode. The `*_CEIL` and `*_FLOOR` modes used to force 0 decimal places; pass `decimalPlaces: 0` for that.
56
+ - `NaN` and `±Infinity` are rejected: `getBigNumber`, `processValue`, and the calculations throw `INVALID_VALUE`, and `isNumber` returns `false`.
57
+ - Importing the package no longer changes the global bignumber.js configuration.
58
+ - `calculateWeightedEntry` returns 0 when the amounts add up to 0, instead of failing.
59
+ - Errors are `Exception`s from `jg-exception`, with the same codes, exported as `ERRORS`.
60
+ - The package uses bignumber.js 11, whose `IBigNumberFormat` adds `negativeSign` and `positiveSign`.
61
+
62
+ ## Documentation
63
+
64
+ The full API reference is at https://jg-packages.jesusgraterol.dev/packages/jg-bignumber/.
65
+
66
+ ## License
67
+
68
+ [MIT](https://github.com/jesusgraterol/jg-packages/blob/main/packages/jg-bignumber/LICENSE)
@@ -0,0 +1,183 @@
1
+ import type { IBigNumberValue, IConfig, IOutput } from '../shared/types.js';
2
+ /**
3
+ * Adds up a list of values. An empty list sums to 0.
4
+ * @param values The values.
5
+ * @param config How the result is processed.
6
+ * @returns The sum.
7
+ * @example
8
+ * calculateSum([0.1, '0.2', 0.3]); // 0.6
9
+ * calculateSum([0.1, 0.2], { type: 'string' }); // '0.3'
10
+ * @throws
11
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
12
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
13
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
14
+ * - INVALID_TYPE: if the processing type is not supported
15
+ * - INVALID_VALUES_ARRAY: if the provided values arg is not a valid array
16
+ */
17
+ export declare const calculateSum: <T extends Partial<IConfig>>(values: IBigNumberValue[], config?: T) => IOutput<T>;
18
+ /**
19
+ * Finds the smallest value in a list. An empty list returns 0.
20
+ * @param values The values.
21
+ * @param config How the result is processed.
22
+ * @returns The smallest value.
23
+ * @example
24
+ * calculateMin([3, '1.5', 2]); // 1.5
25
+ * @throws
26
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
27
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
28
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
29
+ * - INVALID_TYPE: if the processing type is not supported
30
+ * - INVALID_VALUES_ARRAY: if the provided values arg is not a valid array
31
+ */
32
+ export declare const calculateMin: <T extends Partial<IConfig>>(values: IBigNumberValue[], config?: T) => IOutput<T>;
33
+ /**
34
+ * Finds the largest value in a list. An empty list returns 0.
35
+ * @param values The values.
36
+ * @param config How the result is processed.
37
+ * @returns The largest value.
38
+ * @example
39
+ * calculateMax([3, '1.5', 2]); // 3
40
+ * @throws
41
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
42
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
43
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
44
+ * - INVALID_TYPE: if the processing type is not supported
45
+ * - INVALID_VALUES_ARRAY: if the provided values arg is not a valid array
46
+ */
47
+ export declare const calculateMax: <T extends Partial<IConfig>>(values: IBigNumberValue[], config?: T) => IOutput<T>;
48
+ /**
49
+ * Calculates the mean of a list of values. An empty list returns 0.
50
+ * @param values The values.
51
+ * @param config How the result is processed.
52
+ * @returns The mean.
53
+ * @example
54
+ * calculateMean([1, 2, 4]); // 2.33
55
+ * @throws
56
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
57
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
58
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
59
+ * - INVALID_TYPE: if the processing type is not supported
60
+ * - INVALID_VALUES_ARRAY: if the provided values arg is not a valid array
61
+ */
62
+ export declare const calculateMean: <T extends Partial<IConfig>>(values: IBigNumberValue[], config?: T) => IOutput<T>;
63
+ /**
64
+ * Calculates the median of a list of values: the middle value, or the mean of the two middle
65
+ * values when the list has an even length. An empty list returns 0.
66
+ * @param values The values.
67
+ * @param config How the result is processed.
68
+ * @returns The median.
69
+ * @example
70
+ * calculateMedian([5, 1, 3]); // 3
71
+ * calculateMedian([4, 1, 3, 2]); // 2.5
72
+ * @throws
73
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
74
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
75
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
76
+ * - INVALID_TYPE: if the processing type is not supported
77
+ * - INVALID_VALUES_ARRAY: if the provided values arg is not a valid array
78
+ */
79
+ export declare const calculateMedian: <T extends Partial<IConfig>>(values: IBigNumberValue[], config?: T) => IOutput<T>;
80
+ /**
81
+ * Calculates the percentage a value changed by: positive when it increased, negative when it
82
+ * decreased, and 0 when it didn't change. The largest decrease is -100%, returned whenever the new
83
+ * value is 0 or less.
84
+ * @param oldValue The value before the change.
85
+ * @param newValue The value after the change.
86
+ * @param config How the result is processed.
87
+ * @returns The percentage change.
88
+ * @example
89
+ * calculatePercentageChange(100, 125); // 25
90
+ * calculatePercentageChange(100, 80); // -20
91
+ * calculatePercentageChange(100, 0); // -100
92
+ * @throws
93
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
94
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
95
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
96
+ * - INVALID_TYPE: if the processing type is not supported
97
+ * - NEGATIVE_VALUE_NOT_ALLOWED: if the oldValue is not positive
98
+ */
99
+ export declare const calculatePercentageChange: <T extends Partial<IConfig>>(oldValue: IBigNumberValue, newValue: IBigNumberValue, config?: T) => IOutput<T>;
100
+ /**
101
+ * Changes a value by a percentage: a positive percentage increases it, and a negative one
102
+ * decreases it, to 0 at most.
103
+ * @param value The value to change.
104
+ * @param percentage The percentage to change it by.
105
+ * @param config How the result is processed.
106
+ * @returns The changed value.
107
+ * @example
108
+ * adjustByPercentage(100, 25); // 125
109
+ * adjustByPercentage(100, -10); // 90
110
+ * @throws
111
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
112
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
113
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
114
+ * - INVALID_TYPE: if the processing type is not supported
115
+ * - NEGATIVE_VALUE_NOT_ALLOWED: if the value is not positive
116
+ */
117
+ export declare const adjustByPercentage: <T extends Partial<IConfig>>(value: IBigNumberValue, percentage: IBigNumberValue, config?: T) => IOutput<T>;
118
+ /**
119
+ * Calculates what percentage of a total a value represents.
120
+ * @param value The part.
121
+ * @param total The total.
122
+ * @param config How the result is processed.
123
+ * @returns The percentage.
124
+ * @example
125
+ * calculatePercentageRepresentation(25, 200); // 12.5
126
+ * @throws
127
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
128
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
129
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
130
+ * - INVALID_TYPE: if the processing type is not supported
131
+ * - NEGATIVE_VALUE_NOT_ALLOWED: if the value or total are not positive
132
+ */
133
+ export declare const calculatePercentageRepresentation: <T extends Partial<IConfig>>(value: IBigNumberValue, total: IBigNumberValue, config?: T) => IOutput<T>;
134
+ /**
135
+ * Calculates the amount of an asset an exchange gives for a value at a rate, such as how much
136
+ * BTC 100 USDT buys at 65,000 USDT per BTC.
137
+ * @param value The amount being exchanged.
138
+ * @param rate The price of one unit of the asset received.
139
+ * @param config How the result is processed.
140
+ * @returns The amount received.
141
+ * @example
142
+ * calculateExchange(100, 65000, { decimalPlaces: 8 }); // 0.00153846
143
+ * @throws
144
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
145
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
146
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
147
+ * - INVALID_TYPE: if the processing type is not supported
148
+ * - NEGATIVE_VALUE_NOT_ALLOWED: if the value or rate are not positive
149
+ */
150
+ export declare const calculateExchange: <T extends Partial<IConfig>>(value: IBigNumberValue, rate: IBigNumberValue, config?: T) => IOutput<T>;
151
+ /**
152
+ * Calculates the fee charged for an exchange, as a percentage of the value exchanged.
153
+ * @param value The amount being exchanged.
154
+ * @param feePercentage The fee, as a percentage.
155
+ * @param config How the result is processed.
156
+ * @returns The fee.
157
+ * @example
158
+ * calculateExchangeFee(1000, 0.1); // 1
159
+ * @throws
160
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
161
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
162
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
163
+ * - INVALID_TYPE: if the processing type is not supported
164
+ * - NEGATIVE_VALUE_NOT_ALLOWED: if the value or feePercentage are not positive
165
+ */
166
+ export declare const calculateExchangeFee: <T extends Partial<IConfig>>(value: IBigNumberValue, feePercentage: IBigNumberValue, config?: T) => IOutput<T>;
167
+ /**
168
+ * Calculates the average entry price of a position bought in several trades, weighting each price
169
+ * by the amount bought. An empty list, or one whose amounts add up to 0, returns 0.
170
+ * @param trades The trades, each as a `[price, amount]` tuple.
171
+ * @param config How the result is processed.
172
+ * @returns The weighted average price.
173
+ * @example
174
+ * calculateWeightedEntry([[100, 2], [130, 1]]); // 110
175
+ * calculateWeightedEntry([]); // 0
176
+ * @throws
177
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
178
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
179
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
180
+ * - INVALID_TYPE: if the processing type is not supported
181
+ * - INVALID_VALUES_ARRAY: if the provided values arg is not a valid array
182
+ */
183
+ export declare const calculateWeightedEntry: <T extends Partial<IConfig>>(trades: [IBigNumberValue, IBigNumberValue][], config?: T) => IOutput<T>;
@@ -0,0 +1,289 @@
1
+ import { BigNumber } from '../shared/constants.js';
2
+ import { getBigNumber, processValue } from '../processing/processing.js';
3
+ import { sortBigNumbers } from '../utils/utils.js';
4
+ import { validatePositiveValue, validateValuesArray } from '../validations/validations.js';
5
+ /*
6
+ * Essential calculations
7
+ */
8
+ /**
9
+ * Adds up a list of values. An empty list sums to 0.
10
+ * @param values The values.
11
+ * @param config How the result is processed.
12
+ * @returns The sum.
13
+ * @example
14
+ * calculateSum([0.1, '0.2', 0.3]); // 0.6
15
+ * calculateSum([0.1, 0.2], { type: 'string' }); // '0.3'
16
+ * @throws
17
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
18
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
19
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
20
+ * - INVALID_TYPE: if the processing type is not supported
21
+ * - INVALID_VALUES_ARRAY: if the provided values arg is not a valid array
22
+ */
23
+ export const calculateSum = (values, config) => {
24
+ validateValuesArray(values, 'calculateSum');
25
+ return processValue(values.length > 0 ? BigNumber.sum(...values) : 0, config);
26
+ };
27
+ /**
28
+ * Finds the smallest value in a list. An empty list returns 0.
29
+ * @param values The values.
30
+ * @param config How the result is processed.
31
+ * @returns The smallest value.
32
+ * @example
33
+ * calculateMin([3, '1.5', 2]); // 1.5
34
+ * @throws
35
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
36
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
37
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
38
+ * - INVALID_TYPE: if the processing type is not supported
39
+ * - INVALID_VALUES_ARRAY: if the provided values arg is not a valid array
40
+ */
41
+ export const calculateMin = (values, config) => {
42
+ validateValuesArray(values, 'calculateMin');
43
+ return processValue(values.length > 0 ? BigNumber.min(...values) : 0, config);
44
+ };
45
+ /**
46
+ * Finds the largest value in a list. An empty list returns 0.
47
+ * @param values The values.
48
+ * @param config How the result is processed.
49
+ * @returns The largest value.
50
+ * @example
51
+ * calculateMax([3, '1.5', 2]); // 3
52
+ * @throws
53
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
54
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
55
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
56
+ * - INVALID_TYPE: if the processing type is not supported
57
+ * - INVALID_VALUES_ARRAY: if the provided values arg is not a valid array
58
+ */
59
+ export const calculateMax = (values, config) => {
60
+ validateValuesArray(values, 'calculateMax');
61
+ return processValue(values.length > 0 ? BigNumber.max(...values) : 0, config);
62
+ };
63
+ /**
64
+ * Calculates the mean of a list of values. An empty list returns 0.
65
+ * @param values The values.
66
+ * @param config How the result is processed.
67
+ * @returns The mean.
68
+ * @example
69
+ * calculateMean([1, 2, 4]); // 2.33
70
+ * @throws
71
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
72
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
73
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
74
+ * - INVALID_TYPE: if the processing type is not supported
75
+ * - INVALID_VALUES_ARRAY: if the provided values arg is not a valid array
76
+ */
77
+ export const calculateMean = (values, config) => {
78
+ validateValuesArray(values, 'calculateMean');
79
+ if (values.length > 0) {
80
+ return processValue(BigNumber.sum(...values).dividedBy(values.length), config);
81
+ }
82
+ return processValue(0, config);
83
+ };
84
+ /**
85
+ * Calculates the median of a list of values: the middle value, or the mean of the two middle
86
+ * values when the list has an even length. An empty list returns 0.
87
+ * @param values The values.
88
+ * @param config How the result is processed.
89
+ * @returns The median.
90
+ * @example
91
+ * calculateMedian([5, 1, 3]); // 3
92
+ * calculateMedian([4, 1, 3, 2]); // 2.5
93
+ * @throws
94
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
95
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
96
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
97
+ * - INVALID_TYPE: if the processing type is not supported
98
+ * - INVALID_VALUES_ARRAY: if the provided values arg is not a valid array
99
+ */
100
+ export const calculateMedian = (values, config) => {
101
+ validateValuesArray(values, 'calculateMedian');
102
+ if (values.length === 0) {
103
+ return processValue(0, config);
104
+ }
105
+ const half = Math.floor(values.length / 2);
106
+ const sorted = values.map(getBigNumber).sort(sortBigNumbers('asc'));
107
+ const middle = sorted[half];
108
+ const median = values.length % 2 === 1 ? middle : sorted[half - 1].plus(middle).dividedBy(2);
109
+ return processValue(median, config);
110
+ };
111
+ /*
112
+ * Percentage calculations
113
+ */
114
+ /**
115
+ * Calculates the percentage a value changed by: positive when it increased, negative when it
116
+ * decreased, and 0 when it didn't change. The largest decrease is -100%, returned whenever the new
117
+ * value is 0 or less.
118
+ * @param oldValue The value before the change.
119
+ * @param newValue The value after the change.
120
+ * @param config How the result is processed.
121
+ * @returns The percentage change.
122
+ * @example
123
+ * calculatePercentageChange(100, 125); // 25
124
+ * calculatePercentageChange(100, 80); // -20
125
+ * calculatePercentageChange(100, 0); // -100
126
+ * @throws
127
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
128
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
129
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
130
+ * - INVALID_TYPE: if the processing type is not supported
131
+ * - NEGATIVE_VALUE_NOT_ALLOWED: if the oldValue is not positive
132
+ */
133
+ export const calculatePercentageChange = (oldValue, newValue, config) => {
134
+ const oldValueBN = getBigNumber(oldValue);
135
+ const newValueBN = getBigNumber(newValue);
136
+ validatePositiveValue(oldValueBN);
137
+ let change;
138
+ if (newValueBN.isGreaterThan(oldValueBN)) {
139
+ change = newValueBN.minus(oldValueBN).dividedBy(oldValueBN).times(100);
140
+ }
141
+ else if (oldValueBN.isGreaterThan(newValueBN)) {
142
+ change = newValueBN.isGreaterThan(0)
143
+ ? oldValueBN.minus(newValueBN).dividedBy(oldValueBN).times(100).negated()
144
+ : getBigNumber(-100);
145
+ }
146
+ else {
147
+ change = getBigNumber(0);
148
+ }
149
+ return processValue(change, config);
150
+ };
151
+ /**
152
+ * Changes a value by a percentage: a positive percentage increases it, and a negative one
153
+ * decreases it, to 0 at most.
154
+ * @param value The value to change.
155
+ * @param percentage The percentage to change it by.
156
+ * @param config How the result is processed.
157
+ * @returns The changed value.
158
+ * @example
159
+ * adjustByPercentage(100, 25); // 125
160
+ * adjustByPercentage(100, -10); // 90
161
+ * @throws
162
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
163
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
164
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
165
+ * - INVALID_TYPE: if the processing type is not supported
166
+ * - NEGATIVE_VALUE_NOT_ALLOWED: if the value is not positive
167
+ */
168
+ export const adjustByPercentage = (value, percentage, config) => {
169
+ const valueBN = getBigNumber(value);
170
+ const percentageBN = getBigNumber(percentage);
171
+ validatePositiveValue(valueBN);
172
+ let adjusted;
173
+ if (percentageBN.isGreaterThan(0)) {
174
+ adjusted = percentageBN.dividedBy(100).plus(1).times(valueBN);
175
+ }
176
+ else if (percentageBN.isLessThan(0)) {
177
+ adjusted = percentageBN.isGreaterThan(-100)
178
+ ? percentageBN.times(-1).dividedBy(100).minus(1).times(valueBN).times(-1)
179
+ : getBigNumber(0);
180
+ }
181
+ else {
182
+ adjusted = valueBN;
183
+ }
184
+ return processValue(adjusted, config);
185
+ };
186
+ /**
187
+ * Calculates what percentage of a total a value represents.
188
+ * @param value The part.
189
+ * @param total The total.
190
+ * @param config How the result is processed.
191
+ * @returns The percentage.
192
+ * @example
193
+ * calculatePercentageRepresentation(25, 200); // 12.5
194
+ * @throws
195
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
196
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
197
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
198
+ * - INVALID_TYPE: if the processing type is not supported
199
+ * - NEGATIVE_VALUE_NOT_ALLOWED: if the value or total are not positive
200
+ */
201
+ export const calculatePercentageRepresentation = (value, total, config) => {
202
+ const valueBN = getBigNumber(value);
203
+ const totalBN = getBigNumber(total);
204
+ validatePositiveValue(valueBN, true);
205
+ validatePositiveValue(totalBN);
206
+ return processValue(valueBN.times(100).dividedBy(totalBN), config);
207
+ };
208
+ /*
209
+ * Financial calculations
210
+ */
211
+ /**
212
+ * Calculates the amount of an asset an exchange gives for a value at a rate, such as how much
213
+ * BTC 100 USDT buys at 65,000 USDT per BTC.
214
+ * @param value The amount being exchanged.
215
+ * @param rate The price of one unit of the asset received.
216
+ * @param config How the result is processed.
217
+ * @returns The amount received.
218
+ * @example
219
+ * calculateExchange(100, 65000, { decimalPlaces: 8 }); // 0.00153846
220
+ * @throws
221
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
222
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
223
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
224
+ * - INVALID_TYPE: if the processing type is not supported
225
+ * - NEGATIVE_VALUE_NOT_ALLOWED: if the value or rate are not positive
226
+ */
227
+ export const calculateExchange = (value, rate, config) => {
228
+ const valueBN = getBigNumber(value);
229
+ const rateBN = getBigNumber(rate);
230
+ validatePositiveValue(valueBN);
231
+ validatePositiveValue(rateBN);
232
+ return processValue(valueBN.dividedBy(rateBN), config);
233
+ };
234
+ /**
235
+ * Calculates the fee charged for an exchange, as a percentage of the value exchanged.
236
+ * @param value The amount being exchanged.
237
+ * @param feePercentage The fee, as a percentage.
238
+ * @param config How the result is processed.
239
+ * @returns The fee.
240
+ * @example
241
+ * calculateExchangeFee(1000, 0.1); // 1
242
+ * @throws
243
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
244
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
245
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
246
+ * - INVALID_TYPE: if the processing type is not supported
247
+ * - NEGATIVE_VALUE_NOT_ALLOWED: if the value or feePercentage are not positive
248
+ */
249
+ export const calculateExchangeFee = (value, feePercentage, config) => {
250
+ const valueBN = getBigNumber(value);
251
+ const feePercentageBN = getBigNumber(feePercentage);
252
+ validatePositiveValue(valueBN);
253
+ validatePositiveValue(feePercentageBN, true);
254
+ return processValue(feePercentageBN.times(valueBN).dividedBy(100), config);
255
+ };
256
+ /**
257
+ * Calculates the average entry price of a position bought in several trades, weighting each price
258
+ * by the amount bought. An empty list, or one whose amounts add up to 0, returns 0.
259
+ * @param trades The trades, each as a `[price, amount]` tuple.
260
+ * @param config How the result is processed.
261
+ * @returns The weighted average price.
262
+ * @example
263
+ * calculateWeightedEntry([[100, 2], [130, 1]]); // 110
264
+ * calculateWeightedEntry([]); // 0
265
+ * @throws
266
+ * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error
267
+ * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason
268
+ * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported
269
+ * - INVALID_TYPE: if the processing type is not supported
270
+ * - INVALID_VALUES_ARRAY: if the provided values arg is not a valid array
271
+ */
272
+ export const calculateWeightedEntry = (trades, config) => {
273
+ validateValuesArray(trades, 'calculateWeightedEntry');
274
+ if (trades.length === 0) {
275
+ return processValue(0, config);
276
+ }
277
+ let priceTimesShares = getBigNumber(0);
278
+ let totalShares = getBigNumber(0);
279
+ trades.forEach(([price, amount]) => {
280
+ const amountBN = getBigNumber(amount);
281
+ priceTimesShares = priceTimesShares.plus(getBigNumber(price).times(amountBN));
282
+ totalShares = totalShares.plus(amountBN);
283
+ });
284
+ if (totalShares.isZero()) {
285
+ return processValue(0, config);
286
+ }
287
+ return processValue(priceTimesShares.dividedBy(totalShares), config);
288
+ };
289
+ //# sourceMappingURL=calculations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calculations.js","sourceRoot":"","sources":["../../src/calculations/calculations.ts"],"names":[],"mappings":"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","sourcesContent":["import { BigNumber } from '../shared/constants.js';\nimport type { IBigNumber, IBigNumberValue, IConfig, IOutput } from '../shared/types.js';\nimport { getBigNumber, processValue } from '../processing/processing.js';\nimport { sortBigNumbers } from '../utils/utils.js';\nimport { validatePositiveValue, validateValuesArray } from '../validations/validations.js';\n\n/*\n * Essential calculations\n */\n\n/**\n * Adds up a list of values. An empty list sums to 0.\n * @param values The values.\n * @param config How the result is processed.\n * @returns The sum.\n * @example\n * calculateSum([0.1, '0.2', 0.3]); // 0.6\n * calculateSum([0.1, 0.2], { type: 'string' }); // '0.3'\n * @throws\n * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error\n * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason\n * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported\n * - INVALID_TYPE: if the processing type is not supported\n * - INVALID_VALUES_ARRAY: if the provided values arg is not a valid array\n */\nexport const calculateSum = <T extends Partial<IConfig>>(\n values: IBigNumberValue[],\n config?: T,\n): IOutput<T> => {\n validateValuesArray(values, 'calculateSum');\n return processValue(values.length > 0 ? BigNumber.sum(...values) : 0, config);\n};\n\n/**\n * Finds the smallest value in a list. An empty list returns 0.\n * @param values The values.\n * @param config How the result is processed.\n * @returns The smallest value.\n * @example\n * calculateMin([3, '1.5', 2]); // 1.5\n * @throws\n * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error\n * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason\n * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported\n * - INVALID_TYPE: if the processing type is not supported\n * - INVALID_VALUES_ARRAY: if the provided values arg is not a valid array\n */\nexport const calculateMin = <T extends Partial<IConfig>>(\n values: IBigNumberValue[],\n config?: T,\n): IOutput<T> => {\n validateValuesArray(values, 'calculateMin');\n return processValue(values.length > 0 ? BigNumber.min(...values) : 0, config);\n};\n\n/**\n * Finds the largest value in a list. An empty list returns 0.\n * @param values The values.\n * @param config How the result is processed.\n * @returns The largest value.\n * @example\n * calculateMax([3, '1.5', 2]); // 3\n * @throws\n * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error\n * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason\n * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported\n * - INVALID_TYPE: if the processing type is not supported\n * - INVALID_VALUES_ARRAY: if the provided values arg is not a valid array\n */\nexport const calculateMax = <T extends Partial<IConfig>>(\n values: IBigNumberValue[],\n config?: T,\n): IOutput<T> => {\n validateValuesArray(values, 'calculateMax');\n return processValue(values.length > 0 ? BigNumber.max(...values) : 0, config);\n};\n\n/**\n * Calculates the mean of a list of values. An empty list returns 0.\n * @param values The values.\n * @param config How the result is processed.\n * @returns The mean.\n * @example\n * calculateMean([1, 2, 4]); // 2.33\n * @throws\n * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error\n * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason\n * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported\n * - INVALID_TYPE: if the processing type is not supported\n * - INVALID_VALUES_ARRAY: if the provided values arg is not a valid array\n */\nexport const calculateMean = <T extends Partial<IConfig>>(\n values: IBigNumberValue[],\n config?: T,\n): IOutput<T> => {\n validateValuesArray(values, 'calculateMean');\n if (values.length > 0) {\n return processValue(BigNumber.sum(...values).dividedBy(values.length), config);\n }\n return processValue(0, config);\n};\n\n/**\n * Calculates the median of a list of values: the middle value, or the mean of the two middle\n * values when the list has an even length. An empty list returns 0.\n * @param values The values.\n * @param config How the result is processed.\n * @returns The median.\n * @example\n * calculateMedian([5, 1, 3]); // 3\n * calculateMedian([4, 1, 3, 2]); // 2.5\n * @throws\n * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error\n * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason\n * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported\n * - INVALID_TYPE: if the processing type is not supported\n * - INVALID_VALUES_ARRAY: if the provided values arg is not a valid array\n */\nexport const calculateMedian = <T extends Partial<IConfig>>(\n values: IBigNumberValue[],\n config?: T,\n): IOutput<T> => {\n validateValuesArray(values, 'calculateMedian');\n if (values.length === 0) {\n return processValue(0, config);\n }\n const half = Math.floor(values.length / 2);\n const sorted = values.map(getBigNumber).sort(sortBigNumbers('asc'));\n const middle = sorted[half] as IBigNumber;\n const median =\n values.length % 2 === 1 ? middle : (sorted[half - 1] as IBigNumber).plus(middle).dividedBy(2);\n return processValue(median, config);\n};\n\n/*\n * Percentage calculations\n */\n\n/**\n * Calculates the percentage a value changed by: positive when it increased, negative when it\n * decreased, and 0 when it didn't change. The largest decrease is -100%, returned whenever the new\n * value is 0 or less.\n * @param oldValue The value before the change.\n * @param newValue The value after the change.\n * @param config How the result is processed.\n * @returns The percentage change.\n * @example\n * calculatePercentageChange(100, 125); // 25\n * calculatePercentageChange(100, 80); // -20\n * calculatePercentageChange(100, 0); // -100\n * @throws\n * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error\n * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason\n * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported\n * - INVALID_TYPE: if the processing type is not supported\n * - NEGATIVE_VALUE_NOT_ALLOWED: if the oldValue is not positive\n */\nexport const calculatePercentageChange = <T extends Partial<IConfig>>(\n oldValue: IBigNumberValue,\n newValue: IBigNumberValue,\n config?: T,\n): IOutput<T> => {\n const oldValueBN = getBigNumber(oldValue);\n const newValueBN = getBigNumber(newValue);\n validatePositiveValue(oldValueBN);\n\n let change: IBigNumber;\n if (newValueBN.isGreaterThan(oldValueBN)) {\n change = newValueBN.minus(oldValueBN).dividedBy(oldValueBN).times(100);\n } else if (oldValueBN.isGreaterThan(newValueBN)) {\n change = newValueBN.isGreaterThan(0)\n ? oldValueBN.minus(newValueBN).dividedBy(oldValueBN).times(100).negated()\n : getBigNumber(-100);\n } else {\n change = getBigNumber(0);\n }\n return processValue(change, config);\n};\n\n/**\n * Changes a value by a percentage: a positive percentage increases it, and a negative one\n * decreases it, to 0 at most.\n * @param value The value to change.\n * @param percentage The percentage to change it by.\n * @param config How the result is processed.\n * @returns The changed value.\n * @example\n * adjustByPercentage(100, 25); // 125\n * adjustByPercentage(100, -10); // 90\n * @throws\n * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error\n * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason\n * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported\n * - INVALID_TYPE: if the processing type is not supported\n * - NEGATIVE_VALUE_NOT_ALLOWED: if the value is not positive\n */\nexport const adjustByPercentage = <T extends Partial<IConfig>>(\n value: IBigNumberValue,\n percentage: IBigNumberValue,\n config?: T,\n): IOutput<T> => {\n const valueBN = getBigNumber(value);\n const percentageBN = getBigNumber(percentage);\n validatePositiveValue(valueBN);\n\n let adjusted: IBigNumber;\n if (percentageBN.isGreaterThan(0)) {\n adjusted = percentageBN.dividedBy(100).plus(1).times(valueBN);\n } else if (percentageBN.isLessThan(0)) {\n adjusted = percentageBN.isGreaterThan(-100)\n ? percentageBN.times(-1).dividedBy(100).minus(1).times(valueBN).times(-1)\n : getBigNumber(0);\n } else {\n adjusted = valueBN;\n }\n return processValue(adjusted, config);\n};\n\n/**\n * Calculates what percentage of a total a value represents.\n * @param value The part.\n * @param total The total.\n * @param config How the result is processed.\n * @returns The percentage.\n * @example\n * calculatePercentageRepresentation(25, 200); // 12.5\n * @throws\n * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error\n * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason\n * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported\n * - INVALID_TYPE: if the processing type is not supported\n * - NEGATIVE_VALUE_NOT_ALLOWED: if the value or total are not positive\n */\nexport const calculatePercentageRepresentation = <T extends Partial<IConfig>>(\n value: IBigNumberValue,\n total: IBigNumberValue,\n config?: T,\n): IOutput<T> => {\n const valueBN = getBigNumber(value);\n const totalBN = getBigNumber(total);\n validatePositiveValue(valueBN, true);\n validatePositiveValue(totalBN);\n return processValue(valueBN.times(100).dividedBy(totalBN), config);\n};\n\n/*\n * Financial calculations\n */\n\n/**\n * Calculates the amount of an asset an exchange gives for a value at a rate, such as how much\n * BTC 100 USDT buys at 65,000 USDT per BTC.\n * @param value The amount being exchanged.\n * @param rate The price of one unit of the asset received.\n * @param config How the result is processed.\n * @returns The amount received.\n * @example\n * calculateExchange(100, 65000, { decimalPlaces: 8 }); // 0.00153846\n * @throws\n * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error\n * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason\n * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported\n * - INVALID_TYPE: if the processing type is not supported\n * - NEGATIVE_VALUE_NOT_ALLOWED: if the value or rate are not positive\n */\nexport const calculateExchange = <T extends Partial<IConfig>>(\n value: IBigNumberValue,\n rate: IBigNumberValue,\n config?: T,\n): IOutput<T> => {\n const valueBN = getBigNumber(value);\n const rateBN = getBigNumber(rate);\n validatePositiveValue(valueBN);\n validatePositiveValue(rateBN);\n return processValue(valueBN.dividedBy(rateBN), config);\n};\n\n/**\n * Calculates the fee charged for an exchange, as a percentage of the value exchanged.\n * @param value The amount being exchanged.\n * @param feePercentage The fee, as a percentage.\n * @param config How the result is processed.\n * @returns The fee.\n * @example\n * calculateExchangeFee(1000, 0.1); // 1\n * @throws\n * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error\n * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason\n * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported\n * - INVALID_TYPE: if the processing type is not supported\n * - NEGATIVE_VALUE_NOT_ALLOWED: if the value or feePercentage are not positive\n */\nexport const calculateExchangeFee = <T extends Partial<IConfig>>(\n value: IBigNumberValue,\n feePercentage: IBigNumberValue,\n config?: T,\n): IOutput<T> => {\n const valueBN = getBigNumber(value);\n const feePercentageBN = getBigNumber(feePercentage);\n validatePositiveValue(valueBN);\n validatePositiveValue(feePercentageBN, true);\n return processValue(feePercentageBN.times(valueBN).dividedBy(100), config);\n};\n\n/**\n * Calculates the average entry price of a position bought in several trades, weighting each price\n * by the amount bought. An empty list, or one whose amounts add up to 0, returns 0.\n * @param trades The trades, each as a `[price, amount]` tuple.\n * @param config How the result is processed.\n * @returns The weighted average price.\n * @example\n * calculateWeightedEntry([[100, 2], [130, 1]]); // 110\n * calculateWeightedEntry([]); // 0\n * @throws\n * - INVALID_VALUE: if any of the given values isn't a finite number or BigNumber throws an error\n * - INVALID_DECIMAL_PLACES: if the number of decimal places is invalid for any reason\n * - INVALID_ROUNDING_MODE: if the rounding mode name is not supported\n * - INVALID_TYPE: if the processing type is not supported\n * - INVALID_VALUES_ARRAY: if the provided values arg is not a valid array\n */\nexport const calculateWeightedEntry = <T extends Partial<IConfig>>(\n trades: [IBigNumberValue, IBigNumberValue][],\n config?: T,\n): IOutput<T> => {\n validateValuesArray(trades, 'calculateWeightedEntry');\n if (trades.length === 0) {\n return processValue(0, config);\n }\n let priceTimesShares = getBigNumber(0);\n let totalShares = getBigNumber(0);\n trades.forEach(([price, amount]) => {\n const amountBN = getBigNumber(amount);\n priceTimesShares = priceTimesShares.plus(getBigNumber(price).times(amountBN));\n totalShares = totalShares.plus(amountBN);\n });\n if (totalShares.isZero()) {\n return processValue(0, config);\n }\n return processValue(priceTimesShares.dividedBy(totalShares), config);\n};\n"]}
@@ -0,0 +1,5 @@
1
+ export type { IBigNumber, IBigNumberFormat, IBigNumberRoundingMode, IBigNumberRoundingModeName, IBigNumberToType, IBigNumberValue, IConfig, IOutput, IType, } from './shared/types.js';
2
+ export { BigNumber } from './shared/constants.js';
3
+ export { ERRORS } from './shared/errors.js';
4
+ export { getBigNumber, isBigNumber, isFloat, isInteger, isNumber, prettifyValue, processValue, } from './processing/processing.js';
5
+ export { adjustByPercentage, calculateExchange, calculateExchangeFee, calculateMax, calculateMean, calculateMedian, calculateMin, calculatePercentageChange, calculatePercentageRepresentation, calculateSum, calculateWeightedEntry, } from './calculations/calculations.js';
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ // constructor
2
+ export { BigNumber } from './shared/constants.js';
3
+ // error codes
4
+ export { ERRORS } from './shared/errors.js';
5
+ // value processors and type checks
6
+ export { getBigNumber, isBigNumber, isFloat, isInteger, isNumber, prettifyValue, processValue, } from './processing/processing.js';
7
+ // calculations
8
+ export { adjustByPercentage, calculateExchange, calculateExchangeFee, calculateMax, calculateMean, calculateMedian, calculateMin, calculatePercentageChange, calculatePercentageRepresentation, calculateSum, calculateWeightedEntry, } from './calculations/calculations.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,cAAc;AACd,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD,cAAc;AACd,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,mCAAmC;AACnC,OAAO,EACL,YAAY,EACZ,WAAW,EACX,OAAO,EACP,SAAS,EACT,QAAQ,EACR,aAAa,EACb,YAAY,GACb,MAAM,4BAA4B,CAAC;AAEpC,eAAe;AACf,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,YAAY,EACZ,yBAAyB,EACzB,iCAAiC,EACjC,YAAY,EACZ,sBAAsB,GACvB,MAAM,gCAAgC,CAAC","sourcesContent":["// types\nexport type {\n IBigNumber,\n IBigNumberFormat,\n IBigNumberRoundingMode,\n IBigNumberRoundingModeName,\n IBigNumberToType,\n IBigNumberValue,\n IConfig,\n IOutput,\n IType,\n} from './shared/types.js';\n\n// constructor\nexport { BigNumber } from './shared/constants.js';\n\n// error codes\nexport { ERRORS } from './shared/errors.js';\n\n// value processors and type checks\nexport {\n getBigNumber,\n isBigNumber,\n isFloat,\n isInteger,\n isNumber,\n prettifyValue,\n processValue,\n} from './processing/processing.js';\n\n// calculations\nexport {\n adjustByPercentage,\n calculateExchange,\n calculateExchangeFee,\n calculateMax,\n calculateMean,\n calculateMedian,\n calculateMin,\n calculatePercentageChange,\n calculatePercentageRepresentation,\n calculateSum,\n calculateWeightedEntry,\n} from './calculations/calculations.js';\n"]}