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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ohlcv-indicators",
3
- "version": "1.9.7",
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: true,
30
- midPriceHighLow: true,
31
- sessionIndex: true,
32
- sessionIntradayIndex: true,
33
- dayOfTheWeek: true,
34
- dayOfTheMonth: true,
35
- weekOfTheMonth: true
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
- vert[numberColsKeys[i]] = new Array(len)
86
+ main.verticalOhlcv[numberColsKeys[i]] = new Array(len);
92
87
  }
93
88
 
94
- // Extract other keys and initialize their arrays
95
- const inputKeys = Object.keys(input[0])
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
- vert[key] = new Array(len)
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
- main.prevDateStr = input[0].date.slice(0, 10)
126
- main.sessionIndexCount = 0
127
- main.sessionIntradayIndexCount = 0
128
- main.cachedDayInfo = getDateInfo(main.prevDateStr)
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
- // If no valid date, ensure these are null
131
- main.prevDateStr = null
132
- main.sessionIndexCount = null
133
- main.sessionIntradayIndexCount = null
134
- main.cachedDayInfo = null
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
- // If we had a valid date initially, we continue with date computations
149
- isValidDate = main.prevDateStr !== null
136
+ isValidDate = main.prevDateStr !== null;
150
137
  }
151
138
 
152
- // Pre-process indicator params
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
- // Compute indicators and fill data from startIndex to len
198
- for (let x = startIndex; x < len; x++) {
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 (vo_mid_hl) {
213
- vo_mid_hl[x] = (current.high + current.low) / 2
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
- otherArrays[k][x] = current[k]
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
- vert.day_of_the_week[x] = cachedDayInfo.day_of_the_week
174
+ main.verticalOhlcv.day_of_the_week[x] = cachedDayInfo.day_of_the_week;
234
175
  }
235
176
 
236
177
  if (dayOfTheMonth) {
237
- vert.day_of_the_month[x] = cachedDayInfo.day_of_the_month
178
+ main.verticalOhlcv.day_of_the_month[x] = cachedDayInfo.day_of_the_month;
238
179
  }
239
180
 
240
181
  if (weekOfTheMonth) {
241
- vert.week_of_the_month[x] = cachedDayInfo.week_of_the_month
182
+ main.verticalOhlcv.week_of_the_month[x] = cachedDayInfo.week_of_the_month;
242
183
  }
243
184
 
244
185
  if (sessionIndex) {
245
- vert.session_index[x] = sessionIndexCount
186
+ main.verticalOhlcv.session_index[x] = sessionIndexCount;
246
187
  }
247
188
 
248
189
  if (sessionIntradayIndex) {
249
- vert.session_intraday_index[x] = sessionIntradayIndexCount
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
- // Store current state so that we can continue from here in subsequent calls
269
- main.prevDateStr = prevDateStr
270
- main.sessionIndexCount = sessionIndexCount
271
- main.sessionIntradayIndexCount = sessionIntradayIndexCount
272
- main.cachedDayInfo = cachedDayInfo
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
+ };