iso-price 1.0.3
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/LICENSE +21 -0
- package/dist/contract/index.d.ts +25 -0
- package/dist/contract/index.js +60 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/domain.objects/IsoCurrency.d.ts +63 -0
- package/dist/domain.objects/IsoCurrency.js +70 -0
- package/dist/domain.objects/IsoCurrency.js.map +1 -0
- package/dist/domain.objects/IsoPrice.d.ts +16 -0
- package/dist/domain.objects/IsoPrice.js +3 -0
- package/dist/domain.objects/IsoPrice.js.map +1 -0
- package/dist/domain.objects/IsoPriceExponent.d.ts +28 -0
- package/dist/domain.objects/IsoPriceExponent.js +33 -0
- package/dist/domain.objects/IsoPriceExponent.js.map +1 -0
- package/dist/domain.objects/IsoPriceHuman.d.ts +19 -0
- package/dist/domain.objects/IsoPriceHuman.js +3 -0
- package/dist/domain.objects/IsoPriceHuman.js.map +1 -0
- package/dist/domain.objects/IsoPriceRoundMode.d.ts +23 -0
- package/dist/domain.objects/IsoPriceRoundMode.js +28 -0
- package/dist/domain.objects/IsoPriceRoundMode.js.map +1 -0
- package/dist/domain.objects/IsoPriceShape.d.ts +30 -0
- package/dist/domain.objects/IsoPriceShape.js +3 -0
- package/dist/domain.objects/IsoPriceShape.js.map +1 -0
- package/dist/domain.objects/IsoPriceWords.d.ts +20 -0
- package/dist/domain.objects/IsoPriceWords.js +3 -0
- package/dist/domain.objects/IsoPriceWords.js.map +1 -0
- package/dist/domain.operations/arithmetic/allocatePrice.d.ts +48 -0
- package/dist/domain.operations/arithmetic/allocatePrice.js +167 -0
- package/dist/domain.operations/arithmetic/allocatePrice.js.map +1 -0
- package/dist/domain.operations/arithmetic/dividePrice.d.ts +40 -0
- package/dist/domain.operations/arithmetic/dividePrice.js +127 -0
- package/dist/domain.operations/arithmetic/dividePrice.js.map +1 -0
- package/dist/domain.operations/arithmetic/multiplyPrice.d.ts +38 -0
- package/dist/domain.operations/arithmetic/multiplyPrice.js +89 -0
- package/dist/domain.operations/arithmetic/multiplyPrice.js.map +1 -0
- package/dist/domain.operations/arithmetic/subPrices.d.ts +28 -0
- package/dist/domain.operations/arithmetic/subPrices.js +62 -0
- package/dist/domain.operations/arithmetic/subPrices.js.map +1 -0
- package/dist/domain.operations/arithmetic/sumPrices.d.ts +44 -0
- package/dist/domain.operations/arithmetic/sumPrices.js +88 -0
- package/dist/domain.operations/arithmetic/sumPrices.js.map +1 -0
- package/dist/domain.operations/cast/asIsoPrice.d.ts +35 -0
- package/dist/domain.operations/cast/asIsoPrice.js +117 -0
- package/dist/domain.operations/cast/asIsoPrice.js.map +1 -0
- package/dist/domain.operations/cast/asIsoPriceHuman.d.ts +25 -0
- package/dist/domain.operations/cast/asIsoPriceHuman.js +106 -0
- package/dist/domain.operations/cast/asIsoPriceHuman.js.map +1 -0
- package/dist/domain.operations/cast/asIsoPriceShape.d.ts +25 -0
- package/dist/domain.operations/cast/asIsoPriceShape.js +164 -0
- package/dist/domain.operations/cast/asIsoPriceShape.js.map +1 -0
- package/dist/domain.operations/cast/asIsoPriceWords.d.ts +25 -0
- package/dist/domain.operations/cast/asIsoPriceWords.js +103 -0
- package/dist/domain.operations/cast/asIsoPriceWords.js.map +1 -0
- package/dist/domain.operations/guard/isIsoPrice.d.ts +18 -0
- package/dist/domain.operations/guard/isIsoPrice.js +29 -0
- package/dist/domain.operations/guard/isIsoPrice.js.map +1 -0
- package/dist/domain.operations/guard/isIsoPriceHuman.d.ts +24 -0
- package/dist/domain.operations/guard/isIsoPriceHuman.js +76 -0
- package/dist/domain.operations/guard/isIsoPriceHuman.js.map +1 -0
- package/dist/domain.operations/guard/isIsoPriceShape.d.ts +29 -0
- package/dist/domain.operations/guard/isIsoPriceShape.js +50 -0
- package/dist/domain.operations/guard/isIsoPriceShape.js.map +1 -0
- package/dist/domain.operations/guard/isIsoPriceWords.d.ts +24 -0
- package/dist/domain.operations/guard/isIsoPriceWords.js +48 -0
- package/dist/domain.operations/guard/isIsoPriceWords.js.map +1 -0
- package/dist/domain.operations/precision/getIsoPriceExponentByCurrency.d.ts +15 -0
- package/dist/domain.operations/precision/getIsoPriceExponentByCurrency.js +49 -0
- package/dist/domain.operations/precision/getIsoPriceExponentByCurrency.js.map +1 -0
- package/dist/domain.operations/precision/roundPrice.d.ts +25 -0
- package/dist/domain.operations/precision/roundPrice.js +24 -0
- package/dist/domain.operations/precision/roundPrice.js.map +1 -0
- package/dist/domain.operations/precision/setPricePrecision.d.ts +29 -0
- package/dist/domain.operations/precision/setPricePrecision.js +119 -0
- package/dist/domain.operations/precision/setPricePrecision.js.map +1 -0
- package/dist/domain.operations/statistics/calcPriceAvg.d.ts +21 -0
- package/dist/domain.operations/statistics/calcPriceAvg.js +92 -0
- package/dist/domain.operations/statistics/calcPriceAvg.js.map +1 -0
- package/dist/domain.operations/statistics/calcPriceStdev.d.ts +23 -0
- package/dist/domain.operations/statistics/calcPriceStdev.js +137 -0
- package/dist/domain.operations/statistics/calcPriceStdev.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/license.md +21 -0
- package/package.json +102 -0
- package/readme.md +373 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setPricePrecision = void 0;
|
|
4
|
+
const IsoPriceExponent_1 = require("../../domain.objects/IsoPriceExponent");
|
|
5
|
+
const IsoPriceRoundMode_1 = require("../../domain.objects/IsoPriceRoundMode");
|
|
6
|
+
const asIsoPriceShape_1 = require("../cast/asIsoPriceShape");
|
|
7
|
+
const asIsoPriceWords_1 = require("../cast/asIsoPriceWords");
|
|
8
|
+
/**
|
|
9
|
+
* .what = extracts numeric exponent from exponent string
|
|
10
|
+
* .why = enables arithmetic on exponent values
|
|
11
|
+
*/
|
|
12
|
+
const getExponentValue = (exponent) => {
|
|
13
|
+
const match = exponent.match(/\^(-?\d+)$/);
|
|
14
|
+
if (!match)
|
|
15
|
+
return -2; // default to centi
|
|
16
|
+
return parseInt(match[1], 10);
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* .what = applies round mode to a bigint value
|
|
20
|
+
* .why = enables precision decrease with correct round behavior
|
|
21
|
+
*/
|
|
22
|
+
const applyRoundMode = (value, divisor, mode) => {
|
|
23
|
+
const quotient = value / divisor;
|
|
24
|
+
const remainder = value % divisor;
|
|
25
|
+
// no remainder = no need to round
|
|
26
|
+
if (remainder === 0n)
|
|
27
|
+
return quotient;
|
|
28
|
+
const isNegative = value < 0n;
|
|
29
|
+
const absRemainder = isNegative ? -remainder : remainder;
|
|
30
|
+
const halfDivisor = divisor / 2n;
|
|
31
|
+
switch (mode) {
|
|
32
|
+
case IsoPriceRoundMode_1.IsoPriceRoundMode.FLOOR:
|
|
33
|
+
// always toward negative infinity
|
|
34
|
+
return isNegative ? quotient - 1n : quotient;
|
|
35
|
+
case IsoPriceRoundMode_1.IsoPriceRoundMode.CEIL:
|
|
36
|
+
// always toward positive infinity
|
|
37
|
+
return isNegative ? quotient : quotient + 1n;
|
|
38
|
+
case IsoPriceRoundMode_1.IsoPriceRoundMode.HALF_UP:
|
|
39
|
+
// round half toward positive infinity
|
|
40
|
+
if (absRemainder >= halfDivisor) {
|
|
41
|
+
return isNegative ? quotient - 1n : quotient + 1n;
|
|
42
|
+
}
|
|
43
|
+
return quotient;
|
|
44
|
+
case IsoPriceRoundMode_1.IsoPriceRoundMode.HALF_DOWN:
|
|
45
|
+
// round half toward zero
|
|
46
|
+
if (absRemainder > halfDivisor) {
|
|
47
|
+
return isNegative ? quotient - 1n : quotient + 1n;
|
|
48
|
+
}
|
|
49
|
+
return quotient;
|
|
50
|
+
case IsoPriceRoundMode_1.IsoPriceRoundMode.HALF_EVEN:
|
|
51
|
+
// round half to nearest even (bankers round)
|
|
52
|
+
if (absRemainder > halfDivisor) {
|
|
53
|
+
return isNegative ? quotient - 1n : quotient + 1n;
|
|
54
|
+
}
|
|
55
|
+
if (absRemainder === halfDivisor) {
|
|
56
|
+
// round to even
|
|
57
|
+
const absQuotient = isNegative ? -quotient : quotient;
|
|
58
|
+
if (absQuotient % 2n !== 0n) {
|
|
59
|
+
return isNegative ? quotient - 1n : quotient + 1n;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return quotient;
|
|
63
|
+
default:
|
|
64
|
+
return quotient;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* .what = changes the precision of a price to a target exponent
|
|
69
|
+
* .why = enables precision increase (lossless) and decrease (with round)
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* // increase precision (lossless)
|
|
73
|
+
* setPricePrecision({ of: 'USD 50.37', to: 'micro.x10^-6' })
|
|
74
|
+
* // => 'USD 50.370000'
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* // decrease precision (requires round)
|
|
78
|
+
* setPricePrecision({ of: 'USD 5.555', to: 'centi.x10^-2' })
|
|
79
|
+
* // => 'USD 5.56' (default half-up)
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* // decrease precision with explicit round mode
|
|
83
|
+
* setPricePrecision({ of: 'USD 5.555', to: 'centi.x10^-2' }, { round: 'floor' })
|
|
84
|
+
* // => 'USD 5.55'
|
|
85
|
+
*/
|
|
86
|
+
const setPricePrecision = (input, options) => {
|
|
87
|
+
// parse the input price
|
|
88
|
+
const shape = (0, asIsoPriceShape_1.asIsoPriceShape)(input.of);
|
|
89
|
+
// get current and target exponent values
|
|
90
|
+
const currentExp = getExponentValue(shape.exponent ?? IsoPriceExponent_1.IsoPriceExponent.CENTI);
|
|
91
|
+
const targetExp = getExponentValue(input.to);
|
|
92
|
+
// calculate the scale difference
|
|
93
|
+
const scaleDiff = currentExp - targetExp;
|
|
94
|
+
let newAmount;
|
|
95
|
+
if (scaleDiff > 0) {
|
|
96
|
+
// precision increase — multiply (lossless)
|
|
97
|
+
const multiplier = 10n ** BigInt(scaleDiff);
|
|
98
|
+
newAmount = shape.amount * multiplier;
|
|
99
|
+
}
|
|
100
|
+
else if (scaleDiff < 0) {
|
|
101
|
+
// precision decrease — divide with round
|
|
102
|
+
const divisor = 10n ** BigInt(-scaleDiff);
|
|
103
|
+
const mode = options?.round ?? IsoPriceRoundMode_1.IsoPriceRoundMode.HALF_UP;
|
|
104
|
+
newAmount = applyRoundMode(shape.amount, divisor, mode);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// same precision
|
|
108
|
+
newAmount = shape.amount;
|
|
109
|
+
}
|
|
110
|
+
// build new shape with target exponent
|
|
111
|
+
const newShape = {
|
|
112
|
+
amount: newAmount,
|
|
113
|
+
currency: shape.currency,
|
|
114
|
+
exponent: input.to,
|
|
115
|
+
};
|
|
116
|
+
return (0, asIsoPriceWords_1.asIsoPriceWords)(newShape);
|
|
117
|
+
};
|
|
118
|
+
exports.setPricePrecision = setPricePrecision;
|
|
119
|
+
//# sourceMappingURL=setPricePrecision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setPricePrecision.js","sourceRoot":"","sources":["../../../src/domain.operations/precision/setPricePrecision.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { IsoPrice } from '../../domain.objects/IsoPrice';
|
|
2
|
+
import type { IsoPriceShape } from '../../domain.objects/IsoPriceShape';
|
|
3
|
+
import type { IsoPriceWords } from '../../domain.objects/IsoPriceWords';
|
|
4
|
+
/**
|
|
5
|
+
* .what = calculates the average of an array of prices
|
|
6
|
+
* .why = enables statistical analysis of price data with precision safety
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* calcPriceAvg(['USD 10.00', 'USD 20.00', 'USD 30.00'])
|
|
10
|
+
* // => 'USD 20.00'
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* calcPriceAvg(['USD 10.00', 'USD 20.00'], { format: 'shape' })
|
|
14
|
+
* // => { amount: 1500n, currency: 'USD', exponent: 'centi.x10^-2' }
|
|
15
|
+
*/
|
|
16
|
+
export declare function calcPriceAvg<TCurrency extends string = string>(prices: (IsoPrice<TCurrency> | string)[], options?: {
|
|
17
|
+
format?: 'words';
|
|
18
|
+
}): IsoPriceWords<TCurrency>;
|
|
19
|
+
export declare function calcPriceAvg<TCurrency extends string = string>(prices: (IsoPrice<TCurrency> | string)[], options: {
|
|
20
|
+
format: 'shape';
|
|
21
|
+
}): IsoPriceShape<TCurrency>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.calcPriceAvg = void 0;
|
|
4
|
+
const helpful_errors_1 = require("helpful-errors");
|
|
5
|
+
const IsoPriceExponent_1 = require("../../domain.objects/IsoPriceExponent");
|
|
6
|
+
const asIsoPriceShape_1 = require("../cast/asIsoPriceShape");
|
|
7
|
+
const asIsoPriceWords_1 = require("../cast/asIsoPriceWords");
|
|
8
|
+
function calcPriceAvg(prices, options) {
|
|
9
|
+
// validate input
|
|
10
|
+
if (prices.length === 0) {
|
|
11
|
+
throw new helpful_errors_1.BadRequestError('cannot calculate average of empty array', {
|
|
12
|
+
prices,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
// convert all prices to shapes
|
|
16
|
+
const shapes = prices.map((p) => (0, asIsoPriceShape_1.asIsoPriceShape)(p));
|
|
17
|
+
// validate all currencies match
|
|
18
|
+
const currency = shapes[0].currency;
|
|
19
|
+
const mismatch = shapes.find((s) => s.currency !== currency);
|
|
20
|
+
if (mismatch) {
|
|
21
|
+
throw new helpful_errors_1.BadRequestError('cannot calculate average of mixed currencies', {
|
|
22
|
+
expected: currency,
|
|
23
|
+
found: mismatch.currency,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
// find highest precision exponent (most negative = highest precision)
|
|
27
|
+
const exponents = shapes.map((s) => s.exponent ?? IsoPriceExponent_1.IsoPriceExponent.CENTI);
|
|
28
|
+
const targetExponent = findHighestPrecisionExponent(exponents);
|
|
29
|
+
// normalize all amounts to target exponent
|
|
30
|
+
const normalizedAmounts = shapes.map((s) => normalizeAmount(s.amount, s.exponent ?? IsoPriceExponent_1.IsoPriceExponent.CENTI, targetExponent));
|
|
31
|
+
// calculate sum
|
|
32
|
+
const sum = normalizedAmounts.reduce((acc, amt) => acc + amt, 0n);
|
|
33
|
+
// calculate average via bigint division (truncates toward zero)
|
|
34
|
+
const count = BigInt(prices.length);
|
|
35
|
+
const avgAmount = sum / count;
|
|
36
|
+
// build result shape
|
|
37
|
+
const resultShape = {
|
|
38
|
+
amount: avgAmount,
|
|
39
|
+
currency,
|
|
40
|
+
exponent: targetExponent,
|
|
41
|
+
};
|
|
42
|
+
if (options?.format === 'shape') {
|
|
43
|
+
return resultShape;
|
|
44
|
+
}
|
|
45
|
+
return (0, asIsoPriceWords_1.asIsoPriceWords)(resultShape);
|
|
46
|
+
}
|
|
47
|
+
exports.calcPriceAvg = calcPriceAvg;
|
|
48
|
+
/**
|
|
49
|
+
* .what = finds the highest precision exponent from an array
|
|
50
|
+
* .why = ensures no precision loss when amounts are normalized
|
|
51
|
+
*/
|
|
52
|
+
const findHighestPrecisionExponent = (exponents) => {
|
|
53
|
+
let highestPrecision = exponents[0];
|
|
54
|
+
let highestValue = getExponentValue(highestPrecision);
|
|
55
|
+
for (const exp of exponents) {
|
|
56
|
+
const value = getExponentValue(exp);
|
|
57
|
+
if (value < highestValue) {
|
|
58
|
+
highestValue = value;
|
|
59
|
+
highestPrecision = exp;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return highestPrecision;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* .what = extracts numeric exponent value from exponent string
|
|
66
|
+
* .why = enables comparison of exponent precision
|
|
67
|
+
*/
|
|
68
|
+
const getExponentValue = (exponent) => {
|
|
69
|
+
const match = exponent.match(/\^(-?\d+)$/);
|
|
70
|
+
if (!match)
|
|
71
|
+
return -2; // default to centi
|
|
72
|
+
return parseInt(match[1], 10);
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* .what = normalizes amount from source exponent to target exponent
|
|
76
|
+
* .why = enables arithmetic on amounts with different precisions
|
|
77
|
+
*/
|
|
78
|
+
const normalizeAmount = (amount, sourceExponent, targetExponent) => {
|
|
79
|
+
const sourceValue = getExponentValue(sourceExponent);
|
|
80
|
+
const targetValue = getExponentValue(targetExponent);
|
|
81
|
+
const diff = sourceValue - targetValue;
|
|
82
|
+
if (diff > 0) {
|
|
83
|
+
// source has lower precision, multiply to increase
|
|
84
|
+
return amount * 10n ** BigInt(diff);
|
|
85
|
+
}
|
|
86
|
+
if (diff < 0) {
|
|
87
|
+
// source has higher precision, divide to decrease (should not happen if target is highest)
|
|
88
|
+
return amount / 10n ** BigInt(-diff);
|
|
89
|
+
}
|
|
90
|
+
return amount;
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=calcPriceAvg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calcPriceAvg.js","sourceRoot":"","sources":["../../../src/domain.operations/statistics/calcPriceAvg.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { IsoPrice } from '../../domain.objects/IsoPrice';
|
|
2
|
+
import type { IsoPriceShape } from '../../domain.objects/IsoPriceShape';
|
|
3
|
+
import type { IsoPriceWords } from '../../domain.objects/IsoPriceWords';
|
|
4
|
+
/**
|
|
5
|
+
* .what = calculates the standard deviation of an array of prices
|
|
6
|
+
* .why = enables statistical analysis of price variance with precision safety
|
|
7
|
+
*
|
|
8
|
+
* uses population standard deviation formula: sqrt(sum((x - mean)^2) / n)
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* calcPriceStdev(['USD 10.00', 'USD 20.00', 'USD 30.00'])
|
|
12
|
+
* // => 'USD 8.16' (approx)
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* calcPriceStdev(['USD 10.00', 'USD 20.00'], { format: 'shape' })
|
|
16
|
+
* // => { amount: 500n, currency: 'USD', exponent: 'centi.x10^-2' }
|
|
17
|
+
*/
|
|
18
|
+
export declare function calcPriceStdev<TCurrency extends string = string>(prices: (IsoPrice<TCurrency> | string)[], options?: {
|
|
19
|
+
format?: 'words';
|
|
20
|
+
}): IsoPriceWords<TCurrency>;
|
|
21
|
+
export declare function calcPriceStdev<TCurrency extends string = string>(prices: (IsoPrice<TCurrency> | string)[], options: {
|
|
22
|
+
format: 'shape';
|
|
23
|
+
}): IsoPriceShape<TCurrency>;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.calcPriceStdev = void 0;
|
|
4
|
+
const helpful_errors_1 = require("helpful-errors");
|
|
5
|
+
const IsoPriceExponent_1 = require("../../domain.objects/IsoPriceExponent");
|
|
6
|
+
const asIsoPriceShape_1 = require("../cast/asIsoPriceShape");
|
|
7
|
+
const asIsoPriceWords_1 = require("../cast/asIsoPriceWords");
|
|
8
|
+
function calcPriceStdev(prices, options) {
|
|
9
|
+
// validate input
|
|
10
|
+
if (prices.length === 0) {
|
|
11
|
+
throw new helpful_errors_1.BadRequestError('cannot calculate stdev of empty array', {
|
|
12
|
+
prices,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
// single value has zero variance
|
|
16
|
+
if (prices.length === 1) {
|
|
17
|
+
const shape = (0, asIsoPriceShape_1.asIsoPriceShape)(prices[0]);
|
|
18
|
+
const resultShape = {
|
|
19
|
+
amount: 0n,
|
|
20
|
+
currency: shape.currency,
|
|
21
|
+
exponent: (shape.exponent ?? IsoPriceExponent_1.IsoPriceExponent.CENTI),
|
|
22
|
+
};
|
|
23
|
+
if (options?.format === 'shape')
|
|
24
|
+
return resultShape;
|
|
25
|
+
return (0, asIsoPriceWords_1.asIsoPriceWords)(resultShape);
|
|
26
|
+
}
|
|
27
|
+
// convert all prices to shapes
|
|
28
|
+
const shapes = prices.map((p) => (0, asIsoPriceShape_1.asIsoPriceShape)(p));
|
|
29
|
+
// validate all currencies match
|
|
30
|
+
const currency = shapes[0].currency;
|
|
31
|
+
const mismatch = shapes.find((s) => s.currency !== currency);
|
|
32
|
+
if (mismatch) {
|
|
33
|
+
throw new helpful_errors_1.BadRequestError('cannot calculate stdev of mixed currencies', {
|
|
34
|
+
expected: currency,
|
|
35
|
+
found: mismatch.currency,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
// find highest precision exponent
|
|
39
|
+
const exponents = shapes.map((s) => s.exponent ?? IsoPriceExponent_1.IsoPriceExponent.CENTI);
|
|
40
|
+
const targetExponent = findHighestPrecisionExponent(exponents);
|
|
41
|
+
// normalize all amounts to target exponent
|
|
42
|
+
const normalizedAmounts = shapes.map((s) => normalizeAmount(s.amount, s.exponent ?? IsoPriceExponent_1.IsoPriceExponent.CENTI, targetExponent));
|
|
43
|
+
// calculate mean
|
|
44
|
+
const sum = normalizedAmounts.reduce((acc, amt) => acc + amt, 0n);
|
|
45
|
+
const count = BigInt(prices.length);
|
|
46
|
+
const mean = sum / count;
|
|
47
|
+
// calculate sum of squared deviations
|
|
48
|
+
// note: we use number for intermediate sqrt calculation, then convert back
|
|
49
|
+
let sumSquaredDev = 0n;
|
|
50
|
+
for (const amt of normalizedAmounts) {
|
|
51
|
+
const dev = amt - mean;
|
|
52
|
+
sumSquaredDev += dev * dev;
|
|
53
|
+
}
|
|
54
|
+
// calculate variance and stdev
|
|
55
|
+
// variance = sumSquaredDev / n (population stdev)
|
|
56
|
+
const variance = sumSquaredDev / count;
|
|
57
|
+
// sqrt via Newton's method for bigint (integer sqrt)
|
|
58
|
+
const stdevAmount = bigintSqrt(variance);
|
|
59
|
+
// build result shape
|
|
60
|
+
const resultShape = {
|
|
61
|
+
amount: stdevAmount,
|
|
62
|
+
currency,
|
|
63
|
+
exponent: targetExponent,
|
|
64
|
+
};
|
|
65
|
+
if (options?.format === 'shape') {
|
|
66
|
+
return resultShape;
|
|
67
|
+
}
|
|
68
|
+
return (0, asIsoPriceWords_1.asIsoPriceWords)(resultShape);
|
|
69
|
+
}
|
|
70
|
+
exports.calcPriceStdev = calcPriceStdev;
|
|
71
|
+
/**
|
|
72
|
+
* .what = integer square root via Newton's method
|
|
73
|
+
* .why = enables sqrt calculation for bigint values
|
|
74
|
+
*/
|
|
75
|
+
const bigintSqrt = (n) => {
|
|
76
|
+
if (n < 0n) {
|
|
77
|
+
throw new helpful_errors_1.BadRequestError('cannot calculate sqrt of negative number', {
|
|
78
|
+
n,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (n === 0n)
|
|
82
|
+
return 0n;
|
|
83
|
+
if (n === 1n)
|
|
84
|
+
return 1n;
|
|
85
|
+
// initial guess
|
|
86
|
+
let x = n;
|
|
87
|
+
let y = (x + 1n) / 2n;
|
|
88
|
+
// Newton's method iteration
|
|
89
|
+
while (y < x) {
|
|
90
|
+
x = y;
|
|
91
|
+
y = (x + n / x) / 2n;
|
|
92
|
+
}
|
|
93
|
+
return x;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* .what = finds the highest precision exponent from an array
|
|
97
|
+
* .why = ensures no precision loss when amounts are normalized
|
|
98
|
+
*/
|
|
99
|
+
const findHighestPrecisionExponent = (exponents) => {
|
|
100
|
+
let highestPrecision = exponents[0];
|
|
101
|
+
let highestValue = getExponentValue(highestPrecision);
|
|
102
|
+
for (const exp of exponents) {
|
|
103
|
+
const value = getExponentValue(exp);
|
|
104
|
+
if (value < highestValue) {
|
|
105
|
+
highestValue = value;
|
|
106
|
+
highestPrecision = exp;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return highestPrecision;
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* .what = extracts numeric exponent value from exponent string
|
|
113
|
+
* .why = enables comparison of exponent precision
|
|
114
|
+
*/
|
|
115
|
+
const getExponentValue = (exponent) => {
|
|
116
|
+
const match = exponent.match(/\^(-?\d+)$/);
|
|
117
|
+
if (!match)
|
|
118
|
+
return -2; // default to centi
|
|
119
|
+
return parseInt(match[1], 10);
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* .what = normalizes amount from source exponent to target exponent
|
|
123
|
+
* .why = enables arithmetic on amounts with different precisions
|
|
124
|
+
*/
|
|
125
|
+
const normalizeAmount = (amount, sourceExponent, targetExponent) => {
|
|
126
|
+
const sourceValue = getExponentValue(sourceExponent);
|
|
127
|
+
const targetValue = getExponentValue(targetExponent);
|
|
128
|
+
const diff = sourceValue - targetValue;
|
|
129
|
+
if (diff > 0) {
|
|
130
|
+
return amount * 10n ** BigInt(diff);
|
|
131
|
+
}
|
|
132
|
+
if (diff < 0) {
|
|
133
|
+
return amount / 10n ** BigInt(-diff);
|
|
134
|
+
}
|
|
135
|
+
return amount;
|
|
136
|
+
};
|
|
137
|
+
//# sourceMappingURL=calcPriceStdev.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calcPriceStdev.js","sourceRoot":"","sources":["../../../src/domain.operations/statistics/calcPriceStdev.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './contract/index';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./contract/index"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,mDAAiC"}
|
package/license.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 ehmpathy
|
|
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/package.json
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "iso-price",
|
|
3
|
+
"author": "ehmpathy",
|
|
4
|
+
"description": "type-safe price representation with sub-cent precision, iso 4217 currency support, and lossless arithmetic",
|
|
5
|
+
"version": "1.0.3",
|
|
6
|
+
"repository": "ehmpathy/iso-price",
|
|
7
|
+
"homepage": "https://github.com/ehmpathy/iso-price",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"price",
|
|
10
|
+
"currency",
|
|
11
|
+
"money",
|
|
12
|
+
"iso-4217",
|
|
13
|
+
"bigint",
|
|
14
|
+
"precision",
|
|
15
|
+
"arithmetic",
|
|
16
|
+
"typescript"
|
|
17
|
+
],
|
|
18
|
+
"bugs": "https://github.com/ehmpathy/iso-price/issues",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"main": "dist/index.js",
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=8.0.0"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"/dist"
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"domain-glossaries": "1.0.0",
|
|
29
|
+
"helpful-errors": "1.5.3"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"domain-objects": ">=0.24.2"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@biomejs/biome": "2.3.8",
|
|
36
|
+
"@commitlint/cli": "19.5.0",
|
|
37
|
+
"@commitlint/config-conventional": "19.5.0",
|
|
38
|
+
"@swc/core": "1.15.3",
|
|
39
|
+
"@swc/jest": "0.2.39",
|
|
40
|
+
"@tsconfig/node20": "20.1.5",
|
|
41
|
+
"@tsconfig/strictest": "2.0.5",
|
|
42
|
+
"@types/jest": "30.0.0",
|
|
43
|
+
"@types/node": "22.15.21",
|
|
44
|
+
"cz-conventional-changelog": "3.3.0",
|
|
45
|
+
"declapract": "0.13.14",
|
|
46
|
+
"declapract-typescript-ehmpathy": "0.47.27",
|
|
47
|
+
"declastruct": "1.7.3",
|
|
48
|
+
"declastruct-github": "1.3.0",
|
|
49
|
+
"depcheck": "1.4.3",
|
|
50
|
+
"esbuild-register": "3.6.0",
|
|
51
|
+
"husky": "8.0.3",
|
|
52
|
+
"jest": "30.2.0",
|
|
53
|
+
"rhachet": "1.22.7",
|
|
54
|
+
"rhachet-roles-bhrain": "0.5.11",
|
|
55
|
+
"rhachet-roles-bhuild": "0.6.6",
|
|
56
|
+
"rhachet-roles-ehmpathy": "1.17.20",
|
|
57
|
+
"test-fns": "1.4.2",
|
|
58
|
+
"tsc-alias": "1.8.10",
|
|
59
|
+
"tsx": "4.20.6",
|
|
60
|
+
"typescript": "5.4.5",
|
|
61
|
+
"yalc": "1.0.0-pre.53"
|
|
62
|
+
},
|
|
63
|
+
"config": {
|
|
64
|
+
"commitizen": {
|
|
65
|
+
"path": "./node_modules/cz-conventional-changelog"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"scripts": {
|
|
69
|
+
"build:ts": "tsc -p ./tsconfig.build.json",
|
|
70
|
+
"commit:with-cli": "npx cz",
|
|
71
|
+
"fix:format:biome": "biome check --write",
|
|
72
|
+
"fix:format": "npm run fix:format:biome",
|
|
73
|
+
"fix:lint": "biome check --write",
|
|
74
|
+
"fix": "npm run fix:format && npm run fix:lint",
|
|
75
|
+
"build:clean:bun": "rm -f ./bin/*.bc",
|
|
76
|
+
"build:clean:tsc": "(chmod -R u+w dist 2>/dev/null || true) && rm -rf dist/",
|
|
77
|
+
"build:clean": "npm run build:clean:tsc && npm run build:clean:bun",
|
|
78
|
+
"build:compile:tsc": "tsc -p ./tsconfig.build.json && tsc-alias -p ./tsconfig.build.json",
|
|
79
|
+
"build:compile": "npm run build:compile:tsc && npm run build:compile:bun --if-present",
|
|
80
|
+
"build": "npm run build:clean && npm run build:compile && npm run build:complete --if-present",
|
|
81
|
+
"test:auth": "[ \"$ECHO\" = 'true' ] && echo '. .agent/repo=.this/role=any/skills/use.apikeys.sh' || . .agent/repo=.this/role=any/skills/use.apikeys.sh",
|
|
82
|
+
"test:commits": "LAST_TAG=$(git describe --tags --abbrev=0 @^ 2> /dev/null || git rev-list --max-parents=0 HEAD) && npx commitlint --from $LAST_TAG --to HEAD --verbose",
|
|
83
|
+
"test:types": "tsc -p ./tsconfig.json --noEmit",
|
|
84
|
+
"test:format:biome": "biome format",
|
|
85
|
+
"test:format": "npm run test:format:biome",
|
|
86
|
+
"test:lint:deps": "npx depcheck -c ./.depcheckrc.yml",
|
|
87
|
+
"test:lint:biome": "biome check --diagnostic-level=error",
|
|
88
|
+
"test:lint:biome:all": "biome check",
|
|
89
|
+
"test:lint": "npm run test:lint:biome && npm run test:lint:deps",
|
|
90
|
+
"test:unit": "jest -c ./jest.unit.config.ts --forceExit --verbose --passWithNoTests $([ -z $THOROUGH ] && echo '--changedSince=main') $([ -n $RESNAP ] && echo '--updateSnapshot')",
|
|
91
|
+
"test:integration": "jest -c ./jest.integration.config.ts --forceExit --verbose --passWithNoTests $([ -z $THOROUGH ] && echo '--changedSince=main') $([ -n $RESNAP ] && echo '--updateSnapshot')",
|
|
92
|
+
"test:acceptance:locally": "npm run build && LOCALLY=true jest -c ./jest.acceptance.config.ts --forceExit --verbose --runInBand --passWithNoTests $([ -n $RESNAP ] && echo '--updateSnapshot')",
|
|
93
|
+
"test": "eval $(ECHO=true npm run --silent test:auth) && npm run test:commits && npm run test:types && npm run test:format && npm run test:lint && npm run test:unit && npm run test:integration && npm run test:acceptance:locally",
|
|
94
|
+
"test:acceptance": "npm run build && jest -c ./jest.acceptance.config.ts --forceExit --verbose --runInBand --passWithNoTests $([ -n $RESNAP ] && echo '--updateSnapshot')",
|
|
95
|
+
"prepush": "npm run test && npm run build",
|
|
96
|
+
"prepublish": "npm run build",
|
|
97
|
+
"preversion": "npm run prepush",
|
|
98
|
+
"postversion": "git push origin HEAD --tags --no-verify",
|
|
99
|
+
"prepare:husky": "husky install && chmod ug+x .husky/*",
|
|
100
|
+
"prepare:rhachet": "rhachet init --roles behaver mechanic reviewer"
|
|
101
|
+
}
|
|
102
|
+
}
|