ohlcv-indicators 3.1.8 → 3.2.0

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
@@ -5,6 +5,16 @@ import { isAlreadyComputed } from './src/utilities/validators.js'
5
5
  import { divideByMultiplier } from './src/utilities/numberUtilities.js'
6
6
  import { getMovingAveragesParams } from './src/moving-averages/movingAverages.js'
7
7
  import { verticalToHorizontal } from './src/utilities/dataParsingUtilities.js'
8
+ import { pushToMain } from './src/utilities/pushToMain.js'
9
+
10
+ /**
11
+ * Class OHLCV_INDICATORS
12
+ *
13
+ * This class provides methods for calculating and managing technical indicators
14
+ * on financial OHLCV (Open, High, Low, Close, Volume) data. It enables users
15
+ * to parallel compute various technical indicators in 1 single loop.
16
+ * OHLCV datasets.
17
+ */
8
18
 
9
19
  const validMagnitudeValues = [0.001, 0.002, 0.0025, 0.005, 0.01, 0.02, 0.025, 0.05, 0.1, 0.20, 0.25, 0.5, 1, 2, 2.5, 5, 10]
10
20
 
@@ -27,8 +37,6 @@ export default class OHLCV_INDICATORS {
27
37
  this.crossPairsList = []
28
38
  this.verticalOhlcv = {}
29
39
 
30
-
31
-
32
40
  this.studies = {}
33
41
  this.utilities = {
34
42
  correlation
@@ -39,6 +47,7 @@ export default class OHLCV_INDICATORS {
39
47
  this.precision = precision
40
48
  this.precisionMultiplier = (this.precision === true) ? 0 : 1
41
49
 
50
+ this.autoMinMaxKeys = []
42
51
  this.minMaxRanges = {}
43
52
 
44
53
 
@@ -51,60 +60,13 @@ export default class OHLCV_INDICATORS {
51
60
  this.inputParams = []
52
61
  }
53
62
 
54
- return this
55
- }
56
-
57
- pushToMain({index, key, value})
58
- {
59
- if(value === null || typeof value === 'undefined')
60
- {
61
- this.invalidValueIndex = index
62
-
63
- for(let k in this.minMaxRanges)
64
- {
65
- this.minMaxRanges[k] = {min: Infinity, max: -Infinity}
66
- }
67
- }
63
+ this.pushToMain = ({index, key, value}) => pushToMain({main: this, index, key, value})
68
64
 
69
- this.verticalOhlcv[key][index] = value
70
65
 
71
- if(typeof value === 'number')
72
- {
73
- if(key.includes('_diff_'))
74
- {
75
- if(!this.minMaxRanges.hasOwnProperty(key) || (this.minMaxRanges[key].min === Infinity || this.minMaxRanges[key].max === -Infinity))
76
- {
77
- this.minMaxRanges[key] = { min: -1, max: 1}
78
- }
79
- }
80
- else if(key.startsWith('candle_'))
81
- {
82
- this.minMaxRanges[key] = { min: -1, max: 1}
83
- }
84
- else if(key.includes('_range_') && (key.startsWith('bollinger_bands_') || key.startsWith('donchian_channel_')))
85
- {
86
- this.minMaxRanges[key] = { min: 0, max: 1 }
87
- }
88
- else if(!key.includes('_x_')) {
89
- const newValue = (this.precision && this.priceBased.includes(key)) ? (value / this.precisionMultiplier) : value
90
-
91
- if(!this.minMaxRanges.hasOwnProperty(key))
92
- {
93
- this.minMaxRanges[key] = { min: Infinity, max: -Infinity }
94
- }
95
-
96
- if(newValue < this.minMaxRanges[key].min)
97
- {
98
- this.minMaxRanges[key].min = newValue
99
- }
100
- if(newValue > this.minMaxRanges[key].max)
101
- {
102
- this.minMaxRanges[key].max = newValue
103
- }
104
- }
105
- }
66
+ return this
106
67
  }
107
68
 
69
+
108
70
  getDataAsCols(skipNull = true) {
109
71
  this.compute();
110
72
 
@@ -350,7 +312,7 @@ export default class OHLCV_INDICATORS {
350
312
  throw new Error('"options" must be an object in bollingerBands. eg: {target, height, range}');
351
313
  }
352
314
 
353
- const {target = 'close', height = false, range = [], zScore = [], scale = null, lag = 0} = options
315
+ const {target = 'close', height = false, range = [], scale = null, lag = 0, autoMinMax} = options
354
316
 
355
317
  // Validate size and times
356
318
  if (typeof size !== 'number' || size <= 0) {
@@ -362,13 +324,16 @@ export default class OHLCV_INDICATORS {
362
324
  if (!Array.isArray(range)) {
363
325
  throw new Error('If set, "range" must be a array of column names in bollingerBands.');
364
326
  }
365
- if (!Array.isArray(zScore)) {
366
- throw new Error('If set, "zScore" must be a array of column names in bollingerBands.');
367
- }
368
327
  if (typeof lag !== 'number') {
369
328
 
370
329
  throw new Error(`"lag" value in rsi must be a number in bollingerBands.`);
371
330
  }
331
+
332
+ if (typeof autoMinMax !== 'boolean') {
333
+
334
+ throw new Error(`"autoMinMax" value in rsi must be a number in bollingerBands.`);
335
+ }
336
+
372
337
  if (typeof height !== 'boolean') {
373
338
  throw new Error('"height" must be a boolean in bollingerBands.');
374
339
  }
@@ -380,7 +345,7 @@ export default class OHLCV_INDICATORS {
380
345
  }
381
346
  }
382
347
 
383
- this.inputParams.push({key: 'bollingerBands', params: [size, stdDev, {target, height, scale, range, zScore, lag}]});
348
+ this.inputParams.push({key: 'bollingerBands', params: [size, stdDev, {target, height, scale, range, lag, autoMinMax}]});
384
349
 
385
350
  return this;
386
351
  }
@@ -394,7 +359,7 @@ export default class OHLCV_INDICATORS {
394
359
  throw new Error('"size" must be a positive number in rsi.');
395
360
  }
396
361
 
397
- const {scale = null, target = 'close', lag = 0} = options
362
+ const {scale = null, target = 'close', lag = 0, autoMinMax = false} = options
398
363
 
399
364
  if (typeof scale === 'number' && !validMagnitudeValues.includes(scale)) {
400
365
 
@@ -406,7 +371,12 @@ export default class OHLCV_INDICATORS {
406
371
  throw new Error(`"lag" value in rsi must be a number in rsi.`);
407
372
  }
408
373
 
409
- this.inputParams.push({key: 'rsi', params: [size, {scale, target, lag}]})
374
+ if (typeof autoMinMax !== 'boolean') {
375
+
376
+ throw new Error(`"autoMinMax" value in rsi must be a number in rsi.`);
377
+ }
378
+
379
+ this.inputParams.push({key: 'rsi', params: [size, {scale, target, lag, autoMinMax}]})
410
380
 
411
381
  return this
412
382
  }
@@ -421,7 +391,7 @@ export default class OHLCV_INDICATORS {
421
391
  throw new Error('"offset" must be a number greater than or equal to 0 in donchianChannels.');
422
392
  }
423
393
 
424
- const { height = false, range = [], scale = null, lag = 0} = options;
394
+ const { height = false, range = [], scale = null, lag = 0, autoMinMax = false} = options;
425
395
 
426
396
  if (!Array.isArray(range)) {
427
397
  throw new Error('If set, "range" must be an array of column names in donchianChannels.');
@@ -430,6 +400,11 @@ export default class OHLCV_INDICATORS {
430
400
  if (typeof lag !== 'number') {
431
401
  throw new Error(`"lag" value in rsi must be a number in donchianChannels.`);
432
402
  }
403
+
404
+ if (typeof autoMinMax !== 'boolean') {
405
+
406
+ throw new Error(`"autoMinMax" value in rsi must be a number in donchianChannels.`);
407
+ }
433
408
 
434
409
  if (typeof height !== 'boolean') {
435
410
  throw new Error('"height" must be a boolean in donchianChannels.');
@@ -443,37 +418,41 @@ export default class OHLCV_INDICATORS {
443
418
  }
444
419
  }
445
420
 
446
- this.inputParams.push({ key: 'donchianChannels', params: [size, offset, { height, range, scale, lag }] });
421
+ this.inputParams.push({ key: 'donchianChannels', params: [size, offset, { height, range, scale, lag, autoMinMax }] });
447
422
 
448
423
  return this;
449
424
  }
450
425
 
451
426
 
452
- candleStudies(size = 20, options = {}) {
427
+ candleVectors(size = 20, options = {}) {
453
428
  isAlreadyComputed(this);
454
429
 
455
430
  if (typeof size !== 'number' || size <= 0) {
456
- throw new Error('"size" must be a positive number greater than 0 in candleStudies.');
431
+ throw new Error('"size" must be a positive number greater than 0 in candleVectors.');
457
432
  }
458
433
 
459
- const { stdDev = 0.5, lag = 0, scale = 0.05, patternSize = 1 } = options;
434
+ const { stdDev = 0.5, lag = 0, scale = 0.05, patternSize = 1, autoMinMax = false } = options;
460
435
 
461
436
 
462
437
  if (typeof stdDev !== 'number' || stdDev <= 0) {
463
- throw new Error('"stdDev" must be a positive number greater than 0 in candleStudies.');
438
+ throw new Error('"stdDev" must be a positive number greater than 0 in candleVectors.');
464
439
  }
465
440
 
466
441
  if (typeof scale === 'number' && !validMagnitudeValues.includes(scale)) {
467
442
 
468
- throw new Error(`"scale" value in candleStudies must be any of the following numbers: ${validMagnitudeValues.join(', ')}`);
443
+ throw new Error(`"scale" value in candleVectors must be any of the following numbers: ${validMagnitudeValues.join(', ')}`);
469
444
  }
470
445
 
471
446
  if (typeof patternSize !== 'number' || !Number.isInteger(patternSize) || patternSize < 1 ) {
472
447
 
473
- throw new Error(`"patternSize" value in candleStudies must be an integer greater than 0.}`);
448
+ throw new Error(`"patternSize" value in candleVectors must be an integer greater than 0.`);
449
+ }
450
+ if (typeof autoMinMax !== 'boolean') {
451
+
452
+ throw new Error(`"autoMinMax" value in candleVectors must be boolean`);
474
453
  }
475
454
 
476
- this.inputParams.push({ key: 'candleStudies', params: [size, {stdDev, patternSize, lag, scale}] });
455
+ this.inputParams.push({ key: 'candleVectors', params: [size, {stdDev, patternSize, lag, scale, autoMinMax}] });
477
456
  return this;
478
457
  }
479
458
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ohlcv-indicators",
3
- "version": "3.1.8",
3
+ "version": "3.2.0",
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",
@@ -22,6 +22,7 @@
22
22
  "@babel/preset-env": "^7.25.3",
23
23
  "babel-loader": "^9.1.3",
24
24
  "dotenv": "^16.4.5",
25
+ "terser-webpack-plugin": "^5.3.14",
25
26
  "webpack": "^5.93.0",
26
27
  "webpack-cli": "^5.1.4"
27
28
  }
@@ -3,7 +3,7 @@ import { calcMagnitude } from '../utilities/numberUtilities.js';
3
3
 
4
4
  const defaultTarget = 'close'
5
5
 
6
- export const bollingerBands = (main, index, size, stdDev, { height, range = [], zScore = [], target, scale, lag }) => {
6
+ export const bollingerBands = (main, index, size, stdDev, { height, range = [], target, scale, lag, autoMinMax }) => {
7
7
  const { verticalOhlcv, instances, lastIndexReplace } = main;
8
8
  const suffix = target === defaultTarget ? '' : `_${target}`;
9
9
  const indicatorKey = `${size}_${stdDev}${suffix}`;
@@ -11,6 +11,7 @@ export const bollingerBands = (main, index, size, stdDev, { height, range = [],
11
11
 
12
12
  // Initialization on the first call.
13
13
  if (index === 0) {
14
+
14
15
  const { priceBased, inputParams, nullArray, verticalOhlcv } = main;
15
16
 
16
17
  if (!(target in verticalOhlcv)) {
@@ -49,14 +50,12 @@ export const bollingerBands = (main, index, size, stdDev, { height, range = [],
49
50
  throw new Error(`Invalid range item value "${rangeKey}" property for bollingerBands. Only price based key names are accepted:\n${JSON.stringify(priceBased)}`);
50
51
  }
51
52
  keyNames.push(`${prefix}_range_${rangeKey}`)
52
- }
53
53
 
54
- // Set up additional arrays for each zScore property.
55
- for (const zScoreKey of zScore) {
56
- if (!(zScoreKey in verticalOhlcv) || !priceBased.includes(zScoreKey)) {
57
- throw new Error(`Invalid zScore item value "${zScoreKey}" for bollingerBands. Only price based key names are accepted:\n${JSON.stringify(priceBased)}`);
54
+ if(autoMinMax)
55
+ {
56
+ main.autoMinMaxKeys.push(`${prefix}_range_${rangeKey}`)
58
57
  }
59
- keyNames.push(`${prefix}_zscore_${zScoreKey}`)
58
+
60
59
  }
61
60
 
62
61
  const verticalOhlcvSetup = Object.fromEntries(keyNames.map(v => [v, [...nullArray]]))
@@ -104,7 +103,7 @@ export const bollingerBands = (main, index, size, stdDev, { height, range = [],
104
103
  if (height) {
105
104
  let heightValue = null;
106
105
  if (typeof upper === 'number' && typeof lower === 'number' && lower !== 0) {
107
- heightValue = calcMagnitude( ((upper - lower) / lower), 0.005)
106
+ heightValue = calcMagnitude( ((upper - lower) / lower), 0.001)
108
107
  }
109
108
  main.pushToMain({ index, key: `${subPrefix}_height`, value: heightValue });
110
109
  }
@@ -114,7 +113,11 @@ export const bollingerBands = (main, index, size, stdDev, { height, range = [],
114
113
  let rangeValue = null;
115
114
  const priceValue = verticalOhlcv[rangeKey][index];
116
115
  if (typeof priceValue === 'number' && typeof lower === 'number' && typeof upper === 'number' && (upper - lower) !== 0) {
117
- rangeValue = (priceValue - lower) / (upper - lower);
116
+ rangeValue = (autoMinMax)
117
+ ? Math.max(Math.min((priceValue - lower) / (upper - lower), 1), 0)
118
+ : (priceValue - lower) / (upper - lower)
119
+
120
+
118
121
  if (scale) {
119
122
  rangeValue = calcMagnitude(rangeValue, scale);
120
123
  }
@@ -122,24 +125,5 @@ export const bollingerBands = (main, index, size, stdDev, { height, range = [],
122
125
  main.pushToMain({ index, key: `${subPrefix}_range_${rangeKey}`, value: rangeValue });
123
126
  }
124
127
 
125
- // Process each zScore property.
126
- for (const zScoreKey of zScore) {
127
- let zScoreValue = null;
128
- const priceValue = verticalOhlcv[zScoreKey][index];
129
- if (
130
- typeof priceValue === 'number' &&
131
- typeof middle === 'number' &&
132
- typeof upper === 'number' &&
133
- typeof lower === 'number' &&
134
- (upper - lower) !== 0
135
- ) {
136
- zScoreValue = (2 * stdDev * (priceValue - middle)) / (upper - lower);
137
- if (scale) {
138
- zScoreValue = calcMagnitude(zScoreValue, scale);
139
- }
140
- }
141
- main.pushToMain({ index, key: `${subPrefix}_zscore_${zScoreKey}`, value: zScoreValue });
142
- }
143
-
144
128
  return true;
145
129
  }
@@ -1,6 +1,6 @@
1
1
  import { calcMagnitude } from "../utilities/numberUtilities.js"
2
2
 
3
- export const donchianChannels = (main, index, size, offset, { height, range, scale, lag }) => {
3
+ export const donchianChannels = (main, index, size, offset, { height, range, scale, lag, autoMinMax }) => {
4
4
  const indicatorKey = `${size}_${offset}`;
5
5
  const { verticalOhlcv, instances, len } = main;
6
6
 
@@ -32,6 +32,12 @@ export const donchianChannels = (main, index, size, offset, { height, range, sca
32
32
  throw new Error(`Invalid range item value "${rangeKey}" property for donchianChannels. Only price based key names are accepted:\n${JSON.stringify(priceBased)}`);
33
33
  }
34
34
  keyNames.push(`${prefix}_range_${rangeKey}`)
35
+
36
+ if(autoMinMax)
37
+ {
38
+ main.autoMinMaxKeys.push(`${prefix}_range_${rangeKey}`)
39
+ }
40
+
35
41
  }
36
42
 
37
43
  const verticalOhlcvSetup = Object.fromEntries(keyNames.map(v => [v, [...nullArray]]))
@@ -110,7 +116,7 @@ export const donchianChannels = (main, index, size, offset, { height, range, sca
110
116
  if (height) {
111
117
  let heightValue = null;
112
118
  if (typeof upper === 'number' && typeof lower === 'number' && lower !== 0) {
113
- heightValue = calcMagnitude( ((upper - lower) / lower), 0.005)
119
+ heightValue = calcMagnitude( ((upper - lower) / lower), 0.001)
114
120
  }
115
121
  main.pushToMain({ index, key: `${subPrefix}_height`, value: heightValue });
116
122
  }
@@ -125,7 +131,10 @@ export const donchianChannels = (main, index, size, offset, { height, range, sca
125
131
  typeof lower === 'number' &&
126
132
  (upper - lower) !== 0
127
133
  ) {
128
- rangeValue = (priceValue - lower) / (upper - lower);
134
+ rangeValue = (autoMinMax)
135
+ ? Math.max(Math.min((priceValue - lower) / (upper - lower), 1), 0)
136
+ : (priceValue - lower) / (upper - lower)
137
+
129
138
  if (scale) {
130
139
  rangeValue = calcMagnitude(rangeValue, scale);
131
140
  }
@@ -4,7 +4,7 @@ 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 }) => {
7
+ export const movingAverages = (main, index, indicatorName, size, { target, diff, autoMinMax }) => {
8
8
  const { verticalOhlcv, instances, priceBased, lastIndexReplace } = main;
9
9
  let suffix =
10
10
  typeof target === 'string' &&
@@ -43,6 +43,11 @@ export const movingAverages = (main, index, indicatorName, size, { target, diff
43
43
  for (const targetKey of diff.targets) {
44
44
  verticalOhlcv[`${diffKeyName}_${targetKey}`] = [...nullArray];
45
45
 
46
+ if(diff.autoMinMax)
47
+ {
48
+ main.autoMinMaxKeys.push(`${diffKeyName}_${targetKey}`)
49
+ }
50
+
46
51
  if (diff.lag > 0) {
47
52
  lagDiffArr.push(`${diffKeyName}_${targetKey}`);
48
53
  }
@@ -93,7 +98,7 @@ export const movingAverages = (main, index, indicatorName, size, { target, diff
93
98
  diffBoll = null;
94
99
  }
95
100
 
96
- const classified = classifyBoll(diffValue, diffBoll, diff.scale);
101
+ const classified = classifyBoll(diffValue, diffBoll, diff.scale, diff.autoMinMax);
97
102
  main.pushToMain({
98
103
  index,
99
104
  key: `${diffKeyName}_${targetKey}`,
@@ -134,7 +139,8 @@ export const getMovingAveragesParams = (indicatorName, size, options, validMagni
134
139
  scale = 0.05,
135
140
  targets = ['close'],
136
141
  size: diffSize = size,
137
- lag = 0
142
+ lag = 0,
143
+ autoMinMax = false
138
144
  } = diff;
139
145
 
140
146
  if (typeof stdDev !== 'number' || stdDev <= 0) {
@@ -157,7 +163,11 @@ export const getMovingAveragesParams = (indicatorName, size, options, validMagni
157
163
  throw new Error(`"diff.lag" must be an integer greater than 0 in ${indicatorName}.`);
158
164
  }
159
165
 
160
- optionArgs.diff = { stdDev, scale, size: diffSize, targets, lag };
166
+ if (typeof autoMinMax !== 'boolean') {
167
+ throw new Error(`"diff.autoMinMax" must be an boolean in ${indicatorName}.`);
168
+ }
169
+
170
+ optionArgs.diff = { stdDev, scale, size: diffSize, targets, lag, autoMinMax};
161
171
  }
162
172
 
163
173
  return optionArgs;
@@ -4,7 +4,7 @@ import { calcMagnitude } from '../utilities/numberUtilities.js';
4
4
 
5
5
  const defaultTarget = 'close';
6
6
 
7
- export const rsi = (main, index, size, { scale, target, lag }) => {
7
+ export const rsi = (main, index, size, { scale, target, lag, autoMinMax }) => {
8
8
  const { verticalOhlcv, instances, lastIndexReplace } = main;
9
9
  const suffix = target === defaultTarget ? '' : `_${target}`;
10
10
  const rsiKey = `rsi_${size}${suffix}`;
@@ -12,6 +12,7 @@ export const rsi = (main, index, size, { scale, target, lag }) => {
12
12
 
13
13
  // Initialization on the first index.
14
14
  if (index === 0) {
15
+
15
16
  const { crossPairsList, nullArray } = main;
16
17
 
17
18
  if (!verticalOhlcv.hasOwnProperty(target)) {
@@ -34,6 +35,12 @@ export const rsi = (main, index, size, { scale, target, lag }) => {
34
35
  {
35
36
  main.lag([rsiKey, rsiSmaKey], lag)
36
37
  }
38
+
39
+ if(autoMinMax)
40
+ {
41
+ main.autoMinMaxKeys.push(rsiKey, rsiSmaKey)
42
+ }
43
+
37
44
  }
38
45
 
39
46
  const value = verticalOhlcv[target][index];
@@ -7,13 +7,15 @@ const diff = (a, b) => (a - b) / b
7
7
  // Las diferencias estarán basadas en (objetivo - lower) / (upper - lower). Esto arrojará un valor entre -1 y 1
8
8
 
9
9
 
10
- export const candleStudies = (main, index, size, {stdDev, patternSize, lag, scale}) => {
10
+ export const candleVectors = (main, index, size, {stdDev, patternSize, lag, scale, autoMinMax}) => {
11
11
 
12
12
  const { verticalOhlcv, instances, lastIndexReplace } = main
13
- const calcSize = (di, bo) => classifyBoll(di, bo, scale)
13
+ const calcSize = (di, bo) => classifyBoll(di, bo, scale, autoMinMax)
14
14
 
15
15
  if(index === 0)
16
16
  {
17
+
18
+ console.log({autoMinMax})
17
19
  const {nullArray} = main
18
20
 
19
21
  const bodyVectors = [
@@ -42,6 +44,11 @@ export const candleStudies = (main, index, size, {stdDev, patternSize, lag, scal
42
44
  {
43
45
  lookBackVectorKeyNames.push(`candle_change_${x+1}_${currK}_${prevK}`)
44
46
  lookBackVectorsSetup[`candle_change_${x+1}_${currK}_${prevK}`] = [...nullArray]
47
+
48
+ if(autoMinMax)
49
+ {
50
+ main.autoMinMaxKeys.push(`candle_change_${x+1}_${currK}_${prevK}`)
51
+ }
45
52
  }
46
53
  }
47
54
 
@@ -53,6 +60,11 @@ export const candleStudies = (main, index, size, {stdDev, patternSize, lag, scal
53
60
  {
54
61
  bodyVectorKeyNames.push(`candle_body_${a}_${b}`)
55
62
  bodyVectorSetup[`candle_body_${a}_${b}`] = [...nullArray]
63
+
64
+ if(autoMinMax)
65
+ {
66
+ main.autoMinMaxKeys.push(`candle_body_${a}_${b}`)
67
+ }
56
68
  }
57
69
 
58
70
  const keyNames = [...bodyVectorKeyNames, ...lookBackVectorKeyNames]
@@ -60,7 +72,7 @@ export const candleStudies = (main, index, size, {stdDev, patternSize, lag, scal
60
72
  Object.assign(verticalOhlcv, {...bodyVectorSetup, ...lookBackVectorsSetup})
61
73
 
62
74
  Object.assign(instances, {
63
- candleStudies: {
75
+ candleVectors: {
64
76
  bodyVectors,
65
77
  lookBackVectors,
66
78
  keyNames,
@@ -78,7 +90,7 @@ export const candleStudies = (main, index, size, {stdDev, patternSize, lag, scal
78
90
  if (index === 0) return true
79
91
 
80
92
 
81
- const {bodyVectors, lookBackVectors, bodyInstance} = instances.candleStudies
93
+ const {bodyVectors, lookBackVectors, bodyInstance} = instances.candleVectors
82
94
 
83
95
  let bodyBoll = null
84
96
  const currOpen = verticalOhlcv.open[index]
@@ -20,7 +20,7 @@ import { calcMagnitude } from "./numberUtilities.js"
20
20
  */
21
21
 
22
22
 
23
- export const classifyBoll = (value, bollingerBands, scale = 0.05) => {
23
+ export const classifyBoll = (value, bollingerBands, scale = 0.05, autoMinMax = false) => {
24
24
 
25
25
  // Validate that value and bollingerBands are not null/undefined
26
26
  if (value == null || bollingerBands == null) return null
@@ -28,23 +28,17 @@ export const classifyBoll = (value, bollingerBands, scale = 0.05) => {
28
28
  const positive = value >= 0
29
29
  const absValue = Math.abs(value)
30
30
 
31
- const { upper, middle, lower } = bollingerBands
32
-
33
- // Handle values outside the Bollinger bands
34
- if (absValue < lower) return 0
35
- if (absValue > upper) return positive ? 1 : -1
36
-
37
- // Use a tolerance for floating point comparisons
38
- const epsilon = Number.EPSILON * Math.max(absValue, Math.abs(middle))
39
- if (Math.abs(absValue - middle) < epsilon) return positive ? 0.5 : -0.5
40
-
41
- // Prevent division by zero if upper and lower are equal
42
- if (upper === lower) return null
31
+ const { upper, lower } = bollingerBands
43
32
 
44
33
  const rangeValue = (absValue - lower) / (upper - lower)
45
34
  let magnitude = calcMagnitude(rangeValue, scale)
46
35
 
47
- //magnitude = Math.min(Math.max(magnitude, 0), 1)
36
+ if(autoMinMax)
37
+ {
38
+ return positive ? Math.min(magnitude, 1) : Math.max(-magnitude, -1)
39
+ }
40
+ else {
41
+ return positive ? magnitude : -magnitude
42
+ }
48
43
 
49
- return positive ? magnitude : -magnitude
50
44
  }
@@ -6,7 +6,7 @@ import { relativeVolume } from "../moving-averages/relativeVolume.js";
6
6
  import { donchianChannels } from "../moving-averages/donchianChannel.js";
7
7
  import { bollingerBands } from "../moving-averages/bollingerBands.js";
8
8
  import { volumeOscillator } from "../oscillators/volumeOscillator.js";
9
- import { candleStudies } from "../studies/candleStudies.js";
9
+ import { candleVectors } from "../studies/candleVectors.js";
10
10
  import { lag } from "../studies/lag.js";
11
11
  import { crossPairs } from "../studies/findCrosses.js";
12
12
  import { dateTime } from "../studies/dateTime.js";
@@ -25,7 +25,7 @@ const indicatorFunctions = {
25
25
  donchianChannels,
26
26
  bollingerBands,
27
27
  volumeOscillator,
28
- candleStudies
28
+ candleVectors
29
29
  };
30
30
 
31
31
  // Processes the inputParams into a list of indicator calls
@@ -0,0 +1,61 @@
1
+ export const pushToMain = ({main, index, key, value}) => {
2
+
3
+ const {precision, priceBased, minMaxRanges, verticalOhlcv, autoMinMaxKeys} = main
4
+ let {invalidValueIndex } = main
5
+
6
+ if(value === null || typeof value === 'undefined')
7
+ {
8
+ invalidValueIndex = index
9
+
10
+ for(let k in minMaxRanges)
11
+ {
12
+ minMaxRanges[k] = {min: Infinity, max: -Infinity}
13
+ }
14
+ }
15
+
16
+ verticalOhlcv[key][index] = value
17
+
18
+ if(typeof value === 'number' && !key.includes('_x_'))
19
+ {
20
+ const keyInAutoMinMax = autoMinMaxKeys.includes(key)
21
+
22
+ if(key.includes('_diff_') && keyInAutoMinMax)
23
+ {
24
+ if(!minMaxRanges.hasOwnProperty(key) || (minMaxRanges[key].min === Infinity || minMaxRanges[key].max === -Infinity))
25
+ {
26
+ minMaxRanges[key] = { min: -1, max: 1}
27
+ }
28
+ }
29
+ else if(key.startsWith('candle_') && keyInAutoMinMax)
30
+ {
31
+ minMaxRanges[key] = { min: -1, max: 1}
32
+ }
33
+ else if(key.includes('_range_') && keyInAutoMinMax)
34
+ {
35
+ minMaxRanges[key] = { min: 0, max: 1 }
36
+ }
37
+ else if(key.startsWith('rsi_') && keyInAutoMinMax)
38
+ {
39
+ minMaxRanges[key] = { min: 0, max: 1 }
40
+ }
41
+ else {
42
+ const newValue = (precision && priceBased.includes(key)) ? (value / precisionMultiplier) : value
43
+
44
+ if(!minMaxRanges.hasOwnProperty(key))
45
+ {
46
+ minMaxRanges[key] = { min: Infinity, max: -Infinity }
47
+ }
48
+
49
+ if(newValue < minMaxRanges[key].min)
50
+ {
51
+ minMaxRanges[key].min = newValue
52
+ }
53
+ if(newValue > minMaxRanges[key].max)
54
+ {
55
+ minMaxRanges[key].max = newValue
56
+ }
57
+ }
58
+ }
59
+
60
+ return true
61
+ }
package/webpack.config.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { resolve, dirname } from 'path';
2
2
  import { fileURLToPath } from 'url';
3
+ import TerserPlugin from 'terser-webpack-plugin';
4
+
3
5
  const __dirname = dirname(fileURLToPath(import.meta.url));
4
6
 
5
7
  const entry = './index.js';
@@ -14,6 +16,19 @@ export default {
14
16
  output,
15
17
  mode: 'production',
16
18
  target: 'web',
19
+ optimization: { //TerserPlugin start
20
+ minimize: true,
21
+ minimizer: [
22
+ new TerserPlugin({
23
+ terserOptions: {
24
+ output: {
25
+ beautify: true, // Beautify the output
26
+ comments: true, // Preserve comments
27
+ },
28
+ },
29
+ }),
30
+ ],
31
+ }, // TerserPlugin end
17
32
  module: {
18
33
  rules: [
19
34
  {