ohlcv-indicators 3.3.2 → 3.3.4
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 +1646 -1711
- package/index.js +163 -268
- package/package.json +1 -3
- package/src/core-functions/mainLoop.js +100 -0
- package/src/core-functions/pushToMain.js +15 -0
- package/src/moving-averages/bollingerBands.js +22 -30
- package/src/moving-averages/donchianChannel.js +26 -34
- package/src/moving-averages/macd.js +27 -15
- package/src/moving-averages/movingAverages.js +15 -130
- package/src/moving-averages/relativeVolume.js +20 -14
- package/src/oscillators/rsi.js +21 -25
- package/src/oscillators/volumeOscillator.js +20 -19
- package/src/studies/dateTime.js +22 -20
- package/src/studies/findCrosses.js +17 -17
- package/src/studies/lag.js +21 -12
- package/src/studies/scaler.js +112 -0
- package/src/utilities/assignTypes.js +39 -0
- package/src/utilities/dateUtilities.js +79 -0
- package/src/utilities/numberUtilities.js +50 -9
- package/src/utilities/precisionMultiplier.js +24 -0
- package/src/utilities/validators.js +76 -5
- package/src/utilities/{dataParsingUtilities.js → verticalToHorizontal.js} +8 -8
- package/src/studies/candleVectors.js +0 -121
- package/src/studies/priceVariations.js +0 -23
- package/src/utilities/classification.js +0 -75
- package/src/utilities/parsing-utilities.js +0 -189
- package/src/utilities/pushToMain.js +0 -63
package/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mainLoop } from './src/core-functions/mainLoop.js'
|
|
2
2
|
import { correlation } from './src/studies/correlation.js'
|
|
3
|
-
import {
|
|
4
|
-
import { isAlreadyComputed } from './src/utilities/validators.js'
|
|
3
|
+
import { isAlreadyComputed, validateArray, validateObject, validateArrayOptions, validateBoolean, validateNumber } from './src/utilities/validators.js'
|
|
5
4
|
import { divideByMultiplier } from './src/utilities/numberUtilities.js'
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { verticalToHorizontal } from './src/utilities/verticalToHorizontal.js'
|
|
6
|
+
import { pushToMain } from './src/core-functions/pushToMain.js'
|
|
7
|
+
import { assignTypes } from './src/utilities/assignTypes.js'
|
|
8
|
+
import { calcPrecisionMultiplier } from './src/utilities/precisionMultiplier.js'
|
|
9
|
+
import { selectDateFormatter, dateFormaters } from './src/utilities/dateUtilities.js'
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Class OHLCV_INDICATORS
|
|
@@ -16,23 +17,31 @@ import { pushToMain } from './src/utilities/pushToMain.js'
|
|
|
16
17
|
* OHLCV datasets.
|
|
17
18
|
*/
|
|
18
19
|
|
|
19
|
-
const validMagnitudeValues = [0.001, 0.002, 0.0025, 0.005, 0.01, 0.02, 0.025, 0.05, 0.1, 0.20, 0.25, 0.5, 1, 2, 2.5, 5, 10]
|
|
20
|
-
|
|
21
20
|
export default class OHLCV_INDICATORS {
|
|
22
21
|
constructor({input, ticker = null, precision = true, inputParams = null}) {
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
validateArray(input, 'input', (ticker !== null) ? `contructor ${ticker}` : 'constuctor')
|
|
25
24
|
if(input.length === 0) throw Error('input OHLCV must not be empty: ' + ticker)
|
|
26
|
-
if(!input[0].hasOwnProperty('close')) throw Error('input OHLCV array objects require at least close property: ' + ticker)
|
|
27
|
-
this.hasVolume = ((typeof input[0].volume === 'number' && input[0].volume > 0) || (typeof input[0].volume === 'string' && input[0].volume)) ? true : false
|
|
28
25
|
|
|
26
|
+
validateBoolean(precision, 'precision', 'constructor')
|
|
27
|
+
|
|
28
|
+
this.firstRow = input[0]
|
|
29
|
+
|
|
30
|
+
this.inputCols = Object.keys(this.firstRow)
|
|
31
|
+
|
|
32
|
+
const {inputTypes, arrayTypes} = assignTypes(this.firstRow)
|
|
33
|
+
|
|
34
|
+
this.inputTypes = inputTypes
|
|
35
|
+
this.arrayTypes = arrayTypes
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if(!this.firstRow.hasOwnProperty('close')) throw Error(`input OHLCV array objects require at least "close" property: ${ticker}`)
|
|
39
|
+
|
|
40
|
+
this.dateType = this.inputTypes.date ? this.inputTypes.date : null;
|
|
29
41
|
this.isComputed = false
|
|
30
|
-
this.lastComputedIndex = 0
|
|
31
42
|
this.input = input
|
|
32
|
-
this.inputTypes = (this.hasVolume) ? {open: '', high: '', low: '', close: '', volume: ''} : {open: '', high: '', low: '', close: ''}
|
|
33
43
|
this.priceBased = ['open', 'high', 'low', 'close']
|
|
34
44
|
this.len = input.length
|
|
35
|
-
this.lastIndexReplace = false
|
|
36
45
|
this.instances = {}
|
|
37
46
|
this.crossPairsList = []
|
|
38
47
|
this.verticalOhlcv = {}
|
|
@@ -45,14 +54,13 @@ export default class OHLCV_INDICATORS {
|
|
|
45
54
|
this.invalidValueIndex = -1
|
|
46
55
|
|
|
47
56
|
this.precision = precision
|
|
48
|
-
this.precisionMultiplier = (this.
|
|
49
|
-
|
|
50
|
-
this.autoMinMaxKeys = []
|
|
51
|
-
this.minMaxRanges = {}
|
|
57
|
+
this.precisionMultiplier = calcPrecisionMultiplier(this, this.firstRow)
|
|
58
|
+
this.ScaledGroups = {}
|
|
52
59
|
|
|
53
60
|
|
|
54
|
-
if(
|
|
61
|
+
if(inputParams !== null)
|
|
55
62
|
{
|
|
63
|
+
validateArray(inputParams, 'inputParams', 'constructor')
|
|
56
64
|
this.inputParams = inputParams
|
|
57
65
|
this.compute()
|
|
58
66
|
}
|
|
@@ -82,7 +90,6 @@ export default class OHLCV_INDICATORS {
|
|
|
82
90
|
const shouldSlice = invalidValueIndex >= 0 && skipNull;
|
|
83
91
|
const sliceLength = len - (invalidValueIndex + 1);
|
|
84
92
|
|
|
85
|
-
|
|
86
93
|
for (const [key, arr] of Object.entries(verticalOhlcv)) {
|
|
87
94
|
// If slicing is needed, create a sliced copy, otherwise re-use the array.
|
|
88
95
|
let newArr = shouldSlice ? arr.slice(-sliceLength) : arr;
|
|
@@ -125,102 +132,57 @@ export default class OHLCV_INDICATORS {
|
|
|
125
132
|
return (precision) ? divideByMultiplier({row, precisionMultiplier, priceBased}) : row
|
|
126
133
|
}
|
|
127
134
|
|
|
128
|
-
compute(
|
|
135
|
+
compute() {
|
|
129
136
|
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
//stops the compute if compute is called from getData, getDataAsCols or getLastValues an isComputed is false
|
|
134
|
-
if(this.isComputed === true && !change)
|
|
135
|
-
{
|
|
136
|
-
return this
|
|
137
|
+
// If we've already computed, bail out immediately
|
|
138
|
+
if (this.isComputed) {
|
|
139
|
+
return this;
|
|
137
140
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
//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
|
|
153
|
-
//valid date properties: "2024-12-16 15:45:00" or "2024-12-16"
|
|
154
|
-
if (change && typeof change === 'object') {
|
|
155
|
-
|
|
156
|
-
if (!['open', 'high', 'low', 'close', 'volume', 'date'].every(f => Object.keys(change).includes(f))) {
|
|
157
|
-
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"})');
|
|
158
|
-
}
|
|
159
|
-
if(!this.isValidDate)
|
|
160
|
-
{
|
|
161
|
-
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"')
|
|
162
|
-
}
|
|
163
|
-
if(!validateDate(change.date))
|
|
164
|
-
{
|
|
165
|
-
throw Error('The date in the new OHLCV row is invalid. Correct date format: "2024-12-16 15:45:00" or "2024-12-16"')
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const { date: changeDate } = change;
|
|
169
|
-
const lastIndex = this.len - 1;
|
|
170
|
-
const inputDate = this.input[lastIndex]?.date;
|
|
171
|
-
|
|
172
|
-
//fallback if .compute(change) is triggered before .compute() alone
|
|
173
|
-
if (this.lastComputedIndex === 0) {
|
|
174
|
-
this.compute()
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (inputDate !== changeDate) {
|
|
179
|
-
// Add new item
|
|
180
|
-
this.len++
|
|
181
|
-
this.input.push(change)
|
|
182
|
-
parseOhlcvToVertical(this.input, this, this.len)
|
|
183
|
-
} else {
|
|
184
|
-
// Modify the last item
|
|
185
|
-
this.input[lastIndex] = change
|
|
186
|
-
parseOhlcvToVertical(this.input, this, lastIndex)
|
|
187
|
-
}
|
|
188
|
-
|
|
141
|
+
|
|
142
|
+
// Mark as “in progress”
|
|
143
|
+
this.isComputed = false;
|
|
144
|
+
|
|
145
|
+
// Figure out whether there’s a date field in the inputs
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
// Only run the full loop once (or when new data appears later,
|
|
149
|
+
// if you extend this to reset isComputed elsewhere)
|
|
150
|
+
if (this.len > 0) {
|
|
151
|
+
mainLoop(this.input, this);
|
|
152
|
+
this.isComputed = true;
|
|
189
153
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
154
|
+
|
|
193
155
|
return this;
|
|
194
|
-
}
|
|
156
|
+
}
|
|
195
157
|
|
|
196
158
|
|
|
197
159
|
|
|
198
|
-
crossPairs(arr)
|
|
160
|
+
crossPairs(arr = [])
|
|
199
161
|
{
|
|
162
|
+
|
|
163
|
+
const methodName = 'crossPairs'
|
|
164
|
+
|
|
200
165
|
isAlreadyComputed(this)
|
|
201
166
|
|
|
167
|
+
validateArray(arr, 'arr', methodName)
|
|
168
|
+
|
|
202
169
|
this.crossPairsList = [...this.crossPairsList, ...arr]
|
|
203
|
-
this.inputParams.push({key:
|
|
170
|
+
this.inputParams.push({key: methodName, params: [this.crossPairsList]})
|
|
204
171
|
|
|
205
172
|
return this
|
|
206
173
|
}
|
|
207
174
|
|
|
208
175
|
|
|
209
|
-
lag(colKeys = ['close'],
|
|
176
|
+
lag(colKeys = ['close'], lookback = 1) {
|
|
210
177
|
|
|
211
|
-
|
|
178
|
+
const methodName = 'lag'
|
|
212
179
|
|
|
213
|
-
|
|
214
|
-
{
|
|
215
|
-
throw new Error('Param "colKeys" must be a valid array of keyNames in lag.')
|
|
216
|
-
}
|
|
217
|
-
if(typeof lags !== 'number' || !Number.isInteger(lags) || lags < 0)
|
|
218
|
-
{
|
|
219
|
-
throw new Error(`Param "lags" must be a integer greater or equal to 0: ${JSON.stringify(colKeys)}`)
|
|
220
|
-
}
|
|
180
|
+
isAlreadyComputed(this)
|
|
221
181
|
|
|
182
|
+
validateArray(colKeys, 'colKeys', methodName)
|
|
183
|
+
validateNumber(lookback, {min:1, max: this.len, allowDecimals: false}, 'lookback', methodName)
|
|
222
184
|
|
|
223
|
-
this.inputParams.push({key:
|
|
185
|
+
this.inputParams.push({key: methodName, params: [colKeys, lookback]})
|
|
224
186
|
|
|
225
187
|
for(let x = 0; x < colKeys.length; x++)
|
|
226
188
|
{
|
|
@@ -235,43 +197,59 @@ export default class OHLCV_INDICATORS {
|
|
|
235
197
|
return this;
|
|
236
198
|
}
|
|
237
199
|
|
|
238
|
-
relativeVolume(size, options = {}) {
|
|
200
|
+
relativeVolume(size = 10, options = {}) {
|
|
201
|
+
|
|
202
|
+
const methodName = 'relativeVolume'
|
|
239
203
|
|
|
240
|
-
if(this.
|
|
204
|
+
if(!this.inputCols.includes('volume')) {
|
|
241
205
|
throw new Error('If "relativeVolume" is called the input ohlcv must contain valid volume properties.')
|
|
242
206
|
}
|
|
243
207
|
|
|
244
208
|
isAlreadyComputed(this)
|
|
245
209
|
|
|
246
|
-
|
|
210
|
+
validateNumber(size, {min:1, max: this.len, allowDecimals: false}, 'size', methodName)
|
|
211
|
+
validateObject(options, 'options', methodName)
|
|
247
212
|
|
|
248
|
-
|
|
213
|
+
const { lag = 0} = options;
|
|
249
214
|
|
|
250
|
-
|
|
251
|
-
}
|
|
215
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'lag', methodName)
|
|
252
216
|
|
|
253
|
-
this.inputParams.push({key:
|
|
217
|
+
this.inputParams.push({key: methodName, params: [size, {lag}]})
|
|
254
218
|
|
|
255
219
|
return this
|
|
256
220
|
}
|
|
257
221
|
|
|
258
|
-
ema(size, options = {}) {
|
|
222
|
+
ema(size = 5, options = {}) {
|
|
223
|
+
|
|
224
|
+
const methodName = 'ema'
|
|
259
225
|
|
|
260
226
|
isAlreadyComputed(this)
|
|
261
227
|
|
|
262
|
-
|
|
228
|
+
validateNumber(size, {min: 1, max: this.len, allowDecimals: false}, 'size', methodName)
|
|
229
|
+
validateObject(options, 'options', methodName)
|
|
230
|
+
|
|
231
|
+
const {target = 'close', lag = 0} = options
|
|
263
232
|
|
|
264
|
-
|
|
233
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'lag', methodName)
|
|
234
|
+
|
|
235
|
+
this.inputParams.push({key: methodName, params: [methodName, size, {target, lag}]})
|
|
265
236
|
|
|
266
237
|
return this
|
|
267
238
|
}
|
|
268
|
-
sma(size, options = {}) {
|
|
239
|
+
sma(size = 5, options = {}) {
|
|
240
|
+
|
|
241
|
+
const methodName = 'sma'
|
|
269
242
|
|
|
270
243
|
isAlreadyComputed(this)
|
|
271
244
|
|
|
272
|
-
|
|
245
|
+
validateNumber(size, {min: 1, max: this.len, allowDecimals: false}, 'size', methodName)
|
|
246
|
+
validateObject(options, 'options', methodName)
|
|
247
|
+
|
|
248
|
+
const {target = 'close', lag = 0} = options
|
|
249
|
+
|
|
250
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'lag', methodName)
|
|
273
251
|
|
|
274
|
-
this.inputParams.push({key:
|
|
252
|
+
this.inputParams.push({key: methodName, params: [methodName, size, {target, lag}]})
|
|
275
253
|
|
|
276
254
|
return this
|
|
277
255
|
}
|
|
@@ -279,223 +257,140 @@ export default class OHLCV_INDICATORS {
|
|
|
279
257
|
|
|
280
258
|
macd(fast = 12, slow = 26, signal = 9, options = {}) {
|
|
281
259
|
|
|
260
|
+
const methodName = 'macd'
|
|
261
|
+
|
|
282
262
|
isAlreadyComputed(this)
|
|
283
263
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
throw new Error('"slow" must be a positive number greater than "fast" in macd.');
|
|
289
|
-
}
|
|
290
|
-
if (typeof signal !== 'number' || signal <= 0) {
|
|
291
|
-
throw new Error('"signal" must be a positive number in macd.');
|
|
292
|
-
}
|
|
264
|
+
validateNumber(fast, {min: 1, max: this.len, allowDecimals: false}, 'fast', methodName)
|
|
265
|
+
validateNumber(slow, {min: 1, max: this.len, allowDecimals: false}, 'slow', methodName)
|
|
266
|
+
validateNumber(signal, {min: 1, max: this.len, allowDecimals: false}, 'signal', methodName)
|
|
267
|
+
validateObject(options, 'options', methodName)
|
|
293
268
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const {target = 'close'} = options
|
|
269
|
+
const {target = 'close', lag = 0} = options
|
|
270
|
+
|
|
271
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'lag', methodName)
|
|
300
272
|
|
|
301
|
-
this.inputParams.push({key:
|
|
273
|
+
this.inputParams.push({key: methodName, params: [fast, slow, signal, {target, lag}]})
|
|
302
274
|
|
|
303
275
|
return this
|
|
304
276
|
|
|
305
277
|
}
|
|
306
278
|
bollingerBands(size = 20, stdDev = 2, options = {}) {
|
|
307
279
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
if(!options || typeof options !== 'object')
|
|
311
|
-
{
|
|
312
|
-
throw new Error('"options" must be an object in bollingerBands. eg: {target, height, range}');
|
|
313
|
-
}
|
|
280
|
+
const methodName = 'bollingerBands'
|
|
314
281
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
}
|
|
321
|
-
if (typeof stdDev !== 'number' || stdDev <= 0) {
|
|
322
|
-
throw new Error('"stdDev" must be a positive number in bollingerBands.');
|
|
323
|
-
}
|
|
324
|
-
if (!Array.isArray(range)) {
|
|
325
|
-
throw new Error('If set, "range" must be a array of column names in bollingerBands.');
|
|
326
|
-
}
|
|
327
|
-
if (typeof lag !== 'number') {
|
|
328
|
-
|
|
329
|
-
throw new Error(`"lag" value must be a number in bollingerBands.`);
|
|
330
|
-
}
|
|
282
|
+
isAlreadyComputed(this)
|
|
283
|
+
|
|
284
|
+
validateNumber(size, {min:1, max: this.len, allowDecimals: false}, 'size', methodName)
|
|
285
|
+
validateNumber(stdDev, {min: 0.01, max: 50, allowDecimals: true}, 'stdDev', methodName)
|
|
286
|
+
validateObject(options, 'options', methodName)
|
|
331
287
|
|
|
332
|
-
|
|
288
|
+
const {target = 'close', height = false, range = [], lag = 0} = options
|
|
333
289
|
|
|
334
|
-
throw new Error(`"autoMinMax" value in rsi must be a number in bollingerBands.`);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
if (typeof height !== 'boolean') {
|
|
338
|
-
throw new Error('"height" must be a boolean in bollingerBands.');
|
|
339
|
-
}
|
|
340
|
-
else
|
|
341
|
-
{
|
|
342
|
-
if (typeof scale === 'number' && !validMagnitudeValues.includes(scale)) {
|
|
343
290
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
291
|
+
validateArray(range, 'range', methodName)
|
|
292
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'lag', methodName)
|
|
293
|
+
validateBoolean(height, 'height', methodName)
|
|
347
294
|
|
|
348
|
-
this.inputParams.push({key:
|
|
295
|
+
this.inputParams.push({key: methodName, params: [size, stdDev, {target, height, range, lag}]});
|
|
349
296
|
|
|
350
297
|
return this;
|
|
351
298
|
}
|
|
352
299
|
|
|
353
|
-
rsi(size, options = {})
|
|
300
|
+
rsi(size = 14, options = {})
|
|
354
301
|
{
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
// Validate size and times
|
|
358
|
-
if (typeof size !== 'number' || size <= 0) {
|
|
359
|
-
throw new Error('"size" must be a positive number in rsi.');
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
const {scale = null, target = 'close', lag = 0, autoMinMax = false} = options
|
|
363
|
-
|
|
364
|
-
if (typeof scale === 'number' && !validMagnitudeValues.includes(scale)) {
|
|
365
|
-
|
|
366
|
-
throw new Error(`"scale" value in rsi must be any of the following numbers: ${validMagnitudeValues.join(', ')}`);
|
|
367
|
-
}
|
|
302
|
+
const methodName = 'rsi'
|
|
368
303
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
throw new Error(`"lag" value in rsi must be a number in rsi.`);
|
|
372
|
-
}
|
|
304
|
+
isAlreadyComputed(this)
|
|
373
305
|
|
|
374
|
-
|
|
306
|
+
validateNumber(size, {min: 1, max: this.len, allowDecimals: false}, 'size', methodName)
|
|
307
|
+
validateObject(options, 'options', methodName)
|
|
308
|
+
|
|
309
|
+
const {target = 'close', lag = 0} = options
|
|
375
310
|
|
|
376
|
-
|
|
377
|
-
}
|
|
311
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'lag', methodName)
|
|
378
312
|
|
|
379
|
-
this.inputParams.push({key:
|
|
313
|
+
this.inputParams.push({key: methodName, params: [size, {target, lag}]})
|
|
380
314
|
|
|
381
315
|
return this
|
|
382
316
|
}
|
|
383
317
|
donchianChannels(size = 20, offset = 0, options = {}) {
|
|
384
|
-
isAlreadyComputed(this);
|
|
385
|
-
|
|
386
|
-
if (typeof size !== 'number' || size <= 0) {
|
|
387
|
-
throw new Error('"size" must be a positive number greater than 0 in donchianChannels.');
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
if (typeof offset !== 'number' || offset < 0) {
|
|
391
|
-
throw new Error('"offset" must be a number greater than or equal to 0 in donchianChannels.');
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
const { height = false, range = [], scale = null, lag = 0, autoMinMax = false} = options;
|
|
395
|
-
|
|
396
|
-
if (!Array.isArray(range)) {
|
|
397
|
-
throw new Error('If set, "range" must be an array of column names in donchianChannels.');
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
if (typeof lag !== 'number') {
|
|
401
|
-
throw new Error(`"lag" value in rsi must be a number in donchianChannels.`);
|
|
402
|
-
}
|
|
403
318
|
|
|
404
|
-
|
|
319
|
+
const methodName = 'donchianChannels'
|
|
405
320
|
|
|
406
|
-
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
if (typeof height !== 'boolean') {
|
|
410
|
-
throw new Error('"height" must be a boolean in donchianChannels.');
|
|
411
|
-
}
|
|
412
|
-
else
|
|
413
|
-
{
|
|
414
|
-
|
|
415
|
-
if (typeof scale === 'number' && !validMagnitudeValues.includes(scale)) {
|
|
321
|
+
isAlreadyComputed(this)
|
|
416
322
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
this.inputParams.push({ key: 'donchianChannels', params: [size, offset, { height, range, scale, lag, autoMinMax }] });
|
|
323
|
+
|
|
324
|
+
validateNumber(size, {min: 1, max: this.len, allowDecimals: false}, 'size', methodName)
|
|
325
|
+
validateNumber(offset, {min: 0, max: this.len, allowDecimals: false}, 'offset', methodName)
|
|
422
326
|
|
|
423
|
-
|
|
424
|
-
|
|
327
|
+
validateObject(options, 'options', methodName)
|
|
328
|
+
const { height = false, range = [], lag = 0} = options;
|
|
425
329
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
330
|
+
validateArray(range, 'range', methodName)
|
|
331
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'lag', methodName)
|
|
332
|
+
validateBoolean(height, 'height', methodName)
|
|
429
333
|
|
|
430
|
-
|
|
431
|
-
throw new Error('"size" must be a positive number greater than 0 in candleVectors.');
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
const { stdDev = 1, lag = 0, scale = 0.001, patternSize = 0, autoMinMax = false } = options;
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
if (typeof stdDev !== 'number' || stdDev <= 0) {
|
|
438
|
-
throw new Error('"stdDev" must be a positive number greater than 0 in candleVectors.');
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
if (typeof scale === 'number' && !validMagnitudeValues.includes(scale)) {
|
|
442
|
-
|
|
443
|
-
throw new Error(`"scale" value in candleVectors must be any of the following numbers: ${validMagnitudeValues.join(', ')}`);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
if (typeof patternSize !== 'number' || !Number.isInteger(patternSize) || patternSize < 0 ) {
|
|
447
|
-
|
|
448
|
-
throw new Error(`"patternSize" value in candleVectors must be a positive integer.`);
|
|
449
|
-
}
|
|
450
|
-
if (typeof autoMinMax !== 'boolean') {
|
|
451
|
-
|
|
452
|
-
throw new Error(`"autoMinMax" value in candleVectors must be boolean`);
|
|
453
|
-
}
|
|
334
|
+
this.inputParams.push({ key: methodName, params: [size, offset, { height, range, lag }] });
|
|
454
335
|
|
|
455
|
-
this.inputParams.push({ key: 'candleVectors', params: [size, {stdDev, patternSize, lag, scale, autoMinMax}] });
|
|
456
336
|
return this;
|
|
457
|
-
|
|
337
|
+
}
|
|
458
338
|
|
|
459
339
|
|
|
460
340
|
volumeOscillator(fastSize = 5, slowSize = 10, options = {})
|
|
461
341
|
{
|
|
342
|
+
const methodName = 'volumeOscillator'
|
|
462
343
|
|
|
463
|
-
if(this.
|
|
344
|
+
if(!this.inputCols.includes('volume')) {
|
|
464
345
|
throw new Error('If "volumeOscillator" is called the input ohlcv must contain valid volume properties.')
|
|
465
346
|
}
|
|
466
347
|
|
|
467
348
|
isAlreadyComputed(this)
|
|
468
349
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
350
|
+
validateNumber(fastSize, {min: 1, max: this.len, allowDecimals: false}, 'fastSize', methodName)
|
|
351
|
+
validateNumber(slowSize, {min: fastSize, max: this.len, allowDecimals: false}, 'slowSize', methodName)
|
|
352
|
+
validateObject(options, 'options', methodName)
|
|
472
353
|
|
|
473
|
-
|
|
474
|
-
throw new Error('"slowSize" must be a positive number greater than the "fastSize" in volumeOscillator.');
|
|
475
|
-
}
|
|
354
|
+
const {lag = 0} = options
|
|
476
355
|
|
|
477
|
-
|
|
356
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'lag', methodName)
|
|
478
357
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
throw new Error(`"scale" value in volumeOscillator must be any of the following numbers: ${validMagnitudeValues.join(', ')}`);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
this.inputParams.push({key: 'volumeOscillator', params: [fastSize, slowSize, {scale}]})
|
|
358
|
+
this.inputParams.push({key: methodName, params: [fastSize, slowSize, {lag}]})
|
|
485
359
|
return this
|
|
486
360
|
}
|
|
487
|
-
dateTime()
|
|
361
|
+
dateTime(options = {})
|
|
488
362
|
{
|
|
363
|
+
|
|
364
|
+
const methodName = 'dateTime'
|
|
365
|
+
|
|
489
366
|
isAlreadyComputed(this)
|
|
490
367
|
|
|
491
|
-
|
|
368
|
+
validateObject(options, 'options', methodName)
|
|
369
|
+
|
|
370
|
+
const {lag = 0} = options
|
|
371
|
+
|
|
372
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'lag', methodName)
|
|
373
|
+
|
|
374
|
+
this.inputParams.push({key: methodName, params: [{lag}]})
|
|
492
375
|
return this
|
|
493
376
|
}
|
|
494
|
-
|
|
377
|
+
|
|
378
|
+
Scaler(size, colKeys, options)
|
|
495
379
|
{
|
|
380
|
+
const methodName = 'Scaler'
|
|
381
|
+
|
|
496
382
|
isAlreadyComputed(this)
|
|
497
383
|
|
|
498
|
-
|
|
499
|
-
|
|
384
|
+
validateNumber(size, {min: 1, max: this.len, allowDecimals: false}, 'size', methodName)
|
|
385
|
+
validateArray(colKeys, 'colKeys', methodName)
|
|
386
|
+
|
|
387
|
+
const {group = false, range = [0, 1], lag = 0, type = 'minmax'} = options
|
|
388
|
+
|
|
389
|
+
validateBoolean(group, 'group', methodName)
|
|
390
|
+
validateArray(range, 'range', methodName)
|
|
391
|
+
validateArrayOptions(['minmax', 'zscore'], type, 'type', methodName)
|
|
392
|
+
|
|
393
|
+
this.inputParams.push({key: methodName, params: [size, colKeys, type, group, range, lag]})
|
|
394
|
+
return this
|
|
500
395
|
}
|
|
501
396
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ohlcv-indicators",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.4",
|
|
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",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"test": "node test/test.js",
|
|
9
|
-
"study": "node test/study.js",
|
|
10
|
-
"count": "node test/countUps.js",
|
|
11
9
|
"build": "npx webpack",
|
|
12
10
|
"cross": "node src/studies/findCrosses2.js"
|
|
13
11
|
},
|