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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ohlcv-indicators",
3
- "version": "3.5.41",
3
+ "version": "3.5.42",
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,7 +12,6 @@
12
12
  "author": "https://jaimelias.com/",
13
13
  "license": "ISC",
14
14
  "dependencies": {
15
- "ml-pca": "^4.1.1",
16
15
  "trading-signals": "^5.0.4",
17
16
  "xy-scale": "^1.4.1"
18
17
  },
@@ -20,8 +19,18 @@
20
19
  "@babel/core": "^7.25.2",
21
20
  "@babel/plugin-proposal-class-properties": "^7.18.6",
22
21
  "@babel/preset-env": "^7.25.3",
22
+ "arima": "^0.2.5",
23
23
  "babel-loader": "^9.1.3",
24
24
  "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",
25
34
  "terser-webpack-plugin": "^5.3.14",
26
35
  "webpack": "^5.93.0",
27
36
  "webpack-cli": "^5.1.4"
@@ -1,6 +1,9 @@
1
+ import { areKeyValuesValid } from "./pushToMain.js";
1
2
  import { validateInputParams } from "../utilities/validators.js";
2
3
  import { rsi } from "../oscillators/rsi.js";
4
+ import { stochastic } from "../oscillators/stochastic.js";
3
5
  import { movingAverages } from "../moving-averages/movingAverages.js";
6
+ import { vidya } from "../moving-averages/vidya.js";
4
7
  import { macd } from "../moving-averages/macd.js";
5
8
  import { relativeVolume } from "../moving-averages/relativeVolume.js";
6
9
  import { donchianChannels } from "../moving-averages/donchianChannel.js";
@@ -11,31 +14,48 @@ import { crossPairs } from "../studies/findCrosses.js";
11
14
  import { dateTime } from "../studies/dateTime.js";
12
15
  import { scaler } from "../machine-learning/scaler.js";
13
16
  import { atr } from "../volatility/atr.js";
17
+ import { mapCols } from "../studies/mapCols.js";
14
18
 
15
19
  import { buildArray } from "../utilities/assignTypes.js";
16
20
  import { numberFormater } from "../utilities/numberUtilities.js";
17
21
  import { dateFormaters } from "../utilities/dateUtilities.js";
18
22
 
19
23
  import { pca } from "../machine-learning/pca.js";
24
+ import { regressor } from "../machine-learning/regressor.js";
25
+ import { classifier } from "../machine-learning/classifier.js";
20
26
 
21
27
  // Map indicator keys to their respective functions
22
- const indicatorFunctions = {
28
+ const mainFunctions = {
23
29
  dateTime,
24
30
  rsi,
31
+ stochastic,
25
32
  atr,
26
33
  ema: movingAverages,
27
34
  sma: movingAverages,
35
+ vidya,
28
36
  macd,
29
37
  relativeVolume,
30
38
  donchianChannels,
31
39
  bollingerBands,
32
40
  volumeOscillator,
33
- scaler
41
+ mapCols,
42
+ scaler,
43
+ crossPairs,
44
+ lag
34
45
  };
35
46
 
36
-
37
47
  export const mainLoop = (input, main) => {
38
- const { len, inputParams, priceBased, precisionMultiplier, arrayTypes, verticalOhlcv, verticalOhlcvKeyNames, inputTypes, chunkProcess, scaledGroups } = main;
48
+ const { len,
49
+ inputParams,
50
+ precisionMultiplier,
51
+ arrayTypes,
52
+ verticalOhlcv,
53
+ verticalOhlcvKeyNames,
54
+ inputTypes,
55
+ chunkProcess,
56
+ notNumberKeys,
57
+ processSecondaryLoop,
58
+ } = main;
39
59
 
40
60
  validateInputParams(inputParams, len)
41
61
 
@@ -43,7 +63,6 @@ export const mainLoop = (input, main) => {
43
63
  {
44
64
  verticalOhlcv[key] = buildArray(arrayTypes[key], len)
45
65
  }
46
-
47
66
  // Process each row in the input
48
67
  for (let chunkStart = 0; chunkStart < len; chunkStart += chunkProcess) {
49
68
 
@@ -60,7 +79,7 @@ export const mainLoop = (input, main) => {
60
79
  {
61
80
  let formatedValue = numberFormater[formaterKey](value)
62
81
 
63
- if(precisionMultiplier > 1 && priceBased.has(key))
82
+ if(precisionMultiplier > 1 && !notNumberKeys.has(key))
64
83
  {
65
84
  formatedValue = formatedValue * precisionMultiplier
66
85
  }
@@ -76,22 +95,57 @@ export const mainLoop = (input, main) => {
76
95
  main.pushToMain({index, key, value})
77
96
  }
78
97
  }
79
-
80
- // Run all indicator functions except for the ones processed later
81
- for (const { key, params } of inputParams) {
82
- if (key === 'lag' || key === 'crossPairs') continue;
83
- // resolve fn on-demand, no per-item object allocation here
84
- indicatorFunctions[key](main, index, ...params)
85
- }
86
98
 
87
99
  // Process these indicators separately (ensuring their execution order)
88
- lag(main, index)
89
- crossPairs(main, index)
100
+ for (const { key, params, order } of inputParams)
101
+ {
102
+ for(let orderIdx = 0; orderIdx <= 9; orderIdx++)
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
+ }
108
+ }
109
+
90
110
  input[index] = null //flusing data
111
+
112
+ const keyNames = Object.keys(verticalOhlcv)
113
+
114
+ // if any value at this index is missing/NaN/null, mark it invalid
115
+ if (!areKeyValuesValid(main, index, keyNames)) {
116
+ main.invalidValueIndex = index
117
+ }
91
118
  }
92
119
  }
93
120
 
94
- pca(main)
121
+ const isSecondaryLoopNeeded = inputParams.some(({ key }) => key === 'regressor' || key === 'classifier' || key === 'pca')
122
+
123
+ if(isSecondaryLoopNeeded || processSecondaryLoop)
124
+ {
125
+ secondaryLoop(main)
126
+ }
95
127
 
96
128
  verticalOhlcvKeyNames.push(...Object.keys(verticalOhlcv))
129
+
130
+ }
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
+
97
151
  }
