Skip to content

Commit 4c01ef8

Browse files
authored
Merge pull request #267 from fasenderos/bignumber
feat: use bignumber.js for size precision
2 parents 3c6e200 + 4449d68 commit 4c01ef8

File tree

11 files changed

+130
-99
lines changed

11 files changed

+130
-99
lines changed

package-lock.json

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"prepare": "husky install"
4242
},
4343
"dependencies": {
44+
"bignumber.js": "^9.1.1",
4445
"denque": "2.1.0",
4546
"functional-red-black-tree": "1.0.1"
4647
},

src/order.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import BigNumber from 'bignumber.js'
12
import { Side } from './side'
23

34
export interface OrderUpdate {
@@ -20,14 +21,14 @@ export enum TimeInForce {
2021
export class Order {
2122
private readonly _id: string
2223
private readonly _side: Side
23-
private _size: number
24+
private _size: BigNumber
2425
private readonly _price: number
2526
private _time: number
2627
private readonly _isMaker: boolean
2728
constructor (
2829
orderId: string,
2930
side: Side,
30-
size: number,
31+
size: BigNumber,
3132
price: number,
3233
time?: number,
3334
isMaker?: boolean
@@ -56,11 +57,11 @@ export class Order {
5657
}
5758

5859
// returns size of the order
59-
get size (): number {
60+
get size (): BigNumber {
6061
return this._size
6162
}
6263

63-
set size (size: number) {
64+
set size (size: BigNumber) {
6465
this._size = size
6566
}
6667

@@ -81,7 +82,7 @@ export class Order {
8182
toString = (): string => {
8283
return `${this._id}:
8384
side: ${this._side}
84-
size: ${this._side}
85+
size: ${this._size.toNumber() as unknown as string}
8586
price: ${this._price}
8687
time: ${this._time}
8788
isMaker: ${this.isMaker as unknown as string}`
@@ -92,7 +93,7 @@ export class Order {
9293
return JSON.stringify({
9394
id: this._id,
9495
side: this._side,
95-
size: this._size,
96+
size: this._size.toNumber(),
9697
price: this._price,
9798
time: this._time,
9899
isMaker: this.isMaker
@@ -111,7 +112,7 @@ export class Order {
111112
return {
112113
id: this._id,
113114
side: this._side,
114-
size: this._size,
115+
size: this._size.toNumber(),
115116
price: this._price,
116117
time: this._time,
117118
isMaker: this.isMaker

src/orderbook.ts

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import BigNumber from 'bignumber.js'
12
import { ERROR, CustomError } from './errors'
23
import { Order, OrderType, OrderUpdate, TimeInForce } from './order'
34
import { OrderQueue } from './orderqueue'
@@ -225,7 +226,7 @@ export class OrderBook {
225226
const order = new Order(
226227
orderID,
227228
side,
228-
quantityToTrade,
229+
new BigNumber(quantityToTrade),
229230
price,
230231
Date.now(),
231232
true
@@ -240,8 +241,9 @@ export class OrderBook {
240241
let totalPrice = 0
241242

242243
response.done.forEach((order: Order) => {
243-
totalQuantity += order.size
244-
totalPrice += order.price * order.size
244+
const ordrSize: number = order.size.toNumber()
245+
totalQuantity += ordrSize
246+
totalPrice += order.price * ordrSize
245247
})
246248
if (response.partialQuantityProcessed > 0 && response.partial !== null) {
247249
totalQuantity += response.partialQuantityProcessed
@@ -250,7 +252,7 @@ export class OrderBook {
250252
}
251253

252254
response.done.push(
253-
new Order(orderID, side, size, totalPrice / totalQuantity, Date.now())
255+
new Order(orderID, side, new BigNumber(size), totalPrice / totalQuantity, Date.now())
254256
)
255257
}
256258

@@ -317,10 +319,10 @@ export class OrderBook {
317319
const asks: Array<[number, number]> = []
318320
const bids: Array<[number, number]> = []
319321
this.asks.priceTree().forEach((levelPrice, level) => {
320-
asks.push([levelPrice, level.volume()])
322+
asks.push([levelPrice, level.volume().toNumber()])
321323
})
322324
this.bids.priceTree().forEach((levelPrice, level) => {
323-
bids.push([levelPrice, level.volume()])
325+
bids.push([levelPrice, level.volume().toNumber()])
324326
})
325327
return [asks, bids]
326328
}
@@ -356,7 +358,7 @@ export class OrderBook {
356358
}
357359

358360
while (size > 0 && level !== undefined) {
359-
const levelVolume = level.volume()
361+
const levelVolume = level.volume().toNumber()
360362
const levelPrice = level.price()
361363
if (this.greaterThanOrEqual(size, levelVolume)) {
362364
price += levelPrice * levelVolume
@@ -398,11 +400,12 @@ export class OrderBook {
398400
while (orderQueue.len() > 0 && response.quantityLeft > 0) {
399401
const headOrder = orderQueue.head()
400402
if (headOrder !== undefined) {
401-
if (response.quantityLeft < headOrder.size) {
403+
const headSize = headOrder.size.toNumber()
404+
if (response.quantityLeft < headSize) {
402405
response.partial = new Order(
403406
headOrder.id,
404407
headOrder.side,
405-
headOrder.size - response.quantityLeft,
408+
new BigNumber(headSize - response.quantityLeft),
406409
headOrder.price,
407410
headOrder.time,
408411
true
@@ -412,7 +415,7 @@ export class OrderBook {
412415
orderQueue.update(headOrder, response.partial)
413416
response.quantityLeft = 0
414417
} else {
415-
response.quantityLeft = response.quantityLeft - headOrder.size
418+
response.quantityLeft = response.quantityLeft - headSize
416419
const canceledOrder = this.cancel(headOrder.id)
417420
if (canceledOrder !== undefined) response.done.push(canceledOrder)
418421
}
@@ -438,14 +441,16 @@ export class OrderBook {
438441
size: number,
439442
price: number
440443
): boolean => {
441-
if (orderSide.volume() < size) {
444+
const insufficientSideVolume: boolean = orderSide.volume().lt(size)
445+
if (insufficientSideVolume) {
442446
return false
443447
}
444448

445449
let cumulativeSize = 0
446450
orderSide.priceTree().forEach((_key, priceLevel) => {
447451
if (price >= priceLevel.price() && cumulativeSize < size) {
448-
cumulativeSize += priceLevel.volume()
452+
const volume: number = priceLevel.volume().toNumber()
453+
cumulativeSize += volume
449454
} else {
450455
return true // break the loop
451456
}
@@ -458,14 +463,16 @@ export class OrderBook {
458463
size: number,
459464
price: number
460465
): boolean => {
461-
if (orderSide.volume() < size) {
466+
const insufficientSideVolume: boolean = orderSide.volume().lt(size)
467+
if (insufficientSideVolume) {
462468
return false
463469
}
464470

465471
let cumulativeSize = 0
466472
orderSide.priceTree().forEach((_key, priceLevel) => {
467473
if (price <= priceLevel.price() && cumulativeSize < size) {
468-
cumulativeSize += priceLevel.volume()
474+
const volume: number = priceLevel.volume().toNumber()
475+
cumulativeSize += volume
469476
} else {
470477
return true // break the loop
471478
}

src/orderqueue.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1+
import BigNumber from 'bignumber.js'
12
import Denque from 'denque'
23
import { Order } from './order'
34

45
export class OrderQueue {
56
private readonly _price: number
6-
private _volume: number
7+
private _volume: BigNumber
78
private readonly _orders: Denque<Order>
89
// { orderID: index } index in denque
910
private _ordersMap: { [key: string]: number } = {}
1011

1112
constructor (price: number) {
1213
this._price = price
13-
this._volume = 0
14+
this._volume = new BigNumber(0)
1415
this._orders = new Denque<Order>()
1516
}
1617

@@ -29,7 +30,7 @@ export class OrderQueue {
2930
}
3031

3132
// returns price level of the queue
32-
volume = (): number => {
33+
volume = (): BigNumber => {
3334
return this._volume
3435
}
3536

@@ -45,16 +46,15 @@ export class OrderQueue {
4546

4647
// adds order to tail of the queue
4748
append = (order: Order): Order => {
48-
this._volume += order.size
49+
this._volume = this._volume.plus(order.size)
4950
this._orders.push(order)
5051
this._ordersMap[order.id] = this._orders.length - 1
5152
return order
5253
}
5354

5455
// sets up new order to list value
5556
update = (oldOrder: Order, newOrder: Order): void => {
56-
this._volume -= oldOrder.size
57-
this._volume += newOrder.size
57+
this._volume = this._volume.minus(oldOrder.size).plus(newOrder.size)
5858
// Remove old order from head
5959
this._orders.shift()
6060
/* eslint-disable @typescript-eslint/no-dynamic-delete */
@@ -66,7 +66,7 @@ export class OrderQueue {
6666

6767
// removes order from the queue
6868
remove = (order: Order): void => {
69-
this._volume -= order.size
69+
this._volume = this._volume.minus(order.size)
7070
const deletedOrderIndex = this._ordersMap[order.id]
7171
this._orders.removeOne(deletedOrderIndex)
7272
delete this._ordersMap[order.id]
@@ -80,8 +80,9 @@ export class OrderQueue {
8080
}
8181
}
8282

83-
updateOrderSize = (order: Order, newSize: number): void => {
84-
this._volume += newSize - order.size // update volume
83+
updateOrderSize = (order: Order, size: number): void => {
84+
const newSize = new BigNumber(size)
85+
this._volume = this._volume.plus(newSize.minus(order.size)) // update volume
8586
order.size = newSize
8687
order.time = Date.now()
8788
}

src/orderside.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import BigNumber from 'bignumber.js'
12
import createRBTree from 'functional-red-black-tree'
23
import { CustomError, ERROR } from './errors'
34
import { Order, OrderUpdate } from './order'
@@ -7,8 +8,8 @@ import { Side } from './side'
78
export class OrderSide {
89
private _priceTree: createRBTree.Tree<number, OrderQueue>
910
private _prices: { [key: string]: OrderQueue } = {}
10-
private _volume = 0
11-
private _total = 0
11+
private _volume = new BigNumber(0)
12+
private _total = new BigNumber(0)
1213
private _numOrders = 0
1314
private _depthSide = 0
1415
private readonly _side: Side = Side.SELL
@@ -33,12 +34,12 @@ export class OrderSide {
3334
}
3435

3536
// returns total amount of quantity in side
36-
volume = (): number => {
37+
volume = (): BigNumber => {
3738
return this._volume
3839
}
3940

4041
// returns the total (size * price of each price level) in side
41-
total = (): number => {
42+
total = (): BigNumber => {
4243
return this._total
4344
}
4445

@@ -58,8 +59,8 @@ export class OrderSide {
5859
this._depthSide += 1
5960
}
6061
this._numOrders += 1
61-
this._volume += order.size
62-
this._total += order.size * order.price
62+
this._volume = this._volume.plus(order.size)
63+
this._total = this._total.plus(order.size.multipliedBy(order.price))
6364
return this._prices[strPrice].append(order)
6465
}
6566

@@ -77,8 +78,8 @@ export class OrderSide {
7778
}
7879

7980
this._numOrders -= 1
80-
this._volume -= order.size
81-
this._total -= order.size * order.price
81+
this._volume = this._volume.minus(order.size)
82+
this._total = this._total.minus(order.size.multipliedBy(order.price))
8283
return order
8384
}
8485

@@ -92,7 +93,7 @@ export class OrderSide {
9293
const newOrder = new Order(
9394
oldOrder.id,
9495
oldOrder.side,
95-
orderUpdate.size ?? oldOrder.size,
96+
orderUpdate.size !== undefined ? new BigNumber(orderUpdate.size) : oldOrder.size,
9697
orderUpdate.price,
9798
Date.now(),
9899
oldOrder.isMaker
@@ -101,13 +102,15 @@ export class OrderSide {
101102
return newOrder
102103
} else if (
103104
orderUpdate.size !== undefined &&
104-
orderUpdate.size !== oldOrder.size
105+
orderUpdate.size !== oldOrder.size.toNumber()
105106
) {
106107
// Quantity changed. Price is the same.
108+
const oldOrderSize: number = oldOrder.size.toNumber()
107109
const strPrice = oldOrder.price.toString()
108-
this._volume += orderUpdate.size - oldOrder.size
109-
this._total +=
110-
orderUpdate.size * orderUpdate.price - oldOrder.size * oldOrder.price
110+
this._volume = this._volume.plus(orderUpdate.size - oldOrderSize)
111+
this._total = this._total.plus(
112+
orderUpdate.size * orderUpdate.price - oldOrderSize * oldOrder.price
113+
)
111114
this._prices[strPrice].updateOrderSize(oldOrder, orderUpdate.size)
112115
return oldOrder
113116
}
@@ -165,7 +168,8 @@ export class OrderSide {
165168
let s = ''
166169
let level = this.maxPriceQueue()
167170
while (level !== undefined) {
168-
s += `\n${level.price()} -> ${level.volume()}`
171+
const volume: string = level.volume().toString()
172+
s += `\n${level.price()} -> ${volume}`
169173
level = this.lowerThan(level.price())
170174
}
171175
return s

test/error.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { CustomError, ERROR } from '../src/errors'
21
import { test } from 'tap'
2+
import { CustomError, ERROR } from '../src/errors'
33

44
void test('Test default CustomError', ({ equal, end }) => {
55
const a = CustomError()

0 commit comments

Comments
 (0)