ohlcv-indicators 1.8.6 → 1.8.8
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.8",
|
|
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
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {FasterSMA} from 'trading-signals'
|
|
2
|
-
import {
|
|
2
|
+
import { classifySize } from '../utilities/classification.js'
|
|
3
3
|
|
|
4
4
|
export const candlesStudies = (main, period, classify, classificationLevels) => {
|
|
5
5
|
|
|
@@ -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_position = new Array(len).fill(null) //scaled shadow size curr.close - prev.close
|
|
23
|
+
let candle_high_position = new Array(len).fill(null) //scaled shadow size curr.high - prev.high
|
|
24
|
+
let candle_low_position = new Array(len).fill(null) //scaled shadow size curr.low - prev.low
|
|
25
|
+
let candle_open_position = 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 closePositionInstance
|
|
34
|
+
let highPositionInstance
|
|
35
|
+
let lowPositionInstance
|
|
36
|
+
let openPositionInstance
|
|
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
|
+
closePositionInstance = new FasterSMA(period)
|
|
46
|
+
highPositionInstance = new FasterSMA(period)
|
|
47
|
+
lowPositionInstance = new FasterSMA(period)
|
|
48
|
+
openPositionInstance = 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,17 +64,24 @@ 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 closePositionMean = null
|
|
68
|
+
let highPositionMean = null
|
|
69
|
+
let lowPositionMean = null
|
|
70
|
+
let openPositionMean = null
|
|
63
71
|
|
|
64
72
|
//directions
|
|
65
|
-
const
|
|
66
|
-
const
|
|
73
|
+
const isUp = close > open
|
|
74
|
+
const candleBodySize = Math.abs(close - open)
|
|
75
|
+
const shadowSize = Math.abs(curr.high - curr.low)
|
|
67
76
|
|
|
68
77
|
//sizes
|
|
69
78
|
const topSize = high - Math.max(open, close)
|
|
70
79
|
const bottomSize = Math.min(open, close) - low
|
|
71
|
-
const gapSize = prev.close - curr.open
|
|
72
|
-
const
|
|
80
|
+
const gapSize = Math.abs(prev.close - curr.open)
|
|
81
|
+
const closePosition = Math.abs(curr.close - prev.close)
|
|
82
|
+
const highPosition = Math.abs(curr.high - prev.high)
|
|
83
|
+
const lowPosition = Math.abs(curr.low - prev.low)
|
|
84
|
+
const openPosition = Math.abs(curr.open - prev.open)
|
|
73
85
|
|
|
74
86
|
if(classify)
|
|
75
87
|
{
|
|
@@ -80,8 +92,10 @@ const getCandlesStudies = (inputOhlcv, period = 20, len, classify = true, classi
|
|
|
80
92
|
|
|
81
93
|
gapInstance.update(gapSize)
|
|
82
94
|
bodySizeInstance.update(candleBodySize)
|
|
83
|
-
|
|
84
|
-
|
|
95
|
+
closePositionInstance.update(closePosition)
|
|
96
|
+
highPositionInstance.update(highPosition)
|
|
97
|
+
lowPositionInstance.update(lowPosition)
|
|
98
|
+
openPositionInstance.update(openPosition)
|
|
85
99
|
|
|
86
100
|
try
|
|
87
101
|
{
|
|
@@ -90,7 +104,10 @@ const getCandlesStudies = (inputOhlcv, period = 20, len, classify = true, classi
|
|
|
90
104
|
gapMean = gapInstance.getResult()
|
|
91
105
|
bodySizeMean = bodySizeInstance.getResult()
|
|
92
106
|
shadowSizeMean = shadowSizeInstance.getResult()
|
|
93
|
-
|
|
107
|
+
closePositionMean = closePositionInstance.getResult()
|
|
108
|
+
highPositionMean = highPositionInstance.getResult()
|
|
109
|
+
lowPositionMean = lowPositionInstance.getResult()
|
|
110
|
+
openPositionMean = openPositionInstance.getResult()
|
|
94
111
|
}
|
|
95
112
|
catch(err)
|
|
96
113
|
{
|
|
@@ -99,14 +116,39 @@ const getCandlesStudies = (inputOhlcv, period = 20, len, classify = true, classi
|
|
|
99
116
|
topSizeMean = null
|
|
100
117
|
bottomSizeMean = null
|
|
101
118
|
shadowSizeMean = null
|
|
102
|
-
|
|
119
|
+
closePositionMean = null
|
|
120
|
+
highPositionMean = null
|
|
121
|
+
lowPositionMean = null
|
|
122
|
+
openPositionMean = null
|
|
103
123
|
}
|
|
104
124
|
|
|
105
125
|
//negatives and positives
|
|
106
|
-
candle_body_size[x] =
|
|
107
|
-
|
|
108
|
-
|
|
126
|
+
candle_body_size[x] = (isUp)
|
|
127
|
+
? classifySize(candleBodySize, bodySizeMean, 2, changeLevel)
|
|
128
|
+
: -Math.abs(classifySize(candleBodySize, bodySizeMean, 2, changeLevel)) || 0;
|
|
129
|
+
|
|
130
|
+
candle_gap_size[x] = (isUp)
|
|
131
|
+
? classifySize(gapSize, gapMean, 2, changeLevel)
|
|
132
|
+
: -Math.abs(classifySize(gapSize, gapMean, 2, changeLevel)) || 0;
|
|
133
|
+
|
|
134
|
+
candle_close_position[x] = (isUp)
|
|
135
|
+
? classifySize(closePosition, closePositionMean, 2, changeLevel)
|
|
136
|
+
: -Math.abs(classifySize(closePosition, closePositionMean, 2, changeLevel)) || 0;
|
|
109
137
|
|
|
138
|
+
candle_high_position[x] = (isUp)
|
|
139
|
+
? classifySize(highPosition, highPositionMean, 2, changeLevel)
|
|
140
|
+
: -Math.abs(classifySize(highPosition, highPositionMean, 2, changeLevel)) || 0;
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
candle_low_position[x] = (isUp)
|
|
144
|
+
? classifySize(lowPosition, lowPositionMean, 2, changeLevel)
|
|
145
|
+
: -Math.abs(classifySize(lowPosition, lowPositionMean, 2, changeLevel)) || 0;
|
|
146
|
+
|
|
147
|
+
candle_open_position[x] = (isUp)
|
|
148
|
+
? classifySize(openPosition, openPositionMean, 2, changeLevel)
|
|
149
|
+
: -Math.abs(classifySize(openPosition, openPositionMean, 2, changeLevel)) || 0;
|
|
150
|
+
|
|
151
|
+
|
|
110
152
|
//positives only
|
|
111
153
|
candle_top_size[x] = classifySize(topSize, topSizeMean, 0.5, sizeLevel)
|
|
112
154
|
candle_bottom_size[x] = classifySize(bottomSize, bottomSizeMean, 0.5, sizeLevel)
|
|
@@ -116,7 +158,9 @@ const getCandlesStudies = (inputOhlcv, period = 20, len, classify = true, classi
|
|
|
116
158
|
//negatives and positives
|
|
117
159
|
candle_body_size[x] = candleBodySize
|
|
118
160
|
candle_gap_size[x] = gapSize
|
|
119
|
-
|
|
161
|
+
candle_close_position[x] = closePosition
|
|
162
|
+
candle_high_position[x] = highPosition
|
|
163
|
+
candle_low_position[x] = lowPosition
|
|
120
164
|
|
|
121
165
|
//positives only
|
|
122
166
|
candle_top_size[x] = topSize
|
|
@@ -132,6 +176,9 @@ const getCandlesStudies = (inputOhlcv, period = 20, len, classify = true, classi
|
|
|
132
176
|
candle_top_size,
|
|
133
177
|
candle_bottom_size,
|
|
134
178
|
candle_shadow_size,
|
|
135
|
-
|
|
179
|
+
candle_close_position,
|
|
180
|
+
candle_high_position,
|
|
181
|
+
candle_low_position,
|
|
182
|
+
candle_open_position
|
|
136
183
|
}
|
|
137
184
|
}
|
|
@@ -18,32 +18,4 @@ export const classifySize = (value, mean, standardDeviation = 1.5, numLevels = 5
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
return 0
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
export const classifyChange = (value, mean, standardDeviation = 0.5, numLevels = 7) => {
|
|
25
|
-
if (value === null || mean === null || standardDeviation === null) return null;
|
|
26
|
-
if (numLevels < 2) throw new Error("numLevels must be at least 2");
|
|
27
|
-
|
|
28
|
-
// Handle numLevels == 2 explicitly
|
|
29
|
-
if (numLevels === 2) {
|
|
30
|
-
return value >= mean ? 1 : -1;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Calculate the range for each level
|
|
34
|
-
const step = (4 * standardDeviation) / (numLevels - 1);
|
|
35
|
-
const thresholds = [];
|
|
36
|
-
|
|
37
|
-
for (let i = 0; i < numLevels - 1; i++) {
|
|
38
|
-
thresholds.push(mean - (2 * standardDeviation) + (i * step));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Classify the value based on calculated thresholds
|
|
42
|
-
for (let i = thresholds.length - 1; i >= 0; i--) {
|
|
43
|
-
if (value > thresholds[i]) {
|
|
44
|
-
return (i - Math.floor((numLevels - 1) / 2)) / Math.floor((numLevels - 1) / 2);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return -1;
|
|
49
|
-
};
|
|
21
|
+
}
|
|
@@ -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
|
+
}
|