ohlcv-indicators 1.5.2 → 1.5.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.
package/index.js CHANGED
@@ -7,8 +7,10 @@ import { volumeProfile } from './src/studies/volumeProfile.js'
7
7
  import {crossPairs} from './src/studies/findCrosses.js'
8
8
  import { candles } from './src/studies/candles.js'
9
9
  import { orb } from './src/studies/orb.js'
10
+ import { donchianChannels } from './src/moving-averages/donchianChannel.js'
10
11
  import {Big} from 'trading-signals';
11
12
  import { parseOhlcvToVertical } from './src/utilities/parsing-utilities.js'
13
+ import { candlesStudies } from './src/studies/candleStudies.js'
12
14
 
13
15
  export default class OHLCV_INDICATORS {
14
16
  constructor({input, precision = false, ticker = 'undefined'}) {
@@ -205,4 +207,18 @@ export default class OHLCV_INDICATORS {
205
207
 
206
208
  return this
207
209
  }
210
+ donchianChannels(period, offset)
211
+ {
212
+ const result = donchianChannels(this, period, offset)
213
+ Object.assign(this.indicators, result)
214
+
215
+ return this
216
+ }
217
+ candlesStudies(period)
218
+ {
219
+ const result = candlesStudies(this, period)
220
+ Object.assign(this.indicators, result)
221
+
222
+ return this
223
+ }
208
224
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ohlcv-indicators",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
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",
@@ -0,0 +1,61 @@
1
+ export const donchianChannels = (main, period, offset) => {
2
+
3
+ const {verticalOhlcv, precision, len} = main
4
+ const {high, low} = verticalOhlcv
5
+
6
+ return getDonchianChannels(high, low, period, offset, precision, len)
7
+ }
8
+
9
+ export const getDonchianChannels = (high, low, period, offset, precision, 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 = (precision)
18
+ ? ((h, l) => (h.plus(l)).div(2))
19
+ : ((h, l) => (h + l) / 2)
20
+
21
+ for (let i = 0; i < len; i++) {
22
+
23
+ while (maxQueue.length && maxQueue[0] <= i - period) {
24
+ maxQueue.shift()
25
+ }
26
+ // Remove smaller values from the end
27
+ while (maxQueue.length && high[maxQueue[maxQueue.length - 1]] <= high[i]) {
28
+ maxQueue.pop()
29
+ }
30
+ maxQueue.push(i)
31
+
32
+ while (minQueue.length && minQueue[0] <= i - period) {
33
+ minQueue.shift()
34
+ }
35
+ while (minQueue.length && low[minQueue[minQueue.length - 1]] >= low[i]) {
36
+ minQueue.pop()
37
+ }
38
+
39
+ minQueue.push(i)
40
+
41
+ if (i >= period - 1) {
42
+ const upperValue = high[maxQueue[0]]
43
+ const lowerValue = low[minQueue[0]]
44
+ const basisValue = avg(upperValue, lowerValue)
45
+
46
+ const indexWithOffset = i + offset
47
+
48
+ if (indexWithOffset >= 0 && indexWithOffset < len) {
49
+ donchian_channel_upper[indexWithOffset] = upperValue
50
+ donchian_channel_lower[indexWithOffset] = lowerValue
51
+ donchian_channel_basis[indexWithOffset] = basisValue
52
+ }
53
+ }
54
+ }
55
+
56
+ return {
57
+ donchian_channel_upper,
58
+ donchian_channel_lower,
59
+ donchian_channel_basis
60
+ }
61
+ }
@@ -48,6 +48,7 @@ export const getEMA = (data, size, precision) => {
48
48
  }
49
49
 
50
50
 
51
+
51
52
  return output
52
53
 
53
54
  }
@@ -0,0 +1,116 @@
1
+ import {SMA, FasterSMA} from 'trading-signals';
2
+
3
+ export const candlesStudies = (main, period) => {
4
+
5
+ const {inputOhlcv, len} = main
6
+
7
+ const cols = getCandlesStudies(inputOhlcv, period, len)
8
+ return cols
9
+ }
10
+
11
+ const getCandlesStudies = (inputOhlcv, period = 20, len) => {
12
+
13
+ let getSize = (a, b) => Math.abs(a - b)
14
+
15
+ let candle_direction = new Array(len).fill(null)
16
+
17
+ //body
18
+ let candle_body_size = new Array(len).fill(null)
19
+ let candle_body_size_mean = new Array(len).fill(null)
20
+ let candle_body_size_label = new Array(len).fill(null)
21
+
22
+ //top
23
+ let candle_top_size_mean = new Array(len).fill(null)
24
+ let candle_top_size = new Array(len).fill(null)
25
+ let candle_top_size_label = new Array(len).fill(null)
26
+
27
+ //bottom
28
+ let candle_bottom_size_mean = new Array(len).fill(null)
29
+ let candle_bottom_size = new Array(len).fill(null)
30
+ let candle_bottom_size_label = new Array(len).fill(null)
31
+
32
+
33
+ //instances
34
+ let bodyInstance = new FasterSMA(period)
35
+ let topInstance = new FasterSMA(period)
36
+ let bottomInstance = new FasterSMA(period)
37
+
38
+
39
+ for(let x = 0; x < len; x++)
40
+ {
41
+ const {open, high, low, close} = inputOhlcv[x]
42
+ let bodySizeMean = null
43
+ let topSizeMean = null
44
+ let bottomSizeMean = null
45
+ const bodySize = getSize(open, close)
46
+ let topSize
47
+ let bottomSize
48
+ let candleDirection = (close > open) ? 'up' : 'down'
49
+
50
+ if(candleDirection === 'up')
51
+ {
52
+ topSize = getSize(high, close)
53
+ bottomSize = getSize(open, low)
54
+ }
55
+ else
56
+ {
57
+ topSize = getSize(high, open)
58
+ bottomSize = getSize(close, low)
59
+ }
60
+
61
+ bodyInstance.update(bodySize)
62
+ topInstance.update(topSize)
63
+ bottomInstance.update(bottomSize)
64
+
65
+ try
66
+ {
67
+ bodySizeMean = bodyInstance.getResult()
68
+ topSizeMean = topInstance.getResult()
69
+ bottomSizeMean = bottomInstance.getResult()
70
+ }
71
+ catch(err)
72
+ {
73
+ bodySizeMean = null
74
+ }
75
+
76
+ candle_direction[x] = candleDirection
77
+
78
+ //body
79
+ candle_body_size[x] = bodySize
80
+ candle_body_size_mean[x] = bodySizeMean
81
+ candle_body_size_label[x] = labelSize(bodySize, bodySizeMean)
82
+
83
+ //top
84
+ candle_top_size[x] = topSize
85
+ candle_top_size_mean[x] = topSizeMean
86
+ candle_top_size_label[x] = labelSize(topSize, topSizeMean)
87
+
88
+
89
+ //bottom
90
+ candle_bottom_size[x] = bottomSize
91
+ candle_bottom_size_mean[x] = bottomSizeMean
92
+ candle_bottom_size_label[x] = labelSize(bottomSize, bottomSizeMean)
93
+
94
+ }
95
+
96
+ return {
97
+ candle_direction,
98
+ candle_body_size_mean,
99
+ candle_body_size,
100
+ candle_body_size_label,
101
+ candle_top_size_mean,
102
+ candle_top_size,
103
+ candle_top_size_label,
104
+ candle_bottom_size_mean,
105
+ candle_bottom_size,
106
+ candle_bottom_size_label
107
+ }
108
+ }
109
+
110
+ const labelSize = (value, mean) => {
111
+
112
+ if(value === null || mean === null) return null
113
+ if(value > mean * 1.25) return 'large'
114
+ else if(value < mean * 0.75) return 'small'
115
+ else return 'medium'
116
+ }
@@ -27,7 +27,6 @@ export const findCrosses = ({fast, slow, precision}) => {
27
27
  lt = (a, b) => a < b
28
28
  }
29
29
 
30
-
31
30
  const state = new Array(dataLength).fill(null)
32
31
 
33
32
  for(let x = 0; x < dataLength; x++)
@@ -37,17 +36,32 @@ export const findCrosses = ({fast, slow, precision}) => {
37
36
  let value
38
37
 
39
38
  if(f === null || s === null) value = 'neutral'
40
- else if(eq(f, s)) value = 'neutral'
39
+ else if(eq(f, s)){
40
+ if(gt(fast[x-1], slow[x-1]))
41
+ {
42
+ value = 'up'
43
+ }
44
+ else if(lt(fast[x-1], slow[x-1]))
45
+ {
46
+ value = 'down'
47
+ }
48
+ else
49
+ {
50
+ value = 'neutral'
51
+ }
52
+ }
41
53
  else if(gt(f, s)) value = 'up'
42
54
  else if(lt(f, s)) value = 'down'
43
55
 
44
56
  state[x] = value
45
57
  }
46
58
 
59
+
60
+
47
61
  const groups = groupConsecutiveValues(state)
48
62
  const groupLengths = groups.map(g => g.length)
49
63
 
50
- //console.log(groups)
64
+ //console.log(JSON.stringify(groups))
51
65
 
52
66
  return groups.map((group, gIndex) => {
53
67
  let initialValue = groupLengths[gIndex]
@@ -124,26 +138,39 @@ export const crossPairs = (main, arr) => {
124
138
 
125
139
  const splitCount = arr => (typeof count !== 'undefined') ? (arr.slice(-count)).filter(o => o === 1 || o === -1).length : 0
126
140
 
127
- // Find and add crosses
141
+ //cross already exists
128
142
  if(verticalOhlcv[crossName])
129
143
  {
130
144
  const cross = verticalOhlcv[crossName]
131
- c[countName] = splitCount(cross)
132
- x[crossName] = cross
145
+ x[crossName] = cross
146
+
147
+ if(typeof count !== 'undefined')
148
+ {
149
+ c[countName] = splitCount(cross)
150
+ }
151
+
133
152
  }
153
+ //cross created from 2 columns
134
154
  else if (verticalOhlcv[fast] && verticalOhlcv[slow]) {
135
155
 
136
156
  const cross = findCrosses({fast: verticalOhlcv[fast], slow: verticalOhlcv[slow], precision});
137
-
138
- c[countName] = splitCount(cross)
139
157
  x[crossName] = cross
140
158
 
141
- } else if (verticalOhlcv[fast] && slowNumArrCache[slow]) {
159
+ if(typeof count !== 'undefined')
160
+ {
161
+ c[countName] = splitCount(cross)
162
+ }
163
+
164
+ }
165
+ //cross created from 1 column and an number
166
+ else if (verticalOhlcv[fast] && slowNumArrCache[slow]) {
142
167
  const cross = findCrosses({fast: verticalOhlcv[fast], slow: slowNumArrCache[slow], precision})
143
-
144
- c[countName] = splitCount(cross)
145
168
  x[crossName] = cross
146
169
 
170
+ if(typeof count !== 'undefined'){
171
+ c[countName] = splitCount(cross)
172
+ }
173
+
147
174
  } else {
148
175
  console.log(`Missing ohlcv properties for ${fast} or ${slow}`);
149
176
  }
@@ -12,18 +12,20 @@ export const parseOhlcvToVertical = (input, len, big) => {
12
12
  verticalOhlcv[key] = new Array(len)
13
13
  }
14
14
  }
15
+
16
+ for(let x = 0; x < len; x++)
17
+ {
18
+ const { open, high, low, close, volume, ...rest } = input[x]
19
+ verticalOhlcv.open[x] = big(open)
20
+ verticalOhlcv.high[x] = big(high)
21
+ verticalOhlcv.low[x] = big(low)
22
+ verticalOhlcv.close[x] = big(close)
23
+ verticalOhlcv.volume[x] = big(volume)
15
24
 
16
- input.forEach(({ open, high, low, close, volume, ...rest }, index) => {
17
- verticalOhlcv.open[index] = big(open)
18
- verticalOhlcv.high[index] = big(high)
19
- verticalOhlcv.low[index] = big(low)
20
- verticalOhlcv.close[index] = big(close)
21
- verticalOhlcv.volume[index] = big(volume)
22
-
23
- for (const key in rest) {
24
- verticalOhlcv[key][index] = rest[key];
25
- }
26
- })
25
+ for (const [key, value] of Object.entries(rest)) {
26
+ verticalOhlcv[key][x] = value
27
+ }
28
+ }
27
29
 
28
30
  return verticalOhlcv
29
31
  }