@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitfinex/lib-js-util-math",
3
- "version": "0.7.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
- "engine": {
20
- "node": ">=8.0"
19
+ "engines": {
20
+ "node": ">=22.0"
21
21
  },
22
22
  "main": "index.js",
23
23
  "scripts": {
24
- "format": "./node_modules/.bin/standard --fix",
25
- "test": "npm run lint && npm run unit",
26
- "unit": "NODE_ENV=test ./node_modules/.bin/mocha ./test/index.test.js",
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.0.0"
29
+ "bignumber.js": "^9.3.1"
31
30
  },
32
31
  "devDependencies": {
33
- "chai": "^4.2.0",
34
- "dirty-chai": "^2.0.1",
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)
@@ -1,275 +1,274 @@
1
1
  'use strict'
2
2
 
3
- const chai = require('chai')
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
- module.exports = () => {
10
- describe('# array-tests', () => {
11
- it('avg - it should fail with non numeric values', () => {
12
- const values = ['10161', 'a10261.235']
13
-
14
- return expect(
15
- avg.bind(null, values)
16
- ).to.throw()
17
- })
18
-
19
- it('avg - it should fail with no values', () => {
20
- return expect(
21
- avg.bind(null, [])
22
- ).to.throw('ERR_NO_VALUES_PROVIDED')
23
- })
24
-
25
- it('avg - it should match the expected value', () => {
26
- const values = ['10161', '10261.235', '10324.2567', 10724, '10232', '10510.25847', '10261.826']
27
-
28
- const res = avg(values)
29
- return expect(res).to.be.equal('10353.51088142857142857143')
30
- })
31
-
32
- it('avg - it should work with just one element', () => {
33
- const values = ['10161']
34
-
35
- const res = avg(values)
36
- return expect(res).to.be.equal('10161')
37
- })
38
-
39
- it('avg - it should work with selector', () => {
40
- const values = [
41
- { price: '10161', volume: '1' },
42
- { price: '10261.235', volume: '1' },
43
- { price: '10324.2567', volume: '1' },
44
- { price: 10724, volume: '1' },
45
- { price: nBN('10232'), volume: '1' },
46
- { price: '10510.25847', volume: '1' },
47
- { price: '10261.826', volume: '1' }
48
- ]
49
-
50
- const res = avg(values, (a) => a.price)
51
- return expect(res).to.be.equal('10353.51088142857142857143')
52
- })
53
-
54
- it('avg - it should work with negatives', () => {
55
- const values = [
56
- { price: '-10161', volume: '1' },
57
- { price: '-10261.235', volume: '1' },
58
- { price: '-10324.2567', volume: '1' },
59
- { price: '-10724', volume: '1' },
60
- { price: '-10232', volume: '1' },
61
- { price: '-10510.25847', volume: '1' },
62
- { price: '-10261.826', volume: '1' }
63
- ]
64
-
65
- const res = avg(values, (a) => a.price)
66
- return expect(res).to.be.equal('-10353.51088142857142857143')
67
- })
68
-
69
- it('avg - it should fail with non numeric values', () => {
70
- const values = ['10161', 'a10261.235']
71
-
72
- return expect(
73
- avg.bind(null, values)
74
- ).to.throw()
75
- })
76
-
77
- it('max - it should fail with no values', () => {
78
- return expect(
79
- max.bind(null, [])
80
- ).to.throw('ERR_NO_VALUES_PROVIDED')
81
- })
82
-
83
- it('max - it should match the expected value', () => {
84
- const values = ['10161', '10261.235', '10324.2567', 10724, '10232', '10510.25847', '10261.826']
85
-
86
- const res = max(values)
87
- return expect(res).to.be.equal('10724')
88
- })
89
-
90
- it('max - it should work with just one element', () => {
91
- const values = ['10161']
92
-
93
- const res = max(values)
94
- return expect(res).to.be.equal('10161')
95
- })
96
-
97
- it('max - it should work with selector', () => {
98
- const values = [
99
- { price: '10161', volume: '1' },
100
- { price: '10261.235', volume: '1' },
101
- { price: '10324.2567', volume: '1' },
102
- { price: 10724, volume: '1' },
103
- { price: nBN('10232'), volume: '1' },
104
- { price: '10510.25847', volume: '1' },
105
- { price: '10261.826', volume: '1' }
106
- ]
107
-
108
- const res = max(values, (a) => a.price)
109
- return expect(res).to.be.equal('10724')
110
- })
111
-
112
- it('max - it should work with negatives', () => {
113
- const values = [
114
- { price: '-10161', volume: '1' },
115
- { price: '-10261.235', volume: '1' },
116
- { price: '-10324.2567', volume: '1' },
117
- { price: '-10724', volume: '1' },
118
- { price: '-10232', volume: '1' },
119
- { price: '-10510.25847', volume: '1' },
120
- { price: '-10261.826', volume: '1' }
121
- ]
122
-
123
- const res = max(values, (a) => a.price)
124
- return expect(res).to.be.equal('-10161')
125
- })
126
-
127
- it('min - it should fail with no values', () => {
128
- return expect(
129
- min.bind(null, [])
130
- ).to.throw('ERR_NO_VALUES_PROVIDED')
131
- })
132
-
133
- it('min - it should match the expected value', () => {
134
- const values = ['10161', '10261.235', '10324.2567', 10724, '10232', '10510.25847', '10261.826']
135
-
136
- const res = min(values)
137
- return expect(res).to.be.equal('10161')
138
- })
139
-
140
- it('min - it should work with just one element', () => {
141
- const values = ['10161']
142
-
143
- const res = min(values)
144
- return expect(res).to.be.equal('10161')
145
- })
146
-
147
- it('min - it should work with selector', () => {
148
- const values = [
149
- { price: '10161', volume: '1' },
150
- { price: '10261.235', volume: '1' },
151
- { price: '10324.2567', volume: '1' },
152
- { price: 10724, volume: '1' },
153
- { price: nBN('10232'), volume: '1' },
154
- { price: '10510.25847', volume: '1' },
155
- { price: '10261.826', volume: '1' }
156
- ]
157
-
158
- const res = min(values, (a) => a.price)
159
- return expect(res).to.be.equal('10161')
160
- })
161
-
162
- it('min - it should work with negatives', () => {
163
- const values = [
164
- { price: '-10161', volume: '1' },
165
- { price: '-10261.235', volume: '1' },
166
- { price: '-10324.2567', volume: '1' },
167
- { price: '-10724', volume: '1' },
168
- { price: '-10232', volume: '1' },
169
- { price: '-10510.25847', volume: '1' },
170
- { price: '-10261.826', volume: '1' }
171
- ]
172
-
173
- const res = min(values, (a) => a.price)
174
- return expect(res).to.be.equal('-10724')
175
- })
176
-
177
- it('median - it should fail with empty array', () => {
178
- return expect(
179
- median.bind(null, [])
180
- ).to.throw('ERR_NO_VALUES_PROVIDED')
181
- })
182
-
183
- it('median - it should select middle element in case of odd array', () => {
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
- return expect(res.toString()).to.be.equal('10261.826')
196
- })
197
-
198
- it('median - it should select avg from middle element in case of even array', () => {
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
- return expect(res.toString()).to.be.equal('10261.5305')
210
- })
211
-
212
- it('median - it should work with just one item', () => {
213
- const values = ['10161']
214
- const res = median(values)
215
- return expect(res.toString()).to.be.equal('10161')
216
- })
217
-
218
- it('median - it should work with selector', () => {
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
- return expect(res.toString()).to.be.equal('10261.826')
231
- })
232
-
233
- it('median - it should work with negative numbers', () => {
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
- return expect(res.toString()).to.be.equal('-10261.826')
246
- })
247
-
248
- it('filterMedian - it should fail with empty array', () => {
249
- return expect(
250
- median.bind(null, [])
251
- ).to.throw('ERR_NO_VALUES_PROVIDED')
252
- })
253
-
254
- it('filterMedian - it should filter expected values', () => {
255
- const samples = [
256
- ['0.0161545', '0.0161565', '0.0161415', '0.056355'], // filter highest
257
- ['0.0161545', '0.0161565', '0.0361415', '0.056355'], // filter multiple outliners
258
- ['0.0161545', '0.0161565', '0.0161415', '0.006355'], // filter lowest
259
- ['0.0161545', '0.0161565', '0.0161415', '0.016355'] // keep all
260
- ]
261
-
262
- const expected = [
263
- ['0.0161545', '0.0161565', '0.0161415'],
264
- ['0.0161545', '0.0161565'],
265
- ['0.0161545', '0.0161565', '0.0161415'],
266
- ['0.0161545', '0.0161565', '0.0161415', '0.016355']
267
- ]
268
-
269
- const filtered = samples.map(values => filterMedian(values, '0.02'))
270
- filtered.forEach((f, i) => {
271
- expect(f).to.be.eqls(expected[i])
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
+ })