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,99 +1,61 @@
1
-
2
1
  import {FasterBollingerBands, FasterSMA} from 'trading-signals';
3
2
  import { classifySize } from '../utilities/classification.js';
4
3
 
5
- export const bollingerBands = (main, size, times, bollingerBandsStudies) => {
6
-
7
- const {verticalOhlcv} = main
8
- const {close} = verticalOhlcv
9
-
10
- const col = getBollingerBands(close, size, times, bollingerBandsStudies)
11
- return col
12
- }
13
-
14
- export const getBollingerBands = (data, size = 20, times = 2, bollingerBandsStudies = false) => {
15
-
16
- const dataLength = data.length
17
- const bollinger_bands_upper = Array(dataLength).fill(null)
18
- const bollinger_bands_middle = Array(dataLength).fill(null)
19
- const bollinger_bands_lower = Array(dataLength).fill(null)
20
-
21
-
22
- let bollinger_bands_range
23
- let bollinger_bands_height
24
- let heightInstance
25
- let range = null
26
- let height = null
27
-
28
- if(bollingerBandsStudies)
29
- {
30
- bollinger_bands_range = Array(dataLength).fill(null)
31
- bollinger_bands_height = Array(dataLength).fill(null)
32
- heightInstance = new FasterSMA(size)
33
- }
4
+ export const bollingerBands = (main, index, size, times, bollingerBandsStudies = false) => {
34
5
 
35
- let getRange = (value, upper, lower) => ({
36
- range: ((value - lower) / (upper - lower)),
37
- height: upper-lower
38
- })
6
+ const value = main.verticalOhlcv.close[index]
39
7
 
40
- let instance = new FasterBollingerBands(size, times)
8
+ if (!main.instances.hasOwnProperty(`bollinger_bands`)) {
9
+ main.instances[`bollinger_bands`] = {
10
+ instance: new FasterBollingerBands(size, times),
11
+ heightInstance: bollingerBandsStudies ? new FasterSMA(size) : null
12
+ };
13
+ main.verticalOhlcv[`bollinger_bands_upper`] = new Array(main.len).fill(null);
14
+ main.verticalOhlcv[`bollinger_bands_middle`] = new Array(main.len).fill(null);
15
+ main.verticalOhlcv[`bollinger_bands_lower`] = new Array(main.len).fill(null);
41
16
 
42
-
43
- for (let x = 0; x < dataLength; x++) {
44
- let obj = {};
45
- instance.update(data[x])
46
- let hasNull = false
47
- let heightMean
17
+ if (bollingerBandsStudies) {
18
+ main.verticalOhlcv[`bollinger_bands_range`] = new Array(main.len).fill(null);
19
+ main.verticalOhlcv[`bollinger_bands_height`] = new Array(main.len).fill(null);
20
+ }
21
+ }
48
22
 
23
+ const { instance, heightInstance } = main.instances[`bollinger_bands`];
24
+ instance.update(value);
49
25
 
26
+ let result;
50
27
  try {
51
- obj = instance.getResult()
28
+ result = instance.getResult();
52
29
  } catch (err) {
53
- hasNull = true
54
- obj = { upper: null, middle: null, lower: null }
55
- }
56
-
57
- bollinger_bands_upper[x] = obj.upper;
58
- bollinger_bands_middle[x] = obj.middle;
59
- bollinger_bands_lower[x] = obj.lower;
60
-
61
- if(bollingerBandsStudies === false) continue
62
-
63
- if(hasNull)
64
- {
65
- bollinger_bands_range[x] = null
66
- bollinger_bands_height[x] = null
30
+ result = { upper: null, middle: null, lower: null };
67
31
  }
68
- else
69
- {
70
- const extraProps = getRange(data[x], obj.upper, obj.lower)
71
- range = extraProps.range
72
- height = extraProps.height
73
32
 
74
- heightInstance.update(height)
33
+ const { upper, middle, lower } = result;
34
+ main.verticalOhlcv[`bollinger_bands_upper`][index] = upper;
35
+ main.verticalOhlcv[`bollinger_bands_middle`][index] = middle;
36
+ main.verticalOhlcv[`bollinger_bands_lower`][index] = lower;
75
37
 
76
- try{
77
- heightMean = heightInstance.getResult()
78
- }
79
- catch(err)
80
- {
81
- heightMean = null
82
- }
38
+ if (!bollingerBandsStudies) return true;
83
39
 
84
- }
40
+ let range = null;
41
+ let height = null;
42
+ let heightMean;
85
43
 
86
- bollinger_bands_range[x] = range
87
- bollinger_bands_height[x] = classifySize(height, heightMean, 1.5)
88
- }
44
+ if (upper !== null && lower !== null) {
45
+ range = (value - lower) / (upper - lower);
46
+ height = upper - lower;
89
47
 
90
- let output = { bollinger_bands_upper, bollinger_bands_middle, bollinger_bands_lower }
48
+ heightInstance.update(height);
91
49
 
92
- if(bollingerBandsStudies)
93
- {
94
- output = {...output, bollinger_bands_range, bollinger_bands_height}
95
- }
50
+ try {
51
+ heightMean = heightInstance.getResult();
52
+ } catch (err) {
53
+ heightMean = null;
54
+ }
55
+ }
96
56
 
97
- return output
98
- }
57
+ main.verticalOhlcv[`bollinger_bands_range`][index] = range;
58
+ main.verticalOhlcv[`bollinger_bands_height`][index] = classifySize(height, heightMean, 1.5);
99
59
 
60
+ return true;
61
+ };
@@ -1,59 +1,46 @@
1
- export const donchianChannels = (main, period, offset) => {
2
-
3
- const {verticalOhlcv, len} = main
4
- const {high, low} = verticalOhlcv
5
-
6
- return getDonchianChannels(high, low, period, offset, len)
7
- }
8
-
9
- export const getDonchianChannels = (high, low, period, offset, len) => {
10
- const donchian_channel_upper = new Array(len).fill(null)
11
- const donchian_channel_lower = new Array(len).fill(null)
12
- const donchian_channel_basis = new Array(len).fill(null)
13
-
14
- const maxQueue = []
15
- const minQueue = []
16
-
17
- const avg = ((h, l) => (h + l) / 2)
18
-
19
- for (let i = 0; i < len; i++) {
20
-
21
- while (maxQueue.length && maxQueue[0] <= i - period) {
22
- maxQueue.shift()
23
- }
24
- // Remove smaller values from the end
25
- while (maxQueue.length && high[maxQueue[maxQueue.length - 1]] <= high[i]) {
26
- maxQueue.pop()
27
- }
28
- maxQueue.push(i)
29
-
30
- while (minQueue.length && minQueue[0] <= i - period) {
31
- minQueue.shift()
32
- }
33
- while (minQueue.length && low[minQueue[minQueue.length - 1]] >= low[i]) {
34
- minQueue.pop()
35
- }
36
-
37
- minQueue.push(i)
38
-
39
- if (i >= period - 1) {
40
- const upperValue = high[maxQueue[0]]
41
- const lowerValue = low[minQueue[0]]
42
- const basisValue = avg(upperValue, lowerValue)
43
-
44
- const indexWithOffset = i + offset
45
-
46
- if (indexWithOffset >= 0 && indexWithOffset < len) {
47
- donchian_channel_upper[indexWithOffset] = upperValue
48
- donchian_channel_lower[indexWithOffset] = lowerValue
49
- donchian_channel_basis[indexWithOffset] = basisValue
50
- }
51
- }
1
+ const avg = (h, l) => (h + l) / 2;
2
+
3
+ export const donchianChannels = (main, index, size, offset) => {
4
+ const { verticalOhlcv } = main;
5
+ const highs = verticalOhlcv.high;
6
+ const lows = verticalOhlcv.low;
7
+
8
+ // If we don't have enough data to fill one size ending at (index - offset), return
9
+ if ((index - offset) < (size - 1)) return true;
10
+
11
+ // Ensure that the required arrays exist
12
+ if (!main.instances.hasOwnProperty('donchian_channels')) {
13
+ main.instances['donchian_channels'] = {};
14
+ verticalOhlcv['donchian_channel_upper'] = new Array(main.len).fill(null);
15
+ verticalOhlcv['donchian_channel_basis'] = new Array(main.len).fill(null);
16
+ verticalOhlcv['donchian_channel_lower'] = new Array(main.len).fill(null);
52
17
  }
53
18
 
54
- return {
55
- donchian_channel_upper,
56
- donchian_channel_lower,
57
- donchian_channel_basis
19
+
20
+ // Compute the slice start and end to get exactly 'size' bars
21
+ const endIdx = (index - offset) + 1; // inclusive end of the slice
22
+ const startIdx = endIdx - size; // start index for slicing
23
+
24
+ // If any of these indices is out of bounds, return
25
+ if (startIdx < 0 || endIdx > main.len) return true;
26
+
27
+ // Extract the relevant chunks of data
28
+ const highChunk = highs.slice(startIdx, endIdx)
29
+ const lowChunk = lows.slice(startIdx, endIdx)
30
+
31
+ // Compute the Donchian channels
32
+ const upper = Math.max(...highChunk);
33
+ const lower = Math.min(...lowChunk);
34
+ const basis = avg(upper, lower);
35
+
36
+ // Place the result at index
37
+ const placementIndex = index;
38
+
39
+ if (placementIndex >= 0 && placementIndex < main.len) {
40
+ verticalOhlcv['donchian_channel_upper'][placementIndex] = upper;
41
+ verticalOhlcv['donchian_channel_basis'][placementIndex] = basis;
42
+ verticalOhlcv['donchian_channel_lower'][placementIndex] = lower;
58
43
  }
59
- }
44
+
45
+ return true;
46
+ };
@@ -1,54 +1,29 @@
1
1
 
2
- import {EMA, FasterEMA} from 'trading-signals'
2
+ import {FasterEMA} from 'trading-signals'
3
3
 
4
- export const ema = (main, size) => {
4
+ export const ema = (main, index, size) => {
5
5
 
6
- const {verticalOhlcv} = main
7
- const {close} = verticalOhlcv
6
+ const value = main.verticalOhlcv.close[index]
8
7
 
9
- const col = getEMA(close, size)
10
-
11
- return {
12
- [`ema_${size}`]: col
8
+ if(!main.instances.hasOwnProperty(`ema_${size}`))
9
+ {
10
+ main.instances[`ema_${size}`] = new FasterEMA(size)
11
+ main.verticalOhlcv[`ema_${size}`] = new Array(main.len).fill(null)
13
12
  }
14
- }
15
13
 
16
- export const getEMA = (data, size) => {
17
-
14
+ let currSma
15
+ main.instances[`ema_${size}`].update(value)
18
16
 
19
- if(typeof data === 'undefined' || typeof size === 'undefined')
17
+ try{
18
+ currSma = main.instances[`ema_${size}`].getResult()
19
+ } catch(err)
20
20
  {
21
- throw Error('undefined param in getEMA')
21
+ currSma = null
22
22
  }
23
23
 
24
- const dataLength = data.length
25
- const output = new Array(dataLength).fill(null)
26
- const instance = new FasterEMA(size)
27
-
28
- for(let x = 0; x < dataLength; x++)
24
+ if(currSma)
29
25
  {
30
-
31
- let value = null
32
-
33
- if(data[x] !== null)
34
- {
35
- instance.update(data[x])
36
-
37
- try
38
- {
39
- value = instance.getResult()
40
- }
41
- catch(err)
42
- {
43
- value = null
44
- }
45
- }
46
-
47
- output[x] = value
26
+ main.verticalOhlcv[`ema_${size}`][index] = currSma
48
27
  }
49
28
 
50
-
51
-
52
- return output
53
-
54
29
  }
@@ -1,50 +1,47 @@
1
- import { findCrosses } from "../studies/findCrosses.js";
2
- import {EMA, MACD, FasterEMA, FasterMACD} from 'trading-signals';
3
-
4
-
5
- export const macd = (main, fastLine, slowLine, signalLine) => {
6
-
7
- const {verticalOhlcv} = main
8
- const {close} = verticalOhlcv
9
- const col = getMACD(close, fastLine, slowLine, signalLine)
10
-
11
- return col
12
- }
13
-
14
- export const getMACD = (data, fastLine = 12, slowLine = 26, signalLine = 9) => {
15
-
16
- const dataLength = data.length
17
- const diff = new Array(dataLength).fill(null)
18
- const dea = new Array(dataLength).fill(null)
19
- const histogram = new Array(dataLength).fill(null)
20
-
21
- const instance = new FasterMACD(new FasterEMA(fastLine), new FasterEMA(slowLine), new FasterEMA(signalLine))
22
-
23
- for(let x = 0; x < dataLength; x++)
24
- {
25
- let obj = {}
26
- instance.update(data[x])
27
-
28
- try
29
- {
30
- obj = instance.getResult()
31
- }
32
- catch(err)
33
- {
34
- obj = {macd: null, signal: null, histogram: null}
35
- }
36
-
37
- diff[x] = obj.macd
38
- dea[x] = obj.signal
39
- histogram[x] = obj.histogram
1
+ import {FasterEMA, FasterMACD} from 'trading-signals';
2
+
3
+ export const macd = (main, index, fastLine = 12, slowLine = 26, signalLine = 9) => {
4
+
5
+ const value = main.verticalOhlcv.close[index]
6
+
7
+ if (!main.instances.hasOwnProperty('macd')) {
8
+
9
+ main.autoCrossPairsList.push({fast: 'macd_diff', slow: 'macd_dea'});
10
+
11
+ main.instances['macd'] = new FasterMACD(
12
+ new FasterEMA(fastLine),
13
+ new FasterEMA(slowLine),
14
+ new FasterEMA(signalLine)
15
+ );
16
+
17
+ main.verticalOhlcv['macd_diff'] = new Array(main.len).fill(null);
18
+ main.verticalOhlcv['macd_dea'] = new Array(main.len).fill(null);
19
+ main.verticalOhlcv['macd_histogram'] = new Array(main.len).fill(null);
20
+ }
21
+
22
+ const macdInstance = main.instances[`macd`];
23
+ macdInstance.update(value);
24
+
25
+ let macdResult;
26
+ try {
27
+ macdResult = macdInstance.getResult();
28
+ } catch (err) {
29
+ macdResult = { macd: null, signal: null, histogram: null };
40
30
  }
41
31
 
42
- const x = findCrosses({fast: diff, slow: dea})
32
+ const { macd, signal, histogram } = macdResult;
33
+
34
+ if (macd !== null) {
35
+ main.verticalOhlcv['macd_diff'][index] = macd;
36
+ }
43
37
 
44
- return {
45
- macd_diff: diff,
46
- macd_dea: dea,
47
- macd_histogram: histogram,
48
- macd_diff_x_macd_dea: x
38
+ if (signal !== null) {
39
+ main.verticalOhlcv['macd_dea'][index] = signal;
49
40
  }
50
- }
41
+
42
+ if (histogram !== null) {
43
+ main.verticalOhlcv['macd_histogram'][index] = histogram;
44
+ }
45
+
46
+ return true;
47
+ };
@@ -1,43 +1,32 @@
1
- import {FasterSMA} from 'trading-signals'
1
+ import {FasterSMA} from 'trading-signals';
2
2
 
3
- export const relativeVolume = (main, size = 10) => {
4
- const {verticalOhlcv} = main
5
- const {volume} = verticalOhlcv
6
- const col = getRelativeVolume(volume, size)
3
+ export const relativeVolume = (main, index, size = 10) => {
7
4
 
8
- return {
9
- [`relative_volume_${size}`]: col
5
+ const value = main.verticalOhlcv.volume[index]
6
+
7
+ if (!main.instances.hasOwnProperty(`relative_volume_${size}`)) {
8
+ main.instances[`relative_volume_${size}`] = new FasterSMA(size);
9
+ main.verticalOhlcv[`relative_volume_${size}`] = new Array(main.len).fill(null);
10
+ main.verticalOhlcv[`relative_volume_sma_${size}`] = new Array(main.len).fill(null);
10
11
  }
11
- }
12
-
13
- export const getRelativeVolume = (volume, size) => {
14
-
15
- const instance = new FasterSMA(size)
16
- const sma = new Array(volume.length).fill(null)
17
- const output = new Array(volume.length).fill(null)
18
-
19
- for(let x = 0; x < volume.length; x++)
20
- {
21
- let value = null
22
-
23
- instance.update(volume[x])
24
-
25
- try
26
- {
27
- value = instance.getResult()
28
- }
29
- catch(err)
30
- {
31
- value = null
32
- }
33
-
34
- sma[x] = value
35
-
36
- if(typeof sma[x-1] === 'number')
37
- {
38
- output[x] = volume[x] / sma[x-1]
39
- }
12
+
13
+ const smaInstance = main.instances[`relative_volume_${size}`]
14
+ smaInstance.update(value)
15
+
16
+ let smaValue;
17
+ try {
18
+ smaValue = smaInstance.getResult()
19
+ } catch (err) {
20
+ smaValue = null;
40
21
  }
41
-
42
- return output
43
- }
22
+
23
+ main.verticalOhlcv[`relative_volume_sma_${size}`][index] = smaValue;
24
+
25
+ if (smaValue !== null && typeof main.verticalOhlcv[`relative_volume_sma_${size}`][index - 1] === 'number') {
26
+ main.verticalOhlcv[`relative_volume_${size}`][index] = value / main.verticalOhlcv[`relative_volume_sma_${size}`][index - 1];
27
+ } else {
28
+ main.verticalOhlcv[`relative_volume_${size}`][index] = null;
29
+ }
30
+
31
+ return true;
32
+ };
@@ -1,49 +1,29 @@
1
1
 
2
2
  import {FasterSMA} from 'trading-signals';
3
3
 
4
- export const sma = (main, size) => {
4
+ export const sma = (main, index, size) => {
5
5
 
6
- const {verticalOhlcv} = main
7
- const {close} = verticalOhlcv
6
+ const value = main.verticalOhlcv.close[index]
8
7
 
9
- const col = getSMA(close, size)
10
-
11
- return {
12
- [`sma_${size}`]: col
8
+ if(!main.instances.hasOwnProperty(`sma_${size}`))
9
+ {
10
+ main.instances[`sma_${size}`] = new FasterSMA(size)
11
+ main.verticalOhlcv[`sma_${size}`] = new Array(main.len).fill(null)
13
12
  }
14
- }
15
13
 
16
- export const getSMA = (data, size) => {
17
-
18
- if(typeof data === 'undefined' || typeof size === 'undefined')
14
+ let currSma
15
+ main.instances[`sma_${size}`].update(value)
16
+
17
+ try{
18
+ currSma = main.instances[`sma_${size}`].getResult()
19
+ } catch(err)
19
20
  {
20
- throw Error('undefined param in getSMA')
21
+ currSma = null
21
22
  }
22
23
 
23
- const dataLength = data.length
24
- const output = new Array(dataLength).fill(null)
25
- const instance = new FasterSMA(size)
26
-
27
- for(let x = 0; x < dataLength; x++)
24
+ if(currSma)
28
25
  {
29
- let value = null
30
-
31
- if(data[x] !== null)
32
- {
33
- instance.update(data[x])
34
-
35
- try
36
- {
37
- value = instance.getResult()
38
- }
39
- catch(err)
40
- {
41
- value = null
42
- }
43
- }
44
-
45
- output[x] = value
26
+ main.verticalOhlcv[`sma_${size}`][index] = currSma
46
27
  }
47
28
 
48
- return output
49
29
  }
@@ -1,68 +1,50 @@
1
- import { getSMA } from "../moving-averages/sma.js";
2
- import { getEMA } from "../moving-averages/ema.js";
3
- import { findCrosses } from "../studies/findCrosses.js";
4
1
  import {FasterRSI} from 'trading-signals';
2
+ import { FasterSMA } from 'trading-signals';
5
3
 
6
- const ma = {getSMA, getEMA}
7
4
 
8
- export const rsi = (main, period, movingAverage, movingAveragePeriod) => {
9
-
10
- const {verticalOhlcv} = main
11
- const {close} = verticalOhlcv
5
+ export const rsi = (main, index, size) => {
6
+
7
+ const value = main.verticalOhlcv.close[index]
12
8
 
13
- if(typeof period === 'number' && typeof movingAveragePeriod === 'undefined')
9
+ if(!main.instances.hasOwnProperty(`rsi_${size}`))
14
10
  {
15
- movingAveragePeriod = period
16
- }
17
-
18
- const col = getRSI(close, period, movingAverage, movingAveragePeriod)
19
-
20
- return col
21
- }
22
-
23
- export const getRSI = (data, period = 14, movingAverage = 'SMA', movingAveragePeriod = 14) => {
24
-
25
- const dataLength = data.length
26
-
27
- if (dataLength < period) {
28
- return [];
11
+ main.autoCrossPairsList.push({fast: `rsi_${size}`, slow: `rsi_sma_${size}`})
12
+ main.instances[`rsi_${size}`] = new FasterRSI(size)
13
+ main.instances[`rsi_sma_${size}`] = new FasterSMA(size)
14
+ main.verticalOhlcv[`rsi_${size}`] = new Array(main.len).fill(null)
15
+ main.verticalOhlcv[`rsi_sma_${size}`] = new Array(main.len).fill(null)
29
16
  }
17
+
18
+ let currentRsi
19
+ let smoothedRsi
30
20
 
31
- let rsi = new Array(dataLength).fill(null)
32
- const instance = new FasterRSI(period)
21
+ main.instances[`rsi_${size}`].update(value)
33
22
 
34
- for(let x = 0; x < dataLength; x++)
23
+ try
35
24
  {
36
- let value = null
37
-
38
- if(data[x] !== null)
39
- {
40
- instance.update(data[x])
41
-
42
- try
43
- {
44
- value = instance.getResult()
45
- }
46
- catch(err)
47
- {
48
- value = null
49
- }
50
- }
25
+ currentRsi = main.instances[`rsi_${size}`].getResult()
26
+ } catch(err) {
27
+ currentRsi = null
28
+ }
51
29
 
52
- rsi[x] = value
30
+ if(currentRsi)
31
+ {
32
+ main.verticalOhlcv[`rsi_${size}`][index] = currentRsi
33
+ main.instances[`rsi_sma_${size}`].update(currentRsi)
53
34
  }
54
35
 
55
- let output = {[`rsi_${period}`]: rsi}
36
+ try
37
+ {
38
+ smoothedRsi = main.instances[`rsi_sma_${size}`].getResult()
39
+ } catch(err)
40
+ {
41
+ smoothedRsi = null
42
+ }
56
43
 
57
- if(typeof movingAverage === 'string' && typeof movingAveragePeriod === 'number')
44
+ if(smoothedRsi)
58
45
  {
59
- if(ma.hasOwnProperty(`get${movingAverage}`))
60
- {
61
- const rsi_smoothed = ma[`get${movingAverage}`](rsi, movingAveragePeriod)
62
- output[`rsi_${movingAverage}_${movingAveragePeriod}`] = rsi_smoothed
63
- output[`rsi_${period}_x_rsi_${movingAverage}_${movingAveragePeriod}`] = findCrosses({fast: rsi, slow: rsi_smoothed})
64
- }
46
+ main.verticalOhlcv[`rsi_sma_${size}`][index] = smoothedRsi
65
47
  }
66
48
 
67
- return output
68
- }
49
+ return true
50
+ }