From a91758d004e06c849fae95364eb15a42a14d2122 Mon Sep 17 00:00:00 2001 From: Yor Name Date: Mon, 29 Jun 2020 23:48:16 -0400 Subject: [PATCH 1/4] pushing changes --- orderBook.js | 0 package.json | 2 +- tests.js => tests/orderBook.test.js | 0 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 orderBook.js rename tests.js => tests/orderBook.test.js (100%) diff --git a/orderBook.js b/orderBook.js new file mode 100644 index 0000000..e69de29 diff --git a/package.json b/package.json index a3ac999..1b990a3 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": ".eslintrc.js", "scripts": { "lint": "./node_modules/.bin/eslint --format codeframe .", - "test": "./node_modules/.bin/mocha ./tests.js" + "test": "./node_modules/.bin/mocha ./tests/*.js" }, "repository": { "type": "git", diff --git a/tests.js b/tests/orderBook.test.js similarity index 100% rename from tests.js rename to tests/orderBook.test.js From 656588c89924871320b4b6d40536f074f5a1ece8 Mon Sep 17 00:00:00 2001 From: Yor Name Date: Sat, 4 Jul 2020 03:32:06 -0400 Subject: [PATCH 2/4] Working reconsileOrder function which reconsiles bitcoin orders, but needs to be refactored --- orderBook.js | 112 ++++++++++++++++++++++++++++++++++++++++ tests/orderBook.test.js | 6 +-- 2 files changed, 115 insertions(+), 3 deletions(-) diff --git a/orderBook.js b/orderBook.js index e69de29..a466a49 100644 --- a/orderBook.js +++ b/orderBook.js @@ -0,0 +1,112 @@ + + +function reconcileOrder(existingBook, incomingOrder) { + // first check if a deal is possible if not, no need to run any code + // just concat the orders using our function + if (dealPossible(existingBook, incomingOrder) === false) { + return concatOrders(existingBook, incomingOrder) + } + + // if the dealPossible didn't eliminate the function, we have an order + // so now createOrderBook and create our order book, then return it + return createOrderBook(existingBook, incomingOrder) +} + +// Function used to verify there's a possible deal in the first place +function dealPossible(existingBook, incomingOrder) { + // first set a dealPossible variable to keep track of our loop results + let dealPossible = false + + // Now create a loop going through each array inside existingBook object + existingBook.forEach(existingOrder => { + // Now we check if the existing order and incoming order types ever + // differ, if so we have the possibility of an order so exit loop + // and return true, if not return false + if (existingOrder.type !== incomingOrder.type) { + // we found types that are different, so we can maybe make a deal + // First we have to make sure the prices match up + if (priceCheck(existingOrder, incomingOrder) === true) { + dealPossible = true + } + } + }) + + // now return our value which would only be changed if the loop detected + // two types that are not the same + return dealPossible +} + +// Function used to combine the existing book and incoming order into order book +function concatOrders(existingBook, incomingOrder) { + // set a temporary orderBook + let orderBook = [] + + orderBook.push(...existingBook) + orderBook.push({ ...incomingOrder }) + + return orderBook +} + +// Function used to see if prices align in both interests +function priceCheck(existingOrder, incomingOrder) { + // We want to see if when buying our price is greater than or equal too our price, + if (incomingOrder.type === 'buy' && existingOrder.type === 'sell' && incomingOrder.price >= existingOrder.price && + incomingOrder.quantity !== 0 && + existingOrder.quantity !== 0) { + return true + } + // and when selling our price is less than or equal to our price) + if (incomingOrder.type === 'sell' && existingOrder.type === 'buy' && incomingOrder.price <= existingOrder.price && + incomingOrder.quantity !== 0 && existingOrder.quantity !== 0) { + return true + } + + // if nothing else was true, return false as we have no price match + return false +} + +// Function to find all deals and build the order book +function createOrderBook(existingBook, incomingOrder) { + // declare our order array as a global variable so it can be accessed in the forLoop + let orderToExecute = { ...incomingOrder } + let oldBook = [...existingBook] + + + // now create a loop that runs until we have exhausted all quantities of the incomingOrder + // eslint-disable-next-line no-prototype-builtins + while (orderToExecute.hasOwnProperty('type') === true) { + // While we have an order we need to compare it to each existing order + for (let i = 0; i < oldBook.length; i++) { + // Let's see if an order is possible in this pair + if (priceCheck(oldBook[i], orderToExecute) === true) { + // A deal is possible so let's execute it, subtract the quantity from each other + let quantity = Math.min(orderToExecute.quantity, oldBook[i].quantity) + + orderToExecute.quantity -= quantity + oldBook[i].quantity -= quantity + // now move our old order to the back of the book + oldBook.push(...oldBook.splice(oldBook.indexOf(oldBook[i]), 1)) + // cleanse both lists of any values that have a quantity 0 + if (orderToExecute.quantity === 0) { + // if our quantity is 0, delete the object + orderToExecute = {} + } + } + if (oldBook[i].quantity === 0) { + // if this sale made our quantity 0, then delete this object from the array + oldBook.splice(oldBook.indexOf(oldBook[i]), 1) + + // now check if an order is no longer possible + if (!dealPossible(oldBook, orderToExecute) && orderToExecute.hasOwnProperty('type') === true) { + // if not then push the order to the back of the book + oldBook.push(orderToExecute) + orderToExecute = {} + } + } + } } + + return oldBook +} + +// +module.exports = reconcileOrder diff --git a/tests/orderBook.test.js b/tests/orderBook.test.js index 4926c8a..2985c06 100644 --- a/tests/orderBook.test.js +++ b/tests/orderBook.test.js @@ -1,6 +1,6 @@ /* eslint-disable max-len */ const { expect } = require('chai') -const reconcileOrder = require('./orderBook') +const reconcileOrder = require('../orderBook') describe('Order Book', () => { describe('reconcileOrder', () => { @@ -91,7 +91,7 @@ describe('Order Book', () => { expect(updatedBook).to.deep.equal([{ type: 'sell', quantity: 12, price: 6950 }, { type: 'sell', quantity: 5, price: 6150 }]) }) - it.skip('Extra Credit: it fulfills a mismatched order when both parties benefit', () => { + it('Extra Credit: it fulfills a mismatched order when both parties benefit', () => { const existingBook = [{ type: 'buy', quantity: 15, price: 6000 }, { type: 'sell', quantity: 12, price: 6950 }] const incomingOrder = { type: 'sell', quantity: 15, price: 5900 } @@ -100,7 +100,7 @@ describe('Order Book', () => { expect(updatedBook).to.deep.equal([{ type: 'sell', quantity: 12, price: 6950 }]) }) - it.skip('Extra Credit: it does not fulfill a mismatched order when it does not benefit both parties', () => { + it('Extra Credit: it does not fulfill a mismatched order when it does not benefit both parties', () => { const existingBook = [{ type: 'buy', quantity: 15, price: 5900 }, { type: 'sell', quantity: 12, price: 6950 }] const incomingOrder = { type: 'sell', quantity: 15, price: 6000 } From c5046da48caa040659180d6c04560da978e21258 Mon Sep 17 00:00:00 2001 From: Yor Name Date: Sat, 4 Jul 2020 03:40:46 -0400 Subject: [PATCH 3/4] Implements rough draft of order-book application that processes bitcoin buys and sells --- orderBook.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/orderBook.js b/orderBook.js index a466a49..631ea6d 100644 --- a/orderBook.js +++ b/orderBook.js @@ -86,17 +86,18 @@ function createOrderBook(existingBook, incomingOrder) { oldBook[i].quantity -= quantity // now move our old order to the back of the book oldBook.push(...oldBook.splice(oldBook.indexOf(oldBook[i]), 1)) - // cleanse both lists of any values that have a quantity 0 + // check if this depleted our quantity, if so delete our order if (orderToExecute.quantity === 0) { // if our quantity is 0, delete the object orderToExecute = {} } } if (oldBook[i].quantity === 0) { - // if this sale made our quantity 0, then delete this object from the array + // if this sale made our quantity in the book 0, then delete this object from the array oldBook.splice(oldBook.indexOf(oldBook[i]), 1) // now check if an order is no longer possible + // eslint-disable-next-line no-prototype-builtins if (!dealPossible(oldBook, orderToExecute) && orderToExecute.hasOwnProperty('type') === true) { // if not then push the order to the back of the book oldBook.push(orderToExecute) From 6aaf46d8a37ac2baed277c17ebd46f9382c806d4 Mon Sep 17 00:00:00 2001 From: Yor Name Date: Sat, 4 Jul 2020 18:35:18 -0400 Subject: [PATCH 4/4] Refactors reconsileOrder function to use recursion and indices instead of passing whole arrays, much cleaner code --- orderBook.js | 166 +++++++++++++++++++++------------------------------ 1 file changed, 68 insertions(+), 98 deletions(-) diff --git a/orderBook.js b/orderBook.js index 631ea6d..f9623f3 100644 --- a/orderBook.js +++ b/orderBook.js @@ -1,113 +1,83 @@ - - function reconcileOrder(existingBook, incomingOrder) { - // first check if a deal is possible if not, no need to run any code - // just concat the orders using our function - if (dealPossible(existingBook, incomingOrder) === false) { - return concatOrders(existingBook, incomingOrder) + // first thing we do is try to find a deal, if we find one + // return the index of that order, if not then just combine + // our orders together + let dealFoundIndex = findDeal(existingBook, incomingOrder) + + // let's check our index, if it's less than 0 then we didnt find a deal + // so simply concat our lists + if (dealFoundIndex < 0) { + // no deal found, concat our order into our book and return it + return existingBook.concat({ ...incomingOrder }) } - // if the dealPossible didn't eliminate the function, we have an order - // so now createOrderBook and create our order book, then return it - return createOrderBook(existingBook, incomingOrder) + // now let's make our deal using our indexed order and generate a new pending order + const newOrder = makeDeal(existingBook[dealFoundIndex], incomingOrder) + + // now let's trim our existing order from the book, if the quantity isn't 0 + // it will be reinserted on the next recursion + existingBook.splice(dealFoundIndex, 1) + + // now let's check if our order has any quantity, if it doesnt then we are done + if (newOrder.quantity > 0) { + // we still have some quantity left, so let's recall the function using our + // new data causing a recursion that wont be stopped until the quantity is 0 + // or no deal is found on line 11 + return reconcileOrder(existingBook, newOrder) + } else { + // quantity is 0, we are done! no need to concat because it's not a valid order + // just return our book which has been spliced of the order that was processed + return existingBook + } } -// Function used to verify there's a possible deal in the first place -function dealPossible(existingBook, incomingOrder) { - // first set a dealPossible variable to keep track of our loop results - let dealPossible = false - // Now create a loop going through each array inside existingBook object - existingBook.forEach(existingOrder => { - // Now we check if the existing order and incoming order types ever - // differ, if so we have the possibility of an order so exit loop - // and return true, if not return false - if (existingOrder.type !== incomingOrder.type) { - // we found types that are different, so we can maybe make a deal - // First we have to make sure the prices match up - if (priceCheck(existingOrder, incomingOrder) === true) { - dealPossible = true - } - } +// our function which first finds a deal, and returns the index of that deal +function findDeal(existingBook, incomingOrder) { + // we want to return the index of a deal by making a callback function + // find index will cycle through each order until it finds one + return existingBook.findIndex((existingOrder) => { + // if our existingOrder type isn't the same + // as our incoming order, then its a match + // we also need to make sure the prices work + // if both are true, return this order + // this will then make the function return the index + // if no order is found it will return -1 + return existingOrder.type !== incomingOrder.type && goodDeal(existingOrder, incomingOrder) === true }) - - // now return our value which would only be changed if the loop detected - // two types that are not the same - return dealPossible -} - -// Function used to combine the existing book and incoming order into order book -function concatOrders(existingBook, incomingOrder) { - // set a temporary orderBook - let orderBook = [] - - orderBook.push(...existingBook) - orderBook.push({ ...incomingOrder }) - - return orderBook } -// Function used to see if prices align in both interests -function priceCheck(existingOrder, incomingOrder) { - // We want to see if when buying our price is greater than or equal too our price, - if (incomingOrder.type === 'buy' && existingOrder.type === 'sell' && incomingOrder.price >= existingOrder.price && - incomingOrder.quantity !== 0 && - existingOrder.quantity !== 0) { - return true +// this function checks the prices to see if both sides benefit +function goodDeal(existingOrder, incomingOrder) { + // let's check if the incoming order is a buy, if not then its a sell + if (incomingOrder.type === 'buy') { + // if the incoming order is a buy, we need to check if the price + // of the existing order is >= to it + return incomingOrder.price >= existingOrder.price + } else { + // if it's not a buy, it must be a sell, so now we want to check if + // the existing order is <= to incoming order + return incomingOrder.price <= existingOrder.price } - // and when selling our price is less than or equal to our price) - if (incomingOrder.type === 'sell' && existingOrder.type === 'buy' && incomingOrder.price <= existingOrder.price && - incomingOrder.quantity !== 0 && existingOrder.quantity !== 0) { - return true - } - - // if nothing else was true, return false as we have no price match - return false } -// Function to find all deals and build the order book -function createOrderBook(existingBook, incomingOrder) { - // declare our order array as a global variable so it can be accessed in the forLoop - let orderToExecute = { ...incomingOrder } - let oldBook = [...existingBook] - - - // now create a loop that runs until we have exhausted all quantities of the incomingOrder - // eslint-disable-next-line no-prototype-builtins - while (orderToExecute.hasOwnProperty('type') === true) { - // While we have an order we need to compare it to each existing order - for (let i = 0; i < oldBook.length; i++) { - // Let's see if an order is possible in this pair - if (priceCheck(oldBook[i], orderToExecute) === true) { - // A deal is possible so let's execute it, subtract the quantity from each other - let quantity = Math.min(orderToExecute.quantity, oldBook[i].quantity) - - orderToExecute.quantity -= quantity - oldBook[i].quantity -= quantity - // now move our old order to the back of the book - oldBook.push(...oldBook.splice(oldBook.indexOf(oldBook[i]), 1)) - // check if this depleted our quantity, if so delete our order - if (orderToExecute.quantity === 0) { - // if our quantity is 0, delete the object - orderToExecute = {} - } - } - if (oldBook[i].quantity === 0) { - // if this sale made our quantity in the book 0, then delete this object from the array - oldBook.splice(oldBook.indexOf(oldBook[i]), 1) - - // now check if an order is no longer possible - // eslint-disable-next-line no-prototype-builtins - if (!dealPossible(oldBook, orderToExecute) && orderToExecute.hasOwnProperty('type') === true) { - // if not then push the order to the back of the book - oldBook.push(orderToExecute) - orderToExecute = {} - } - } - } } - - return oldBook +// this function makes a deal and returns our newOrder +function makeDeal(existingOrder, incomingOrder) { + // let's see which order will be sent back depending on the quantity + if (incomingOrder.quantity >= existingOrder.quantity) { + // our incoming order has more quantity, so subtract the existing order from it + // then return it as our newest order to be fulfilled + incomingOrder.quantity -= existingOrder.quantity + + return { ...incomingOrder } + } else { + // now our incoming order has less quantity, so let's take its quantity from + // our existing order then return it to be processed through + existingOrder.quantity -= incomingOrder.quantity + + return { ...existingOrder } + } } -// +// export our module to tests and the front end module.exports = reconcileOrder