ohlcv-indicators 1.8.7 → 1.8.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.8.
|
|
3
|
+
"version": "1.8.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",
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
export const vwap = (main) => {
|
|
4
|
+
const { verticalOhlcv } = main;
|
|
5
|
+
|
|
6
|
+
const vwapValues = calculateVWAP(verticalOhlcv);
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
[`vwap`]: vwapValues,
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const calculateVWAP = data => {
|
|
14
|
+
const { high, low, close, volume } = data;
|
|
15
|
+
const output = new Array(close.length).fill(null)
|
|
16
|
+
|
|
17
|
+
// Cumulative sums for VWAP calculation
|
|
18
|
+
let cumulativePriceVolume = 0;
|
|
19
|
+
let cumulativeVolume = 0;
|
|
20
|
+
|
|
21
|
+
for (let x = 0; x < close.length; x++) {
|
|
22
|
+
|
|
23
|
+
const typicalPrice = (high[x] + low[x] + close[x]) / 3;
|
|
24
|
+
|
|
25
|
+
// Calculate Price * Volume for this interval
|
|
26
|
+
const priceVolume = typicalPrice * volume[x];
|
|
27
|
+
|
|
28
|
+
// Update cumulative sums
|
|
29
|
+
cumulativePriceVolume += priceVolume;
|
|
30
|
+
cumulativeVolume += volume[x];
|
|
31
|
+
|
|
32
|
+
// Calculate VWAP for this interval
|
|
33
|
+
const vwap = cumulativePriceVolume / cumulativeVolume;
|
|
34
|
+
|
|
35
|
+
// Push the VWAP value into the array
|
|
36
|
+
output[x] = vwap
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return output
|
|
40
|
+
}
|
|
@@ -19,8 +19,10 @@ const getCandlesStudies = (inputOhlcv, period = 20, len, classify = true, classi
|
|
|
19
19
|
let candle_bottom_size = new Array(len).fill(null) //scaled bottom size(0, 0.5 and 1)
|
|
20
20
|
let candle_gap_size = new Array(len).fill(null) //scaled gap between the current open and previous close (-1 to 1)
|
|
21
21
|
let candle_shadow_size = new Array(len).fill(null) //scaled shadow size high - slow
|
|
22
|
-
let
|
|
23
|
-
|
|
22
|
+
let candle_close_direction = new Array(len).fill(null) //scaled shadow size curr.close - prev.close
|
|
23
|
+
let candle_high_direction = new Array(len).fill(null) //scaled shadow size curr.high - prev.high
|
|
24
|
+
let candle_low_direction = new Array(len).fill(null) //scaled shadow size curr.low - prev.low
|
|
25
|
+
let candle_open_direction = new Array(len).fill(null) //scaled shadow size curr.open - prev.open
|
|
24
26
|
|
|
25
27
|
//instances
|
|
26
28
|
let topInstance
|
|
@@ -28,7 +30,10 @@ const getCandlesStudies = (inputOhlcv, period = 20, len, classify = true, classi
|
|
|
28
30
|
let gapInstance
|
|
29
31
|
let bodySizeInstance
|
|
30
32
|
let shadowSizeInstance
|
|
31
|
-
let
|
|
33
|
+
let closeDirectionInstance
|
|
34
|
+
let highDirectionInstance
|
|
35
|
+
let lowDirectionInstance
|
|
36
|
+
let openDirectionInstance
|
|
32
37
|
|
|
33
38
|
if(classify)
|
|
34
39
|
{
|
|
@@ -37,7 +42,10 @@ const getCandlesStudies = (inputOhlcv, period = 20, len, classify = true, classi
|
|
|
37
42
|
gapInstance = new FasterSMA(period)
|
|
38
43
|
bodySizeInstance = new FasterSMA(period)
|
|
39
44
|
shadowSizeInstance = new FasterSMA(period)
|
|
40
|
-
|
|
45
|
+
closeDirectionInstance = new FasterSMA(period)
|
|
46
|
+
highDirectionInstance = new FasterSMA(period)
|
|
47
|
+
lowDirectionInstance = new FasterSMA(period)
|
|
48
|
+
openDirectionInstance = new FasterSMA(period)
|
|
41
49
|
}
|
|
42
50
|
|
|
43
51
|
for(let x = 0; x < len; x++)
|
|
@@ -45,10 +53,7 @@ const getCandlesStudies = (inputOhlcv, period = 20, len, classify = true, classi
|
|
|
45
53
|
const curr = inputOhlcv[x]
|
|
46
54
|
const prev = inputOhlcv[x-1]
|
|
47
55
|
|
|
48
|
-
if(typeof prev === 'undefined')
|
|
49
|
-
{
|
|
50
|
-
continue
|
|
51
|
-
}
|
|
56
|
+
if(typeof prev === 'undefined') continue
|
|
52
57
|
|
|
53
58
|
const {open, high, low, close} = curr
|
|
54
59
|
|
|
@@ -59,7 +64,10 @@ const getCandlesStudies = (inputOhlcv, period = 20, len, classify = true, classi
|
|
|
59
64
|
let topSizeMean = null
|
|
60
65
|
let bodySizeMean = null
|
|
61
66
|
let shadowSizeMean = null
|
|
62
|
-
let
|
|
67
|
+
let closeDirectionMean = null
|
|
68
|
+
let highDirectionMean = null
|
|
69
|
+
let lowDirectionMean = null
|
|
70
|
+
let openDirectionMean = null
|
|
63
71
|
|
|
64
72
|
//directions
|
|
65
73
|
const isUp = close > open
|
|
@@ -70,7 +78,10 @@ const getCandlesStudies = (inputOhlcv, period = 20, len, classify = true, classi
|
|
|
70
78
|
const topSize = high - Math.max(open, close)
|
|
71
79
|
const bottomSize = Math.min(open, close) - low
|
|
72
80
|
const gapSize = Math.abs(prev.close - curr.open)
|
|
73
|
-
const
|
|
81
|
+
const closeDirection = Math.abs(curr.close - prev.close)
|
|
82
|
+
const highDirection = Math.abs(curr.high - prev.high)
|
|
83
|
+
const lowDirection = Math.abs(curr.low - prev.low)
|
|
84
|
+
const openDirection = Math.abs(curr.open - prev.open)
|
|
74
85
|
|
|
75
86
|
if(classify)
|
|
76
87
|
{
|
|
@@ -81,8 +92,10 @@ const getCandlesStudies = (inputOhlcv, period = 20, len, classify = true, classi
|
|
|
81
92
|
|
|
82
93
|
gapInstance.update(gapSize)
|
|
83
94
|
bodySizeInstance.update(candleBodySize)
|
|
84
|
-
|
|
85
|
-
|
|
95
|
+
closeDirectionInstance.update(closeDirection)
|
|
96
|
+
highDirectionInstance.update(highDirection)
|
|
97
|
+
lowDirectionInstance.update(lowDirection)
|
|
98
|
+
openDirectionInstance.update(openDirection)
|
|
86
99
|
|
|
87
100
|
try
|
|
88
101
|
{
|
|
@@ -91,7 +104,10 @@ const getCandlesStudies = (inputOhlcv, period = 20, len, classify = true, classi
|
|
|
91
104
|
gapMean = gapInstance.getResult()
|
|
92
105
|
bodySizeMean = bodySizeInstance.getResult()
|
|
93
106
|
shadowSizeMean = shadowSizeInstance.getResult()
|
|
94
|
-
|
|
107
|
+
closeDirectionMean = closeDirectionInstance.getResult()
|
|
108
|
+
highDirectionMean = highDirectionInstance.getResult()
|
|
109
|
+
lowDirectionMean = lowDirectionInstance.getResult()
|
|
110
|
+
openDirectionMean = openDirectionInstance.getResult()
|
|
95
111
|
}
|
|
96
112
|
catch(err)
|
|
97
113
|
{
|
|
@@ -100,7 +116,10 @@ const getCandlesStudies = (inputOhlcv, period = 20, len, classify = true, classi
|
|
|
100
116
|
topSizeMean = null
|
|
101
117
|
bottomSizeMean = null
|
|
102
118
|
shadowSizeMean = null
|
|
103
|
-
|
|
119
|
+
closeDirectionMean = null
|
|
120
|
+
highDirectionMean = null
|
|
121
|
+
lowDirectionMean = null
|
|
122
|
+
openDirectionMean = null
|
|
104
123
|
}
|
|
105
124
|
|
|
106
125
|
//negatives and positives
|
|
@@ -112,9 +131,23 @@ const getCandlesStudies = (inputOhlcv, period = 20, len, classify = true, classi
|
|
|
112
131
|
? classifySize(gapSize, gapMean, 2, changeLevel)
|
|
113
132
|
: -Math.abs(classifySize(gapSize, gapMean, 2, changeLevel)) || 0;
|
|
114
133
|
|
|
115
|
-
|
|
116
|
-
? classifySize(
|
|
117
|
-
: -Math.abs(classifySize(
|
|
134
|
+
candle_close_direction[x] = (isUp)
|
|
135
|
+
? classifySize(closeDirection, closeDirectionMean, 2, changeLevel)
|
|
136
|
+
: -Math.abs(classifySize(closeDirection, closeDirectionMean, 2, changeLevel)) || 0;
|
|
137
|
+
|
|
138
|
+
candle_high_direction[x] = (isUp)
|
|
139
|
+
? classifySize(highDirection, highDirectionMean, 2, changeLevel)
|
|
140
|
+
: -Math.abs(classifySize(highDirection, highDirectionMean, 2, changeLevel)) || 0;
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
candle_low_direction[x] = (isUp)
|
|
144
|
+
? classifySize(lowDirection, lowDirectionMean, 2, changeLevel)
|
|
145
|
+
: -Math.abs(classifySize(lowDirection, lowDirectionMean, 2, changeLevel)) || 0;
|
|
146
|
+
|
|
147
|
+
candle_open_direction[x] = (isUp)
|
|
148
|
+
? classifySize(openDirection, openDirectionMean, 2, changeLevel)
|
|
149
|
+
: -Math.abs(classifySize(openDirection, openDirectionMean, 2, changeLevel)) || 0;
|
|
150
|
+
|
|
118
151
|
|
|
119
152
|
//positives only
|
|
120
153
|
candle_top_size[x] = classifySize(topSize, topSizeMean, 0.5, sizeLevel)
|
|
@@ -125,7 +158,9 @@ const getCandlesStudies = (inputOhlcv, period = 20, len, classify = true, classi
|
|
|
125
158
|
//negatives and positives
|
|
126
159
|
candle_body_size[x] = candleBodySize
|
|
127
160
|
candle_gap_size[x] = gapSize
|
|
128
|
-
|
|
161
|
+
candle_close_direction[x] = closeDirection
|
|
162
|
+
candle_high_direction[x] = highDirection
|
|
163
|
+
candle_low_direction[x] = lowDirection
|
|
129
164
|
|
|
130
165
|
//positives only
|
|
131
166
|
candle_top_size[x] = topSize
|
|
@@ -141,6 +176,9 @@ const getCandlesStudies = (inputOhlcv, period = 20, len, classify = true, classi
|
|
|
141
176
|
candle_top_size,
|
|
142
177
|
candle_bottom_size,
|
|
143
178
|
candle_shadow_size,
|
|
144
|
-
|
|
179
|
+
candle_close_direction,
|
|
180
|
+
candle_high_direction,
|
|
181
|
+
candle_low_direction,
|
|
182
|
+
candle_open_direction
|
|
145
183
|
}
|
|
146
184
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
//change between current and prev
|
|
2
|
+
export const arrayChange = data => {
|
|
3
|
+
|
|
4
|
+
if(!Array.isArray(data))
|
|
5
|
+
{
|
|
6
|
+
throw Error('data param in arrayGt function must be array')
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const output = Array(data.length).fill(0)
|
|
10
|
+
|
|
11
|
+
for (let x = 1; x < output.length; x++) {
|
|
12
|
+
|
|
13
|
+
if(data[x] === null || data[x - 1] === null)
|
|
14
|
+
{
|
|
15
|
+
output[x] === null
|
|
16
|
+
}
|
|
17
|
+
else
|
|
18
|
+
{
|
|
19
|
+
const curr = data[x]
|
|
20
|
+
const prev = data[x - 1]
|
|
21
|
+
output[x] = ((curr - prev) / prev) * 100
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return output
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//tells if array a is greater than array b
|
|
29
|
+
|
|
30
|
+
export const arrayGt = (a, b) => {
|
|
31
|
+
|
|
32
|
+
if(!Array.isArray(a) || !Array.isArray(b))
|
|
33
|
+
{
|
|
34
|
+
throw Error('a and b params in arrayGt function must be arrays')
|
|
35
|
+
}
|
|
36
|
+
if(a.length !== b.length)
|
|
37
|
+
{
|
|
38
|
+
throw Error('array a and b must be the same length in arrayGt function')
|
|
39
|
+
}
|
|
40
|
+
if(a.length < 2)
|
|
41
|
+
{
|
|
42
|
+
throw Error('arrays in arrayGt function must have at least 2 numeric items')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const output = Array(a.length).fill(0.5)
|
|
46
|
+
|
|
47
|
+
for (let x = 1; x < output.length; x++) {
|
|
48
|
+
|
|
49
|
+
if(a[x] === null || b[x] === null)
|
|
50
|
+
{
|
|
51
|
+
output[x] = null
|
|
52
|
+
}
|
|
53
|
+
if(a[x] > b[x])
|
|
54
|
+
{
|
|
55
|
+
output[x] = 1
|
|
56
|
+
}
|
|
57
|
+
else if(a[x] < b[x])
|
|
58
|
+
{
|
|
59
|
+
output[x] = 0
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return output
|
|
64
|
+
}
|