@bitfinex/lib-js-util-math 0.7.0 → 1.0.0
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 +9 -29
- package/src/bn.js +4 -0
- package/test/array.test.js +268 -269
- package/test/bn.test.js +169 -167
- package/test/ema.test.js +23 -27
- package/test/impact-mid-price.test.js +223 -214
- package/test/std-deviation.test.js +161 -166
- package/test/twap.test.js +209 -215
- package/test/vwap.test.js +115 -117
- package/test/index.test.js +0 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitfinex/lib-js-util-math",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Bitfinex JS Math Lib",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "prdn",
|
|
@@ -16,40 +16,20 @@
|
|
|
16
16
|
"type": "git",
|
|
17
17
|
"url": "git+https://github.com/bitfinexcom/lib-js-util-math.git"
|
|
18
18
|
},
|
|
19
|
-
"
|
|
20
|
-
"node": ">=
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=22.0"
|
|
21
21
|
},
|
|
22
22
|
"main": "index.js",
|
|
23
23
|
"scripts": {
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"lint": "./node_modules/.bin/standard"
|
|
24
|
+
"test": "npm run lint && brittle-node ./test/*.js",
|
|
25
|
+
"lint": "bfx-lint-js",
|
|
26
|
+
"lint:fix": "bfx-lint-js --fix"
|
|
28
27
|
},
|
|
29
28
|
"dependencies": {
|
|
30
|
-
"bignumber.js": "^9.
|
|
29
|
+
"bignumber.js": "^9.3.1"
|
|
31
30
|
},
|
|
32
31
|
"devDependencies": {
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"husky": "^4.3.6",
|
|
36
|
-
"mocha": "^8.2.1",
|
|
37
|
-
"standard": "^16.0.3"
|
|
38
|
-
},
|
|
39
|
-
"standard": {
|
|
40
|
-
"globals": [
|
|
41
|
-
"it",
|
|
42
|
-
"describe",
|
|
43
|
-
"before",
|
|
44
|
-
"after",
|
|
45
|
-
"beforeEach",
|
|
46
|
-
"afterEach"
|
|
47
|
-
]
|
|
48
|
-
},
|
|
49
|
-
"husky": {
|
|
50
|
-
"hooks": {
|
|
51
|
-
"pre-commit": "npm run lint",
|
|
52
|
-
"pre-push": "npm run test"
|
|
53
|
-
}
|
|
32
|
+
"@bitfinex/lint-js": "^1.1.0",
|
|
33
|
+
"brittle": "^4.5.0"
|
|
54
34
|
}
|
|
55
35
|
}
|
package/src/bn.js
CHANGED
|
@@ -25,6 +25,10 @@ const nBN = (value) => {
|
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* @param {string|number|BigNumber} value
|
|
28
|
+
* @param {object} [opts]
|
|
29
|
+
* @param {boolean} [opts.allowNegative=true]
|
|
30
|
+
* @param {boolean} [opts.allowZero=true]
|
|
31
|
+
* @param {boolean} [opts.allowDecimals=true]
|
|
28
32
|
*/
|
|
29
33
|
const validateBN = (value, opts = { allowNegative: true, allowZero: true, allowDecimals: true }) => {
|
|
30
34
|
const n = nBN(value)
|
package/test/array.test.js
CHANGED
|
@@ -1,275 +1,274 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
.use(require('dirty-chai'))
|
|
5
|
-
const { expect } = chai
|
|
3
|
+
const test = require('brittle')
|
|
6
4
|
const { nBN } = require('../src/bn')
|
|
7
5
|
const { avg, max, min, median, filterMedian } = require('../src/array')
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
7
|
+
test('avg - it should fail with non numeric values', (t) => {
|
|
8
|
+
const values = ['10161', 'a10261.235']
|
|
9
|
+
|
|
10
|
+
t.exception.all(
|
|
11
|
+
avg.bind(null, values)
|
|
12
|
+
)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('avg - it should fail with no values', (t) => {
|
|
16
|
+
t.exception(
|
|
17
|
+
avg.bind(null, []),
|
|
18
|
+
/ERR_NO_VALUES_PROVIDED/
|
|
19
|
+
)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('avg - it should match the expected value', (t) => {
|
|
23
|
+
const values = ['10161', '10261.235', '10324.2567', 10724, '10232', '10510.25847', '10261.826']
|
|
24
|
+
|
|
25
|
+
const res = avg(values)
|
|
26
|
+
t.is(res, '10353.51088142857142857143')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('avg - it should work with just one element', (t) => {
|
|
30
|
+
const values = ['10161']
|
|
31
|
+
|
|
32
|
+
const res = avg(values)
|
|
33
|
+
t.is(res, '10161')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('avg - it should work with selector', (t) => {
|
|
37
|
+
const values = [
|
|
38
|
+
{ price: '10161', volume: '1' },
|
|
39
|
+
{ price: '10261.235', volume: '1' },
|
|
40
|
+
{ price: '10324.2567', volume: '1' },
|
|
41
|
+
{ price: 10724, volume: '1' },
|
|
42
|
+
{ price: nBN('10232'), volume: '1' },
|
|
43
|
+
{ price: '10510.25847', volume: '1' },
|
|
44
|
+
{ price: '10261.826', volume: '1' }
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
const res = avg(values, (a) => a.price)
|
|
48
|
+
t.is(res, '10353.51088142857142857143')
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('avg - it should work with negatives', (t) => {
|
|
52
|
+
const values = [
|
|
53
|
+
{ price: '-10161', volume: '1' },
|
|
54
|
+
{ price: '-10261.235', volume: '1' },
|
|
55
|
+
{ price: '-10324.2567', volume: '1' },
|
|
56
|
+
{ price: '-10724', volume: '1' },
|
|
57
|
+
{ price: '-10232', volume: '1' },
|
|
58
|
+
{ price: '-10510.25847', volume: '1' },
|
|
59
|
+
{ price: '-10261.826', volume: '1' }
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
const res = avg(values, (a) => a.price)
|
|
63
|
+
t.is(res, '-10353.51088142857142857143')
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('avg - it should fail with non numeric values', (t) => {
|
|
67
|
+
const values = ['10161', 'a10261.235']
|
|
68
|
+
|
|
69
|
+
t.exception.all(
|
|
70
|
+
avg.bind(null, values)
|
|
71
|
+
)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test('max - it should fail with no values', (t) => {
|
|
75
|
+
t.exception(
|
|
76
|
+
max.bind(null, []),
|
|
77
|
+
/ERR_NO_VALUES_PROVIDED/
|
|
78
|
+
)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test('max - it should match the expected value', (t) => {
|
|
82
|
+
const values = ['10161', '10261.235', '10324.2567', 10724, '10232', '10510.25847', '10261.826']
|
|
83
|
+
|
|
84
|
+
const res = max(values)
|
|
85
|
+
t.is(res, '10724')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test('max - it should work with just one element', (t) => {
|
|
89
|
+
const values = ['10161']
|
|
90
|
+
|
|
91
|
+
const res = max(values)
|
|
92
|
+
t.is(res, '10161')
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
test('max - it should work with selector', (t) => {
|
|
96
|
+
const values = [
|
|
97
|
+
{ price: '10161', volume: '1' },
|
|
98
|
+
{ price: '10261.235', volume: '1' },
|
|
99
|
+
{ price: '10324.2567', volume: '1' },
|
|
100
|
+
{ price: 10724, volume: '1' },
|
|
101
|
+
{ price: nBN('10232'), volume: '1' },
|
|
102
|
+
{ price: '10510.25847', volume: '1' },
|
|
103
|
+
{ price: '10261.826', volume: '1' }
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
const res = max(values, (a) => a.price)
|
|
107
|
+
t.is(res, '10724')
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('max - it should work with negatives', (t) => {
|
|
111
|
+
const values = [
|
|
112
|
+
{ price: '-10161', volume: '1' },
|
|
113
|
+
{ price: '-10261.235', volume: '1' },
|
|
114
|
+
{ price: '-10324.2567', volume: '1' },
|
|
115
|
+
{ price: '-10724', volume: '1' },
|
|
116
|
+
{ price: '-10232', volume: '1' },
|
|
117
|
+
{ price: '-10510.25847', volume: '1' },
|
|
118
|
+
{ price: '-10261.826', volume: '1' }
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
const res = max(values, (a) => a.price)
|
|
122
|
+
t.is(res, '-10161')
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
test('min - it should fail with no values', (t) => {
|
|
126
|
+
t.exception(
|
|
127
|
+
min.bind(null, []),
|
|
128
|
+
/ERR_NO_VALUES_PROVIDED/
|
|
129
|
+
)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test('min - it should match the expected value', (t) => {
|
|
133
|
+
const values = ['10161', '10261.235', '10324.2567', 10724, '10232', '10510.25847', '10261.826']
|
|
134
|
+
|
|
135
|
+
const res = min(values)
|
|
136
|
+
t.is(res, '10161')
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test('min - it should work with just one element', (t) => {
|
|
140
|
+
const values = ['10161']
|
|
141
|
+
|
|
142
|
+
const res = min(values)
|
|
143
|
+
t.is(res, '10161')
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
test('min - it should work with selector', (t) => {
|
|
147
|
+
const values = [
|
|
148
|
+
{ price: '10161', volume: '1' },
|
|
149
|
+
{ price: '10261.235', volume: '1' },
|
|
150
|
+
{ price: '10324.2567', volume: '1' },
|
|
151
|
+
{ price: 10724, volume: '1' },
|
|
152
|
+
{ price: nBN('10232'), volume: '1' },
|
|
153
|
+
{ price: '10510.25847', volume: '1' },
|
|
154
|
+
{ price: '10261.826', volume: '1' }
|
|
155
|
+
]
|
|
156
|
+
|
|
157
|
+
const res = min(values, (a) => a.price)
|
|
158
|
+
t.is(res, '10161')
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
test('min - it should work with negatives', (t) => {
|
|
162
|
+
const values = [
|
|
163
|
+
{ price: '-10161', volume: '1' },
|
|
164
|
+
{ price: '-10261.235', volume: '1' },
|
|
165
|
+
{ price: '-10324.2567', volume: '1' },
|
|
166
|
+
{ price: '-10724', volume: '1' },
|
|
167
|
+
{ price: '-10232', volume: '1' },
|
|
168
|
+
{ price: '-10510.25847', volume: '1' },
|
|
169
|
+
{ price: '-10261.826', volume: '1' }
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
const res = min(values, (a) => a.price)
|
|
173
|
+
t.is(res, '-10724')
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
test('median - it should fail with empty array', (t) => {
|
|
177
|
+
t.exception(
|
|
178
|
+
median.bind(null, []),
|
|
179
|
+
/ERR_NO_VALUES_PROVIDED/
|
|
180
|
+
)
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
test('median - it should select middle element in case of odd array', (t) => {
|
|
184
|
+
const values = [
|
|
185
|
+
'10161',
|
|
186
|
+
10261.235,
|
|
187
|
+
10324.2567,
|
|
188
|
+
'10724',
|
|
189
|
+
'10232',
|
|
190
|
+
10510.25847,
|
|
191
|
+
10261.826
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
const res = median(values)
|
|
195
|
+
t.is(res.toString(), '10261.826')
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
test('median - it should select avg from middle element in case of even array', (t) => {
|
|
199
|
+
const values = [
|
|
200
|
+
'10161',
|
|
201
|
+
10261.235,
|
|
202
|
+
10324.2567,
|
|
203
|
+
'10724',
|
|
204
|
+
'10232',
|
|
205
|
+
10261.826
|
|
206
|
+
]
|
|
207
|
+
|
|
208
|
+
const res = median(values)
|
|
209
|
+
t.is(res.toString(), '10261.5305')
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
test('median - it should work with just one item', (t) => {
|
|
213
|
+
const values = ['10161']
|
|
214
|
+
const res = median(values)
|
|
215
|
+
t.is(res.toString(), '10161')
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
test('median - it should work with selector', (t) => {
|
|
219
|
+
const values = [
|
|
220
|
+
{ price: '10161', volume: '1' },
|
|
221
|
+
{ price: '10261.235', volume: '1' },
|
|
222
|
+
{ price: '10324.2567', volume: '1' },
|
|
223
|
+
{ price: '10724', volume: '1' },
|
|
224
|
+
{ price: '10232', volume: '1' },
|
|
225
|
+
{ price: '10510.25847', volume: '1' },
|
|
226
|
+
{ price: '10261.826', volume: '1' }
|
|
227
|
+
]
|
|
228
|
+
|
|
229
|
+
const res = median(values, (a) => a.price)
|
|
230
|
+
t.is(res.toString(), '10261.826')
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
test('median - it should work with negative numbers', (t) => {
|
|
234
|
+
const values = [
|
|
235
|
+
'-10161',
|
|
236
|
+
-10261.235,
|
|
237
|
+
-10324.2567,
|
|
238
|
+
'-10724',
|
|
239
|
+
'-10232',
|
|
240
|
+
-10510.25847,
|
|
241
|
+
-10261.826
|
|
242
|
+
]
|
|
243
|
+
|
|
244
|
+
const res = median(values)
|
|
245
|
+
t.is(res.toString(), '-10261.826')
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
test('filterMedian - it should fail with empty array', (t) => {
|
|
249
|
+
t.exception(
|
|
250
|
+
median.bind(null, []),
|
|
251
|
+
/ERR_NO_VALUES_PROVIDED/
|
|
252
|
+
)
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
test('filterMedian - it should filter expected values', (t) => {
|
|
256
|
+
const samples = [
|
|
257
|
+
['0.0161545', '0.0161565', '0.0161415', '0.056355'], // filter highest
|
|
258
|
+
['0.0161545', '0.0161565', '0.0361415', '0.056355'], // filter multiple outliners
|
|
259
|
+
['0.0161545', '0.0161565', '0.0161415', '0.006355'], // filter lowest
|
|
260
|
+
['0.0161545', '0.0161565', '0.0161415', '0.016355'] // keep all
|
|
261
|
+
]
|
|
262
|
+
|
|
263
|
+
const expected = [
|
|
264
|
+
['0.0161545', '0.0161565', '0.0161415'],
|
|
265
|
+
['0.0161545', '0.0161565'],
|
|
266
|
+
['0.0161545', '0.0161565', '0.0161415'],
|
|
267
|
+
['0.0161545', '0.0161565', '0.0161415', '0.016355']
|
|
268
|
+
]
|
|
269
|
+
|
|
270
|
+
const filtered = samples.map(values => filterMedian(values, '0.02'))
|
|
271
|
+
filtered.forEach((f, i) => {
|
|
272
|
+
t.alike(f, expected[i])
|
|
274
273
|
})
|
|
275
|
-
}
|
|
274
|
+
})
|