nzediegwu1 / namebase-orderbook-coding-challenge

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Namebase Orderbook Coding Challenge

For this coding challenge, the task is to create a price-time priority limit order book data structure [1]. This is a mouthful and sounds more complicated than it is -- for an overview, refer to BACKGROUND.md.

The assets we're buying and selling on this order book are scoops of ice cream, and the currency we're using to buy and sell those scoops is the US penny (market symbol ICEPEN). No complicated decimals here -- integers only!

[1] The subject matter of this coding challenging is not necessarily indicative of this job's responsibilities. It's just a fun software challenge!

Requirements

Your submission should include the ability to:

  1. run some kind of unit tests
  2. create limit buy/sell orders
  3. recover the state of the order book after a crash/restart
  4. retrieve the total quantity at each limit price
  5. retrieve the state of an old order

These features are listed in priority order, so if you feel like you won't have enough time to finish this challenge, start at 1 and work your way down.

Challenge guidelines

Estimated completion time: 1.5-3 hours; please don't spend more time than this! Cut features and make tradeoffs instead.

For the purposes of this challenge, it is far more important to be bug-free than it is to be fast. If all operations run in O(n^2) or faster, then your program's performance and asymptotic complexity will not at all be considered in this challenge.

Note: n is the number of open orders (on the book) and m is the number of closed orders (off the book, no more remaining quantity to trade with).

When you submit your program, be sure to describe any suboptimal design and implementation decisions you made but were unable to fix in the alloted time in your README. We don't expect your submission to be 100% perfect -- software rarely is -- and we would like to get a sense of your ability to self-critique and build products incrementally.

Prioritize 1) being feature complete, 2) including enough tests to convince yourself you don't have any big bugs, and 3) writing clean code that is ready for change. Final tip: be cautious about overthinking your solution. Our reference implementation is just 70 SLOCs (excluding tests).

Submission

Send me (anthony@namebase.io) a zip file with your source code (or a link to a github repo) with instructions on how to run your tests.

Technical specification

The ./lib/index.js file should export a single class that implements the following functions:

Sync

Recover the state of the order book from a file. Please do not use a library like sqlite. Use node.js's built-in libraries to write and read to a file that allows you to recover your order book.

/**
 * @param {string} fileName -- the file that contains whatever info you need to recover
 */
sync(fileName);

Limit buy

Add a new limit buy order to the order book. Generates an id so that the client can reference this order in the future.

/**
 * @param {integer} quantity -- how many scoops of ice cream to buy
 * @param {integer} price -- the maximum number of pennies to pay per scoop
 */
buy(quantity, price);

Returns:

{
  id: ...,               // generated by your code, type is up to you
  isBuyOrder: true,      // whether this order is a buy or a sell
  quantity: ...,         // the order's original quantity
  price: ...,            // the order's price
  executedQuantity: ..., // the # of ice cream scoops that this order has purchased
}

Limit sell

Add a new limit sell order to the order book. Generates an id so that the client can reference this order in the future.

/**
 * @param {integer} quantity -- how many scoops of ice cream to sell
 * @param {integer} price -- the minimum number of pennies to receive per scoop
 */
sell(quantity, price);

Returns:

{
  id: ...,               // generated by your code, type is up to you
  isBuyOrder: false,     // whether this order is a buy or a sell
  quantity: ...,         // the order's original quantity
  price: ...,            // the order's price
  executedQuantity: ..., // the # of ice cream scoops that this order has sold
}

Get quantity at price

Retrieve the total number of ice cream scoops being bought or sold at a given limit price.

/**
 * @param {integer} price -- the limit price we're querying the quantity at
 */
getQuantityAtPrice(price);

Returns:

4 
// integer >= 0; the number of ice cream scoops at this price point
// note: all of the orders at a limit price are always on the same side,
//       they are either all BUYs or all SELLs

Get order

Retrieve the state of an old order.

/**
 * @param {number|string|up to you} id -- the order's id
 */
getOrder(id);

Returns:

{
  id: ...,               // generated by your code, type is up to you
  isBuyOrder: false,     // whether this order is a buy or a sell
  quantity: ...,         // the order's original quantity
  price: ...,            // the order's price
  executedQuantity: ..., // the # of ice cream scoops that this order has sold
}

Setup

  1. Clone the repository: git clone https://github.com/nzediegwu1/namebase-orderbook-coding-challenge.git
  2. Install dependencies: yarn
  3. Run tests: yarn test
  4. Start the application: yarn start

Finally

Go to http://localhost:8000 on postman to interact with the API

Documentation

To create a sell order, use: POST /sell with request body below:

{
    "price": 61,
    "quantity": 7
}

To create a buy order, use: POST /buy with request body below:

{
    "price": 50,
    "quantity": 7
}

To sync the order book, use:

GET /sync

To get order by id, use:

GET /order/order-id

To get total quantity at a given price, use:

GET /quantity?price=60

Suboptimal Design & Implementation Decisions I Made But was Unable to Fix Due to Time Constraints

  1. I made use of array instead of Binary Search Tree data structure. Given a very large order book, this would require much computational resources. Binary Search Tree data structure would be a better shot with great boost to the speed of getQuantityAtPrice using a single lookup at the exact collection the data is stored. However I queried the buy-orders collection first, and when not found, I then queried the sell-orders collection, which is not a very efficient approach. With Binary Search Tree, I do not need to search both tables since data is ordered and I can check the max price in each order table without scanning through them, hence determine whether a collection should be searched or not

  2. I implemented getOrderById functionality by invoking Array.find method, which loops through the array while searching for the order. With a million record order book, this would be highly inefficient. Given more time, I would implement indexing for the id field, so that getOrderById operation becomes super fast

  3. I had to change things & make use of synchronous writeFileSync node.js fs method for writing file to my JSON db, instead of the writeFile I initially used for asynchronous, faster & non-blocking I/O. This was because tests were crashing when fs.writeFile was used and fs.writeFile implementation was difficult to mock/replace within test considering time constraints

About

License:MIT License


Languages

Language:JavaScript 100.0%