ohlcv-indicators 2.0.4 → 2.0.6
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 +56 -45
- package/package.json +1 -1
- package/src/moving-averages/bollingerBands.js +115 -65
- package/src/moving-averages/donchianChannel.js +98 -29
- package/src/moving-averages/ema.js +1 -1
- package/src/moving-averages/macd.js +41 -11
- package/src/moving-averages/relativeVolume.js +16 -12
- package/src/moving-averages/sma.js +1 -1
- package/src/oscillators/rsi.js +2 -2
- package/src/oscillators/volumeOscillator.js +7 -5
- package/src/studies/candleStudies.js +35 -26
- package/src/studies/dateTime.js +8 -9
- package/src/studies/findCrosses.js +1 -1
- package/src/studies/lag.js +14 -1
- package/src/studies/priceVariations.js +5 -4
- package/src/utilities/parsing-utilities.js +157 -93
|
@@ -1,82 +1,132 @@
|
|
|
1
|
-
import {FasterBollingerBands, FasterSMA} from 'trading-signals';
|
|
1
|
+
import { FasterBollingerBands, FasterSMA } from 'trading-signals';
|
|
2
2
|
import { classifySize } from '../utilities/classification.js';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export const bollingerBands = (main, index, size, times, options) => {
|
|
7
|
-
|
|
8
|
-
const {height, range, target} = options
|
|
9
|
-
let suffix = (target === defaultTarget) ? '' : `_${target}`
|
|
10
|
-
|
|
11
|
-
if (index === 0) {
|
|
12
|
-
|
|
13
|
-
if(!main.verticalOhlcv.hasOwnProperty(target))
|
|
14
|
-
{
|
|
15
|
-
throw new Error(`bollingerBands could not find target "${target}"`)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
Object.assign(main.instances, {
|
|
19
|
-
[`bollinger_bands${suffix}`]: {
|
|
20
|
-
instance: new FasterBollingerBands(size, times),
|
|
21
|
-
heightInstance: height ? new FasterSMA(size) : null
|
|
22
|
-
}
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
Object.assign(main.verticalOhlcv, {
|
|
26
|
-
[`bollinger_bands_upper${suffix}`]: [...main.nullArray],
|
|
27
|
-
[`bollinger_bands_middle${suffix}`]: [...main.nullArray],
|
|
28
|
-
[`bollinger_bands_lower${suffix}`]: [...main.nullArray],
|
|
29
|
-
...(height && {
|
|
30
|
-
[`bollinger_bands_height${suffix}`]: [...main.nullArray]
|
|
31
|
-
}),
|
|
32
|
-
...(range && {
|
|
33
|
-
[`bollinger_bands_range${suffix}`]: [...main.nullArray],
|
|
34
|
-
})
|
|
35
|
-
})
|
|
36
|
-
}
|
|
4
|
+
const defaultTarget = 'close';
|
|
37
5
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
6
|
+
export const bollingerBands = (main, index, size, stdDev, options) => {
|
|
7
|
+
// Destructure options; ensure range and zScore default to empty arrays if not provided.
|
|
8
|
+
const { height, range = [], zScore = [], target } = options;
|
|
9
|
+
const suffix = target === defaultTarget ? '' : `_${target}`;
|
|
10
|
+
const indicatorKey = `${size}_${stdDev}${suffix}`;
|
|
11
|
+
let prefix; // Will be computed during initialization
|
|
42
12
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
//do nothing
|
|
13
|
+
// Initialize indicator instances and output arrays on the first call.
|
|
14
|
+
if (index === 0) {
|
|
15
|
+
if (!(target in main.verticalOhlcv)) {
|
|
16
|
+
throw new Error(`bollingerBands could not find target "${target}"`);
|
|
48
17
|
}
|
|
49
18
|
|
|
50
|
-
|
|
19
|
+
const numberOfIndicators = main.inputParams.filter(o => o.key === 'bollingerBands').length;
|
|
20
|
+
// Choose a prefix based on the number of indicators.
|
|
21
|
+
prefix = numberOfIndicators > 1
|
|
22
|
+
? `bollinger_bands_${indicatorKey}`
|
|
23
|
+
: `bollinger_bands${suffix}`;
|
|
24
|
+
|
|
25
|
+
// Ensure the container exists.
|
|
26
|
+
if (!main.instances.bollinger_bands) {
|
|
27
|
+
main.instances.bollinger_bands = {
|
|
28
|
+
numberOfIndicators,
|
|
29
|
+
settings: {}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
51
32
|
|
|
52
|
-
|
|
53
|
-
main.
|
|
54
|
-
|
|
55
|
-
|
|
33
|
+
// Create the Bollinger Bands (and optional height) indicator instance.
|
|
34
|
+
main.instances.bollinger_bands.settings[indicatorKey] = {
|
|
35
|
+
instance: new FasterBollingerBands(size, stdDev),
|
|
36
|
+
heightInstance: height >= size ? new FasterSMA(height) : null
|
|
37
|
+
};
|
|
56
38
|
|
|
57
|
-
|
|
39
|
+
// Build the base output arrays.
|
|
40
|
+
const ohlcvSetup = {
|
|
41
|
+
[`${prefix}_upper`]: [...main.nullArray],
|
|
42
|
+
[`${prefix}_middle`]: [...main.nullArray],
|
|
43
|
+
[`${prefix}_lower`]: [...main.nullArray],
|
|
44
|
+
...(height >= size && { [`${prefix}_height`]: [...main.nullArray] })
|
|
45
|
+
};
|
|
58
46
|
|
|
59
|
-
let rangeValue = null;
|
|
60
|
-
let heightValue = null;
|
|
61
|
-
let heightMean;
|
|
62
47
|
|
|
63
|
-
rangeValue = (value - lower) / (upper - lower);
|
|
64
|
-
heightValue = upper - lower;
|
|
65
48
|
|
|
66
|
-
heightInstance.update(heightValue, main.lastIndexReplace);
|
|
67
49
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
50
|
+
// Set up additional arrays for each range property.
|
|
51
|
+
for (const rangeKey of range) {
|
|
52
|
+
if (!(rangeKey in main.verticalOhlcv) || !main.priceBased.includes(rangeKey)) {
|
|
53
|
+
throw new Error(`Invalid range item value "${rangeKey}" property for bollingerBands. Only price based key names are accepted:\n${JSON.stringify(main.priceBased)}`);
|
|
54
|
+
}
|
|
55
|
+
ohlcvSetup[`${prefix}_range_${rangeKey}`] = [...main.nullArray];
|
|
72
56
|
}
|
|
73
57
|
|
|
74
|
-
|
|
58
|
+
// Set up additional arrays for each zScore property.
|
|
59
|
+
for (const zScoreKey of zScore) {
|
|
60
|
+
if (!(zScoreKey in main.verticalOhlcv) || !main.priceBased.includes(zScoreKey)) {
|
|
61
|
+
throw new Error(`Invalid zScore item value "${zScoreKey}" for bollingerBands. Only price based key names are accepted:\n${JSON.stringify(main.priceBased)}`);
|
|
62
|
+
}
|
|
63
|
+
ohlcvSetup[`${prefix}_zscore_${zScoreKey}`] = [...main.nullArray];
|
|
64
|
+
}
|
|
75
65
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
66
|
+
// Merge the new arrays into verticalOhlcv.
|
|
67
|
+
Object.assign(main.verticalOhlcv, ohlcvSetup);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// For subsequent calls, if prefix wasn’t computed (i.e. index > 0), derive it.
|
|
71
|
+
if (!prefix) {
|
|
72
|
+
const num = main.instances.bollinger_bands.numberOfIndicators;
|
|
73
|
+
prefix = num > 1 ? `bollinger_bands_${indicatorKey}` : `bollinger_bands${suffix}`;
|
|
74
|
+
}
|
|
75
|
+
// Use the same prefix for output keys.
|
|
76
|
+
const subPrefix = prefix;
|
|
77
|
+
|
|
78
|
+
// Retrieve the indicator instance(s) and update with the current value.
|
|
79
|
+
const { instance, heightInstance } = main.instances.bollinger_bands.settings[indicatorKey];
|
|
80
|
+
const value = main.verticalOhlcv[target][index];
|
|
81
|
+
instance.update(value, main.lastIndexReplace);
|
|
82
|
+
|
|
83
|
+
let result;
|
|
84
|
+
try {
|
|
85
|
+
result = instance.getResult();
|
|
86
|
+
} catch (err) {
|
|
87
|
+
// If the result is not available yet, simply exit.
|
|
88
|
+
}
|
|
89
|
+
if (!result) return true;
|
|
90
|
+
|
|
91
|
+
const { upper, middle, lower } = result;
|
|
92
|
+
main.pushToMain({index, key: `${subPrefix}_upper`, value: upper})
|
|
93
|
+
main.pushToMain({index, key: `${subPrefix}_middle`, value: middle})
|
|
94
|
+
main.pushToMain({index, key: `${subPrefix}_lower`, value: lower})
|
|
95
|
+
|
|
96
|
+
// Exit early if neither height nor range are requested.
|
|
97
|
+
if (!height && !range) return true;
|
|
98
|
+
|
|
99
|
+
// Process height if a height instance exists.
|
|
100
|
+
if (heightInstance) {
|
|
101
|
+
const heightValue = upper - lower;
|
|
102
|
+
heightInstance.update(heightValue, main.lastIndexReplace);
|
|
103
|
+
let heightMean;
|
|
104
|
+
try {
|
|
105
|
+
heightMean = heightInstance.getResult();
|
|
106
|
+
} catch (err) {
|
|
107
|
+
// Swallow error if the height result is not ready.
|
|
108
|
+
}
|
|
109
|
+
if (heightMean) {
|
|
110
|
+
main.pushToMain({index, key: `${subPrefix}_height`, value: classifySize(heightValue, heightMean, 0.5, 7)})
|
|
79
111
|
}
|
|
80
|
-
|
|
81
|
-
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Process each range property.
|
|
115
|
+
for (const rangeKey of range) {
|
|
116
|
+
const rangeValue = (main.verticalOhlcv[rangeKey][index] - lower) / (upper - lower);
|
|
117
|
+
main.pushToMain({index, key: `${subPrefix}_range_${rangeKey}`, value: rangeValue})
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Process each zScore property.
|
|
121
|
+
for (const zScoreKey of zScore) {
|
|
122
|
+
const denominator = upper - lower;
|
|
123
|
+
const zScoreValue = denominator !== 0
|
|
124
|
+
? (2 * stdDev * (main.verticalOhlcv[zScoreKey][index] - middle)) / denominator
|
|
125
|
+
: 0;
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
main.pushToMain({index, key: `${subPrefix}_zscore_${zScoreKey}`, value: zScoreValue})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return true;
|
|
82
132
|
};
|
|
@@ -1,43 +1,112 @@
|
|
|
1
|
-
|
|
1
|
+
import { FasterSMA } from 'trading-signals';
|
|
2
|
+
import { classifySize } from '../utilities/classification.js';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
export const donchianChannels = (main, index, size, offset, options) => {
|
|
5
|
+
const { height, range } = options;
|
|
6
|
+
const indicatorKey = `${size}_${offset}`;
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
// Initialization: create output arrays and indicator instance on the first call.
|
|
9
|
+
if (index === 0) {
|
|
10
|
+
const numberOfIndicators = main.inputParams.filter(o => o.key === 'donchianChannels').length;
|
|
11
|
+
const prefix = numberOfIndicators > 1 ? `donchian_channel_${indicatorKey}` : 'donchian_channel';
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
Object.assign(main.verticalOhlcv, {
|
|
14
|
+
[`${prefix}_upper`]: [...main.nullArray],
|
|
15
|
+
[`${prefix}_basis`]: [...main.nullArray],
|
|
16
|
+
[`${prefix}_lower`]: [...main.nullArray],
|
|
17
|
+
...(height && { [`${prefix}_height`]: [...main.nullArray] }),
|
|
18
|
+
...(range && { [`${prefix}_range`]: [...main.nullArray] })
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
if (!main.instances.hasOwnProperty('donchian_channel')) {
|
|
22
|
+
main.instances.donchian_channel = { numberOfIndicators, settings: {} };
|
|
14
23
|
}
|
|
15
24
|
|
|
25
|
+
// In addition to your heightInstance, also initialize deques for highs and lows.
|
|
26
|
+
main.instances.donchian_channel.settings[indicatorKey] = {
|
|
27
|
+
heightInstance: height
|
|
28
|
+
? new FasterSMA(typeof height === 'number' && height > 0 ? height : size)
|
|
29
|
+
: null,
|
|
30
|
+
maxDeque: [], // will hold indices for highs in descending order
|
|
31
|
+
minDeque: [] // will hold indices for lows in ascending order
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const numberOfIndicators = main.instances.donchian_channel.numberOfIndicators;
|
|
36
|
+
const subPrefix = numberOfIndicators > 1 ? `donchian_channel_${indicatorKey}` : 'donchian_channel';
|
|
37
|
+
const state = main.instances.donchian_channel.settings[indicatorKey];
|
|
38
|
+
const { heightInstance, maxDeque, minDeque } = state;
|
|
39
|
+
|
|
40
|
+
// Compute the “current bar index” for the window.
|
|
41
|
+
// Note: We use index - offset so that the window is built on the proper part of the array.
|
|
42
|
+
const currentBarIdx = index - offset;
|
|
43
|
+
const endIdx = currentBarIdx + 1; // currentBarIdx is inclusive
|
|
44
|
+
const startIdx = endIdx - size; // window: [startIdx, endIdx)
|
|
45
|
+
|
|
46
|
+
// If the window is not fully available, exit early.
|
|
47
|
+
if (startIdx < 0 || endIdx > main.len) return true;
|
|
16
48
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const startIdx = endIdx - size; // start index for slicing
|
|
49
|
+
const highs = main.verticalOhlcv.high;
|
|
50
|
+
const lows = main.verticalOhlcv.low;
|
|
20
51
|
|
|
21
|
-
|
|
22
|
-
|
|
52
|
+
// **Update the maximum deque:**
|
|
53
|
+
// Remove indices that are out of the current window.
|
|
54
|
+
while (maxDeque.length && maxDeque[0] < startIdx) {
|
|
55
|
+
maxDeque.shift();
|
|
56
|
+
}
|
|
57
|
+
// Remove indices whose corresponding high value is less than or equal to the current high.
|
|
58
|
+
while (maxDeque.length && highs[maxDeque[maxDeque.length - 1]] <= highs[currentBarIdx]) {
|
|
59
|
+
maxDeque.pop();
|
|
60
|
+
}
|
|
61
|
+
// Add the current index.
|
|
62
|
+
maxDeque.push(currentBarIdx);
|
|
23
63
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
64
|
+
// **Update the minimum deque:**
|
|
65
|
+
// Remove indices that are out of the current window.
|
|
66
|
+
while (minDeque.length && minDeque[0] < startIdx) {
|
|
67
|
+
minDeque.shift();
|
|
68
|
+
}
|
|
69
|
+
// Remove indices whose corresponding low value is greater than or equal to the current low.
|
|
70
|
+
while (minDeque.length && lows[minDeque[minDeque.length - 1]] >= lows[currentBarIdx]) {
|
|
71
|
+
minDeque.pop();
|
|
72
|
+
}
|
|
73
|
+
// Add the current index.
|
|
74
|
+
minDeque.push(currentBarIdx);
|
|
27
75
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
76
|
+
// Now the maximum high is at the front of maxDeque and the minimum low is at the front of minDeque.
|
|
77
|
+
const upper = highs[maxDeque[0]];
|
|
78
|
+
const lower = lows[minDeque[0]];
|
|
79
|
+
const basis = (upper + lower) / 2;
|
|
80
|
+
const close = main.verticalOhlcv.close[index];
|
|
81
|
+
const rangeValue = (close - lower) / (upper - lower);
|
|
82
|
+
const heightValue = upper - lower;
|
|
32
83
|
|
|
33
|
-
|
|
34
|
-
|
|
84
|
+
// Update the height indicator if requested.
|
|
85
|
+
if (height && heightInstance) {
|
|
86
|
+
heightInstance.update(heightValue, main.lastIndexReplace);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Update the computed values in the output arrays.
|
|
90
|
+
|
|
91
|
+
main.pushToMain({index, key: `${subPrefix}_upper`, value: upper})
|
|
92
|
+
main.pushToMain({index, key: `${subPrefix}_basis`, value: basis})
|
|
93
|
+
main.pushToMain({index, key: `${subPrefix}_lower`, value: lower})
|
|
94
|
+
|
|
95
|
+
if (range) {
|
|
96
|
+
main.pushToMain({index, key: `${subPrefix}_range`, value: rangeValue})
|
|
97
|
+
}
|
|
98
|
+
if (height && heightInstance) {
|
|
99
|
+
let heightMean;
|
|
100
|
+
try {
|
|
101
|
+
heightMean = heightInstance.getResult();
|
|
102
|
+
} catch (err) {
|
|
103
|
+
// Ignore errors if the result isn't ready.
|
|
104
|
+
}
|
|
105
|
+
if (heightMean) {
|
|
35
106
|
|
|
36
|
-
|
|
37
|
-
main.verticalOhlcv['donchian_channel_upper'][placementIndex] = upper;
|
|
38
|
-
main.verticalOhlcv['donchian_channel_basis'][placementIndex] = basis;
|
|
39
|
-
main.verticalOhlcv['donchian_channel_lower'][placementIndex] = lower;
|
|
107
|
+
main.pushToMain({index, key: `${subPrefix}_height`, value: classifySize(heightValue, heightMean, 1.5)})
|
|
40
108
|
}
|
|
109
|
+
}
|
|
41
110
|
|
|
42
|
-
|
|
111
|
+
return true;
|
|
43
112
|
};
|
|
@@ -1,27 +1,56 @@
|
|
|
1
1
|
import {FasterEMA, FasterMACD} from 'trading-signals';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const defaultTarget = 'close'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
export const macd = (main, index, fast, slow, signal, options) => {
|
|
6
|
+
|
|
7
|
+
const {target} = options
|
|
8
|
+
const suffix = target === defaultTarget ? '' : `_${target}`;
|
|
9
|
+
const indicatorKey = `${fast}_${slow}_${signal}${suffix}`;
|
|
6
10
|
|
|
7
11
|
if (index === 0) {
|
|
8
12
|
|
|
9
|
-
main.
|
|
13
|
+
if(!main.verticalOhlcv.hasOwnProperty(target))
|
|
14
|
+
{
|
|
15
|
+
throw new Error(`Target property ${target} not found in verticalOhlcv for macd.`)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const numberOfIndicators = main.inputParams.filter(o => o.key === 'macd').length;
|
|
19
|
+
const prefix =
|
|
20
|
+
numberOfIndicators > 1
|
|
21
|
+
? `macd_${indicatorKey}`
|
|
22
|
+
: `macd${suffix}`;
|
|
23
|
+
|
|
24
|
+
main.crossPairsList.push({fast: `${prefix}_diff`, slow: `${prefix}_dea`, isDefault: true});
|
|
25
|
+
|
|
26
|
+
if(!main.instances.hasOwnProperty('macd'))
|
|
27
|
+
{
|
|
28
|
+
main.instances.macd = {
|
|
29
|
+
numberOfIndicators,
|
|
30
|
+
settings: {}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
10
33
|
|
|
11
|
-
main.instances[
|
|
34
|
+
main.instances.macd.settings[indicatorKey] = new FasterMACD(
|
|
12
35
|
new FasterEMA(fast),
|
|
13
36
|
new FasterEMA(slow),
|
|
14
37
|
new FasterEMA(signal)
|
|
15
38
|
);
|
|
16
39
|
|
|
17
40
|
Object.assign(main.verticalOhlcv, {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
41
|
+
[`${prefix}_diff`]: [...main.nullArray],
|
|
42
|
+
[`${prefix}_dea`]: [...main.nullArray],
|
|
43
|
+
[`${prefix}_histogram`]: [...main.nullArray],
|
|
21
44
|
})
|
|
22
45
|
}
|
|
23
46
|
|
|
24
|
-
|
|
47
|
+
|
|
48
|
+
const subPrefix = main.instances.macd.numberOfIndicators > 1
|
|
49
|
+
? `macd_${indicatorKey}`
|
|
50
|
+
: `macd${suffix}`;
|
|
51
|
+
|
|
52
|
+
const macdInstance = main.instances.macd.settings[indicatorKey];
|
|
53
|
+
const value = main.verticalOhlcv[target][index]
|
|
25
54
|
macdInstance.update(value, main.lastIndexReplace);
|
|
26
55
|
|
|
27
56
|
let macdResult;
|
|
@@ -34,9 +63,10 @@ export const macd = (main, index, fast, slow, signal) => {
|
|
|
34
63
|
|
|
35
64
|
|
|
36
65
|
if (macdResult) {
|
|
37
|
-
|
|
38
|
-
main.
|
|
39
|
-
main.
|
|
66
|
+
|
|
67
|
+
main.pushToMain({index, key: `${subPrefix}_diff`, value: macdResult.macd})
|
|
68
|
+
main.pushToMain({index, key: `${subPrefix}_dea`, value: macdResult.signal})
|
|
69
|
+
main.pushToMain({index, key: `${subPrefix}_histogram`, value: macdResult.histogram})
|
|
40
70
|
}
|
|
41
71
|
|
|
42
72
|
return true;
|
|
@@ -2,39 +2,43 @@ import {FasterSMA} from 'trading-signals';
|
|
|
2
2
|
|
|
3
3
|
export const relativeVolume = (main, index, size = 10) => {
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const key = `relative_volume_${size}`
|
|
6
6
|
|
|
7
7
|
if (index === 0) {
|
|
8
|
-
main.instances[
|
|
8
|
+
main.instances[key] = {
|
|
9
9
|
instance: new FasterSMA(size),
|
|
10
10
|
prevRelativeVolumeSma: null
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
Object.assign(main.verticalOhlcv, {
|
|
14
|
-
[
|
|
14
|
+
[key]: [...main.nullArray],
|
|
15
15
|
})
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
|
|
19
|
+
const value = main.verticalOhlcv.volume[index]
|
|
20
|
+
|
|
21
|
+
const smaInstance = main.instances[key].instance
|
|
19
22
|
smaInstance.update(value, main.lastIndexReplace)
|
|
20
23
|
|
|
21
24
|
let smaValue;
|
|
22
25
|
try {
|
|
23
26
|
smaValue = smaInstance.getResult()
|
|
27
|
+
|
|
28
|
+
|
|
24
29
|
} catch (err) {
|
|
25
30
|
//do nothing
|
|
26
31
|
}
|
|
27
32
|
|
|
28
|
-
const {prevRelativeVolumeSma} = main.instances[
|
|
29
|
-
|
|
30
|
-
if (smaValue !== null && prevRelativeVolumeSma !== null) {
|
|
31
|
-
main.verticalOhlcv[`relative_volume_${size}`][index] = value / prevRelativeVolumeSma;
|
|
32
|
-
} else {
|
|
33
|
-
main.verticalOhlcv[`relative_volume_${size}`][index] = null;
|
|
34
|
-
}
|
|
33
|
+
const {prevRelativeVolumeSma} = main.instances[key]
|
|
35
34
|
|
|
35
|
+
main.pushToMain({
|
|
36
|
+
index,
|
|
37
|
+
key,
|
|
38
|
+
value: (smaValue !== null && prevRelativeVolumeSma !== null) ? value / prevRelativeVolumeSma : null
|
|
39
|
+
})
|
|
36
40
|
|
|
37
|
-
main.instances[
|
|
41
|
+
main.instances[key].prevRelativeVolumeSma = smaValue
|
|
38
42
|
|
|
39
43
|
return true;
|
|
40
44
|
};
|
package/src/oscillators/rsi.js
CHANGED
|
@@ -35,7 +35,7 @@ export const rsi = (main, index, size) => {
|
|
|
35
35
|
|
|
36
36
|
if(currentRsi)
|
|
37
37
|
{
|
|
38
|
-
main.
|
|
38
|
+
main.pushToMain({index, key: `rsi_${size}`, value: currentRsi})
|
|
39
39
|
main.instances[`rsi_sma_${size}`].update(currentRsi, main.lastIndexReplace)
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -49,7 +49,7 @@ export const rsi = (main, index, size) => {
|
|
|
49
49
|
|
|
50
50
|
if(smoothedRsi)
|
|
51
51
|
{
|
|
52
|
-
main.
|
|
52
|
+
main.pushToMain({index, key: `rsi_sma_${size}`, value: smoothedRsi})
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
return true
|
|
@@ -3,6 +3,7 @@ import { FasterEMA } from 'trading-signals';
|
|
|
3
3
|
export const volumeOscillator = (main, index, fast, slow) => {
|
|
4
4
|
|
|
5
5
|
const value = main.verticalOhlcv.volume[index]
|
|
6
|
+
const key = 'volume_oscillator'
|
|
6
7
|
|
|
7
8
|
if (index === 0) {
|
|
8
9
|
|
|
@@ -13,11 +14,11 @@ export const volumeOscillator = (main, index, fast, slow) => {
|
|
|
13
14
|
}
|
|
14
15
|
})
|
|
15
16
|
|
|
16
|
-
main.verticalOhlcv[
|
|
17
|
-
main.crossPairsList.push({ fast:
|
|
17
|
+
main.verticalOhlcv[key] = [...main.nullArray];
|
|
18
|
+
main.crossPairsList.push({ fast: key, slow: 0, isDefault: true });
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
const { fastEMA, slowEMA } = main.instances[
|
|
21
|
+
const { fastEMA, slowEMA } = main.instances[key];
|
|
21
22
|
|
|
22
23
|
fastEMA.update(value, main.lastIndexReplace);
|
|
23
24
|
slowEMA.update(value, main.lastIndexReplace);
|
|
@@ -38,9 +39,10 @@ export const volumeOscillator = (main, index, fast, slow) => {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
if (typeof fastValue === 'number' && typeof slowValue === 'number' && slowValue !== 0) {
|
|
41
|
-
|
|
42
|
+
|
|
43
|
+
main.pushToMain({index, key, value: 100 * (fastValue - slowValue) / slowValue})
|
|
42
44
|
} else {
|
|
43
|
-
main.
|
|
45
|
+
main.pushToMain({index, key, value: null})
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
return true;
|
|
@@ -112,45 +112,54 @@ export const candleStudies = (main, index, size, classify = true, classification
|
|
|
112
112
|
// If there's not enough data, means remain null
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
main.
|
|
116
|
-
|
|
117
|
-
:
|
|
115
|
+
main.pushToMain({
|
|
116
|
+
index,
|
|
117
|
+
key: 'candle_body_size',
|
|
118
|
+
value: isUp
|
|
119
|
+
? classifySize(candleBodySize, bodySizeMean, 2, changeLevel)
|
|
120
|
+
: -Math.abs(classifySize(candleBodySize, bodySizeMean, 2, changeLevel)) || 0
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
|
|
118
124
|
|
|
119
|
-
main.
|
|
125
|
+
main.pushToMain({index, key: 'candle_gap_size', value: isUp
|
|
120
126
|
? classifySize(gapSize, gapMean, 2, changeLevel)
|
|
121
|
-
: -Math.abs(classifySize(gapSize, gapMean, 2, changeLevel)) || 0
|
|
127
|
+
: -Math.abs(classifySize(gapSize, gapMean, 2, changeLevel)) || 0})
|
|
128
|
+
|
|
122
129
|
|
|
123
|
-
main.
|
|
130
|
+
main.pushToMain({index, key: 'candle_close_direction', value: isUp
|
|
124
131
|
? classifySize(closeDirection, closeDirectionMean, 2, changeLevel)
|
|
125
|
-
: -Math.abs(classifySize(closeDirection, closeDirectionMean, 2, changeLevel)) || 0
|
|
132
|
+
: -Math.abs(classifySize(closeDirection, closeDirectionMean, 2, changeLevel)) || 0})
|
|
126
133
|
|
|
127
|
-
|
|
134
|
+
|
|
135
|
+
main.pushToMain({index, key: 'candle_high_direction', value: isUp
|
|
128
136
|
? classifySize(highDirection, highDirectionMean, 2, changeLevel)
|
|
129
|
-
: -Math.abs(classifySize(highDirection, highDirectionMean, 2, changeLevel)) || 0
|
|
137
|
+
: -Math.abs(classifySize(highDirection, highDirectionMean, 2, changeLevel)) || 0})
|
|
130
138
|
|
|
131
|
-
main.
|
|
139
|
+
main.pushToMain({index, key: 'candle_low_direction', value: isUp
|
|
132
140
|
? classifySize(lowDirection, lowDirectionMean, 2, changeLevel)
|
|
133
|
-
: -Math.abs(classifySize(lowDirection, lowDirectionMean, 2, changeLevel)) || 0
|
|
141
|
+
: -Math.abs(classifySize(lowDirection, lowDirectionMean, 2, changeLevel)) || 0})
|
|
134
142
|
|
|
135
|
-
main.
|
|
143
|
+
main.pushToMain({index, key: 'candle_open_direction', value: isUp
|
|
136
144
|
? classifySize(openDirection, openDirectionMean, 2, changeLevel)
|
|
137
|
-
: -Math.abs(classifySize(openDirection, openDirectionMean, 2, changeLevel)) || 0
|
|
145
|
+
: -Math.abs(classifySize(openDirection, openDirectionMean, 2, changeLevel)) || 0})
|
|
146
|
+
|
|
147
|
+
main.pushToMain({index, key: 'candle_top_size', value: classifySize(topSize, topSizeMean, 0.5, sizeLevel)})
|
|
148
|
+
main.pushToMain({index, key: 'candle_bottom_size', value: classifySize(bottomSize, bottomSizeMean, 0.5, sizeLevel)})
|
|
149
|
+
main.pushToMain({index, key: 'candle_shadow_size', value: classifySize(shadowSize, shadowSizeMean, 0.5, sizeLevel)})
|
|
138
150
|
|
|
139
|
-
main.verticalOhlcv.candle_top_size[index] = classifySize(topSize, topSizeMean, 0.5, sizeLevel);
|
|
140
|
-
main.verticalOhlcv.candle_bottom_size[index] = classifySize(bottomSize, bottomSizeMean, 0.5, sizeLevel);
|
|
141
|
-
main.verticalOhlcv.candle_shadow_size[index] = classifySize(shadowSize, shadowSizeMean, 0.5, sizeLevel);
|
|
142
151
|
} else {
|
|
143
152
|
// Without classification, just store raw values
|
|
144
|
-
|
|
145
|
-
main.
|
|
146
|
-
main.
|
|
147
|
-
main.
|
|
148
|
-
main.
|
|
149
|
-
main.
|
|
150
|
-
|
|
151
|
-
main.
|
|
152
|
-
main.
|
|
153
|
-
main.
|
|
153
|
+
|
|
154
|
+
main.pushToMain({index, key: 'candle_body_size', value: isUp ? candleBodySize : -candleBodySize})
|
|
155
|
+
main.pushToMain({index, key: 'candle_gap_size', value: isUp ? gapSize : -gapSize})
|
|
156
|
+
main.pushToMain({index, key: 'candle_close_direction', value: isUp ? closeDirection : -closeDirection})
|
|
157
|
+
main.pushToMain({index, key: 'candle_high_direction', value: isUp ? highDirection : -highDirection})
|
|
158
|
+
main.pushToMain({index, key: 'candle_low_direction', value: isUp ? lowDirection : -lowDirection})
|
|
159
|
+
main.pushToMain({index, key: 'candle_open_direction', value: isUp ? openDirection : -openDirection})
|
|
160
|
+
main.pushToMain({index, key: 'candle_top_size', value: topSize})
|
|
161
|
+
main.pushToMain({index, key: 'candle_bottom_size', value: bottomSize})
|
|
162
|
+
main.pushToMain({index, key: 'candle_shadow_size', value: shadowSize})
|
|
154
163
|
}
|
|
155
164
|
|
|
156
165
|
return true;
|
package/src/studies/dateTime.js
CHANGED
|
@@ -48,15 +48,14 @@ export const dateTime = (main, index) => {
|
|
|
48
48
|
main.instances.dateTime.sessionIntradayIndexCount = 0
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
main.
|
|
53
|
-
main.
|
|
54
|
-
main.
|
|
55
|
-
main.
|
|
56
|
-
main.
|
|
57
|
-
main.
|
|
58
|
-
main.
|
|
59
|
-
main.verticalOhlcv.hour[index] = hour
|
|
51
|
+
main.pushToMain({index, key: 'session_daily_index', value: main.instances.dateTime.sessionDailyIndexCount})
|
|
52
|
+
main.pushToMain({index, key: 'session_intraday_index', value: main.instances.dateTime.sessionIntradayIndexCount})
|
|
53
|
+
main.pushToMain({index, key: 'day_of_the_week', value: day_of_the_week})
|
|
54
|
+
main.pushToMain({index, key: 'day_of_the_month', value: day_of_the_month})
|
|
55
|
+
main.pushToMain({index, key: 'week_of_the_month', value: week_of_the_month})
|
|
56
|
+
main.pushToMain({index, key: 'month', value: month})
|
|
57
|
+
main.pushToMain({index, key: 'year', value: year})
|
|
58
|
+
main.pushToMain({index, key: 'hour', value: hour})
|
|
60
59
|
|
|
61
60
|
}
|
|
62
61
|
|