ohlcv-indicators 3.5.67 → 3.5.69

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
@@ -17,8 +17,6 @@ import { dateOutputFormaters } from './src/utilities/dateUtilities.js'
17
17
  import { defaultMapColsCallback } from './src/studies/mapCols.js'
18
18
  import { calcPrecisionMultiplier } from './src/utilities/precisionMultiplier.js'
19
19
 
20
- import { normalizeMinMax } from './src/machine-learning/ml-utilities.js'
21
-
22
20
  /**
23
21
  * Class OHLCV_INDICATORS
24
22
  *
@@ -28,13 +26,14 @@ import { normalizeMinMax } from './src/machine-learning/ml-utilities.js'
28
26
  */
29
27
 
30
28
  export default class OHLCV_INDICATORS {
31
- constructor({input, ticker = null, inputParams = null, chunkProcess = 2000, precision = false}) {
29
+ constructor({input, ticker = null, inputParams = null, chunkProcess = 2000, precision = false, useFullNames = false}) {
32
30
 
33
31
  validateArray(input, 'input', (ticker !== null) ? `contructor ${ticker}` : 'constuctor')
34
32
  if(input.length === 0) throw Error('input OHLCV must not be empty: ' + ticker)
35
33
 
36
34
  validateNumber(chunkProcess, {min: 100, max: 50000, allowDecimals: false}, 'chunkProcess', 'constructor')
37
35
  validateBoolean(precision, 'precision', 'contructor')
36
+ validateBoolean(precision, 'useFullNames', 'contructor')
38
37
 
39
38
  this.chunkProcess = chunkProcess
40
39
 
@@ -42,6 +41,7 @@ export default class OHLCV_INDICATORS {
42
41
  this.len = this.input.length
43
42
  this.firstRow = this.input[0]
44
43
  this.precision = precision
44
+ this.useFullNames = useFullNames
45
45
 
46
46
  const initialPriceBasedArr = ['open', 'high', 'low', 'close', 'mid_price']
47
47
  this.initialPriceBased = new Set(initialPriceBasedArr)
@@ -371,7 +371,7 @@ export default class OHLCV_INDICATORS {
371
371
 
372
372
  validateNumber(lag, {min: 0, max, allowDecimals: false}, 'options.lag', methodName)
373
373
 
374
- this.inputParams.push({key: methodName, order: 0, params: [kPeriod, kSlowingPeriod, dPeriod, {lag}]})
374
+ this.inputParams.push({key: methodName, params: [kPeriod, kSlowingPeriod, dPeriod, {lag}]})
375
375
 
376
376
  return this
377
377
  }
@@ -410,14 +410,11 @@ export default class OHLCV_INDICATORS {
410
410
  validateNumber(stdDev, {min: 0.01, max: 50, allowDecimals: true}, 'stdDev', methodName)
411
411
  validateObject(options, 'options', methodName)
412
412
 
413
- const {target = 'close', height = false, range = [], lag = 0} = options
413
+ const { lag = 0} = options
414
414
 
415
- validateString(target, 'options.target', methodName)
416
- validateArray(range, 'options.range', methodName)
417
415
  validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'options.lag', methodName)
418
- validateBoolean(height, 'options.height', methodName)
419
416
 
420
- this.inputParams.push({key: methodName, params: [size, stdDev, {target, height, range, lag}]});
417
+ this.inputParams.push({key: methodName, params: [size, stdDev, {lag}]});
421
418
 
422
419
  return this;
423
420
  }
@@ -450,13 +447,11 @@ export default class OHLCV_INDICATORS {
450
447
  validateNumber(offset, {min: 0, max: this.len, allowDecimals: false}, 'offset', methodName)
451
448
 
452
449
  validateObject(options, 'options', methodName)
453
- const { height = false, range = [], lag = 0} = options;
450
+ const { lag = 0} = options;
454
451
 
455
- validateArray(range, 'options.range', methodName)
456
452
  validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'options.lag', methodName)
457
- validateBoolean(height, 'options.height', methodName)
458
453
 
459
- this.inputParams.push({ key: methodName, order: 0, params: [size, offset, { height, range, lag}] });
454
+ this.inputParams.push({ key: methodName, order: 0, params: [size, offset, {lag}] });
460
455
 
461
456
  return this;
462
457
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ohlcv-indicators",
3
- "version": "3.5.67",
3
+ "version": "3.5.69",
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,126 +1,80 @@
1
1
  import { FasterBollingerBands } from 'trading-signals'
2
- const defaultTarget = 'close'
3
2
 
4
- export const bollingerBands = (main, index, size, stdDev, { height, range = [], target, lag }) => {
5
-
6
- const { verticalOhlcv, instances } = main;
7
- const indicatorKey = `${size}_${stdDev}`;
3
+ export const bollingerBands = (main, index, size, stdDev, { lag } = {}) => {
4
+ const { verticalOhlcv, instances, useFullNames } = main
5
+ const indicatorKey = `${size}_${stdDev}`
8
6
  const prefix = 'bollinger_bands'
7
+ const target = 'close'
9
8
 
10
9
  // Initialization on the first call.
11
10
  if (index === 0) {
12
-
13
- const {inputParams, verticalOhlcv, len, priceBased } = main;
11
+ const { inputParams, verticalOhlcv, len, priceBased } = main
14
12
 
15
13
  if (!(target in verticalOhlcv)) {
16
- throw new Error(`bollingerBands could not find target "${target}"`);
14
+ throw new Error(`bollingerBands could not find target "${target}"`)
17
15
  }
18
16
 
19
17
  let numberOfIndicators = 0
20
18
 
21
19
  for (const o of inputParams) {
22
- if (o.key === 'bollingerBands') numberOfIndicators++;
20
+ if (o.key === 'bollingerBands') numberOfIndicators++
23
21
  }
24
22
 
25
- // Only create the container if it doesn't already exist.
26
23
  if (!instances.bollinger_bands) {
27
24
  instances.bollinger_bands = {
28
25
  numberOfIndicators,
29
26
  settings: {}
30
- };
27
+ }
31
28
  }
32
- // Add (or override) the indicator instance keyed by indicatorKey.
33
- instances.bollinger_bands.settings[indicatorKey] = new FasterBollingerBands(size, stdDev);
34
-
35
- let suffix = ''
36
- if((numberOfIndicators > 1)) suffix += `_${indicatorKey}`
37
- if(target !== defaultTarget) suffix += `_${target}`
38
-
39
- const keyNames = [
40
- `${prefix}_upper`,
41
- `${prefix}_middle`,
42
- `${prefix}_lower`,
43
- ].map(v => `${v}${suffix}`)
44
29
 
30
+ instances.bollinger_bands.settings[indicatorKey] = new FasterBollingerBands(size, stdDev)
45
31
 
46
- if (height) {
47
- keyNames.push(
48
- `${prefix}_height${suffix}`
49
- )
50
- }
32
+ const suffix = (numberOfIndicators > 1 || useFullNames) ? `_${indicatorKey}` : ''
51
33
 
52
- // Set up additional arrays for each range property.
53
- for (const rangeKey of range) {
54
- if (!(rangeKey in verticalOhlcv)) {
55
- throw new Error(`Invalid range item value "${rangeKey}" property for bollingerBands.`);
56
- }
57
- keyNames.push(`${prefix}_range_${rangeKey}${suffix}`)
58
-
59
- }
34
+ const keyNames = [
35
+ `${prefix}_upper${suffix}`,
36
+ `${prefix}_middle${suffix}`,
37
+ `${prefix}_lower${suffix}`,
38
+ ]
60
39
 
61
- if(priceBased.has(target)) {
62
- for(const k of keyNames) {
40
+ if (priceBased.has(target)) {
41
+ for (const k of keyNames) {
63
42
  priceBased.add(k)
64
43
  }
65
44
  }
66
45
 
46
+ const verticalOhlcvSetup = Object.fromEntries(
47
+ keyNames.map(v => [v, new Float64Array(len).fill(NaN)])
48
+ )
67
49
 
68
- const verticalOhlcvSetup = Object.fromEntries(keyNames.map(v => [v, new Float64Array(len).fill(NaN)]))
69
- Object.assign(verticalOhlcv, {...verticalOhlcvSetup})
50
+ Object.assign(verticalOhlcv, { ...verticalOhlcvSetup })
70
51
 
71
- if(lag > 0)
72
- {
52
+ if (lag > 0) {
73
53
  main.lag(keyNames, lag)
74
54
  }
75
55
  }
76
56
 
77
- const {numberOfIndicators} = instances.bollinger_bands
57
+ const { numberOfIndicators } = instances.bollinger_bands
58
+ const suffix = (numberOfIndicators > 1 || useFullNames) ? `_${indicatorKey}` : ''
78
59
 
79
- let suffix = ''
80
- if((numberOfIndicators > 1)) suffix += `_${indicatorKey}`
81
- if(target !== defaultTarget) suffix += `_${target}`
82
-
83
- // Update the indicator with the current value.
84
60
  const instance = instances.bollinger_bands.settings[indicatorKey]
85
61
  const value = verticalOhlcv[target][index]
86
62
  instance.update(value)
87
63
 
88
- // Attempt to retrieve the result.
89
- let result = {};
64
+ let result = {}
90
65
  try {
91
- result = instance.getResult();
66
+ result = instance.getResult()
92
67
  } catch (err) {
93
68
  // If not available, result stays {}.
94
69
  }
95
70
 
96
- // Use NaN fallbacks for the primary values.
97
- const upper = result?.upper ?? NaN;
98
- const middle = result?.middle ?? NaN;
99
- const lower = result?.lower ?? NaN;
100
-
101
- // Always push the indicator outputs.
102
- main.pushToMain({ index, key: `${prefix}_upper${suffix}`, value: upper });
103
- main.pushToMain({ index, key: `${prefix}_middle${suffix}`, value: middle });
104
- main.pushToMain({ index, key: `${prefix}_lower${suffix}`, value: lower });
105
-
106
- // Process height if requested.
107
- if (height) {
108
- let heightValue = NaN;
109
- if (!Number.isNaN(lower) && !Number.isNaN(upper)) {
110
- heightValue = ((upper - lower) / lower)
111
- }
112
- main.pushToMain({ index, key: `${prefix}_height${suffix}`, value: heightValue });
113
- }
71
+ const upper = result?.upper ?? NaN
72
+ const middle = result?.middle ?? NaN
73
+ const lower = result?.lower ?? NaN
114
74
 
115
- // Process each range property.
116
- for (const rangeKey of range) {
117
- let rangeValue = NaN;
118
- const priceValue = verticalOhlcv[rangeKey][index];
119
- if (!Number.isNaN(priceValue) && !Number.isNaN(lower) && !Number.isNaN(upper)) {
120
- rangeValue = (priceValue - lower) / (upper - lower)
121
- }
122
- main.pushToMain({ index, key: `${prefix}_range_${rangeKey}${suffix}`, value: rangeValue });
123
- }
75
+ main.pushToMain({ index, key: `${prefix}_upper${suffix}`, value: upper })
76
+ main.pushToMain({ index, key: `${prefix}_middle${suffix}`, value: middle })
77
+ main.pushToMain({ index, key: `${prefix}_lower${suffix}`, value: lower })
124
78
 
125
- return true;
126
- }
79
+ return true
80
+ }
@@ -1,120 +1,82 @@
1
-
2
1
  export const donchianChannels = (main, index, size, offset, options) => {
3
-
4
-
5
-
6
- const {verticalOhlcv, instances, len, inputParams, lag, priceBased} = main
7
- const { height: includeHeight, range: rangeKeys } = options
2
+ const { verticalOhlcv, instances, len, inputParams, priceBased, useFullNames } = main
3
+ const { lag: outputLag = 0 } = options
8
4
  const indicatorKey = `${size}_${offset}`
9
5
 
10
6
  // ---- INIT (only at first bar) ----
11
7
  if (index === 0) {
12
- // how many donchianChannels calls in this session?
13
- const numberOfIndicators = inputParams.filter(o => o.key === 'donchianChannels').length;
14
- const basePrefix = 'donchian_channel';
15
- const prefix = numberOfIndicators > 1
16
- ? `${basePrefix}_${indicatorKey}`
17
- : basePrefix;
18
-
19
- // build all output keys once
20
- const keys = ['upper', 'basis', 'lower'].map(n => `${prefix}_${n}`);
21
- if (includeHeight) keys.push(`${prefix}_height`);
22
- for (const rk of rangeKeys) {
23
- if (!(rk in verticalOhlcv)) {
24
- throw new Error(
25
- `Invalid range key "${rk}".`
26
- );
27
- }
28
- keys.push(`${prefix}_range_${rk}`);
29
- }
8
+ const numberOfIndicators = inputParams.filter(o => o.key === 'donchianChannels').length
9
+ const useIndexedKeys = numberOfIndicators > 1 || useFullNames
10
+
11
+ const getKey = name =>
12
+ useIndexedKeys
13
+ ? `donchian_channel_${name}_${indicatorKey}`
14
+ : `donchian_channel_${name}`
30
15
 
31
- for(const k of keys) {
16
+ const keys = ['upper', 'basis', 'lower'].map(getKey)
17
+
18
+ for (const k of keys) {
32
19
  priceBased.add(k)
33
20
  }
34
21
 
35
- // bootstrap instance
36
22
  if (!instances.donchian_channel) {
37
- instances.donchian_channel = { numberOfIndicators, settings: {} };
23
+ instances.donchian_channel = { numberOfIndicators, settings: {} }
38
24
  }
39
- instances.donchian_channel.numberOfIndicators = numberOfIndicators;
40
- instances.donchian_channel.settings[indicatorKey] = { maxDeque: [], minDeque: [] };
41
25
 
42
- // allocate & fill NaNs
26
+ instances.donchian_channel.numberOfIndicators = numberOfIndicators
27
+ instances.donchian_channel.settings[indicatorKey] = { maxDeque: [], minDeque: [] }
28
+
43
29
  Object.assign(
44
30
  verticalOhlcv,
45
31
  Object.fromEntries(keys.map(k => [k, new Float64Array(len).fill(NaN)]))
46
- );
32
+ )
47
33
 
48
- // apply lag if requested
49
- if (lag > 0) main.lag(keys, lag)
34
+ if (outputLag > 0) {
35
+ main.lag(keys, outputLag)
36
+ }
50
37
  }
51
38
 
52
39
  // ---- PER-BAR COMPUTATION ----
53
- const { numberOfIndicators, settings } = instances.donchian_channel;
54
- const prefix = numberOfIndicators > 1
55
- ? `donchian_channel_${indicatorKey}`
56
- : 'donchian_channel';
40
+ const { numberOfIndicators, settings } = instances.donchian_channel
41
+ const useIndexedKeys = numberOfIndicators > 1 || useFullNames
57
42
 
58
- const { maxDeque, minDeque } = settings[indicatorKey];
59
- const current = index - offset;
60
- const start = current - size + 1;
43
+ const getKey = name =>
44
+ useIndexedKeys
45
+ ? `donchian_channel_${name}_${indicatorKey}`
46
+ : `donchian_channel_${name}`
61
47
 
62
- // prepare key lists again (cheap)
63
- const outKeys = ['upper','basis','lower'].map(n => `${prefix}_${n}`);
64
- if (includeHeight) outKeys.push(`${prefix}_height`);
65
- const rangeOut = rangeKeys.map(rk => `${prefix}_range_${rk}`);
48
+ const { maxDeque, minDeque } = settings[indicatorKey]
49
+ const current = index - offset
50
+ const start = current - size + 1
51
+
52
+ const outKeys = ['upper', 'basis', 'lower'].map(getKey)
66
53
 
67
- // not enough data → push NaNs
68
54
  if (start < 0 || current + 1 > len) {
69
- [...outKeys, ...rangeOut].forEach(key =>
55
+ outKeys.forEach(key =>
70
56
  main.pushToMain({ index, key, value: NaN })
71
- );
72
- return true;
57
+ )
58
+ return true
73
59
  }
74
60
 
75
- const { high: highs, low: lows } = verticalOhlcv;
61
+ const { high: highs, low: lows } = verticalOhlcv
76
62
 
77
- // tiny helper to keep deques up-to-date
78
63
  const update = (dq, arr, cmp) => {
79
- while (dq.length && dq[0] < start) dq.shift();
80
- while (dq.length && cmp(arr[dq.at(-1)], arr[current])) dq.pop();
81
- dq.push(current);
82
- };
83
- update(maxDeque, highs, (a,b) => a <= b);
84
- update(minDeque, lows, (a,b) => a >= b);
85
-
86
- // single “bounds exist?” flag instead of repeated isNaN
87
- const hasBounds = maxDeque.length && minDeque.length;
88
- const upper = hasBounds ? highs[maxDeque[0]] : NaN;
89
- const lower = hasBounds ? lows[minDeque[0]] : NaN;
90
- const basis = hasBounds ? (upper + lower) / 2 : NaN;
91
-
92
- main.pushToMain({ index, key: `${prefix}_upper`, value: upper });
93
- main.pushToMain({ index, key: `${prefix}_basis`, value: basis });
94
- main.pushToMain({ index, key: `${prefix}_lower`, value: lower });
95
-
96
- if (includeHeight) {
97
- // avoid divide-by-zero
98
- const heightVal = hasBounds && lower
99
- ? (upper - lower) / lower
100
- : NaN;
101
- main.pushToMain({ index, key: `${prefix}_height`, value: heightVal });
64
+ while (dq.length && dq[0] < start) dq.shift()
65
+ while (dq.length && cmp(arr[dq.at(-1)], arr[current])) dq.pop()
66
+ dq.push(current)
102
67
  }
103
68
 
104
- // precompute spread once
105
- const spread = hasBounds ? (upper - lower) : NaN;
69
+ update(maxDeque, highs, (a, b) => a <= b)
70
+ update(minDeque, lows, (a, b) => a >= b)
106
71
 
107
- // for each extra range key, use price===price to skip NaNs
72
+ const hasBounds = maxDeque.length && minDeque.length
73
+ const upper = hasBounds ? highs[maxDeque[0]] : NaN
74
+ const lower = hasBounds ? lows[minDeque[0]] : NaN
75
+ const basis = hasBounds ? (upper + lower) / 2 : NaN
108
76
 
109
- for(let i = 0; i < rangeKeys.length; i++)
110
- {
111
- const rk = rangeKeys[i]
112
- const price = verticalOhlcv[rk][index];
113
- const val = hasBounds && price === price && spread
114
- ? (price - lower) / spread
115
- : NaN;
116
- main.pushToMain({ index, key: rangeOut[i], value: val });
117
- }
77
+ main.pushToMain({ index, key: getKey('upper'), value: upper })
78
+ main.pushToMain({ index, key: getKey('basis'), value: basis })
79
+ main.pushToMain({ index, key: getKey('lower'), value: lower })
118
80
 
119
- return true;
120
- };
81
+ return true
82
+ }
@@ -2,14 +2,25 @@ import { FasterEMA } from 'trading-signals';
2
2
 
3
3
  export const heikenAshi = (main, index, smoothLength, afterSmoothLength, { lag = 0, bothNull = false } = {}) => {
4
4
  const { verticalOhlcv, instances, len, priceBased } = main;
5
- const prefix = (bothNull) ? 'heiken_ashi' : `heiken_ashi_${smoothLength}_${afterSmoothLength}`;
5
+ const indicatorKey = `${smoothLength}_${afterSmoothLength}`;
6
6
  const keys = ['open', 'high', 'low', 'close'];
7
7
 
8
+ const getKey = key =>
9
+ bothNull
10
+ ? `heiken_ashi_${key}`
11
+ : `heiken_ashi_${key}_${indicatorKey}`;
12
+
13
+ const crossKey = bothNull
14
+ ? 'heiken_ashi_cross'
15
+ : `heiken_ashi_cross_${indicatorKey}`;
16
+
17
+ const instanceKey = bothNull
18
+ ? 'heiken_ashi'
19
+ : `heiken_ashi_${indicatorKey}`;
20
+
8
21
  // ---- INIT ----
9
22
  if (index === 0) {
10
- // Initialize state
11
- instances[prefix] = {
12
- // Only allocate EMAs when smoothing is requested
23
+ instances[instanceKey] = {
13
24
  emaPre: !bothNull
14
25
  ? Object.fromEntries(keys.map(k => [k, new FasterEMA(smoothLength)]))
15
26
  : null,
@@ -21,13 +32,13 @@ export const heikenAshi = (main, index, smoothLength, afterSmoothLength, { lag =
21
32
  isTrendUp: false,
22
33
  };
23
34
 
24
- const keyNames = keys.map(k => `${prefix}_${k}`);
35
+ const keyNames = keys.map(getKey);
25
36
  const verticalOhlcvSetup = Object.fromEntries(
26
- [...keyNames, `${prefix}_cross`].map(v => [v, new Float64Array(len).fill(NaN)])
37
+ [...keyNames, crossKey].map(v => [v, new Float64Array(len).fill(NaN)])
27
38
  );
28
39
 
29
- for(const k of keyNames) {
30
- priceBased.add(k)
40
+ for (const k of keyNames) {
41
+ priceBased.add(k);
31
42
  }
32
43
 
33
44
  Object.assign(verticalOhlcv, { ...verticalOhlcvSetup });
@@ -42,7 +53,7 @@ export const heikenAshi = (main, index, smoothLength, afterSmoothLength, { lag =
42
53
  const high = verticalOhlcv.high[index];
43
54
  const low = verticalOhlcv.low[index];
44
55
  const close = verticalOhlcv.close[index];
45
- const inst = instances[prefix];
56
+ const inst = instances[instanceKey];
46
57
 
47
58
  let sOpen, sHigh, sLow, sClose;
48
59
 
@@ -59,17 +70,16 @@ export const heikenAshi = (main, index, smoothLength, afterSmoothLength, { lag =
59
70
  sLow = inst.emaPre.low.getResult();
60
71
  sClose = inst.emaPre.close.getResult();
61
72
  } catch {
62
- return true; // skip if not enough data
73
+ return true;
63
74
  }
64
75
  } else {
65
- // No pre-smoothing: use raw OHLC
66
76
  sOpen = open;
67
77
  sHigh = high;
68
78
  sLow = low;
69
79
  sClose = close;
70
80
  }
71
81
 
72
- // ---- HEIKEN ASHI CORE (from s*) ----
82
+ // ---- HEIKEN ASHI CORE ----
73
83
  const haClose = (sOpen + sHigh + sLow + sClose) / 4;
74
84
  const haOpen = (Number.isNaN(inst.prevHaOpen) || Number.isNaN(inst.prevHaClose))
75
85
  ? (sOpen + sClose) / 2
@@ -77,7 +87,6 @@ export const heikenAshi = (main, index, smoothLength, afterSmoothLength, { lag =
77
87
  const haHigh = Math.max(sHigh, haOpen, haClose);
78
88
  const haLow = Math.min(sLow, haOpen, haClose);
79
89
 
80
- // Store for next round
81
90
  inst.prevHaOpen = haOpen;
82
91
  inst.prevHaClose = haClose;
83
92
 
@@ -96,20 +105,19 @@ export const heikenAshi = (main, index, smoothLength, afterSmoothLength, { lag =
96
105
  smLow = inst.emaPost.low.getResult();
97
106
  smClose = inst.emaPost.close.getResult();
98
107
  } catch {
99
- return true; // skip if not enough data
108
+ return true;
100
109
  }
101
110
  } else {
102
- // No post-smoothing: final values are plain HA
103
111
  smOpen = haOpen;
104
112
  smHigh = haHigh;
105
113
  smLow = haLow;
106
114
  smClose = haClose;
107
115
  }
108
116
 
109
- // ---- TREND/CROSS LOGIC (works for both modes) ----
110
- const prevCross = index > 0 ? verticalOhlcv[`${prefix}_cross`][index - 1] : 0;
111
- const prevHaOpenArr = index > 0 ? verticalOhlcv[`${prefix}_open`][index - 1] : NaN;
112
- const prevHaCloseArr = index > 0 ? verticalOhlcv[`${prefix}_close`][index - 1] : NaN;
117
+ // ---- TREND/CROSS LOGIC ----
118
+ const prevCross = index > 0 ? verticalOhlcv[crossKey][index - 1] : 0;
119
+ const prevHaOpenArr = index > 0 ? verticalOhlcv[getKey('open')][index - 1] : NaN;
120
+ const prevHaCloseArr = index > 0 ? verticalOhlcv[getKey('close')][index - 1] : NaN;
113
121
 
114
122
  const crossUp = !Number.isNaN(prevHaCloseArr) && prevHaCloseArr <= prevHaOpenArr && smClose > smOpen;
115
123
  const crossDown = !Number.isNaN(prevHaCloseArr) && prevHaCloseArr >= prevHaOpenArr && smClose < smOpen;
@@ -127,11 +135,11 @@ export const heikenAshi = (main, index, smoothLength, afterSmoothLength, { lag =
127
135
  }
128
136
 
129
137
  // ---- PUSH OUTPUTS ----
130
- main.pushToMain({ index, key: `${prefix}_open`, value: smOpen });
131
- main.pushToMain({ index, key: `${prefix}_high`, value: smHigh });
132
- main.pushToMain({ index, key: `${prefix}_low`, value: smLow });
133
- main.pushToMain({ index, key: `${prefix}_close`, value: smClose });
134
- main.pushToMain({ index, key: `${prefix}_cross`, value: cross });
138
+ main.pushToMain({ index, key: getKey('open'), value: smOpen });
139
+ main.pushToMain({ index, key: getKey('high'), value: smHigh });
140
+ main.pushToMain({ index, key: getKey('low'), value: smLow });
141
+ main.pushToMain({ index, key: getKey('close'), value: smClose });
142
+ main.pushToMain({ index, key: crossKey, value: cross });
135
143
 
136
144
  return true;
137
- };
145
+ };
@@ -1,13 +1,10 @@
1
- import { FasterEMA, FasterMACD } from 'trading-signals';
1
+ import { FasterEMA, FasterMACD } from 'trading-signals'
2
2
 
3
- const defaultTarget = 'close';
3
+ const defaultTarget = 'close'
4
4
 
5
- export const macd = (main, index, fast, slow, signal, {target, lag, precomputed}) => {
6
-
7
-
8
-
9
- const { verticalOhlcv, instances, priceBased } = main
10
- const {instanceKey} = precomputed
5
+ export const macd = (main, index, fast, slow, signal, { target, lag, precomputed }) => {
6
+ const { verticalOhlcv, instances, priceBased, useFullNames } = main
7
+ const { instanceKey } = precomputed
11
8
 
12
9
  // Initialization on the first index.
13
10
  if (index === 0) {
@@ -23,13 +20,25 @@ export const macd = (main, index, fast, slow, signal, {target, lag, precomputed}
23
20
  if (o.key === 'macd') numberOfIndicators++
24
21
  }
25
22
 
26
- // Choose a display prefix based on the number of indicators.
27
- const displayPrefix = numberOfIndicators > 1 ? `macd_${fast}_${slow}_${signal}` : 'macd'
23
+ const useIndexedKeys = numberOfIndicators > 1 || useFullNames
24
+ const indicatorSuffix = `${fast}_${slow}_${signal}`
25
+
26
+ const getKey = name => {
27
+ if (target === defaultTarget) {
28
+ return useIndexedKeys
29
+ ? `macd_${name}_${indicatorSuffix}`
30
+ : `macd_${name}`
31
+ }
32
+
33
+ return useIndexedKeys
34
+ ? `macd_${name}_${target}_${indicatorSuffix}`
35
+ : `macd_${name}_${target}`
36
+ }
28
37
 
29
38
  // Build the keys.
30
- const diffKey = target === defaultTarget ? `${displayPrefix}_diff` : `${displayPrefix}_diff_${target}`
31
- const deaKey = target === defaultTarget ? `${displayPrefix}_dea` : `${displayPrefix}_dea_${target}`
32
- const histogramKey = target === defaultTarget ? `${displayPrefix}_histogram` : `${displayPrefix}_histogram_${target}`
39
+ const diffKey = getKey('diff')
40
+ const deaKey = getKey('dea')
41
+ const histogramKey = getKey('histogram')
33
42
 
34
43
  if (!instances.hasOwnProperty('macd')) {
35
44
  instances.macd = {
@@ -52,22 +61,34 @@ export const macd = (main, index, fast, slow, signal, {target, lag, precomputed}
52
61
 
53
62
  const keyNames = [diffKey, deaKey, histogramKey]
54
63
 
55
- for(const k of keyNames) {
64
+ for (const k of keyNames) {
56
65
  priceBased.add(k)
57
66
  }
58
67
 
59
- if(lag > 0)
60
- {
68
+ if (lag > 0) {
61
69
  main.lag(keyNames, lag)
62
70
  }
63
-
64
71
  }
65
72
 
66
73
  const { numberOfIndicators, settings } = instances.macd
67
- const displayPrefix = numberOfIndicators > 1 ? `macd_${fast}_${slow}_${signal}` : 'macd'
68
- const diffKey = target === defaultTarget ? `${displayPrefix}_diff` : `${displayPrefix}_diff_${target}`
69
- const deaKey = target === defaultTarget ? `${displayPrefix}_dea` : `${displayPrefix}_dea_${target}`
70
- const histogramKey = target === defaultTarget ? `${displayPrefix}_histogram` : `${displayPrefix}_histogram_${target}`
74
+ const useIndexedKeys = numberOfIndicators > 1 || useFullNames
75
+ const indicatorSuffix = `${fast}_${slow}_${signal}`
76
+
77
+ const getKey = name => {
78
+ if (target === defaultTarget) {
79
+ return useIndexedKeys
80
+ ? `macd_${name}_${indicatorSuffix}`
81
+ : `macd_${name}`
82
+ }
83
+
84
+ return useIndexedKeys
85
+ ? `macd_${name}_${target}_${indicatorSuffix}`
86
+ : `macd_${name}_${target}`
87
+ }
88
+
89
+ const diffKey = getKey('diff')
90
+ const deaKey = getKey('dea')
91
+ const histogramKey = getKey('histogram')
71
92
 
72
93
  const macdInstance = settings[instanceKey]
73
94
  const value = verticalOhlcv[target][index]
@@ -86,4 +107,4 @@ export const macd = (main, index, fast, slow, signal, {target, lag, precomputed}
86
107
  main.pushToMain({ index, key: histogramKey, value: macdResult ? macdResult.histogram : NaN })
87
108
 
88
109
  return true
89
- };
110
+ }
@@ -4,9 +4,9 @@ export const stochastic = (main, index, kPeriod, kSlowingPeriod, dPeriod, {lag})
4
4
 
5
5
 
6
6
 
7
- const { verticalOhlcv, instances } = main;
7
+ const { verticalOhlcv, instances , useFullNames} = main;
8
8
 
9
- const paramsKey = (kPeriod === 14 && kSlowingPeriod === 3 && dPeriod === 3) ? '' : `${kPeriod}_${kSlowingPeriod}_${dPeriod}`
9
+ const paramsKey = ((kPeriod === 14 && kSlowingPeriod === 3 && dPeriod === 3) && !useFullNames) ? '' : `_${kPeriod}_${kSlowingPeriod}_${dPeriod}`
10
10
  const stochD = `stoch_d${paramsKey}`;
11
11
  const stochK = `stoch_k${paramsKey}`;
12
12
  const instanceKey = paramsKey