ohlcv-indicators 1.9.2 → 1.9.4

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.
@@ -1,28 +1,62 @@
1
+ import { rsi } from "../oscillators/rsi.js"
2
+ import { sma } from "../moving-averages/sma.js"
3
+ import { ema } from "../moving-averages/ema.js"
4
+ import { macd } from "../moving-averages/macd.js"
5
+ import { relativeVolume } from "../moving-averages/relativeVolume.js"
6
+ import { donchianChannels } from "../moving-averages/donchianChannel.js"
7
+ import { bollingerBands } from "../moving-averages/bollingerBands.js"
8
+ import { volumeOscillator } from "../oscillators/volumeOscillator.js"
9
+ import { candlesStudies } from "../studies/candleStudies.js"
10
+ import { lag } from "../studies/lag.js"
11
+
12
+ const indicatorFunctions = {
13
+ rsi,
14
+ sma,
15
+ ema,
16
+ macd,
17
+ relativeVolume,
18
+ donchianChannels,
19
+ bollingerBands,
20
+ volumeOscillator,
21
+ candlesStudies,
22
+ lag
23
+ }
1
24
 
2
-
3
-
4
- //true if first row date starts with yyyy-mm-dd date
5
- const validateFirstDate = arr => arr[0].hasOwnProperty('date') && typeof arr[0].date === 'string' && /^\d{4}-\d{2}-\d{2}/.test(arr[0].date)
25
+ const validateFirstDate = (arr) => arr[0].hasOwnProperty('date') && typeof arr[0].date === 'string' && /^\d{4}-\d{2}-\d{2}/.test(arr[0].date)
6
26
 
7
27
  export const defaultStudyOptions = {
8
- midPriceOpenClose: false,
9
- midPriceHighLow: false,
10
- sessionIndex: false,
11
- sessionIntradayIndex: false,
12
- dayOfTheWeek: false,
13
- dayOfTheMonth: false,
14
- weekOfTheMonth: false
28
+ midPriceOpenClose: true,
29
+ midPriceHighLow: true,
30
+ sessionIndex: true,
31
+ sessionIntradayIndex: true,
32
+ dayOfTheWeek: true,
33
+ dayOfTheMonth: true,
34
+ weekOfTheMonth: true
15
35
  }
16
36
 
