ohlcv-indicators 3.5.55 → 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.
@@ -1,219 +0,0 @@
1
- export const defaultYCallback = (index, verticalOhlcv, horizon) => {
2
-
3
- //this function will be executed inside a secondary loop after most of the indicates are calculated
4
- //use the "verticalOhlcv" object to access desired indicators
5
-
6
- if(horizon !== 2) throw new Error(`The default "yCallback" function in this classifier requires that "options.horizon" and "options.predictions" are set to "2".`)
7
-
8
- const currClose = verticalOhlcv.close[index]
9
- const nextCloseArr = Array.from({length: horizon}, (_, i) => verticalOhlcv.close[i + 1 + index])
10
-
11
- if(nextCloseArr.some(v => typeof v === 'undefined')) return null
12
-
13
- const [nextClose, nextNextClose] = nextCloseArr
14
-
15
-
16
- //the total length of item is this output must be equal to "options.yColumns" property
17
- return [Number(nextClose > currClose), Number(nextNextClose > nextClose)]
18
- }
19
-
20
- export const exportTrainedModels = main => {
21
-
22
- const {ML, inputParams} = main
23
- const allModels = ML.models
24
- const output = {}
25
-
26
- for (const { key, params } of inputParams) {
27
-
28
- if(key !== 'classifier' && key !== 'regressor') continue
29
-
30
- const [_, {type, predictions, precompute}] = params
31
- const {flatY, prefix} = precompute
32
- const expectedLoops = (flatY) ? predictions : 1
33
-
34
- let exportModel
35
-
36
- if(key === 'regressor')
37
- {
38
- exportModel = validRegressors[type].exportModel
39
- }
40
- else if (key === 'classifier')
41
- {
42
- exportModel = validClassifiers[type].exportModel
43
- }
44
-
45
- for(let loopIdx = 0; loopIdx < expectedLoops; loopIdx++)
46
- {
47
- const modelKey = `${prefix}_${(loopIdx+1)}`
48
- output[modelKey] = exportModel(allModels[modelKey])
49
- }
50
-
51
- }
52
-
53
- return output
54
- }
55
-
56
-
57
- export const validClassifiers = {
58
- 'KNN': {
59
- shortName: 'knn',
60
- flatY: true,
61
- exportModel: m => m.toJSON(),
62
- train: ({modelClass, xRows, yRows, modelArgs}) => new modelClass(xRows, yRows, modelArgs),
63
- unsupervised: false
64
- },
65
- 'FeedForwardNeuralNetworks': {
66
- shortName: 'feed_forward',
67
- flatY: false,
68
- exportModel: m => m.toJSON(),
69
- train: ({modelClass, xRows, yRows, modelArgs, uniqueLabels}) => {
70
-
71
- if(!modelArgs) modelArgs = {}
72
-
73
- modelArgs = {
74
- ...modelArgs,
75
- activation: (uniqueLabels === 2) ? 'logistic' : 'identity'
76
- }
77
-
78
- const m = new modelClass(modelArgs)
79
- m.train(xRows, yRows)
80
- return m
81
- },
82
- unsupervised: false
83
- },
84
- 'GaussianNB': {
85
- shortName: 'naive_bayes',
86
- flatY: true,
87
- exportModel: m => m.toJSON(),
88
- train: ({modelClass, xRows, yRows, modelArgs}) => {
89
- const m = new modelClass(modelArgs)
90
- m.train(xRows, yRows)
91
- return m
92
- },
93
- unsupervised: false
94
- },
95
- 'MultinomialNB': {
96
- shortName: 'naive_bayes',
97
- flatY: true,
98
- exportModel: m => m.toJSON(),
99
- train: ({modelClass, xRows, yRows, modelArgs}) => {
100
- const m = new modelClass(modelArgs)
101
- m.train(xRows, yRows)
102
- return m
103
- },
104
- unsupervised: false
105
- },
106
- 'DecisionTreeClassifier': {
107
- shortName: 'decision_tree',
108
- flatY: true,
109
- exportModel: m => m.toJSON(),
110
- train: ({modelClass, xRows, yRows, modelArgs}) => {
111
- const m = new modelClass(modelArgs)
112
- m.train(xRows, yRows)
113
- return m
114
- },
115
- unsupervised: false
116
- },
117
- 'RandomForestClassifier': {
118
- shortName: 'random_forest',
119
- flatY: true,
120
- exportModel: m => m.toJSON(),
121
- train: ({modelClass, xRows, yRows, modelArgs}) => {
122
- const m = new modelClass(modelArgs)
123
- m.train(xRows, yRows)
124
- return m
125
- }
126
- },
127
- unsupervised: false
128
- }
129
-
130
- export const validRegressors = {
131
- 'ARIMA': {
132
- shortName: 'arima',
133
- flatX: true,
134
- flatY: false,
135
- exportModel: m => m,
136
- train: ({modelClass, xRows, modelArgs}) => {
137
-
138
- if(!modelArgs) modelArgs = {}
139
-
140
- modelArgs = { ...modelArgs, p: 2, d: 1, q: 2, P: 0, D: 0, Q: 0, S: 0, verbose: false }
141
-
142
- const m = new modelClass(modelArgs)
143
- m.train(xRows)
144
- return m
145
- },
146
- unsupervised: true
147
- },
148
- 'SimpleLinearRegression': {
149
- shortName: 'linear',
150
- flatX: true,
151
- flatY: true,
152
-
153
- exportModel: m => m.toJSON(),
154
- train: ({modelClass, xRows, yRows, modelArgs}) => new modelClass(xRows, yRows, modelArgs),
155
- unsupervised: false
156
- },
157
- 'PolynomialRegression': {
158
- shortName: 'polynomial',
159
- flatX: true,
160
- flatY: true,
161
-
162
- exportModel: m => m.toJSON(),
163
- train: ({modelClass, xRows, yRows, modelArgs}) => new modelClass(xRows, yRows, modelArgs),
164
- unsupervised: false
165
- },
166
- 'MultivariateLinearRegression': {
167
- shortName: 'multivariable',
168
- flatX: false,
169
- flatY: false,
170
-
171
- exportModel: m => m.toJSON(),
172
- train: ({modelClass, xRows, yRows, modelArgs}) => new modelClass(xRows, yRows, modelArgs),
173
- unsupervised: false
174
- },
175
- 'DecisionTreeRegression': {
176
- shortName: 'decision_tree',
177
- flatX: false,
178
- flatY: true,
179
- exportModel: m => m.toJSON(),
180
- train: ({modelClass, xRows, yRows, modelArgs}) => {
181
- const m = new modelClass(modelArgs)
182
- m.train(xRows, yRows)
183
- return m
184
- },
185
- unsupervised: false
186
- },
187
- 'RandomForestRegression': {
188
- shortName: 'random_forest',
189
- flatX: false,
190
- flatY: true,
191
- exportModel: m => m.toJSON(),
192
- train: ({modelClass, xRows, yRows, modelArgs}) => {
193
- const m = new modelClass(modelArgs)
194
- m.train(xRows, yRows)
195
- return m
196
- },
197
- unsupervised: false
198
- },
199
- 'FeedForwardNeuralNetworks': {
200
- shortName: 'feed_forward',
201
- flatX: false,
202
- flatY: false,
203
- exportModel: m => m.toJSON(),
204
- train: ({modelClass, xRows, yRows, modelArgs}) => {
205
-
206
- if(!modelArgs) modelArgs = {}
207
-
208
- modelArgs = {
209
- ...modelArgs,
210
- activation: 'identity'
211
- }
212
-
213
- const m = new modelClass(modelArgs)
214
- m.train(xRows, yRows)
215
- return m
216
- },
217
- unsupervised: false
218
- }
219
- }
@@ -1,95 +0,0 @@
1
- import { getFeaturedKeys, computeFlatFeaturesLen, findGroupsFunc } from "./ml-utilities.js";
2
- import { buildTrainX } from "./trainX.js";
3
- import { areKeyValuesValid } from "../core-functions/pushToMain.js";
4
-
5
- const algo = 'pca'
6
-
7
- export const pca = (main, index, {prefix, trainingSize, findGroups, trainingCols, lookbackAbs, modelArgs, filterCallback}) => {
8
-
9
- const {verticalOhlcv, scaledGroups, len, instances, ML, invalidValueIndex} = main
10
-
11
- const startIndex = (invalidValueIndex + 2)
12
- const allModels = ML.models
13
-
14
- if(index === startIndex)
15
- {
16
- if(!instances.hasOwnProperty(algo)) instances[algo] = {}
17
-
18
- instances[algo][prefix] = {
19
- flatFeaturesColLen: 0,
20
- featureCols: [],
21
- X: []
22
- }
23
-
24
- verticalOhlcv[prefix] = new Array(len).fill(null)
25
- }
26
- else if(index < startIndex || index < lookbackAbs)
27
- {
28
- return
29
- }
30
- else if(filterCallback(verticalOhlcv, index) === false)
31
- {
32
- return
33
- }
34
- else if(index + 1 === len)
35
- {
36
-
37
- if(instances[algo][prefix].featureCols.length === 0)
38
- {
39
- const inputFeatures = [...trainingCols, ...(findGroupsFunc(findGroups, scaledGroups))]
40
-
41
- throw new Error(`Some of the provided features in ${prefix} features where not found in "verticalOhlcv": ${JSON.stringify(inputFeatures)}`)
42
- }
43
-
44
- //last execution
45
- for(const featureKey of instances[algo][prefix].featureCols)
46
- {
47
- if(!verticalOhlcv.hasOwnProperty(featureKey)) throw new Error(`Feature "${featureKey}" not found in verticalOhlcv for pca.`)
48
- }
49
-
50
- if(instances[algo][prefix].X.length < trainingSize)
51
- {
52
- const requiredDatapoints = instances[algo][prefix].X.length - trainingSize
53
- throw new Error(`The current "trainingSize" at "${prefix}" requires at least ${requiredDatapoints} more datapoints. Try adding more input ohlcv rows or reducing the "trainingSize" by ${requiredDatapoints}.`)
54
- }
55
- }
56
-
57
- const dataSetInstance = instances[algo][prefix]
58
-
59
- if(dataSetInstance.flatFeaturesColLen === 0)
60
- {
61
- dataSetInstance.featureCols = getFeaturedKeys({trainingCols, findGroups, verticalOhlcv, scaledGroups})
62
-
63
- if(areKeyValuesValid(main, index, dataSetInstance.featureCols))
64
- {
65
- dataSetInstance.flatFeaturesColLen = computeFlatFeaturesLen(dataSetInstance.featureCols, verticalOhlcv, index)
66
- } else {
67
- return
68
- }
69
- }
70
-
71
- if(dataSetInstance.flatFeaturesColLen === 0) return
72
-
73
- const {X: xRows, featureCols, flatFeaturesColLen} = dataSetInstance
74
-
75
- const trainX = buildTrainX({featureCols, flatFeaturesColLen, type: algo, index, lookbackAbs, main})
76
-
77
- if (trainX == null) return
78
-
79
- if(allModels.hasOwnProperty(prefix))
80
- {
81
-
82
- const futureRow = allModels[prefix].predict([trainX]).to2DArray()
83
-
84
- verticalOhlcv[prefix][index] = futureRow[0]
85
- }
86
-
87
- xRows.push(trainX)
88
-
89
- if (xRows.length > trainingSize) xRows.shift()
90
-
91
- if(xRows.length === trainingSize)
92
- {
93
- allModels[prefix] = new ML.classes.PCA(xRows, modelArgs)
94
- }
95
- }
@@ -1,227 +0,0 @@
1
- import { getFeaturedKeys, computeFlatFeaturesLen, logMlTraining, findGroupsFunc } from './ml-utilities.js'
2
- import { buildTrainX } from './trainX.js'
3
- import { areKeyValuesValid } from '../core-functions/pushToMain.js'
4
-
5
- const algo = 'regressor'
6
-
7
- export const regressor = (main, index, trainingSize, {target, predictions, retrain, trainingCols, findGroups, type, modelArgs, precompute, filterCallback, modelConfig, modelClass}) => {
8
-
9
- const {lookbackAbs, prefix, flatX, flatY} = precompute
10
- const {verticalOhlcv, len, instances, scaledGroups, invalidValueIndex, ML} = main
11
- const allModels = ML.models
12
- const startIndex = (invalidValueIndex + 1)
13
-
14
- if(index === startIndex)
15
- {
16
- if (!instances.hasOwnProperty(algo)) instances.regressor = {}
17
- if(!instances.regressor.hasOwnProperty(prefix)) instances.regressor[prefix] = {}
18
-
19
- const expectedLoops = (flatY) ? predictions : 1 //
20
-
21
- instances.regressor[prefix] = {
22
- expectedLoops,
23
- trainingSize,
24
- isTrained: new Array(expectedLoops).fill(false),
25
- shouldRetrain: retrain,
26
- featureCols: [],
27
- flatFeaturesColLen: 0,
28
- X: [],
29
- Y: Array.from({ length: expectedLoops }, () => []),
30
- }
31
-
32
- for(let x = 0; x < predictions; x++)
33
- {
34
- const predictionKey = `${prefix}_${(x+1)}`
35
- verticalOhlcv[predictionKey] = new Float64Array(len).fill(NaN)
36
-
37
- if(!allModels.hasOwnProperty(predictionKey))
38
- {
39
- allModels[predictionKey] = []
40
- }
41
- }
42
- }
43
- else if(index < startIndex || index < lookbackAbs)
44
- {
45
- return
46
- }
47
- else if(filterCallback(verticalOhlcv, index) === false)
48
- {
49
- return
50
- }
51
- else if(index + 1 === len)
52
- {
53
- if(instances.regressor[prefix].featureCols.length === 0)
54
- {
55
- const inputFeatures = [...trainingCols, ...(findGroupsFunc(findGroups, scaledGroups))]
56
-
57
- throw new Error(`Some of the provided features in ${prefix} features where not found in "verticalOhlcv": ${JSON.stringify(inputFeatures)}`)
58
- }
59
-
60
- //last execution
61
- for(const featureKey of instances.regressor[prefix].featureCols)
62
- {
63
- if(!verticalOhlcv.hasOwnProperty(featureKey)) throw new Error(`Feature "${featureKey}" not found in verticalOhlcv for ${prefix}.`)
64
- }
65
-
66
- if(instances.regressor[prefix].X.length < trainingSize)
67
- {
68
- const requiredDatapoints = instances.regressor[prefix].X.length - trainingSize
69
- throw new Error(`The current "trainingSize" at "${prefix}" requires at least ${requiredDatapoints} more datapoints. Try adding more input ohlcv rows or reducing the "trainingSize" by ${requiredDatapoints}.`)
70
- }
71
- }
72
-
73
- const dataSetInstance = instances.regressor[prefix]
74
-
75
- if(dataSetInstance.flatFeaturesColLen === 0)
76
- {
77
- dataSetInstance.featureCols = getFeaturedKeys({trainingCols, findGroups, verticalOhlcv, scaledGroups})
78
-
79
- if(areKeyValuesValid(main, index, dataSetInstance.featureCols))
80
- {
81
- dataSetInstance.flatFeaturesColLen = computeFlatFeaturesLen(dataSetInstance.featureCols, verticalOhlcv, index)
82
-
83
- logMlTraining({
84
- featureCols: dataSetInstance.featureCols,
85
- flatFeaturesColLen: dataSetInstance.flatFeaturesColLen,
86
- type,
87
- trainingSize
88
- })
89
-
90
- } else {
91
- return
92
- }
93
- }
94
-
95
- if(dataSetInstance.flatFeaturesColLen === 0) return
96
-
97
- if(!verticalOhlcv.hasOwnProperty(target))
98
- {
99
- throw new Error(`Target property "${target}" not found in verticalOhlcv for regressor.`)
100
- }
101
-
102
- const {
103
- expectedLoops,
104
- X: xRows,
105
- Y,
106
- flatFeaturesColLen,
107
- featureCols,
108
- shouldRetrain
109
- } = dataSetInstance
110
-
111
- if(!featureCols.includes(target))
112
- {
113
- throw new Error(`Target property "${target}" not found in ${type} features: ${JSON.stringify(featureCols)}`)
114
- }
115
-
116
- const trainX = (flatX) ? verticalOhlcv[target][index] : buildTrainX({featureCols, flatFeaturesColLen, type, index, lookbackAbs, main})
117
-
118
- if (flatX) {
119
- if (!Number.isFinite(trainX)) return; // or `trainX == null`
120
- } else {
121
- if (trainX == null) return;
122
- }
123
-
124
- let trainY = []
125
-
126
- for (let i = 0; i < predictions; i++) {
127
- const v = verticalOhlcv[target][index + i + 1]
128
-
129
- if (!Number.isFinite(v)) {
130
- break
131
- }
132
-
133
- trainY.push(v)
134
- }
135
-
136
- const {train, unsupervised} = modelConfig
137
-
138
- //if univariable Y (flatY) a model is created for each prediction
139
- for(let loopIdx = 0; loopIdx < expectedLoops; loopIdx++)
140
- {
141
- const predictionKey = `${prefix}_${(loopIdx+1)}`
142
- const yRows = Y[loopIdx]
143
- const currTrainY = (flatY) ? (typeof trainY[loopIdx] === 'undefined') ? null : trainY[loopIdx] : trainY
144
- const isTrained = dataSetInstance.isTrained[loopIdx]
145
-
146
- const shouldPredict = allModels.hasOwnProperty(predictionKey) && isTrained && allModels[predictionKey].length === expectedLoops
147
-
148
- //predicts using previously saved models even if current currTrainY is not available
149
- if(shouldPredict)
150
- {
151
- const currModel = allModels[predictionKey][0]
152
- const futureRow = (unsupervised) ? currModel.predict(predictions)[0] : currModel.predict([trainX])[0]
153
-
154
- if(flatY)
155
- {
156
- if(Number.isNaN(futureRow)) throw new Error(`Prediction of ${type} at index ${index} was expecting a number.`)
157
-
158
- main.pushToMain({index, key: predictionKey, value: futureRow})
159
- }
160
- else {
161
-
162
- if (!Array.isArray(futureRow) || futureRow.length !== predictions) {
163
- throw new Error(
164
- `Prediction output of "${type}" at index ${index} was expecting an array of values.`
165
- )
166
- }
167
-
168
- for(let preIdx = 0; preIdx < predictions; preIdx++) {
169
- main.pushToMain({index, key: `${prefix}_${(preIdx+1)}`, value: futureRow[preIdx]})
170
- }
171
- }
172
- }
173
-
174
- if((index + predictions + 1) > len) continue
175
- if(trainY.length < predictions) continue
176
- if(currTrainY === null) continue
177
-
178
- if(flatY)
179
- {
180
- if (typeof currTrainY !== 'number' || Number.isNaN(currTrainY))
181
- {
182
- throw new Error(`currTrainY must return number, got ${typeof currTrainY} at index ${index}`)
183
- }
184
- } else {
185
- if (!Array.isArray(currTrainY)) {
186
- throw new Error(`currTrainY must return an array, got ${typeof currTrainY} at index ${index}`)
187
- }
188
-
189
- if ((currTrainY.length !== predictions)) {
190
-
191
- throw new Error(`currTrainY length (${currTrainY.length}) ≠ "options.predictions" (${predictions}) for regressor "${type}"`)
192
- }
193
- }
194
-
195
- // enqueue
196
- yRows.push(currTrainY)
197
- if (yRows.length > trainingSize) yRows.shift()
198
-
199
- if(loopIdx === 0)
200
- {
201
- xRows.push(trainX)
202
- if (xRows.length > trainingSize) xRows.shift()
203
- }
204
-
205
- const shouldTrainModel = shouldRetrain || !isTrained
206
-
207
- if (shouldTrainModel && xRows.length === trainingSize && yRows.length === trainingSize) {
208
-
209
- const trainedModel = train({modelClass, xRows, yRows, modelArgs, uniqueLabels: 0})
210
-
211
- if(unsupervised)
212
- {
213
- allModels[predictionKey] = [trainedModel]
214
- } else {
215
- allModels[predictionKey].push(trainedModel)
216
-
217
- if(allModels[predictionKey].length > predictions)
218
- {
219
- allModels[predictionKey].shift()
220
- }
221
- }
222
-
223
- dataSetInstance.isTrained[loopIdx] = true
224
- }
225
-
226
- }
227
- }
@@ -1,73 +0,0 @@
1
- /**
2
- * Builds a flattened lookback feature vector or returns null if any value is non‐finite.
3
- *
4
- * @param {string[]} featureCols – list of feature column keys
5
- * @param {Object} instances – your instances object (with crossPairs)
6
- * @param {number} flatFeaturesColLen – total number of flattened feature slots
7
- * @param {string} type – a string used only for error reporting
8
- * @param {number} index – current time index
9
- * @param {number} lookbackAbs – number of lags to include
10
- * @param {Object} verticalOhlcv – an object mapping each key to its time series array
11
- * @returns {number[]\|null} – the feature vector or null if exiting early
12
- */
13
- export const buildTrainX = ({
14
- featureCols,
15
- flatFeaturesColLen,
16
- type,
17
- index,
18
- lookbackAbs,
19
- main
20
- }) => {
21
-
22
- const {verticalOhlcv} = main
23
-
24
- let shouldExit = false
25
-
26
- // --- BUILD A FLATTENED LIST OF “COLUMN SLOTS” ---
27
- const slots = [];
28
- for (const key of featureCols) {
29
- if (key.startsWith('one_hot_') || key.startsWith('pca_')) {
30
-
31
- const colSize = verticalOhlcv[key][index].length
32
-
33
- for (let bit = 0; bit < colSize; bit++) {
34
- slots.push({ key, bit });
35
- }
36
- } else {
37
- slots.push({ key });
38
- }
39
- }
40
-
41
- if (slots.length !== flatFeaturesColLen) {
42
- throw new Error(
43
- `slots (${slots.length}) ≠ flatFeaturesColLen (${flatFeaturesColLen}) in ${type} index ${index}`
44
- );
45
- }
46
-
47
- // --- ALLOCATE AND FILL trainX ---
48
- const trainX = new Array(flatFeaturesColLen * lookbackAbs).fill(NaN);
49
-
50
- for (let lag = 0; lag < lookbackAbs; lag++) {
51
- const tIdx = index - lag;
52
-
53
- for (let s = 0; s < slots.length; s++) {
54
- const { key, bit } = slots[s];
55
- const cell = verticalOhlcv[key][tIdx];
56
- const value = (bit != null) ? cell[bit] : cell;
57
-
58
- if (!Number.isFinite(value)) {
59
- shouldExit = true;
60
- break;
61
- }
62
- trainX[lag * flatFeaturesColLen + s] = value;
63
- }
64
-
65
- if (shouldExit) break;
66
- }
67
-
68
- if (shouldExit) {
69
- return null;
70
- }
71
-
72
- return trainX;
73
- };
@@ -1,51 +0,0 @@
1
-
2
- import { validateArray, validateString } from "./validators.js"
3
-
4
- export const getOrderFromArray = (arr, methodName) => {
5
-
6
- validateArray(arr, 'arr', 'getOrderFromArray')
7
- validateString(methodName, 'methodName', 'getOrderFromArray')
8
-
9
- const level2Methods = ['regressor', 'classifier', 'pca']
10
- const baseKeys = ['open', 'high', 'low', 'close', 'volume', 'date']
11
-
12
- // 1. Level-2: does this method live in your level-2 list?
13
- const isSecondary = level2Methods.includes(methodName);
14
-
15
- // 2. Level-0: is every field one of the primitives *and* not level-2?
16
- const isPrimary = arr.every(v => baseKeys.includes(v)) && !isSecondary;
17
-
18
- if(isPrimary)
19
- {
20
- return 0
21
- }
22
-
23
- let order = 1
24
- const level1Prefixes = ['_x_', 'zscore_', 'minmax_', 'lag_']
25
-
26
- for(const k of level1Prefixes)
27
- {
28
- if(arr.some(v => v.includes(k)))
29
- {
30
- order++
31
- }
32
- }
33
-
34
- if(!isSecondary)
35
- {
36
- return order
37
- }
38
-
39
- const level2Prefixes = ['prediction_', 'reg_', 'cla_', 'pca_']
40
- order = 10
41
-
42
- for(const k of level2Prefixes)
43
- {
44
- if(arr.some(v => v.includes(k)))
45
- {
46
- order++
47
- }
48
- }
49
-
50
- return order
51
- }