ohlcv-indicators 3.5.56 → 3.5.59
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 +39 -337
- package/package.json +2 -13
- package/src/core-functions/mainLoop.js +10 -42
- package/src/machine-learning/ml-utilities.js +1 -117
- package/src/machine-learning/scaler.js +78 -76
- package/src/studies/dateTime.js +29 -25
- package/src/studies/priceFeatures.js +78 -0
- package/src/utilities/dateUtilities.js +1 -1
- package/src/machine-learning/balancing.js +0 -65
- package/src/machine-learning/classifier.js +0 -321
- package/src/machine-learning/ml-config.js +0 -219
- package/src/machine-learning/pca.js +0 -95
- package/src/machine-learning/regressor.js +0 -227
- package/src/machine-learning/trainX.js +0 -73
- package/src/utilities/order.js +0 -51
package/index.js
CHANGED
|
@@ -15,35 +15,25 @@ import { pushToMain } from './src/core-functions/pushToMain.js'
|
|
|
15
15
|
import { assignTypes } from './src/utilities/assignTypes.js'
|
|
16
16
|
import { dateOutputFormaters } from './src/utilities/dateUtilities.js'
|
|
17
17
|
import { defaultMapColsCallback } from './src/studies/mapCols.js'
|
|
18
|
-
import { getOrderFromArray } from './src/utilities/order.js'
|
|
19
18
|
import { calcPrecisionMultiplier } from './src/utilities/precisionMultiplier.js'
|
|
20
19
|
|
|
21
|
-
import {
|
|
22
|
-
defaultYCallback,
|
|
23
|
-
validRegressors,
|
|
24
|
-
validClassifiers,
|
|
25
|
-
exportTrainedModels
|
|
26
|
-
} from './src/machine-learning/ml-config.js'
|
|
27
|
-
|
|
28
20
|
import { normalizeMinMax } from './src/machine-learning/ml-utilities.js'
|
|
29
21
|
|
|
30
22
|
/**
|
|
31
23
|
* Class OHLCV_INDICATORS
|
|
32
24
|
*
|
|
33
25
|
* This class provides methods for calculating and managing technical indicators
|
|
34
|
-
* on financial OHLCV (Open, High, Low, Close, Volume) data. It enables users
|
|
35
|
-
* to parallel compute various technical indicators in 1 single loop for standard indicators and a second loop for ML regressors and classifier.
|
|
26
|
+
* on financial OHLCV (Open, High, Low, Close, Volume) data. It enables users
|
|
36
27
|
* OHLCV datasets.
|
|
37
28
|
*/
|
|
38
29
|
|
|
39
30
|
export default class OHLCV_INDICATORS {
|
|
40
|
-
constructor({input, ticker = null, inputParams = null, chunkProcess = 2000,
|
|
31
|
+
constructor({input, ticker = null, inputParams = null, chunkProcess = 2000, precision = false}) {
|
|
41
32
|
|
|
42
33
|
validateArray(input, 'input', (ticker !== null) ? `contructor ${ticker}` : 'constuctor')
|
|
43
34
|
if(input.length === 0) throw Error('input OHLCV must not be empty: ' + ticker)
|
|
44
35
|
|
|
45
36
|
validateNumber(chunkProcess, {min: 100, max: 50000, allowDecimals: false}, 'chunkProcess', 'constructor')
|
|
46
|
-
validateObject(ML, 'ML', 'constructor')
|
|
47
37
|
validateBoolean(precision, 'precision', 'contructor')
|
|
48
38
|
|
|
49
39
|
this.chunkProcess = chunkProcess
|
|
@@ -78,24 +68,12 @@ export default class OHLCV_INDICATORS {
|
|
|
78
68
|
this.scaledGroups = {}
|
|
79
69
|
this.isAlreadyComputed = new Set()
|
|
80
70
|
|
|
81
|
-
const {classes: mlClasses} = ML
|
|
82
|
-
|
|
83
|
-
if(typeof mlClasses !== 'undefined') validateObject(mlClasses, 'ML.classes', 'constructor')
|
|
84
|
-
|
|
85
|
-
this.ML = {
|
|
86
|
-
models: {},
|
|
87
|
-
classes: mlClasses,
|
|
88
|
-
metrics: {},
|
|
89
|
-
featureCols: {}
|
|
90
|
-
}
|
|
91
|
-
this.processSecondaryLoop = false
|
|
92
|
-
|
|
93
71
|
this.pushToMain = ({index, key, value}) => pushToMain({main: this, index, key, value})
|
|
94
72
|
|
|
95
73
|
if(inputParams !== null)
|
|
96
74
|
{
|
|
97
75
|
validateInputParams(inputParams, this.len)
|
|
98
|
-
this.inputParams = inputParams
|
|
76
|
+
this.inputParams = inputParams.sort((a, b) => a.order - b.order)
|
|
99
77
|
this.compute()
|
|
100
78
|
}
|
|
101
79
|
else
|
|
@@ -150,6 +128,8 @@ export default class OHLCV_INDICATORS {
|
|
|
150
128
|
if (this.isComputed) {
|
|
151
129
|
return this;
|
|
152
130
|
}
|
|
131
|
+
|
|
132
|
+
this.inputParams = this.inputParams.sort((a, b) => a.order - b.order)
|
|
153
133
|
|
|
154
134
|
// Mark as “in progress”
|
|
155
135
|
this.isComputed = false;
|
|
@@ -208,9 +188,7 @@ export default class OHLCV_INDICATORS {
|
|
|
208
188
|
|
|
209
189
|
this.isAlreadyComputed.add(methodName)
|
|
210
190
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
this.inputParams.push({key: methodName, order, params: [arr, {limit, oneHot}]})
|
|
191
|
+
this.inputParams.push({key: methodName, params: [arr, {limit, oneHot}]})
|
|
214
192
|
|
|
215
193
|
return this
|
|
216
194
|
}
|
|
@@ -224,8 +202,7 @@ export default class OHLCV_INDICATORS {
|
|
|
224
202
|
validateArray(colKeys, 'colKeys', methodName)
|
|
225
203
|
validateNumber(lookback, {min:1, max: this.len, allowDecimals: false}, 'lookback', methodName)
|
|
226
204
|
|
|
227
|
-
|
|
228
|
-
this.inputParams.push({key: methodName, order, params: [colKeys, lookback]})
|
|
205
|
+
this.inputParams.push({key: methodName, params: [colKeys, lookback]})
|
|
229
206
|
|
|
230
207
|
return this;
|
|
231
208
|
}
|
|
@@ -309,9 +286,7 @@ export default class OHLCV_INDICATORS {
|
|
|
309
286
|
validateString(target, 'options.target', methodName)
|
|
310
287
|
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'options.lag', methodName)
|
|
311
288
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
this.inputParams.push({key: methodName, order, params: [methodName, size, {target, lag}]})
|
|
289
|
+
this.inputParams.push({key: methodName, params: [methodName, size, {target, lag}]})
|
|
315
290
|
|
|
316
291
|
return this
|
|
317
292
|
}
|
|
@@ -329,9 +304,7 @@ export default class OHLCV_INDICATORS {
|
|
|
329
304
|
validateString(target, 'options.target', methodName)
|
|
330
305
|
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'options.lag', methodName)
|
|
331
306
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
this.inputParams.push({key: methodName, order, params: [methodName, size, {target, lag}]})
|
|
307
|
+
this.inputParams.push({key: methodName, params: [methodName, size, {target, lag}]})
|
|
335
308
|
|
|
336
309
|
return this
|
|
337
310
|
}
|
|
@@ -379,9 +352,7 @@ export default class OHLCV_INDICATORS {
|
|
|
379
352
|
validateNumber(atrLength, {min: 2, max: this.len, allowDecimals: false}, 'options.atrLength', methodName)
|
|
380
353
|
validateNumber(liquidityLookback, {min: 1, max: this.len, allowDecimals: false}, 'options.liquidityLookback', methodName)
|
|
381
354
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
this.inputParams.push({key: methodName, order, params: [size, momentum, {target, lag}]})
|
|
355
|
+
this.inputParams.push({key: methodName, params: [size, momentum, {target, lag}]})
|
|
385
356
|
|
|
386
357
|
return this
|
|
387
358
|
}
|
|
@@ -443,9 +414,7 @@ export default class OHLCV_INDICATORS {
|
|
|
443
414
|
const instanceKey = `${fast}_${slow}_${signal}${target === 'close' ? '' : `_${target}`}`
|
|
444
415
|
const precomputed = {instanceKey}
|
|
445
416
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
this.inputParams.push({key: methodName, order, params: [fast, slow, signal, {target, lag, precomputed}]})
|
|
417
|
+
this.inputParams.push({key: methodName, params: [fast, slow, signal, {target, lag, precomputed}]})
|
|
449
418
|
|
|
450
419
|
return this
|
|
451
420
|
|
|
@@ -467,9 +436,7 @@ export default class OHLCV_INDICATORS {
|
|
|
467
436
|
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'options.lag', methodName)
|
|
468
437
|
validateBoolean(height, 'options.height', methodName)
|
|
469
438
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
this.inputParams.push({key: methodName, order, params: [size, stdDev, {target, height, range, lag}]});
|
|
439
|
+
this.inputParams.push({key: methodName, params: [size, stdDev, {target, height, range, lag}]});
|
|
473
440
|
|
|
474
441
|
return this;
|
|
475
442
|
}
|
|
@@ -505,9 +472,7 @@ export default class OHLCV_INDICATORS {
|
|
|
505
472
|
prefix = 'minmax_'
|
|
506
473
|
}
|
|
507
474
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
this.inputParams.push({key: methodName, order, params: [size, {target, lag, parser, prefix, minmax}]})
|
|
475
|
+
this.inputParams.push({key: methodName, params: [size, {target, lag, parser, prefix, minmax}]})
|
|
511
476
|
|
|
512
477
|
return this
|
|
513
478
|
}
|
|
@@ -589,328 +554,67 @@ export default class OHLCV_INDICATORS {
|
|
|
589
554
|
return this
|
|
590
555
|
}
|
|
591
556
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
let methodName = 'scaler'
|
|
557
|
+
priceFeatures(options = {}) {
|
|
558
|
+
let methodName = 'priceFeatures'
|
|
595
559
|
|
|
596
560
|
isAlreadyComputed(this)
|
|
597
561
|
|
|
598
|
-
validateNumber(size, {min: 1, max: this.len, allowDecimals: false}, 'size', methodName)
|
|
599
|
-
|
|
600
562
|
validateObject(options, 'options', methodName)
|
|
601
563
|
|
|
602
|
-
const {
|
|
564
|
+
const {lag = 0, colKeys = []} = options
|
|
603
565
|
|
|
566
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'options.lag', methodName)
|
|
604
567
|
validateArray(colKeys, 'options.colKeys', methodName)
|
|
605
|
-
validateBoolean(group, 'options.group', methodName)
|
|
606
|
-
validateArrayOfRanges(range, 'options.range', methodName)
|
|
607
|
-
validateArrayOptions(['minmax', 'zscore'], type, 'options.type', methodName)
|
|
608
|
-
|
|
609
|
-
const groupKey = `${type}_${size}`
|
|
610
|
-
const groupKeyLen = colKeys.length
|
|
611
|
-
const precomputed = {groupKey, groupKeyLen}
|
|
612
568
|
|
|
613
|
-
let secondaryLoop = false
|
|
614
569
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
if(order >= 10)
|
|
618
|
-
{
|
|
619
|
-
secondaryLoop = true
|
|
620
|
-
}
|
|
570
|
+
this.inputParams.push({key: methodName, params: [{lag, colKeys}]})
|
|
621
571
|
|
|
622
|
-
this.inputParams.push({key: methodName, order, params: [size, colKeys, {type, group, range, lag, precomputed, secondaryLoop}]})
|
|
623
572
|
return this
|
|
624
573
|
}
|
|
625
574
|
|
|
626
|
-
|
|
575
|
+
scaler(type = 'zscore', size, options = {})
|
|
627
576
|
{
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
const methodName = 'classifier'
|
|
631
|
-
validateNumber(trainingSize, {max: this.len, allowDecimals: false}, 'trainingSize', methodName)
|
|
632
|
-
validateObject(options, 'options', methodName)
|
|
633
|
-
|
|
634
|
-
const {
|
|
635
|
-
retrain = false,
|
|
636
|
-
trainingCols = [],
|
|
637
|
-
type = 'KNN',
|
|
638
|
-
lookback = 0,
|
|
639
|
-
findGroups = [],
|
|
640
|
-
predictions = 2,
|
|
641
|
-
horizon = 2,
|
|
642
|
-
modelArgs = undefined,
|
|
643
|
-
filterCallback = () => true
|
|
644
|
-
} = options
|
|
645
|
-
|
|
646
|
-
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}`);
|
|
647
|
-
|
|
648
|
-
const yCallback = options.yCallback ?? defaultYCallback
|
|
649
|
-
|
|
650
|
-
if(typeof yCallback !== 'function')
|
|
651
|
-
{
|
|
652
|
-
throw new Error(`"yCallback" must be a function in the following format:\n\n---\n\nconst yCallback = ${defaultYCallback.toString()}\n\n---\n\n`)
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
validateArrayOptions(Object.keys(validClassifiers), type, 'options.type', methodName)
|
|
656
|
-
|
|
657
|
-
if (!this.ML.classes.hasOwnProperty(type)) {
|
|
658
|
-
throw new Error(
|
|
659
|
-
`"${type}" isn’t available because its library wasn’t imported into OHLCV_INDICATORS.ML.`
|
|
660
|
-
)
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
validateBoolean(retrain, 'options.retrain', methodName)
|
|
664
|
-
validateArray(trainingCols, 'options.trainingCols', methodName)
|
|
665
|
-
validateNumber(horizon, {min: 1, max: this.len, allowDecimals: false}, 'options.horizon', methodName)
|
|
666
|
-
validateNumber(lookback, {max: 0, allowDecimals: false}, 'options.lookback', methodName)
|
|
667
|
-
validateNumber(predictions, {min: 1, allowDecimals: false}, 'options.predictions', methodName)
|
|
668
|
-
|
|
669
|
-
const orderArr = [...trainingCols]
|
|
670
|
-
|
|
671
|
-
if(validateArray(findGroups, 'options.findGroups', 'scaler') && findGroups.length > 0)
|
|
672
|
-
{
|
|
673
|
-
for (const { type = '', size, groupName = '' } of findGroups) {
|
|
674
|
-
|
|
675
|
-
if (!type || (!size && !groupName) || (size && groupName)) {
|
|
676
|
-
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.`);
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
orderArr.push(type.toString(), groupName.toString());
|
|
680
|
-
}
|
|
681
|
-
} else {
|
|
682
|
-
if(trainingCols.length === 0)
|
|
683
|
-
{
|
|
684
|
-
throw new Error(`"trainingCols" array is empty in ${methodName}`)
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
const {shortName, flatY} = validClassifiers[type]
|
|
689
|
-
|
|
690
|
-
const prefix = `cla_${shortName}_${trainingSize}_prediction`
|
|
691
|
-
|
|
692
|
-
if(this.isAlreadyComputed.has(prefix))
|
|
693
|
-
{
|
|
694
|
-
throw new Error(
|
|
695
|
-
`Each classifier must have a unique pair of “type” and trainingSize.\n` +
|
|
696
|
-
`This rule ensures that your output columns are labeled unambiguously.\n` +
|
|
697
|
-
`You provided a duplicate: type="${type}" with trainingSize=${trainingSize}.`
|
|
698
|
-
);
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
const precompute = {
|
|
702
|
-
lookbackAbs: Math.abs(lookback) + 1,
|
|
703
|
-
flatY,
|
|
704
|
-
prefix
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
this.isAlreadyComputed.add(prefix)
|
|
708
|
-
|
|
709
|
-
const order = getOrderFromArray(orderArr, methodName)
|
|
710
|
-
|
|
711
|
-
const modelConfig = validClassifiers[type]
|
|
712
|
-
const modelClass = this.ML.classes[type]
|
|
713
|
-
|
|
714
|
-
this.inputParams.push({key: methodName, order, params: [trainingSize, {yCallback, predictions, lookback, retrain, trainingCols, findGroups, horizon, type, modelArgs, precompute, filterCallback, modelConfig, modelClass}]})
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
return this
|
|
718
|
-
}
|
|
577
|
+
let methodName = 'scaler'
|
|
719
578
|
|
|
720
|
-
regressor(trainingSize = 200, options = {})
|
|
721
|
-
{
|
|
722
579
|
isAlreadyComputed(this)
|
|
723
580
|
|
|
724
|
-
|
|
581
|
+
validateNumber(size, {min: 1, max: this.len, allowDecimals: false}, 'size', methodName)
|
|
725
582
|
|
|
726
|
-
|
|
727
|
-
validateNumber(trainingSize, {max: this.len, allowDecimals: false}, 'trainingSize', methodName)
|
|
728
583
|
validateObject(options, 'options', methodName)
|
|
729
584
|
|
|
730
|
-
const {
|
|
731
|
-
retrain = false,
|
|
732
|
-
target = 'close',
|
|
733
|
-
predictions = 1,
|
|
734
|
-
trainingCols = [],
|
|
735
|
-
type = 'SimpleLinearRegression',
|
|
736
|
-
lookback = 0,
|
|
737
|
-
findGroups = [],
|
|
738
|
-
modelArgs = undefined,
|
|
739
|
-
filterCallback = () => true
|
|
740
|
-
} = options
|
|
741
|
-
|
|
742
|
-
validateArrayOptions(Object.keys(validRegressors), type, 'type', methodName)
|
|
743
|
-
|
|
744
|
-
if (!this.ML.classes.hasOwnProperty(type)) {
|
|
745
|
-
throw new Error(
|
|
746
|
-
`"${type}" isn’t available because its library wasn’t imported into OHLCV_INDICATORS.ML.`
|
|
747
|
-
)
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
//regressor
|
|
751
|
-
validateBoolean(retrain, 'options.retrain', methodName)
|
|
752
|
-
validateString(target, 'options.target', methodName)
|
|
753
|
-
validateNumber(predictions, {min: 1, allowDecimals: false}, 'predictions', methodName)
|
|
754
|
-
validateNumber(lookback, {max: 0, allowDecimals: false}, 'lookback', methodName)
|
|
755
|
-
validateArray(trainingCols, 'options.trainingCols', methodName)
|
|
585
|
+
const {range = [0, 1], lag = false, colKeys = [], weights = {}, euclideanWeights = false} = options
|
|
756
586
|
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
587
|
+
validateArray(colKeys, 'options.colKeys', methodName)
|
|
588
|
+
validateBoolean(lag, 'options.lag', methodName)
|
|
589
|
+
validateArrayOfRanges(range, 'options.range', methodName)
|
|
590
|
+
validateArrayOptions(['minmax', 'zscore'], type, 'options.type', methodName)
|
|
591
|
+
validateObject(weights, 'options.weights', methodName)
|
|
760
592
|
|
|
761
|
-
|
|
762
|
-
{
|
|
763
|
-
for (const { type = '', size, groupName = '' } of findGroups) {
|
|
593
|
+
const lookback = lag ? size - 1 : 0
|
|
764
594
|
|
|
765
|
-
if (!type || (!size && !groupName) || (size && groupName)) {
|
|
766
|
-
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.`);
|
|
767
|
-
}
|
|
768
595
|
|
|
769
|
-
|
|
770
|
-
}
|
|
596
|
+
for(const [key, arr] of Object.entries(weights)) {
|
|
597
|
+
validateArray(arr, `options.weights.${key}`)
|
|
771
598
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
{
|
|
775
|
-
throw new Error(`"trainingCols" array is empty in ${methodName} ${type}`)
|
|
599
|
+
if(!lag && arr.size > 0) {
|
|
600
|
+
throw new Error(`If "options.lag" is set to false "options.weights.${key}" can only contain 1 item (float).`)
|
|
776
601
|
}
|
|
777
|
-
}
|
|
778
602
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
{
|
|
782
|
-
throw new Error(`If regressor type is ${type} then leave "options.trainingCols" array empty.`)
|
|
603
|
+
if(arr.length > size) {
|
|
604
|
+
throw new Error(`The length of the property "options.weights.${key}" can not be longer than "size" in ${methodName}.`)
|
|
783
605
|
}
|
|
784
|
-
if(lookback > 0)
|
|
785
|
-
{
|
|
786
|
-
throw new Error(`If regressor type is ${type} then "options.lookback" must be 0.`)
|
|
787
|
-
}
|
|
788
|
-
if(findGroups.length > 0)
|
|
789
|
-
{
|
|
790
|
-
throw new Error(`If regressor type is ${type} then "options.findGroups" must be null.`)
|
|
791
|
-
}
|
|
792
|
-
else {
|
|
793
|
-
trainingCols.push(target)
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
if(this.isAlreadyComputed.has(prefix))
|
|
798
|
-
{
|
|
799
|
-
throw new Error(
|
|
800
|
-
`Each regressor must have a unique "type", "trainingSize" and "target".\n` +
|
|
801
|
-
`This rule ensures that your output columns are labeled unambiguously.\n` +
|
|
802
|
-
`You provided a duplicate: type="${type}", trainingSize=${trainingSize} target=${target}.`
|
|
803
|
-
);
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
const precompute = {
|
|
807
|
-
lookbackAbs: Math.abs(lookback) + 1,
|
|
808
|
-
flatX,
|
|
809
|
-
flatY,
|
|
810
|
-
prefix
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
this.isAlreadyComputed.add(prefix)
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
const order = getOrderFromArray(orderArr, methodName)
|
|
817
606
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
this.inputParams.push({key: methodName, order, params: [trainingSize, {target, predictions, retrain, lookback, trainingCols, findGroups, type, modelArgs, precompute, filterCallback, modelConfig, modelClass}]})
|
|
822
|
-
|
|
823
|
-
return this
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
pca(suffix, trainingSize, options = {})
|
|
827
|
-
{
|
|
828
|
-
const methodName = 'pca'
|
|
829
|
-
validateNumber(trainingSize, {min: 1, max: this.len}, 'trainingSize', methodName)
|
|
830
|
-
validateString(suffix, 'suffix', methodName)
|
|
831
|
-
validateObject(options, 'options', methodName)
|
|
832
|
-
|
|
833
|
-
const {
|
|
834
|
-
findGroups = [],
|
|
835
|
-
trainingCols = [],
|
|
836
|
-
lookback = 0,
|
|
837
|
-
filterCallback = () => true
|
|
838
|
-
} = options
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
validateArray(trainingCols, 'options.trainigCols', methodName)
|
|
842
|
-
validateNumber(lookback, {min: 0, max: this.len, allowDecimals: false}, 'options.lookback', methodName)
|
|
843
|
-
if(typeof options.modelArgs !== 'undefined') {
|
|
844
|
-
validateObject(options.modelArgs, 'options.modelArgs', methodName)
|
|
845
|
-
} else {
|
|
846
|
-
options.modelArgs = {}
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
//modelArgs
|
|
850
|
-
|
|
851
|
-
const modelArgs = {
|
|
852
|
-
showSource: false,
|
|
853
|
-
storeModel: false,
|
|
854
|
-
isCovarianceMatrix: false,
|
|
855
|
-
method: 'NIPALS', //SVD, covarianceMatrix or NIPALS
|
|
856
|
-
center: true,
|
|
857
|
-
scale: false,
|
|
858
|
-
nCompNIPALS: 5,
|
|
859
|
-
ignoreZeroVariance: false,
|
|
860
|
-
...options.modelArgs
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
validateBoolean(modelArgs.showSource, 'options.modelArgs.showSource', methodName)
|
|
864
|
-
validateBoolean(modelArgs.storeModel, 'options.modelArgs.storeModel', methodName)
|
|
865
|
-
validateBoolean(modelArgs.isCovarianceMatrix, 'options.modelArgs.isCovarianceMatrix', methodName)
|
|
866
|
-
validateBoolean(modelArgs.center, 'options.modelArgs.center', methodName)
|
|
867
|
-
validateBoolean(modelArgs.scale, 'options.modelArgs.scale', methodName)
|
|
868
|
-
validateBoolean(modelArgs.ignoreZeroVariance, 'options.modelArgs.ignoreZeroVariance', methodName)
|
|
869
|
-
validateArrayOptions(['SVD', 'NIPALS', 'covarianceMatrix'], modelArgs.method, 'options.modelArgs.method', methodName)
|
|
870
|
-
validateNumber(modelArgs.nCompNIPALS, {min: 1}, 'options.modelArgs.nCompNIPALS', methodName)
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
const orderArr = []
|
|
876
|
-
|
|
877
|
-
if(validateArray(findGroups, 'options.findGroups', 'pca') && findGroups.length > 0)
|
|
878
|
-
{
|
|
879
|
-
|
|
880
|
-
for (const { type = '', size, groupName = '' } of findGroups) {
|
|
881
|
-
|
|
882
|
-
if (!type || (!size && !groupName) || (size && groupName)) {
|
|
883
|
-
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.`);
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
orderArr.push(type.toString(), groupName.toString())
|
|
607
|
+
for(let x = 0; x < arr.length; x++) {
|
|
608
|
+
const val = arr[x]
|
|
609
|
+
validateNumber(val, {min: 0.01, max: 10, allowDecimals: true}, `options.weights.${key}[${x}]`, methodName)
|
|
887
610
|
}
|
|
888
|
-
}
|
|
889
611
|
|
|
890
|
-
const order = getOrderFromArray(orderArr, methodName)
|
|
891
|
-
const prefix = `pca_${suffix}_${trainingSize}`
|
|
892
|
-
|
|
893
|
-
if(this.isAlreadyComputed.has(prefix)) {
|
|
894
|
-
throw new Error(
|
|
895
|
-
`Each regressor must have a unique "suffix" param.\n` +
|
|
896
|
-
`This rule ensures that your output columns are labeled unambiguously.\n` +
|
|
897
|
-
`You provided a duplicate: type="${suffix}".`
|
|
898
|
-
)
|
|
899
612
|
}
|
|
900
613
|
|
|
901
|
-
|
|
902
|
-
this.isAlreadyComputed.add(prefix)
|
|
903
|
-
|
|
904
|
-
this.inputParams.push({key: methodName, order, params: [{prefix, trainingSize, findGroups, trainingCols, lookbackAbs, modelArgs, filterCallback}]})
|
|
905
|
-
|
|
614
|
+
this.inputParams.push({key: methodName, params: [size, colKeys, {type, range, lookback, weights, euclideanWeights}]})
|
|
906
615
|
return this
|
|
907
616
|
}
|
|
908
617
|
|
|
909
|
-
exportTrainedModels()
|
|
910
|
-
{
|
|
911
|
-
return exportTrainedModels(this)
|
|
912
|
-
}
|
|
913
|
-
|
|
914
618
|
mapCols(newCols = ['change'], callback = null, options = {}) {
|
|
915
619
|
|
|
916
620
|
isAlreadyComputed(this)
|
|
@@ -934,9 +638,7 @@ export default class OHLCV_INDICATORS {
|
|
|
934
638
|
throw new Error(`Invalid param: If "mapCols.options.isPriceBased" is true, the "constructor.precision" param must be also true.`)
|
|
935
639
|
}
|
|
936
640
|
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
this.inputParams.push({key: methodName, order, params: [newCols, callback, {lag, isPriceBased}]})
|
|
641
|
+
this.inputParams.push({key: methodName, params: [newCols, callback, {lag, isPriceBased}]})
|
|
940
642
|
|
|
941
643
|
return this
|
|
942
644
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ohlcv-indicators",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.59",
|
|
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",
|
|
@@ -12,25 +12,14 @@
|
|
|
12
12
|
"author": "https://jaimelias.com/",
|
|
13
13
|
"license": "ISC",
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"trading-signals": "^5.0.4"
|
|
16
|
-
"xy-scale": "^1.4.1"
|
|
15
|
+
"trading-signals": "^5.0.4"
|
|
17
16
|
},
|
|
18
17
|
"devDependencies": {
|
|
19
18
|
"@babel/core": "^7.25.2",
|
|
20
19
|
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
|
21
20
|
"@babel/preset-env": "^7.25.3",
|
|
22
|
-
"arima": "^0.2.5",
|
|
23
21
|
"babel-loader": "^9.1.3",
|
|
24
22
|
"dotenv": "^16.4.5",
|
|
25
|
-
"merge-multi-timeframe": "^1.1.6",
|
|
26
|
-
"ml-cart": "^2.1.1",
|
|
27
|
-
"ml-fnn": "^5.0.0",
|
|
28
|
-
"ml-knn": "^3.0.0",
|
|
29
|
-
"ml-naivebayes": "^4.0.0",
|
|
30
|
-
"ml-pca": "^4.1.1",
|
|
31
|
-
"ml-random-forest": "^2.1.0",
|
|
32
|
-
"ml-regression-multivariate-linear": "^2.0.4",
|
|
33
|
-
"ml-regression-simple-linear": "^3.0.1",
|
|
34
23
|
"terser-webpack-plugin": "^5.3.14",
|
|
35
24
|
"webpack": "^5.93.0",
|
|
36
25
|
"webpack-cli": "^5.1.4"
|
|
@@ -21,13 +21,12 @@ import { mapCols } from "../studies/mapCols.js";
|
|
|
21
21
|
import { buildArray } from "../utilities/assignTypes.js";
|
|
22
22
|
import { inputNumberFormatter } from "../utilities/numberUtilities.js";
|
|
23
23
|
import { dateFormaters } from "../utilities/dateUtilities.js";
|
|
24
|
+
import { priceFeatures } from "../studies/priceFeatures.js";
|
|
24
25
|
|
|
25
|
-
import { pca } from "../machine-learning/pca.js";
|
|
26
|
-
import { regressor } from "../machine-learning/regressor.js";
|
|
27
|
-
import { classifier } from "../machine-learning/classifier.js";
|
|
28
26
|
|
|
29
27
|
// Map indicator keys to their respective functions
|
|
30
28
|
const mainFunctions = {
|
|
29
|
+
priceFeatures,
|
|
31
30
|
dateTime,
|
|
32
31
|
heikenAshi,
|
|
33
32
|
rsi,
|
|
@@ -49,17 +48,18 @@ const mainFunctions = {
|
|
|
49
48
|
};
|
|
50
49
|
|
|
51
50
|
export const mainLoop = (input, main) => {
|
|
52
|
-
const {
|
|
53
|
-
|
|
51
|
+
const {
|
|
52
|
+
len,
|
|
54
53
|
arrayTypes,
|
|
55
54
|
verticalOhlcv,
|
|
56
55
|
verticalOhlcvKeyNames,
|
|
57
56
|
inputTypes,
|
|
58
57
|
chunkProcess,
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
precisionMultiplier,
|
|
59
|
+
inputParams
|
|
61
60
|
} = main;
|
|
62
61
|
|
|
62
|
+
|
|
63
63
|
validateInputParams(inputParams, len)
|
|
64
64
|
|
|
65
65
|
for(const key of Object.keys(inputTypes))
|
|
@@ -96,15 +96,10 @@ export const mainLoop = (input, main) => {
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
// Process these indicators separately (ensuring their execution order)
|
|
100
|
-
for (const { key, params
|
|
99
|
+
// Process these indicators separately (ensuring their execution in the order of initialization)
|
|
100
|
+
for (const { key, params} of inputParams)
|
|
101
101
|
{
|
|
102
|
-
|
|
103
|
-
{
|
|
104
|
-
if(typeof order !== 'number') throw new Error(`order property of ${key} not found: ${params}`)
|
|
105
|
-
if(orderIdx !== order) continue
|
|
106
|
-
mainFunctions[key](main, index, ...params)
|
|
107
|
-
}
|
|
102
|
+
mainFunctions[key](main, index, ...params)
|
|
108
103
|
}
|
|
109
104
|
|
|
110
105
|
input[index] = null //flusing data
|
|
@@ -118,34 +113,7 @@ export const mainLoop = (input, main) => {
|
|
|
118
113
|
}
|
|
119
114
|
}
|
|
120
115
|
|
|
121
|
-
const isSecondaryLoopNeeded = inputParams.some(({ key }) => key === 'regressor' || key === 'classifier' || key === 'pca')
|
|
122
|
-
|
|
123
|
-
if(isSecondaryLoopNeeded || processSecondaryLoop)
|
|
124
|
-
{
|
|
125
|
-
secondaryLoop(main)
|
|
126
|
-
}
|
|
127
116
|
|
|
128
117
|
verticalOhlcvKeyNames.push(...Object.keys(verticalOhlcv))
|
|
129
118
|
|
|
130
119
|
}
|
|
131
|
-
|
|
132
|
-
export const secondaryLoop = (main) => {
|
|
133
|
-
const {inputParams, len, invalidValueIndex} = main
|
|
134
|
-
|
|
135
|
-
const startIndex = invalidValueIndex + 1
|
|
136
|
-
|
|
137
|
-
for(let index = startIndex; index < len; index++)
|
|
138
|
-
{
|
|
139
|
-
for (const { key, params, order } of inputParams)
|
|
140
|
-
{
|
|
141
|
-
for(let orderIdx = 10; orderIdx <= 20; orderIdx++) {
|
|
142
|
-
if(typeof order !== 'number') throw new Error(`order property of ${key} not found: ${params}`)
|
|
143
|
-
if(orderIdx !== order) continue
|
|
144
|
-
if(key === 'pca') pca(main, index, ...params)
|
|
145
|
-
if(key === 'classifier') classifier(main, index, ...params)
|
|
146
|
-
if(key === 'regressor') regressor(main, index, ...params)
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
}
|