ohlcv-indicators 3.5.45 → 3.5.47
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 +2 -8
- package/src/oscillators/stochastic.js +2 -8
- package/src/oscillators/volumeOscillator.js +1 -3
- package/src/studies/dateTime.js +3 -9
- package/src/studies/findCrosses.js +12 -24
- 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.47",
|
|
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
|
@@ -6,7 +6,7 @@ export const rsi = (main, index, size, { target, lag, parser, prefix, minmax })
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
const { verticalOhlcv, instances
|
|
9
|
+
const { verticalOhlcv, instances } = main;
|
|
10
10
|
|
|
11
11
|
const suffix = target === defaultTarget ? '' : `_${target}`;
|
|
12
12
|
const rsiKey = `${prefix}rsi_${size}${suffix}`;
|
|
@@ -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,12 +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
|
-
notNumberKeys.add(key)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
53
|
}
|
|
60
54
|
|
|
61
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,12 +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
|
-
notNumberKeys.add(key)
|
|
52
|
-
}
|
|
53
47
|
}
|
|
54
48
|
|
|
55
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,20 +147,7 @@ export const crossPairs = (main, index, crossPairsList, {limit, oneHot}) => {
|
|
|
147
147
|
|
|
148
148
|
const col = slow.toString()
|
|
149
149
|
verticalOhlcvTempCols.add(col)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
let slowNumArrType = 'Int16Array'
|
|
153
|
-
|
|
154
|
-
if(slow === 0 || slow === 1)
|
|
155
|
-
{
|
|
156
|
-
slowNumArrType = 'Uint8Array'
|
|
157
|
-
} else if(slow >= -125 && slow <= 125)
|
|
158
|
-
{
|
|
159
|
-
slowNumArrType = 'Int8Array'
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
verticalOhlcv[col] = buildArray(slowNumArrType, len)
|
|
163
|
-
arrayTypes[col] = 'slowNumArrType'
|
|
150
|
+
verticalOhlcv[col] = buildArray('Int16Array', len)
|
|
164
151
|
}
|
|
165
152
|
|
|
166
153
|
if(!instances.hasOwnProperty('crossPairs'))
|
|
@@ -187,13 +174,8 @@ export const crossPairs = (main, index, crossPairsList, {limit, oneHot}) => {
|
|
|
187
174
|
if(oneHot)
|
|
188
175
|
{
|
|
189
176
|
verticalOhlcv[`one_hot_${crossName}`] = buildArray('Array', len)
|
|
190
|
-
notNumberKeys.add(`one_hot_${crossName}`)
|
|
191
177
|
}
|
|
192
178
|
|
|
193
|
-
notNumberKeys.add(crossName)
|
|
194
|
-
|
|
195
|
-
arrayTypes[crossName] = 'crossArrType'
|
|
196
|
-
|
|
197
179
|
} else if(index + 1 === len) {
|
|
198
180
|
// sanity checks
|
|
199
181
|
if (fast !== "price" && !verticalOhlcv.hasOwnProperty(fast)) {
|
|
@@ -227,15 +209,21 @@ export const crossPairs = (main, index, crossPairsList, {limit, oneHot}) => {
|
|
|
227
209
|
|
|
228
210
|
const rawValue = run.getResult()
|
|
229
211
|
|
|
230
|
-
const value = Math.max(-limit, Math.min(limit, rawValue))
|
|
212
|
+
const value = rawValue//Math.max(-limit, Math.min(limit, rawValue))
|
|
231
213
|
|
|
232
214
|
main.pushToMain({index, key: crossName, value})
|
|
233
215
|
|
|
234
216
|
if(oneHot)
|
|
235
217
|
{
|
|
236
|
-
|
|
237
|
-
const
|
|
238
|
-
|
|
218
|
+
// clamp _just_ for the one-hot index
|
|
219
|
+
const clamped = Math.max(-limit, Math.min(limit, rawValue));
|
|
220
|
+
const oneHotIdx = clamped + limit;
|
|
221
|
+
const oneHotSize = 2 * limit + 1;
|
|
222
|
+
main.pushToMain({
|
|
223
|
+
index,
|
|
224
|
+
key: `one_hot_${crossName}`,
|
|
225
|
+
value: oneHotEncode(oneHotIdx, oneHotSize)
|
|
226
|
+
});
|
|
239
227
|
}
|
|
240
228
|
|
|
241
229
|
}
|
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)
|