ohlcv-indicators 1.9.8 → 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 +89 -132
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,187 +71,147 @@ 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 = processIndicatorCalls(inputParams)
|
|
154
|
-
|
|
155
|
-
// Store references for numeric arrays to avoid repeated dictionary lookups
|
|
156
|
-
const vo_open = vert.open
|
|
157
|
-
const vo_high = vert.high
|
|
158
|
-
const vo_low = vert.low
|
|
159
|
-
const vo_close = vert.close
|
|
160
|
-
const vo_volume = vert.volume
|
|
161
|
-
const vo_mid_oc = midPriceOpenClose ? vert.mid_price_open_close : null
|
|
162
|
-
const vo_mid_hl = midPriceHighLow ? vert.mid_price_high_low : null
|
|
163
|
-
|
|
164
|
-
// References for other keys arrays
|
|
165
|
-
const otherArrays = {}
|
|
166
|
-
for (let i = 0; i < main.otherKeys.length; i++) {
|
|
167
|
-
const k = main.otherKeys[i]
|
|
168
|
-
otherArrays[k] = vert[k]
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// For date/session logic, use previously stored state if startIndex > 0
|
|
172
|
-
let prevDateStr = main.prevDateStr
|
|
173
|
-
let sessionIndexCount = main.sessionIndexCount
|
|
174
|
-
let sessionIntradayIndexCount = main.sessionIntradayIndexCount
|
|
175
|
-
let cachedDayInfo = main.cachedDayInfo
|
|
139
|
+
const indicatorCalls = processIndicatorCalls(inputParams);
|
|
176
140
|
|
|
141
|
+
let {prevDateStr, sessionIndexCount, sessionIntradayIndexCount, cachedDayInfo} = main
|
|
177
142
|
|
|
143
|
+
for (let x = startIndex; x < len; x++) {
|
|
144
|
+
const current = input[x];
|
|
178
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;
|
|
179
151
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const current = input[x]
|
|
183
|
-
|
|
184
|
-
// Assign OHLCV
|
|
185
|
-
vo_open[x] = current.open
|
|
186
|
-
vo_high[x] = current.high
|
|
187
|
-
vo_low[x] = current.low
|
|
188
|
-
vo_close[x] = current.close
|
|
189
|
-
vo_volume[x] = current.volume
|
|
190
|
-
|
|
191
|
-
// Mid-price calculations
|
|
192
|
-
if (vo_mid_oc) {
|
|
193
|
-
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;
|
|
194
154
|
}
|
|
195
|
-
if (
|
|
196
|
-
|
|
155
|
+
if (midPriceHighLow) {
|
|
156
|
+
main.verticalOhlcv.mid_price_high_low[x] = (current.high + current.low) / 2;
|
|
197
157
|
}
|
|
198
158
|
|
|
199
|
-
// Other keys
|
|
200
159
|
for (let i = 0; i < main.otherKeys.length; i++) {
|
|
201
|
-
const k = main.otherKeys[i]
|
|
202
|
-
|
|
160
|
+
const k = main.otherKeys[i];
|
|
161
|
+
main.verticalOhlcv[k][x] = current[k];
|
|
203
162
|
}
|
|
204
163
|
|
|
205
|
-
// Date processing for session indices and date fields
|
|
206
164
|
if (isValidDate) {
|
|
207
|
-
const thisDateStr = current.date.slice(0, 10)
|
|
165
|
+
const thisDateStr = current.date.slice(0, 10);
|
|
208
166
|
if (thisDateStr !== prevDateStr) {
|
|
209
|
-
prevDateStr = thisDateStr
|
|
210
|
-
sessionIndexCount
|
|
211
|
-
sessionIntradayIndexCount = 0
|
|
212
|
-
cachedDayInfo = getDateInfo(thisDateStr)
|
|
167
|
+
prevDateStr = thisDateStr;
|
|
168
|
+
sessionIndexCount++;
|
|
169
|
+
sessionIntradayIndexCount = 0;
|
|
170
|
+
cachedDayInfo = getDateInfo(thisDateStr);
|
|
213
171
|
}
|
|
214
172
|
|
|
215
173
|
if (dayOfTheWeek) {
|
|
216
|
-
|
|
174
|
+
main.verticalOhlcv.day_of_the_week[x] = cachedDayInfo.day_of_the_week;
|
|
217
175
|
}
|
|
218
176
|
|
|
219
177
|
if (dayOfTheMonth) {
|
|
220
|
-
|
|
178
|
+
main.verticalOhlcv.day_of_the_month[x] = cachedDayInfo.day_of_the_month;
|
|
221
179
|
}
|
|
222
180
|
|
|
223
181
|
if (weekOfTheMonth) {
|
|
224
|
-
|
|
182
|
+
main.verticalOhlcv.week_of_the_month[x] = cachedDayInfo.week_of_the_month;
|
|
225
183
|
}
|
|
226
184
|
|
|
227
185
|
if (sessionIndex) {
|
|
228
|
-
|
|
186
|
+
main.verticalOhlcv.session_index[x] = sessionIndexCount;
|
|
229
187
|
}
|
|
230
188
|
|
|
231
189
|
if (sessionIntradayIndex) {
|
|
232
|
-
|
|
190
|
+
main.verticalOhlcv.session_intraday_index[x] = sessionIntradayIndexCount;
|
|
233
191
|
}
|
|
234
192
|
|
|
235
|
-
sessionIntradayIndexCount
|
|
236
|
-
|
|
193
|
+
sessionIntradayIndexCount++;
|
|
237
194
|
|
|
238
|
-
// Run indicator functions
|
|
239
195
|
for (let i = 0; i < indicatorCalls.length; i++) {
|
|
240
|
-
const { fn, args, key } = indicatorCalls[i]
|
|
241
|
-
if(key === 'crossPairs' || key === 'lag') continue
|
|
242
|
-
fn(main, x, ...args)
|
|
196
|
+
const { fn, args, key } = indicatorCalls[i];
|
|
197
|
+
if (key === 'crossPairs' || key === 'lag') continue;
|
|
198
|
+
fn(main, x, ...args);
|
|
243
199
|
}
|
|
244
200
|
}
|
|
245
201
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
main.lastComputedIndex++
|
|
202
|
+
lag(main, x, inputParams.lag);
|
|
203
|
+
crossPairs(main, x, main.crossPairsList);
|
|
204
|
+
main.lastComputedIndex++;
|
|
250
205
|
}
|
|
251
206
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
207
|
+
Object.assign(main, {
|
|
208
|
+
prevDateStr,
|
|
209
|
+
sessionIndexCount,
|
|
210
|
+
sessionIntradayIndexCount,
|
|
211
|
+
cachedDayInfo
|
|
212
|
+
})
|
|
213
|
+
};
|
|
214
|
+
|
|
258
215
|
|
|
259
216
|
const processIndicatorCalls = (inputParams) => {
|
|
260
217
|
if (!inputParams || typeof inputParams !== 'object') return [];
|