ohlcv-indicators 1.9.9 → 2.0.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/index.js CHANGED
@@ -1,39 +1,28 @@
1
- import { parseOhlcvToVertical, defaultStudyOptions } from './src/utilities/parsing-utilities.js'
1
+ import { parseOhlcvToVertical } from './src/utilities/parsing-utilities.js'
2
2
  import { correlation } from './src/studies/correlation.js'
3
3
  import { setIndicatorsFromInputParams } from './src/utilities/setIndicatorsFromInputParams.js'
4
+ import { validateDate } from './src/utilities/validators.js'
5
+ import { isAlreadyComputed } from './src/utilities/validators.js'
4
6
 
5
7
  export default class OHLCV_INDICATORS {
6
- constructor({input, ticker = null, studyOptions = null}) {
8
+ constructor({input, ticker = null}) {
7
9
 
8
- if(!Array.isArray(input)) throw Error('input ohlcv must be an array: ' + ticker)
9
- if(input.length === 0) throw Error('input ohlcv must not be empty: ' + ticker)
10
- if(!input[0].hasOwnProperty('close')) throw Error('input ohlcv array objects require at least close property: ' + ticker)
10
+ if(!Array.isArray(input)) throw Error('input OHLCV must be an array: ' + ticker)
11
+ if(input.length === 0) throw Error('input OHLCV must not be empty: ' + ticker)
12
+ if(!input[0].hasOwnProperty('close')) throw Error('input OHLCV array objects require at least close property: ' + ticker)
11
13
 
12
14
  this.lastComputedIndex = 0
13
15
 
14
16
  this.input = input
15
- this.priceBased = ['open', 'high', 'low', 'close', 'mid_price_open_close', 'mid_price_high_low']
17
+ this.priceBased = ['open', 'high', 'low', 'close']
16
18
  this.len = input.length
17
- this.studyOptions = (studyOptions === null) ? defaultStudyOptions : studyOptions
19
+ this.lastIndexReplace = false
18
20
  this.instances = {}
19
21
  this.crossPairsList = []
20
22
  this.verticalOhlcv = {}
21
23
 
22
- this.inputParams = {
23
- crossPairs: null,
24
- lag: null,
25
- relativeVolume: null,
26
- ema: null,
27
- sma: null,
28
- macd: null,
29
- bollingerBands: null,
30
- rsi: null,
31
- donchianChannels: null,
32
- candlesStudies: null,
33
- volumeOscillator: null,
34
- }
24
+ this.inputParams = []
35
25
 
36
-
37
26
  this.studies = {}
38
27
  this.utilities = {
39
28
  correlation
@@ -44,9 +33,11 @@ export default class OHLCV_INDICATORS {
44
33
 
45
34
  return this
46
35
  }
47
-
36
+
37
+
48
38
  getData() {
49
39
 
40
+ //getData method returns the last object (row) of the new OHLCV with indicators: {open, high, low, close, rsi_14, bollinger_bands_upper}
50
41
  this.compute()
51
42
 
52
43
  const {verticalOhlcv} = this
@@ -94,30 +85,57 @@ export default class OHLCV_INDICATORS {
94
85
  }
95
86
 
96
87
  compute(change) {
97
- if (change) {
88
+
89
+ //checks the first row in OHLCV input to verify if the date property is valid
90
+ this.isValidDate = this.input[0].hasOwnProperty('date') && validateDate(this.input[0].date)
91
+
92
+ //compute method can be used to process indicators if no arguments are provided
93
+ //compute method can be called to access the .verticalOhlcv object
94
+ //compute method is called automatically if getLastValues or getDate methods are called
95
+ if (this.len > this.lastComputedIndex && !change) {
96
+ parseOhlcvToVertical(this.input, this, 0);
97
+ }
98
+
99
+ //compute method can be used to add new or update the last datapoints if a new OHLCV object is passed with a valid date property
100
+ //valid date properties: "2024-12-16 15:45:00" or "2024-12-16"
101
+ if (change && typeof change === 'object') {
102
+
103
+ if (!['open', 'high', 'low', 'close', 'volume', 'date'].every(f => Object.keys(change).includes(f))) {
104
+ throw Error('Invalid OHLCV object sent in "compute". Correct usage: .compute({open: 108.25, high: 108.410004, low: 108.25, close: 99999999, volume: 875903, date: "2024-12-16 15:45:00" || "2024-12-16"})');
105
+ }
106
+ if(!this.isValidDate)
107
+ {
108
+ throw Error('All the OHLCV rows require a valid date property to access the compute change method. Correct date format: "2024-12-16 15:45:00" or "2024-12-16"')
109
+ }
110
+ if(!validateDate(change.date))
111
+ {
112
+ throw Error('The date in the new OHLCV row is invalid. Correct date format: "2024-12-16 15:45:00" or "2024-12-16"')
113
+ }
114
+
98
115
  const { date: changeDate } = change;
99
116
  const lastIndex = this.len - 1;
100
117
  const inputDate = this.input[lastIndex]?.date;
101
118
 
119
+ //fallback if .compute(change) is triggered before .compute() alone
102
120
  if (this.lastComputedIndex === 0) {
103
- this.compute();
121
+ this.compute()
104
122
  }
105
-
123
+
124
+
106
125
  if (inputDate !== changeDate) {
107
126
  // Add new item
108
- this.len++;
109
- this.input.push(change);
110
- parseOhlcvToVertical(this.input, this, this.len);
127
+ this.len++
128
+ this.input.push(change)
129
+ parseOhlcvToVertical(this.input, this, this.len)
111
130
  } else {
112
131
  // Modify the last item
113
- this.input[lastIndex] = change;
114
- parseOhlcvToVertical(this.input, this, lastIndex);
132
+ this.input[lastIndex] = change
133
+ parseOhlcvToVertical(this.input, this, lastIndex)
115
134
  }
135
+
116
136
  }
117
137
 
118
- if (this.len > this.lastComputedIndex && !change) {
119
- parseOhlcvToVertical(this.input, this, 0);
120
- }
138
+
121
139
 
122
140
  return this;
123
141
  }
@@ -126,9 +144,10 @@ export default class OHLCV_INDICATORS {
126
144
 
127
145
  crossPairs(arr)
128
146
  {
147
+ isAlreadyComputed(this)
148
+
129
149
  this.crossPairsList = [...this.crossPairsList, ...arr]
130
- this.inputParams.crossPairs ??= []
131
- this.inputParams.crossPairs.push([this.crossPairsList])
150
+ this.inputParams.push({key: 'crossPairs', params: [this.crossPairsList]})
132
151
 
133
152
  return this
134
153
  }
@@ -136,8 +155,9 @@ export default class OHLCV_INDICATORS {
136
155
 
137
156
  lag(colKeys = ['close'], lags = 1) {
138
157
 
139
- this.inputParams.lag ??= []
140
- this.inputParams.lag.push([colKeys, lags])
158
+ isAlreadyComputed(this)
159
+
160
+ this.inputParams.push({key: 'lag', params: [colKeys, lags]})
141
161
 
142
162
  for(let x = 0; x < colKeys.length; x++)
143
163
  {
@@ -154,38 +174,44 @@ export default class OHLCV_INDICATORS {
154
174
 
155
175
  relativeVolume(size) {
156
176
 
157
- this.inputParams.relativeVolume ??= []
158
- this.inputParams.relativeVolume.push([size])
177
+ isAlreadyComputed(this)
178
+
179
+ this.inputParams.push({key: 'relativeVolume', params: [size]})
159
180
 
160
181
  return this
161
182
  }
162
183
 
163
184
  ema(size) {
164
185
 
186
+ isAlreadyComputed(this)
187
+
165
188
  if (typeof size !== 'number' || size <= 0) {
166
189
  throw new Error('"size" must be a positive number in ema.');
167
190
  }
168
191
 
169
- this.inputParams.ema ??= []
170
- this.inputParams.ema.push([size])
192
+ this.inputParams.push({key: 'ema', params: [size]})
171
193
  this.priceBased.push(`ema_${size}`)
172
194
 
173
195
  return this
174
196
  }
175
197
  sma(size) {
176
198
 
199
+ isAlreadyComputed(this)
200
+
177
201
  if (typeof size !== 'number' || size <= 0) {
178
202
  throw new Error('"size" must be a positive number in sma.');
179
203
  }
180
204
 
181
- this.inputParams.sma ??= []
182
- this.inputParams.sma.push([size])
205
+
206
+ this.inputParams.push({key: 'sma', params: [size]})
183
207
  this.priceBased.push(`sma_${size}`)
184
208
 
185
209
  return this
186
210
  }
187
211
  macd(fast = 12, slow = 26, signal = 9) {
188
212
 
213
+ isAlreadyComputed(this)
214
+
189
215
  if (typeof fast !== 'number' || fast <= 0) {
190
216
  throw new Error('"fast" must be a positive number in macd.');
191
217
  }
@@ -196,23 +222,32 @@ export default class OHLCV_INDICATORS {
196
222
  throw new Error('"signal" must be a positive number in macd.');
197
223
  }
198
224
 
199
- this.inputParams.macd ??= []
200
- this.inputParams.macd.push([fast, slow, signal])
225
+ this.inputParams.push({key: 'macd', params: [fast, slow, signal]})
201
226
 
202
227
  return this
203
228
 
204
229
  }
205
- bollingerBands(size = 20, times = 2, bollingerBandsStudies) {
230
+ bollingerBands(size = 20, stdDev = 2, options = {}) {
231
+
232
+
233
+ isAlreadyComputed(this)
234
+
235
+ if(!options || typeof options !== 'object')
236
+ {
237
+ throw new Error('"options" must be an object in bollingerBands. eg: {target, height, range}');
238
+ }
239
+
240
+ const {target = 'close', height = false, range = false} = options
241
+
206
242
  // Validate size and times
207
243
  if (typeof size !== 'number' || size <= 0) {
208
- throw new Error('Invalid parameter: "size" must be a positive number in bollingerBands.');
244
+ throw new Error('"size" must be a positive number in bollingerBands.');
209
245
  }
210
- if (typeof times !== 'number' || times <= 0) {
211
- throw new Error('Invalid parameter: "times" must be a positive number in bollingerBands.');
246
+ if (typeof stdDev !== 'number' || stdDev <= 0) {
247
+ throw new Error('"stdDev" must be a positive number in bollingerBands.');
212
248
  }
213
249
 
214
- this.inputParams.bollingerBands ??= [];
215
- this.inputParams.bollingerBands.push([size, times, bollingerBandsStudies]);
250
+ this.inputParams.push({key: 'bollingerBands', params: [size, stdDev, {target, height, range}]});
216
251
  this.priceBased.push('bollinger_bands_middle', 'bollinger_bands_upper', 'bollinger_bands_lower');
217
252
 
218
253
  return this;
@@ -220,18 +255,21 @@ export default class OHLCV_INDICATORS {
220
255
 
221
256
  rsi(size)
222
257
  {
258
+ isAlreadyComputed(this)
259
+
223
260
  // Validate size and times
224
261
  if (typeof size !== 'number' || size <= 0) {
225
262
  throw new Error('"size" must be a positive number in rsi.');
226
263
  }
227
264
 
228
- this.inputParams.rsi ??= []
229
- this.inputParams.rsi.push([size])
265
+ this.inputParams.push({key: 'rsi', params: [size]})
230
266
 
231
267
  return this
232
268
  }
233
269
  donchianChannels(size = 20, offset = 0)
234
270
  {
271
+ isAlreadyComputed(this)
272
+
235
273
  if (typeof size !== 'number' || size <= 0) {
236
274
  throw new Error('"size" must be a positive number or 0 in donchianChannels.');
237
275
  }
@@ -240,31 +278,32 @@ export default class OHLCV_INDICATORS {
240
278
  throw new Error('"offset" must be a positive number or 0 in donchianChannels.');
241
279
  }
242
280
 
243
- this.inputParams.donchianChannels ??= []
244
- this.inputParams.donchianChannels.push([size, offset])
281
+ this.inputParams.push({key: 'donchianChannels', params: [size, offset]})
245
282
  this.priceBased.push('donchian_channel_upper', 'donchian_channel_lower', 'donchian_channel_basis')
246
283
 
247
284
  return this
248
285
  }
249
- candlesStudies(size = 20, classify = true, classificationLevels)
286
+ candleStudies(size = 20, classify = true, classificationLevels)
250
287
  {
251
288
 
289
+ isAlreadyComputed(this)
290
+
252
291
  if (typeof size !== 'number' || size <= 0) {
253
- throw new Error('"size" must be a positive number or 0 in candlesStudies.');
292
+ throw new Error('"size" must be a positive number or 0 in candleStudies.');
254
293
  }
255
294
 
256
295
  if (typeof classify !== 'boolean') {
257
- throw new Error('"classify" must be a true or false in candlesStudies.');
296
+ throw new Error('"classify" must be a true or false in candleStudies.');
258
297
  }
259
298
 
260
- this.inputParams.candlesStudies ??= []
261
- this.inputParams.candlesStudies.push([size, classify, classificationLevels])
299
+ this.inputParams.push({key: 'candleStudies', params: [size, classify, classificationLevels]})
262
300
  return this
263
301
  }
264
302
 
265
303
  volumeOscillator(fastSize = 5, slowSize = 10)
266
304
  {
267
305
 
306
+ isAlreadyComputed(this)
268
307
 
269
308
  if (typeof fastSize !== 'number' || fastSize <= 0) {
270
309
  throw new Error('fastSize" must be a positive number in volumeOscillator.');
@@ -274,9 +313,21 @@ export default class OHLCV_INDICATORS {
274
313
  throw new Error('"slowSize" must be a positive number greater than the "fastSize" in volumeOscillator.');
275
314
  }
276
315
 
316
+ this.inputParams.push({key: 'volumeOscillator', params: [fastSize, slowSize]})
317
+ return this
318
+ }
319
+ dateTime()
320
+ {
321
+ isAlreadyComputed(this)
322
+
323
+ this.inputParams.push({key: 'dateTime', params: []})
324
+ return this
325
+ }
326
+ priceVariations()
327
+ {
328
+ isAlreadyComputed(this)
277
329
 
278
- this.inputParams.volumeOscillator ??= []
279
- this.inputParams.volumeOscillator.push([fastSize, slowSize])
330
+ this.inputParams.push({key: 'priceVariations', params: []})
280
331
  return this
281
332
  }
282
333
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ohlcv-indicators",
3
- "version": "1.9.9",
3
+ "version": "2.0.1",
4
4
  "description": "A JavaScript library featuring optimized trading indicators for OHLCV arrays [{open, high, low, close, value}]. This library is optimized for backtesting and trading bot development.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -1,35 +1,44 @@
1
1
  import {FasterBollingerBands, FasterSMA} from 'trading-signals';
2
2
  import { classifySize } from '../utilities/classification.js';
3
3
 
4
- export const bollingerBands = (main, index, size, times, bollingerBandsStudies = false) => {
4
+ let defaultTarget = 'close'
5
5
 
6
- const value = main.verticalOhlcv.close[index]
6
+ export const bollingerBands = (main, index, size, times, options) => {
7
7
 
8
- if (index === 0) {
8
+ const {height, range, target} = options
9
+ let suffix = (target === defaultTarget) ? '' : `_${target}`
9
10
 
10
- Object.assign(main.instances, {bollinger_bands: {
11
- instance: new FasterBollingerBands(size, times),
12
- heightInstance: bollingerBandsStudies ? new FasterSMA(size) : null
13
- }})
11
+ if (index === 0) {
14
12
 
13
+ if(!main.verticalOhlcv.hasOwnProperty(target))
14
+ {
15
+ throw new Error(`bollingerBands could not find target "${target}"`)
16
+ }
15
17
 
16
- Object.assign(main.verticalOhlcv, {
17
- bollinger_bands_upper: new Array(main.len).fill(null),
18
- bollinger_bands_middle: new Array(main.len).fill(null),
19
- bollinger_bands_lower: new Array(main.len).fill(null)
18
+ Object.assign(main.instances, {
19
+ [`bollinger_bands${suffix}`]: {
20
+ instance: new FasterBollingerBands(size, times),
21
+ heightInstance: height ? new FasterSMA(size) : null
22
+ }
20
23
  })
21
-
22
- if (bollingerBandsStudies) {
23
-
24
- Object.assign(main.verticalOhlcv, {
25
- bollinger_bands_range: new Array(main.len).fill(null),
26
- bollinger_bands_height: new Array(main.len).fill(null),
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],
27
34
  })
28
- }
35
+ })
29
36
  }
30
37
 
31
- const { instance, heightInstance } = main.instances[`bollinger_bands`];
32
- instance.update(value);
38
+
39
+ const { instance, heightInstance } = main.instances[`bollinger_bands${suffix}`]
40
+ const value = main.verticalOhlcv[target][index]
41
+ instance.update(value, main.lastIndexReplace)
33
42
 
34
43
  let result;
35
44
  try {
@@ -41,20 +50,20 @@ export const bollingerBands = (main, index, size, times, bollingerBandsStudies =
41
50
  if(!result) return true
42
51
 
43
52
  const { upper, middle, lower } = result;
44
- main.verticalOhlcv.bollinger_bands_upper[index] = upper;
45
- main.verticalOhlcv.bollinger_bands_middle[index] = middle;
46
- main.verticalOhlcv.bollinger_bands_lower[index] = lower;
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;
47
56
 
48
- if (!bollingerBandsStudies) return true;
57
+ if (!height && !range) return true;
49
58
 
50
- let range = null;
51
- let height = null;
59
+ let rangeValue = null;
60
+ let heightValue = null;
52
61
  let heightMean;
53
62
 
54
- range = (value - lower) / (upper - lower);
55
- height = upper - lower;
63
+ rangeValue = (value - lower) / (upper - lower);
64
+ heightValue = upper - lower;
56
65
 
57
- heightInstance.update(height);
66
+ heightInstance.update(heightValue, main.lastIndexReplace);
58
67
 
59
68
  try {
60
69
  heightMean = heightInstance.getResult();
@@ -62,11 +71,11 @@ export const bollingerBands = (main, index, size, times, bollingerBandsStudies =
62
71
  //do nothing
63
72
  }
64
73
 
65
- main.verticalOhlcv.bollinger_bands_range[index] = range;
74
+ main.verticalOhlcv[`bollinger_bands_range${suffix}`][index] = rangeValue;
66
75
 
67
76
  if(heightMean)
68
77
  {
69
- main.verticalOhlcv.bollinger_bands_height[index] = classifySize(height, heightMean, 1.5);
78
+ main.verticalOhlcv[`bollinger_bands_height${suffix}`][index] = classifySize(heightValue, heightMean, 1.5);
70
79
  }
71
80
 
72
81
  return true;
@@ -1,5 +1,3 @@
1
- const avg = (h, l) => (h + l) / 2;
2
-
3
1
  export const donchianChannels = (main, index, size, offset) => {
4
2
 
5
3
  const highs = main.verticalOhlcv.high;
@@ -7,13 +5,11 @@ export const donchianChannels = (main, index, size, offset) => {
7
5
 
8
6
  // Ensure that the required arrays exist
9
7
  if (index === 0) {
10
-
11
- main.instances['donchian_channels'] = {};
12
8
 
13
9
  Object.assign(main.verticalOhlcv, {
14
- donchian_channel_upper: new Array(main.len).fill(null),
15
- donchian_channel_basis: new Array(main.len).fill(null),
16
- donchian_channel_lower: new Array(main.len).fill(null),
10
+ donchian_channel_upper: [...main.nullArray],
11
+ donchian_channel_basis: [...main.nullArray],
12
+ donchian_channel_lower: [...main.nullArray],
17
13
  })
18
14
  }
19
15
 
@@ -32,7 +28,7 @@ export const donchianChannels = (main, index, size, offset) => {
32
28
  // Compute the Donchian channels
33
29
  const upper = Math.max(...highChunk);
34
30
  const lower = Math.min(...lowChunk);
35
- const basis = avg(upper, lower);
31
+ const basis = (upper + lower) / 2;
36
32
 
37
33
  // Place the result at index
38
34
  const placementIndex = index;
@@ -8,11 +8,11 @@ export const ema = (main, index, size) => {
8
8
  if(index === 0)
9
9
  {
10
10
  main.instances[`ema_${size}`] = new FasterEMA(size)
11
- main.verticalOhlcv[`ema_${size}`] = new Array(main.len).fill(null)
11
+ main.verticalOhlcv[`ema_${size}`] = [...main.nullArray]
12
12
  }
13
13
 
14
14
  let currSma
15
- main.instances[`ema_${size}`].update(value)
15
+ main.instances[`ema_${size}`].update(value, main.lastIndexReplace)
16
16
 
17
17
  try{
18
18
  currSma = main.instances[`ema_${size}`].getResult()
@@ -6,7 +6,7 @@ export const macd = (main, index, fast, slow, signal) => {
6
6
 
7
7
  if (index === 0) {
8
8
 
9
- main.crossPairsList.push({fast: 'macd_diff', slow: 'macd_dea'});
9
+ main.crossPairsList.push({fast: 'macd_diff', slow: 'macd_dea', isDefault: true});
10
10
 
11
11
  main.instances['macd'] = new FasterMACD(
12
12
  new FasterEMA(fast),
@@ -15,14 +15,14 @@ export const macd = (main, index, fast, slow, signal) => {
15
15
  );
16
16
 
17
17
  Object.assign(main.verticalOhlcv, {
18
- macd_diff: new Array(main.len).fill(null),
19
- macd_dea: new Array(main.len).fill(null),
20
- macd_histogram: new Array(main.len).fill(null),
18
+ macd_diff: [...main.nullArray],
19
+ macd_dea: [...main.nullArray],
20
+ macd_histogram: [...main.nullArray],
21
21
  })
22
22
  }
23
23
 
24
24
  const macdInstance = main.instances[`macd`];
25
- macdInstance.update(value);
25
+ macdInstance.update(value, main.lastIndexReplace);
26
26
 
27
27
  let macdResult;
28
28
 
@@ -5,16 +5,18 @@ export const relativeVolume = (main, index, size = 10) => {
5
5
  const value = main.verticalOhlcv.volume[index]
6
6
 
7
7
  if (index === 0) {
8
- main.instances[`relative_volume_${size}`] = new FasterSMA(size);
8
+ main.instances[`relative_volume_${size}`] = {
9
+ instance: new FasterSMA(size),
10
+ prevRelativeVolumeSma: null
11
+ }
9
12
 
10
13
  Object.assign(main.verticalOhlcv, {
11
- [`relative_volume_${size}`]: new Array(main.len).fill(null),
12
- [`relative_volume_sma_${size}`]: new Array(main.len).fill(null)
14
+ [`relative_volume_${size}`]: [...main.nullArray],
13
15
  })
14
16
  }
15
17
 
16
- const smaInstance = main.instances[`relative_volume_${size}`]
17
- smaInstance.update(value)
18
+ const smaInstance = main.instances[`relative_volume_${size}`].instance
19
+ smaInstance.update(value, main.lastIndexReplace)
18
20
 
19
21
  let smaValue;
20
22
  try {
@@ -23,13 +25,16 @@ export const relativeVolume = (main, index, size = 10) => {
23
25
  //do nothing
24
26
  }
25
27
 
26
- main.verticalOhlcv[`relative_volume_sma_${size}`][index] = smaValue;
28
+ const {prevRelativeVolumeSma} = main.instances[`relative_volume_${size}`]
27
29
 
28
- if (smaValue !== null && typeof main.verticalOhlcv[`relative_volume_sma_${size}`][index - 1] === 'number') {
29
- main.verticalOhlcv[`relative_volume_${size}`][index] = value / main.verticalOhlcv[`relative_volume_sma_${size}`][index - 1];
30
+ if (smaValue !== null && prevRelativeVolumeSma !== null) {
31
+ main.verticalOhlcv[`relative_volume_${size}`][index] = value / prevRelativeVolumeSma;
30
32
  } else {
31
33
  main.verticalOhlcv[`relative_volume_${size}`][index] = null;
32
34
  }
33
35
 
36
+
37
+ main.instances[`relative_volume_${size}`].prevRelativeVolumeSma = smaValue
38
+
34
39
  return true;
35
40
  };
@@ -8,11 +8,11 @@ export const sma = (main, index, size) => {
8
8
  if(index === 0)
9
9
  {
10
10
  main.instances[`sma_${size}`] = new FasterSMA(size)
11
- main.verticalOhlcv[`sma_${size}`] = new Array(main.len).fill(null)
11
+ main.verticalOhlcv[`sma_${size}`] = [...main.nullArray]
12
12
  }
13
13
 
14
14
  let currSma
15
- main.instances[`sma_${size}`].update(value)
15
+ main.instances[`sma_${size}`].update(value, main.lastIndexReplace)
16
16
 
17
17
  try{
18
18
  currSma = main.instances[`sma_${size}`].getResult()
@@ -8,7 +8,7 @@ export const rsi = (main, index, size) => {
8
8
 
9
9
  if(index === 0)
10
10
  {
11
- main.crossPairsList.push({fast: `rsi_${size}`, slow: `rsi_sma_${size}`})
11
+ main.crossPairsList.push({fast: `rsi_${size}`, slow: `rsi_sma_${size}`, isDefault: true})
12
12
 
13
13
  Object.assign(main.instances, {
14
14
  [`rsi_${size}`]: new FasterRSI(size),
@@ -16,15 +16,15 @@ export const rsi = (main, index, size) => {
16
16
  })
17
17
 
18
18
  Object.assign(main.verticalOhlcv, {
19
- [`rsi_${size}`]: new Array(main.len).fill(null),
20
- [`rsi_sma_${size}`]: new Array(main.len).fill(null)
19
+ [`rsi_${size}`]: [...main.nullArray],
20
+ [`rsi_sma_${size}`]: [...main.nullArray]
21
21
  })
22
22
  }
23
23
 
24
24
  let currentRsi
25
25
  let smoothedRsi
26
26
 
27
- main.instances[`rsi_${size}`].update(value)
27
+ main.instances[`rsi_${size}`].update(value, main.lastIndexReplace)
28
28
 
29
29
  try
30
30
  {
@@ -36,7 +36,7 @@ export const rsi = (main, index, size) => {
36
36
  if(currentRsi)
37
37
  {
38
38
  main.verticalOhlcv[`rsi_${size}`][index] = currentRsi
39
- main.instances[`rsi_sma_${size}`].update(currentRsi)
39
+ main.instances[`rsi_sma_${size}`].update(currentRsi, main.lastIndexReplace)
40
40
  }
41
41
 
42
42
  try
@@ -13,14 +13,14 @@ export const volumeOscillator = (main, index, fast, slow) => {
13
13
  }
14
14
  })
15
15
 
16
- main.verticalOhlcv[`volume_oscillator`] = new Array(main.len).fill(null);
17
- main.crossPairsList.push({ fast: `volume_oscillator`, slow: 0 });
16
+ main.verticalOhlcv[`volume_oscillator`] = [...main.nullArray];
17
+ main.crossPairsList.push({ fast: `volume_oscillator`, slow: 0, isDefault: true });
18
18
  }
19
19
 
20
20
  const { fastEMA, slowEMA } = main.instances[`volume_oscillator`];
21
21
 
22
- fastEMA.update(value);
23
- slowEMA.update(value);
22
+ fastEMA.update(value, main.lastIndexReplace);
23
+ slowEMA.update(value, main.lastIndexReplace);
24
24
 
25
25
  let fastValue = null;
26
26
  let slowValue = null;