ohlcv-indicators 2.0.5 → 2.0.7

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.
@@ -1,79 +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
- let defaultTarget = 'close'
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
- const { instance, heightInstance } = main.instances[`bollinger_bands${suffix}`]
40
- const value = main.verticalOhlcv[target][index]
41
- instance.update(value, main.lastIndexReplace)
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
- let result;
44
- try {
45
- result = instance.getResult();
46
- } catch (err) {
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
- if(!result) return true
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
- const { upper, middle, lower } = result;
53
- main.verticalOhlcv[`bollinger_bands_upper${suffix}`][index] = upper;
54
- main.verticalOhlcv[`bollinger_bands_middle${suffix}`][index] = middle;
55
- main.verticalOhlcv[`bollinger_bands_lower${suffix}`][index] = lower;
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
- if (!height && !range) return true;
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 heightMean;
60
- const rangeValue = (value - lower) / (upper - lower);
61
- const heightValue = upper - lower;
62
47
 
63
- heightInstance.update(heightValue, main.lastIndexReplace);
64
48
 
65
- try {
66
- heightMean = heightInstance.getResult();
67
- } catch (err) {
68
- //do nothing
49
+
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];
69
56
  }
70
57
 
71
- main.verticalOhlcv[`bollinger_bands_range${suffix}`][index] = rangeValue;
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
+ }
72
65
 
73
- if(heightMean)
74
- {
75
- main.verticalOhlcv[`bollinger_bands_height${suffix}`][index] = classifySize(heightValue, heightMean, 1.5);
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)})
76
111
  }
77
-
78
- return true;
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;
79
132
  };
@@ -1,91 +1,112 @@
1
- import {FasterSMA} from 'trading-signals'
2
- import { classifySize } from '../utilities/classification.js'
3
-
1
+ import { FasterSMA } from 'trading-signals';
2
+ import { classifySize } from '../utilities/classification.js';
4
3
 
5
4
  export const donchianChannels = (main, index, size, offset, options) => {
6
-
7
- const highs = main.verticalOhlcv.high
8
- const lows = main.verticalOhlcv.low
9
- const {height, range} = options
10
-
11
-
12
- // Ensure that the required arrays exist
13
- if (index === 0) {
14
-
15
- Object.assign(main.verticalOhlcv, {
16
- donchian_channel_upper: [...main.nullArray],
17
- donchian_channel_basis: [...main.nullArray],
18
- donchian_channel_lower: [...main.nullArray],
19
- ...(height && {
20
- [`donchian_channel_height`]: [...main.nullArray]
21
- }),
22
- ...(range && {
23
- [`donchian_channel_range`]: [...main.nullArray],
24
- })
25
- })
26
-
27
- Object.assign(main.instances, {
28
- ['donchian_channel']: {
29
- heightInstance: height ? new FasterSMA((typeof height === 'number' && height > 0) ? height : size) : null
30
- }
31
- })
5
+ const { height, range } = options;
6
+ const indicatorKey = `${size}_${offset}`;
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';
12
+
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: {} };
32
23
  }
33
24
 
34
- const { heightInstance } = main.instances['donchian_channel']
35
-
36
- // Compute the slice start and end to get exactly 'size' bars
37
- const endIdx = (index - offset) + 1 // inclusive end of the slice
38
- const startIdx = endIdx - size // start index for slicing
39
-
40
- // If any of these indices is out of bounds, return
41
- if (startIdx < 0 || endIdx > main.len) return true
42
-
43
- // Extract the relevant chunks of data
44
- const highChunk = highs.slice(startIdx, endIdx)
45
- const lowChunk = lows.slice(startIdx, endIdx)
46
-
47
- // Compute the Donchian channels
48
- const upper = Math.max(...highChunk)
49
- const lower = Math.min(...lowChunk)
50
- const basis = (upper + lower) / 2
51
- const close = main.verticalOhlcv.close[index]
52
- const rangeValue = (close - lower) / (upper - lower)
53
- const heightValue = upper - lower
54
-
55
- if(height)
56
- {
57
- heightInstance.update(heightValue, main.lastIndexReplace)
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;
48
+
49
+ const highs = main.verticalOhlcv.high;
50
+ const lows = main.verticalOhlcv.low;
51
+
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);
63
+
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);
75
+
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;
83
+
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.
58
104
  }
