Skip to content

Commit

Permalink
feat(orderbook): don’t match with quantity on hold (#697)
Browse files Browse the repository at this point in the history
* don’t match with quantity on hold

* lint

* PR fixes

* PR fix
  • Loading branch information
moshababo authored Nov 30, 2018
1 parent 1b88899 commit 2444ab9
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 4 deletions.
25 changes: 21 additions & 4 deletions lib/orderbook/TradingPair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ class TradingPair {
let remainingOrder: OwnOrder | undefined = { ...takerOrder };

const queue = takerOrder.isBuy ? this.queues!.sell : this.queues!.buy;
const queueRemovedOrdersWithHold: OwnOrder[] = [];
const getMatchingQuantity = (remainingOrder: OwnOrder, oppositeOrder: Order) => takerOrder.isBuy
? TradingPair.getMatchingQuantity(remainingOrder, oppositeOrder)
: TradingPair.getMatchingQuantity(oppositeOrder, remainingOrder);
Expand All @@ -300,13 +301,18 @@ class TradingPair {
while (remainingOrder && !queue.isEmpty()) {
// get the best available maker order from the top of the queue
const makerOrder = queue.peek()!;
const matchingQuantity = getMatchingQuantity(remainingOrder, makerOrder);
const makerAvailableQuantityOrder = isOwnOrder(makerOrder)
? { ...makerOrder, quantity: makerOrder.quantity - makerOrder.hold, hold: 0 }
: makerOrder;

const matchingQuantity = getMatchingQuantity(remainingOrder, makerAvailableQuantityOrder);
if (matchingQuantity <= 0) {
// there's no match with the best available maker order, so end the matching routine
break;
} else {
/** Whether the maker order is fully matched and should be removed from the queue. */
const makerFullyMatched = makerOrder.quantity === matchingQuantity;
const makerAvailableQuantityFullyMatched = makerAvailableQuantityOrder.quantity === matchingQuantity;
const remainingFullyMatched = remainingOrder.quantity === matchingQuantity;

if (makerFullyMatched && remainingFullyMatched) {
Expand All @@ -317,27 +323,38 @@ class TradingPair {
const matchedMakerOrder = TradingPair.splitOrderByQuantity(makerOrder, matchingQuantity);
this.logger.debug(`reduced order ${makerOrder.id} by ${matchingQuantity} quantity while matching order ${takerOrder.id}`);
matches.push({ maker: matchedMakerOrder, taker: remainingOrder });
} else if (makerFullyMatched) {
} else if (makerAvailableQuantityFullyMatched) {
// maker order quantity is not sufficient. taker order will split
const matchedTakerOrder = TradingPair.splitOrderByQuantity(remainingOrder, matchingQuantity);
matches.push({ maker: makerOrder, taker: matchedTakerOrder });
matches.push({ maker: makerAvailableQuantityOrder, taker: matchedTakerOrder });
} else {
assert(false, 'matchingQuantity should not be lower than both orders quantity values');
assert(false, 'matchingQuantity should not be lower than both orders available quantity values');
}

if (remainingFullyMatched) {
remainingOrder = undefined;
}

if (makerFullyMatched) {
// maker order is fully matched, so remove it from the queue and map
assert(queue.poll() === makerOrder);
const map = this.getOrderMap(makerOrder)!;
map.delete(makerOrder.id);
this.logger.debug(`removed order ${makerOrder.id} while matching order ${takerOrder.id}`);
} else if (makerAvailableQuantityFullyMatched) {
// only an own order can be fully matched for available quantity, but not fully matched in the overall
assert(isOwnOrder(makerOrder));

assert(queue.poll() === makerOrder);
queueRemovedOrdersWithHold.push(makerOrder as OwnOrder);
}
}
}

// return the removed orders with hold to the queue.
// their hold quantity might be released later
queueRemovedOrdersWithHold.forEach(order => queue.add(order));

return { matches, remainingOrder };
}
}
Expand Down
55 changes: 55 additions & 0 deletions test/unit/TradingPair.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,61 @@ describe('TradingPair.match', () => {
expect(peekResult).to.not.be.undefined;
expect(peekResult!.quantity).to.equal(1);
});

it('should not match maker own order hold quantity', () => {
const ownOrder = createOwnOrder(5, 5, false);
tp.addOwnOrder(ownOrder);
tp.addOrderHold(ownOrder.id, 5);

const { matches, remainingOrder } = tp.match(createOwnOrder(5, 5, true));
expect(remainingOrder).to.not.be.undefined;
expect(remainingOrder!.quantity).to.equal(5);
expect(matches.length).to.be.equal(0);
});

it('should not match maker own order hold quantity, and should split the taker order', () => {
const ownOrder = createOwnOrder(5, 5, false);
tp.addOwnOrder(ownOrder);
tp.addOrderHold(ownOrder.id, 3);

const { matches, remainingOrder } = tp.match(createOwnOrder(5, 5, true));
expect(remainingOrder).to.not.be.undefined;
expect(remainingOrder!.quantity).to.equal(3);
expect(matches.length).to.be.equal(1);
expect(matches[0].maker.quantity).to.be.equal(2);
expect(matches[0].taker.quantity).to.be.equal(2);
});

it('should not match maker own order hold quantity, and should fully match the taker order', () => {
const ownOrder = createOwnOrder(5, 5, false);
tp.addOwnOrder(ownOrder);
tp.addOrderHold(ownOrder.id, 3);

const { matches, remainingOrder } = tp.match(createOwnOrder(5, 2, true));
expect(remainingOrder).to.be.undefined;
expect(matches.length).to.be.equal(1);
expect(matches[0].maker.quantity).to.be.equal(2);
expect(matches[0].taker.quantity).to.be.equal(2);
});

it('should not match maker own order hold quantity, but keep the order for the next match', () => {
const ownOrder = createOwnOrder(5, 5, false);
tp.addOwnOrder(ownOrder);
tp.addOrderHold(ownOrder.id, 5);

let mr = tp.match(createOwnOrder(5, 5, true));
expect(mr.remainingOrder).to.not.be.undefined;
expect(mr.remainingOrder!.quantity).to.equal(5);
expect(mr.matches.length).to.be.equal(0);

tp.removeOrderHold(ownOrder.id, 5);

mr = tp.match(createOwnOrder(5, 5, true));
expect(mr.remainingOrder).to.be.undefined;
expect(mr.matches.length).to.be.equal(1);
expect(mr.matches[0].maker.quantity).to.be.equal(5);
expect(mr.matches[0].taker.quantity).to.be.equal(5);
});
});

describe('TradingPair.removeOwnOrder', () => {
Expand Down

0 comments on commit 2444ab9

Please sign in to comment.