17
- export const parseOhlcvToVertical = (input, len, studyOptions) => {
37
+ const getDateInfo = (dateString) => {
38
+ const date = new Date(dateString);
39
+ const day_of_the_week = date.getDay();
40
+ const day_of_the_month = date.getDate();
41
+ const firstDayOfMonth = new Date(date.getFullYear(), date.getMonth(), 1);
42
+ const dayOffset = firstDayOfMonth.getDay();
43
+ const week_of_the_month = Math.ceil((day_of_the_month + dayOffset) / 7);
18
44
 
45
+ return {
46
+ day_of_the_week,
47
+ day_of_the_month,
48
+ week_of_the_month
49
+ };
50
+ }
51
+
52
+ export const parseOhlcvToVertical = (input, main, startIndex = 0) => {
53
+ const { len, studyOptions = {}, inputParams } = main
19
54
 
20
- if(Boolean(studyOptions))
21
- {
22
- for(let k in studyOptions)
23
- {
24
- if(!defaultStudyOptions.hasOwnProperty(k))
25
- {
55
+ if (studyOptions) {
56
+ const studyOptionKeys = Object.keys(studyOptions)
57
+ for (let i = 0; i < studyOptionKeys.length; i++) {
58
+ const k = studyOptionKeys[i]
59
+ if (!defaultStudyOptions.hasOwnProperty(k)) {
26
60
  throw new Error(`Key ${k} not found in defaultStudyOptions. Available options: ${Object.keys(defaultStudyOptions).join(', ')}`)
27
61
  }
28
62
  }
@@ -38,170 +72,189 @@ export const parseOhlcvToVertical = (input, len, studyOptions) => {
38
72
  weekOfTheMonth
39
73
  } = studyOptions
40
74
 
41
- let highMax
42
- let lowMin
43
- let volMax
44
- let volMin
45
-
46
-
47
75
  const numberColsKeys = ['open', 'high', 'low', 'close', 'volume']
48
-
49
- if(midPriceOpenClose)
50
- {
51
- numberColsKeys.push('mid_price_open_close')
52
- }
53
-
54
- if(midPriceHighLow)
55
- {
56
- numberColsKeys.push('mid_price_high_low')
57
- }
76
+ if (midPriceOpenClose) numberColsKeys.push('mid_price_open_close')
77
+ if (midPriceHighLow) numberColsKeys.push('mid_price_high_low')
58
78
 
59
79
  const numberColsKeysSet = new Set(numberColsKeys)
60
- const verticalOhlcv = {}
61
-
62
- // Initialize arrays for numerical columns
63
- for (const key of numberColsKeys) {
64
- verticalOhlcv[key] = new Array(len)
65
- }
66
-
67
- // Extract other keys and initialize their arrays
68
- const inputKeys = Object.keys(input[0])
69
- const otherKeys = []
80
+ const vert = main.verticalOhlcv
81
+
82
+ let isValidDate = false
83
+ if (startIndex === 0) {
84
+ // Fresh initialization
85
+ // Initialize arrays for numerical columns
86
+ for (let i = 0; i < numberColsKeys.length; i++) {
87
+ vert[numberColsKeys[i]] = new Array(len)
88
+ }
70
89
 
71
- for (const key of inputKeys) {
72
- if (!numberColsKeysSet.has(key)) {
73
- verticalOhlcv[key] = new Array(len)
74
- otherKeys.push(key)
90
+ // Extract other keys and initialize their arrays
91
+ const inputKeys = Object.keys(input[0])
92
+ const otherKeys = []
93
+ for (let i = 0; i < inputKeys.length; i++) {
94
+ const key = inputKeys[i]
95
+ if (!numberColsKeysSet.has(key)) {
96
+ vert[key] = new Array(len)
97
+ otherKeys.push(key)
98
+ }
75
99
  }
76
- }
100
+ main.otherKeys = otherKeys
77
101
 
102
+ isValidDate = validateFirstDate(input)
78
103
 
79
- let prevDateStr
80
- let sessionIndexCount = 0
81
- let sessionIntradayIndexCount = 0
82
- const isValidDate = validateFirstDate(input)
104
+ if (isValidDate) {
105
+ if (sessionIndex) {
106
+ vert['session_index'] = new Array(len)
107
+ }
108
+ if (sessionIntradayIndex) {
109
+ vert['session_intraday_index'] = new Array(len)
110
+ }
111
+ if (dayOfTheWeek) {
112
+ vert['day_of_the_week'] = new Array(len)
113
+ }
114
+ if (dayOfTheMonth) {
115
+ vert['day_of_the_month'] = new Array(len)
116
+ }
117
+ if (weekOfTheMonth) {
118
+ vert['week_of_the_month'] = new Array(len)
119
+ }
83
120
 
84
- if(isValidDate)
85
- {
86
- if(sessionIndex)
87
- {
88
- verticalOhlcv['session_index'] = new Array(len)
121
+ main.prevDateStr = input[0].date.slice(0, 10)
122
+ main.sessionIndexCount = 0
123
+ main.sessionIntradayIndexCount = 0
124
+ main.cachedDayInfo = getDateInfo(main.prevDateStr)
125
+ } else {
126
+ // If no valid date, ensure these are null
127
+ main.prevDateStr = null
128
+ main.sessionIndexCount = null
129
+ main.sessionIntradayIndexCount = null
130
+ main.cachedDayInfo = null
89
131
  }
90
-
91
- if(sessionIntradayIndex)
92
- {
93
- verticalOhlcv['session_intraday_index'] = new Array(len)
132
+ } else {
133
+ // startIndex > 0
134
+ // We assume arrays and keys are already set
135
+ if (!main.otherKeys) {
136
+ throw new Error("otherKeys not found in main. Ensure you run with startIndex=0 first to initialize.")
94
137
  }
95
-
96
- if(dayOfTheWeek)
97
- {
98
- verticalOhlcv['day_of_the_week'] = new Array(len)
99
- }
100
-
101
- if(dayOfTheMonth)
102
- {
103
- verticalOhlcv['day_of_the_month'] = new Array(len)
138
+
139
+ // Validate that state variables needed are present
140
+ if (!main.hasOwnProperty('prevDateStr')) {
141
+ throw new Error("prevDateStr missing in main. Make sure to store state after initial run.")
104
142
  }
105
-
106
- if(weekOfTheMonth)
107
- {
108
- verticalOhlcv['week_of_the_month'] = new Array(len)
143
+
144
+ // If we had a valid date initially, we continue with date computations
145
+ isValidDate = main.prevDateStr !== null
146
+ }
147
+
148
+ // Pre-process indicator params
149
+ const indicatorCalls = []
150
+ if (inputParams && typeof inputParams === 'object') {
151
+ for (const key in inputParams) {
152
+ if (key === 'crossPairs') continue
153
+ const technical = inputParams[key]
154
+ if (Array.isArray(technical)) {
155
+ for (let i = 0; i < technical.length; i++) {
156
+ const params = technical[i];
157
+ if (Array.isArray(params)) {
158
+ indicatorCalls.push({
159
+ fn: indicatorFunctions[key],
160
+ args: params
161
+ })
162
+ }
163
+ }
164
+ }
109
165
  }
110
-
111
- prevDateStr = input[0].date.slice(0, 10)
112
166
  }
113
-
114
- for (let x = 0; x < len; x++) {
115
- const current = input[x]
116
167
 
168
+ // Store references for numeric arrays to avoid repeated dictionary lookups
169
+ const vo_open = vert.open
170
+ const vo_high = vert.high
171
+ const vo_low = vert.low
172
+ const vo_close = vert.close
173
+ const vo_volume = vert.volume
174
+ const vo_mid_oc = midPriceOpenClose ? vert.mid_price_open_close : null
175
+ const vo_mid_hl = midPriceHighLow ? vert.mid_price_high_low : null
176
+
177
+ // References for other keys arrays
178
+ const otherArrays = {}
179
+ for (let i = 0; i < main.otherKeys.length; i++) {
180
+ const k = main.otherKeys[i]
181
+ otherArrays[k] = vert[k]
182
+ }
183
+
184
+ // For date/session logic, use previously stored state if startIndex > 0
185
+ let prevDateStr = main.prevDateStr
186
+ let sessionIndexCount = main.sessionIndexCount
187
+ let sessionIntradayIndexCount = main.sessionIntradayIndexCount
188
+ let cachedDayInfo = main.cachedDayInfo
117
189
 
118
- verticalOhlcv.open[x] = current.open
119
- verticalOhlcv.high[x] = current.high
120
- verticalOhlcv.low[x] = current.low
121
- verticalOhlcv.close[x] = current.close
122
- verticalOhlcv.volume[x] = current.volume
190
+ // Compute indicators and fill data from startIndex to len
191
+ for (let x = startIndex; x < len; x++) {
192
+ const current = input[x]
123
193
 
124
- if(midPriceOpenClose)
125
- {
126
- verticalOhlcv.mid_price_open_close[x] = (current.open + current.close) / 2
194
+ // Assign OHLCV
195
+ vo_open[x] = current.open
196
+ vo_high[x] = current.high
197
+ vo_low[x] = current.low
198
+ vo_close[x] = current.close
199
+ vo_volume[x] = current.volume
200
+
201
+ // Run indicator functions
202
+ for (let i = 0; i < indicatorCalls.length; i++) {
203
+ const { fn, args } = indicatorCalls[i]
204
+ fn(main, x, ...args)
127
205
  }
128
206
 
129
- if(midPriceHighLow)
130
- {
131
- verticalOhlcv.mid_price_high_low[x] = (current.high + current.low) / 2
207
+ // Mid-price calculations
208
+ if (vo_mid_oc) {
209
+ vo_mid_oc[x] = (current.open + current.close) / 2
132
210
  }
133
-
134
- // Process other keys
135
- for (const key of otherKeys) {
136
- verticalOhlcv[key][x] = current[key]
211
+ if (vo_mid_hl) {
212
+ vo_mid_hl[x] = (current.high + current.low) / 2
137
213
  }
138
214
 
139
- // Date processing for session indices
215
+ // Other keys
216
+ for (let i = 0; i < main.otherKeys.length; i++) {
217
+ const k = main.otherKeys[i]
218
+ otherArrays[k][x] = current[k]
219
+ }
220
+
221
+ // Date processing for session indices and date fields
140
222
  if (isValidDate) {
141
223
  const thisDateStr = current.date.slice(0, 10)
142
-
143
224
  if (thisDateStr !== prevDateStr) {
144
225
  prevDateStr = thisDateStr
145
226
  sessionIndexCount++
146
227
  sessionIntradayIndexCount = 0
228
+ cachedDayInfo = getDateInfo(thisDateStr)
147
229
  }
148
230
 
149
- const {
150
- day_of_the_week,
151
- day_of_the_month,
152
- week_of_the_month
153
- } = getDateInfo(thisDateStr)
154
-
155
- if(dayOfTheWeek)
156
- {
157
- verticalOhlcv.day_of_the_week[x] = day_of_the_week
231
+ if (dayOfTheWeek) {
232
+ vert.day_of_the_week[x] = cachedDayInfo.day_of_the_week
158
233
  }
159
-
160
- if(dayOfTheMonth)
161
- {
162
- verticalOhlcv.day_of_the_month[x] = day_of_the_month
234
+
235
+ if (dayOfTheMonth) {
236
+ vert.day_of_the_month[x] = cachedDayInfo.day_of_the_month
163
237
  }
164
-
165
- if(weekOfTheMonth)
166
- {
167
- verticalOhlcv.week_of_the_month[x] = week_of_the_month
238
+
239
+ if (weekOfTheMonth) {
240
+ vert.week_of_the_month[x] = cachedDayInfo.week_of_the_month
168
241
  }
169
242
 
170
- if(sessionIndex)
171
- {
172
- verticalOhlcv.session_index[x] = sessionIndexCount
243
+ if (sessionIndex) {
244
+ vert.session_index[x] = sessionIndexCount
173
245
  }
174
-
175
- if(sessionIntradayIndex)
176
- {
177
- verticalOhlcv.session_intraday_index[x] = sessionIntradayIndexCount
246
+
247
+ if (sessionIntradayIndex) {
248
+ vert.session_intraday_index[x] = sessionIntradayIndexCount
178
249
  }
179
250
 
180
251
  sessionIntradayIndexCount++
181
252
  }
182
253
  }
183
254
 
184
- return verticalOhlcv
255
+ // Store current state so that we can continue from here in subsequent calls
256
+ main.prevDateStr = prevDateStr
257
+ main.sessionIndexCount = sessionIndexCount
258
+ main.sessionIntradayIndexCount = sessionIntradayIndexCount
259
+ main.cachedDayInfo = cachedDayInfo
185
260
  }
186
-
187
-
188
- const getDateInfo = dateString => {
189
- const date = new Date(dateString);
190
-
191
- // Get day of the week (0 for Sunday, 1 for Monday, etc.)
192
- const day_of_the_week = date.getDay()
193
-
194
- // Get day of the month
195
- const day_of_the_month = date.getDate();
196
-
197
- // Calculate the week of the month
198
- const firstDayOfMonth = new Date(date.getFullYear(), date.getMonth(), 1);
199
- const dayOffset = firstDayOfMonth.getDay(); // Day of the week for the 1st day of the month
200
- const week_of_the_month = Math.ceil((day_of_the_month + dayOffset) / 7);
201
-
202
- return {
203
- day_of_the_week,
204
- day_of_the_month,
205
- week_of_the_month
206
- };
207
- }
@@ -38,8 +38,6 @@ export const setIndicatorsFromInputParams = ({ input, inputParams, OHLCV_INDICAT
38
38
  }
39
39
  }
40
40
  }
41
-
42
- console.log(indicators.getLastValues());
43
-
41
+
44
42
  return indicators;
45
43
  };
@@ -1,41 +0,0 @@
1
- import { findCrosses } from "../studies/findCrosses.js"
2
-
3
- export const orb = main => {
4
- const {inputOhlcv, len} = main
5
- const cols = getOrbOfTheFirstCandle(inputOhlcv, len)
6
- return cols
7
- }
8
-
9
- const getOrbOfTheFirstCandle = (inputOhlcv, len) => {
10
- const orb_high = new Array(len).fill(0)
11
- const orb_low = new Array(len).fill(0)
12
- let startIndex = 0
13
- let currentDate = null
14
- let firstCandleHigh = 0
15
- let firstCandleLow = 0
16
-
17
- for (let i = 0; i < len; i++) {
18
- const current = inputOhlcv[i]
19
-
20
- if(i === 0){
21
- if(!current.hasOwnProperty('date'))
22
- {
23
- throw Error(`input ohlcv array must have a date property available in candle datapoint`)
24
- }
25
- }
26
-
27
- const date = current.date.split(' ')[0]
28
-
29
- if (date !== currentDate) {
30
- currentDate = date;
31
- firstCandleHigh = current.high
32
- firstCandleLow = current.low
33
- }
34
-
35
- orb_high[startIndex] = firstCandleHigh
36
- orb_low[startIndex] = firstCandleLow
37
- startIndex++
38
- }
39
-
40
- return {orb_high, orb_low}
41
- }