Skip to content

Commit

Permalink
Merge pull request #525 from ExchangeUnion/order-quantity
Browse files Browse the repository at this point in the history
refactor: use positive quantity for buys & sells
  • Loading branch information
sangaman authored Sep 25, 2018
2 parents 80ccf3c + 77c8a5f commit 2f0636d
Show file tree
Hide file tree
Showing 19 changed files with 292 additions and 105 deletions.
15 changes: 15 additions & 0 deletions docs/api.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions lib/cli/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { callback, loadXudClient } from './command';
import { Arguments, Argv } from 'yargs';
import { PlaceOrderRequest } from '../proto/xudrpc_pb';
import { PlaceOrderRequest, OrderSide } from '../proto/xudrpc_pb';

export const orderBuilder = (argv: Argv, command: string) => argv
.option('quantity', {
Expand Down Expand Up @@ -28,7 +28,8 @@ export const orderHandler = (argv: Arguments, isSell = false) => {
const numericPrice = Number(argv.price);
const priceStr = argv.price.toLowerCase();

request.setQuantity(isSell ? argv.quantity * -1 : argv.quantity);
request.setQuantity(argv.quantity);
request.setSide(isSell ? OrderSide.SELL : OrderSide.BUY);
request.setPairId(argv.pair_id.toUpperCase());

if (!isNaN(numericPrice)) {
Expand Down
2 changes: 1 addition & 1 deletion lib/grpc/GrpcService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { errorCodes as serviceErrorCodes } from '../service/errors';
import { errorCodes as p2pErrorCodes } from '../p2p/errors';
import { errorCodes as lndErrorCodes } from '../lndclient/errors';
import { LndInfo } from '../lndclient/LndClient';
import { OrderSidesArrays } from '../orderbook/MatchingEngine';

/**
* Convert a [[StampedOrder]] to an xudrpc Order message.
Expand All @@ -25,6 +24,7 @@ const getOrder = (order: StampedOrder) => {
grpcOrder.setPeerPubKey((order as StampedPeerOrder).peerPubKey);
grpcOrder.setPrice(order.price);
grpcOrder.setQuantity(order.quantity);
grpcOrder.setSide(order.isBuy ? xudrpc.OrderSide.BUY : xudrpc.OrderSide.SELL);
return grpcOrder;
};

Expand Down
43 changes: 18 additions & 25 deletions lib/orderbook/MatchingEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class MatchingEngine {
*/
public static getMatchingQuantity = (buyOrder: StampedOrder, sellOrder: StampedOrder): number => {
if (buyOrder.price >= sellOrder.price) {
return Math.min(buyOrder.quantity, sellOrder.quantity * -1);
return Math.min(buyOrder.quantity, sellOrder.quantity);
} else {
return 0;
}
Expand All @@ -91,13 +91,11 @@ class MatchingEngine {
*/
public static splitOrderByQuantity = (order: StampedOrder, matchingQuantity: number): SplitOrder => {
const { quantity } = order;
const absQuantity = Math.abs(quantity);
assert(absQuantity > matchingQuantity, 'order abs quantity must be greater than matchingQuantity');
assert(quantity > matchingQuantity, 'order quantity must be greater than matchingQuantity');

const direction = quantity / absQuantity;
return {
matched: { ...order, quantity: matchingQuantity * direction },
remaining: { ...order, quantity: quantity - (matchingQuantity * direction) },
matched: { ...order, quantity: matchingQuantity },
remaining: { ...order, quantity: quantity - matchingQuantity },
};
}

Expand Down Expand Up @@ -128,9 +126,8 @@ class MatchingEngine {
}

private addOrder = (order: StampedOrder, lists: OrderSidesLists<StampedOrder>): boolean => {
const isBuyOrder = order.quantity > 0;
const list = isBuyOrder ? lists.buy : lists.sell;
const queue = isBuyOrder ? this.queues.buy : this.queues.sell;
const list = order.isBuy ? lists.buy : lists.sell;
const queue = order.isBuy ? this.queues.buy : this.queues.sell;

if (list.has(order.id)) {
return false;
Expand All @@ -151,7 +148,7 @@ class MatchingEngine {
return;
}

if (quantityToDecrease && quantityToDecrease < Math.abs(order.quantity)) {
if (quantityToDecrease && quantityToDecrease < order.quantity) {
// if quantityToDecrease is below the order quantity, mutate the order quantity, and return a simulation of the removed order portion
order.quantity = order.quantity - quantityToDecrease;
return { ...order, quantity: quantityToDecrease };
Expand All @@ -176,7 +173,7 @@ class MatchingEngine {

// remove from lists. for further optimization, we can maintain a separate list for each peer pubKey
removedOrders.forEach((order: StampedPeerOrder) => {
const list = order.quantity > 0 ? this.peerOrders.buy : this.peerOrders.sell;
const list = order.isBuy ? this.peerOrders.buy : this.peerOrders.sell;
list.delete(order.id);
});

Expand All @@ -198,19 +195,18 @@ class MatchingEngine {
}

private removeOrder = (order: StampedOrder, lists: OrderSidesLists<StampedOrder>) => {
const isBuyOrder = order.quantity > 0;
const list = isBuyOrder ? lists.buy : lists.sell;
const queue = isBuyOrder ? this.queues.buy : this.queues.sell;
const list = order.isBuy ? lists.buy : lists.sell;
const queue = order.isBuy ? this.queues.buy : this.queues.sell;

list.delete(order.id);
queue.remove(order);
}

private getOrderList = (order: StampedOrder): OrderList<StampedOrder> => {
if (isOwnOrder(order)) {
return order.quantity > 0 ? this.ownOrders.buy : this.ownOrders.sell;
return order.isBuy ? this.ownOrders.buy : this.ownOrders.sell;
} else {
return order.quantity > 0 ? this.peerOrders.buy : this.peerOrders.sell;
return order.isBuy ? this.peerOrders.buy : this.peerOrders.sell;
}
}

Expand All @@ -234,13 +230,12 @@ class MatchingEngine {
* @returns a [[MatchingResult]] with the matches as well as the remaining, unmatched portion of the order
*/
private match = (takerOrder: StampedOwnOrder): matchingEngine.MatchingResult => {
const isBuyOrder = takerOrder.quantity > 0;
const matches: matchingEngine.OrderMatch[] = [];
/** The unmatched remaining taker order, if there is still leftover quantity after matching is complete it will enter the queue. */
let remainingOrder: StampedOwnOrder | undefined = { ...takerOrder };

const queue = isBuyOrder ? this.queues.sell : this.queues.buy;
const getMatchingQuantity = (remainingOrder: StampedOwnOrder, oppositeOrder: StampedOrder) => isBuyOrder
const queue = takerOrder.isBuy ? this.queues.sell : this.queues.buy;
const getMatchingQuantity = (remainingOrder: StampedOwnOrder, oppositeOrder: StampedOrder) => takerOrder.isBuy
? MatchingEngine.getMatchingQuantity(remainingOrder, oppositeOrder)
: MatchingEngine.getMatchingQuantity(oppositeOrder, remainingOrder);

Expand All @@ -257,16 +252,14 @@ class MatchingEngine {
const list = this.getOrderList(makerOrder);
list.delete(makerOrder.id);

const makerOrderAbsQuantity = Math.abs(makerOrder.quantity);
const remainingOrderAbsQuantity = Math.abs(remainingOrder.quantity);
if (
makerOrderAbsQuantity === matchingQuantity &&
remainingOrderAbsQuantity === matchingQuantity
makerOrder.quantity === matchingQuantity &&
remainingOrder.quantity === matchingQuantity
) { // order quantities are fully matching
matches.push({ maker: makerOrder, taker: remainingOrder });

remainingOrder = undefined;
} else if (remainingOrderAbsQuantity === matchingQuantity) { // taker order quantity is not sufficient. maker order will split
} else if (remainingOrder.quantity === matchingQuantity) { // taker order quantity is not sufficient. maker order will split
const splitOrder = MatchingEngine.splitOrderByQuantity(makerOrder, matchingQuantity);
matches.push({ maker: splitOrder.matched, taker: remainingOrder });

Expand All @@ -275,7 +268,7 @@ class MatchingEngine {
list.set(splitOrder.remaining.id, splitOrder.remaining);

remainingOrder = undefined;
} else if (makerOrderAbsQuantity === matchingQuantity) { // maker order quantity is not sufficient. taker order will split
} else if (makerOrder.quantity === matchingQuantity) { // maker order quantity is not sufficient. taker order will split
const splitOrder = MatchingEngine.splitOrderByQuantity(remainingOrder, matchingQuantity);
matches.push({ maker: makerOrder, taker: splitOrder.matched });
remainingOrder = splitOrder.remaining as StampedOwnOrder;
Expand Down
2 changes: 1 addition & 1 deletion lib/orderbook/OrderBook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ class OrderBook extends EventEmitter {
}

public addMarketOrder = (order: orders.OwnMarketOrder): matchingEngine.MatchingResult => {
const price = order.quantity > 0 ? Number.MAX_VALUE : 0;
const price = order.isBuy ? Number.MAX_VALUE : 0;
const result = this.addOwnOrder({ ...order, price }, true);
delete result.remainingOrder;
return result;
Expand Down
21 changes: 21 additions & 0 deletions lib/proto/hash_resolver_grpc_pb.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions lib/proto/lndrpc_grpc_pb.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions lib/proto/xudrpc.swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions lib/proto/xudrpc_grpc_pb.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions lib/proto/xudrpc_pb.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 2f0636d

Please sign in to comment.