ohlcv-indicators 1.9.2 → 1.9.3

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,47 +1,43 @@
1
- import { FasterEMA } from 'trading-signals'
2
- import { findCrosses } from "../studies/findCrosses.js";
1
+ import { FasterEMA } from 'trading-signals';
3
2
 
3
+ export const volumeOscillator = (main, index, fastSize = 5, slowSize = 10) => {
4
4
 
5
- export const volumeOscillator = (main, fastPeriod = 5, slowPeriod = 10) => {
6
- const { verticalOhlcv } = main;
7
- const { volume } = verticalOhlcv;
5
+ const value = main.verticalOhlcv.volume[index]
8
6
 
9
- return getVolumeOscillator(volume, fastPeriod, slowPeriod);
10
-
11
- }
12
-
13
- export const getVolumeOscillator = (volume, fastPeriod, slowPeriod) => {
14
- const fastEMA = new FasterEMA(fastPeriod)
15
- const slowEMA = new FasterEMA(slowPeriod)
16
- const volume_oscillator = new Array(volume.length).fill(null)
17
-
18
- for (let i = 0; i < volume.length; i++) {
19
-
20
- fastEMA.update(volume[i])
21
- slowEMA.update(volume[i])
7
+ if (!main.instances.hasOwnProperty(`volume_oscillator`)) {
8
+ main.instances[`volume_oscillator`] = {
9
+ fastEMA: new FasterEMA(fastSize),
10
+ slowEMA: new FasterEMA(slowSize)
11
+ };
12
+ main.verticalOhlcv[`volume_oscillator`] = new Array(main.len).fill(null);
13
+ main.autoCrossPairsList.push({ fast: `volume_oscillator`, slow: 0 });
14
+ }
22
15
 
23
- let fastValue = null
24
- let slowValue = null
16
+ const { fastEMA, slowEMA } = main.instances[`volume_oscillator`];
25
17
 
26
- try {
27
- fastValue = fastEMA.getResult()
28
- } catch (err) {
29
- fastValue = null
30
- }
18
+ fastEMA.update(value);
19
+ slowEMA.update(value);
31
20
 
32
- try {
33
- slowValue = slowEMA.getResult()
34
- } catch (err) {
35
- slowValue = null
36
- }
21
+ let fastValue = null;
22
+ let slowValue = null;
37
23
 
38
- if (typeof fastValue === 'number' && typeof slowValue === 'number' && slowValue !== 0) {
24
+ try {
25
+ fastValue = fastEMA.getResult();
26
+ } catch (err) {
27
+ fastValue = null;
28
+ }
39
29
 
40
- volume_oscillator[i] = 100 * (fastValue - slowValue) / slowValue
41
- }
30
+ try {
31
+ slowValue = slowEMA.getResult();
32
+ } catch (err) {
33
+ slowValue = null;
42
34
  }
43
35
 
44
- const volume_oscillator_x_0 = findCrosses({fast: volume_oscillator, slow: new Array(volume.length).fill(0)})
36
+ if (typeof fastValue === 'number' && typeof slowValue === 'number' && slowValue !== 0) {
37
+ main.verticalOhlcv[`volume_oscillator`][index] = 100 * (fastValue - slowValue) / slowValue
38
+ } else {
39
+ main.verticalOhlcv[`volume_oscillator`][index] = null;
40
+ }
45
41
 
46
- return {volume_oscillator, volume_oscillator_x_0}
47
- }
42
+ return true;
43
+ };
@@ -1,184 +1,172 @@
1
- import {FasterSMA} from 'trading-signals'
2
- import { classifySize } from '../utilities/classification.js'
3
-
4
- export const candlesStudies = (main, period, classify, classificationLevels) => {
5
-
6
- const {inputOhlcv, len} = main
7
-
8
- const cols = getCandlesStudies(inputOhlcv, period, len, classify, classificationLevels)
9
- return cols
10
- }
11
-
12
- const getCandlesStudies = (inputOhlcv, period = 20, len, classify = true, classificationLevels = {}) => {
13
-
14
- const {changeLevel = 7, sizeLevel = 5} = classificationLevels
15
-
16
- //close > open
17
- let candle_body_size = new Array(len).fill(null) //scaled candle direction close - open
18
- let candle_top_size = new Array(len).fill(null) //scaled top size (0, 0.5 and 1)
19
- let candle_bottom_size = new Array(len).fill(null) //scaled bottom size(0, 0.5 and 1)
20
- let candle_gap_size = new Array(len).fill(null) //scaled gap between the current open and previous close (-1 to 1)
21
- let candle_shadow_size = new Array(len).fill(null) //scaled shadow size high - slow
22
- let candle_close_direction = new Array(len).fill(null) //scaled shadow size curr.close - prev.close
23
- let candle_high_direction = new Array(len).fill(null) //scaled shadow size curr.high - prev.high
24
- let candle_low_direction = new Array(len).fill(null) //scaled shadow size curr.low - prev.low
25
- let candle_open_direction = new Array(len).fill(null) //scaled shadow size curr.open - prev.open
26
-
27
- //instances
28
- let topInstance
29
- let bottomInstance
30
- let gapInstance
31
- let bodySizeInstance
32
- let shadowSizeInstance
33
- let closeDirectionInstance
34
- let highDirectionInstance
35
- let lowDirectionInstance
36
- let openDirectionInstance
37
-
38
- if(classify)
39
- {
40
- topInstance = new FasterSMA(period)
41
- bottomInstance = new FasterSMA(period)
42
- gapInstance = new FasterSMA(period)
43
- bodySizeInstance = new FasterSMA(period)
44
- shadowSizeInstance = new FasterSMA(period)
45
- closeDirectionInstance = new FasterSMA(period)
46
- highDirectionInstance = new FasterSMA(period)
47
- lowDirectionInstance = new FasterSMA(period)
48
- openDirectionInstance = new FasterSMA(period)
1
+ import { FasterSMA } from 'trading-signals';
2
+ import { classifySize } from '../utilities/classification.js';
3
+
4
+ export const candlesStudies = (main, index, size, classify = true, classificationLevels = {}) => {
5
+
6
+
7
+
8
+ const { changeLevel = 7, sizeLevel = 5 } = classificationLevels;
9
+
10
+ // Ensure arrays exist in main.verticalOhlcv
11
+ const fields = [
12
+ 'candle_gap_size',
13
+ 'candle_body_size',
14
+ 'candle_top_size',
15
+ 'candle_bottom_size',
16
+ 'candle_shadow_size',
17
+ 'candle_close_direction',
18
+ 'candle_high_direction',
19
+ 'candle_low_direction',
20
+ 'candle_open_direction'
21
+ ];
22
+
23
+ for (const field of fields) {
24
+ if (!main.verticalOhlcv.hasOwnProperty(field)) {
25
+ main.verticalOhlcv[field] = new Array(main.len).fill(null);
26
+ }
49
27
  }
50
28
 
51
- for(let x = 0; x < len; x++)
52
- {
53
- const curr = inputOhlcv[x]
54
- const prev = inputOhlcv[x-1]
55
-
56
- if(typeof prev === 'undefined') continue
57
-
58
- const {open, high, low, close} = curr
59
-
60
-
61
- //initial means
62
- let gapMean = null
63
- let bottomSizeMean = null
64
- let topSizeMean = null
65
- let bodySizeMean = null
66
- let shadowSizeMean = null
67
- let closeDirectionMean = null
68
- let highDirectionMean = null
69
- let lowDirectionMean = null
70
- let openDirectionMean = null
71
-
72
- //directions
73
- const isUp = close > open
74
- const candleBodySize = Math.abs(close - open)
75
- const shadowSize = Math.abs(curr.high - curr.low)
76
-
77
- //sizes
78
- const topSize = high - Math.max(open, close)
79
- const bottomSize = Math.min(open, close) - low
80
- const gapSize = Math.abs(prev.close - curr.open)
81
- const closeDirection = Math.abs(curr.close - prev.close)
82
- const highDirection = Math.abs(curr.high - prev.high)
83
- const lowDirection = Math.abs(curr.low - prev.low)
84
- const openDirection = Math.abs(curr.open - prev.open)
85
-
86
- if(classify)
87
- {
88
- //instances
89
- topInstance.update(Math.abs(topSize))
90
- bottomInstance.update(Math.abs(bottomSize))
91
- shadowSizeInstance.update(Math.abs(shadowSize))
92
-
93
- gapInstance.update(gapSize)
94
- bodySizeInstance.update(candleBodySize)
95
- closeDirectionInstance.update(closeDirection)
96
- highDirectionInstance.update(highDirection)
97
- lowDirectionInstance.update(lowDirection)
98
- openDirectionInstance.update(openDirection)
99
-
100
- try
101
- {
102
- topSizeMean = topInstance.getResult()
103
- bottomSizeMean = bottomInstance.getResult()
104
- gapMean = gapInstance.getResult()
105
- bodySizeMean = bodySizeInstance.getResult()
106
- shadowSizeMean = shadowSizeInstance.getResult()
107
- closeDirectionMean = closeDirectionInstance.getResult()
108
- highDirectionMean = highDirectionInstance.getResult()
109
- lowDirectionMean = lowDirectionInstance.getResult()
110
- openDirectionMean = openDirectionInstance.getResult()
111
- }
112
- catch(err)
113
- {
114
- bodySizeMean = null
115
- gapMean = null
116
- topSizeMean = null
117
- bottomSizeMean = null
118
- shadowSizeMean = null
119
- closeDirectionMean = null
120
- highDirectionMean = null
121
- lowDirectionMean = null
122
- openDirectionMean = null
29
+ // Ensure instances exist if classification is enabled
30
+ if (classify) {
31
+ const instanceNames = [
32
+ 'topInstance',
33
+ 'bottomInstance',
34
+ 'gapInstance',
35
+ 'bodySizeInstance',
36
+ 'shadowSizeInstance',
37
+ 'closeDirectionInstance',
38
+ 'highDirectionInstance',
39
+ 'lowDirectionInstance',
40
+ 'openDirectionInstance'
41
+ ];
42
+
43
+ // Create a namespace for candle studies if it doesn't exist
44
+ if (!main.instances.hasOwnProperty('candleStudies')) {
45
+ main.instances.candleStudies = {};
46
+ }
47
+
48
+ // Create instances if they don't exist
49
+ for (const name of instanceNames) {
50
+ if (!main.instances.candleStudies.hasOwnProperty(name)) {
51
+ main.instances.candleStudies[name] = new FasterSMA(size);
123
52
  }
53
+ }
54
+ }
124
55
 
125
- //negatives and positives
126
- candle_body_size[x] = (isUp)
127
- ? classifySize(candleBodySize, bodySizeMean, 2, changeLevel)
56
+ // If we do not have a previous candle, we cannot compute differences
57
+ const prevOpen = main.verticalOhlcv.open[index-1]
58
+ const prevHigh = main.verticalOhlcv.high[index-1]
59
+ const prevLow = main.verticalOhlcv.low[index-1]
60
+ const prevClose = main.verticalOhlcv.close[index-1]
61
+
62
+ if ([prevOpen, prevHigh, prevLow, prevClose].some(v => typeof v === 'undefined')) return true;
63
+
64
+ const currOpen = main.verticalOhlcv.open[index]
65
+ const currHigh = main.verticalOhlcv.high[index]
66
+ const currLow = main.verticalOhlcv.low[index]
67
+ const currClose = main.verticalOhlcv.close[index]
68
+
69
+ // Determine candle direction and sizes
70
+ const isUp = currClose > currOpen;
71
+ const candleBodySize = Math.abs(currClose - currOpen);
72
+ const shadowSize = Math.abs(currHigh - currLow);
73
+
74
+ const topSize = currHigh - Math.max(currOpen, currClose);
75
+ const bottomSize = Math.min(currOpen, currClose) - currLow;
76
+ const gapSize = Math.abs(prevClose - currOpen);
77
+ const closeDirection = Math.abs(currClose - prevClose);
78
+ const highDirection = Math.abs(currHigh - prevHigh);
79
+ const lowDirection = Math.abs(currLow - prevLow);
80
+ const openDirection = Math.abs(currOpen - prevOpen);
81
+
82
+ let gapMean = null;
83
+ let bottomSizeMean = null;
84
+ let topSizeMean = null;
85
+ let bodySizeMean = null;
86
+ let shadowSizeMean = null;
87
+ let closeDirectionMean = null;
88
+ let highDirectionMean = null;
89
+ let lowDirectionMean = null;
90
+ let openDirectionMean = null;
91
+
92
+ if (classify) {
93
+ const {
94
+ topInstance,
95
+ bottomInstance,
96
+ gapInstance,
97
+ bodySizeInstance,
98
+ shadowSizeInstance,
99
+ closeDirectionInstance,
100
+ highDirectionInstance,
101
+ lowDirectionInstance,
102
+ openDirectionInstance
103
+ } = main.instances.candleStudies;
104
+
105
+ // Update instances with current absolute values
106
+ topInstance.update(Math.abs(topSize));
107
+ bottomInstance.update(Math.abs(bottomSize));
108
+ shadowSizeInstance.update(Math.abs(shadowSize));
109
+ gapInstance.update(gapSize);
110
+ bodySizeInstance.update(candleBodySize);
111
+ closeDirectionInstance.update(closeDirection);
112
+ highDirectionInstance.update(highDirection);
113
+ lowDirectionInstance.update(lowDirection);
114
+ openDirectionInstance.update(openDirection);
115
+
116
+ try {
117
+ topSizeMean = topInstance.getResult();
118
+ bottomSizeMean = bottomInstance.getResult();
119
+ gapMean = gapInstance.getResult();
120
+ bodySizeMean = bodySizeInstance.getResult();
121
+ shadowSizeMean = shadowSizeInstance.getResult();
122
+ closeDirectionMean = closeDirectionInstance.getResult();
123
+ highDirectionMean = highDirectionInstance.getResult();
124
+ lowDirectionMean = lowDirectionInstance.getResult();
125
+ openDirectionMean = openDirectionInstance.getResult();
126
+ } catch (err) {
127
+ // If there's not enough data, means remain null
128
+ }
129
+
130
+ main.verticalOhlcv['candle_body_size'][index] = isUp
131
+ ? classifySize(candleBodySize, bodySizeMean, 2, changeLevel)
128
132
  : -Math.abs(classifySize(candleBodySize, bodySizeMean, 2, changeLevel)) || 0;
129
133
 
130
- candle_gap_size[x] = (isUp)
131
- ? classifySize(gapSize, gapMean, 2, changeLevel)
134
+ main.verticalOhlcv['candle_gap_size'][index] = isUp
135
+ ? classifySize(gapSize, gapMean, 2, changeLevel)
132
136
  : -Math.abs(classifySize(gapSize, gapMean, 2, changeLevel)) || 0;
133
137
 
134
- candle_close_direction[x] = (isUp)
135
- ? classifySize(closeDirection, closeDirectionMean, 2, changeLevel)
138
+ main.verticalOhlcv['candle_close_direction'][index] = isUp
139
+ ? classifySize(closeDirection, closeDirectionMean, 2, changeLevel)
136
140
  : -Math.abs(classifySize(closeDirection, closeDirectionMean, 2, changeLevel)) || 0;
137
141
 
138
- candle_high_direction[x] = (isUp)
139
- ? classifySize(highDirection, highDirectionMean, 2, changeLevel)
140
- : -Math.abs(classifySize(highDirection, highDirectionMean, 2, changeLevel)) || 0;
141
-
142
-
143
- candle_low_direction[x] = (isUp)
144
- ? classifySize(lowDirection, lowDirectionMean, 2, changeLevel)
142
+ main.verticalOhlcv['candle_high_direction'][index] = isUp
143
+ ? classifySize(highDirection, highDirectionMean, 2, changeLevel)
144
+ : -Math.abs(classifySize(highDirection, highDirectionMean, 2, changeLevel)) || 0;
145
+
146
+ main.verticalOhlcv['candle_low_direction'][index] = isUp
147
+ ? classifySize(lowDirection, lowDirectionMean, 2, changeLevel)
145
148
  : -Math.abs(classifySize(lowDirection, lowDirectionMean, 2, changeLevel)) || 0;
146
149
 
147
- candle_open_direction[x] = (isUp)
148
- ? classifySize(openDirection, openDirectionMean, 2, changeLevel)
150
+ main.verticalOhlcv['candle_open_direction'][index] = isUp
151
+ ? classifySize(openDirection, openDirectionMean, 2, changeLevel)
149
152
  : -Math.abs(classifySize(openDirection, openDirectionMean, 2, changeLevel)) || 0;
150
153
 
151
-
152
- //positives only
153
- candle_top_size[x] = classifySize(topSize, topSizeMean, 0.5, sizeLevel)
154
- candle_bottom_size[x] = classifySize(bottomSize, bottomSizeMean, 0.5, sizeLevel)
155
- candle_shadow_size[x] = classifySize(shadowSize, shadowSizeMean, 0.5, sizeLevel)
156
- }
157
- else{
158
- //negatives and positives
159
- candle_body_size[x] = candleBodySize
160
- candle_gap_size[x] = gapSize
161
- candle_close_direction[x] = closeDirection
162
- candle_high_direction[x] = highDirection
163
- candle_low_direction[x] = lowDirection
164
-
165
- //positives only
166
- candle_top_size[x] = topSize
167
- candle_bottom_size[x] = bottomSize
168
- candle_shadow_size[x] = shadowSize
169
- }
170
-
154
+ main.verticalOhlcv['candle_top_size'][index] = classifySize(topSize, topSizeMean, 0.5, sizeLevel);
155
+ main.verticalOhlcv['candle_bottom_size'][index] = classifySize(bottomSize, bottomSizeMean, 0.5, sizeLevel);
156
+ main.verticalOhlcv['candle_shadow_size'][index] = classifySize(shadowSize, shadowSizeMean, 0.5, sizeLevel);
157
+ } else {
158
+ // Without classification, just store raw values
159
+ main.verticalOhlcv['candle_body_size'][index] = isUp ? candleBodySize : -candleBodySize;
160
+ main.verticalOhlcv['candle_gap_size'][index] = isUp ? gapSize : -gapSize;
161
+ main.verticalOhlcv['candle_close_direction'][index] = isUp ? closeDirection : -closeDirection;
162
+ main.verticalOhlcv['candle_high_direction'][index] = isUp ? highDirection : -highDirection;
163
+ main.verticalOhlcv['candle_low_direction'][index] = isUp ? lowDirection : -lowDirection;
164
+ main.verticalOhlcv['candle_open_direction'][index] = isUp ? openDirection : -openDirection;
165
+
166
+ main.verticalOhlcv['candle_top_size'][index] = topSize;
167
+ main.verticalOhlcv['candle_bottom_size'][index] = bottomSize;
168
+ main.verticalOhlcv['candle_shadow_size'][index] = shadowSize;
171
169
  }
172
170
 
173
- return {
174
- candle_gap_size,
175
- candle_body_size,
176
- candle_top_size,
177
- candle_bottom_size,
178
- candle_shadow_size,
179
- candle_close_direction,
180
- candle_high_direction,
181
- candle_low_direction,
182
- candle_open_direction
183
- }
184
- }
171
+ return true;
172
+ };
@@ -0,0 +1,27 @@
1
+ export const lag = (main, index, colKeys = ['close'], lags = 1) => {
2
+
3
+ const { verticalOhlcv, len } = main;
4
+
5
+ // Ensure arrays exist for each lag key before we populate
6
+ for (let x = 0; x < colKeys.length; x++) {
7
+ for (let lag = 1; lag <= lags; lag++) {
8
+ const key = `${colKeys[x]}_lag_${lag}`;
9
+ // If the array does not exist, initialize it
10
+ if (!verticalOhlcv.hasOwnProperty(key)) {
11
+ verticalOhlcv[key] = new Array(len).fill(null);
12
+ }
13
+
14
+ // Compute the lagged index
15
+ const laggedIndex = index - lag;
16
+
17
+ // If laggedIndex is negative, we don't have a previous value
18
+ const value = (laggedIndex >= 0)
19
+ ? verticalOhlcv[colKeys[x]][laggedIndex]
20
+ : null;
21
+
22
+ verticalOhlcv[key][index] = value;
23
+ }
24
+ }
25
+
26
+ return true;
27
+ };