ohlcv-indicators 3.5.44 → 3.5.46

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
@@ -14,8 +14,7 @@ import {
14
14
  import { verticalToHorizontal } from './src/utilities/verticalToHorizontal.js'
15
15
  import { pushToMain } from './src/core-functions/pushToMain.js'
16
16
  import { assignTypes } from './src/utilities/assignTypes.js'
17
- import { calcPrecisionMultiplier } from './src/utilities/precisionMultiplier.js'
18
- import { buildArray } from './src/utilities/assignTypes.js'
17
+ import { buildArray, getArrayType } from './src/utilities/assignTypes.js'
19
18
  import { dateOutputFormaters } from './src/utilities/dateUtilities.js'
20
19
  import { defaultMapColsCallback } from './src/studies/mapCols.js'
21
20
  import { getOrderFromArray } from './src/utilities/order.js'
@@ -39,16 +38,14 @@ import { normalizeMinMax } from './src/machine-learning/ml-utilities.js'
39
38
  */
40
39
 
41
40
  export default class OHLCV_INDICATORS {
42
- constructor({input, ticker = null, precision = true, inputParams = null, chunkProcess = 2000, ML = {}}) {
41
+ constructor({input, ticker = null, inputParams = null, chunkProcess = 2000, ML = {}}) {
43
42
 
44
43
  validateArray(input, 'input', (ticker !== null) ? `contructor ${ticker}` : 'constuctor')
45
44
  if(input.length === 0) throw Error('input OHLCV must not be empty: ' + ticker)
46
45
 
47
- validateBoolean(precision, 'precision', 'constructor')
48
46
  validateNumber(chunkProcess, {min: 100, max: 50000, allowDecimals: false}, 'chunkProcess', 'constructor')
49
47
  validateObject(ML, 'ML', 'constructor')
50
48
 
51
- this.notNumberKeys = new Set()
52
49
  this.chunkProcess = chunkProcess
53
50
 
54
51
  this.input = [...input]
@@ -74,8 +71,6 @@ export default class OHLCV_INDICATORS {
74
71
  }
75
72
 
76
73
  this.invalidValueIndex = -1
77
- this.precision = precision
78
- this.precisionMultiplier = calcPrecisionMultiplier(this, this.firstRow)
79
74
  this.scaledGroups = {}
80
75
  this.isAlreadyComputed = new Set()
81
76
 
@@ -119,14 +114,10 @@ export default class OHLCV_INDICATORS {
119
114
  validateObject(options, 'options', 'getDataAsCols')
120
115
 
121
116
  const {
122
- precisionMultiplier,
123
- precision,
124
117
  len,
125
118
  invalidValueIndex,
126
119
  verticalOhlcv,
127
- arrayTypes,
128
120
  verticalOhlcvTempCols,
129
- notNumberKeys
130
121
  } = this
131
122
  const result = {}
132
123
 
@@ -137,16 +128,13 @@ export default class OHLCV_INDICATORS {
137
128
 
138
129
  if(verticalOhlcvTempCols.has(key)) continue
139
130
 
140
- const shouldApplyPrecision = precision && !notNumberKeys.has(key)
141
- result[key] = buildArray(arrayTypes[key], newLen)
131
+ const thisArrType = getArrayType(key, verticalOhlcv[key])
132
+
133
+ result[key] = buildArray(thisArrType, newLen)
142
134
 
143
135
  for (let x = startIndex; x < len; x++)
144
136
  {
145
- if(shouldApplyPrecision)
146
- {
147
- result[key][x] = arr[x] / precisionMultiplier
148
- }
149
- else if(key === 'date')
137
+ if(key === 'date')
150
138
  {
151
139
  result[key][x] = dateOutputFormaters[dateFormat](arr[x])
152
140
  }
@@ -377,10 +365,13 @@ export default class OHLCV_INDICATORS {
377
365
  validateNumber(smoothing, {min: size, max: this.len, allowDecimals: false}, 'smoothing', methodName)
378
366
  validateObject(options, 'options', methodName)
379
367
 
380
- const {target = 'close', lag = 0} = options
368
+ const {target = 'close', lag = 0, atrLength = 200, bandDistance = 2, liquidityLookback = 20} = options
381
369
 
382
370
  validateString(target, 'options.target', methodName)
383
371
  validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'options.lag', methodName)
372
+ validateNumber(bandDistance, {min: 0.01, max: 10, allowDecimals: true}, 'options.bandDistance', methodName)
373
+ validateNumber(atrLength, {min: 2, max: this.len, allowDecimals: false}, 'options.atrLength', methodName)
374
+ validateNumber(liquidityLookback, {min: 1, max: this.len, allowDecimals: false}, 'options.liquidityLookback', methodName)
384
375
 
385
376
  const order = getOrderFromArray([target], methodName)
386
377
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ohlcv-indicators",
3
- "version": "3.5.44",
3
+ "version": "3.5.46",
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",
@@ -47,13 +47,11 @@ const mainFunctions = {
47
47
  export const mainLoop = (input, main) => {
48
48
  const { len,
49
49
  inputParams,
50
- precisionMultiplier,
51
50
  arrayTypes,
52
51
  verticalOhlcv,
53
52
  verticalOhlcvKeyNames,
54
53
  inputTypes,
55
54
  chunkProcess,
56
- notNumberKeys,
57
55
  processSecondaryLoop,
58
56
  } = main;
59
57
 
@@ -77,12 +75,7 @@ export const mainLoop = (input, main) => {
77
75
 
78
76
  if(numberFormater.hasOwnProperty(formaterKey))
79
77
  {
80
- let formatedValue = numberFormater[formaterKey](value)
81
-
82
- if(precisionMultiplier > 1 && !notNumberKeys.has(key))
83
- {
84
- formatedValue = formatedValue * precisionMultiplier
85
- }
78
+ const formatedValue = numberFormater[formaterKey](value)
86
79
 
87
80
  main.pushToMain({index, key, value: formatedValue})
88
81
  }
@@ -0,0 +1,65 @@
1
+ export const countUniqueLabels = Y => {
2
+ // 1. Guard against empty or non-array input
3
+ if (!Array.isArray(Y) || Y.length === 0) {
4
+ return 1
5
+ }
6
+
7
+ // 2. Detect if Y is 2D or 1D
8
+ const is2D = Array.isArray(Y[0])
9
+ const nCols = is2D ? Y[0].length : 1
10
+
11
+ let total = 0
12
+
13
+ // 3. For each column, collect uniques in a single pass
14
+ for (let col = 0; col < nCols; col++) {
15
+ const seen = new Set()
16
+ for (let row = 0; row < Y.length; row++) {
17
+ // pick the cell: Y[row][col] if 2D, or Y[row] if 1D
18
+ const cell = is2D ? Y[row][col] : Y[row]
19
+ seen.add(cell)
20
+ }
21
+ total += seen.size
22
+ }
23
+
24
+ // 4. Return “sum of uniques per column”
25
+ return total
26
+ }
27
+
28
+ // X and Y can have multivariable or univariable rows
29
+ export const oversampleXY = (X, Y) => {
30
+ if (!Array.isArray(X) || !Array.isArray(Y)) {
31
+ throw new TypeError('Both X and Y must be arrays');
32
+ }
33
+ if (X.length !== Y.length) {
34
+ throw new Error('X and Y must have the same length');
35
+ }
36
+ if (Y.length === 0) {
37
+ return { X: [], Y: [] };
38
+ }
39
+
40
+ // 2) Group X-values by label using a Map
41
+ const groups = Y.reduce((map, label, i) => {
42
+ if (!map.has(label)) map.set(label, []);
43
+ map.get(label).push(X[i]);
44
+ return map;
45
+ }, new Map());
46
+
47
+ // 3) Find the largest group size
48
+ let maxSize = 0;
49
+ for (const arr of groups.values()) {
50
+ if (arr.length > maxSize) maxSize = arr.length;
51
+ }
52
+
53
+ // 4) Build the oversampled arrays
54
+ const outX = [];
55
+ const outY = [];
56
+ for (const [label, xArr] of groups.entries()) {
57
+ const sz = xArr.length;
58
+ for (let i = 0; i < maxSize; i++) {
59
+ outX.push(xArr[i % sz]);
60
+ outY.push(label);
61
+ }
62
+ }
63
+
64
+ return { X: outX, Y: outY };
65
+ };
@@ -1,6 +1,7 @@
1
- import { getFeaturedKeys, computeFlatFeaturesLen, countUniqueLabels, logMlTraining, updateClassifierMetrics, findGroupsFunc } from "./ml-utilities.js"
1
+ import { getFeaturedKeys, computeFlatFeaturesLen, logMlTraining, updateClassifierMetrics, findGroupsFunc } from "./ml-utilities.js"
2
2
  import { buildTrainX } from "./trainX.js"
3
3
  import { areKeyValuesValid } from "../core-functions/pushToMain.js"
4
+ import { countUniqueLabels, oversampleXY } from "./balancing.js"
4
5
 
5
6
  const algo = 'classifier'
6
7
 
@@ -246,9 +247,9 @@ export const classifier = (
246
247
 
247
248
  const {train} = modelConfig
248
249
 
249
- console.log(yRows)
250
+ const {X: overX, Y: overY} = oversampleXY(xRows, yRows)
250
251
 
251
- const trainedModel = train({modelClass, xRows, yRows, modelArgs, uniqueLabels: uniqueLabels[loopIdx]})
252
+ const trainedModel = train({modelClass, xRows: overX, yRows: overY, modelArgs, uniqueLabels: uniqueLabels[loopIdx]})
252
253
 
253
254
  allModels[predictionKey].push(
254
255
  trainedModel
@@ -69,24 +69,6 @@ export const computeFlatFeaturesLen = (featureCols, verticalOhlcv, index) => {
69
69
  }
70
70
 
71
71
 
72
- export const countUniqueLabels = Y => {
73
- // If Y[0] isn't an array, treat Y as a single-column 2D array
74
- const data = Array.isArray(Y[0]) ? Y : Y.map(v => [v]);
75
-
76
- // Number of columns in our (possibly wrapped) data
77
- const nCols = data[0].length;
78
-
79
- // Sum up the unique values in each column
80
- const totalLabels = Array.from({ length: nCols }).reduce((sum, _, col) => {
81
- const uniques = new Set(data.map(row => row[col]));
82
- return sum + uniques.size;
83
- }, 0);
84
-
85
- // Return count + 1 (same as before)
86
- return totalLabels + 1;
87
- }
88
-
89
-
90
72
  export const getFeaturedKeys = ({trainingCols, findGroups, verticalOhlcv, scaledGroups}) => {
91
73
 
92
74
  const featureCols = [...trainingCols, ...(findGroupsFunc(findGroups, scaledGroups))]
@@ -22,7 +22,6 @@ export const pca = (main, index, {prefix, trainingSize, findGroups, trainingCols
22
22
  }
23
23
 
24
24
  verticalOhlcv[prefix] = new Array(len).fill(null)
25
- main.notNumberKeys.add(prefix)
26
25
  }
27
26
  else if(index < startIndex || index < lookbackAbs)
28
27
  {
@@ -5,7 +5,7 @@ export const scaler = (main, index, size, colKeys, {type, group, range, lag, pre
5
5
 
6
6
 
7
7
  const {groupKey, groupKeyLen} = precomputed
8
- const { verticalOhlcv, instances, arrayTypes, invalidValueIndex } = main;
8
+ const { verticalOhlcv, instances, invalidValueIndex } = main;
9
9
  const prefix = `${type}_${size}`;
10
10
 
11
11
  if(index === 0) {
@@ -37,8 +37,7 @@ export const scaler = (main, index, size, colKeys, {type, group, range, lag, pre
37
37
  }
38
38
 
39
39
  const key = `${prefix}_${target}`;
40
- verticalOhlcv[key] = new Float64Array(len).fill(NaN);
41
- arrayTypes[key] = 'Float64Array'
40
+ verticalOhlcv[key] = new Float64Array(len).fill(NaN)
42
41
 
43
42
  const winKey = group ? groupKey : target;
44
43
  instances.scaler[prefix].windows[winKey] = [];
@@ -14,7 +14,7 @@ export const bollingerBands = (main, index, size, stdDev, { height, range = [],
14
14
  // Initialization on the first call.
15
15
  if (index === 0) {
16
16
 
17
- const {inputParams, verticalOhlcv, len, arrayTypes } = main;
17
+ const {inputParams, verticalOhlcv, len } = main;
18
18
 
19
19
  if (!(target in verticalOhlcv)) {
20
20
  throw new Error(`bollingerBands could not find target "${target}"`);
@@ -69,11 +69,6 @@ export const bollingerBands = (main, index, size, stdDev, { height, range = [],
69
69
  {
70
70
  main.lag(keyNames, lag)
71
71
  }
72
-
73
- for(const key of keyNames)
74
- {
75
- arrayTypes[key] = 'Float64Array'
76
- }
77
72
  }
78
73
 
79
74
  const {numberOfIndicators} = instances.bollinger_bands
@@ -5,7 +5,7 @@ export const donchianChannels = (main, index, size, offset, options) => {
5
5
 
6
6
 
7
7
 
8
- const {verticalOhlcv, instances, len, inputParams, arrayTypes, lag} = main
8
+ const {verticalOhlcv, instances, len, inputParams, lag} = main
9
9
  const { height: includeHeight, range: rangeKeys, decimals } = options
10
10
  const indicatorKey = `${size}_${offset}`
11
11
 
@@ -44,10 +44,7 @@ export const donchianChannels = (main, index, size, offset, options) => {
44
44
  );
45
45
 
46
46
  // apply lag if requested
47
- if (lag > 0) main.lag(keys, lag);
48
-
49
- // mark price-based outputs
50
- keys.forEach(k => arrayTypes[k] = 'Float64Array');
47
+ if (lag > 0) main.lag(keys, lag)
51
48
  }
52
49
 
53
50
  // ---- PER-BAR COMPUTATION ----
@@ -11,7 +11,7 @@ export const macd = (main, index, fast, slow, signal, {target, lag, precomputed}
11
11
 
12
12
  // Initialization on the first index.
13
13
  if (index === 0) {
14
- const { inputParams, len , arrayTypes} = main
14
+ const { inputParams, len } = main
15
15
 
16
16
  if (!verticalOhlcv.hasOwnProperty(target)) {
17
17
  throw new Error(`Target property ${target} not found in verticalOhlcv for macd.`)
@@ -57,11 +57,6 @@ export const macd = (main, index, fast, slow, signal, {target, lag, precomputed}
57
57
  main.lag(keyNames, lag)
58
58
  }
59
59
 
60
- for(const key of keyNames)
61
- {
62
- arrayTypes[key] = 'Float64Array'
63
- }
64
-
65
60
  }
66
61
 
67
62
  const { numberOfIndicators, settings } = instances.macd
@@ -16,7 +16,7 @@ export const movingAverages = (main, index, methodName, size, { target, lag }) =
16
16
 
17
17
  if (index === 0) {
18
18
 
19
- const {arrayTypes, len} = main
19
+ const {len} = main
20
20
 
21
21
  if (!verticalOhlcv.hasOwnProperty(target)) {
22
22
  throw new Error(
@@ -33,8 +33,6 @@ export const movingAverages = (main, index, methodName, size, { target, lag }) =
33
33
  {
34
34
  main.lag([keyName], lag)
35
35
  }
36
-
37
- arrayTypes[keyName] = 'Float64Array'
38
36
  }
39
37
 
40
38
  // Retrieve the current price value
@@ -8,7 +8,7 @@ export const relativeVolume = (main, index, size, {lag}) => {
8
8
  const { instances, verticalOhlcv } = main;
9
9
 
10
10
  if (index === 0) {
11
- const { len, arrayTypes } = main;
11
+ const { len } = main;
12
12
 
13
13
  instances[key] = {
14
14
  instance: new FasterSMA(size),
@@ -21,8 +21,7 @@ export const relativeVolume = (main, index, size, {lag}) => {
21
21
  {
22
22
  main.lag([key], lag)
23
23
  }
24
-
25
- arrayTypes[key] = 'Float64Array'
24
+
26
25
  }
27
26
 
28
27
  const value = verticalOhlcv.volume[index];
@@ -2,8 +2,8 @@ import { FasterSMA } from 'trading-signals';
2
2
  // Variable Index Dynamic Average (VIDYA) indicator with volume stats
3
3
 
4
4
  export const vidya = (main, index, size, momentum, options = {}) => {
5
- const { verticalOhlcv, instances, len, arrayTypes, lag } = main;
6
- const { target = 'close', atrLength = 200, bandDistance = 2, liquidityLookback = 20 } = options
5
+ const { verticalOhlcv, instances, len } = main;
6
+ const { target, lag, atrLength = 200, bandDistance = 2, liquidityLookback = 5 } = options
7
7
  const indicatorKey = `${size}_${momentum}`;
8
8
 
9
9
  // Initialization on the first bar
@@ -37,9 +37,9 @@ export const vidya = (main, index, size, momentum, options = {}) => {
37
37
 
38
38
  // Allocate output arrays
39
39
  Object.values(keys).forEach(key => {
40
- verticalOhlcv[key] = new Float64Array(len).fill(NaN);
41
- arrayTypes[key] = 'Float64Array';
42
- });
40
+ verticalOhlcv[key] = new Float64Array(len).fill(NaN)
41
+ })
42
+
43
43
  if (lag > 0) main.lag(Object.values(keys), lag);
44
44
  }
45
45
 
@@ -15,7 +15,7 @@ export const rsi = (main, index, size, { target, lag, parser, prefix, minmax })
15
15
  // Initialization on the first index.
16
16
  if (index === 0) {
17
17
 
18
- const {len, arrayTypes } = main;
18
+ const {len } = main;
19
19
 
20
20
  if (!verticalOhlcv.hasOwnProperty(target)) {
21
21
  throw new Error(`Target property ${target} not found in verticalOhlcv for rsi.`);
@@ -50,11 +50,6 @@ export const rsi = (main, index, size, { target, lag, parser, prefix, minmax })
50
50
  group.push(...keyNames);
51
51
  }
52
52
 
53
- for(const key of keyNames)
54
- {
55
- arrayTypes[key] = 'Float64Array'
56
- }
57
-
58
53
  }
59
54
 
60
55
  const value = verticalOhlcv[target][index];
@@ -5,7 +5,7 @@ export const stochastic = (main, index, kPeriod, kSlowingPeriod, dPeriod, {minma
5
5
 
6
6
 
7
7
 
8
- const { verticalOhlcv, instances, scaledGroups } = main;
8
+ const { verticalOhlcv, instances } = main;
9
9
 
10
10
  const paramsKey = (kPeriod === 14 && kSlowingPeriod === 3 && dPeriod === 3) ? '' : `${kPeriod}_${kSlowingPeriod}_${dPeriod}`
11
11
  const stochD = `${prefix}stoch_d${paramsKey}`;
@@ -15,7 +15,7 @@ export const stochastic = (main, index, kPeriod, kSlowingPeriod, dPeriod, {minma
15
15
  // Initialization on the first index.
16
16
  if (index === 0) {
17
17
 
18
- const {len, arrayTypes } = main;
18
+ const {len } = main;
19
19
 
20
20
  Object.assign(instances, {
21
21
  [instanceKey]: new FasterStochasticOscillator(kPeriod, kSlowingPeriod, dPeriod)
@@ -44,11 +44,6 @@ export const stochastic = (main, index, kPeriod, kSlowingPeriod, dPeriod, {minma
44
44
  const group = main.scaledGroups.minmax_rsi ??= [];
45
45
  group.push(...keyNames)
46
46
  }
47
-
48
- for(const key of keyNames)
49
- {
50
- arrayTypes[key] = 'Float64Array'
51
- }
52
47
  }
53
48
 
54
49
  const close = verticalOhlcv['close'][index]
@@ -10,7 +10,7 @@ export const volumeOscillator = (main, index, fast, slow, {lag}) => {
10
10
 
11
11
  if (index === 0) {
12
12
 
13
- const {len, arrayTypes} = main
13
+ const {len} = main
14
14
  Object.assign(instances, {
15
15
  [key]: {
16
16
  fastEMA: new FasterEMA(fast),
@@ -24,8 +24,6 @@ export const volumeOscillator = (main, index, fast, slow, {lag}) => {
24
24
  {
25
25
  main.lag([key], lag)
26
26
  }
27
-
28
- arrayTypes[key] = 'Float64Array'
29
27
  }
30
28
 
31
29
  const { fastEMA, slowEMA } = instances[key];
@@ -4,12 +4,12 @@ export const dateTime = (main, index, {lag, oneHot, precompute}) => {
4
4
 
5
5
 
6
6
 
7
- const {instances, verticalOhlcv, notNumberKeys} = main
7
+ const {instances, verticalOhlcv} = main
8
8
  const {prefix} = precompute
9
9
 
10
10
  if(index === 0)
11
11
  {
12
- const {len, dateType, arrayTypes} = main
12
+ const {len, dateType} = main
13
13
  if(!dateType) throw Error('dateTime method found and invalid "date" in input ohlcv')
14
14
 
15
15
  const startYear = verticalOhlcv.date[0].getUTCFullYear()
@@ -31,18 +31,12 @@ export const dateTime = (main, index, {lag, oneHot, precompute}) => {
31
31
  // choose your ctor, fill-value and type-name once
32
32
  const ctor = oneHot ? Array : Int16Array
33
33
  const fillVal = oneHot ? null : NaN
34
- const typeName = oneHot ? 'Array' : 'Int16Array'
35
34
 
36
35
  // single loop instead of three
37
36
  for (const key of colKeys) {
38
- // 1) set the arrayType
39
- arrayTypes[key] = typeName
40
37
 
41
- // 2) allocate and fill the backing array
42
- verticalOhlcv[key] = new ctor(len).fill(fillVal)
38
+ verticalOhlcv[key] = new ctor(len).fill(fillVal)
43
39
 
44
- // 3) mark as non-numeric
45
- notNumberKeys.add(key)
46
40
  }
47
41
 
48
42
  // finally, apply lag once
@@ -134,7 +134,7 @@ class CrossInstance {
134
134
 
135
135
  export const crossPairs = (main, index, crossPairsList, {limit, oneHot}) => {
136
136
 
137
- const {verticalOhlcv, verticalOhlcvTempCols, instances, len, arrayTypes, notNumberKeys} = main
137
+ const {verticalOhlcv, verticalOhlcvTempCols, instances, len} = main
138
138
 
139
139
  for (const { fast, slow } of crossPairsList) {
140
140
 
@@ -147,7 +147,6 @@ export const crossPairs = (main, index, crossPairsList, {limit, oneHot}) => {
147
147
 
148
148
  const col = slow.toString()
149
149
  verticalOhlcvTempCols.add(col)
150
- notNumberKeys.add(col)
151
150
 
152
151
  let slowNumArrType = 'Int16Array'
153
152
 
@@ -160,7 +159,6 @@ export const crossPairs = (main, index, crossPairsList, {limit, oneHot}) => {
160
159
  }
161
160
 
162
161
  verticalOhlcv[col] = buildArray(slowNumArrType, len)
163
- arrayTypes[col] = 'slowNumArrType'
164
162
  }
165
163
 
166
164
  if(!instances.hasOwnProperty('crossPairs'))
@@ -187,13 +185,8 @@ export const crossPairs = (main, index, crossPairsList, {limit, oneHot}) => {
187
185
  if(oneHot)
188
186
  {
189
187
  verticalOhlcv[`one_hot_${crossName}`] = buildArray('Array', len)
190
- notNumberKeys.add(`one_hot_${crossName}`)
191
188
  }
192
189
 
193
- notNumberKeys.add(crossName)
194
-
195
- arrayTypes[crossName] = 'crossArrType'
196
-
197
190
  } else if(index + 1 === len) {
198
191
  // sanity checks
199
192
  if (fast !== "price" && !verticalOhlcv.hasOwnProperty(fast)) {
@@ -1,24 +1,25 @@
1
- import { buildArray } from "../utilities/assignTypes.js"
1
+ import { buildArray, getArrayType } from "../utilities/assignTypes.js"
2
2
 
3
3
  export const lag = (main, index, colKeys, lookback) => {
4
4
 
5
5
 
6
6
 
7
- const { verticalOhlcv, len, arrayTypes } = main
7
+ const { verticalOhlcv, len } = main
8
8
 
9
9
  if(index === 0)
10
10
  {
11
11
  for (const colKey of colKeys) {
12
- if (!arrayTypes.hasOwnProperty(colKey)) {
12
+ if (!verticalOhlcv.hasOwnProperty(colKey)) {
13
13
  throw new Error(
14
- `Lag processing invoked by "${colKey}" expected arrayTypes to have a "${colKey}" property, but it wasn’t found.`
14
+ `Lag processing invoked by col "${colKey}" was not found in "verticalOhlcv".`
15
15
  )
16
16
  }
17
17
 
18
18
  for (let step = 1; step <= lookback; step++) {
19
19
  const key = `${colKey}_lag_${step}`
20
- verticalOhlcv[key] = buildArray(arrayTypes[colKey], len)
21
- arrayTypes[key] = arrayTypes[colKey]
20
+ const thisArrType = getArrayType(colKey, verticalOhlcv[colKey])
21
+
22
+ verticalOhlcv[key] = buildArray(thisArrType, len)
22
23
  }
23
24
  }
24
25
  }
@@ -2,7 +2,7 @@ export const mapCols = (main, index, newCols, callback, {lag}) => {
2
2
 
3
3
 
4
4
 
5
- const {verticalOhlcv, len, arrayTypes} = main
5
+ const {verticalOhlcv, len} = main
6
6
 
7
7
  if(index === 0)
8
8
  {
@@ -14,7 +14,6 @@ export const mapCols = (main, index, newCols, callback, {lag}) => {
14
14
  }
15
15
 
16
16
  verticalOhlcv[key] = new Array(len).fill(0)
17
- arrayTypes[key] = 'Array'
18
17
  }
19
18
 
20
19
  if(lag > 0)
@@ -25,7 +25,7 @@ export const buildArray = (arrayType, len) => {
25
25
  }
26
26
 
27
27
  export const assignTypes = main => {
28
- const {firstRow, notNumberKeys} = main
28
+ const {firstRow} = main
29
29
  const inputTypes = {}
30
30
  const arrayTypes = {}
31
31
 
@@ -34,13 +34,11 @@ export const assignTypes = main => {
34
34
  if (colName === 'date') {
35
35
  inputTypes[colName] = selectDateFormatter(cellValue, true)
36
36
  arrayTypes[colName] = 'Array'
37
- notNumberKeys.add(colName)
38
37
  }
39
38
  else if(colName === 'volume')
40
39
  {
41
40
  inputTypes[colName] = classifyNum(cellValue, true)
42
41
  arrayTypes[colName] = 'Int32Array'
43
- notNumberKeys.add(colName)
44
42
  }
45
43
  else if(numberKeys.has(colName))
46
44
  {
@@ -50,14 +48,16 @@ export const assignTypes = main => {
50
48
  const thisType = classifyNum(cellValue, false)
51
49
  inputTypes[colName] = thisType
52
50
  arrayTypes[colName] = (thisType === 'number') ? 'Float64Array' : 'Array'
53
-
54
- if(!thisType.startsWith('number'))
55
- {
56
- notNumberKeys.add(colName)
57
- }
58
-
59
51
  }
60
52
  }
61
53
 
62
54
  return { inputTypes, arrayTypes }
63
55
  }
56
+
57
+
58
+ export const getArrayType = (key, arr) => {
59
+ if (!arr || typeof arr !== 'object') {
60
+ throw new TypeError(`Invalid array type: "${key}"`);
61
+ }
62
+ return arr.constructor.name;
63
+ }
@@ -2,7 +2,7 @@ import { dateOutputFormaters } from "./dateUtilities.js"
2
2
 
3
3
  export const verticalToHorizontal = ({main, skipNull = false, startIndex = 0, dateFormat}) => {
4
4
 
5
- const {precisionMultiplier, precision, verticalOhlcv, invalidValueIndex, len, verticalOhlcvKeyNames, verticalOhlcvTempCols, notNumberKeys} = main
5
+ const {verticalOhlcv, invalidValueIndex, len, verticalOhlcvKeyNames, verticalOhlcvTempCols} = main
6
6
 
7
7
  if (verticalOhlcvKeyNames.length === 0) return []
8
8
 
@@ -13,16 +13,10 @@ export const verticalToHorizontal = ({main, skipNull = false, startIndex = 0, da
13
13
 
14
14
  for(const [key, arr] of Object.entries(verticalOhlcv)){
15
15
  if(verticalOhlcvTempCols.has(key)) continue
16
- const shouldApplyPrecision = precision && !notNumberKeys.has(key)
17
16
 
18
17
  for (let i = maxStartIndex; i < len; i++)
19
18
  {
20
- if(shouldApplyPrecision)
21
- {
22
- result[i - maxStartIndex][key] = arr[i] / precisionMultiplier
23
-
24
- }
25
- else if(key === 'date')
19
+ if(key === 'date')
26
20
  {
27
21
  result[i - maxStartIndex][key] = dateOutputFormaters[dateFormat](arr[i])
28
22
  }