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