ohlcv-indicators 3.5.41 → 3.5.42
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/dist/ohlcv-indicators.min.js +1 -1
- package/index.js +533 -67
- package/package.json +11 -2
- package/src/core-functions/mainLoop.js +70 -16
- package/src/core-functions/pushToMain.js +28 -7
- package/src/machine-learning/classifier.js +267 -0
- package/src/machine-learning/ml-config.js +219 -0
- package/src/machine-learning/ml-utilities.js +148 -0
- package/src/machine-learning/pca.js +71 -80
- package/src/machine-learning/regressor.js +227 -0
- package/src/machine-learning/scaler.js +97 -64
- package/src/machine-learning/trainX.js +73 -0
- package/src/moving-averages/bollingerBands.js +30 -31
- package/src/moving-averages/donchianChannel.js +6 -4
- package/src/moving-averages/macd.js +3 -7
- package/src/moving-averages/movingAverages.js +30 -33
- package/src/moving-averages/relativeVolume.js +2 -0
- package/src/moving-averages/vidya.js +181 -0
- package/src/oscillators/rsi.js +26 -15
- package/src/oscillators/stochastic.js +74 -0
- package/src/oscillators/volumeOscillator.js +3 -2
- package/src/studies/dateTime.js +58 -65
- package/src/studies/findCrosses.js +88 -50
- package/src/studies/lag.js +37 -70
- package/src/studies/mapCols.js +47 -0
- package/src/utilities/assignTypes.js +28 -4
- package/src/utilities/numberUtilities.js +0 -26
- package/src/utilities/order.js +51 -0
- package/src/utilities/precisionMultiplier.js +3 -4
- package/src/utilities/validators.js +7 -0
- package/src/utilities/verticalToHorizontal.js +2 -2
- package/src/volatility/atr.js +2 -7
package/index.js
CHANGED
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
validateBoolean,
|
|
9
9
|
validateNumber,
|
|
10
10
|
validateArrayOfRanges,
|
|
11
|
-
validateInputParams
|
|
11
|
+
validateInputParams,
|
|
12
|
+
validateString
|
|
12
13
|
} from './src/utilities/validators.js'
|
|
13
14
|
import { verticalToHorizontal } from './src/utilities/verticalToHorizontal.js'
|
|
14
15
|
import { pushToMain } from './src/core-functions/pushToMain.js'
|
|
@@ -16,33 +17,45 @@ import { assignTypes } from './src/utilities/assignTypes.js'
|
|
|
16
17
|
import { calcPrecisionMultiplier } from './src/utilities/precisionMultiplier.js'
|
|
17
18
|
import { buildArray } from './src/utilities/assignTypes.js'
|
|
18
19
|
import { dateOutputFormaters } from './src/utilities/dateUtilities.js'
|
|
20
|
+
import { defaultMapColsCallback } from './src/studies/mapCols.js'
|
|
21
|
+
import { getOrderFromArray } from './src/utilities/order.js'
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
import {
|
|
24
|
+
defaultYCallback,
|
|
25
|
+
validRegressors,
|
|
26
|
+
validClassifiers,
|
|
27
|
+
exportTrainedModels
|
|
28
|
+
} from './src/machine-learning/ml-config.js'
|
|
29
|
+
|
|
30
|
+
import { normalizeMinMax } from './src/machine-learning/ml-utilities.js'
|
|
22
31
|
|
|
23
32
|
/**
|
|
24
33
|
* Class OHLCV_INDICATORS
|
|
25
34
|
*
|
|
26
35
|
* This class provides methods for calculating and managing technical indicators
|
|
27
36
|
* on financial OHLCV (Open, High, Low, Close, Volume) data. It enables users
|
|
28
|
-
* to parallel compute various technical indicators in 1 single loop.
|
|
37
|
+
* to parallel compute various technical indicators in 1 single loop for standard indicators and a second loop for ML regressors and classifier.
|
|
29
38
|
* OHLCV datasets.
|
|
30
39
|
*/
|
|
31
40
|
|
|
32
41
|
export default class OHLCV_INDICATORS {
|
|
33
|
-
constructor({input, ticker = null, precision = true, inputParams = null, chunkProcess = 2000}) {
|
|
42
|
+
constructor({input, ticker = null, precision = true, inputParams = null, chunkProcess = 2000, ML = {}}) {
|
|
34
43
|
|
|
35
44
|
validateArray(input, 'input', (ticker !== null) ? `contructor ${ticker}` : 'constuctor')
|
|
36
45
|
if(input.length === 0) throw Error('input OHLCV must not be empty: ' + ticker)
|
|
37
46
|
|
|
38
47
|
validateBoolean(precision, 'precision', 'constructor')
|
|
39
48
|
validateNumber(chunkProcess, {min: 100, max: 50000, allowDecimals: false}, 'chunkProcess', 'constructor')
|
|
49
|
+
validateObject(ML, 'ML', 'constructor')
|
|
40
50
|
|
|
41
|
-
|
|
51
|
+
this.notNumberKeys = new Set()
|
|
42
52
|
this.chunkProcess = chunkProcess
|
|
43
|
-
this.firstRow = input[0]
|
|
44
53
|
|
|
45
|
-
|
|
54
|
+
this.input = [...input]
|
|
55
|
+
this.len = this.input.length
|
|
56
|
+
this.firstRow = this.input[0]
|
|
57
|
+
|
|
58
|
+
const {inputTypes, arrayTypes} = assignTypes(this)
|
|
46
59
|
|
|
47
60
|
this.inputTypes = inputTypes
|
|
48
61
|
this.arrayTypes = arrayTypes
|
|
@@ -50,11 +63,8 @@ export default class OHLCV_INDICATORS {
|
|
|
50
63
|
|
|
51
64
|
this.dateType = this.inputTypes.date ? this.inputTypes.date : null;
|
|
52
65
|
this.isComputed = false
|
|
53
|
-
|
|
54
|
-
this.priceBased = new Set(['open', 'high', 'low', 'close'])
|
|
55
|
-
this.len = input.length
|
|
66
|
+
|
|
56
67
|
this.instances = {}
|
|
57
|
-
this.crossPairsList = []
|
|
58
68
|
this.verticalOhlcv = {}
|
|
59
69
|
this.verticalOhlcvKeyNames = []
|
|
60
70
|
this.verticalOhlcvTempCols = new Set()
|
|
@@ -67,8 +77,19 @@ export default class OHLCV_INDICATORS {
|
|
|
67
77
|
this.precision = precision
|
|
68
78
|
this.precisionMultiplier = calcPrecisionMultiplier(this, this.firstRow)
|
|
69
79
|
this.scaledGroups = {}
|
|
70
|
-
this.
|
|
71
|
-
|
|
80
|
+
this.isAlreadyComputed = new Set()
|
|
81
|
+
|
|
82
|
+
const {classes: mlClasses} = ML
|
|
83
|
+
|
|
84
|
+
if(typeof mlClasses !== 'undefined') validateObject(mlClasses, 'ML.classes', 'constructor')
|
|
85
|
+
|
|
86
|
+
this.ML = {
|
|
87
|
+
models: {},
|
|
88
|
+
classes: mlClasses,
|
|
89
|
+
metrics: {},
|
|
90
|
+
featureCols: {}
|
|
91
|
+
}
|
|
92
|
+
this.processSecondaryLoop = false
|
|
72
93
|
|
|
73
94
|
this.pushToMain = ({index, key, value}) => pushToMain({main: this, index, key, value})
|
|
74
95
|
|
|
@@ -78,6 +99,7 @@ export default class OHLCV_INDICATORS {
|
|
|
78
99
|
this.inputParams = inputParams
|
|
79
100
|
this.compute()
|
|
80
101
|
}
|
|
102
|
+
else
|
|
81
103
|
{
|
|
82
104
|
this.inputParams = []
|
|
83
105
|
}
|
|
@@ -99,14 +121,15 @@ export default class OHLCV_INDICATORS {
|
|
|
99
121
|
const {
|
|
100
122
|
precisionMultiplier,
|
|
101
123
|
precision,
|
|
102
|
-
invalidValueIndex,
|
|
103
124
|
len,
|
|
125
|
+
invalidValueIndex,
|
|
104
126
|
verticalOhlcv,
|
|
105
|
-
priceBased,
|
|
106
127
|
arrayTypes,
|
|
107
|
-
verticalOhlcvTempCols
|
|
128
|
+
verticalOhlcvTempCols,
|
|
129
|
+
notNumberKeys
|
|
108
130
|
} = this
|
|
109
131
|
const result = {}
|
|
132
|
+
|
|
110
133
|
const startIndex = skipNull ? invalidValueIndex + 1 : 0
|
|
111
134
|
const newLen = len - startIndex
|
|
112
135
|
|
|
@@ -114,7 +137,7 @@ export default class OHLCV_INDICATORS {
|
|
|
114
137
|
|
|
115
138
|
if(verticalOhlcvTempCols.has(key)) continue
|
|
116
139
|
|
|
117
|
-
const shouldApplyPrecision =
|
|
140
|
+
const shouldApplyPrecision = precision && !notNumberKeys.has(key)
|
|
118
141
|
result[key] = buildArray(arrayTypes[key], newLen)
|
|
119
142
|
|
|
120
143
|
for (let x = startIndex; x < len; x++)
|
|
@@ -162,7 +185,8 @@ export default class OHLCV_INDICATORS {
|
|
|
162
185
|
this.compute()
|
|
163
186
|
|
|
164
187
|
const {dateFormat = 'string'} = options
|
|
165
|
-
|
|
188
|
+
|
|
189
|
+
validateArrayOptions(Object.keys(dateOutputFormaters), dateFormat, 'dateFormat', 'getData')
|
|
166
190
|
|
|
167
191
|
return verticalToHorizontal({
|
|
168
192
|
skipNull: false,
|
|
@@ -188,13 +212,14 @@ export default class OHLCV_INDICATORS {
|
|
|
188
212
|
// Only run the full loop once (or when new data appears later,
|
|
189
213
|
// if you extend this to reset isComputed elsewhere)
|
|
190
214
|
if (this.len > 0) {
|
|
191
|
-
|
|
192
|
-
|
|
215
|
+
mainLoop(this.input, this)
|
|
216
|
+
|
|
217
|
+
this.isComputed = true;
|
|
193
218
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
219
|
+
//flushing after mainLoop
|
|
220
|
+
this.input = []
|
|
221
|
+
this.instances = {}
|
|
222
|
+
this.firstRow = []
|
|
198
223
|
}
|
|
199
224
|
|
|
200
225
|
return this;
|
|
@@ -202,16 +227,42 @@ export default class OHLCV_INDICATORS {
|
|
|
202
227
|
|
|
203
228
|
|
|
204
229
|
|
|
205
|
-
crossPairs(arr = [])
|
|
230
|
+
crossPairs(arr = [], options = {})
|
|
206
231
|
{
|
|
207
232
|
const methodName = 'crossPairs'
|
|
208
233
|
|
|
209
234
|
isAlreadyComputed(this)
|
|
210
|
-
|
|
211
235
|
validateArray(arr, 'arr', methodName)
|
|
236
|
+
validateObject(options, 'options', methodName)
|
|
237
|
+
|
|
238
|
+
const {limit = 125, oneHot = false} = options
|
|
239
|
+
|
|
240
|
+
validateNumber(limit, {min: 2, max: this.len}, 'options.limit', methodName)
|
|
241
|
+
validateBoolean(oneHot, 'options.oneHot', methodName)
|
|
242
|
+
|
|
243
|
+
const orderArr = []
|
|
244
|
+
|
|
245
|
+
for (const {fast = '', slow = ''} of arr) {
|
|
246
|
+
|
|
247
|
+
if (fast === '' || slow === '') {
|
|
248
|
+
throw new Error(
|
|
249
|
+
`Invalid crossPairs[${fast}_${slow}]: Object property “fast” must be a non-null column name and “slow” must be a non-null column name or integer.`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
orderArr.push(fast.toString(), slow.toString())
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if(this.isAlreadyComputed.has(methodName))
|
|
257
|
+
{
|
|
258
|
+
throw new Error(`You can only call the "${methodName}" method once.`)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
this.isAlreadyComputed.add(methodName)
|
|
262
|
+
|
|
263
|
+
const order = getOrderFromArray(orderArr, methodName)
|
|
212
264
|
|
|
213
|
-
this.
|
|
214
|
-
this.inputParams.push({key: methodName, params: [this.crossPairsList]})
|
|
265
|
+
this.inputParams.push({key: methodName, order, params: [arr, {limit, oneHot}]})
|
|
215
266
|
|
|
216
267
|
return this
|
|
217
268
|
}
|
|
@@ -222,19 +273,11 @@ export default class OHLCV_INDICATORS {
|
|
|
222
273
|
const methodName = 'lag'
|
|
223
274
|
|
|
224
275
|
isAlreadyComputed(this)
|
|
225
|
-
|
|
226
276
|
validateArray(colKeys, 'colKeys', methodName)
|
|
227
277
|
validateNumber(lookback, {min:1, max: this.len, allowDecimals: false}, 'lookback', methodName)
|
|
228
278
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
for(const key of colKeys)
|
|
232
|
-
{
|
|
233
|
-
if(this.priceBased.has(key))
|
|
234
|
-
{
|
|
235
|
-
this.priceBased.add(key);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
279
|
+
const order = getOrderFromArray(colKeys, methodName)
|
|
280
|
+
this.inputParams.push({key: methodName, order, params: [colKeys, lookback]})
|
|
238
281
|
|
|
239
282
|
return this;
|
|
240
283
|
}
|
|
@@ -256,7 +299,7 @@ export default class OHLCV_INDICATORS {
|
|
|
256
299
|
|
|
257
300
|
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'options.lag', methodName)
|
|
258
301
|
|
|
259
|
-
this.inputParams.push({key: methodName, params: [size, {lag}]})
|
|
302
|
+
this.inputParams.push({key: methodName, order: 0, params: [size, {lag}]})
|
|
260
303
|
|
|
261
304
|
return this
|
|
262
305
|
}
|
|
@@ -278,7 +321,7 @@ export default class OHLCV_INDICATORS {
|
|
|
278
321
|
if(upper !== null) validateNumber(upper, {min: 0.001, max: 100, allowDecimals: true}, 'options.upper', 'atr')
|
|
279
322
|
if(lower !== null) validateNumber(lower, {min: 0.001, max: 100, allowDecimals: true}, 'options.lower', 'atr')
|
|
280
323
|
|
|
281
|
-
this.inputParams.push({key: methodName, params: [size, {lag, percentage, upper, lower}]})
|
|
324
|
+
this.inputParams.push({key: methodName, order: 0, params: [size, {lag, percentage, upper, lower}]})
|
|
282
325
|
|
|
283
326
|
return this
|
|
284
327
|
}
|
|
@@ -294,11 +337,12 @@ export default class OHLCV_INDICATORS {
|
|
|
294
337
|
|
|
295
338
|
const {target = 'close', lag = 0} = options
|
|
296
339
|
|
|
340
|
+
validateString(target, 'options.target', methodName)
|
|
297
341
|
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'options.lag', methodName)
|
|
298
342
|
|
|
299
|
-
const
|
|
343
|
+
const order = getOrderFromArray([target], methodName)
|
|
300
344
|
|
|
301
|
-
this.inputParams.push({key: methodName, params: [methodName, size, {target, lag
|
|
345
|
+
this.inputParams.push({key: methodName, order, params: [methodName, size, {target, lag}]})
|
|
302
346
|
|
|
303
347
|
return this
|
|
304
348
|
}
|
|
@@ -313,16 +357,75 @@ export default class OHLCV_INDICATORS {
|
|
|
313
357
|
|
|
314
358
|
const {target = 'close', lag = 0} = options
|
|
315
359
|
|
|
360
|
+
validateString(target, 'options.target', methodName)
|
|
316
361
|
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'options.lag', methodName)
|
|
317
362
|
|
|
363
|
+
const order = getOrderFromArray([target], methodName)
|
|
364
|
+
|
|
365
|
+
this.inputParams.push({key: methodName, order, params: [methodName, size, {target, lag}]})
|
|
366
|
+
|
|
367
|
+
return this
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
vidya(size = 14, smoothing = 20, options = {}) {
|
|
371
|
+
|
|
372
|
+
const methodName = 'vidya'
|
|
373
|
+
|
|
374
|
+
isAlreadyComputed(this)
|
|
375
|
+
|
|
376
|
+
validateNumber(size, {min: 2, max: this.len, allowDecimals: false}, 'size', methodName)
|
|
377
|
+
validateNumber(smoothing, {min: size, max: this.len, allowDecimals: false}, 'smoothing', methodName)
|
|
378
|
+
validateObject(options, 'options', methodName)
|
|
318
379
|
|
|
319
|
-
const
|
|
380
|
+
const {target = 'close', lag = 0} = options
|
|
320
381
|
|
|
321
|
-
|
|
382
|
+
validateString(target, 'options.target', methodName)
|
|
383
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'options.lag', methodName)
|
|
384
|
+
|
|
385
|
+
const order = getOrderFromArray([target], methodName)
|
|
386
|
+
|
|
387
|
+
this.inputParams.push({key: methodName, order, params: [size, smoothing, {target, lag}]})
|
|
322
388
|
|
|
323
389
|
return this
|
|
324
390
|
}
|
|
325
391
|
|
|
392
|
+
stochastic(kPeriod = 14, kSlowingPeriod = 3, dPeriod = 3, options = {}){
|
|
393
|
+
const methodName = 'stochastic'
|
|
394
|
+
|
|
395
|
+
const {len: max} = this
|
|
396
|
+
|
|
397
|
+
validateNumber(kPeriod, {min: 1, max, allowDecimals: false}, 'kPeriod', methodName)
|
|
398
|
+
validateNumber(kSlowingPeriod, {min: 1, max, allowDecimals: false}, 'kSlowingPeriod', methodName)
|
|
399
|
+
validateNumber(dPeriod, {min: 1, max, allowDecimals: false}, 'dPeriod', methodName)
|
|
400
|
+
validateObject(options, 'options', methodName)
|
|
401
|
+
|
|
402
|
+
let parser = v => v
|
|
403
|
+
let prefix = ''
|
|
404
|
+
|
|
405
|
+
const {lag = 0} = options
|
|
406
|
+
|
|
407
|
+
validateNumber(lag, {min: 0, max, allowDecimals: false}, 'options.lag', methodName)
|
|
408
|
+
|
|
409
|
+
let minmax = options.minmax
|
|
410
|
+
|
|
411
|
+
if(Array.isArray(minmax) || (typeof minmax === 'boolean' && minmax === true))
|
|
412
|
+
{
|
|
413
|
+
if(Array.isArray(minmax)){
|
|
414
|
+
validateArrayOfRanges(minmax, 'options.minmax', methodName)
|
|
415
|
+
parser = value => (minmax !== null) ? normalizeMinMax(value, 0, 100, minmax) : value
|
|
416
|
+
} else
|
|
417
|
+
{
|
|
418
|
+
validateBoolean(minmax, 'options.minmax', methodName)
|
|
419
|
+
minmax = [0, 1]
|
|
420
|
+
parser = value => (minmax !== null) ? normalizeMinMax(value, 0, 100, minmax) : value
|
|
421
|
+
}
|
|
422
|
+
prefix = 'minmax_'
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
this.inputParams.push({key: methodName, order: 0, params: [kPeriod, kSlowingPeriod, dPeriod, {minmax, prefix, parser, lag}]})
|
|
426
|
+
|
|
427
|
+
return this
|
|
428
|
+
}
|
|
326
429
|
|
|
327
430
|
macd(fast = 12, slow = 26, signal = 9, options = {}) {
|
|
328
431
|
|
|
@@ -337,12 +440,15 @@ export default class OHLCV_INDICATORS {
|
|
|
337
440
|
|
|
338
441
|
const {target = 'close', lag = 0} = options
|
|
339
442
|
|
|
443
|
+
validateString(target, 'options.target', methodName)
|
|
340
444
|
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'options.lag', methodName)
|
|
341
445
|
|
|
342
446
|
const instanceKey = `${fast}_${slow}_${signal}${target === 'close' ? '' : `_${target}`}`
|
|
343
447
|
const precomputed = {instanceKey}
|
|
344
448
|
|
|
345
|
-
|
|
449
|
+
const order = getOrderFromArray([target], methodName)
|
|
450
|
+
|
|
451
|
+
this.inputParams.push({key: methodName, order, params: [fast, slow, signal, {target, lag, precomputed}]})
|
|
346
452
|
|
|
347
453
|
return this
|
|
348
454
|
|
|
@@ -359,13 +465,15 @@ export default class OHLCV_INDICATORS {
|
|
|
359
465
|
|
|
360
466
|
const {target = 'close', height = false, range = [], lag = 0, decimals = null} = options
|
|
361
467
|
|
|
362
|
-
|
|
468
|
+
validateString(target, 'options.target', methodName)
|
|
363
469
|
validateArray(range, 'options.range', methodName)
|
|
364
470
|
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'options.lag', methodName)
|
|
365
471
|
validateBoolean(height, 'options.height', methodName)
|
|
366
472
|
if(decimals !== null) validateNumber(decimals, {min: 1, max: 15, allowDecimals: false}, 'decimals', methodName)
|
|
367
|
-
|
|
368
|
-
|
|
473
|
+
|
|
474
|
+
const order = getOrderFromArray([target], methodName)
|
|
475
|
+
|
|
476
|
+
this.inputParams.push({key: methodName, order, params: [size, stdDev, {target, height, range, lag, decimals}]});
|
|
369
477
|
|
|
370
478
|
return this;
|
|
371
479
|
}
|
|
@@ -380,10 +488,30 @@ export default class OHLCV_INDICATORS {
|
|
|
380
488
|
validateObject(options, 'options', methodName)
|
|
381
489
|
|
|
382
490
|
const {target = 'close', lag = 0} = options
|
|
383
|
-
|
|
491
|
+
let minmax = options.minmax
|
|
492
|
+
validateString(target, 'options.target', methodName)
|
|
384
493
|
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'options.lag', methodName)
|
|
385
494
|
|
|
386
|
-
|
|
495
|
+
let parser = v => v
|
|
496
|
+
let prefix = ''
|
|
497
|
+
|
|
498
|
+
if(Array.isArray(minmax) || (typeof minmax === 'boolean' && minmax === true))
|
|
499
|
+
{
|
|
500
|
+
if(Array.isArray(minmax)){
|
|
501
|
+
validateArrayOfRanges(minmax, 'options.minmax', methodName)
|
|
502
|
+
parser = value => (minmax !== null) ? normalizeMinMax(value, 0, 100, minmax) : value
|
|
503
|
+
} else
|
|
504
|
+
{
|
|
505
|
+
validateBoolean(minmax, 'options.minmax', methodName)
|
|
506
|
+
minmax = [0, 1]
|
|
507
|
+
parser = value => (minmax !== null) ? normalizeMinMax(value, 0, 100, minmax) : value
|
|
508
|
+
}
|
|
509
|
+
prefix = 'minmax_'
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const order = getOrderFromArray([target], methodName)
|
|
513
|
+
|
|
514
|
+
this.inputParams.push({key: methodName, order, params: [size, {target, lag, parser, prefix, minmax}]})
|
|
387
515
|
|
|
388
516
|
return this
|
|
389
517
|
}
|
|
@@ -405,13 +533,13 @@ export default class OHLCV_INDICATORS {
|
|
|
405
533
|
validateBoolean(height, 'options.height', methodName)
|
|
406
534
|
if(decimals !== null) validateNumber(decimals, {min: 1, max: 15, allowDecimals: false}, 'decimals', methodName)
|
|
407
535
|
|
|
408
|
-
this.inputParams.push({ key: methodName, params: [size, offset, { height, range, lag, decimals}] });
|
|
536
|
+
this.inputParams.push({ key: methodName, order: 0, params: [size, offset, { height, range, lag, decimals}] });
|
|
409
537
|
|
|
410
538
|
return this;
|
|
411
539
|
}
|
|
412
540
|
|
|
413
541
|
|
|
414
|
-
volumeOscillator(
|
|
542
|
+
volumeOscillator(fastsize = 5, slowsize = 10, options = {})
|
|
415
543
|
{
|
|
416
544
|
const methodName = 'volumeOscillator'
|
|
417
545
|
|
|
@@ -421,15 +549,15 @@ export default class OHLCV_INDICATORS {
|
|
|
421
549
|
|
|
422
550
|
isAlreadyComputed(this)
|
|
423
551
|
|
|
424
|
-
validateNumber(
|
|
425
|
-
validateNumber(
|
|
552
|
+
validateNumber(fastsize, {min: 1, max: this.len, allowDecimals: false}, 'fastsize', methodName)
|
|
553
|
+
validateNumber(slowsize, {min: fastsize, max: this.len, allowDecimals: false}, 'slowsize', methodName)
|
|
426
554
|
validateObject(options, 'options', methodName)
|
|
427
555
|
|
|
428
556
|
const {lag = 0} = options
|
|
429
557
|
|
|
430
558
|
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'options.lag', methodName)
|
|
431
559
|
|
|
432
|
-
this.inputParams.push({key: methodName, params: [
|
|
560
|
+
this.inputParams.push({key: methodName, order: 0, params: [fastsize, slowsize, {lag}]})
|
|
433
561
|
return this
|
|
434
562
|
}
|
|
435
563
|
dateTime(options = {})
|
|
@@ -441,37 +569,375 @@ export default class OHLCV_INDICATORS {
|
|
|
441
569
|
|
|
442
570
|
validateObject(options, 'options', methodName)
|
|
443
571
|
|
|
444
|
-
const {lag = 0} = options
|
|
572
|
+
const {lag = 0, oneHot = false} = options
|
|
445
573
|
|
|
446
574
|
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'options.lag', methodName)
|
|
575
|
+
validateBoolean(oneHot, 'options.oneHot', methodName)
|
|
576
|
+
|
|
577
|
+
const prefix = (oneHot) ? 'one_hot_' : ''
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
const colKeySizes = {
|
|
581
|
+
[`${prefix}month`]: 12,
|
|
582
|
+
[`${prefix}day_of_the_month`]: 31,
|
|
583
|
+
[`${prefix}day_of_the_week`]: 7,
|
|
584
|
+
[`${prefix}hour`]: 24,
|
|
585
|
+
[`${prefix}minute`]: 60
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const precompute = {
|
|
589
|
+
prefix,
|
|
590
|
+
colKeySizes,
|
|
591
|
+
colKeys: Object.keys(colKeySizes)
|
|
592
|
+
}
|
|
447
593
|
|
|
448
|
-
this.inputParams.push({key: methodName, params: [{lag}]})
|
|
594
|
+
this.inputParams.push({key: methodName, order: 0, params: [{lag, oneHot, precompute}]})
|
|
449
595
|
return this
|
|
450
596
|
}
|
|
451
597
|
|
|
452
|
-
scaler(
|
|
598
|
+
scaler(type = 'zscore', size, options = {})
|
|
453
599
|
{
|
|
454
|
-
|
|
600
|
+
let methodName = 'scaler'
|
|
455
601
|
|
|
456
602
|
isAlreadyComputed(this)
|
|
457
603
|
|
|
458
604
|
validateNumber(size, {min: 1, max: this.len, allowDecimals: false}, 'size', methodName)
|
|
459
|
-
|
|
605
|
+
|
|
606
|
+
validateObject(options, 'options', methodName)
|
|
460
607
|
|
|
461
|
-
const {group = false, range = [0, 1], lag = 0,
|
|
608
|
+
const {group = false, range = [0, 1], lag = 0, colKeys = []} = options
|
|
462
609
|
|
|
610
|
+
validateArray(colKeys, 'options.colKeys', methodName)
|
|
463
611
|
validateBoolean(group, 'options.group', methodName)
|
|
464
|
-
if(pca !== null) validateObject(pca, 'options.pca', methodName)
|
|
465
612
|
validateArrayOfRanges(range, 'options.range', methodName)
|
|
466
613
|
validateArrayOptions(['minmax', 'zscore'], type, 'options.type', methodName)
|
|
467
614
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
const groupKey = (group) ? `${type}_${size}_group_${colKeys.join('_')}` : ''
|
|
615
|
+
const groupKey = `${type}_${size}`
|
|
471
616
|
const groupKeyLen = colKeys.length
|
|
472
617
|
const precomputed = {groupKey, groupKeyLen}
|
|
473
618
|
|
|
474
|
-
|
|
619
|
+
let secondaryLoop = false
|
|
620
|
+
|
|
621
|
+
const order = getOrderFromArray(colKeys, methodName)
|
|
622
|
+
|
|
623
|
+
if(order >= 10)
|
|
624
|
+
{
|
|
625
|
+
secondaryLoop = true
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
this.inputParams.push({key: methodName, order, params: [size, colKeys, {type, group, range, lag, precomputed, secondaryLoop}]})
|
|
629
|
+
return this
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
classifier(trainingSize = 200, options = {})
|
|
633
|
+
{
|
|
634
|
+
isAlreadyComputed(this)
|
|
635
|
+
|
|
636
|
+
const methodName = 'classifier'
|
|
637
|
+
validateNumber(trainingSize, {max: this.len, allowDecimals: false}, 'trainingSize', methodName)
|
|
638
|
+
validateObject(options, 'options', methodName)
|
|
639
|
+
|
|
640
|
+
const {
|
|
641
|
+
retrain = false,
|
|
642
|
+
trainingCols = [],
|
|
643
|
+
type = 'KNN',
|
|
644
|
+
lookback = 0,
|
|
645
|
+
findGroups = [],
|
|
646
|
+
predictions = 2,
|
|
647
|
+
horizon = 2,
|
|
648
|
+
modelArgs = undefined,
|
|
649
|
+
filterCallback = () => true
|
|
650
|
+
} = options
|
|
651
|
+
|
|
652
|
+
if (modelArgs !== undefined && (typeof modelArgs !== 'object' || modelArgs === null || Array.isArray(modelArgs))) throw new TypeError(`"modelArgs" must be either undefined or a plain object in ${type}`);
|
|
653
|
+
|
|
654
|
+
const yCallback = options.yCallback ?? defaultYCallback
|
|
655
|
+
|
|
656
|
+
if(typeof yCallback !== 'function')
|
|
657
|
+
{
|
|
658
|
+
throw new Error(`"yCallback" must be a function in the following format:\n\n---\n\nconst yCallback = ${defaultYCallback.toString()}\n\n---\n\n`)
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
validateArrayOptions(Object.keys(validClassifiers), type, 'options.type', methodName)
|
|
662
|
+
|
|
663
|
+
if (!this.ML.classes.hasOwnProperty(type)) {
|
|
664
|
+
throw new Error(
|
|
665
|
+
`"${type}" isn’t available because its library wasn’t imported into OHLCV_INDICATORS.ML.`
|
|
666
|
+
)
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
validateBoolean(retrain, 'options.retrain', methodName)
|
|
670
|
+
validateArray(trainingCols, 'options.trainingCols', methodName)
|
|
671
|
+
validateNumber(horizon, {min: 1, max: this.len, allowDecimals: false}, 'options.horizon', methodName)
|
|
672
|
+
validateNumber(lookback, {max: 0, allowDecimals: false}, 'options.lookback', methodName)
|
|
673
|
+
validateNumber(predictions, {min: 1, allowDecimals: false}, 'options.predictions', methodName)
|
|
674
|
+
|
|
675
|
+
const orderArr = [...trainingCols]
|
|
676
|
+
|
|
677
|
+
if(validateArray(findGroups, 'options.findGroups', 'scaler') && findGroups.length > 0)
|
|
678
|
+
{
|
|
679
|
+
for (const { type = '', size, groupName = '' } of findGroups) {
|
|
680
|
+
|
|
681
|
+
if (!type || (!size && !groupName) || (size && groupName)) {
|
|
682
|
+
throw new Error(`If "options.findGroups" array is set, each item must be an object that includes the "type" (mandatory) and either "size" or "groupName" (choose 1) used to locate previously scaled (minmax or zscore) groups.`);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
orderArr.push(type.toString(), groupName.toString());
|
|
686
|
+
}
|
|
687
|
+
} else {
|
|
688
|
+
if(trainingCols.length === 0)
|
|
689
|
+
{
|
|
690
|
+
throw new Error(`"trainingCols" array is empty in ${methodName}`)
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
const {shortName, flatY} = validClassifiers[type]
|
|
695
|
+
|
|
696
|
+
const prefix = `cla_${shortName}_${trainingSize}_prediction`
|
|
697
|
+
|
|
698
|
+
if(this.isAlreadyComputed.has(prefix))
|
|
699
|
+
{
|
|
700
|
+
throw new Error(
|
|
701
|
+
`Each classifier must have a unique pair of “type” and trainingSize.\n` +
|
|
702
|
+
`This rule ensures that your output columns are labeled unambiguously.\n` +
|
|
703
|
+
`You provided a duplicate: type="${type}" with trainingSize=${trainingSize}.`
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
const precompute = {
|
|
708
|
+
lookbackAbs: Math.abs(lookback) + 1,
|
|
709
|
+
flatY,
|
|
710
|
+
prefix
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
this.isAlreadyComputed.add(prefix)
|
|
714
|
+
|
|
715
|
+
const order = getOrderFromArray(orderArr, methodName)
|
|
716
|
+
|
|
717
|
+
const modelConfig = validClassifiers[type]
|
|
718
|
+
const modelClass = this.ML.classes[type]
|
|
719
|
+
|
|
720
|
+
this.inputParams.push({key: methodName, order, params: [trainingSize, {yCallback, predictions, lookback, retrain, trainingCols, findGroups, horizon, type, modelArgs, precompute, filterCallback, modelConfig, modelClass}]})
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
return this
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
regressor(trainingSize = 200, options = {})
|
|
727
|
+
{
|
|
728
|
+
isAlreadyComputed(this)
|
|
729
|
+
|
|
730
|
+
const methodName = 'regressor'
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
validateNumber(trainingSize, {max: this.len, allowDecimals: false}, 'trainingSize', methodName)
|
|
734
|
+
validateObject(options, 'options', methodName)
|
|
735
|
+
|
|
736
|
+
const {
|
|
737
|
+
retrain = false,
|
|
738
|
+
target = 'close',
|
|
739
|
+
predictions = 1,
|
|
740
|
+
trainingCols = [],
|
|
741
|
+
type = 'SimpleLinearRegression',
|
|
742
|
+
lookback = 0,
|
|
743
|
+
findGroups = [],
|
|
744
|
+
modelArgs = undefined,
|
|
745
|
+
filterCallback = () => true
|
|
746
|
+
} = options
|
|
747
|
+
|
|
748
|
+
validateArrayOptions(Object.keys(validRegressors), type, 'type', methodName)
|
|
749
|
+
|
|
750
|
+
if (!this.ML.classes.hasOwnProperty(type)) {
|
|
751
|
+
throw new Error(
|
|
752
|
+
`"${type}" isn’t available because its library wasn’t imported into OHLCV_INDICATORS.ML.`
|
|
753
|
+
)
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
//regressor
|
|
757
|
+
validateBoolean(retrain, 'options.retrain', methodName)
|
|
758
|
+
validateString(target, 'options.target', methodName)
|
|
759
|
+
validateNumber(predictions, {min: 1, allowDecimals: false}, 'predictions', methodName)
|
|
760
|
+
validateNumber(lookback, {max: 0, allowDecimals: false}, 'lookback', methodName)
|
|
761
|
+
validateArray(trainingCols, 'options.trainingCols', methodName)
|
|
762
|
+
|
|
763
|
+
const {shortName, flatX, flatY} = validRegressors[type]
|
|
764
|
+
const prefix = `reg_${shortName}_${trainingSize}_${target}_prediction`
|
|
765
|
+
const orderArr = [...trainingCols]
|
|
766
|
+
|
|
767
|
+
if(validateArray(findGroups, 'options.findGroups', 'scaler') && findGroups.length > 0)
|
|
768
|
+
{
|
|
769
|
+
for (const { type = '', size, groupName = '' } of findGroups) {
|
|
770
|
+
|
|
771
|
+
if (!type || (!size && !groupName) || (size && groupName)) {
|
|
772
|
+
throw new Error(`If "options.findGroups" array is set, each item must be an object that includes the "type" (mandatory) and either "size" or "groupName" (choose 1) used to locate previously scaled (minmax or zscore) groups.`);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
orderArr.push(type.toString(), groupName.toString());
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
} else {
|
|
779
|
+
if(trainingCols.length === 0 && flatX === false)
|
|
780
|
+
{
|
|
781
|
+
throw new Error(`"trainingCols" array is empty in ${methodName} ${type}`)
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
if(flatX) {
|
|
786
|
+
if(trainingCols.length > 0)
|
|
787
|
+
{
|
|
788
|
+
throw new Error(`If regressor type is ${type} then leave "options.trainingCols" array empty.`)
|
|
789
|
+
}
|
|
790
|
+
if(lookback > 0)
|
|
791
|
+
{
|
|
792
|
+
throw new Error(`If regressor type is ${type} then "options.lookback" must be 0.`)
|
|
793
|
+
}
|
|
794
|
+
if(findGroups.length > 0)
|
|
795
|
+
{
|
|
796
|
+
throw new Error(`If regressor type is ${type} then "options.findGroups" must be null.`)
|
|
797
|
+
}
|
|
798
|
+
else {
|
|
799
|
+
trainingCols.push(target)
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
if(this.isAlreadyComputed.has(prefix))
|
|
804
|
+
{
|
|
805
|
+
throw new Error(
|
|
806
|
+
`Each regressor must have a unique "type", "trainingSize" and "target".\n` +
|
|
807
|
+
`This rule ensures that your output columns are labeled unambiguously.\n` +
|
|
808
|
+
`You provided a duplicate: type="${type}", trainingSize=${trainingSize} target=${target}.`
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
const precompute = {
|
|
813
|
+
lookbackAbs: Math.abs(lookback) + 1,
|
|
814
|
+
flatX,
|
|
815
|
+
flatY,
|
|
816
|
+
prefix
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
this.isAlreadyComputed.add(prefix)
|
|
820
|
+
|
|
821
|
+
|
|
822
|
+
const order = getOrderFromArray(orderArr, methodName)
|
|
823
|
+
|
|
824
|
+
const modelConfig = validRegressors[type]
|
|
825
|
+
const modelClass = this.ML.classes[type]
|
|
826
|
+
|
|
827
|
+
this.inputParams.push({key: methodName, order, params: [trainingSize, {target, predictions, retrain, lookback, trainingCols, findGroups, type, modelArgs, precompute, filterCallback, modelConfig, modelClass}]})
|
|
828
|
+
|
|
829
|
+
return this
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
pca(suffix, trainingSize, options = {})
|
|
833
|
+
{
|
|
834
|
+
const methodName = 'pca'
|
|
835
|
+
validateNumber(trainingSize, {min: 1, max: this.len}, 'trainingSize', methodName)
|
|
836
|
+
validateString(suffix, 'suffix', methodName)
|
|
837
|
+
validateObject(options, 'options', methodName)
|
|
838
|
+
|
|
839
|
+
const {
|
|
840
|
+
findGroups = [],
|
|
841
|
+
trainingCols = [],
|
|
842
|
+
lookback = 0,
|
|
843
|
+
filterCallback = () => true
|
|
844
|
+
} = options
|
|
845
|
+
|
|
846
|
+
|
|
847
|
+
validateArray(trainingCols, 'options.trainigCols', methodName)
|
|
848
|
+
validateNumber(lookback, {min: 0, max: this.len, allowDecimals: false}, 'options.lookback', methodName)
|
|
849
|
+
if(typeof options.modelArgs !== 'undefined') {
|
|
850
|
+
validateObject(options.modelArgs, 'options.modelArgs', methodName)
|
|
851
|
+
} else {
|
|
852
|
+
options.modelArgs = {}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
//modelArgs
|
|
856
|
+
|
|
857
|
+
const modelArgs = {
|
|
858
|
+
showSource: false,
|
|
859
|
+
storeModel: false,
|
|
860
|
+
isCovarianceMatrix: false,
|
|
861
|
+
method: 'NIPALS', //SVD, covarianceMatrix or NIPALS
|
|
862
|
+
center: true,
|
|
863
|
+
scale: false,
|
|
864
|
+
nCompNIPALS: 5,
|
|
865
|
+
ignoreZeroVariance: false,
|
|
866
|
+
...options.modelArgs
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
validateBoolean(modelArgs.showSource, 'options.modelArgs.showSource', methodName)
|
|
870
|
+
validateBoolean(modelArgs.storeModel, 'options.modelArgs.storeModel', methodName)
|
|
871
|
+
validateBoolean(modelArgs.isCovarianceMatrix, 'options.modelArgs.isCovarianceMatrix', methodName)
|
|
872
|
+
validateBoolean(modelArgs.center, 'options.modelArgs.center', methodName)
|
|
873
|
+
validateBoolean(modelArgs.scale, 'options.modelArgs.scale', methodName)
|
|
874
|
+
validateBoolean(modelArgs.ignoreZeroVariance, 'options.modelArgs.ignoreZeroVariance', methodName)
|
|
875
|
+
validateArrayOptions(['SVD', 'NIPALS', 'covarianceMatrix'], modelArgs.method, 'options.modelArgs.method', methodName)
|
|
876
|
+
validateNumber(modelArgs.nCompNIPALS, {min: 1}, 'options.modelArgs.nCompNIPALS', methodName)
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
const orderArr = []
|
|
882
|
+
|
|
883
|
+
if(validateArray(findGroups, 'options.findGroups', 'pca') && findGroups.length > 0)
|
|
884
|
+
{
|
|
885
|
+
|
|
886
|
+
for (const { type = '', size, groupName = '' } of findGroups) {
|
|
887
|
+
|
|
888
|
+
if (!type || (!size && !groupName) || (size && groupName)) {
|
|
889
|
+
throw new Error(`If "options.findGroups" array is set, each item must be an object that includes the "type" (mandatory) and either "size" or "groupName" (choose 1) used to locate previously scaled (minmax or zscore) groups.`);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
orderArr.push(type.toString(), groupName.toString())
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
const order = getOrderFromArray(orderArr, methodName)
|
|
897
|
+
const prefix = `pca_${suffix}_${trainingSize}`
|
|
898
|
+
|
|
899
|
+
if(this.isAlreadyComputed.has(prefix)) {
|
|
900
|
+
throw new Error(
|
|
901
|
+
`Each regressor must have a unique "suffix" param.\n` +
|
|
902
|
+
`This rule ensures that your output columns are labeled unambiguously.\n` +
|
|
903
|
+
`You provided a duplicate: type="${suffix}".`
|
|
904
|
+
)
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
const lookbackAbs = Math.abs(lookback) + 1
|
|
908
|
+
this.isAlreadyComputed.add(prefix)
|
|
909
|
+
|
|
910
|
+
this.inputParams.push({key: methodName, order, params: [{prefix, trainingSize, findGroups, trainingCols, lookbackAbs, modelArgs, filterCallback}]})
|
|
911
|
+
|
|
912
|
+
return this
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
exportTrainedModels()
|
|
916
|
+
{
|
|
917
|
+
return exportTrainedModels(this)
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
mapCols(newCols = ['change'], callback = null, options = {}) {
|
|
921
|
+
|
|
922
|
+
isAlreadyComputed(this)
|
|
923
|
+
|
|
924
|
+
const methodName = 'mapCols'
|
|
925
|
+
|
|
926
|
+
if(typeof callback === 'undefined' || callback === null)
|
|
927
|
+
{
|
|
928
|
+
callback = defaultMapColsCallback
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
validateObject(options, 'options', methodName)
|
|
932
|
+
|
|
933
|
+
const {lag = 0} = options
|
|
934
|
+
|
|
935
|
+
validateArray(newCols, 'newCols', methodName)
|
|
936
|
+
validateNumber(lag, {min: 0, allowDecimals: false}, 'options.lag', methodName)
|
|
937
|
+
const order = getOrderFromArray(newCols, methodName)
|
|
938
|
+
|
|
939
|
+
this.inputParams.push({key: methodName, order, params: [newCols, callback, {lag}]})
|
|
940
|
+
|
|
475
941
|
return this
|
|
476
942
|
}
|
|
477
943
|
}
|