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!
Your submission should include the ability to:
- run some kind of unit tests
- create limit buy/sell orders
- recover the state of the order book after a crash/restart
- retrieve the total quantity at each limit price
- 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.
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).
Send me ([email protected]) a zip file with your source code (or a link to a github repo) with instructions on how to run your tests.
The ./lib/index.js
file should export a single class that implements the following functions:
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);
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
}
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
}
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
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
}
- guid generation relies on poor RNG
- overall O() can be reduced leveraging BST
- OOP for sellOrder and buyOrder
- file system concurrency and error handling
- logging
- documentation headers
- sync is manual, requires differential read/write (1 file per order)
- remove coupling from business logic and persitence (services, repositories, ...)