ohlcv-indicators 1.9.7 → 1.9.9
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/package.json +1 -1
- package/src/utilities/parsing-utilities.js +110 -149
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ohlcv-indicators",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.9",
|
|
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",
|
|
@@ -26,13 +26,13 @@ const indicatorFunctions = {
|
|
|
26
26
|
const validateFirstDate = arr => arr[0].hasOwnProperty('date') && typeof arr[0].date === 'string' && /^\d{4}-\d{2}-\d{2}/.test(arr[0].date)
|
|
27
27
|
|
|
28
28
|
export const defaultStudyOptions = {
|
|
29
|
-
midPriceOpenClose:
|
|
30
|
-
midPriceHighLow:
|
|
31
|
-
sessionIndex:
|
|
32
|
-
sessionIntradayIndex:
|
|
33
|
-
dayOfTheWeek:
|
|
34
|
-
dayOfTheMonth:
|
|
35
|
-
weekOfTheMonth:
|
|
29
|
+
midPriceOpenClose: false,
|
|
30
|
+
midPriceHighLow: false,
|
|
31
|
+
sessionIndex: false,
|
|
32
|
+
sessionIntradayIndex: false,
|
|
33
|
+
dayOfTheWeek: false,
|
|
34
|
+
dayOfTheMonth: false,
|
|
35
|
+
weekOfTheMonth: false
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
const getDateInfo = (dateString) => {
|
|
@@ -51,17 +51,14 @@ const getDateInfo = (dateString) => {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export const parseOhlcvToVertical = (input, main, startIndex = 0) => {
|
|
54
|
-
const { len, studyOptions = {}, inputParams } = main
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
const { len, studyOptions = {}, inputParams } = main;
|
|
58
55
|
|
|
59
56
|
if (studyOptions) {
|
|
60
|
-
const studyOptionKeys = Object.keys(studyOptions)
|
|
57
|
+
const studyOptionKeys = Object.keys(studyOptions);
|
|
61
58
|
for (let i = 0; i < studyOptionKeys.length; i++) {
|
|
62
|
-
const k = studyOptionKeys[i]
|
|
59
|
+
const k = studyOptionKeys[i];
|
|
63
60
|
if (!defaultStudyOptions.hasOwnProperty(k)) {
|
|
64
|
-
throw new Error(`Key ${k} not found in defaultStudyOptions. Available options: ${Object.keys(defaultStudyOptions).join(', ')}`)
|
|
61
|
+
throw new Error(`Key ${k} not found in defaultStudyOptions. Available options: ${Object.keys(defaultStudyOptions).join(', ')}`);
|
|
65
62
|
}
|
|
66
63
|
}
|
|
67
64
|
}
|
|
@@ -74,200 +71,164 @@ export const parseOhlcvToVertical = (input, main, startIndex = 0) => {
|
|
|
74
71
|
dayOfTheWeek,
|
|
75
72
|
dayOfTheMonth,
|
|
76
73
|
weekOfTheMonth
|
|
77
|
-
} = studyOptions
|
|
74
|
+
} = studyOptions;
|
|
78
75
|
|
|
79
|
-
const numberColsKeys = ['open', 'high', 'low', 'close', 'volume']
|
|
80
|
-
if (midPriceOpenClose) numberColsKeys.push('mid_price_open_close')
|
|
81
|
-
if (midPriceHighLow) numberColsKeys.push('mid_price_high_low')
|
|
76
|
+
const numberColsKeys = ['open', 'high', 'low', 'close', 'volume'];
|
|
77
|
+
if (midPriceOpenClose) numberColsKeys.push('mid_price_open_close');
|
|
78
|
+
if (midPriceHighLow) numberColsKeys.push('mid_price_high_low');
|
|
82
79
|
|
|
83
|
-
const numberColsKeysSet = new Set(numberColsKeys)
|
|
84
|
-
const vert = main.verticalOhlcv
|
|
80
|
+
const numberColsKeysSet = new Set(numberColsKeys);
|
|
85
81
|
|
|
86
|
-
let isValidDate = false
|
|
82
|
+
let isValidDate = false;
|
|
87
83
|
if (startIndex === 0) {
|
|
88
84
|
// Fresh initialization
|
|
89
|
-
// Initialize arrays for numerical columns
|
|
90
85
|
for (let i = 0; i < numberColsKeys.length; i++) {
|
|
91
|
-
|
|
86
|
+
main.verticalOhlcv[numberColsKeys[i]] = new Array(len);
|
|
92
87
|
}
|
|
93
88
|
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
const otherKeys = []
|
|
89
|
+
const inputKeys = Object.keys(input[0]);
|
|
90
|
+
const otherKeys = [];
|
|
97
91
|
for (let i = 0; i < inputKeys.length; i++) {
|
|
98
|
-
const key = inputKeys[i]
|
|
92
|
+
const key = inputKeys[i];
|
|
99
93
|
if (!numberColsKeysSet.has(key)) {
|
|
100
|
-
|
|
101
|
-
otherKeys.push(key)
|
|
94
|
+
main.verticalOhlcv[key] = new Array(len);
|
|
95
|
+
otherKeys.push(key);
|
|
102
96
|
}
|
|
103
97
|
}
|
|
104
|
-
main.otherKeys = otherKeys
|
|
98
|
+
main.otherKeys = otherKeys;
|
|
105
99
|
|
|
106
|
-
isValidDate = validateFirstDate(input)
|
|
100
|
+
isValidDate = validateFirstDate(input);
|
|
107
101
|
|
|
108
102
|
if (isValidDate) {
|
|
109
|
-
if (sessionIndex) {
|
|
110
|
-
vert['session_index'] = new Array(len)
|
|
111
|
-
}
|
|
112
|
-
if (sessionIntradayIndex) {
|
|
113
|
-
vert['session_intraday_index'] = new Array(len)
|
|
114
|
-
}
|
|
115
|
-
if (dayOfTheWeek) {
|
|
116
|
-
vert['day_of_the_week'] = new Array(len)
|
|
117
|
-
}
|
|
118
|
-
if (dayOfTheMonth) {
|
|
119
|
-
vert['day_of_the_month'] = new Array(len)
|
|
120
|
-
}
|
|
121
|
-
if (weekOfTheMonth) {
|
|
122
|
-
vert['week_of_the_month'] = new Array(len)
|
|
123
|
-
}
|
|
124
103
|
|
|
125
|
-
|
|
126
|
-
main.
|
|
127
|
-
main.
|
|
128
|
-
main.
|
|
104
|
+
|
|
105
|
+
if(sessionIndex) main.verticalOhlcv.session_index = new Array(len)
|
|
106
|
+
if(sessionIntradayIndex) main.verticalOhlcv.session_intraday_index = new Array(len)
|
|
107
|
+
if(dayOfTheWeek) main.verticalOhlcv.day_of_the_week = new Array(len)
|
|
108
|
+
if(dayOfTheMonth) main.verticalOhlcv.day_of_the_month = new Array(len)
|
|
109
|
+
if(weekOfTheMonth) main.verticalOhlcv.week_of_the_month = new Array(len)
|
|
110
|
+
|
|
111
|
+
Object.assign(main, {
|
|
112
|
+
prevDateStr: input[0].date.slice(0, 10),
|
|
113
|
+
sessionIndexCount: 0,
|
|
114
|
+
sessionIntradayIndexCount: 0,
|
|
115
|
+
cachedDayInfo: getDateInfo(main.prevDateStr)
|
|
116
|
+
})
|
|
117
|
+
|
|
129
118
|
} else {
|
|
130
|
-
|
|
131
|
-
main
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
119
|
+
|
|
120
|
+
Object.assign(main, {
|
|
121
|
+
prevDateStr: null,
|
|
122
|
+
sessionIndexCount: null,
|
|
123
|
+
sessionIntradayIndexCount: null,
|
|
124
|
+
cachedDayInfo: null,
|
|
125
|
+
})
|
|
135
126
|
}
|
|
136
127
|
} else {
|
|
137
|
-
// startIndex > 0
|
|
138
|
-
// We assume arrays and keys are already set
|
|
139
128
|
if (!main.otherKeys) {
|
|
140
|
-
throw new Error("otherKeys not found in main. Ensure you run with startIndex=0 first to initialize.")
|
|
129
|
+
throw new Error("otherKeys not found in main. Ensure you run with startIndex=0 first to initialize.");
|
|
141
130
|
}
|
|
142
131
|
|
|
143
|
-
// Validate that state variables needed are present
|
|
144
132
|
if (!main.hasOwnProperty('prevDateStr')) {
|
|
145
|
-
throw new Error("prevDateStr missing in main. Make sure to store state after initial run.")
|
|
133
|
+
throw new Error("prevDateStr missing in main. Make sure to store state after initial run.");
|
|
146
134
|
}
|
|
147
135
|
|
|
148
|
-
|
|
149
|
-
isValidDate = main.prevDateStr !== null
|
|
136
|
+
isValidDate = main.prevDateStr !== null;
|
|
150
137
|
}
|
|
151
138
|
|
|
152
|
-
|
|
153
|
-
const indicatorCalls = []
|
|
154
|
-
if (inputParams && typeof inputParams === 'object') {
|
|
155
|
-
for (const key in inputParams) {
|
|
156
|
-
const technical = inputParams[key]
|
|
157
|
-
if (Array.isArray(technical)) {
|
|
158
|
-
for (let i = 0; i < technical.length; i++) {
|
|
159
|
-
const params = technical[i];
|
|
160
|
-
if (Array.isArray(params)) {
|
|
161
|
-
indicatorCalls.push({
|
|
162
|
-
key,
|
|
163
|
-
fn: indicatorFunctions[key],
|
|
164
|
-
args: params
|
|
165
|
-
})
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Store references for numeric arrays to avoid repeated dictionary lookups
|
|
173
|
-
const vo_open = vert.open
|
|
174
|
-
const vo_high = vert.high
|
|
175
|
-
const vo_low = vert.low
|
|
176
|
-
const vo_close = vert.close
|
|
177
|
-
const vo_volume = vert.volume
|
|
178
|
-
const vo_mid_oc = midPriceOpenClose ? vert.mid_price_open_close : null
|
|
179
|
-
const vo_mid_hl = midPriceHighLow ? vert.mid_price_high_low : null
|
|
180
|
-
|
|
181
|
-
// References for other keys arrays
|
|
182
|
-
const otherArrays = {}
|
|
183
|
-
for (let i = 0; i < main.otherKeys.length; i++) {
|
|
184
|
-
const k = main.otherKeys[i]
|
|
185
|
-
otherArrays[k] = vert[k]
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// For date/session logic, use previously stored state if startIndex > 0
|
|
189
|
-
let prevDateStr = main.prevDateStr
|
|
190
|
-
let sessionIndexCount = main.sessionIndexCount
|
|
191
|
-
let sessionIntradayIndexCount = main.sessionIntradayIndexCount
|
|
192
|
-
let cachedDayInfo = main.cachedDayInfo
|
|
139
|
+
const indicatorCalls = processIndicatorCalls(inputParams);
|
|
193
140
|
|
|
141
|
+
let {prevDateStr, sessionIndexCount, sessionIntradayIndexCount, cachedDayInfo} = main
|
|
194
142
|
|
|
143
|
+
for (let x = startIndex; x < len; x++) {
|
|
144
|
+
const current = input[x];
|
|
195
145
|
|
|
146
|
+
main.verticalOhlcv.open[x] = current.open;
|
|
147
|
+
main.verticalOhlcv.high[x] = current.high;
|
|
148
|
+
main.verticalOhlcv.low[x] = current.low;
|
|
149
|
+
main.verticalOhlcv.close[x] = current.close;
|
|
150
|
+
main.verticalOhlcv.volume[x] = current.volume;
|
|
196
151
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const current = input[x]
|
|
200
|
-
|
|
201
|
-
// Assign OHLCV
|
|
202
|
-
vo_open[x] = current.open
|
|
203
|
-
vo_high[x] = current.high
|
|
204
|
-
vo_low[x] = current.low
|
|
205
|
-
vo_close[x] = current.close
|
|
206
|
-
vo_volume[x] = current.volume
|
|
207
|
-
|
|
208
|
-
// Mid-price calculations
|
|
209
|
-
if (vo_mid_oc) {
|
|
210
|
-
vo_mid_oc[x] = (current.open + current.close) / 2
|
|
152
|
+
if (midPriceOpenClose) {
|
|
153
|
+
main.verticalOhlcv.mid_price_open_close[x] = (current.open + current.close) / 2;
|
|
211
154
|
}
|
|
212
|
-
if (
|
|
213
|
-
|
|
155
|
+
if (midPriceHighLow) {
|
|
156
|
+
main.verticalOhlcv.mid_price_high_low[x] = (current.high + current.low) / 2;
|
|
214
157
|
}
|
|
215
158
|
|
|
216
|
-
// Other keys
|
|
217
159
|
for (let i = 0; i < main.otherKeys.length; i++) {
|
|
218
|
-
const k = main.otherKeys[i]
|
|
219
|
-
|
|
160
|
+
const k = main.otherKeys[i];
|
|
161
|
+
main.verticalOhlcv[k][x] = current[k];
|
|
220
162
|
}
|
|
221
163
|
|
|
222
|
-
// Date processing for session indices and date fields
|
|
223
164
|
if (isValidDate) {
|
|
224
|
-
const thisDateStr = current.date.slice(0, 10)
|
|
165
|
+
const thisDateStr = current.date.slice(0, 10);
|
|
225
166
|
if (thisDateStr !== prevDateStr) {
|
|
226
|
-
prevDateStr = thisDateStr
|
|
227
|
-
sessionIndexCount
|
|
228
|
-
sessionIntradayIndexCount = 0
|
|
229
|
-
cachedDayInfo = getDateInfo(thisDateStr)
|
|
167
|
+
prevDateStr = thisDateStr;
|
|
168
|
+
sessionIndexCount++;
|
|
169
|
+
sessionIntradayIndexCount = 0;
|
|
170
|
+
cachedDayInfo = getDateInfo(thisDateStr);
|
|
230
171
|
}
|
|
231
172
|
|
|
232
173
|
if (dayOfTheWeek) {
|
|
233
|
-
|
|
174
|
+
main.verticalOhlcv.day_of_the_week[x] = cachedDayInfo.day_of_the_week;
|
|
234
175
|
}
|
|
235
176
|
|
|
236
177
|
if (dayOfTheMonth) {
|
|
237
|
-
|
|
178
|
+
main.verticalOhlcv.day_of_the_month[x] = cachedDayInfo.day_of_the_month;
|
|
238
179
|
}
|
|
239
180
|
|
|
240
181
|
if (weekOfTheMonth) {
|
|
241
|
-
|
|
182
|
+
main.verticalOhlcv.week_of_the_month[x] = cachedDayInfo.week_of_the_month;
|
|
242
183
|
}
|
|
243
184
|
|
|
244
185
|
if (sessionIndex) {
|
|
245
|
-
|
|
186
|
+
main.verticalOhlcv.session_index[x] = sessionIndexCount;
|
|
246
187
|
}
|
|
247
188
|
|
|
248
189
|
if (sessionIntradayIndex) {
|
|
249
|
-
|
|
190
|
+
main.verticalOhlcv.session_intraday_index[x] = sessionIntradayIndexCount;
|
|
250
191
|
}
|
|
251
192
|
|
|
252
|
-
sessionIntradayIndexCount
|
|
193
|
+
sessionIntradayIndexCount++;
|
|
253
194
|
|
|
254
|
-
|
|
255
|
-
// Run indicator functions
|
|
256
195
|
for (let i = 0; i < indicatorCalls.length; i++) {
|
|
257
|
-
const { fn, args, key } = indicatorCalls[i]
|
|
258
|
-
if(key === 'crossPairs' || key === 'lag') continue
|
|
259
|
-
fn(main, x, ...args)
|
|
196
|
+
const { fn, args, key } = indicatorCalls[i];
|
|
197
|
+
if (key === 'crossPairs' || key === 'lag') continue;
|
|
198
|
+
fn(main, x, ...args);
|
|
260
199
|
}
|
|
261
|
-
|
|
262
|
-
lag(main, x, inputParams.lag)
|
|
263
|
-
crossPairs(main, x, main.crossPairsList)
|
|
264
|
-
main.lastComputedIndex++
|
|
265
200
|
}
|
|
201
|
+
|
|
202
|
+
lag(main, x, inputParams.lag);
|
|
203
|
+
crossPairs(main, x, main.crossPairsList);
|
|
204
|
+
main.lastComputedIndex++;
|
|
266
205
|
}
|
|
267
206
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
}
|
|
207
|
+
Object.assign(main, {
|
|
208
|
+
prevDateStr,
|
|
209
|
+
sessionIndexCount,
|
|
210
|
+
sessionIntradayIndexCount,
|
|
211
|
+
cachedDayInfo
|
|
212
|
+
})
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
const processIndicatorCalls = (inputParams) => {
|
|
217
|
+
if (!inputParams || typeof inputParams !== 'object') return [];
|
|
218
|
+
|
|
219
|
+
const indicatorCalls = [];
|
|
220
|
+
for (const [key, technical] of Object.entries(inputParams)) {
|
|
221
|
+
if (Array.isArray(technical)) {
|
|
222
|
+
for (const params of technical) {
|
|
223
|
+
if (Array.isArray(params)) {
|
|
224
|
+
indicatorCalls.push({
|
|
225
|
+
key,
|
|
226
|
+
fn: indicatorFunctions[key],
|
|
227
|
+
args: params,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return indicatorCalls;
|
|
234
|
+
};
|