105
+ if (heightMean) {
59
106
 
60
- // Place the result at index
61
- const placementIndex = index
62
-
63
- if (placementIndex >= 0 && placementIndex < main.len) {
64
-
65
- main.verticalOhlcv['donchian_channel_upper'][placementIndex] = upper
66
- main.verticalOhlcv['donchian_channel_basis'][placementIndex] = basis
67
- main.verticalOhlcv['donchian_channel_lower'][placementIndex] = lower
68
-
69
- if(range)
70
- {
71
- main.verticalOhlcv['donchian_channel_range'][placementIndex] = rangeValue
72
- }
73
- if(height)
74
- {
75
- let heightMean
76
-
77
- try {
78
- heightMean = heightInstance.getResult();
79
- } catch (err) {
80
- //do nothing
81
- }
82
-
83
- if(heightMean)
84
- {
85
- main.verticalOhlcv['donchian_channel_height'][placementIndex] = classifySize(heightValue, heightMean, 1.5)
86
- }
87
- }
107
+ main.pushToMain({index, key: `${subPrefix}_height`, value: classifySize(heightValue, heightMean, 1.5)})
88
108
  }
109
+ }
89
110
 
90
- return true
91
- }
111
+ return true;
112
+ };
@@ -26,7 +26,7 @@ export const ema = (main, index, size, target) => {
26
26
 
27
27
  if(currEma)
28
28
  {
29
- main.verticalOhlcv[keyName][index] = currEma
29
+ main.pushToMain({index, key: keyName, value: currEma})
30
30
  }
31
31
 
32
32
  }
@@ -1,27 +1,56 @@
1
1
  import {FasterEMA, FasterMACD} from 'trading-signals';
2
2
 
3
- export const macd = (main, index, fast, slow, signal) => {
3
+ const defaultTarget = 'close'
4
4
 
5
- const value = main.verticalOhlcv.close[index]
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.crossPairsList.push({fast: 'macd_diff', slow: 'macd_dea', isDefault: true});
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['macd'] = new FasterMACD(
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
- macd_diff: [...main.nullArray],
19
- macd_dea: [...main.nullArray],
20
- macd_histogram: [...main.nullArray],
41
+ [`${prefix}_diff`]: [...main.nullArray],
42
+ [`${prefix}_dea`]: [...main.nullArray],
43
+ [`${prefix}_histogram`]: [...main.nullArray],
21
44
  })
22
45
  }
23
46
 
24
- const macdInstance = main.instances[`macd`];
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
- main.verticalOhlcv['macd_diff'][index] = macdResult.macd;
38
- main.verticalOhlcv['macd_dea'][index] = macdResult.signal;
39
- main.verticalOhlcv['macd_histogram'][index] = macdResult.histogram;
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 value = main.verticalOhlcv.volume[index]
5
+ const key = `relative_volume_${size}`
6
6
 
7
7
  if (index === 0) {
8
- main.instances[`relative_volume_${size}`] = {
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
- [`relative_volume_${size}`]: [...main.nullArray],
14
+ [key]: [...main.nullArray],
15
15
  })
16
16
  }
17
17
 
18
- const smaInstance = main.instances[`relative_volume_${size}`].instance
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[`relative_volume_${size}`]
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[`relative_volume_${size}`].prevRelativeVolumeSma = smaValue
41
+ main.instances[key].prevRelativeVolumeSma = smaValue
38
42
 
39
43
  return true;
40
44
  };
@@ -27,7 +27,7 @@ export const sma = (main, index, size, target) => {
27
27
 
28
28
  if(currSma)
29
29
  {
30
- main.verticalOhlcv[keyName][index] = currSma
30
+ main.pushToMain({index, key: keyName, value: currSma})
31
31
  }
32
32
 
33
33
  }
@@ -35,7 +35,7 @@ export const rsi = (main, index, size) => {
35
35
 
36
36
  if(currentRsi)
37
37
  {
38
- main.verticalOhlcv[`rsi_${size}`][index] = currentRsi
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.verticalOhlcv[`rsi_sma_${size}`][index] = smoothedRsi
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[`volume_oscillator`] = [...main.nullArray];
17
- main.crossPairsList.push({ fast: `volume_oscillator`, slow: 0, isDefault: true });
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[`volume_oscillator`];
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
- main.verticalOhlcv[`volume_oscillator`][index] = 100 * (fastValue - slowValue) / slowValue
42
+
43
+ main.pushToMain({index, key, value: 100 * (fastValue - slowValue) / slowValue})
42
44
  } else {
43
- main.verticalOhlcv[`volume_oscillator`][index] = null;
45
+ main.pushToMain({index, key, value: null})
44
46
  }
45
47
 
46
48
  return true;