ohlcv-indicators 3.5.4 → 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/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 +72 -79
- 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 +181 -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
|
@@ -1,39 +1,36 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { normalizeMinMax, normalizeZScore } from "./ml-utilities.js";
|
|
2
|
+
|
|
3
|
+
export const scaler = (main, index, size, colKeys, {type, group, range, lag, precomputed, secondaryLoop}) => {
|
|
5
4
|
|
|
5
|
+
|
|
6
6
|
|
|
7
|
-
const normalizeMinMax = (value, min, max, [validMin, validMax]) => {
|
|
8
|
-
const clamped = Math.min(Math.max(value, min), max);
|
|
9
|
-
return ((clamped - min) / (max - min)) * (validMax - validMin) + validMin;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const normalizeZScore = (value, mean, std) => {
|
|
13
|
-
return std === 0 ? 0 : (value - mean) / std;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export const scaler = (main, index, size, colKeys, {type, group, range, lag, precomputed, decimals}) => {
|
|
17
7
|
const {groupKey, groupKeyLen} = precomputed
|
|
18
|
-
const { verticalOhlcv, instances, arrayTypes } = main;
|
|
8
|
+
const { verticalOhlcv, instances, arrayTypes, invalidValueIndex } = main;
|
|
19
9
|
const prefix = `${type}_${size}`;
|
|
20
10
|
|
|
21
|
-
if
|
|
22
|
-
const { len,
|
|
11
|
+
if(index === 0) {
|
|
12
|
+
const { len, isAlreadyComputed } = main
|
|
23
13
|
|
|
24
|
-
if(
|
|
14
|
+
if(isAlreadyComputed.has(prefix))
|
|
25
15
|
{
|
|
26
|
-
throw new Error(`
|
|
16
|
+
throw new Error(`In scaler method can not repeat params "size" and "options.type" together between indicators.`)
|
|
27
17
|
}
|
|
28
|
-
|
|
18
|
+
|
|
19
|
+
isAlreadyComputed.add(prefix)
|
|
29
20
|
|
|
30
|
-
if(!instances.hasOwnProperty('scaler'))
|
|
21
|
+
if(!instances.hasOwnProperty('scaler')) {
|
|
22
|
+
instances.scaler = {}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if(!instances.scaler.hasOwnProperty(prefix))
|
|
31
26
|
{
|
|
32
|
-
instances.scaler = {
|
|
27
|
+
instances.scaler[prefix] = {
|
|
33
28
|
windows: {}
|
|
34
29
|
}
|
|
35
30
|
}
|
|
36
31
|
|
|
32
|
+
const features = []
|
|
33
|
+
|
|
37
34
|
for (const target of colKeys) {
|
|
38
35
|
if (!verticalOhlcv.hasOwnProperty(target)) {
|
|
39
36
|
throw new Error(`Target property "${target}" not found in verticalOhlcv`);
|
|
@@ -44,66 +41,102 @@ export const scaler = (main, index, size, colKeys, {type, group, range, lag, pre
|
|
|
44
41
|
arrayTypes[key] = 'Float64Array'
|
|
45
42
|
|
|
46
43
|
const winKey = group ? groupKey : target;
|
|
47
|
-
instances.scaler.windows[winKey] = [];
|
|
44
|
+
instances.scaler[prefix].windows[winKey] = [];
|
|
48
45
|
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
main.scaledGroups[groupKey].push(key);
|
|
52
|
-
}
|
|
46
|
+
if (!main.scaledGroups[groupKey]) main.scaledGroups[groupKey] = [];
|
|
47
|
+
main.scaledGroups[groupKey].push(key);
|
|
53
48
|
|
|
54
49
|
if (lag > 0) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
main.lag([key], lag);
|
|
50
|
+
const lags = Array.from({ length: lag }).map((_, i) => `${key}_lag_${i + 1}`)
|
|
51
|
+
main.scaledGroups[groupKey].push(...lags)
|
|
52
|
+
features.push(key)
|
|
60
53
|
}
|
|
61
54
|
}
|
|
55
|
+
|
|
56
|
+
if(lag > 0)
|
|
57
|
+
{
|
|
58
|
+
main.lag(features, lag)
|
|
59
|
+
}
|
|
62
60
|
}
|
|
61
|
+
|
|
62
|
+
if(secondaryLoop === true && index <= invalidValueIndex) true
|
|
63
63
|
|
|
64
|
-
const { windows } = instances.scaler
|
|
65
|
-
|
|
64
|
+
const { windows } = instances.scaler[prefix]
|
|
65
|
+
|
|
66
|
+
let hasInvalidVal = false
|
|
67
|
+
|
|
66
68
|
// update windows with current values
|
|
67
|
-
for (
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
|
|
69
|
+
for (let x = 0; x < colKeys.length; x++) {
|
|
70
|
+
const target = colKeys[x]
|
|
71
|
+
const col = verticalOhlcv[target]
|
|
72
|
+
|
|
73
|
+
if(typeof col === 'undefined')
|
|
74
|
+
{
|
|
75
|
+
hasInvalidVal = true
|
|
76
|
+
break
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const val = col[index]
|
|
80
|
+
|
|
81
|
+
if(Number.isNaN(val) || val === null)
|
|
82
|
+
{
|
|
83
|
+
hasInvalidVal = true
|
|
84
|
+
break
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const winKey = group ? groupKey : target
|
|
88
|
+
const win = windows[winKey]
|
|
71
89
|
|
|
72
|
-
win.push(val)
|
|
90
|
+
win.push(val)
|
|
73
91
|
|
|
74
92
|
if (win.length > (group ? size * groupKeyLen : size)) {
|
|
75
|
-
win.shift()
|
|
93
|
+
win.shift()
|
|
76
94
|
}
|
|
77
95
|
|
|
78
96
|
}
|
|
97
|
+
|
|
98
|
+
if(hasInvalidVal) return
|
|
79
99
|
|
|
80
|
-
const ready = index + 1 >= size
|
|
100
|
+
const ready = index + 1 >= size
|
|
101
|
+
|
|
102
|
+
if(!ready) return
|
|
81
103
|
|
|
82
104
|
// scale values once enough data
|
|
83
|
-
for (
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
for (let x = 0; x < colKeys.length; x++) {
|
|
106
|
+
const target = colKeys[x]
|
|
107
|
+
const col = verticalOhlcv[target]
|
|
108
|
+
|
|
109
|
+
if(typeof col === 'undefined')
|
|
110
|
+
{
|
|
111
|
+
break
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const val = col[index]
|
|
115
|
+
|
|
116
|
+
if(Number.isNaN(val) || val === null)
|
|
117
|
+
{
|
|
118
|
+
break
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const key = `${prefix}_${target}`
|
|
122
|
+
let scaled = null
|
|
123
|
+
const winKey = group ? groupKey : target
|
|
124
|
+
const win = windows[winKey]
|
|
125
|
+
|
|
126
|
+
if (type === 'minmax') {
|
|
127
|
+
const mn = Math.min(...win)
|
|
128
|
+
const mx = Math.max(...win)
|
|
129
|
+
scaled = normalizeMinMax(val, mn, mx, range)
|
|
130
|
+
} else if (type === 'zscore') {
|
|
131
|
+
const mean = win.reduce((sum, x) => sum + x, 0) / win.length;
|
|
132
|
+
const variance = win.reduce((sum, x) => sum + Math.pow(x - mean, 2), 0) / win.length
|
|
133
|
+
const std = Math.sqrt(variance);
|
|
134
|
+
scaled = normalizeZScore(val, mean, std)
|
|
135
|
+
} else {
|
|
136
|
+
throw new Error(`Unknown scaler type "${type}"`)
|
|
104
137
|
}
|
|
105
138
|
|
|
106
|
-
main.pushToMain({ index, key, value:
|
|
139
|
+
main.pushToMain({ index, key, value: scaled})
|
|
107
140
|
}
|
|
108
|
-
|
|
141
|
+
}
|
|
109
142
|
|
|
@@ -0,0 +1,73 @@
|
|
|
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
|
+
};
|
|
@@ -4,15 +4,17 @@ import { roundDecimalPlaces } from '../utilities/numberUtilities.js';
|
|
|
4
4
|
const defaultTarget = 'close'
|
|
5
5
|
|
|
6
6
|
export const bollingerBands = (main, index, size, stdDev, { height, range = [], target, lag, decimals }) => {
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
7
10
|
const { verticalOhlcv, instances } = main;
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
let prefix;
|
|
11
|
+
const indicatorKey = `${size}_${stdDev}`;
|
|
12
|
+
const prefix = 'bollinger_bands'
|
|
11
13
|
|
|
12
14
|
// Initialization on the first call.
|
|
13
15
|
if (index === 0) {
|
|
14
16
|
|
|
15
|
-
const {
|
|
17
|
+
const {inputParams, verticalOhlcv, len, arrayTypes } = main;
|
|
16
18
|
|
|
17
19
|
if (!(target in verticalOhlcv)) {
|
|
18
20
|
throw new Error(`bollingerBands could not find target "${target}"`);
|
|
@@ -24,10 +26,6 @@ export const bollingerBands = (main, index, size, stdDev, { height, range = [],
|
|
|
24
26
|
if (o.key === 'bollingerBands') numberOfIndicators++;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
prefix = numberOfIndicators > 1
|
|
28
|
-
? `bollinger_bands_${indicatorKey}`
|
|
29
|
-
: `bollinger_bands${suffix}`;
|
|
30
|
-
|
|
31
29
|
// Only create the container if it doesn't already exist.
|
|
32
30
|
if (!instances.bollinger_bands) {
|
|
33
31
|
instances.bollinger_bands = {
|
|
@@ -38,23 +36,29 @@ export const bollingerBands = (main, index, size, stdDev, { height, range = [],
|
|
|
38
36
|
// Add (or override) the indicator instance keyed by indicatorKey.
|
|
39
37
|
instances.bollinger_bands.settings[indicatorKey] = new FasterBollingerBands(size, stdDev);
|
|
40
38
|
|
|
39
|
+
let suffix = ''
|
|
40
|
+
if((numberOfIndicators > 1)) suffix += `_${indicatorKey}`
|
|
41
|
+
if(target !== defaultTarget) suffix += `_${target}`
|
|
42
|
+
|
|
41
43
|
const keyNames = [
|
|
42
44
|
`${prefix}_upper`,
|
|
43
45
|
`${prefix}_middle`,
|
|
44
46
|
`${prefix}_lower`,
|
|
45
|
-
]
|
|
47
|
+
].map(v => `${v}${suffix}`)
|
|
46
48
|
|
|
47
49
|
|
|
48
50
|
if (height) {
|
|
49
|
-
keyNames.push(
|
|
51
|
+
keyNames.push(
|
|
52
|
+
`${prefix}_height${suffix}`
|
|
53
|
+
)
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
// Set up additional arrays for each range property.
|
|
53
57
|
for (const rangeKey of range) {
|
|
54
|
-
if (!(rangeKey in verticalOhlcv)
|
|
55
|
-
throw new Error(`Invalid range item value "${rangeKey}" property for bollingerBands
|
|
58
|
+
if (!(rangeKey in verticalOhlcv)) {
|
|
59
|
+
throw new Error(`Invalid range item value "${rangeKey}" property for bollingerBands.`);
|
|
56
60
|
}
|
|
57
|
-
keyNames.push(`${prefix}_range_${rangeKey}`)
|
|
61
|
+
keyNames.push(`${prefix}_range_${rangeKey}${suffix}`)
|
|
58
62
|
|
|
59
63
|
}
|
|
60
64
|
|
|
@@ -66,27 +70,22 @@ export const bollingerBands = (main, index, size, stdDev, { height, range = [],
|
|
|
66
70
|
main.lag(keyNames, lag)
|
|
67
71
|
}
|
|
68
72
|
|
|
69
|
-
[`${prefix}_upper`, `${prefix}_middle`, `${prefix}_lower`].forEach(v => {
|
|
70
|
-
priceBased.add(v)
|
|
71
|
-
})
|
|
72
|
-
|
|
73
73
|
for(const key of keyNames)
|
|
74
74
|
{
|
|
75
75
|
arrayTypes[key] = 'Float64Array'
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
if (!prefix) {
|
|
81
|
-
const num = instances.bollinger_bands.numberOfIndicators;
|
|
82
|
-
prefix = num > 1 ? `bollinger_bands_${indicatorKey}` : `bollinger_bands${suffix}`;
|
|
83
|
-
}
|
|
84
|
-
const subPrefix = prefix;
|
|
79
|
+
const {numberOfIndicators} = instances.bollinger_bands
|
|
85
80
|
|
|
81
|
+
let suffix = ''
|
|
82
|
+
if((numberOfIndicators > 1)) suffix += `_${indicatorKey}`
|
|
83
|
+
if(target !== defaultTarget) suffix += `_${target}`
|
|
84
|
+
|
|
86
85
|
// Update the indicator with the current value.
|
|
87
|
-
const instance = instances.bollinger_bands.settings[indicatorKey]
|
|
88
|
-
const value = verticalOhlcv[target][index]
|
|
89
|
-
instance.update(value)
|
|
86
|
+
const instance = instances.bollinger_bands.settings[indicatorKey]
|
|
87
|
+
const value = verticalOhlcv[target][index]
|
|
88
|
+
instance.update(value)
|
|
90
89
|
|
|
91
90
|
// Attempt to retrieve the result.
|
|
92
91
|
let result = {};
|
|
@@ -102,9 +101,9 @@ export const bollingerBands = (main, index, size, stdDev, { height, range = [],
|
|
|
102
101
|
const lower = result?.lower ?? NaN;
|
|
103
102
|
|
|
104
103
|
// Always push the indicator outputs.
|
|
105
|
-
main.pushToMain({ index, key: `${
|
|
106
|
-
main.pushToMain({ index, key: `${
|
|
107
|
-
main.pushToMain({ index, key: `${
|
|
104
|
+
main.pushToMain({ index, key: `${prefix}_upper${suffix}`, value: upper });
|
|
105
|
+
main.pushToMain({ index, key: `${prefix}_middle${suffix}`, value: middle });
|
|
106
|
+
main.pushToMain({ index, key: `${prefix}_lower${suffix}`, value: lower });
|
|
108
107
|
|
|
109
108
|
// Process height if requested.
|
|
110
109
|
if (height) {
|
|
@@ -112,7 +111,7 @@ export const bollingerBands = (main, index, size, stdDev, { height, range = [],
|
|
|
112
111
|
if (!Number.isNaN(lower) && !Number.isNaN(upper)) {
|
|
113
112
|
heightValue = ((upper - lower) / lower)
|
|
114
113
|
}
|
|
115
|
-
main.pushToMain({ index, key: `${
|
|
114
|
+
main.pushToMain({ index, key: `${prefix}_height${suffix}`, value: (decimals === null) ? heightValue : roundDecimalPlaces(heightValue, decimals) });
|
|
116
115
|
}
|
|
117
116
|
|
|
118
117
|
// Process each range property.
|
|
@@ -122,7 +121,7 @@ export const bollingerBands = (main, index, size, stdDev, { height, range = [],
|
|
|
122
121
|
if (!Number.isNaN(priceValue) && !Number.isNaN(lower) && !Number.isNaN(upper)) {
|
|
123
122
|
rangeValue = (priceValue - lower) / (upper - lower)
|
|
124
123
|
}
|
|
125
|
-
main.pushToMain({ index, key: `${
|
|
124
|
+
main.pushToMain({ index, key: `${prefix}_range_${rangeKey}${suffix}`, value: (decimals === null) ? rangeValue : roundDecimalPlaces(rangeValue, decimals) });
|
|
126
125
|
}
|
|
127
126
|
|
|
128
127
|
return true;
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
import { roundDecimalPlaces } from "../utilities/numberUtilities.js";
|
|
3
3
|
|
|
4
4
|
export const donchianChannels = (main, index, size, offset, options) => {
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
const {verticalOhlcv, instances, len, inputParams, arrayTypes, lag} = main
|
|
6
9
|
const { height: includeHeight, range: rangeKeys, decimals } = options
|
|
7
10
|
const indicatorKey = `${size}_${offset}`
|
|
8
11
|
|
|
@@ -19,9 +22,9 @@ export const donchianChannels = (main, index, size, offset, options) => {
|
|
|
19
22
|
const keys = ['upper', 'basis', 'lower'].map(n => `${prefix}_${n}`);
|
|
20
23
|
if (includeHeight) keys.push(`${prefix}_height`);
|
|
21
24
|
for (const rk of rangeKeys) {
|
|
22
|
-
if (!(rk in verticalOhlcv)
|
|
25
|
+
if (!(rk in verticalOhlcv)) {
|
|
23
26
|
throw new Error(
|
|
24
|
-
`Invalid range key "${rk}"
|
|
27
|
+
`Invalid range key "${rk}".`
|
|
25
28
|
);
|
|
26
29
|
}
|
|
27
30
|
keys.push(`${prefix}_range_${rk}`);
|
|
@@ -44,7 +47,6 @@ export const donchianChannels = (main, index, size, offset, options) => {
|
|
|
44
47
|
if (lag > 0) main.lag(keys, lag);
|
|
45
48
|
|
|
46
49
|
// mark price-based outputs
|
|
47
|
-
['upper','basis','lower'].forEach(n => priceBased.add(`${prefix}_${n}`));
|
|
48
50
|
keys.forEach(k => arrayTypes[k] = 'Float64Array');
|
|
49
51
|
}
|
|
50
52
|
|
|
@@ -4,12 +4,14 @@ const defaultTarget = 'close';
|
|
|
4
4
|
|
|
5
5
|
export const macd = (main, index, fast, slow, signal, {target, lag, precomputed}) => {
|
|
6
6
|
|
|
7
|
+
|
|
8
|
+
|
|
7
9
|
const { verticalOhlcv, instances } = main
|
|
8
10
|
const {instanceKey} = precomputed
|
|
9
11
|
|
|
10
12
|
// Initialization on the first index.
|
|
11
13
|
if (index === 0) {
|
|
12
|
-
const { inputParams,
|
|
14
|
+
const { inputParams, len , arrayTypes} = main
|
|
13
15
|
|
|
14
16
|
if (!verticalOhlcv.hasOwnProperty(target)) {
|
|
15
17
|
throw new Error(`Target property ${target} not found in verticalOhlcv for macd.`)
|
|
@@ -29,8 +31,6 @@ export const macd = (main, index, fast, slow, signal, {target, lag, precomputed}
|
|
|
29
31
|
const deaKey = target === defaultTarget ? `${displayPrefix}_dea` : `${displayPrefix}_dea_${target}`
|
|
30
32
|
const histogramKey = target === defaultTarget ? `${displayPrefix}_histogram` : `${displayPrefix}_histogram_${target}`
|
|
31
33
|
|
|
32
|
-
crossPairsList.push({ fast: diffKey, slow: deaKey, isDefault: true })
|
|
33
|
-
|
|
34
34
|
if (!instances.hasOwnProperty('macd')) {
|
|
35
35
|
instances.macd = {
|
|
36
36
|
numberOfIndicators,
|
|
@@ -50,10 +50,6 @@ export const macd = (main, index, fast, slow, signal, {target, lag, precomputed}
|
|
|
50
50
|
[histogramKey]: new Float64Array(len).fill(NaN),
|
|
51
51
|
})
|
|
52
52
|
|
|
53
|
-
[diffKey, deaKey, histogramKey].forEach(v => {
|
|
54
|
-
priceBased.add(v)
|
|
55
|
-
})
|
|
56
|
-
|
|
57
53
|
const keyNames = [diffKey, deaKey, histogramKey]
|
|
58
54
|
|
|
59
55
|
if(lag > 0)
|
|
@@ -4,59 +4,56 @@ import {FasterEMA, FasterSMA} from 'trading-signals';
|
|
|
4
4
|
const indicatorClasses = {
|
|
5
5
|
ema: FasterEMA,
|
|
6
6
|
sma: FasterSMA
|
|
7
|
-
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const movingAverages = (main, index, methodName, size, { target, lag }) => {
|
|
10
|
+
|
|
11
|
+
|
|
8
12
|
|
|
9
|
-
export const movingAverages = (main, index, indicatorName, size, { target, lag, precomputed }) => {
|
|
10
13
|
const { verticalOhlcv, instances } = main
|
|
11
|
-
const {
|
|
14
|
+
const suffix = (target !== 'close') ? `_${target}` : ''
|
|
15
|
+
const keyName = `${methodName}_${size}${suffix}`
|
|
12
16
|
|
|
13
17
|
if (index === 0) {
|
|
14
18
|
|
|
19
|
+
const {arrayTypes, len} = main
|
|
20
|
+
|
|
15
21
|
if (!verticalOhlcv.hasOwnProperty(target)) {
|
|
16
22
|
throw new Error(
|
|
17
|
-
`Target property ${target} not found in verticalOhlcv for ${
|
|
23
|
+
`Target property ${target} not found in verticalOhlcv for ${methodName}.`
|
|
18
24
|
);
|
|
19
25
|
}
|
|
26
|
+
|
|
27
|
+
// Create the main moving average instance.
|
|
28
|
+
instances[keyName] = new indicatorClasses[methodName](size)
|
|
29
|
+
|
|
30
|
+
verticalOhlcv[keyName] = new Float64Array(len).fill(NaN)
|
|
31
|
+
|
|
32
|
+
if(lag > 0)
|
|
33
|
+
{
|
|
34
|
+
main.lag([keyName], lag)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
arrayTypes[keyName] = 'Float64Array'
|
|
20
38
|
}
|
|
21
39
|
|
|
22
|
-
// Retrieve the current price value
|
|
40
|
+
// Retrieve the current price value
|
|
23
41
|
const value = verticalOhlcv[target][index]
|
|
24
42
|
const instance = instances[keyName]
|
|
25
43
|
|
|
26
44
|
// Update the moving average instance.
|
|
27
|
-
instance.update(value)
|
|
28
|
-
|
|
45
|
+
instance.update(value)
|
|
46
|
+
|
|
47
|
+
let currMa = NaN
|
|
48
|
+
|
|
29
49
|
try {
|
|
30
|
-
currMa = instance.getResult()
|
|
50
|
+
currMa = instance.getResult()
|
|
31
51
|
} catch (err) {
|
|
32
52
|
|
|
33
53
|
}
|
|
34
54
|
|
|
35
55
|
// Always push the MA value (even if NaN).
|
|
36
|
-
main.pushToMain({ index, key: keyName, value: currMa })
|
|
37
|
-
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export const precomputeMovingAverages = ({main, size, target, lag, methodName}) => {
|
|
42
|
-
|
|
43
|
-
const {instances, verticalOhlcv, priceBased, arrayTypes, len} = main
|
|
44
|
-
const suffix = (target !== 'close') ? `_${target}` : ''
|
|
45
|
-
const keyName = `${methodName}_${size}${suffix}`
|
|
46
|
-
|
|
47
|
-
// Create the main moving average instance.
|
|
48
|
-
instances[keyName] = new indicatorClasses[methodName](size)
|
|
56
|
+
main.pushToMain({ index, key: keyName, value: currMa })
|
|
49
57
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
priceBased.add(keyName)
|
|
53
|
-
|
|
54
|
-
if(lag > 0)
|
|
55
|
-
{
|
|
56
|
-
main.lag([keyName], lag)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
arrayTypes[keyName] = 'Float64Array'
|
|
60
|
-
|
|
61
|
-
return {keyName}
|
|
58
|
+
return true
|
|
62
59
|
}
|