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.
|
|
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] <
|
|
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
|
}
|
package/src/studies/mapCols.js
CHANGED
|
@@ -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
|
|
39
|
-
const
|
|
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: (
|
|
44
|
+
change: ((close - open) / open) * 100
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
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 (!
|
|
22
|
+
if (!priceBased.has(key)) continue
|
|
22
23
|
|
|
23
|
-
const [, decimals = ''] = strNum.split('.')
|
|
24
|
-
const
|
|
25
|
-
|
|
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
|
-
|
|
31
|
+
|
|
32
|
+
const maxMultiplier = 10 ** 9 //1000000000
|
|
33
|
+
|
|
34
|
+
return Math.min(maxMultiplier, Math.max(...multipliers))
|
|
30
35
|
}
|
|
31
36
|
|
|
32
|
-
//
|
|
33
|
-
|
|
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
|
|
43
|
-
const mulStr = String(
|
|
44
|
-
const decimals = (
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
|
70
|
-
|
|
66
|
+
const negative = num < 0
|
|
67
|
+
let abs = Math.abs(num)
|
|
71
68
|
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
// Make sure we're working with an integer, mirroring addPrecisionAsNumber
|
|
70
|
+
abs = Math.trunc(abs)
|
|
74
71
|
|
|
75
|
-
|
|
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
|
-
|
|
82
|
-
|
|
74
|
+
if (decimals === 0) {
|
|
75
|
+
// No fractional part; just restore sign
|
|
76
|
+
return negative ? -abs : abs
|
|
77
|
+
}
|
|
83
78
|
|
|
84
|
-
//
|
|
85
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
87
|
+
const resultStr = fracStr.length === 0
|
|
88
|
+
? String(intPart)
|
|
89
|
+
: `${intPart}.${fracStr}`
|
|
95
90
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
91
|
+
return Number(negative ? `-${resultStr}` : resultStr)
|
|
92
|
+
}
|
package/src/volatility/atr.js
CHANGED
|
@@ -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)
|