ohlcv-indicators 3.5.41 → 3.5.43
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 +533 -67
- package/package.json +11 -2
- package/src/core-functions/mainLoop.js +70 -16
- package/src/core-functions/pushToMain.js +28 -7
- package/src/machine-learning/classifier.js +267 -0
- package/src/machine-learning/ml-config.js +219 -0
- package/src/machine-learning/ml-utilities.js +148 -0
- package/src/machine-learning/pca.js +71 -80
- package/src/machine-learning/regressor.js +227 -0
- package/src/machine-learning/scaler.js +97 -64
- package/src/machine-learning/trainX.js +73 -0
- package/src/moving-averages/bollingerBands.js +30 -31
- package/src/moving-averages/donchianChannel.js +6 -4
- package/src/moving-averages/macd.js +3 -7
- package/src/moving-averages/movingAverages.js +30 -33
- package/src/moving-averages/relativeVolume.js +2 -0
- package/src/moving-averages/vidya.js +179 -0
- package/src/oscillators/rsi.js +26 -15
- package/src/oscillators/stochastic.js +74 -0
- package/src/oscillators/volumeOscillator.js +3 -2
- package/src/studies/dateTime.js +58 -65
- package/src/studies/findCrosses.js +88 -50
- package/src/studies/lag.js +37 -70
- package/src/studies/mapCols.js +47 -0
- package/src/utilities/assignTypes.js +28 -4
- package/src/utilities/numberUtilities.js +0 -26
- package/src/utilities/order.js +51 -0
- package/src/utilities/precisionMultiplier.js +3 -4
- package/src/utilities/validators.js +7 -0
- package/src/utilities/verticalToHorizontal.js +2 -2
- package/src/volatility/atr.js +2 -7
package/src/studies/lag.js
CHANGED
|
@@ -1,77 +1,44 @@
|
|
|
1
|
-
import { buildArray } from "../utilities/assignTypes.js"
|
|
2
|
-
|
|
3
|
-
export const lag = (main, index) => {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if(priceBased.has(colKey))
|
|
24
|
-
{
|
|
25
|
-
for (let step = 1; step <= lookback; step++) {
|
|
26
|
-
priceBased.add(`${colKey}_lag_${step}`)
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
instances.lag = {
|
|
33
|
-
lagParams: params
|
|
34
|
-
}
|
|
1
|
+
import { buildArray } from "../utilities/assignTypes.js"
|
|
2
|
+
|
|
3
|
+
export const lag = (main, index, colKeys, lookback) => {
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const { verticalOhlcv, len, arrayTypes } = main
|
|
8
|
+
|
|
9
|
+
if(index === 0)
|
|
10
|
+
{
|
|
11
|
+
for (const colKey of colKeys) {
|
|
12
|
+
if (!arrayTypes.hasOwnProperty(colKey)) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
`Lag processing invoked by "${colKey}" expected arrayTypes to have a "${colKey}" property, but it wasn’t found.`
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
for (let step = 1; step <= lookback; step++) {
|
|
19
|
+
const key = `${colKey}_lag_${step}`
|
|
20
|
+
verticalOhlcv[key] = buildArray(arrayTypes[colKey], len)
|
|
21
|
+
arrayTypes[key] = arrayTypes[colKey]
|
|
22
|
+
}
|
|
35
23
|
}
|
|
36
|
-
|
|
37
|
-
const {lagParams} = instances.lag
|
|
38
|
-
|
|
39
|
-
for (const [colKeys, lookback] of lagParams) {
|
|
40
|
-
|
|
41
|
-
for (const colKey of colKeys) {
|
|
42
|
-
const currentColumn = verticalOhlcv[colKey];
|
|
43
|
-
|
|
44
|
-
// Initialize lagged arrays only on the first index
|
|
45
|
-
if (index === 0) {
|
|
46
|
-
for (let step = 1; step <= lookback; step++) {
|
|
47
|
-
const key = `${colKey}_lag_${step}`;
|
|
48
|
-
|
|
49
|
-
if(!arrayTypes.hasOwnProperty(colKey))
|
|
50
|
-
{
|
|
51
|
-
throw new Error(`Lag processing invoked by "${colKey}" expected arrayTypes to have a "${colKey}" property, but it wasn’t found.`)
|
|
52
|
-
}
|
|
24
|
+
}
|
|
53
25
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
}
|
|
26
|
+
for (const colKey of colKeys) {
|
|
27
|
+
const currentColumn = verticalOhlcv[colKey]
|
|
58
28
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
29
|
+
// Populate the lagged values each tick:
|
|
30
|
+
for (let step = 1; step <= lookback; step++) {
|
|
31
|
+
const key = `${colKey}_lag_${step}`
|
|
32
|
+
const laggedIndex = index - step
|
|
33
|
+
const value =
|
|
34
|
+
laggedIndex < 0 || currentColumn[laggedIndex] === undefined
|
|
35
|
+
? null
|
|
36
|
+
: currentColumn[laggedIndex]
|
|
63
37
|
|
|
64
|
-
|
|
65
|
-
? null
|
|
66
|
-
: currentColumn[laggedIndex]
|
|
38
|
+
main.pushToMain({ index, key, value })
|
|
67
39
|
|
|
68
|
-
main.pushToMain({index, key, value})
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
40
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
41
|
+
}
|
|
75
42
|
|
|
76
|
-
|
|
77
|
-
}
|
|
43
|
+
return true
|
|
44
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export const mapCols = (main, index, newCols, callback, {lag}) => {
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
const {verticalOhlcv, len, arrayTypes} = main
|
|
6
|
+
|
|
7
|
+
if(index === 0)
|
|
8
|
+
{
|
|
9
|
+
|
|
10
|
+
for(const key of newCols)
|
|
11
|
+
{
|
|
12
|
+
if(verticalOhlcv.hasOwnProperty(key)) {
|
|
13
|
+
throw new Error(`New property "${key}" already exist in "verticalOhlcv" and can not be modified using mapCols.`)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
verticalOhlcv[key] = new Array(len).fill(0)
|
|
17
|
+
arrayTypes[key] = 'Array'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if(lag > 0)
|
|
21
|
+
{
|
|
22
|
+
main.lag(newCols, lag)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const cols = callback({index, main})
|
|
27
|
+
|
|
28
|
+
if(cols == null) return true
|
|
29
|
+
|
|
30
|
+
for(const [key, value] of Object.entries(cols))
|
|
31
|
+
{
|
|
32
|
+
main.pushToMain({ index, key, value })
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const defaultMapColsCallback = ({main, index}) => {
|
|
37
|
+
const {verticalOhlcv} = main
|
|
38
|
+
|
|
39
|
+
const currClose = verticalOhlcv.close[index]
|
|
40
|
+
const prevClose = verticalOhlcv.close[index-1]
|
|
41
|
+
|
|
42
|
+
if(typeof prevClose === 'undefined') return null
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
change: (currClose - prevClose) / prevClose
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -4,12 +4,28 @@ import { selectDateFormatter } from "./dateUtilities.js";
|
|
|
4
4
|
const numberKeys = new Set(['open', 'high', 'low', 'close'])
|
|
5
5
|
|
|
6
6
|
export const buildArray = (arrayType, len) => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
const ctorMap = {
|
|
8
|
+
Array: Array,
|
|
9
|
+
Float64Array: Float64Array,
|
|
10
|
+
Float32Array: Float32Array,
|
|
11
|
+
Int32Array: Int32Array,
|
|
12
|
+
Uint8Array: Uint8Array,
|
|
13
|
+
Int8Array: Int8Array,
|
|
14
|
+
Int16Array: Int16Array,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const Ctor = ctorMap[arrayType];
|
|
18
|
+
if (!Ctor || typeof Ctor.from !== 'function') {
|
|
19
|
+
throw new Error(`Unsupported array type: ${arrayType}`);
|
|
20
|
+
}
|
|
21
|
+
// Arrays get nulls, typed-arrays get NaNs
|
|
22
|
+
const fillValue = arrayType === 'Array' ? null : NaN;
|
|
23
|
+
// Ctor.from({ length }, mapFn) works for Array *and* all TypedArrays
|
|
24
|
+
return Ctor.from({ length: len }, () => fillValue);
|
|
10
25
|
}
|
|
11
26
|
|
|
12
|
-
export const assignTypes =
|
|
27
|
+
export const assignTypes = main => {
|
|
28
|
+
const {firstRow, notNumberKeys} = main
|
|
13
29
|
const inputTypes = {}
|
|
14
30
|
const arrayTypes = {}
|
|
15
31
|
|
|
@@ -18,11 +34,13 @@ export const assignTypes = firstRow => {
|
|
|
18
34
|
if (colName === 'date') {
|
|
19
35
|
inputTypes[colName] = selectDateFormatter(cellValue, true)
|
|
20
36
|
arrayTypes[colName] = 'Array'
|
|
37
|
+
notNumberKeys.add(colName)
|
|
21
38
|
}
|
|
22
39
|
else if(colName === 'volume')
|
|
23
40
|
{
|
|
24
41
|
inputTypes[colName] = classifyNum(cellValue, true)
|
|
25
42
|
arrayTypes[colName] = 'Int32Array'
|
|
43
|
+
notNumberKeys.add(colName)
|
|
26
44
|
}
|
|
27
45
|
else if(numberKeys.has(colName))
|
|
28
46
|
{
|
|
@@ -32,6 +50,12 @@ export const assignTypes = firstRow => {
|
|
|
32
50
|
const thisType = classifyNum(cellValue, false)
|
|
33
51
|
inputTypes[colName] = thisType
|
|
34
52
|
arrayTypes[colName] = (thisType === 'number') ? 'Float64Array' : 'Array'
|
|
53
|
+
|
|
54
|
+
if(!thisType.startsWith('number'))
|
|
55
|
+
{
|
|
56
|
+
notNumberKeys.add(colName)
|
|
57
|
+
}
|
|
58
|
+
|
|
35
59
|
}
|
|
36
60
|
}
|
|
37
61
|
|
|
@@ -7,32 +7,6 @@ export const numberFormater = {
|
|
|
7
7
|
numberDirtyString: n => Number(sanitizeDirtyNumberString(n))
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export const divideByMultiplier = ({row, precisionMultiplier, priceBased}) => {
|
|
11
|
-
const output = {}
|
|
12
|
-
|
|
13
|
-
for(const [key, value] of Object.entries(row))
|
|
14
|
-
{
|
|
15
|
-
|
|
16
|
-
let newValue = value
|
|
17
|
-
|
|
18
|
-
if(priceBased.has(key))
|
|
19
|
-
{
|
|
20
|
-
newValue = newValue / precisionMultiplier
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
output[key] = newValue
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return output
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Parses a number from either a numeric or string input, applying a multiplier to fix precision issues
|
|
30
|
-
export const parseNumber = (num, type, precisionMultiplier) => {
|
|
31
|
-
const addMultiplier = cNum => precisionMultiplier > 1 ? cNum * precisionMultiplier : cNum;
|
|
32
|
-
|
|
33
|
-
return addMultiplier(numberFormater[type](num))
|
|
34
|
-
|
|
35
|
-
};
|
|
36
10
|
|
|
37
11
|
export const classifyNum = (num, throwError = false) => {
|
|
38
12
|
// 1) true numbers
|
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
export const calcPrecisionMultiplier = (main, firstRow) => {
|
|
2
|
-
const {precision
|
|
2
|
+
const {precision} = main
|
|
3
3
|
|
|
4
4
|
if(precision === false) return 1
|
|
5
5
|
|
|
6
6
|
let output = 1
|
|
7
|
-
const priceBasedValues = Object.entries(firstRow).filter(([k, _]) => priceBased.has(k)).map(([_, v]) => v)
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
for(const value of priceBasedValues)
|
|
8
|
+
for(const [key, value] of Object.entries(firstRow))
|
|
11
9
|
{
|
|
10
|
+
if(new Set(['open', 'high', 'low', 'close']).has(key) === false) continue
|
|
12
11
|
const [, decimals = ''] = String(value).split('.')
|
|
13
12
|
const decimalPrecision = Math.max(4, decimals.length)
|
|
14
13
|
const multiplier = decimalPrecision > 1 ? Math.pow(10, decimalPrecision - 1) : 1
|
|
@@ -132,4 +132,11 @@ export const validateArrayOfRanges = (range, paramName, callerName) => {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
return true
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export const validateString = (txt, paramName, callerName) => {
|
|
138
|
+
|
|
139
|
+
if(typeof txt !== 'string' || !txt.length) throw new Error(`Invalid "${paramName}" property in "${callerName}". Only strings are accepted.`)
|
|
140
|
+
|
|
141
|
+
return true
|
|
135
142
|
}
|
|
@@ -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 {precisionMultiplier,
|
|
5
|
+
const {precisionMultiplier, precision, verticalOhlcv, invalidValueIndex, len, verticalOhlcvKeyNames, verticalOhlcvTempCols, notNumberKeys} = main
|
|
6
6
|
|
|
7
7
|
if (verticalOhlcvKeyNames.length === 0) return []
|
|
8
8
|
|
|
@@ -13,7 +13,7 @@ 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 =
|
|
16
|
+
const shouldApplyPrecision = precision && !notNumberKeys.has(key)
|
|
17
17
|
|
|
18
18
|
for (let i = maxStartIndex; i < len; i++)
|
|
19
19
|
{
|
package/src/volatility/atr.js
CHANGED
|
@@ -7,7 +7,7 @@ export const atr = (main, index, size, {lag, percentage, upper, lower}) => {
|
|
|
7
7
|
|
|
8
8
|
if (index === 0) {
|
|
9
9
|
|
|
10
|
-
const {instances, verticalOhlcv, arrayTypes, len
|
|
10
|
+
const {instances, verticalOhlcv, arrayTypes, len} = main
|
|
11
11
|
|
|
12
12
|
instances[baseKeyName] = new FasterATR(size, FasterWSMA)
|
|
13
13
|
|
|
@@ -18,12 +18,7 @@ export const atr = (main, index, size, {lag, percentage, upper, lower}) => {
|
|
|
18
18
|
if(lower !== null) keyNames.push(`${baseKeyName}_lower`)
|
|
19
19
|
|
|
20
20
|
for(const k of keyNames)
|
|
21
|
-
{
|
|
22
|
-
if(!k.endsWith('_percentage'))
|
|
23
|
-
{
|
|
24
|
-
priceBased.add(k)
|
|
25
|
-
}
|
|
26
|
-
|
|
21
|
+
{
|
|
27
22
|
verticalOhlcv[k] = new Float64Array(len).fill(NaN)
|
|
28
23
|
arrayTypes[k] = 'Float64Array'
|
|
29
24
|
}
|