ohlcv-indicators 1.9.9 → 2.0.1
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/dist/ohlcv-indicators.min.js +1 -1
- package/index.js +113 -62
- package/package.json +1 -1
- package/src/moving-averages/bollingerBands.js +40 -31
- package/src/moving-averages/donchianChannel.js +4 -8
- package/src/moving-averages/ema.js +2 -2
- package/src/moving-averages/macd.js +5 -5
- package/src/moving-averages/relativeVolume.js +13 -8
- package/src/moving-averages/sma.js +2 -2
- package/src/oscillators/rsi.js +5 -5
- package/src/oscillators/volumeOscillator.js +4 -4
- package/src/studies/candleStudies.js +20 -20
- package/src/studies/dateTime.js +86 -0
- package/src/studies/findCrosses.js +34 -22
- package/src/studies/lag.js +31 -22
- package/src/studies/priceVariations.js +22 -0
- package/src/utilities/parsing-utilities.js +37 -161
- package/src/utilities/setIndicatorsFromInputParams.js +24 -36
- package/src/utilities/validators.js +20 -0
package/index.js
CHANGED
|
@@ -1,39 +1,28 @@
|
|
|
1
|
-
import { parseOhlcvToVertical
|
|
1
|
+
import { parseOhlcvToVertical } from './src/utilities/parsing-utilities.js'
|
|
2
2
|
import { correlation } from './src/studies/correlation.js'
|
|
3
3
|
import { setIndicatorsFromInputParams } from './src/utilities/setIndicatorsFromInputParams.js'
|
|
4
|
+
import { validateDate } from './src/utilities/validators.js'
|
|
5
|
+
import { isAlreadyComputed } from './src/utilities/validators.js'
|
|
4
6
|
|
|
5
7
|
export default class OHLCV_INDICATORS {
|
|
6
|
-
constructor({input, ticker = null
|
|
8
|
+
constructor({input, ticker = null}) {
|
|
7
9
|
|
|
8
|
-
if(!Array.isArray(input)) throw Error('input
|
|
9
|
-
if(input.length === 0) throw Error('input
|
|
10
|
-
if(!input[0].hasOwnProperty('close')) throw Error('input
|
|
10
|
+
if(!Array.isArray(input)) throw Error('input OHLCV must be an array: ' + ticker)
|
|
11
|
+
if(input.length === 0) throw Error('input OHLCV must not be empty: ' + ticker)
|
|
12
|
+
if(!input[0].hasOwnProperty('close')) throw Error('input OHLCV array objects require at least close property: ' + ticker)
|
|
11
13
|
|
|
12
14
|
this.lastComputedIndex = 0
|
|
13
15
|
|
|
14
16
|
this.input = input
|
|
15
|
-
this.priceBased = ['open', 'high', 'low', 'close'
|
|
17
|
+
this.priceBased = ['open', 'high', 'low', 'close']
|
|
16
18
|
this.len = input.length
|
|
17
|
-
this.
|
|
19
|
+
this.lastIndexReplace = false
|
|
18
20
|
this.instances = {}
|
|
19
21
|
this.crossPairsList = []
|
|
20
22
|
this.verticalOhlcv = {}
|
|
21
23
|
|
|
22
|
-
this.inputParams =
|
|
23
|
-
crossPairs: null,
|
|
24
|
-
lag: null,
|
|
25
|
-
relativeVolume: null,
|
|
26
|
-
ema: null,
|
|
27
|
-
sma: null,
|
|
28
|
-
macd: null,
|
|
29
|
-
bollingerBands: null,
|
|
30
|
-
rsi: null,
|
|
31
|
-
donchianChannels: null,
|
|
32
|
-
candlesStudies: null,
|
|
33
|
-
volumeOscillator: null,
|
|
34
|
-
}
|
|
24
|
+
this.inputParams = []
|
|
35
25
|
|
|
36
|
-
|
|
37
26
|
this.studies = {}
|
|
38
27
|
this.utilities = {
|
|
39
28
|
correlation
|
|
@@ -44,9 +33,11 @@ export default class OHLCV_INDICATORS {
|
|
|
44
33
|
|
|
45
34
|
return this
|
|
46
35
|
}
|
|
47
|
-
|
|
36
|
+
|
|
37
|
+
|
|
48
38
|
getData() {
|
|
49
39
|
|
|
40
|
+
//getData method returns the last object (row) of the new OHLCV with indicators: {open, high, low, close, rsi_14, bollinger_bands_upper}
|
|
50
41
|
this.compute()
|
|
51
42
|
|
|
52
43
|
const {verticalOhlcv} = this
|
|
@@ -94,30 +85,57 @@ export default class OHLCV_INDICATORS {
|
|
|
94
85
|
}
|
|
95
86
|
|
|
96
87
|
compute(change) {
|
|
97
|
-
|
|
88
|
+
|
|
89
|
+
//checks the first row in OHLCV input to verify if the date property is valid
|
|
90
|
+
this.isValidDate = this.input[0].hasOwnProperty('date') && validateDate(this.input[0].date)
|
|
91
|
+
|
|
92
|
+
//compute method can be used to process indicators if no arguments are provided
|
|
93
|
+
//compute method can be called to access the .verticalOhlcv object
|
|
94
|
+
//compute method is called automatically if getLastValues or getDate methods are called
|
|
95
|
+
if (this.len > this.lastComputedIndex && !change) {
|
|
96
|
+
parseOhlcvToVertical(this.input, this, 0);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
//compute method can be used to add new or update the last datapoints if a new OHLCV object is passed with a valid date property
|
|
100
|
+
//valid date properties: "2024-12-16 15:45:00" or "2024-12-16"
|
|
101
|
+
if (change && typeof change === 'object') {
|
|
102
|
+
|
|
103
|
+
if (!['open', 'high', 'low', 'close', 'volume', 'date'].every(f => Object.keys(change).includes(f))) {
|
|
104
|
+
throw Error('Invalid OHLCV object sent in "compute". Correct usage: .compute({open: 108.25, high: 108.410004, low: 108.25, close: 99999999, volume: 875903, date: "2024-12-16 15:45:00" || "2024-12-16"})');
|
|
105
|
+
}
|
|
106
|
+
if(!this.isValidDate)
|
|
107
|
+
{
|
|
108
|
+
throw Error('All the OHLCV rows require a valid date property to access the compute change method. Correct date format: "2024-12-16 15:45:00" or "2024-12-16"')
|
|
109
|
+
}
|
|
110
|
+
if(!validateDate(change.date))
|
|
111
|
+
{
|
|
112
|
+
throw Error('The date in the new OHLCV row is invalid. Correct date format: "2024-12-16 15:45:00" or "2024-12-16"')
|
|
113
|
+
}
|
|
114
|
+
|
|
98
115
|
const { date: changeDate } = change;
|
|
99
116
|
const lastIndex = this.len - 1;
|
|
100
117
|
const inputDate = this.input[lastIndex]?.date;
|
|
101
118
|
|
|
119
|
+
//fallback if .compute(change) is triggered before .compute() alone
|
|
102
120
|
if (this.lastComputedIndex === 0) {
|
|
103
|
-
this.compute()
|
|
121
|
+
this.compute()
|
|
104
122
|
}
|
|
105
|
-
|
|
123
|
+
|
|
124
|
+
|
|
106
125
|
if (inputDate !== changeDate) {
|
|
107
126
|
// Add new item
|
|
108
|
-
this.len
|
|
109
|
-
this.input.push(change)
|
|
110
|
-
parseOhlcvToVertical(this.input, this, this.len)
|
|
127
|
+
this.len++
|
|
128
|
+
this.input.push(change)
|
|
129
|
+
parseOhlcvToVertical(this.input, this, this.len)
|
|
111
130
|
} else {
|
|
112
131
|
// Modify the last item
|
|
113
|
-
this.input[lastIndex] = change
|
|
114
|
-
parseOhlcvToVertical(this.input, this, lastIndex)
|
|
132
|
+
this.input[lastIndex] = change
|
|
133
|
+
parseOhlcvToVertical(this.input, this, lastIndex)
|
|
115
134
|
}
|
|
135
|
+
|
|
116
136
|
}
|
|
117
137
|
|
|
118
|
-
|
|
119
|
-
parseOhlcvToVertical(this.input, this, 0);
|
|
120
|
-
}
|
|
138
|
+
|
|
121
139
|
|
|
122
140
|
return this;
|
|
123
141
|
}
|
|
@@ -126,9 +144,10 @@ export default class OHLCV_INDICATORS {
|
|
|
126
144
|
|
|
127
145
|
crossPairs(arr)
|
|
128
146
|
{
|
|
147
|
+
isAlreadyComputed(this)
|
|
148
|
+
|
|
129
149
|
this.crossPairsList = [...this.crossPairsList, ...arr]
|
|
130
|
-
this.inputParams.crossPairs
|
|
131
|
-
this.inputParams.crossPairs.push([this.crossPairsList])
|
|
150
|
+
this.inputParams.push({key: 'crossPairs', params: [this.crossPairsList]})
|
|
132
151
|
|
|
133
152
|
return this
|
|
134
153
|
}
|
|
@@ -136,8 +155,9 @@ export default class OHLCV_INDICATORS {
|
|
|
136
155
|
|
|
137
156
|
lag(colKeys = ['close'], lags = 1) {
|
|
138
157
|
|
|
139
|
-
this
|
|
140
|
-
|
|
158
|
+
isAlreadyComputed(this)
|
|
159
|
+
|
|
160
|
+
this.inputParams.push({key: 'lag', params: [colKeys, lags]})
|
|
141
161
|
|
|
142
162
|
for(let x = 0; x < colKeys.length; x++)
|
|
143
163
|
{
|
|
@@ -154,38 +174,44 @@ export default class OHLCV_INDICATORS {
|
|
|
154
174
|
|
|
155
175
|
relativeVolume(size) {
|
|
156
176
|
|
|
157
|
-
this
|
|
158
|
-
|
|
177
|
+
isAlreadyComputed(this)
|
|
178
|
+
|
|
179
|
+
this.inputParams.push({key: 'relativeVolume', params: [size]})
|
|
159
180
|
|
|
160
181
|
return this
|
|
161
182
|
}
|
|
162
183
|
|
|
163
184
|
ema(size) {
|
|
164
185
|
|
|
186
|
+
isAlreadyComputed(this)
|
|
187
|
+
|
|
165
188
|
if (typeof size !== 'number' || size <= 0) {
|
|
166
189
|
throw new Error('"size" must be a positive number in ema.');
|
|
167
190
|
}
|
|
168
191
|
|
|
169
|
-
this.inputParams.ema
|
|
170
|
-
this.inputParams.ema.push([size])
|
|
192
|
+
this.inputParams.push({key: 'ema', params: [size]})
|
|
171
193
|
this.priceBased.push(`ema_${size}`)
|
|
172
194
|
|
|
173
195
|
return this
|
|
174
196
|
}
|
|
175
197
|
sma(size) {
|
|
176
198
|
|
|
199
|
+
isAlreadyComputed(this)
|
|
200
|
+
|
|
177
201
|
if (typeof size !== 'number' || size <= 0) {
|
|
178
202
|
throw new Error('"size" must be a positive number in sma.');
|
|
179
203
|
}
|
|
180
204
|
|
|
181
|
-
|
|
182
|
-
this.inputParams.
|
|
205
|
+
|
|
206
|
+
this.inputParams.push({key: 'sma', params: [size]})
|
|
183
207
|
this.priceBased.push(`sma_${size}`)
|
|
184
208
|
|
|
185
209
|
return this
|
|
186
210
|
}
|
|
187
211
|
macd(fast = 12, slow = 26, signal = 9) {
|
|
188
212
|
|
|
213
|
+
isAlreadyComputed(this)
|
|
214
|
+
|
|
189
215
|
if (typeof fast !== 'number' || fast <= 0) {
|
|
190
216
|
throw new Error('"fast" must be a positive number in macd.');
|
|
191
217
|
}
|
|
@@ -196,23 +222,32 @@ export default class OHLCV_INDICATORS {
|
|
|
196
222
|
throw new Error('"signal" must be a positive number in macd.');
|
|
197
223
|
}
|
|
198
224
|
|
|
199
|
-
this.inputParams.macd
|
|
200
|
-
this.inputParams.macd.push([fast, slow, signal])
|
|
225
|
+
this.inputParams.push({key: 'macd', params: [fast, slow, signal]})
|
|
201
226
|
|
|
202
227
|
return this
|
|
203
228
|
|
|
204
229
|
}
|
|
205
|
-
bollingerBands(size = 20,
|
|
230
|
+
bollingerBands(size = 20, stdDev = 2, options = {}) {
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
isAlreadyComputed(this)
|
|
234
|
+
|
|
235
|
+
if(!options || typeof options !== 'object')
|
|
236
|
+
{
|
|
237
|
+
throw new Error('"options" must be an object in bollingerBands. eg: {target, height, range}');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const {target = 'close', height = false, range = false} = options
|
|
241
|
+
|
|
206
242
|
// Validate size and times
|
|
207
243
|
if (typeof size !== 'number' || size <= 0) {
|
|
208
|
-
throw new Error('
|
|
244
|
+
throw new Error('"size" must be a positive number in bollingerBands.');
|
|
209
245
|
}
|
|
210
|
-
if (typeof
|
|
211
|
-
throw new Error('
|
|
246
|
+
if (typeof stdDev !== 'number' || stdDev <= 0) {
|
|
247
|
+
throw new Error('"stdDev" must be a positive number in bollingerBands.');
|
|
212
248
|
}
|
|
213
249
|
|
|
214
|
-
this.inputParams.bollingerBands
|
|
215
|
-
this.inputParams.bollingerBands.push([size, times, bollingerBandsStudies]);
|
|
250
|
+
this.inputParams.push({key: 'bollingerBands', params: [size, stdDev, {target, height, range}]});
|
|
216
251
|
this.priceBased.push('bollinger_bands_middle', 'bollinger_bands_upper', 'bollinger_bands_lower');
|
|
217
252
|
|
|
218
253
|
return this;
|
|
@@ -220,18 +255,21 @@ export default class OHLCV_INDICATORS {
|
|
|
220
255
|
|
|
221
256
|
rsi(size)
|
|
222
257
|
{
|
|
258
|
+
isAlreadyComputed(this)
|
|
259
|
+
|
|
223
260
|
// Validate size and times
|
|
224
261
|
if (typeof size !== 'number' || size <= 0) {
|
|
225
262
|
throw new Error('"size" must be a positive number in rsi.');
|
|
226
263
|
}
|
|
227
264
|
|
|
228
|
-
this.inputParams.rsi
|
|
229
|
-
this.inputParams.rsi.push([size])
|
|
265
|
+
this.inputParams.push({key: 'rsi', params: [size]})
|
|
230
266
|
|
|
231
267
|
return this
|
|
232
268
|
}
|
|
233
269
|
donchianChannels(size = 20, offset = 0)
|
|
234
270
|
{
|
|
271
|
+
isAlreadyComputed(this)
|
|
272
|
+
|
|
235
273
|
if (typeof size !== 'number' || size <= 0) {
|
|
236
274
|
throw new Error('"size" must be a positive number or 0 in donchianChannels.');
|
|
237
275
|
}
|
|
@@ -240,31 +278,32 @@ export default class OHLCV_INDICATORS {
|
|
|
240
278
|
throw new Error('"offset" must be a positive number or 0 in donchianChannels.');
|
|
241
279
|
}
|
|
242
280
|
|
|
243
|
-
this.inputParams.donchianChannels
|
|
244
|
-
this.inputParams.donchianChannels.push([size, offset])
|
|
281
|
+
this.inputParams.push({key: 'donchianChannels', params: [size, offset]})
|
|
245
282
|
this.priceBased.push('donchian_channel_upper', 'donchian_channel_lower', 'donchian_channel_basis')
|
|
246
283
|
|
|
247
284
|
return this
|
|
248
285
|
}
|
|
249
|
-
|
|
286
|
+
candleStudies(size = 20, classify = true, classificationLevels)
|
|
250
287
|
{
|
|
251
288
|
|
|
289
|
+
isAlreadyComputed(this)
|
|
290
|
+
|
|
252
291
|
if (typeof size !== 'number' || size <= 0) {
|
|
253
|
-
throw new Error('"size" must be a positive number or 0 in
|
|
292
|
+
throw new Error('"size" must be a positive number or 0 in candleStudies.');
|
|
254
293
|
}
|
|
255
294
|
|
|
256
295
|
if (typeof classify !== 'boolean') {
|
|
257
|
-
throw new Error('"classify" must be a true or false in
|
|
296
|
+
throw new Error('"classify" must be a true or false in candleStudies.');
|
|
258
297
|
}
|
|
259
298
|
|
|
260
|
-
this.inputParams.
|
|
261
|
-
this.inputParams.candlesStudies.push([size, classify, classificationLevels])
|
|
299
|
+
this.inputParams.push({key: 'candleStudies', params: [size, classify, classificationLevels]})
|
|
262
300
|
return this
|
|
263
301
|
}
|
|
264
302
|
|
|
265
303
|
volumeOscillator(fastSize = 5, slowSize = 10)
|
|
266
304
|
{
|
|
267
305
|
|
|
306
|
+
isAlreadyComputed(this)
|
|
268
307
|
|
|
269
308
|
if (typeof fastSize !== 'number' || fastSize <= 0) {
|
|
270
309
|
throw new Error('fastSize" must be a positive number in volumeOscillator.');
|
|
@@ -274,9 +313,21 @@ export default class OHLCV_INDICATORS {
|
|
|
274
313
|
throw new Error('"slowSize" must be a positive number greater than the "fastSize" in volumeOscillator.');
|
|
275
314
|
}
|
|
276
315
|
|
|
316
|
+
this.inputParams.push({key: 'volumeOscillator', params: [fastSize, slowSize]})
|
|
317
|
+
return this
|
|
318
|
+
}
|
|
319
|
+
dateTime()
|
|
320
|
+
{
|
|
321
|
+
isAlreadyComputed(this)
|
|
322
|
+
|
|
323
|
+
this.inputParams.push({key: 'dateTime', params: []})
|
|
324
|
+
return this
|
|
325
|
+
}
|
|
326
|
+
priceVariations()
|
|
327
|
+
{
|
|
328
|
+
isAlreadyComputed(this)
|
|
277
329
|
|
|
278
|
-
this.inputParams.
|
|
279
|
-
this.inputParams.volumeOscillator.push([fastSize, slowSize])
|
|
330
|
+
this.inputParams.push({key: 'priceVariations', params: []})
|
|
280
331
|
return this
|
|
281
332
|
}
|
|
282
333
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ohlcv-indicators",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
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,35 +1,44 @@
|
|
|
1
1
|
import {FasterBollingerBands, FasterSMA} from 'trading-signals';
|
|
2
2
|
import { classifySize } from '../utilities/classification.js';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
let defaultTarget = 'close'
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
export const bollingerBands = (main, index, size, times, options) => {
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
const {height, range, target} = options
|
|
9
|
+
let suffix = (target === defaultTarget) ? '' : `_${target}`
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
instance: new FasterBollingerBands(size, times),
|
|
12
|
-
heightInstance: bollingerBandsStudies ? new FasterSMA(size) : null
|
|
13
|
-
}})
|
|
11
|
+
if (index === 0) {
|
|
14
12
|
|
|
13
|
+
if(!main.verticalOhlcv.hasOwnProperty(target))
|
|
14
|
+
{
|
|
15
|
+
throw new Error(`bollingerBands could not find target "${target}"`)
|
|
16
|
+
}
|
|
15
17
|
|
|
16
|
-
Object.assign(main.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
Object.assign(main.instances, {
|
|
19
|
+
[`bollinger_bands${suffix}`]: {
|
|
20
|
+
instance: new FasterBollingerBands(size, times),
|
|
21
|
+
heightInstance: height ? new FasterSMA(size) : null
|
|
22
|
+
}
|
|
20
23
|
})
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
|
|
25
|
+
Object.assign(main.verticalOhlcv, {
|
|
26
|
+
[`bollinger_bands_upper${suffix}`]: [...main.nullArray],
|
|
27
|
+
[`bollinger_bands_middle${suffix}`]: [...main.nullArray],
|
|
28
|
+
[`bollinger_bands_lower${suffix}`]: [...main.nullArray],
|
|
29
|
+
...(height && {
|
|
30
|
+
[`bollinger_bands_height${suffix}`]: [...main.nullArray]
|
|
31
|
+
}),
|
|
32
|
+
...(range && {
|
|
33
|
+
[`bollinger_bands_range${suffix}`]: [...main.nullArray],
|
|
27
34
|
})
|
|
28
|
-
}
|
|
35
|
+
})
|
|
29
36
|
}
|
|
30
37
|
|
|
31
|
-
|
|
32
|
-
instance.
|
|
38
|
+
|
|
39
|
+
const { instance, heightInstance } = main.instances[`bollinger_bands${suffix}`]
|
|
40
|
+
const value = main.verticalOhlcv[target][index]
|
|
41
|
+
instance.update(value, main.lastIndexReplace)
|
|
33
42
|
|
|
34
43
|
let result;
|
|
35
44
|
try {
|
|
@@ -41,20 +50,20 @@ export const bollingerBands = (main, index, size, times, bollingerBandsStudies =
|
|
|
41
50
|
if(!result) return true
|
|
42
51
|
|
|
43
52
|
const { upper, middle, lower } = result;
|
|
44
|
-
main.verticalOhlcv
|
|
45
|
-
main.verticalOhlcv
|
|
46
|
-
main.verticalOhlcv
|
|
53
|
+
main.verticalOhlcv[`bollinger_bands_upper${suffix}`][index] = upper;
|
|
54
|
+
main.verticalOhlcv[`bollinger_bands_middle${suffix}`][index] = middle;
|
|
55
|
+
main.verticalOhlcv[`bollinger_bands_lower${suffix}`][index] = lower;
|
|
47
56
|
|
|
48
|
-
if (!
|
|
57
|
+
if (!height && !range) return true;
|
|
49
58
|
|
|
50
|
-
let
|
|
51
|
-
let
|
|
59
|
+
let rangeValue = null;
|
|
60
|
+
let heightValue = null;
|
|
52
61
|
let heightMean;
|
|
53
62
|
|
|
54
|
-
|
|
55
|
-
|
|
63
|
+
rangeValue = (value - lower) / (upper - lower);
|
|
64
|
+
heightValue = upper - lower;
|
|
56
65
|
|
|
57
|
-
heightInstance.update(
|
|
66
|
+
heightInstance.update(heightValue, main.lastIndexReplace);
|
|
58
67
|
|
|
59
68
|
try {
|
|
60
69
|
heightMean = heightInstance.getResult();
|
|
@@ -62,11 +71,11 @@ export const bollingerBands = (main, index, size, times, bollingerBandsStudies =
|
|
|
62
71
|
//do nothing
|
|
63
72
|
}
|
|
64
73
|
|
|
65
|
-
main.verticalOhlcv
|
|
74
|
+
main.verticalOhlcv[`bollinger_bands_range${suffix}`][index] = rangeValue;
|
|
66
75
|
|
|
67
76
|
if(heightMean)
|
|
68
77
|
{
|
|
69
|
-
main.verticalOhlcv
|
|
78
|
+
main.verticalOhlcv[`bollinger_bands_height${suffix}`][index] = classifySize(heightValue, heightMean, 1.5);
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
return true;
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const avg = (h, l) => (h + l) / 2;
|
|
2
|
-
|
|
3
1
|
export const donchianChannels = (main, index, size, offset) => {
|
|
4
2
|
|
|
5
3
|
const highs = main.verticalOhlcv.high;
|
|
@@ -7,13 +5,11 @@ export const donchianChannels = (main, index, size, offset) => {
|
|
|
7
5
|
|
|
8
6
|
// Ensure that the required arrays exist
|
|
9
7
|
if (index === 0) {
|
|
10
|
-
|
|
11
|
-
main.instances['donchian_channels'] = {};
|
|
12
8
|
|
|
13
9
|
Object.assign(main.verticalOhlcv, {
|
|
14
|
-
donchian_channel_upper:
|
|
15
|
-
donchian_channel_basis:
|
|
16
|
-
donchian_channel_lower:
|
|
10
|
+
donchian_channel_upper: [...main.nullArray],
|
|
11
|
+
donchian_channel_basis: [...main.nullArray],
|
|
12
|
+
donchian_channel_lower: [...main.nullArray],
|
|
17
13
|
})
|
|
18
14
|
}
|
|
19
15
|
|
|
@@ -32,7 +28,7 @@ export const donchianChannels = (main, index, size, offset) => {
|
|
|
32
28
|
// Compute the Donchian channels
|
|
33
29
|
const upper = Math.max(...highChunk);
|
|
34
30
|
const lower = Math.min(...lowChunk);
|
|
35
|
-
const basis =
|
|
31
|
+
const basis = (upper + lower) / 2;
|
|
36
32
|
|
|
37
33
|
// Place the result at index
|
|
38
34
|
const placementIndex = index;
|
|
@@ -8,11 +8,11 @@ export const ema = (main, index, size) => {
|
|
|
8
8
|
if(index === 0)
|
|
9
9
|
{
|
|
10
10
|
main.instances[`ema_${size}`] = new FasterEMA(size)
|
|
11
|
-
main.verticalOhlcv[`ema_${size}`] =
|
|
11
|
+
main.verticalOhlcv[`ema_${size}`] = [...main.nullArray]
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
let currSma
|
|
15
|
-
main.instances[`ema_${size}`].update(value)
|
|
15
|
+
main.instances[`ema_${size}`].update(value, main.lastIndexReplace)
|
|
16
16
|
|
|
17
17
|
try{
|
|
18
18
|
currSma = main.instances[`ema_${size}`].getResult()
|
|
@@ -6,7 +6,7 @@ export const macd = (main, index, fast, slow, signal) => {
|
|
|
6
6
|
|
|
7
7
|
if (index === 0) {
|
|
8
8
|
|
|
9
|
-
main.crossPairsList.push({fast: 'macd_diff', slow: 'macd_dea'});
|
|
9
|
+
main.crossPairsList.push({fast: 'macd_diff', slow: 'macd_dea', isDefault: true});
|
|
10
10
|
|
|
11
11
|
main.instances['macd'] = new FasterMACD(
|
|
12
12
|
new FasterEMA(fast),
|
|
@@ -15,14 +15,14 @@ export const macd = (main, index, fast, slow, signal) => {
|
|
|
15
15
|
);
|
|
16
16
|
|
|
17
17
|
Object.assign(main.verticalOhlcv, {
|
|
18
|
-
macd_diff:
|
|
19
|
-
macd_dea:
|
|
20
|
-
macd_histogram:
|
|
18
|
+
macd_diff: [...main.nullArray],
|
|
19
|
+
macd_dea: [...main.nullArray],
|
|
20
|
+
macd_histogram: [...main.nullArray],
|
|
21
21
|
})
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
const macdInstance = main.instances[`macd`];
|
|
25
|
-
macdInstance.update(value);
|
|
25
|
+
macdInstance.update(value, main.lastIndexReplace);
|
|
26
26
|
|
|
27
27
|
let macdResult;
|
|
28
28
|
|
|
@@ -5,16 +5,18 @@ export const relativeVolume = (main, index, size = 10) => {
|
|
|
5
5
|
const value = main.verticalOhlcv.volume[index]
|
|
6
6
|
|
|
7
7
|
if (index === 0) {
|
|
8
|
-
main.instances[`relative_volume_${size}`] =
|
|
8
|
+
main.instances[`relative_volume_${size}`] = {
|
|
9
|
+
instance: new FasterSMA(size),
|
|
10
|
+
prevRelativeVolumeSma: null
|
|
11
|
+
}
|
|
9
12
|
|
|
10
13
|
Object.assign(main.verticalOhlcv, {
|
|
11
|
-
[`relative_volume_${size}`]:
|
|
12
|
-
[`relative_volume_sma_${size}`]: new Array(main.len).fill(null)
|
|
14
|
+
[`relative_volume_${size}`]: [...main.nullArray],
|
|
13
15
|
})
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
const smaInstance = main.instances[`relative_volume_${size}`]
|
|
17
|
-
smaInstance.update(value)
|
|
18
|
+
const smaInstance = main.instances[`relative_volume_${size}`].instance
|
|
19
|
+
smaInstance.update(value, main.lastIndexReplace)
|
|
18
20
|
|
|
19
21
|
let smaValue;
|
|
20
22
|
try {
|
|
@@ -23,13 +25,16 @@ export const relativeVolume = (main, index, size = 10) => {
|
|
|
23
25
|
//do nothing
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
main.
|
|
28
|
+
const {prevRelativeVolumeSma} = main.instances[`relative_volume_${size}`]
|
|
27
29
|
|
|
28
|
-
if (smaValue !== null &&
|
|
29
|
-
main.verticalOhlcv[`relative_volume_${size}`][index] = value /
|
|
30
|
+
if (smaValue !== null && prevRelativeVolumeSma !== null) {
|
|
31
|
+
main.verticalOhlcv[`relative_volume_${size}`][index] = value / prevRelativeVolumeSma;
|
|
30
32
|
} else {
|
|
31
33
|
main.verticalOhlcv[`relative_volume_${size}`][index] = null;
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
|
|
37
|
+
main.instances[`relative_volume_${size}`].prevRelativeVolumeSma = smaValue
|
|
38
|
+
|
|
34
39
|
return true;
|
|
35
40
|
};
|
|
@@ -8,11 +8,11 @@ export const sma = (main, index, size) => {
|
|
|
8
8
|
if(index === 0)
|
|
9
9
|
{
|
|
10
10
|
main.instances[`sma_${size}`] = new FasterSMA(size)
|
|
11
|
-
main.verticalOhlcv[`sma_${size}`] =
|
|
11
|
+
main.verticalOhlcv[`sma_${size}`] = [...main.nullArray]
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
let currSma
|
|
15
|
-
main.instances[`sma_${size}`].update(value)
|
|
15
|
+
main.instances[`sma_${size}`].update(value, main.lastIndexReplace)
|
|
16
16
|
|
|
17
17
|
try{
|
|
18
18
|
currSma = main.instances[`sma_${size}`].getResult()
|
package/src/oscillators/rsi.js
CHANGED
|
@@ -8,7 +8,7 @@ export const rsi = (main, index, size) => {
|
|
|
8
8
|
|
|
9
9
|
if(index === 0)
|
|
10
10
|
{
|
|
11
|
-
main.crossPairsList.push({fast: `rsi_${size}`, slow: `rsi_sma_${size}
|
|
11
|
+
main.crossPairsList.push({fast: `rsi_${size}`, slow: `rsi_sma_${size}`, isDefault: true})
|
|
12
12
|
|
|
13
13
|
Object.assign(main.instances, {
|
|
14
14
|
[`rsi_${size}`]: new FasterRSI(size),
|
|
@@ -16,15 +16,15 @@ export const rsi = (main, index, size) => {
|
|
|
16
16
|
})
|
|
17
17
|
|
|
18
18
|
Object.assign(main.verticalOhlcv, {
|
|
19
|
-
[`rsi_${size}`]:
|
|
20
|
-
[`rsi_sma_${size}`]:
|
|
19
|
+
[`rsi_${size}`]: [...main.nullArray],
|
|
20
|
+
[`rsi_sma_${size}`]: [...main.nullArray]
|
|
21
21
|
})
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
let currentRsi
|
|
25
25
|
let smoothedRsi
|
|
26
26
|
|
|
27
|
-
main.instances[`rsi_${size}`].update(value)
|
|
27
|
+
main.instances[`rsi_${size}`].update(value, main.lastIndexReplace)
|
|
28
28
|
|
|
29
29
|
try
|
|
30
30
|
{
|
|
@@ -36,7 +36,7 @@ export const rsi = (main, index, size) => {
|
|
|
36
36
|
if(currentRsi)
|
|
37
37
|
{
|
|
38
38
|
main.verticalOhlcv[`rsi_${size}`][index] = currentRsi
|
|
39
|
-
main.instances[`rsi_sma_${size}`].update(currentRsi)
|
|
39
|
+
main.instances[`rsi_sma_${size}`].update(currentRsi, main.lastIndexReplace)
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
try
|
|
@@ -13,14 +13,14 @@ export const volumeOscillator = (main, index, fast, slow) => {
|
|
|
13
13
|
}
|
|
14
14
|
})
|
|
15
15
|
|
|
16
|
-
main.verticalOhlcv[`volume_oscillator`] =
|
|
17
|
-
main.crossPairsList.push({ fast: `volume_oscillator`, slow: 0 });
|
|
16
|
+
main.verticalOhlcv[`volume_oscillator`] = [...main.nullArray];
|
|
17
|
+
main.crossPairsList.push({ fast: `volume_oscillator`, slow: 0, isDefault: true });
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const { fastEMA, slowEMA } = main.instances[`volume_oscillator`];
|
|
21
21
|
|
|
22
|
-
fastEMA.update(value);
|
|
23
|
-
slowEMA.update(value);
|
|
22
|
+
fastEMA.update(value, main.lastIndexReplace);
|
|
23
|
+
slowEMA.update(value, main.lastIndexReplace);
|
|
24
24
|
|
|
25
25
|
let fastValue = null;
|
|
26
26
|
let slowValue = null;
|