ohlcv-indicators 3.3.2 → 3.3.5
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 +1731 -1879
- package/index.js +170 -292
- package/package.json +1 -3
- package/src/core-functions/mainLoop.js +85 -0
- package/src/core-functions/pushToMain.js +15 -0
- package/src/moving-averages/bollingerBands.js +26 -32
- package/src/moving-averages/donchianChannel.js +31 -36
- package/src/moving-averages/macd.js +30 -16
- package/src/moving-averages/movingAverages.js +15 -129
- 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 +22 -13
- 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 +51 -10
- package/src/utilities/precisionMultiplier.js +24 -0
- package/src/utilities/validators.js +77 -8
- package/src/utilities/verticalToHorizontal.js +30 -0
- package/src/studies/candleVectors.js +0 -121
- package/src/studies/priceVariations.js +0 -23
- package/src/utilities/classification.js +0 -75
- package/src/utilities/dataParsingUtilities.js +0 -32
- package/src/utilities/parsing-utilities.js +0 -189
- package/src/utilities/pushToMain.js +0 -63
package/index.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mainLoop } from './src/core-functions/mainLoop.js'
|
|
2
2
|
import { correlation } from './src/studies/correlation.js'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { pushToMain } from './src/utilities/pushToMain.js'
|
|
3
|
+
import { isAlreadyComputed, validateArray, validateObject, validateArrayOptions, validateBoolean, validateNumber } from './src/utilities/validators.js'
|
|
4
|
+
import { verticalToHorizontal } from './src/utilities/verticalToHorizontal.js'
|
|
5
|
+
import { pushToMain } from './src/core-functions/pushToMain.js'
|
|
6
|
+
import { assignTypes } from './src/utilities/assignTypes.js'
|
|
7
|
+
import { calcPrecisionMultiplier } from './src/utilities/precisionMultiplier.js'
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
10
|
* Class OHLCV_INDICATORS
|
|
@@ -16,43 +15,44 @@ import { pushToMain } from './src/utilities/pushToMain.js'
|
|
|
16
15
|
* OHLCV datasets.
|
|
17
16
|
*/
|
|
18
17
|
|
|
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
18
|
export default class OHLCV_INDICATORS {
|
|
22
19
|
constructor({input, ticker = null, precision = true, inputParams = null}) {
|
|
23
20
|
|
|
24
|
-
|
|
21
|
+
validateArray(input, 'input', (ticker !== null) ? `contructor ${ticker}` : 'constuctor')
|
|
25
22
|
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
23
|
|
|
24
|
+
validateBoolean(precision, 'precision', 'constructor')
|
|
25
|
+
|
|
26
|
+
this.firstRow = input[0]
|
|
27
|
+
|
|
28
|
+
const {inputTypes, arrayTypes} = assignTypes(this.firstRow)
|
|
29
|
+
|
|
30
|
+
this.inputTypes = inputTypes
|
|
31
|
+
this.arrayTypes = arrayTypes
|
|
32
|
+
if(!this.firstRow.hasOwnProperty('close')) throw Error(`input OHLCV array objects require at least "close" property: ${ticker}`)
|
|
33
|
+
|
|
34
|
+
this.dateType = this.inputTypes.date ? this.inputTypes.date : null;
|
|
29
35
|
this.isComputed = false
|
|
30
|
-
this.lastComputedIndex = 0
|
|
31
36
|
this.input = input
|
|
32
|
-
this.
|
|
33
|
-
this.priceBased = ['open', 'high', 'low', 'close']
|
|
37
|
+
this.priceBased = new Set(['open', 'high', 'low', 'close'])
|
|
34
38
|
this.len = input.length
|
|
35
|
-
this.lastIndexReplace = false
|
|
36
39
|
this.instances = {}
|
|
37
40
|
this.crossPairsList = []
|
|
38
41
|
this.verticalOhlcv = {}
|
|
42
|
+
this.verticalOhlcvKeyNames = []
|
|
39
43
|
|
|
40
|
-
this.studies = {}
|
|
41
44
|
this.utilities = {
|
|
42
45
|
correlation
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
this.invalidValueIndex = -1
|
|
46
|
-
|
|
47
49
|
this.precision = precision
|
|
48
|
-
this.precisionMultiplier = (this.
|
|
49
|
-
|
|
50
|
-
this.autoMinMaxKeys = []
|
|
51
|
-
this.minMaxRanges = {}
|
|
52
|
-
|
|
50
|
+
this.precisionMultiplier = calcPrecisionMultiplier(this, this.firstRow)
|
|
51
|
+
this.ScaledGroups = {}
|
|
53
52
|
|
|
54
|
-
if(
|
|
53
|
+
if(inputParams !== null)
|
|
55
54
|
{
|
|
55
|
+
validateArray(inputParams, 'inputParams', 'constructor')
|
|
56
56
|
this.inputParams = inputParams
|
|
57
57
|
this.compute()
|
|
58
58
|
}
|
|
@@ -62,7 +62,6 @@ export default class OHLCV_INDICATORS {
|
|
|
62
62
|
|
|
63
63
|
this.pushToMain = ({index, key, value}) => pushToMain({main: this, index, key, value})
|
|
64
64
|
|
|
65
|
-
|
|
66
65
|
return this
|
|
67
66
|
}
|
|
68
67
|
|
|
@@ -82,13 +81,12 @@ export default class OHLCV_INDICATORS {
|
|
|
82
81
|
const shouldSlice = invalidValueIndex >= 0 && skipNull;
|
|
83
82
|
const sliceLength = len - (invalidValueIndex + 1);
|
|
84
83
|
|
|
85
|
-
|
|
86
84
|
for (const [key, arr] of Object.entries(verticalOhlcv)) {
|
|
87
85
|
// If slicing is needed, create a sliced copy, otherwise re-use the array.
|
|
88
86
|
let newArr = shouldSlice ? arr.slice(-sliceLength) : arr;
|
|
89
87
|
|
|
90
88
|
// If this key is price based and precision is enabled, map over the array.
|
|
91
|
-
if (precision && priceBased.
|
|
89
|
+
if (precision && priceBased.has(key)) {
|
|
92
90
|
newArr = newArr.map(v => (v == null ? null : v / precisionMultiplier));
|
|
93
91
|
}
|
|
94
92
|
result[key] = newArr;
|
|
@@ -103,9 +101,7 @@ export default class OHLCV_INDICATORS {
|
|
|
103
101
|
//getData method returns the last object (row) of the new OHLCV with indicators: {open, high, low, close, rsi_14, bollinger_bands_upper}
|
|
104
102
|
this.compute()
|
|
105
103
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return verticalToHorizontal(verticalOhlcv, skipNull, precision, precisionMultiplier, priceBased, invalidValueIndex)
|
|
104
|
+
return verticalToHorizontal(skipNull, this, 0)
|
|
109
105
|
}
|
|
110
106
|
|
|
111
107
|
|
|
@@ -113,165 +109,130 @@ export default class OHLCV_INDICATORS {
|
|
|
113
109
|
|
|
114
110
|
this.compute()
|
|
115
111
|
|
|
116
|
-
|
|
117
|
-
const keyNames = Object.keys(verticalOhlcv)
|
|
118
|
-
const row = {}
|
|
119
|
-
|
|
120
|
-
for(const key of keyNames)
|
|
121
|
-
{
|
|
122
|
-
row[key] = verticalOhlcv[key][len - 1]
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return (precision) ? divideByMultiplier({row, precisionMultiplier, priceBased}) : row
|
|
112
|
+
return verticalToHorizontal(false, this, this.len - 1)[0]
|
|
126
113
|
}
|
|
127
114
|
|
|
128
|
-
compute(
|
|
129
|
-
|
|
130
|
-
//if change is a valid object and change.date is after the last row in this.input[this.input.length - 1].date pushes change to this.input using the parseOhlcvToVertical function.
|
|
115
|
+
compute() {
|
|
131
116
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
{
|
|
136
|
-
return this
|
|
117
|
+
// If we've already computed, bail out immediately
|
|
118
|
+
if (this.isComputed) {
|
|
119
|
+
return this;
|
|
137
120
|
}
|
|
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
|
-
}
|
|
121
|
+
|
|
122
|
+
// Mark as “in progress”
|
|
123
|
+
this.isComputed = false;
|
|
124
|
+
|
|
125
|
+
// Figure out whether there’s a date field in the inputs
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
// Only run the full loop once (or when new data appears later,
|
|
129
|
+
// if you extend this to reset isComputed elsewhere)
|
|
130
|
+
if (this.len > 0) {
|
|
131
|
+
mainLoop(this.input, this);
|
|
132
|
+
this.isComputed = true;
|
|
188
133
|
|
|
134
|
+
//flushing after mainLoop
|
|
135
|
+
this.input = []
|
|
136
|
+
this.instances = {}
|
|
137
|
+
this.firstRow = []
|
|
189
138
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
139
|
+
|
|
193
140
|
return this;
|
|
194
|
-
}
|
|
141
|
+
}
|
|
195
142
|
|
|
196
143
|
|
|
197
144
|
|
|
198
|
-
crossPairs(arr)
|
|
145
|
+
crossPairs(arr = [])
|
|
199
146
|
{
|
|
147
|
+
|
|
148
|
+
const methodName = 'crossPairs'
|
|
149
|
+
|
|
200
150
|
isAlreadyComputed(this)
|
|
201
151
|
|
|
152
|
+
validateArray(arr, 'arr', methodName)
|
|
153
|
+
|
|
202
154
|
this.crossPairsList = [...this.crossPairsList, ...arr]
|
|
203
|
-
this.inputParams.push({key:
|
|
155
|
+
this.inputParams.push({key: methodName, params: [this.crossPairsList]})
|
|
204
156
|
|
|
205
157
|
return this
|
|
206
158
|
}
|
|
207
159
|
|
|
208
160
|
|
|
209
|
-
lag(colKeys = ['close'],
|
|
161
|
+
lag(colKeys = ['close'], lookback = 1) {
|
|
210
162
|
|
|
211
|
-
|
|
163
|
+
const methodName = 'lag'
|
|
212
164
|
|
|
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
|
-
}
|
|
165
|
+
isAlreadyComputed(this)
|
|
221
166
|
|
|
167
|
+
validateArray(colKeys, 'colKeys', methodName)
|
|
168
|
+
validateNumber(lookback, {min:1, max: this.len, allowDecimals: false}, 'lookback', methodName)
|
|
222
169
|
|
|
223
|
-
this.inputParams.push({key:
|
|
170
|
+
this.inputParams.push({key: methodName, params: [colKeys, lookback]})
|
|
224
171
|
|
|
225
|
-
for(
|
|
172
|
+
for(const key of colKeys)
|
|
226
173
|
{
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if(this.priceBased.find(v => key === v))
|
|
174
|
+
if(this.priceBased.has(key))
|
|
230
175
|
{
|
|
231
|
-
this.priceBased.
|
|
176
|
+
this.priceBased.add(key);
|
|
232
177
|
}
|
|
233
178
|
}
|
|
234
179
|
|
|
235
180
|
return this;
|
|
236
181
|
}
|
|
237
182
|
|
|
238
|
-
relativeVolume(size, options = {}) {
|
|
183
|
+
relativeVolume(size = 10, options = {}) {
|
|
239
184
|
|
|
240
|
-
|
|
185
|
+
const methodName = 'relativeVolume'
|
|
186
|
+
|
|
187
|
+
if(!this.inputTypes.hasOwnProperty('volume')) {
|
|
241
188
|
throw new Error('If "relativeVolume" is called the input ohlcv must contain valid volume properties.')
|
|
242
189
|
}
|
|
243
190
|
|
|
244
191
|
isAlreadyComputed(this)
|
|
245
192
|
|
|
246
|
-
|
|
193
|
+
validateNumber(size, {min:1, max: this.len, allowDecimals: false}, 'size', methodName)
|
|
194
|
+
validateObject(options, 'options', methodName)
|
|
247
195
|
|
|
248
|
-
|
|
196
|
+
const { lag = 0} = options;
|
|
249
197
|
|
|
250
|
-
|
|
251
|
-
}
|
|
198
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'lag', methodName)
|
|
252
199
|
|
|
253
|
-
this.inputParams.push({key:
|
|
200
|
+
this.inputParams.push({key: methodName, params: [size, {lag}]})
|
|
254
201
|
|
|
255
202
|
return this
|
|
256
203
|
}
|
|
257
204
|
|
|
258
|
-
ema(size, options = {}) {
|
|
205
|
+
ema(size = 5, options = {}) {
|
|
206
|
+
|
|
207
|
+
const methodName = 'ema'
|
|
259
208
|
|
|
260
209
|
isAlreadyComputed(this)
|
|
261
210
|
|
|
262
|
-
|
|
211
|
+
validateNumber(size, {min: 1, max: this.len, allowDecimals: false}, 'size', methodName)
|
|
212
|
+
validateObject(options, 'options', methodName)
|
|
213
|
+
|
|
214
|
+
const {target = 'close', lag = 0} = options
|
|
263
215
|
|
|
264
|
-
|
|
216
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'lag', methodName)
|
|
217
|
+
|
|
218
|
+
this.inputParams.push({key: methodName, params: [methodName, size, {target, lag}]})
|
|
265
219
|
|
|
266
220
|
return this
|
|
267
221
|
}
|
|
268
|
-
sma(size, options = {}) {
|
|
222
|
+
sma(size = 5, options = {}) {
|
|
223
|
+
|
|
224
|
+
const methodName = 'sma'
|
|
269
225
|
|
|
270
226
|
isAlreadyComputed(this)
|
|
271
227
|
|
|
272
|
-
|
|
228
|
+
validateNumber(size, {min: 1, max: this.len, allowDecimals: false}, 'size', methodName)
|
|
229
|
+
validateObject(options, 'options', methodName)
|
|
273
230
|
|
|
274
|
-
|
|
231
|
+
const {target = 'close', lag = 0} = options
|
|
232
|
+
|
|
233
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'lag', methodName)
|
|
234
|
+
|
|
235
|
+
this.inputParams.push({key: methodName, params: [methodName, size, {target, lag}]})
|
|
275
236
|
|
|
276
237
|
return this
|
|
277
238
|
}
|
|
@@ -279,223 +240,140 @@ export default class OHLCV_INDICATORS {
|
|
|
279
240
|
|
|
280
241
|
macd(fast = 12, slow = 26, signal = 9, options = {}) {
|
|
281
242
|
|
|
282
|
-
|
|
243
|
+
const methodName = 'macd'
|
|
283
244
|
|
|
284
|
-
|
|
285
|
-
throw new Error('"fast" must be a positive number in macd.');
|
|
286
|
-
}
|
|
287
|
-
if (typeof slow !== 'number' || slow <= fast) {
|
|
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
|
-
}
|
|
245
|
+
isAlreadyComputed(this)
|
|
293
246
|
|
|
294
|
-
|
|
295
|
-
{
|
|
296
|
-
|
|
297
|
-
|
|
247
|
+
validateNumber(fast, {min: 1, max: this.len, allowDecimals: false}, 'fast', methodName)
|
|
248
|
+
validateNumber(slow, {min: 1, max: this.len, allowDecimals: false}, 'slow', methodName)
|
|
249
|
+
validateNumber(signal, {min: 1, max: this.len, allowDecimals: false}, 'signal', methodName)
|
|
250
|
+
validateObject(options, 'options', methodName)
|
|
298
251
|
|
|
299
|
-
const {target = 'close'} = options
|
|
252
|
+
const {target = 'close', lag = 0} = options
|
|
253
|
+
|
|
254
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'lag', methodName)
|
|
300
255
|
|
|
301
|
-
this.inputParams.push({key:
|
|
256
|
+
this.inputParams.push({key: methodName, params: [fast, slow, signal, {target, lag}]})
|
|
302
257
|
|
|
303
258
|
return this
|
|
304
259
|
|
|
305
260
|
}
|
|
306
261
|
bollingerBands(size = 20, stdDev = 2, options = {}) {
|
|
307
262
|
|
|
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
|
-
}
|
|
263
|
+
const methodName = 'bollingerBands'
|
|
314
264
|
|
|
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
|
-
}
|
|
265
|
+
isAlreadyComputed(this)
|
|
266
|
+
|
|
267
|
+
validateNumber(size, {min:1, max: this.len, allowDecimals: false}, 'size', methodName)
|
|
268
|
+
validateNumber(stdDev, {min: 0.01, max: 50, allowDecimals: true}, 'stdDev', methodName)
|
|
269
|
+
validateObject(options, 'options', methodName)
|
|
331
270
|
|
|
332
|
-
|
|
271
|
+
const {target = 'close', height = false, range = [], lag = 0} = options
|
|
333
272
|
|
|
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
273
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
274
|
+
validateArray(range, 'range', methodName)
|
|
275
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'lag', methodName)
|
|
276
|
+
validateBoolean(height, 'height', methodName)
|
|
347
277
|
|
|
348
|
-
this.inputParams.push({key:
|
|
278
|
+
this.inputParams.push({key: methodName, params: [size, stdDev, {target, height, range, lag}]});
|
|
349
279
|
|
|
350
280
|
return this;
|
|
351
281
|
}
|
|
352
282
|
|
|
353
|
-
rsi(size, options = {})
|
|
283
|
+
rsi(size = 14, options = {})
|
|
354
284
|
{
|
|
355
|
-
|
|
285
|
+
const methodName = 'rsi'
|
|
356
286
|
|
|
357
|
-
|
|
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
|
-
}
|
|
368
|
-
|
|
369
|
-
if (typeof lag !== 'number') {
|
|
370
|
-
|
|
371
|
-
throw new Error(`"lag" value in rsi must be a number in rsi.`);
|
|
372
|
-
}
|
|
287
|
+
isAlreadyComputed(this)
|
|
373
288
|
|
|
374
|
-
|
|
289
|
+
validateNumber(size, {min: 1, max: this.len, allowDecimals: false}, 'size', methodName)
|
|
290
|
+
validateObject(options, 'options', methodName)
|
|
291
|
+
|
|
292
|
+
const {target = 'close', lag = 0} = options
|
|
375
293
|
|
|
376
|
-
|
|
377
|
-
}
|
|
294
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'lag', methodName)
|
|
378
295
|
|
|
379
|
-
this.inputParams.push({key:
|
|
296
|
+
this.inputParams.push({key: methodName, params: [size, {target, lag}]})
|
|
380
297
|
|
|
381
298
|
return this
|
|
382
299
|
}
|
|
383
300
|
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
301
|
|
|
400
|
-
|
|
401
|
-
throw new Error(`"lag" value in rsi must be a number in donchianChannels.`);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
if (typeof autoMinMax !== 'boolean') {
|
|
302
|
+
const methodName = 'donchianChannels'
|
|
405
303
|
|
|
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)) {
|
|
304
|
+
isAlreadyComputed(this)
|
|
416
305
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
}
|
|
306
|
+
|
|
307
|
+
validateNumber(size, {min: 1, max: this.len, allowDecimals: false}, 'size', methodName)
|
|
308
|
+
validateNumber(offset, {min: 0, max: this.len, allowDecimals: false}, 'offset', methodName)
|
|
420
309
|
|
|
421
|
-
|
|
310
|
+
validateObject(options, 'options', methodName)
|
|
311
|
+
const { height = false, range = [], lag = 0} = options;
|
|
422
312
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
candleVectors(size = 200, options = {}) {
|
|
428
|
-
isAlreadyComputed(this);
|
|
313
|
+
validateArray(range, 'range', methodName)
|
|
314
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'lag', methodName)
|
|
315
|
+
validateBoolean(height, 'height', methodName)
|
|
429
316
|
|
|
430
|
-
|
|
431
|
-
throw new Error('"size" must be a positive number greater than 0 in candleVectors.');
|
|
432
|
-
}
|
|
317
|
+
this.inputParams.push({ key: methodName, params: [size, offset, { height, range, lag }] });
|
|
433
318
|
|
|
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
|
-
}
|
|
454
|
-
|
|
455
|
-
this.inputParams.push({ key: 'candleVectors', params: [size, {stdDev, patternSize, lag, scale, autoMinMax}] });
|
|
456
319
|
return this;
|
|
457
|
-
|
|
320
|
+
}
|
|
458
321
|
|
|
459
322
|
|
|
460
323
|
volumeOscillator(fastSize = 5, slowSize = 10, options = {})
|
|
461
324
|
{
|
|
325
|
+
const methodName = 'volumeOscillator'
|
|
462
326
|
|
|
463
|
-
if(this.
|
|
327
|
+
if(!this.inputTypes.hasOwnProperty('volume')) {
|
|
464
328
|
throw new Error('If "volumeOscillator" is called the input ohlcv must contain valid volume properties.')
|
|
465
329
|
}
|
|
466
330
|
|
|
467
331
|
isAlreadyComputed(this)
|
|
468
332
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
333
|
+
validateNumber(fastSize, {min: 1, max: this.len, allowDecimals: false}, 'fastSize', methodName)
|
|
334
|
+
validateNumber(slowSize, {min: fastSize, max: this.len, allowDecimals: false}, 'slowSize', methodName)
|
|
335
|
+
validateObject(options, 'options', methodName)
|
|
472
336
|
|
|
473
|
-
|
|
474
|
-
throw new Error('"slowSize" must be a positive number greater than the "fastSize" in volumeOscillator.');
|
|
475
|
-
}
|
|
337
|
+
const {lag = 0} = options
|
|
476
338
|
|
|
477
|
-
|
|
339
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'lag', methodName)
|
|
478
340
|
|
|
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}]})
|
|
341
|
+
this.inputParams.push({key: methodName, params: [fastSize, slowSize, {lag}]})
|
|
485
342
|
return this
|
|
486
343
|
}
|
|
487
|
-
dateTime()
|
|
344
|
+
dateTime(options = {})
|
|
488
345
|
{
|
|
346
|
+
|
|
347
|
+
const methodName = 'dateTime'
|
|
348
|
+
|
|
489
349
|
isAlreadyComputed(this)
|
|
490
350
|
|
|
491
|
-
|
|
351
|
+
validateObject(options, 'options', methodName)
|
|
352
|
+
|
|
353
|
+
const {lag = 0} = options
|
|
354
|
+
|
|
355
|
+
validateNumber(lag, {min: 0, max: this.len, allowDecimals: false}, 'lag', methodName)
|
|
356
|
+
|
|
357
|
+
this.inputParams.push({key: methodName, params: [{lag}]})
|
|
492
358
|
return this
|
|
493
359
|
}
|
|
494
|
-
|
|
360
|
+
|
|
361
|
+
Scaler(size, colKeys, options)
|
|
495
362
|
{
|
|
363
|
+
const methodName = 'Scaler'
|
|
364
|
+
|
|
496
365
|
isAlreadyComputed(this)
|
|
497
366
|
|
|
498
|
-
|
|
499
|
-
|
|
367
|
+
validateNumber(size, {min: 1, max: this.len, allowDecimals: false}, 'size', methodName)
|
|
368
|
+
validateArray(colKeys, 'colKeys', methodName)
|
|
369
|
+
|
|
370
|
+
const {group = false, range = [0, 1], lag = 0, type = 'minmax'} = options
|
|
371
|
+
|
|
372
|
+
validateBoolean(group, 'group', methodName)
|
|
373
|
+
validateArray(range, 'range', methodName)
|
|
374
|
+
validateArrayOptions(['minmax', 'zscore'], type, 'type', methodName)
|
|
375
|
+
|
|
376
|
+
this.inputParams.push({key: methodName, params: [size, colKeys, type, group, range, lag]})
|
|
377
|
+
return this
|
|
500
378
|
}
|
|
501
379
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ohlcv-indicators",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.5",
|
|
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
|
},
|