ohlcv-indicators 3.5.54 → 3.5.55

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/index.js CHANGED
@@ -924,13 +924,19 @@ export default class OHLCV_INDICATORS {
924
924
 
925
925
  validateObject(options, 'options', methodName)
926
926
 
927
- const {lag = 0} = options
927
+ const {lag = 0, isPriceBased = false} = options
928
928
 
929
929
  validateArray(newCols, 'newCols', methodName)
930
930
  validateNumber(lag, {min: 0, allowDecimals: false}, 'options.lag', methodName)
931
+ validateBoolean(isPriceBased, 'isPriceBased', 'mapCols')
932
+
933
+ if(this.precision === false && isPriceBased) {
934
+ throw new Error(`Invalid param: If "mapCols.options.isPriceBased" is true, the "constructor.precision" param must be also true.`)
935
+ }
936
+
931
937
  const order = getOrderFromArray(newCols, methodName)
932
938
 
933
- this.inputParams.push({key: methodName, order, params: [newCols, callback, {lag}]})
939
+ this.inputParams.push({key: methodName, order, params: [newCols, callback, {lag, isPriceBased}]})
934
940
 
935
941
  return this
936
942
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ohlcv-indicators",
3
- "version": "3.5.54",
3
+ "version": "3.5.55",
4
4
  "description": "A JavaScript library featuring optimized trading indicators for OHLCV arrays [{open, high, low, close, value}]. This library is optimized for backtesting and trading bot development.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -291,7 +291,7 @@ export const classifier = (
291
291
  {
292
292
  uniqueLabels[loopIdx] = countUniqueLabels(yRows)
293
293
 
294
- if(uniqueLabels[loopIdx] < 2)
294
+ if(uniqueLabels[loopIdx] < 1)
295
295
  {
296
296
  throw new Error(`Invalid number or labels in ${prefix}. Check the logic of your "yCallback" function.`)
297
297
  }
@@ -1,12 +1,9 @@
1
- export const mapCols = (main, index, newCols, callback, {lag}) => {
1
+ export const mapCols = (main, index, newCols, callback, {lag, isPriceBased}) => {
2
2
 
3
-
4
-
5
- const {verticalOhlcv, len} = main
3
+ const {verticalOhlcv, len, priceBased, precision} = main
6
4
 
7
5
  if(index === 0)
8
6
  {
9
-
10
7
  for(const key of newCols)
11
8
  {
12
9
  if(verticalOhlcv.hasOwnProperty(key)) {
@@ -14,6 +11,11 @@ export const mapCols = (main, index, newCols, callback, {lag}) => {
14
11
  }
15
12
 
16
13
  verticalOhlcv[key] = new Array(len).fill(0)
14
+
15
+ if(precision && isPriceBased)
16
+ {
17
+ priceBased.add(key)
18
+ }
17
19
  }
18
20
 
19
21
  if(lag > 0)
@@ -35,12 +37,10 @@ export const mapCols = (main, index, newCols, callback, {lag}) => {
35
37
  export const defaultMapColsCallback = ({main, index}) => {
36
38
  const {verticalOhlcv} = main
37
39
 
38
- const currClose = verticalOhlcv.close[index]
39
- const prevClose = verticalOhlcv.close[index-1]
40
+ const close = verticalOhlcv.close[index]
41
+ const open = verticalOhlcv.open[index-1]
40
42
 
41
- if(typeof prevClose === 'undefined') return null
42
-
43
43
  return {
44
- change: (currClose - prevClose) / prevClose
44
+ change: ((close - open) / open) * 100
45
45
  }
46
46
  }
@@ -1,8 +1,9 @@
1
- // ------------------------------
2
- // calcPrecisionMultiplier (unchanged semantics)
3
- // ------------------------------
1
+
2
+ //calcPrecisionMultiplier is calculated once, using the open, high, low and close values of the first row
3
+ //the multiplier (min 10000 and max 1000000000) is store in constructor
4
+
4
5
  export const calcPrecisionMultiplier = main => {
5
- const { precision, firstRow, initialPriceBased } = main;
6
+ const { precision, firstRow, initialPriceBased, priceBased } = main;
6
7
  if (precision === false) return 1
7
8
 
8
9
  const multipliers = []
@@ -18,81 +19,74 @@ export const calcPrecisionMultiplier = main => {
18
19
  Property "${key}" had the value "${strNum}" type ${typeof strNum}.`)
19
20
  }
20
21
 
21
- if (!new Set(['open', 'high', 'low', 'close']).has(key)) continue;
22
+ if (!priceBased.has(key)) continue
22
23
 
23
- const [, decimals = ''] = strNum.split('.')
24
- const decimalPrecision = Math.max(4, decimals.length)
25
- multipliers.push(Math.pow(10, decimalPrecision)) // returns Number
24
+ const [integerStr, decimals = ''] = strNum.split('.')
25
+ const integer = Number(integerStr)
26
+ const decimalPrecision = integer > 0 ? 4 : Math.max(4, decimals.length)
27
+ multipliers.push(Math.pow(10, decimalPrecision))
26
28
  }
27
29
 
28
30
  if (multipliers.length === 0) return 1
29
- return Math.max(...multipliers)
31
+
32
+ const maxMultiplier = 10 ** 9 //1000000000
33
+
34
+ return Math.min(maxMultiplier, Math.max(...multipliers))
30
35
  }
31
36
 
32
- // ------------------------------
33
- // Precise BigInt-based helpers (no `n` literals)
34
- // ------------------------------
35
- export const addPrecisionExact = (strNum, mul) => {
37
+ //this function converts the strNum into a integer * multiplier
38
+ export const addPrecisionAsNumber = (strNum, multiplier) => {
36
39
  if (typeof strNum !== 'string') throw new Error('addPrecisionExact expects a string when precision=true')
37
40
 
38
41
  const negative = strNum.charAt(0) === '-'
39
42
  const s = negative ? strNum.slice(1) : strNum
40
43
  const [intPart, decPart = ''] = s.split('.')
41
44
 
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
+ // Compute decimals from multiplier (multiplier expected as 10^decimals)
46
+ const mulStr = String(multiplier);
47
+ const decimals = (multiplier === 1) ? 0 : (mulStr.length - 1)
45
48
 
46
49
  const decPadded = decPart.padEnd(decimals, '0').slice(0, decimals); // pad or trim
47
50
  const integerString = intPart + decPadded // e.g. "12" + "3400" => "123400"
48
51
 
49
- // Use BigInt constructor (no n-literals)
50
- let result = BigInt(integerString || '0'); // handle "0" or empty
52
+ let result = parseInt(integerString || '0', 10)
53
+
51
54
  if (negative) result = -result
55
+
52
56
  return result
53
57
  }
54
58
 
55
- export const revertPrecisionExact = (num, mul) => {
56
59
 
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
60
+ //this function converts the outputs the number generated in addPrecisionAsNumber / multiplier
61
+ export const revertPrecisionAsNumber = (num, multiplier) => {
62
+ if (typeof num !== 'number' || !Number.isFinite(num)) {
63
+ throw new Error('revertPrecisionAsNumber expects a finite number')
67
64
  }
68
65
 
69
- const intPart = abs / mulBig
70
- const fracPart = abs % mulBig
66
+ const negative = num < 0
67
+ let abs = Math.abs(num)
71
68
 
72
- const decimals = String(mul).length - 1
73
- const fracStr = fracPart.toString().padStart(decimals, '0')
69
+ // Make sure we're working with an integer, mirroring addPrecisionAsNumber
70
+ abs = Math.trunc(abs)
74
71
 
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}`
72
+ const decimals = (multiplier === 1) ? 0 : (String(multiplier).length - 1)
80
73
 
81
- return neg ? `-${result}` : result
82
- }
74
+ if (decimals === 0) {
75
+ // No fractional part; just restore sign
76
+ return negative ? -abs : abs
77
+ }
83
78
 
84
- // ------------------------------
85
- // Convenience wrappers (Numbers) — may lose precision
86
- // ------------------------------
87
- export const addPrecisionAsNumber = (strNum, mul) => {
79
+ // Integer division and remainder
80
+ const intPart = Math.trunc(abs / multiplier)
81
+ const fracPart = abs % multiplier
88
82
 
89
- const big = addPrecisionExact(strNum, mul)
90
-
91
- return Number(big)
92
- }
83
+ // Build fractional string with leading zeros, then trim trailing zeros
84
+ let fracStr = String(fracPart).padStart(decimals, '0')
85
+ fracStr = fracStr.replace(/0+$/, '')
93
86
 
94
- export const revertPrecisionAsNumber = (num, mul) => {
87
+ const resultStr = fracStr.length === 0
88
+ ? String(intPart)
89
+ : `${intPart}.${fracStr}`
95
90
 
96
- const asStr = revertPrecisionExact(num, mul)
97
- return Number(asStr)
98
- }
91
+ return Number(negative ? `-${resultStr}` : resultStr)
92
+ }
@@ -2,7 +2,7 @@
2
2
  import {FasterATR, FasterWSMA} from 'trading-signals';
3
3
 
4
4
  export const atr = (main, index, size, {lag, percentage, upper, lower}) => {
5
- const { verticalOhlcv, instances } = main
5
+ const { verticalOhlcv, instances, priceBased } = main
6
6
  const baseKeyName = `atr_${size}`
7
7
 
8
8
  if (index === 0) {
@@ -20,6 +20,7 @@ export const atr = (main, index, size, {lag, percentage, upper, lower}) => {
20
20
  for(const k of keyNames)
21
21
  {
22
22
  verticalOhlcv[k] = new Float64Array(len).fill(NaN)
23
+ priceBased.add(k)
23
24
  }
24
25
 
25
26
  if(lag > 0)