ohlcv-indicators 1.9.3 → 1.9.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 +1 -1
- package/index.js +6 -12
- package/package.json +3 -2
- package/src/moving-averages/bollingerBands.js +35 -23
- package/src/moving-averages/donchianChannel.js +13 -12
- package/src/moving-averages/ema.js +1 -1
- package/src/moving-averages/macd.js +13 -17
- package/src/moving-averages/relativeVolume.js +7 -4
- package/src/moving-averages/sma.js +2 -2
- package/src/oscillators/rsi.js +12 -6
- package/src/oscillators/volumeOscillator.js +10 -6
- package/src/studies/candleStudies.js +46 -61
- package/src/studies/findCrosses.js +125 -150
- package/src/studies/lag.js +26 -19
- package/src/utilities/parsing-utilities.js +103 -60
|
@@ -8,6 +8,7 @@ import { bollingerBands } from "../moving-averages/bollingerBands.js"
|
|
|
8
8
|
import { volumeOscillator } from "../oscillators/volumeOscillator.js"
|
|
9
9
|
import { candlesStudies } from "../studies/candleStudies.js"
|
|
10
10
|
import { lag } from "../studies/lag.js"
|
|
11
|
+
import {crossPairs} from "../studies/findCrosses.js"
|
|
11
12
|
|
|
12
13
|
const indicatorFunctions = {
|
|
13
14
|
rsi,
|
|
@@ -19,19 +20,19 @@ const indicatorFunctions = {
|
|
|
19
20
|
bollingerBands,
|
|
20
21
|
volumeOscillator,
|
|
21
22
|
candlesStudies,
|
|
22
|
-
lag
|
|
23
|
+
lag,
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
const validateFirstDate = (arr) => arr[0].hasOwnProperty('date') && typeof arr[0].date === 'string' && /^\d{4}-\d{2}-\d{2}/.test(arr[0].date)
|
|
26
27
|
|
|
27
28
|
export const defaultStudyOptions = {
|
|
28
|
-
midPriceOpenClose:
|
|
29
|
-
midPriceHighLow:
|
|
30
|
-
sessionIndex:
|
|
31
|
-
sessionIntradayIndex:
|
|
32
|
-
dayOfTheWeek:
|
|
33
|
-
dayOfTheMonth:
|
|
34
|
-
weekOfTheMonth:
|
|
29
|
+
midPriceOpenClose: true,
|
|
30
|
+
midPriceHighLow: true,
|
|
31
|
+
sessionIndex: true,
|
|
32
|
+
sessionIntradayIndex: true,
|
|
33
|
+
dayOfTheWeek: true,
|
|
34
|
+
dayOfTheMonth: true,
|
|
35
|
+
weekOfTheMonth: true
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
const getDateInfo = (dateString) => {
|
|
@@ -49,8 +50,8 @@ const getDateInfo = (dateString) => {
|
|
|
49
50
|
};
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
export const parseOhlcvToVertical = (input, main) => {
|
|
53
|
-
const { len, studyOptions, inputParams } = main
|
|
53
|
+
export const parseOhlcvToVertical = (input, main, startIndex = 0) => {
|
|
54
|
+
const { len, studyOptions = {}, inputParams } = main
|
|
54
55
|
|
|
55
56
|
if (studyOptions) {
|
|
56
57
|
const studyOptionKeys = Object.keys(studyOptions)
|
|
@@ -79,59 +80,83 @@ export const parseOhlcvToVertical = (input, main) => {
|
|
|
79
80
|
const numberColsKeysSet = new Set(numberColsKeys)
|
|
80
81
|
const vert = main.verticalOhlcv
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
let isValidDate = false
|
|
84
|
+
if (startIndex === 0) {
|
|
85
|
+
// Fresh initialization
|
|
86
|
+
// Initialize arrays for numerical columns
|
|
87
|
+
for (let i = 0; i < numberColsKeys.length; i++) {
|
|
88
|
+
vert[numberColsKeys[i]] = new Array(len)
|
|
89
|
+
}
|
|
86
90
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
// Extract other keys and initialize their arrays
|
|
92
|
+
const inputKeys = Object.keys(input[0])
|
|
93
|
+
const otherKeys = []
|
|
94
|
+
for (let i = 0; i < inputKeys.length; i++) {
|
|
95
|
+
const key = inputKeys[i]
|
|
96
|
+
if (!numberColsKeysSet.has(key)) {
|
|
97
|
+
vert[key] = new Array(len)
|
|
98
|
+
otherKeys.push(key)
|
|
99
|
+
}
|
|
95
100
|
}
|
|
96
|
-
|
|
101
|
+
main.otherKeys = otherKeys
|
|
97
102
|
|
|
98
|
-
|
|
99
|
-
let prevDateStr
|
|
100
|
-
let sessionIndexCount = 0
|
|
101
|
-
let sessionIntradayIndexCount = 0
|
|
103
|
+
isValidDate = validateFirstDate(input)
|
|
102
104
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
105
|
+
if (isValidDate) {
|
|
106
|
+
if (sessionIndex) {
|
|
107
|
+
vert['session_index'] = new Array(len)
|
|
108
|
+
}
|
|
109
|
+
if (sessionIntradayIndex) {
|
|
110
|
+
vert['session_intraday_index'] = new Array(len)
|
|
111
|
+
}
|
|
112
|
+
if (dayOfTheWeek) {
|
|
113
|
+
vert['day_of_the_week'] = new Array(len)
|
|
114
|
+
}
|
|
115
|
+
if (dayOfTheMonth) {
|
|
116
|
+
vert['day_of_the_month'] = new Array(len)
|
|
117
|
+
}
|
|
118
|
+
if (weekOfTheMonth) {
|
|
119
|
+
vert['week_of_the_month'] = new Array(len)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
main.prevDateStr = input[0].date.slice(0, 10)
|
|
123
|
+
main.sessionIndexCount = 0
|
|
124
|
+
main.sessionIntradayIndexCount = 0
|
|
125
|
+
main.cachedDayInfo = getDateInfo(main.prevDateStr)
|
|
126
|
+
} else {
|
|
127
|
+
// If no valid date, ensure these are null
|
|
128
|
+
main.prevDateStr = null
|
|
129
|
+
main.sessionIndexCount = null
|
|
130
|
+
main.sessionIntradayIndexCount = null
|
|
131
|
+
main.cachedDayInfo = null
|
|
113
132
|
}
|
|
114
|
-
|
|
115
|
-
|
|
133
|
+
} else {
|
|
134
|
+
// startIndex > 0
|
|
135
|
+
// We assume arrays and keys are already set
|
|
136
|
+
if (!main.otherKeys) {
|
|
137
|
+
throw new Error("otherKeys not found in main. Ensure you run with startIndex=0 first to initialize.")
|
|
116
138
|
}
|
|
117
|
-
|
|
118
|
-
|
|
139
|
+
|
|
140
|
+
// Validate that state variables needed are present
|
|
141
|
+
if (!main.hasOwnProperty('prevDateStr')) {
|
|
142
|
+
throw new Error("prevDateStr missing in main. Make sure to store state after initial run.")
|
|
119
143
|
}
|
|
120
144
|
|
|
121
|
-
|
|
145
|
+
// If we had a valid date initially, we continue with date computations
|
|
146
|
+
isValidDate = main.prevDateStr !== null
|
|
122
147
|
}
|
|
123
148
|
|
|
124
149
|
// Pre-process indicator params
|
|
125
150
|
const indicatorCalls = []
|
|
126
151
|
if (inputParams && typeof inputParams === 'object') {
|
|
127
152
|
for (const key in inputParams) {
|
|
128
|
-
if (key === 'crossPairs') continue
|
|
129
153
|
const technical = inputParams[key]
|
|
130
154
|
if (Array.isArray(technical)) {
|
|
131
155
|
for (let i = 0; i < technical.length; i++) {
|
|
132
156
|
const params = technical[i];
|
|
133
157
|
if (Array.isArray(params)) {
|
|
134
158
|
indicatorCalls.push({
|
|
159
|
+
key,
|
|
135
160
|
fn: indicatorFunctions[key],
|
|
136
161
|
args: params
|
|
137
162
|
})
|
|
@@ -152,14 +177,19 @@ export const parseOhlcvToVertical = (input, main) => {
|
|
|
152
177
|
|
|
153
178
|
// References for other keys arrays
|
|
154
179
|
const otherArrays = {}
|
|
155
|
-
for (let i = 0; i < otherKeys.length; i++) {
|
|
156
|
-
const k = otherKeys[i]
|
|
180
|
+
for (let i = 0; i < main.otherKeys.length; i++) {
|
|
181
|
+
const k = main.otherKeys[i]
|
|
157
182
|
otherArrays[k] = vert[k]
|
|
158
183
|
}
|
|
159
184
|
|
|
160
|
-
//
|
|
161
|
-
let
|
|
162
|
-
|
|
185
|
+
// For date/session logic, use previously stored state if startIndex > 0
|
|
186
|
+
let prevDateStr = main.prevDateStr
|
|
187
|
+
let sessionIndexCount = main.sessionIndexCount
|
|
188
|
+
let sessionIntradayIndexCount = main.sessionIntradayIndexCount
|
|
189
|
+
let cachedDayInfo = main.cachedDayInfo
|
|
190
|
+
|
|
191
|
+
// Compute indicators and fill data from startIndex to len
|
|
192
|
+
for (let x = startIndex; x < len; x++) {
|
|
163
193
|
const current = input[x]
|
|
164
194
|
|
|
165
195
|
// Assign OHLCV
|
|
@@ -169,11 +199,7 @@ export const parseOhlcvToVertical = (input, main) => {
|
|
|
169
199
|
vo_close[x] = current.close
|
|
170
200
|
vo_volume[x] = current.volume
|
|
171
201
|
|
|
172
|
-
|
|
173
|
-
for (let i = 0; i < indicatorCalls.length; i++) {
|
|
174
|
-
const { fn, args } = indicatorCalls[i]
|
|
175
|
-
fn(main, x, ...args)
|
|
176
|
-
}
|
|
202
|
+
|
|
177
203
|
|
|
178
204
|
// Mid-price calculations
|
|
179
205
|
if (vo_mid_oc) {
|
|
@@ -184,8 +210,8 @@ export const parseOhlcvToVertical = (input, main) => {
|
|
|
184
210
|
}
|
|
185
211
|
|
|
186
212
|
// Other keys
|
|
187
|
-
for (let i = 0; i < otherKeys.length; i++) {
|
|
188
|
-
const k = otherKeys[i]
|
|
213
|
+
for (let i = 0; i < main.otherKeys.length; i++) {
|
|
214
|
+
const k = main.otherKeys[i]
|
|
189
215
|
otherArrays[k][x] = current[k]
|
|
190
216
|
}
|
|
191
217
|
|
|
@@ -200,26 +226,43 @@ export const parseOhlcvToVertical = (input, main) => {
|
|
|
200
226
|
}
|
|
201
227
|
|
|
202
228
|
if (dayOfTheWeek) {
|
|
203
|
-
|
|
229
|
+
vert.day_of_the_week[x] = cachedDayInfo.day_of_the_week
|
|
204
230
|
}
|
|
205
231
|
|
|
206
232
|
if (dayOfTheMonth) {
|
|
207
|
-
|
|
233
|
+
vert.day_of_the_month[x] = cachedDayInfo.day_of_the_month
|
|
208
234
|
}
|
|
209
235
|
|
|
210
236
|
if (weekOfTheMonth) {
|
|
211
|
-
|
|
237
|
+
vert.week_of_the_month[x] = cachedDayInfo.week_of_the_month
|
|
212
238
|
}
|
|
213
239
|
|
|
214
240
|
if (sessionIndex) {
|
|
215
|
-
|
|
241
|
+
vert.session_index[x] = sessionIndexCount
|
|
216
242
|
}
|
|
217
243
|
|
|
218
244
|
if (sessionIntradayIndex) {
|
|
219
|
-
|
|
245
|
+
vert.session_intraday_index[x] = sessionIntradayIndexCount
|
|
220
246
|
}
|
|
221
247
|
|
|
222
248
|
sessionIntradayIndexCount++
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
// Run indicator functions
|
|
252
|
+
for (let i = 0; i < indicatorCalls.length; i++) {
|
|
253
|
+
const { fn, args, key } = indicatorCalls[i]
|
|
254
|
+
if(key === 'crossPairs' || key === 'lag') continue
|
|
255
|
+
fn(main, x, ...args)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
lag(main, x, inputParams.lag)
|
|
259
|
+
crossPairs(main, x, main.crossPairsList)
|
|
223
260
|
}
|
|
224
261
|
}
|
|
262
|
+
|
|
263
|
+
// Store current state so that we can continue from here in subsequent calls
|
|
264
|
+
main.prevDateStr = prevDateStr
|
|
265
|
+
main.sessionIndexCount = sessionIndexCount
|
|
266
|
+
main.sessionIntradayIndexCount = sessionIntradayIndexCount
|
|
267
|
+
main.cachedDayInfo = cachedDayInfo
|
|
225
268
|
}
|