@@ -2,14 +2,35 @@ export const pushToMain = ({main, index, key, value}) => {
2
2
 
3
3
  const {verticalOhlcv} = main
4
4
 
5
- if(value === null || typeof value === 'undefined' || Number.isNaN(value))
6
- {
7
- main.invalidValueIndex = index
8
- verticalOhlcv[key][index] = value
9
- return false
10
- }
11
-
12
5
  verticalOhlcv[key][index] = value
13
6
 
14
7
  return true
8
+ }
9
+
10
+ export const areKeyValuesValid = (main, index, keyNames) => {
11
+
12
+ if(keyNames.length === 0) return false
13
+
14
+ const {verticalOhlcv} = main
15
+
16
+ let output = true
17
+
18
+ for(let x = 0 ; x < keyNames.length; x++)
19
+ {
20
+ const key = keyNames[x]
21
+ if(!verticalOhlcv.hasOwnProperty(key)) {
22
+ output = false
23
+ break
24
+ }
25
+
26
+ const val = verticalOhlcv[key][index]
27
+
28
+ if(typeof val === 'undefined' || val === null || Number.isNaN(val))
29
+ {
30
+ output = false
31
+ break
32
+ }
33
+ }
34
+
35
+ return output
15
36
  }
@@ -0,0 +1,267 @@
1
+ import { getFeaturedKeys, computeFlatFeaturesLen, countUniqueLabels, logMlTraining, updateClassifierMetrics, findGroupsFunc } from "./ml-utilities.js"
2
+ import { buildTrainX } from "./trainX.js"
3
+ import { areKeyValuesValid } from "../core-functions/pushToMain.js"
4
+
5
+ const algo = 'classifier'
6
+
7
+ export const classifier = (
8
+ main,
9
+ index,
10
+ trainingSize,
11
+ {
12
+ yCallback,
13
+ trainingCols,
14
+ findGroups = [],
15
+ retrain,
16
+ type,
17
+ modelArgs,
18
+ horizon,
19
+ precompute,
20
+ predictions,
21
+ filterCallback,
22
+ modelConfig,
23
+ modelClass
24
+ }
25
+ ) => {
26
+ const { lookbackAbs, prefix, flatY } = precompute
27
+ const { verticalOhlcv, len, instances, scaledGroups, invalidValueIndex, ML } = main
28
+ const allModels = ML.models
29
+ const startIndex = (invalidValueIndex + 1)
30
+
31
+ // ─── INITIALIZATION ───────────────────────────────────────────────
32
+ if(index === startIndex) {
33
+
34
+ // prepare instance storage
35
+ if (!instances.hasOwnProperty(algo)) instances[algo] = {}
36
+ if(!instances[algo].hasOwnProperty(prefix)) instances[algo][prefix] = {}
37
+
38
+ const expectedLoops = (flatY) ? predictions : 1 //
39
+
40
+ instances[algo][prefix] = {
41
+
42
+ expectedLoops,
43
+ isTrained: new Array(expectedLoops).fill(false),
44
+ uniqueLabels: new Array(expectedLoops).fill(0),
45
+ shouldRetrain: retrain,
46
+ featureCols: [],
47
+ flatFeaturesColLen: 0,
48
+ X: [],
49
+ Y: Array.from({ length: expectedLoops }, () => []),
50
+ }
51
+
52
+ for (let i = 0; i < predictions; i++) {
53
+ const predictionKey = `${prefix}_${i + 1}`
54
+
55
+ verticalOhlcv[predictionKey] = (type === 'KNN') ? new Array(len).fill(null) : new Float32Array(len).fill(NaN)
56
+ ML.metrics[predictionKey] = {accuracy: {}, total: 0, correct: 0, labels: {}}
57
+
58
+ if(!allModels.hasOwnProperty(predictionKey))
59
+ {
60
+ allModels[predictionKey] = []
61
+ }
62
+ }
63
+ }
64
+ else if(index < startIndex || index < lookbackAbs)
65
+ {
66
+ return
67
+ }
68
+ else if(filterCallback(verticalOhlcv, index) === false)
69
+ {
70
+ return
71
+ }
72
+ else if(index + 1 === len)
73
+ {
74
+
75
+ if(instances[algo][prefix].featureCols.length === 0)
76
+ {
77
+ const inputFeatures = [...trainingCols, ...(findGroupsFunc(findGroups, scaledGroups))]
78
+
79
+ throw new Error(`Some of the provided features in ${algo} "${prefix}" features where not found in "verticalOhlcv": ${JSON.stringify(inputFeatures)}`)
80
+ }
81
+
82
+ //last execution
83
+ for(const featureKey of instances[algo][prefix].featureCols)
84
+ {
85
+ if(!verticalOhlcv.hasOwnProperty(featureKey)) throw new Error(`Feature "${featureKey}" not found in verticalOhlcv for ${prefix}.`)
86
+ }
87
+
88
+ if(instances[algo][prefix].X.length < trainingSize)
89
+ {
90
+ const requiredDatapoints = instances[algo][prefix].X.length - trainingSize
91
+ throw new Error(`The current "trainingSize" at ${algo} "${prefix}" requires at least ${requiredDatapoints} more datapoints. Try adding more input ohlcv rows or reducing the "trainingSize" by ${requiredDatapoints}.`)
92
+ }
93
+
94
+ }
95
+
96
+ const dataSetInstance = instances[algo][prefix]
97
+
98
+ if(dataSetInstance.flatFeaturesColLen === 0)
99
+ {
100
+ dataSetInstance.featureCols = getFeaturedKeys({trainingCols, findGroups, verticalOhlcv, scaledGroups})
101
+
102
+ if(areKeyValuesValid(main, index, dataSetInstance.featureCols))
103
+ {
104
+ dataSetInstance.flatFeaturesColLen = computeFlatFeaturesLen(dataSetInstance.featureCols, verticalOhlcv, index)
105
+
106
+ logMlTraining({
107
+ featureCols: dataSetInstance.featureCols,
108
+ flatFeaturesColLen: dataSetInstance.flatFeaturesColLen,
109
+ type,
110
+ trainingSize
111
+ })
112
+ } else {
113
+ return
114
+ }
115
+ }
116
+
117
+ if(dataSetInstance.flatFeaturesColLen === 0) return
118
+
119
+ const {
120
+ uniqueLabels,
121
+ expectedLoops,
122
+ featureCols,
123
+ flatFeaturesColLen,
124
+ shouldRetrain,
125
+ X: xRows
126
+ } = dataSetInstance
127
+
128
+ const trainX = buildTrainX({
129
+ featureCols,
130
+ flatFeaturesColLen,
131
+ type,
132
+ index,
133
+ lookbackAbs,
134
+ main
135
+ })
136
+
137
+ if(!trainX) return
138
+
139
+ //if univariable Y (flatY) a model is created for each prediction
140
+ const trainY = yCallback(index, verticalOhlcv, horizon)
141
+
142
+ if (trainY !== null && (!Array.isArray(trainY) || trainY.length !== predictions)) {
143
+
144
+ throw new Error(`trainY must be null or an array with ${predictions} items, got "${JSON.stringify(trainY)}" at index ${index} for ${type}`);
145
+ }
146
+
147
+ for(let loopIdx = 0; loopIdx < expectedLoops; loopIdx++)
148
+ {
149
+ const predictionKey = `${prefix}_${(loopIdx+1)}`
150
+ const yRows = dataSetInstance.Y[loopIdx]
151
+ const currTrainY = (flatY) ? (trainY === null) ? null : trainY[loopIdx] : trainY
152
+ const isTrained = dataSetInstance.isTrained[loopIdx]
153
+
154
+ const shouldPredict = allModels.hasOwnProperty(predictionKey) && isTrained && (shouldRetrain === false || allModels[predictionKey].length === horizon)
155
+
156
+ //predicts using previously saved models even if current currTrainY is not available
157
+ if(shouldPredict)
158
+ {
159
+ const futureRow = allModels[predictionKey][0].predict([trainX])[0]
160
+
161
+ if(flatY)
162
+ {
163
+ if(futureRow == null) throw new Error(`Prediction of ${prefix} at index ${index} was expecting a number.`)
164
+
165
+ main.pushToMain({index, key: predictionKey, value: futureRow})
166
+
167
+ if(currTrainY != null)
168
+ {
169
+ updateClassifierMetrics({
170
+ metrics: ML.metrics[predictionKey],
171
+ trueLabel: currTrainY,
172
+ predictedLabel: futureRow
173
+ })
174
+ }
175
+ }
176
+ else {
177
+
178
+ if (!Array.isArray(futureRow) || futureRow.length !== predictions) {
179
+ throw new Error(
180
+ `Prediction output of ${algo} "${prefix}" at index ${index} was expecting an array of values.`
181
+ )
182
+ }
183
+
184
+ for(let preIdx = 0; preIdx < predictions; preIdx++) {
185
+ main.pushToMain({index, key: `${prefix}_${(preIdx+1)}`, value: futureRow[preIdx]})
186
+
187
+ if(Array.isArray(currTrainY) && currTrainY.length === predictions)
188
+ {
189
+ updateClassifierMetrics({
190
+ metrics: ML.metrics[`${prefix}_${(preIdx+1)}`],
191
+ trueLabel: currTrainY[preIdx],
192
+ predictedLabel: futureRow[preIdx]
193
+ })
194
+ }
195
+
196
+ }
197
+ }
198
+ }
199
+
200
+ if((index + predictions + 1) > len) continue
201
+ if(currTrainY === null) continue // future not defined
202
+
203
+ if(flatY)
204
+ {
205
+ if (typeof currTrainY === 'undefined' || currTrainY == null)
206
+ {
207
+ throw new Error(`currTrainY must return number, string or boolean got ${typeof currTrainY} at index ${index}`)
208
+ }
209
+ } else {
210
+
211
+ if ((currTrainY.length !== predictions)) {
212
+
213
+ throw new Error(
214
+ `The number of label columns returned (${currTrainY.length}) doesn’t match the options.predictions (${predictions}) setting for the ${algo} "${prefix}" classifier. ` +
215
+ `Please update your yCallback function so its output array items align with options.predictions.`
216
+ );
217
+
218
+ }
219
+ }
220
+
221
+ // enqueue
222
+
223
+ yRows.push(currTrainY)
224
+ if (yRows.length > trainingSize) yRows.shift()
225
+
226
+ if(loopIdx === 0)
227
+ {
228
+ xRows.push(trainX)
229
+ if (xRows.length > trainingSize) xRows.shift()
230
+ }
231
+
232
+ const shouldTrainModel = shouldRetrain || !isTrained
233
+
234
+ if (shouldTrainModel && xRows.length === trainingSize && yRows.length === trainingSize) {
235
+
236
+ if(isTrained === false)
237
+ {
238
+ uniqueLabels[loopIdx] = countUniqueLabels(yRows)
239
+
240
+ if(uniqueLabels[loopIdx] < 2)
241
+ {
242
+ throw new Error(`Invalid number or labels in ${prefix}. Check the logic of your "yCallback" function.`)
243
+ }
244
+ }
245
+
246
+
247
+ const {train} = modelConfig
248
+
249
+ console.log(yRows)
250
+
251
+ const trainedModel = train({modelClass, xRows, yRows, modelArgs, uniqueLabels: uniqueLabels[loopIdx]})
252
+
253
+ allModels[predictionKey].push(
254
+ trainedModel
255
+ )
256
+
257
+ if(allModels[predictionKey].length > horizon)
258
+ {
259
+ allModels[predictionKey].shift()
260
+ }
261
+
262
+ dataSetInstance.isTrained[loopIdx] = true
263
+ }
264
+ }
265
+
266
+ return true
267
+ }
@@ -0,0 +1,219 @@
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
+ }