ohlcv-indicators 3.5.52 → 3.5.54

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.
@@ -1,23 +1,98 @@
1
- export const calcPrecisionMultiplier = (main, firstRow) => {
2
- const {precision} = main
1
+ // ------------------------------
2
+ // calcPrecisionMultiplier (unchanged semantics)
3
+ // ------------------------------
4
+ export const calcPrecisionMultiplier = main => {
5
+ const { precision, firstRow, initialPriceBased } = main;
6
+ if (precision === false) return 1
3
7
 
4
- if(precision === false) return 1
8
+ const multipliers = []
5
9
 
6
- let output = 1
10
+ const err = 'If "precision" is set to true all the "open", "high", "low" and "close" values must be numeric strings like "123.45" with no commas or currency symbols.'
7
11
 
8
- for(const [key, value] of Object.entries(firstRow))
9
- {
10
- if(new Set(['open', 'high', 'low', 'close']).has(key) === false) continue
11
- const [, decimals = ''] = String(value).split('.')
12
- const decimalPrecision = Math.max(4, decimals.length)
13
- const multiplier = decimalPrecision > 1 ? Math.pow(10, decimalPrecision - 1) : 1
12
+ for (const [key, strNum] of Object.entries(firstRow)) {
14
13
 
15
- if(multiplier > output)
16
- {
17
- output = multiplier
18
- }
14
+ if(!initialPriceBased.has(key)) continue
15
+
16
+ if (typeof strNum !== 'string' || !/^-?\d+(\.\d+)?$/.test(strNum)) {
17
+ throw new Error(`${err}
18
+ Property "${key}" had the value "${strNum}" type ${typeof strNum}.`)
19
19
  }
20
20
 
21
- return output
21
+ if (!new Set(['open', 'high', 'low', 'close']).has(key)) continue;
22
22
 
23
- }
23
+ const [, decimals = ''] = strNum.split('.')
24
+ const decimalPrecision = Math.max(4, decimals.length)
25
+ multipliers.push(Math.pow(10, decimalPrecision)) // returns Number
26
+ }
27
+
28
+ if (multipliers.length === 0) return 1
29
+ return Math.max(...multipliers)
30
+ }
31
+
32
+ // ------------------------------
33
+ // Precise BigInt-based helpers (no `n` literals)
34
+ // ------------------------------
35
+ export const addPrecisionExact = (strNum, mul) => {
36
+ if (typeof strNum !== 'string') throw new Error('addPrecisionExact expects a string when precision=true')
37
+
38
+ const negative = strNum.charAt(0) === '-'
39
+ const s = negative ? strNum.slice(1) : strNum
40
+ const [intPart, decPart = ''] = s.split('.')
41
+
42
+ // Compute decimals from mul (mul expected as 10^decimals)
43
+ const mulStr = String(mul);
44
+ const decimals = (mul === 1) ? 0 : (mulStr.length - 1)
45
+
46
+ const decPadded = decPart.padEnd(decimals, '0').slice(0, decimals); // pad or trim
47
+ const integerString = intPart + decPadded // e.g. "12" + "3400" => "123400"
48
+
49
+ // Use BigInt constructor (no n-literals)
50
+ let result = BigInt(integerString || '0'); // handle "0" or empty
51
+ if (negative) result = -result
52
+ return result
53
+ }
54
+
55
+ export const revertPrecisionExact = (num, mul) => {
56
+
57
+
58
+ // Convert input to BigInt without using literal suffixes
59
+ const big = (typeof num === 'bigint') ? num : BigInt(parseInt(num))
60
+ const neg = big < BigInt(0)
61
+ const abs = neg ? -big : big
62
+
63
+ const mulBig = BigInt(mul);
64
+ if (mulBig === BigInt(1)) {
65
+ const s = abs.toString()
66
+ return neg ? `-${s}` : s
67
+ }
68
+
69
+ const intPart = abs / mulBig
70
+ const fracPart = abs % mulBig
71
+
72
+ const decimals = String(mul).length - 1
73
+ const fracStr = fracPart.toString().padStart(decimals, '0')
74
+
75
+ // Remove trailing zeros in fractional part (optional)
76
+ const trimmedFrac = fracStr.replace(/0+$/, '')
77
+ const result = trimmedFrac.length === 0
78
+ ? intPart.toString()
79
+ : `${intPart.toString()}.${trimmedFrac}`
80
+
81
+ return neg ? `-${result}` : result
82
+ }
83
+
84
+ // ------------------------------
85
+ // Convenience wrappers (Numbers) — may lose precision
86
+ // ------------------------------
87
+ export const addPrecisionAsNumber = (strNum, mul) => {
88
+
89
+ const big = addPrecisionExact(strNum, mul)
90
+
91
+ return Number(big)
92
+ }
93
+
94
+ export const revertPrecisionAsNumber = (num, mul) => {
95
+
96
+ const asStr = revertPrecisionExact(num, mul)
97
+ return Number(asStr)
98
+ }
@@ -16,7 +16,7 @@ export const validateInputParams = (inputParams, len) => {
16
16
  }
17
17
 
18
18
  export const isAlreadyComputed = main => {
19
- if(main.isComputed === true) throw Error('ohlcv is already computed, you can not add new indicators after "compute", "getLastValues" , "getDataAsCols" or "getData" methods are called.')
19
+ if(main.isComputed === true) throw Error('ohlcv is already computed, you can not add new indicators after "compute", "getLastValues" or "getData" methods are called.')
20
20
  }
21
21
 
22
22
  export const validateArray = (arr, paramName, callerName) => {
@@ -1,8 +1,11 @@
1
1
  import { dateOutputFormaters } from "./dateUtilities.js"
2
+ import { outputNumberFormatter } from "./numberUtilities.js"
2
3
 
3
4
  export const verticalToHorizontal = ({main, skipNull = false, startIndex = 0, dateFormat}) => {
4
5
 
5
- const {verticalOhlcv, invalidValueIndex, len, verticalOhlcvKeyNames, verticalOhlcvTempCols} = main
6
+ const {verticalOhlcv, invalidValueIndex, len, verticalOhlcvKeyNames, verticalOhlcvTempCols, priceBased, precision, precisionMultiplier} = main
7
+
8
+
6
9
 
7
10
  if (verticalOhlcvKeyNames.length === 0) return []
8
11
 
@@ -10,8 +13,18 @@ export const verticalToHorizontal = ({main, skipNull = false, startIndex = 0, da
10
13
  const maxStartIndex = Math.max(skipNullIndex, startIndex)
11
14
  const diffLen = len - maxStartIndex
12
15
  const result = Array.from({ length: diffLen }, () => ({}))
16
+ const formatter = {}
17
+
18
+ for(const key of verticalOhlcvKeyNames) {
19
+ if(precision && priceBased.has(key)) {
20
+ formatter[key] = (num, mul) => outputNumberFormatter.precisionNumberCleanString(num, mul)
21
+ }
22
+ else {
23
+ formatter[key] = v => v
24
+ }
25
+ }
13
26
 
14
- for(const [key, arr] of Object.entries(verticalOhlcv)){
27
+ for(const [key, arr] of Object.entries(verticalOhlcv)){
15
28
  if(verticalOhlcvTempCols.has(key)) continue
16
29
 
17
30
  for (let i = maxStartIndex; i < len; i++)
@@ -22,7 +35,7 @@ export const verticalToHorizontal = ({main, skipNull = false, startIndex = 0, da
22
35
  }
23
36
  else
24
37
  {
25
- result[i - maxStartIndex][key] = arr[i]
38
+ result[i - maxStartIndex][key] = formatter[key](arr[i], precisionMultiplier)
26
39
  }
27
40
  }
28
41
  }
@@ -1,98 +0,0 @@
1
- import { FasterSMA } from "trading-signals";
2
-
3
- // Normalize the array (z-score normalization)
4
- const smoothNormalize = (arr, smoothing) => {
5
- const len = arr.length;
6
- const instance = new FasterSMA(smoothing);
7
- const smoothedArr = new Array(len);
8
- let sum = 0;
9
- let sumSq = 0;
10
- let count = 0;
11
-
12
- for (let x = 0; x < len; x++) {
13
- instance.update(arr[x]);
14
-
15
- if (instance.isStable) {
16
- const value = instance.getResult();
17
- smoothedArr[x] = value;
18
- sum += value;
19
- sumSq += value * value;
20
- count++;
21
- } else {
22
- smoothedArr[x] = null;
23
- }
24
- }
25
-
26
- const meanValue = sum / count;
27
- const variance = (sumSq / count) - (meanValue * meanValue);
28
- const stdDev = Math.sqrt(variance);
29
-
30
- return smoothedArr.map(val => (val !== null ? (val - meanValue) / stdDev : null));
31
- }
32
-
33
- // Calculate Pearson Correlation
34
- export const correlation = (x, y, smoothing = 5) => {
35
- if (x.length !== y.length) {
36
- throw new Error('Both arrays must have the same length');
37
- }
38
-
39
- const normX = smoothNormalize(x, smoothing);
40
- const normY = smoothNormalize(y, smoothing);
41
- const n = x.length;
42
- const result = [];
43
-
44
- let sumX = 0, sumY = 0, sumXY = 0;
45
- let sumXSquare = 0, sumYSquare = 0;
46
- let count = 0;
47
-
48
- // Find the first index where both normX and normY have valid values
49
- let firstValidIndex = -1;
50
- for (let i = 0; i < n; i++) {
51
- if (normX[i] !== null && normY[i] !== null) {
52
- firstValidIndex = i;
53
- break;
54
- } else {
55
- result.push(0); // Fill initial invalid indices with 0
56
- }
57
- }
58
-
59
- if (firstValidIndex === -1) {
60
- // No valid data points
61
- return new Array(n).fill(0);
62
- }
63
-
64
- for (let i = firstValidIndex; i < n; i++) {
65
- const xVal = normX[i];
66
- const yVal = normY[i];
67
-
68
- if (xVal === null || yVal === null) {
69
- // Reset sums and counts when encountering null values
70
- sumX = sumY = sumXY = sumXSquare = sumYSquare = count = 0;
71
- result.push(0);
72
- continue;
73
- }
74
-
75
- sumX += xVal;
76
- sumY += yVal;
77
- sumXY += xVal * yVal;
78
- sumXSquare += xVal * xVal;
79
- sumYSquare += yVal * yVal;
80
- count++;
81
-
82
- if (count < 2) {
83
- result.push(0); // Not enough data points to calculate correlation
84
- continue;
85
- }
86
-
87
- const numerator = (count * sumXY) - (sumX * sumY);
88
- const denominator = Math.sqrt(
89
- (count * sumXSquare - sumX * sumX) *
90
- (count * sumYSquare - sumY * sumY)
91
- );
92
-
93
- const correlation = denominator === 0 ? 0 : numerator / denominator;
94
- result.push(correlation);
95
- }
96
-
97
- return result;
98
- }