ohlcv-indicators 3.5.79 → 3.5.82

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,65 +1,53 @@
1
1
  import { FasterEMA } from '../core-indicators/index.js';
2
2
  import { validateInputValues } from '../utilities/validators.js';
3
3
  import { initializeColumns } from '../core-functions/initializeColumns.js';
4
-
4
+ import { mathLog } from '../utilities/math.js';
5
5
  const isBadNumber = v => v == null || !Number.isFinite(v);
6
+ const arithmeticReturn = (next, prev) => (next - prev) / prev;
7
+
6
8
 
7
9
  export const heikenAshi = (
8
10
  main,
9
11
  index,
10
12
  smoothLength,
11
13
  afterSmoothLength,
12
- { lag = 0, bothNull = false, retLogs = true } = {}
14
+ { lag = 0, noSmoothing = false, retLogs = false } = {}
13
15
  ) => {
14
16
  const { verticalOhlcv, instances, scaledGroups } = main;
15
- const indicatorKey = `${smoothLength}_${afterSmoothLength}`;
16
- const ohlcKeys = ['open', 'high', 'low', 'close'];
17
-
18
- const getRet = (next, prev) => retLogs
19
- ? Math.log(next / prev)
20
- : (next - prev) / prev;
21
-
22
- const prefix = retLogs ? 'ret_log_' : 'ret_';
23
-
24
- const paramsKey = bothNull ? '' : `_${indicatorKey}`;
25
-
26
- const getKey = key => `${prefix}heiken_ashi_${key}${paramsKey}`;
27
-
28
- const featureKeys = [
29
- 'body',
30
- 'upper_wick',
31
- 'lower_wick',
32
- 'range'
33
- ];
34
-
35
- const crossKey = bothNull
36
- ? 'heiken_ashi_cross'
37
- : `heiken_ashi_cross_${indicatorKey}`;
38
-
39
- const instanceKey = bothNull
17
+ const instanceKey = noSmoothing
40
18
  ? 'heiken_ashi'
41
- : `heiken_ashi_${indicatorKey}`;
19
+ : `heiken_ashi_${smoothLength}_${afterSmoothLength}`;
42
20
 
43
21
  // ---- INIT ----
44
22
  if (index === 0) {
45
23
  validateInputValues({ open: true, high: true, low: true, close: true }, verticalOhlcv, index, 'heikenAshi');
24
+ const ohlcKeys = ['open', 'high', 'low', 'close'];
25
+ const featureKeys = ['body', 'upper_wick', 'lower_wick', 'range'];
26
+ const paramsKey = noSmoothing ? '' : `_${smoothLength}_${afterSmoothLength}`;
27
+ const prefix = retLogs ? 'ret_log_' : 'ret_';
28
+ const outputKeys = Object.fromEntries(
29
+ featureKeys.map(key => [key, `${prefix}heiken_ashi_${key}${paramsKey}`])
30
+ );
31
+ const crossKey = `heiken_ashi_cross${paramsKey}`;
32
+ const keyNames = Object.values(outputKeys);
46
33
 
47
34
  instances[instanceKey] = {
48
- emaPre: !bothNull
35
+ emaPre: !noSmoothing
49
36
  ? Object.fromEntries(ohlcKeys.map(k => [k, new FasterEMA(smoothLength)]))
50
37
  : null,
51
- emaPost: !bothNull
38
+ emaPost: !noSmoothing
52
39
  ? Object.fromEntries(ohlcKeys.map(k => [k, new FasterEMA(afterSmoothLength)]))
53
40
  : null,
54
41
  prevHaOpen: NaN,
55
42
  prevHaClose: NaN,
56
43
  prevSmOpen: NaN,
57
44
  prevSmClose: NaN,
58
- isTrendUp: false,
45
+ trendDirection: 0,
46
+ getRet: retLogs ? mathLog : arithmeticReturn,
47
+ outputKeys,
48
+ crossKey
59
49
  };
60
50
 
61
- const keyNames = featureKeys.map(getKey);
62
-
63
51
  initializeColumns(main, [...keyNames, crossKey].map(key => ({ key })), { lag });
64
52
 
65
53
  if (scaledGroups) {
@@ -73,6 +61,7 @@ export const heikenAshi = (
73
61
  const low = verticalOhlcv.low[index];
74
62
  const close = verticalOhlcv.close[index];
75
63
  const inst = instances[instanceKey];
64
+ const { crossKey, emaPre, emaPost, getRet, outputKeys } = inst;
76
65
 
77
66
  if (
78
67
  isBadNumber(open) ||
@@ -85,19 +74,19 @@ export const heikenAshi = (
85
74
 
86
75
  let sOpen, sHigh, sLow, sClose;
87
76
 
88
- if (!bothNull) {
77
+ if (emaPre !== null) {
89
78
  // ---- PRE-SMOOTHING EMA ----
90
- inst.emaPre.open.update(open);
91
- inst.emaPre.high.update(high);
92
- inst.emaPre.low.update(low);
93
- inst.emaPre.close.update(close);
94
-
95
- if (!inst.emaPre.open.isStable || !inst.emaPre.high.isStable ||
96
- !inst.emaPre.low.isStable || !inst.emaPre.close.isStable) return;
97
- sOpen = inst.emaPre.open.getResult();
98
- sHigh = inst.emaPre.high.getResult();
99
- sLow = inst.emaPre.low.getResult();
100
- sClose = inst.emaPre.close.getResult();
79
+ emaPre.open.update(open);
80
+ emaPre.high.update(high);
81
+ emaPre.low.update(low);
82
+ emaPre.close.update(close);
83
+
84
+ if (!emaPre.open.isStable || !emaPre.high.isStable ||
85
+ !emaPre.low.isStable || !emaPre.close.isStable) return;
86
+ sOpen = emaPre.open.getResult();
87
+ sHigh = emaPre.high.getResult();
88
+ sLow = emaPre.low.getResult();
89
+ sClose = emaPre.close.getResult();
101
90
  } else {
102
91
  sOpen = open;
103
92
  sHigh = high;
@@ -132,19 +121,19 @@ export const heikenAshi = (
132
121
 
133
122
  let smOpen, smHigh, smLow, smClose;
134
123
 
135
- if (!bothNull) {
124
+ if (emaPost !== null) {
136
125
  // ---- POST-SMOOTHING EMA ----
137
- inst.emaPost.open.update(haOpen);
138
- inst.emaPost.high.update(haHigh);
139
- inst.emaPost.low.update(haLow);
140
- inst.emaPost.close.update(haClose);
141
-
142
- if (!inst.emaPost.open.isStable || !inst.emaPost.high.isStable ||
143
- !inst.emaPost.low.isStable || !inst.emaPost.close.isStable) return;
144
- smOpen = inst.emaPost.open.getResult();
145
- smHigh = inst.emaPost.high.getResult();
146
- smLow = inst.emaPost.low.getResult();
147
- smClose = inst.emaPost.close.getResult();
126
+ emaPost.open.update(haOpen);
127
+ emaPost.high.update(haHigh);
128
+ emaPost.low.update(haLow);
129
+ emaPost.close.update(haClose);
130
+
131
+ if (!emaPost.open.isStable || !emaPost.high.isStable ||
132
+ !emaPost.low.isStable || !emaPost.close.isStable) return;
133
+ smOpen = emaPost.open.getResult();
134
+ smHigh = emaPost.high.getResult();
135
+ smLow = emaPost.low.getResult();
136
+ smClose = emaPost.close.getResult();
148
137
  } else {
149
138
  smOpen = haOpen;
150
139
  smHigh = haHigh;
@@ -167,57 +156,49 @@ export const heikenAshi = (
167
156
 
168
157
  // ---- TREND/CROSS LOGIC ----
169
158
  // Cross uses raw smoothed HA values, not the returned log/return features.
170
- const prevCross = index > 0 ? verticalOhlcv[crossKey][index - 1] : 0;
171
- const prevSmOpen = inst.prevSmOpen;
172
- const prevSmClose = inst.prevSmClose;
173
-
174
- const crossUp = (
175
- Number.isFinite(prevSmOpen) &&
176
- Number.isFinite(prevSmClose) &&
177
- prevSmClose <= prevSmOpen &&
178
- smClose > smOpen
179
- );
180
-
181
- const crossDown = (
182
- Number.isFinite(prevSmOpen) &&
183
- Number.isFinite(prevSmClose) &&
184
- prevSmClose >= prevSmOpen &&
185
- smClose < smOpen
186
- );
187
-
188
- if (crossUp) inst.isTrendUp = true;
189
- if (crossDown) inst.isTrendUp = false;
190
-
191
- let cross = 0;
192
-
193
- if (index > 0) {
194
- if (inst.isTrendUp) {
195
- cross = prevCross > 0 ? prevCross + 1 : 1;
196
- } else {
197
- cross = prevCross < 0 ? prevCross - 1 : -1;
198
- }
159
+ const hasPrevious = (
160
+ Number.isFinite(inst.prevSmOpen) &&
161
+ Number.isFinite(inst.prevSmClose)
162
+ )
163
+
164
+ const prevCross = index > 0
165
+ ? verticalOhlcv[crossKey][index - 1]
166
+ : NaN
167
+
168
+ // A doji preserves the established direction.
169
+ // Before any direction exists, it remains neutral.
170
+ const direction = smClose > smOpen
171
+ ? 1
172
+ : smClose < smOpen
173
+ ? -1
174
+ : inst.trendDirection
175
+
176
+ let cross = 0
177
+
178
+ if (hasPrevious && direction !== 0) {
179
+ cross = Number.isFinite(prevCross) &&
180
+ Math.sign(prevCross) === direction
181
+ ? prevCross + direction
182
+ : direction
199
183
  }
200
184
 
201
- // Store previous raw smoothed HA values after cross calculation.
202
- inst.prevSmOpen = smOpen;
203
- inst.prevSmClose = smClose;
185
+ inst.trendDirection = direction
186
+ inst.prevSmOpen = smOpen
187
+ inst.prevSmClose = smClose
204
188
 
205
189
  // ---- HEIKEN ASHI STRUCTURE RETURNS ----
206
190
  const bodyTop = Math.max(smOpen, smClose);
207
191
  const bodyBottom = Math.min(smOpen, smClose);
208
192
 
209
- const row = {
210
- [getKey('body')]: getRet(smClose, smOpen),
211
- [getKey('upper_wick')]: getRet(smHigh, bodyTop),
212
- [getKey('lower_wick')]: getRet(bodyBottom, smLow),
213
- [getKey('range')]: getRet(smHigh, smLow),
214
- };
193
+ const body = getRet(smClose, smOpen);
194
+ const upperWick = getRet(smHigh, bodyTop);
195
+ const lowerWick = getRet(bodyBottom, smLow);
196
+ const range = getRet(smHigh, smLow);
215
197
 
216
- for (const [key, value] of Object.entries(row)) {
217
- if (!isBadNumber(value)) {
218
- main.pushToMain({ index, key, value });
219
- }
220
- }
198
+ if (!isBadNumber(body)) main.pushToMain({ index, key: outputKeys.body, value: body });
199
+ if (!isBadNumber(upperWick)) main.pushToMain({ index, key: outputKeys.upper_wick, value: upperWick });
200
+ if (!isBadNumber(lowerWick)) main.pushToMain({ index, key: outputKeys.lower_wick, value: lowerWick });
201
+ if (!isBadNumber(range)) main.pushToMain({ index, key: outputKeys.range, value: range });
221
202
 
222
203
  // Do not add return logs to cross.
223
204
  main.pushToMain({ index, key: crossKey, value: cross });
@@ -2,17 +2,21 @@
2
2
  import {FasterEMA, FasterSMA} from '../core-indicators/index.js';
3
3
  import { validateInputValues } from '../utilities/validators.js';
4
4
  import { initializeColumns } from '../core-functions/initializeColumns.js';
5
+ import { mathLog } from '../utilities/math.js';
6
+
7
+ const isBadNumber = v => v == null || !Number.isFinite(v);
8
+
5
9
 
6
10
  const indicatorClasses = {
7
11
  ema: FasterEMA,
8
12
  sma: FasterSMA
9
13
  }
10
14
 
11
- export const movingAverages = (main, index, methodName, size, { target, lag }) => {
15
+ export const movingAverages = (main, index, methodName, size, { target, lag, retLogs = false }) => {
12
16
 
13
17
  const { verticalOhlcv, instances, priceBased } = main
14
18
  const suffix = (target !== 'close') ? `_${target}` : ''
15
- const keyName = `${methodName}_${size}${suffix}`
19
+ const keyName = `${retLogs ? 'ret_log_' : ''}${methodName}_${size}${suffix}`
16
20
 
17
21
  if (index === 0) {
18
22
  validateInputValues({ [target]: true }, verticalOhlcv, index, methodName)
@@ -26,18 +30,49 @@ export const movingAverages = (main, index, methodName, size, { target, lag }) =
26
30
  // Create the main moving average instance.
27
31
  instances[keyName] = new indicatorClasses[methodName](size)
28
32
 
29
- initializeColumns(main, [{ key: keyName, priceBased: priceBased.has(target) }], { lag })
33
+ if(retLogs) {
34
+ initializeColumns(main, [
35
+ { key: `${keyName}_distance`, priceBased: false },
36
+ { key: `${keyName}_change`, priceBased: false }
37
+ ], { lag })
38
+ } else {
39
+ initializeColumns(main, [{ key: keyName, priceBased: priceBased.has(target) }], { lag })
40
+ }
41
+
30
42
  }
31
43
 
32
44
  // Retrieve the current price value
33
- const value = verticalOhlcv[target][index]
34
- const instance = instances[keyName]
45
+ const currVal = verticalOhlcv[target][index];
46
+
47
+
48
+ if(isBadNumber(currVal)) return;
49
+
50
+ const instance = instances[keyName];
51
+
52
+ //The easiest way to compute prevMa change is to retrieve the previous result before updating the indicator.
53
+ const prevMa = instance.isStable ? instance.getResult() : NaN;
35
54
 
36
55
  // Update the moving average instance.
37
- instance.update(value)
56
+ instance.update(currVal)
57
+
58
+ const currMa = instance.isStable ? instance.getResult() : NaN;
38
59
 
39
- const currMa = instance.isStable ? instance.getResult() : NaN
60
+ if(isBadNumber(currMa)) return;
61
+
62
+ if (retLogs) {
63
+
64
+ const ratio = currVal / currMa;
65
+ const distance = mathLog(currVal, currMa);
66
+
67
+ main.pushToMain({ index, key: `${keyName}_distance`, value: distance })
68
+
69
+ if(isBadNumber(prevMa)) return;
70
+
71
+ const change = mathLog(currMa, prevMa);
72
+
73
+ main.pushToMain({ index, key: `${keyName}_change`, value: change })
74
+ } else {
75
+ main.pushToMain({ index, key: keyName, value: currMa })
76
+ }
40
77
 
41
- // Always push the MA value (even if NaN).
42
- main.pushToMain({ index, key: keyName, value: currMa })
43
78
  }
@@ -1,12 +1,12 @@
1
1
  import { FasterSMA } from '../core-indicators/index.js';
2
- import { isPositiveInteger } from '../utilities/numberUtilities.js';
2
+ import { mathLog } from '../utilities/math.js';
3
3
  import { validateInputValues } from '../utilities/validators.js';
4
4
  import { initializeColumns } from '../core-functions/initializeColumns.js';
5
5
 
6
6
 
7
- export const relativeVolume = (main, index, size, {lag}) => {
7
+ export const relativeVolume = (main, index, size, {lag, retLogs = false}) => {
8
8
 
9
- const key = `relative_volume_${size}`;
9
+ const key = `${retLogs ? 'ret_log_' : ''}relative_volume_${size}`;
10
10
  const { instances, verticalOhlcv } = main;
11
11
 
12
12
 
@@ -42,9 +42,13 @@ export const relativeVolume = (main, index, size, {lag}) => {
42
42
  return
43
43
  }
44
44
 
45
- let currRelativeVolume = volume / prevSma;
45
+ const ratio = volume / prevSma;
46
+ const currRelativeVolume = retLogs
47
+ ? volume > 0 && prevSma > 0 && ratio > 0 && Number.isFinite(ratio)
48
+ ? mathLog(ratio, 1)
49
+ : NaN
50
+ : ratio;
46
51
 
47
52
  main.pushToMain({ index, key, value: currRelativeVolume });
48
- instances[key].prevRelativeVolumeSma = smaValue;
49
53
 
50
54
  };
@@ -0,0 +1,56 @@
1
+ import { mathLog } from '../utilities/math.js';
2
+ import { validateInputValues } from '../utilities/validators.js';
3
+ import { initializeColumns } from '../core-functions/initializeColumns.js';
4
+
5
+ export const mfi = (main, index, size, { lag, retLogs }) => {
6
+ const { verticalOhlcv, instances } = main;
7
+ const key = `${retLogs ? 'ret_log_' : ''}mfi_${size}`;
8
+
9
+ if (index === 0) {
10
+ validateInputValues({ high: true, low: true, close: true, volume: true }, verticalOhlcv, index, 'mfi');
11
+ // Raw and logged variants own separate state, even at the same period.
12
+ initializeColumns(main, [{ key }], { lag });
13
+ instances[key] = {
14
+ flows: new Float64Array(size),
15
+ next: 0,
16
+ count: 0,
17
+ previousTypical: NaN
18
+ };
19
+ }
20
+
21
+ const state = instances[key];
22
+ const typical = (verticalOhlcv.high[index] + verticalOhlcv.low[index] + verticalOhlcv.close[index]) / 3;
23
+ const change = typical - state.previousTypical;
24
+ state.previousTypical = typical;
25
+
26
+ // The first candle seeds the comparison; a size-period MFI needs size + 1 rows.
27
+ if (index === 0) return;
28
+
29
+ const flow = typical * verticalOhlcv.volume[index];
30
+ // A tie is neutral, but an invalid comparison must not silently become zero.
31
+ // Zero-volume candles contribute zero and still advance the candle window.
32
+ state.flows[state.next] = Number.isFinite(change) && Number.isFinite(flow) && flow >= 0
33
+ ? Math.sign(change) * flow : NaN;
34
+ state.next = (state.next + 1) % size;
35
+ if (state.count < size) state.count++;
36
+ if (state.count < size) return;
37
+
38
+ // Sum oldest to newest: bounded storage, no accumulated subtraction drift,
39
+ // and invalid values recover naturally after leaving the window.
40
+ let positive = 0;
41
+ let negative = 0;
42
+ let slot = state.next;
43
+ for (let step = 0; step < size; step++) {
44
+ const value = state.flows[slot];
45
+ if (Number.isNaN(value)) return;
46
+ if (value > 0) positive += value;
47
+ else if (value < 0) negative -= value;
48
+ if (++slot === size) slot = 0;
49
+ }
50
+
51
+ if (!Number.isFinite(positive) || !Number.isFinite(negative)) return;
52
+ // No directional money flow gives an undefined ratio, not a neutral signal.
53
+ const value = positive === 0 && negative === 0
54
+ ? NaN : 100 - 100 / (1 + positive / negative);
55
+ main.pushToMain({ index, key, value: Number.isNaN(value) ? NaN : retLogs ? mathLog(value, 50) : value });
56
+ };
@@ -7,7 +7,7 @@ import { initializeColumns } from '../core-functions/initializeColumns.js';
7
7
  export const volumeOscillator = (main, index, fast, slow, {lag, retLogs}) => {
8
8
 
9
9
  const {verticalOhlcv, instances} = main
10
- const key = `volume_oscillator_${fast}_${slow}`
10
+ const key = retLogs ? `ret_log_volume_oscillator_${fast}_${slow}` : `volume_oscillator_${fast}_${slow}`
11
11
 
12
12
  if (index === 0) {
13
13
  validateInputValues({ volume: true }, verticalOhlcv, index, 'volumeOscillator');
@@ -19,10 +19,7 @@ export const volumeOscillator = (main, index, fast, slow, {lag, retLogs}) => {
19
19
  }
20
20
  })
21
21
 
22
- initializeColumns(main, [
23
- { key },
24
- { key: `ret_log_${key}`, enabled: retLogs }
25
- ], { lag });
22
+ initializeColumns(main, [{ key }], { lag });
26
23
  }
27
24
 
28
25
  const { fastEMA, slowEMA } = instances[key];
@@ -46,12 +43,9 @@ export const volumeOscillator = (main, index, fast, slow, {lag, retLogs}) => {
46
43
  return;
47
44
  }
48
45
 
49
- volumeOscValue = 100 * (fastValue - slowValue) / slowValue
50
-
51
- if(retLogs) {
52
- volumeOscRetLog = mathLog(fastValue, slowValue)
53
- main.pushToMain({index, key: `ret_log_${key}`, value: volumeOscRetLog})
54
- }
46
+ volumeOscValue = retLogs
47
+ ? mathLog(fastValue, slowValue)
48
+ : 100 * (fastValue - slowValue) / slowValue
55
49
 
56
50
  main.pushToMain({index, key, value: volumeOscValue})
57
51
  };
@@ -56,14 +56,34 @@ export const candleFeatures = (main, index, {lag, colKeys, retLogs}) => {
56
56
  const upperWickTop = Math.max(currOpen, currClose)
57
57
  const lowerWickTop = Math.min(currOpen, currClose)
58
58
 
59
+ const change = getRet(currClose, prevClose)
60
+ const midPriceChange = getRet(currMidPrice, prevMidPrice)
61
+ const upperWick = getRet(currHigh, upperWickTop)
62
+ const lowerWick = getRet(lowerWickTop, currLow)
63
+ const gap = getRet(currOpen, prevClose)
64
+ const body = getRet(currClose, currOpen)
65
+ const range = getRet(currHigh, currLow)
66
+
67
+ if (colKeys.length === 0) {
68
+ main.pushToMain({ index, key: `${prefix}change`, value: change })
69
+ main.pushToMain({ index, key: `${prefix}mid_price_change`, value: midPriceChange })
70
+ main.pushToMain({ index, key: `${prefix}upper_wick`, value: upperWick })
71
+ main.pushToMain({ index, key: `${prefix}lower_wick`, value: lowerWick })
72
+ main.pushToMain({ index, key: `${prefix}gap`, value: gap })
73
+ main.pushToMain({ index, key: `${prefix}body`, value: body })
74
+ main.pushToMain({ index, key: `${prefix}range`, value: range })
75
+ return
76
+ }
77
+
78
+ // Keep optional-target overwrite/order semantics and all-or-nothing validation.
59
79
  const row = {
60
- [`${prefix}change`]: getRet(currClose, prevClose),
61
- [`${prefix}mid_price_change`]: getRet(currMidPrice, prevMidPrice),
62
- [`${prefix}upper_wick`]: getRet(currHigh, upperWickTop),
63
- [`${prefix}lower_wick`]: getRet(lowerWickTop, currLow),
64
- [`${prefix}gap`]: getRet(currOpen, prevClose),
65
- [`${prefix}body`]: getRet(currClose, currOpen),
66
- [`${prefix}range`]: getRet(currHigh, currLow)
80
+ [`${prefix}change`]: change,
81
+ [`${prefix}mid_price_change`]: midPriceChange,
82
+ [`${prefix}upper_wick`]: upperWick,
83
+ [`${prefix}lower_wick`]: lowerWick,
84
+ [`${prefix}gap`]: gap,
85
+ [`${prefix}body`]: body,
86
+ [`${prefix}range`]: range
67
87
  }
68
88
 
69
89
  let hasInvalidTarget = false
@@ -86,4 +106,4 @@ export const candleFeatures = (main, index, {lag, colKeys, retLogs}) => {
86
106
  main.pushToMain({index, key, value})
87
107
  }
88
108
 
89
- }
109
+ }
@@ -1,6 +1,8 @@
1
1
  import {oneHotEncode} from '../machine-learning/ml-utilities.js'
2
2
  import { initializeColumns } from '../core-functions/initializeColumns.js'
3
3
 
4
+ const weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
5
+
4
6
  export const dateTime = (main, index, {lag, oneHot, precompute}) => {
5
7
 
6
8
 
@@ -40,43 +42,44 @@ export const dateTime = (main, index, {lag, oneHot, precompute}) => {
40
42
 
41
43
  const currDate = verticalOhlcv.date[index]
42
44
 
43
- const dateInfo = getDateInfo(currDate, oneHot, colKeySizes, formatter)
44
-
45
- for(const [key, value] of Object.entries(dateInfo))
46
- {
47
- main.pushToMain({index, key, value})
45
+ let yearPart, monthPart, hourPart, minutePart, weekdayPart, dayPart
46
+ for (const part of formatter.formatToParts(currDate)) {
47
+ switch (part.type) {
48
+ case 'year': yearPart = part.value; break
49
+ case 'month': monthPart = part.value; break
50
+ case 'hour': hourPart = part.value; break
51
+ case 'minute': minutePart = part.value; break
52
+ case 'weekday': weekdayPart = part.value; break
53
+ case 'day': dayPart = part.value; break
54
+ }
55
+ }
56
+ const year = Number(yearPart)
57
+ const month = Number(monthPart) - 1
58
+ const hour = Number(hourPart)
59
+ const minute = Number(minutePart)
60
+ const dayOfWeek = weekdays.indexOf(weekdayPart)
61
+ const dayOfMonth = Number(dayPart) - 1
62
+
63
+ if (!oneHot) {
64
+ main.pushToMain({ index, key: 'year', value: year })
65
+ main.pushToMain({ index, key: 'month', value: month + 1 })
66
+ main.pushToMain({ index, key: 'hour', value: hour })
67
+ main.pushToMain({ index, key: 'minute', value: minute })
68
+ main.pushToMain({ index, key: 'day_of_the_week', value: ((dayOfWeek + 6) % 7) + 1 })
69
+ main.pushToMain({ index, key: 'day_of_the_month', value: dayOfMonth + 1 })
70
+ return
48
71
  }
49
- }
50
-
51
-
52
-
53
- const getDateInfo = (date, oneHot, colKeySizes, formatter) => {
54
-
55
-
56
- const parts = Object.fromEntries(formatter.formatToParts(date).map(({ type, value }) => [type, value]));
57
- const year = Number(parts.year);
58
- const month = Number(parts.month) - 1;
59
- const hour = Number(parts.hour);
60
- const minute = Number(parts.minute);
61
- const dayOfWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].indexOf(parts.weekday);
62
- const dayOfMonth = Number(parts.day) - 1;
63
-
64
- if (!oneHot) {
65
- return {
66
- year: year,
67
- month: month + 1, //iso month
68
- hour: hour,
69
- minute: minute,
70
- day_of_the_week: ((dayOfWeek + 6) % 7) + 1, //iso date of the week
71
- day_of_the_month: dayOfMonth + 1, //iso date of the month
72
- };
73
- }
74
72
 
75
- return {
76
- one_hot_month: oneHotEncode(month, colKeySizes.one_hot_month),
77
- one_hot_hour: oneHotEncode(hour, colKeySizes.one_hot_hour),
78
- one_hot_minute: oneHotEncode(minute, colKeySizes.one_hot_minute),
79
- one_hot_day_of_the_week: oneHotEncode(dayOfWeek, colKeySizes.one_hot_day_of_the_week),
80
- one_hot_day_of_the_month: oneHotEncode(dayOfMonth, colKeySizes.one_hot_day_of_the_month),
81
- };
82
- };
73
+ // Build a fresh vector for every field and row before writing.
74
+ const oneHotMonth = oneHotEncode(month, colKeySizes.one_hot_month)
75
+ const oneHotHour = oneHotEncode(hour, colKeySizes.one_hot_hour)
76
+ const oneHotMinute = oneHotEncode(minute, colKeySizes.one_hot_minute)
77
+ const oneHotWeekday = oneHotEncode(dayOfWeek, colKeySizes.one_hot_day_of_the_week)
78
+ const oneHotDay = oneHotEncode(dayOfMonth, colKeySizes.one_hot_day_of_the_month)
79
+
80
+ main.pushToMain({ index, key: 'one_hot_month', value: oneHotMonth })
81
+ main.pushToMain({ index, key: 'one_hot_hour', value: oneHotHour })
82
+ main.pushToMain({ index, key: 'one_hot_minute', value: oneHotMinute })
83
+ main.pushToMain({ index, key: 'one_hot_day_of_the_week', value: oneHotWeekday })
84
+ main.pushToMain({ index, key: 'one_hot_day_of_the_month', value: oneHotDay })
85
+ }
@@ -24,11 +24,7 @@ class CrossInstance {
24
24
  prevSlow: NaN,
25
25
  prevHigh: NaN,
26
26
  prevLow: NaN,
27
- areHighAndLowUndefined: false,
28
- crossIndexes: {
29
- up: [],
30
- down: []
31
- }
27
+ areHighAndLowUndefined: false
32
28
  })
33
29
 
34
30
  }
@@ -115,9 +111,6 @@ class CrossInstance {
115
111
  }
116
112
 
117
113
 
118
- if(this.interval === 1) this.crossIndexes.up.push(index)
119
- if(this.interval === -1) this.crossIndexes.down.push(index)
120
-
121
114
  //save prev state
122
115
  Object.assign(this, {
123
116
  prevState: currState,
@@ -238,4 +231,4 @@ export const crossPairs = (main, index, crossPairsList, {oneHot, limit}) => {
238
231
 
239
232
  }
240
233
 
241
- }
234
+ }