ohlcv-indicators 1.0.0 → 1.0.2

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/index.js CHANGED
@@ -7,7 +7,7 @@ import { IchimokuCloud } from './src/moving-averages/ichimokuCloud.js'
7
7
  import { RSI} from './src/oscillators/rsi.js'
8
8
  import { MFI} from './src/oscillators/mfi.js'
9
9
  import { RelativeVolume} from './src/moving-averages/relativeVolume.js'
10
- import { findCrosses} from './src/utilities.js'
10
+ import {findCrosses} from './src/utilities.js'
11
11
 
12
12
  export default class OHLCV_INDICATORS {
13
13
  constructor() {
@@ -17,24 +17,28 @@ export default class OHLCV_INDICATORS {
17
17
  init(ohlcv) {
18
18
  const { BigNumber } = this
19
19
 
20
- const ohlcvObj = ohlcv.map(({ open, high, low, close, volume, timestamp }) => ({
20
+ const ohlcvObj = ohlcv.map(({ open, high, low, close, volume, ...rest }) => ({
21
21
  open: BigNumber(open),
22
22
  high: BigNumber(high),
23
23
  low: BigNumber(low),
24
24
  close: BigNumber(close),
25
25
  volume: BigNumber(volume),
26
- timestamp
27
- }))
28
-
29
- this.ohlcv = ohlcvObj.reduce((acc, { open, high, low, close, volume, timestamp }) => {
30
- acc.open.push(open)
31
- acc.high.push(high)
32
- acc.low.push(low)
33
- acc.close.push(close)
34
- acc.volume.push(volume)
35
- acc.timestamp.push(timestamp)
36
- return acc
37
- }, { open: [], high: [], low: [], close: [], volume: [], timestamp: [] })
26
+ ...rest
27
+ }));
28
+
29
+ this.ohlcv = ohlcvObj.reduce((acc, { open, high, low, close, volume, ...rest }) => {
30
+ acc.open.push(open);
31
+ acc.high.push(high);
32
+ acc.low.push(low);
33
+ acc.close.push(close);
34
+ acc.volume.push(volume);
35
+ Object.keys(rest).forEach(key => {
36
+ if (!acc[key]) acc[key] = [];
37
+ acc[key].push(rest[key]);
38
+ });
39
+ return acc;
40
+ }, { open: [], high: [], low: [], close: [], volume: [] });
41
+
38
42
 
39
43
  return this
40
44
  }
@@ -46,6 +50,17 @@ export default class OHLCV_INDICATORS {
46
50
  getData() {
47
51
  return this.ohlcv
48
52
  }
53
+ getLastValues(){
54
+ const output = {}
55
+
56
+ for(let k in this.ohlcv)
57
+ {
58
+ const arr = this.ohlcv[k]
59
+ output[k] = arr[arr.length - 1]
60
+ }
61
+
62
+ return output
63
+ }
49
64
 
50
65
  addColumn(key, arr) {
51
66
  const ohlcvLength = this.ohlcv.open.length
@@ -68,6 +83,7 @@ export default class OHLCV_INDICATORS {
68
83
 
69
84
  //crossing single column pairs
70
85
  const ohlcv = this.getData()
86
+ let slowNumArr = []
71
87
 
72
88
  //each parameter [{fast, slow}, {fast, slow}]
73
89
 
@@ -82,8 +98,8 @@ export default class OHLCV_INDICATORS {
82
98
 
83
99
  //if the column does not exist, will try to create a new one
84
100
  //this code can only add single column output indicators such as SMA and EMA
85
- [fast, slow].forEach(v => {
86
- if (!ohlcv.hasOwnProperty(v)) {
101
+ [fast, slow].forEach((v, i) => {
102
+ if (!ohlcv.hasOwnProperty(v) && typeof v === 'string') {
87
103
  const arr = v.split('_')
88
104
  if (arr.length > 0) {
89
105
  const funcName = arr[0].toUpperCase()
@@ -94,13 +110,23 @@ export default class OHLCV_INDICATORS {
94
110
  }
95
111
  }
96
112
  }
113
+ else if(typeof v === 'number' && i === 1)
114
+ {
115
+ slowNumArr = ohlcv.close.map(o => 30)
116
+ }
97
117
  })
98
118
 
99
119
  //finds the cross and adds is to the columns
100
120
  if (ohlcv.hasOwnProperty(fast) && ohlcv.hasOwnProperty(slow)) {
101
121
  const cross = findCrosses(this.BigNumber, ohlcv[fast], ohlcv[slow])
102
122
  this.addColumn(`${fast}_x_${slow}`, cross)
103
- } else {
123
+ }
124
+ else if(ohlcv.hasOwnProperty(fast) && slowNumArr.length > 0)
125
+ {
126
+ const cross = findCrosses(this.BigNumber, ohlcv[fast], slowNumArr)
127
+ this.addColumn(`${fast}_x_${slow}`, cross)
128
+ }
129
+ else {
104
130
  console.error(`Missing ohlcv properties for ${fast} or ${slow}`)
105
131
  }
106
132
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ohlcv-indicators",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "A JavaScript library featuring optimized trading indicators for OHLCV arrays [{open, high, low, close, value}], utilizing BigNumber precision for enhanced accuracy.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -7,9 +7,12 @@ export const BollingerBands = (main, size, times) => {
7
7
  const data = ohlcv['close'];
8
8
  const bb = getBollingerBands(main.BigNumber, data, size, times)
9
9
  const {upper, mid, lower} = bb
10
- main.addColumn(`bollinger_bands_upper`, upper)
11
- main.addColumn(`bollinger_bands_mid`, mid)
12
- main.addColumn(`bollinger_bands_lower`, lower)
10
+
11
+
12
+ for(let k in bb)
13
+ {
14
+ main.addColumn(`bollinger_bands_${k}`, bb[k])
15
+ }
13
16
  }
14
17
 
15
18
 
@@ -28,8 +31,11 @@ export const getBollingerBands = (BigNumber, data, size = 20, times = 2) => {
28
31
  let upper = avg.map((o, i) => o.plus(timesSd[i]))
29
32
  let mid = avg
30
33
  let lower = avg.map((o, i) => o.minus(timesSd[i]))
34
+ let range = bollingerBandsRange(data, {upper, lower})
35
+
36
+ console.log({range})
31
37
 
32
- return { upper, mid, lower }
38
+ return { upper, mid, lower, range }
33
39
  };
34
40
 
35
41
  const deviation = (BigNumber, data, size) => {
@@ -49,5 +55,15 @@ export const getBollingerBands = (BigNumber, data, size = 20, times = 2) => {
49
55
  }
50
56
 
51
57
  return ret
52
- };
53
-
58
+ }
59
+
60
+
61
+ const bollingerBandsRange = (data, bollingerBands) => {
62
+ let {upper, lower} = bollingerBands;
63
+
64
+ const range = upper.map((v, i) => v.minus(lower[i]))
65
+
66
+ const output = data.map((v, i) => (v.minus(lower[i]).dividedBy(range[i])).times(100))
67
+
68
+ return output
69
+ }
@@ -2,19 +2,16 @@
2
2
  const apiRoot = 'https://nasdaq.jaimelias.workers.dev/api'
3
3
  const convertCurrencyToFloat = str => parseFloat(str.replace(/[,$]/g, ''))
4
4
 
5
-
6
-
7
-
8
5
  export const getOHLCV = async (symbol, limit) => {
9
6
 
10
7
 
11
- const oneDay = await (getOneDayOHLC(symbol, limit))
8
+ const oneDay = await (getOneDayOHLCV(symbol, limit))
12
9
  const latest = await getLatestOHLCV(symbol)
13
10
 
14
11
  return [...oneDay, ...latest]
15
12
  }
16
13
 
17
- const getOneDayOHLC = async (symbol, limit) => {
14
+ const getOneDayOHLCV = async (symbol, limit) => {
18
15
 
19
16
  const {fromDate, toDate} = getDateRange(limit)
20
17
  const response = await fetch(`${apiRoot}/quote/${symbol}/chart?assetclass=stocks&fromdate=${fromDate}&todate=${toDate}`)
@@ -26,13 +23,13 @@ const getOneDayOHLC = async (symbol, limit) => {
26
23
  low: convertCurrencyToFloat(z.low),
27
24
  close: convertCurrencyToFloat(z.close),
28
25
  volume: convertCurrencyToFloat(z.volume),
29
- timestamp: x
26
+ ...convertNyMillisecondsToUtc(x) //{utcTimestamp, utcDate}
30
27
  }))
31
28
 
32
29
  return ohlcv
33
30
  }
34
31
 
35
- const getLatestOHLCV = async (symbol) => {
32
+ export const getLatestOHLCV = async (symbol) => {
36
33
 
37
34
  const response = await fetch(`${apiRoot}/quote/${symbol}/chart?assetClass=stocks`)
38
35
  const data = await response.json()
@@ -45,7 +42,7 @@ const getLatestOHLCV = async (symbol) => {
45
42
  low: Math.min(...allValues),
46
43
  close: convertCurrencyToFloat(lastSalePrice),
47
44
  volume: convertCurrencyToFloat(volume),
48
- timestamp: chart[chart.length -1].x
45
+ ...convertNyMillisecondsToUtc(chart[chart.length -1].x)
49
46
  }]
50
47
  }
51
48
 
@@ -57,4 +54,130 @@ const getDateRange = limit => {
57
54
  const fromDateFormatted = fromDate.toISOString().slice(0, 10) // Get the date limit number of days ago in yyyy-mm-dd format
58
55
 
59
56
  return { fromDate: fromDateFormatted, toDate }
60
- };
57
+ };
58
+
59
+
60
+ export const fetchHistoricalOHLCV = async ({symbol, days}) => {
61
+
62
+ //this function removes the lates "today result from output"
63
+
64
+ const response = await fetch(`https://charting.nasdaq.com/data/charting/intraday?symbol=${symbol}&mostRecent=${days}&includeLatestIntradayData=0&`, {
65
+ referrer: "https://charting.nasdaq.com/dynamic/chart.html"
66
+ })
67
+
68
+ const data = await response.json()
69
+ const {marketData} = data
70
+
71
+
72
+ const parsedData = marketData.map(o => ({
73
+ value: o.Value,
74
+ volume: o.Volume,
75
+ data: o.Date,
76
+ ...convertNYDateStringToUTC(o.Date)
77
+ }))
78
+
79
+ const lastItem = parsedData[parsedData.length - 1]
80
+ const lastDay = new Date(lastItem.utcTimestamp);
81
+ lastDay.setUTCHours(0, 0, 0, 0)
82
+ const lastTimeStamp = lastDay.getTime()
83
+
84
+ const filteredData = parsedData.filter(o => o.utcTimestamp < lastTimeStamp)
85
+
86
+ return filteredData;
87
+ }
88
+
89
+
90
+ const convertNyMillisecondsToUtc = milliseconds => {
91
+ // Create a new Date object using the milliseconds
92
+ const nyDate = new Date(milliseconds);
93
+
94
+ // Calculate the timezone offset in milliseconds (New York is UTC-5 or UTC-4 during Daylight Saving Time)
95
+ const offset = nyDate.getTimezoneOffset() * 60000;
96
+
97
+ // Create a new Date object in UTC by subtracting the offset
98
+ const utcDate = new Date(milliseconds + offset);
99
+
100
+ // Get the UTC milliseconds
101
+ const utcMilliseconds = utcDate.getTime();
102
+
103
+ // Get individual components of the date
104
+ const year = utcDate.getUTCFullYear();
105
+ const month = String(utcDate.getUTCMonth() + 1).padStart(2, '0'); // Months are zero-indexed, so add 1
106
+ const day = String(utcDate.getUTCDate()).padStart(2, '0');
107
+ const hours = String(utcDate.getUTCHours()).padStart(2, '0');
108
+ const minutes = String(utcDate.getUTCMinutes()).padStart(2, '0');
109
+ const seconds = String(utcDate.getUTCSeconds()).padStart(2, '0');
110
+
111
+ // Format the date string
112
+ const utcDateString = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
113
+
114
+ // Return the result as an object
115
+ return {
116
+ utcTimestamp: utcMilliseconds,
117
+ utcDate: utcDateString
118
+ };
119
+ }
120
+
121
+
122
+ function convertNYDateStringToUTC(dateString) {
123
+ // Create a date object in the New York timezone
124
+ const nyDate = new Date(dateString + ' GMT-0400');
125
+
126
+ // Get the UTC date string
127
+ const utcDateString = nyDate.toISOString().replace('T', ' ').substring(0, 19);
128
+
129
+ // Get the UTC timestamp
130
+ const utcTimestamp = nyDate.getTime();
131
+
132
+ return {
133
+ utcDateString: utcDateString,
134
+ utcTimestamp: utcTimestamp
135
+ };
136
+ }
137
+
138
+
139
+ export const convertToOHLCV = (data, intervalMinutes) => {
140
+ const validMinuteIntervals = [5, 10, 15, 30, 45, 60, 120, 180, 240];
141
+
142
+ if (!validMinuteIntervals.includes(intervalMinutes)) {
143
+ const errMessage = `Invalid interval. Please use any of the following intervals: ${validMinuteIntervals.join(", ")}`;
144
+ throw Error(errMessage);
145
+ }
146
+
147
+ const result = [];
148
+ let tempGroup = [];
149
+ let currentIntervalStart = null;
150
+
151
+ data.forEach(item => {
152
+ if (!currentIntervalStart) {
153
+ currentIntervalStart = item.utcTimestamp;
154
+ }
155
+
156
+ if ((item.utcTimestamp - currentIntervalStart) < intervalMinutes * 60000) {
157
+ tempGroup.push(item);
158
+ } else {
159
+ result.push(calculateOHLCV(tempGroup));
160
+ tempGroup = [item];
161
+ currentIntervalStart = item.utcTimestamp;
162
+ }
163
+ });
164
+
165
+ if (tempGroup.length) {
166
+ result.push(calculateOHLCV(tempGroup));
167
+ }
168
+
169
+ return result;
170
+ };
171
+
172
+ const calculateOHLCV = (group) => {
173
+ const open = group[0].value;
174
+ const close = group[group.length - 1].value;
175
+ const high = Math.max(...group.map(item => item.value));
176
+ const low = Math.min(...group.map(item => item.value));
177
+ const volume = group[group.length - 1].volume;
178
+ const utcDateString = group[group.length - 1].utcDateString;
179
+ const utcTimestamp = group[group.length - 1].utcTimestamp;
180
+
181
+ return { open, high, low, close, volume, utcDateString, utcTimestamp };
182
+ };
183
+
package/test/test.js CHANGED
@@ -1,26 +1,42 @@
1
1
  import OHLCV_INDICATORS from '../index.js'
2
- import { getOHLCV } from './fetchLiveData.js'
2
+ import { getOHLCV, fetchHistoricalOHLCV , getLatestOHLCV, convertToOHLCV} from './fetchLiveData.js'
3
3
 
4
4
  const indicators = new OHLCV_INDICATORS();
5
5
  const {BigNumber} = indicators
6
6
 
7
7
  const TEST = async () => {
8
8
 
9
+
10
+
11
+ //console.log(await getLatestOHLCV('NVDA'))
12
+
13
+
14
+ //const historical = await fetchHistoricalOHLCV({symbol: 'NVDA', days: 30})
15
+
16
+ //console.log(historical[0])
17
+
18
+ //const historical = convertToOHLCV(await fetchHistoricalOHLCV({symbol: 'NVDA', days: 15}), 5)
19
+ //console.log(historical.length)
20
+ //console.log(historical[0])
21
+ //console.log(historical[historical.length - 1])
22
+ //return
23
+
24
+
9
25
  const ohlcv = await getOHLCV('AAPL', 230)
10
26
 
11
27
  indicators
12
28
  .init(ohlcv)
13
- .EMA(21)
14
- .EMA(9, 'ema21')
15
- .SMA(200)
16
- .SMA(100, 'sma_200')
17
- .MACD(12, 26, 9)
29
+ //.EMA(21)
30
+ //.EMA(9, 'ema21')
31
+ //.SMA(200)
32
+ //.SMA(100, 'sma_200')
33
+ //.MACD(12, 26, 9)
18
34
  .BollingerBands(20, 2)
19
- .IchimokuCloud(9, 26, 52)
35
+ //.IchimokuCloud(9, 26, 52)
20
36
  .RSI(14, 14)
21
37
  .MFI(14)
22
- .RelativeVolume(10)
23
- .crossPairs([{fast: 'ema_9', slow: 'ema_21'}, {fast: 'close', slow: 'sma_200'}])
38
+ //.RelativeVolume(10)
39
+ .crossPairs([{fast: 'ema_9', slow: 'ema_21'}, {fast: 'close', slow: 'sma_200'}, {fast: 'rsi_14_14', slow: 30}])
24
40
 
25
41
  const dataSet = indicators.getData()
26
42
 
@@ -40,12 +56,14 @@ const TEST = async () => {
40
56
  }
41
57
  })
42
58
 
43
- console.log(`${k}: ${JSON.stringify({firstValue, firstValidIndex, lastValue})}`)
44
- console.log('---\n')
59
+ //console.log(`${k}: ${JSON.stringify({firstValue, firstValidIndex, lastValue})}`)
60
+ //console.log('---\n')
45
61
  }
46
62
 
47
- console.log('<<<<<<<<<< HEADERS >>>>>>>>>>')
48
- console.log(indicators.getHeaders())
63
+ //console.log('<<<<<<<<<< HEADERS >>>>>>>>>>')
64
+ //console.log(indicators.getHeaders())
65
+
66
+ console.log(indicators.getLastValues())
49
67
  }
50
68
 
51
69
  TEST()