ohlcv-indicators 2.1.9 → 2.2.1
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 +31 -34
- package/package.json +1 -1
- package/src/moving-averages/bollingerBands.js +78 -74
- package/src/moving-averages/donchianChannel.js +72 -64
- package/src/moving-averages/macd.js +17 -17
- package/src/moving-averages/movingAverages.js +77 -73
- package/src/moving-averages/relativeVolume.js +37 -55
- package/src/oscillators/rsi.js +56 -62
- package/src/studies/lag.js +4 -2
- package/src/utilities/dataParsingUtilities.js +3 -1
|
@@ -4,102 +4,106 @@ import { classifyBoll } from '../utilities/classification.js'
|
|
|
4
4
|
|
|
5
5
|
const indicatorClasses = {ema: FasterEMA, sma: FasterSMA}
|
|
6
6
|
|
|
7
|
-
export const movingAverages = (main, index, indicatorName, size, {target, diff}) => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
{
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
7
|
+
export const movingAverages = (main, index, indicatorName, size, { target, diff }) => {
|
|
8
|
+
const { verticalOhlcv, instances, priceBased, lastIndexReplace } = main;
|
|
9
|
+
let suffix =
|
|
10
|
+
typeof target === 'string' &&
|
|
11
|
+
verticalOhlcv.hasOwnProperty(target) &&
|
|
12
|
+
target !== 'close'
|
|
13
|
+
? `_${target}`
|
|
14
|
+
: '';
|
|
15
|
+
const keyName = `${indicatorName}_${size}${suffix}`;
|
|
16
|
+
const diffKeyName = `${indicatorName}_${size}_diff${suffix}`;
|
|
17
|
+
|
|
18
|
+
if (index === 0) {
|
|
19
|
+
const { nullArray } = main;
|
|
20
|
+
|
|
21
|
+
if (!verticalOhlcv.hasOwnProperty(target)) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
`Target property ${target} not found in verticalOhlcv for ${indicatorName}.`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
// Create the main moving average instance.
|
|
28
|
+
instances[keyName] = {
|
|
29
|
+
maInstance: new indicatorClasses[indicatorName](size)
|
|
30
|
+
};
|
|
26
31
|
|
|
27
|
-
|
|
32
|
+
verticalOhlcv[keyName] = [...nullArray];
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
// If diff configuration is provided, create the diff indicator.
|
|
35
|
+
if (diff !== null) {
|
|
36
|
+
instances[keyName].diffInstance = new FasterBollingerBands(
|
|
37
|
+
diff.size,
|
|
38
|
+
diff.stdDev
|
|
39
|
+
);
|
|
32
40
|
|
|
33
|
-
|
|
41
|
+
const lagDiffArr = [];
|
|
34
42
|
|
|
35
|
-
|
|
36
|
-
{
|
|
37
|
-
verticalOhlcv[`${diffKeyName}_${targetKey}`] = [...nullArray]
|
|
43
|
+
for (const targetKey of diff.targets) {
|
|
44
|
+
verticalOhlcv[`${diffKeyName}_${targetKey}`] = [...nullArray];
|
|
38
45
|
|
|
39
|
-
|
|
40
|
-
{
|
|
41
|
-
lagDiffArr.push(`${diffKeyName}_${targetKey}`)
|
|
42
|
-
}
|
|
46
|
+
if (diff.lag > 0) {
|
|
47
|
+
lagDiffArr.push(`${diffKeyName}_${targetKey}`);
|
|
43
48
|
}
|
|
49
|
+
}
|
|
44
50
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
console.log(lagDiffArr, diff.lag)
|
|
48
|
-
main.lag(lagDiffArr, diff.lag)
|
|
49
|
-
}
|
|
51
|
+
if (diff.lag > 0) {
|
|
52
|
+
main.lag(lagDiffArr, diff.lag);
|
|
50
53
|
}
|
|
54
|
+
}
|
|
51
55
|
|
|
52
|
-
|
|
56
|
+
priceBased.push(keyName);
|
|
53
57
|
}
|
|
54
58
|
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
maInstance
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
{
|
|
65
|
-
|
|
59
|
+
// Retrieve the current price value.
|
|
60
|
+
const value = verticalOhlcv[target][index];
|
|
61
|
+
const { maInstance, diffInstance } = instances[keyName];
|
|
62
|
+
|
|
63
|
+
// Update the moving average instance.
|
|
64
|
+
maInstance.update(value, lastIndexReplace);
|
|
65
|
+
let currMa = null;
|
|
66
|
+
try {
|
|
67
|
+
currMa = maInstance.getResult();
|
|
68
|
+
} catch (err) {
|
|
69
|
+
currMa = null;
|
|
66
70
|
}
|
|
67
71
|
|
|
68
|
-
if
|
|
69
|
-
{
|
|
70
|
-
main.pushToMain({index, key: keyName, value: currMa})
|
|
71
|
-
|
|
72
|
-
if(diff === null)
|
|
73
|
-
{
|
|
74
|
-
return true
|
|
75
|
-
}
|
|
72
|
+
// Always push the MA value (even if null).
|
|
73
|
+
main.pushToMain({ index, key: keyName, value: currMa });
|
|
76
74
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
75
|
+
// Process the diff values if a diff is requested.
|
|
76
|
+
if (diff !== null) {
|
|
77
|
+
for (const targetKey of diff.targets) {
|
|
78
|
+
// Compute diffValue only if we have a valid MA value.
|
|
79
|
+
let diffValue = null;
|
|
80
|
+
if (currMa !== null && typeof currMa === 'number') {
|
|
81
|
+
diffValue = target === targetKey ? value - currMa : verticalOhlcv[targetKey][index];
|
|
82
|
+
}
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
// Update the diff indicator only if diffValue is a valid number.
|
|
85
|
+
if (diffValue !== null && typeof diffValue === 'number') {
|
|
86
|
+
diffInstance.update(Math.abs(diffValue), lastIndexReplace);
|
|
87
|
+
}
|
|
85
88
|
|
|
89
|
+
let diffBoll = null;
|
|
86
90
|
try {
|
|
87
|
-
diffBoll = diffInstance.getResult()
|
|
88
|
-
} catch(err)
|
|
89
|
-
|
|
90
|
-
diffBoll = null
|
|
91
|
+
diffBoll = diffInstance.getResult();
|
|
92
|
+
} catch (err) {
|
|
93
|
+
diffBoll = null;
|
|
91
94
|
}
|
|
92
95
|
|
|
93
|
-
|
|
94
|
-
|
|
96
|
+
const classified = classifyBoll(diffValue, diffBoll, diff.scale);
|
|
97
|
+
main.pushToMain({
|
|
98
|
+
index,
|
|
99
|
+
key: `${diffKeyName}_${targetKey}`,
|
|
100
|
+
value: classified
|
|
101
|
+
});
|
|
95
102
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
103
|
}
|
|
100
104
|
|
|
101
|
-
|
|
102
|
-
|
|
105
|
+
return true;
|
|
106
|
+
};
|
|
103
107
|
|
|
104
108
|
|
|
105
109
|
export const getMovingAveragesParams = (indicatorName, size, options, validMagnitudeValues) => {
|
|
@@ -1,61 +1,43 @@
|
|
|
1
|
-
import {FasterSMA} from 'trading-signals';
|
|
1
|
+
import { FasterSMA } from 'trading-signals';
|
|
2
2
|
import { calcMagnitude } from '../utilities/numberUtilities.js';
|
|
3
3
|
|
|
4
|
-
export const relativeVolume = (main, index, size = 10, {scale}) => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const {prevRelativeVolumeSma} = instances[key]
|
|
39
|
-
|
|
40
|
-
let currRelativeVolume = null
|
|
41
|
-
|
|
42
|
-
if(smaValue !== null && prevRelativeVolumeSma !== null && typeof smaValue !== 'undefined' && prevRelativeVolumeSma !== 'undefined')
|
|
43
|
-
{
|
|
44
|
-
currRelativeVolume = value / prevRelativeVolumeSma
|
|
45
|
-
|
|
46
|
-
if(scale)
|
|
47
|
-
{
|
|
48
|
-
currRelativeVolume = calcMagnitude(currRelativeVolume, scale)
|
|
49
|
-
}
|
|
4
|
+
export const relativeVolume = (main, index, size = 10, { scale }) => {
|
|
5
|
+
const key = `relative_volume_${size}`;
|
|
6
|
+
const { instances, verticalOhlcv, lastIndexReplace } = main;
|
|
7
|
+
|
|
8
|
+
if (index === 0) {
|
|
9
|
+
const { nullArray } = main;
|
|
10
|
+
instances[key] = {
|
|
11
|
+
instance: new FasterSMA(size),
|
|
12
|
+
prevRelativeVolumeSma: null
|
|
13
|
+
};
|
|
14
|
+
verticalOhlcv[key] = [...nullArray];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const value = verticalOhlcv.volume[index];
|
|
18
|
+
const smaInstance = instances[key].instance;
|
|
19
|
+
smaInstance.update(value, lastIndexReplace);
|
|
20
|
+
|
|
21
|
+
let smaValue = null;
|
|
22
|
+
try {
|
|
23
|
+
smaValue = smaInstance.getResult();
|
|
24
|
+
} catch (err) {
|
|
25
|
+
smaValue = null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const prevSma = instances[key].prevRelativeVolumeSma;
|
|
29
|
+
let currRelativeVolume = null;
|
|
30
|
+
|
|
31
|
+
// Only calculate relative volume if both current SMA and previous SMA are valid numbers and prevSma is not zero.
|
|
32
|
+
if (typeof smaValue === 'number' && typeof prevSma === 'number' && prevSma !== 0) {
|
|
33
|
+
currRelativeVolume = value / prevSma;
|
|
34
|
+
if (scale) {
|
|
35
|
+
currRelativeVolume = calcMagnitude(currRelativeVolume, scale);
|
|
50
36
|
}
|
|
37
|
+
}
|
|
51
38
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
key,
|
|
55
|
-
value: currRelativeVolume
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
instances[key].prevRelativeVolumeSma = smaValue
|
|
39
|
+
main.pushToMain({ index, key, value: currRelativeVolume });
|
|
40
|
+
instances[key].prevRelativeVolumeSma = smaValue;
|
|
59
41
|
|
|
60
|
-
|
|
42
|
+
return true;
|
|
61
43
|
};
|
package/src/oscillators/rsi.js
CHANGED
|
@@ -1,77 +1,71 @@
|
|
|
1
|
-
import {FasterRSI} from 'trading-signals';
|
|
1
|
+
import { FasterRSI } from 'trading-signals';
|
|
2
2
|
import { FasterSMA } from 'trading-signals';
|
|
3
3
|
import { calcMagnitude } from '../utilities/numberUtilities.js';
|
|
4
4
|
|
|
5
|
-
const defaultTarget = 'close'
|
|
5
|
+
const defaultTarget = 'close';
|
|
6
6
|
|
|
7
|
-
export const rsi = (main, index, size, {scale, target}) => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const rsiSmaKey = `rsi_sma_${size}${suffix}`
|
|
7
|
+
export const rsi = (main, index, size, { scale, target }) => {
|
|
8
|
+
const { verticalOhlcv, instances, lastIndexReplace } = main;
|
|
9
|
+
const suffix = target === defaultTarget ? '' : `_${target}`;
|
|
10
|
+
const rsiKey = `rsi_${size}${suffix}`;
|
|
11
|
+
const rsiSmaKey = `rsi_sma_${size}${suffix}`;
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
// Initialization on the first index.
|
|
14
|
+
if (index === 0) {
|
|
15
|
+
const { crossPairsList, nullArray } = main;
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
17
|
+
if (!verticalOhlcv.hasOwnProperty(target)) {
|
|
18
|
+
throw new Error(`Target property ${target} not found in verticalOhlcv for rsi.`);
|
|
19
|
+
}
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
crossPairsList.push({ fast: rsiKey, slow: rsiSmaKey, isDefault: true });
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
Object.assign(instances, {
|
|
24
|
+
[rsiKey]: new FasterRSI(size),
|
|
25
|
+
[rsiSmaKey]: new FasterSMA(size)
|
|
26
|
+
});
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
Object.assign(verticalOhlcv, {
|
|
29
|
+
[rsiKey]: [...nullArray],
|
|
30
|
+
[rsiSmaKey]: [...nullArray],
|
|
31
|
+
});
|
|
32
|
+
}
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
const value = verticalOhlcv[target][index];
|
|
35
|
+
let currentRsi = null;
|
|
36
|
+
let smoothedRsi = null;
|
|
39
37
|
|
|
40
|
-
|
|
38
|
+
// Update the RSI indicator.
|
|
39
|
+
instances[rsiKey].update(value, lastIndexReplace);
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if(currentRsi)
|
|
50
|
-
{
|
|
51
|
-
if(scale)
|
|
52
|
-
{
|
|
53
|
-
currentRsi = calcMagnitude(currentRsi, scale)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
main.pushToMain({index, key: rsiKey, value: currentRsi})
|
|
57
|
-
instances[rsiSmaKey].update(currentRsi, lastIndexReplace)
|
|
58
|
-
}
|
|
41
|
+
try {
|
|
42
|
+
currentRsi = instances[rsiKey].getResult();
|
|
43
|
+
} catch (err) {
|
|
44
|
+
currentRsi = null;
|
|
45
|
+
}
|
|
59
46
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
} catch(err)
|
|
64
|
-
{
|
|
65
|
-
smoothedRsi = null
|
|
66
|
-
}
|
|
47
|
+
if (currentRsi !== null && typeof currentRsi === 'number' && scale) {
|
|
48
|
+
currentRsi = calcMagnitude(currentRsi, scale);
|
|
49
|
+
}
|
|
67
50
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if(scale)
|
|
71
|
-
{
|
|
72
|
-
smoothedRsi = calcMagnitude(smoothedRsi, scale)
|
|
73
|
-
}
|
|
51
|
+
// Always push the RSI value, using null as a fallback.
|
|
52
|
+
main.pushToMain({ index, key: rsiKey, value: currentRsi });
|
|
74
53
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
54
|
+
// Update the SMA indicator only if a valid RSI value is available.
|
|
55
|
+
if (currentRsi !== null && typeof currentRsi === 'number') {
|
|
56
|
+
instances[rsiSmaKey].update(currentRsi, lastIndexReplace);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
smoothedRsi = instances[rsiSmaKey].getResult();
|
|
61
|
+
} catch (err) {
|
|
62
|
+
smoothedRsi = null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (smoothedRsi !== null && typeof smoothedRsi === 'number' && scale) {
|
|
66
|
+
smoothedRsi = calcMagnitude(smoothedRsi, scale);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Always push the smoothed RSI value.
|
|
70
|
+
main.pushToMain({ index, key: rsiSmaKey, value: smoothedRsi });
|
|
71
|
+
};
|
package/src/studies/lag.js
CHANGED
|
@@ -49,9 +49,11 @@ export const lag = (main, index) => {
|
|
|
49
49
|
const key = `${colKey}_lag_${lag}`;
|
|
50
50
|
const laggedIndex = index - lag;
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
const value = (laggedIndex <= 0 || typeof currentColumn[laggedIndex] === 'undefined')
|
|
53
|
+
? null
|
|
54
|
+
: currentColumn[laggedIndex]
|
|
53
55
|
|
|
54
|
-
main.pushToMain({index, key, value
|
|
56
|
+
main.pushToMain({index, key, value})
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
59
|
}
|
|
@@ -14,6 +14,8 @@ export const verticalToHorizontal = (
|
|
|
14
14
|
const len = endIndex - startIndex;
|
|
15
15
|
|
|
16
16
|
const result = new Array(len);
|
|
17
|
+
|
|
18
|
+
console.log({skipNull, invalidValueIndex, len})
|
|
17
19
|
|
|
18
20
|
for (let i = startIndex; i < endIndex; i++) {
|
|
19
21
|
const row = {};
|
|
@@ -21,7 +23,7 @@ export const verticalToHorizontal = (
|
|
|
21
23
|
const key = keys[j];
|
|
22
24
|
const value = obj[key][i];
|
|
23
25
|
|
|
24
|
-
if(value === null ||
|
|
26
|
+
if(value === null || typeof value === 'undefined')
|
|
25
27
|
{
|
|
26
28
|
row[key] = null
|
|
27
29
|
}
|