diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..03d9549 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/shelf/Uncommitted_changes_before_Update_at_11_08_2020,_22_16_[Default_Changelist]/shelved.patch b/.idea/shelf/Uncommitted_changes_before_Update_at_11_08_2020,_22_16_[Default_Changelist]/shelved.patch new file mode 100644 index 0000000..e10d09b --- /dev/null +++ b/.idea/shelf/Uncommitted_changes_before_Update_at_11_08_2020,_22_16_[Default_Changelist]/shelved.patch @@ -0,0 +1,132 @@ +Index: main.js +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>const {asda,costco,budgens} = require(\"./marketplace\");\nconst {Market} = require(\"./Market\");\nconst { Buyer } = require(\"./Buyer\");\n\n\nfunction main(){ \n const market = new Market([asda,budgens,costco]);\n let buyer = new Buyer(market);\n let product = \"Apples\";\n let quantity = 10;\n buyerFunctions(product, quantity, buyer);\n // observeMarket(market);\n};\n\nfunction buyerFunctions(product, quantity, buyer){\n console.log(`The best price for ${product} is ${buyer.getBestPrice(product)}`) ;\n console.log(`To completely fill a order of ${quantity} ${product} costs ${buyer.completelyFill(product,quantity)}`) ;\n console.log(`To buy as quickly as possible ${quantity} ${product} costs ${buyer.quicklyFill(product,quantity)}`) ;\n\n}\n\nfunction observeMarket(market){\n market.observable.subscribe( (mkt) => {\n console.log(`The current price of apples are ${market.sellers[0].inventory[\"Apples\"].price}`)});\n}\n\nmain(); +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- main.js (revision e6b7c987d84ebdcf143c23238a39a662a5604309) ++++ main.js (date 1597155171121) +@@ -14,8 +14,8 @@ + + function buyerFunctions(product, quantity, buyer){ + console.log(`The best price for ${product} is ${buyer.getBestPrice(product)}`) ; +- console.log(`To completely fill a order of ${quantity} ${product} costs ${buyer.completelyFill(product,quantity)}`) ; +- console.log(`To buy as quickly as possible ${quantity} ${product} costs ${buyer.quicklyFill(product,quantity)}`) ; ++ // console.log(`To completely fill a order of ${quantity} ${product} costs ${buyer.completelyFill(product,quantity)}`) ; ++ // console.log(`To buy as quickly as possible ${quantity} ${product} costs ${buyer.quicklyFill(product,quantity)}`) ; + + } + +Index: .idea/workspace.xml +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n 1597136254764\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +--- .idea/workspace.xml (revision e6b7c987d84ebdcf143c23238a39a662a5604309) ++++ .idea/workspace.xml (date 1597180559733) +@@ -2,8 +2,8 @@ + + + +- +- ++ ++ + + +- ++ + + + +@@ -47,6 +48,19 @@ + + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -59,6 +73,7 @@ + + + ++ + + + +@@ -74,6 +89,7 @@ + 1597136254764 + + ++ + + + +@@ -81,22 +97,34 @@ + + +- +- ++ ++ + ++ ++ ++ ++ ++ + +- +- ++ ++ + ++ + +- +- ++ ++ + ++ + +- +- ++ ++ + ++ + ++ ++ ++ ++ + + + diff --git a/.idea/shelf/Uncommitted_changes_before_Update_at_11_08_2020__22_16__Default_Changelist_.xml b/.idea/shelf/Uncommitted_changes_before_Update_at_11_08_2020__22_16__Default_Changelist_.xml new file mode 100644 index 0000000..93d62ae --- /dev/null +++ b/.idea/shelf/Uncommitted_changes_before_Update_at_11_08_2020__22_16__Default_Changelist_.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 208ab54..bc1b403 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -1,10 +1,35 @@ + + + + + + + + + + + + + + + + + + + - + - + @@ -49,8 +81,21 @@ + + + + + + + + + + + + + - + @@ -59,10 +104,15 @@ + + + + + @@ -75,38 +125,75 @@ + + + - - + + - - - + + + - - - + + + + - - - + + + + - - - + + + + - - - + + + + - + + + + + + + + + diff --git a/Buyer.js b/Buyer.js index 2d67385..848fa7c 100644 --- a/Buyer.js +++ b/Buyer.js @@ -1,99 +1,190 @@ class Buyer { - constructor(market) { - this.market = market; - } + constructor(market) { + this.market = market; + } - /** - * Build an order preference table for particular product - * @param product - */ - buildOrderPreference(product) { - let sellers = []; + /** + * Build an order preference table for particular product + * @param product + */ + buildOrderPrefBestPrice(product) { + let sellers = []; - // build up the initial list - this.market.sellers.forEach((seller, i) => { - if (seller.inventory.hasOwnProperty(product)) { - const price = seller.quote(product); - sellers.push({id: seller.id, price: price, index: i, quantity:seller.inventory[product].quantity}) - } + // build up the initial list + this.market.sellers.forEach((seller, i) => { + if (seller.inventory.hasOwnProperty(product)) { + const price = seller.quote(product); + sellers.push({id: seller.id, price: price, index: i, quantity: seller.inventory[product].quantity}) + } - }); + }); - // sort by best price - sellers.sort((a, b) => { - return a.price - b.price; - }) + // sort by best price + sellers.sort((a, b) => { + return a.price - b.price; + }) - return sellers; - } - - /** - * This method should get the best price for a given product - * across all sellers - */ - getBestPrice(product) { - let lowestPrice = null; - this.market.sellers.forEach(seller => { - if (seller.inventory.hasOwnProperty(product)) { - const price = seller.quote(product); - if (lowestPrice === null || lowestPrice > price) lowestPrice = price; - } - - }); - - return lowestPrice || 0; - } + return sellers; + } - /** - * This method should optimise price when filling an order - * if the quantity is greater than any single seller can accomodate - * then the next cheapest seller should be used. - */ - fillWithBestPrices(product, quantity) { + buildOrderPrefFastFill(product) { + let sellers = []; - let wantedQuantity = quantity; - let sellerPreference = this.buildOrderPreference(product); + // build up the initial list + this.market.sellers.forEach((seller, i) => { + if (seller.inventory.hasOwnProperty(product)) { + const price = seller.quote(product); + sellers.push({id: seller.id, price: price, index: i, quantity: seller.inventory[product].quantity, deliveryWait:seller.deliveryWait}) + } - console.log(sellerPreference); + }); - let reciept = []; + // sort by best price + sellers.sort((a, b) => { + return a.deliveryWait - b.deliveryWait; + }) - while (sellerPreference.length > 0 && wantedQuantity > 0) { + return sellers; + } - let seller = sellerPreference.shift(); + buildOrderPrefMostFill(product) { + let sellers = []; - let r = this.market.sellers[seller.index].sell(product, wantedQuantity); - wantedQuantity = (wantedQuantity - r.boughtQuantity) < 0 ? 0 : (wantedQuantity - r.boughtQuantity); + // build up the initial list + this.market.sellers.forEach((seller, i) => { + if (seller.inventory.hasOwnProperty(product)) { + const price = seller.quote(product); + sellers.push({id: seller.id, price: price, index: i, quantity: seller.inventory[product].quantity, deliveryWait:seller.deliveryWait}) + } - reciept.push(r); + }); + + // sort by best price + sellers.sort((a, b) => { + return b.quantity - a.quantity; + }) + + return sellers; + } + + /** + * This method should get the best price for a given product + * across all sellers + */ + getBestPrice(product) { + let lowestPrice = null; + this.market.sellers.forEach(seller => { + if (seller.inventory.hasOwnProperty(product)) { + const price = seller.quote(product); + if (lowestPrice === null || lowestPrice > price) lowestPrice = price; + } + + }); + + return lowestPrice || 0; + } + + + /** + * This method should optimise price when filling an order + * if the quantity is greater than any single seller can accomodate + * then the next cheapest seller should be used. + */ + fillWithBestPrices(product, quantity) { + + let total = 0; + let wantedQuantity = quantity; + let sellerPreference = this.buildOrderPrefBestPrice(product); + + let receipt = []; + + while (sellerPreference.length > 0 && wantedQuantity > 0) { + + let seller = sellerPreference.shift(); + + let r = this.market.sellers[seller.index].sell(product, wantedQuantity); + wantedQuantity = (wantedQuantity - r.boughtQuantity) < 0 ? 0 : (wantedQuantity - r.boughtQuantity); + + receipt.push(r); + } + + if (receipt.length > 1) { + total = receipt.reduce((a, cv) => { + return (typeof a === 'number' ? a : a.cost) + cv.cost; + }); + } else if (receipt.length === 1) total = receipt[0].cost; + + return total; + } + + + /** + * This method should optimise for sellers with the largest inventory when filling an order + * if the quantity is greater than any single seller can accomodate + * then the next largest seller should be used. + * if multiple sellers have the same amount of inventory + * you should use the cheaper of the two. + */ + fillWithLargestSellers(product, quantity) { + let total = 0; + let wantedQuantity = quantity; + let sellerPreference = this.buildOrderPrefMostFill(product); + + let receipt = []; + + while (sellerPreference.length > 0 && wantedQuantity > 0) { + + let seller = sellerPreference.shift(); + + let r = this.market.sellers[seller.index].sell(product, wantedQuantity); + wantedQuantity = (wantedQuantity - r.boughtQuantity) < 0 ? 0 : (wantedQuantity - r.boughtQuantity); + + receipt.push(r); + } + + if (receipt.length > 1) { + total = receipt.reduce((a, cv) => { + return (typeof a === 'number' ? a : a.cost) + cv.cost; + }); + } else if (receipt.length === 1) total = receipt[0].cost; + + return total; } - console.log(reciept); + /** + * This fulfils orders based on time to deliver + * @param product + * @param quantity + * @returns {number} + */ + quicklyFill(product, quantity) { + let total = 0; + let wantedQuantity = quantity; + let sellerPreference = this.buildOrderPrefFastFill(product); - console.log(reciept.reduce((a, cv) => { - return a.cost + cv.cost; - }) + let receipt = []; + while (sellerPreference.length > 0 && wantedQuantity > 0) { - ); + let seller = sellerPreference.shift(); - throw Error("Not Implemented"); + let r = this.market.sellers[seller.index].sell(product, wantedQuantity); + wantedQuantity = (wantedQuantity - r.boughtQuantity) < 0 ? 0 : (wantedQuantity - r.boughtQuantity); - } + receipt.push(r); + } + if (receipt.length > 1) { + total = receipt.reduce((a, cv) => { + return (typeof a === 'number' ? a : a.cost) + cv.cost; + }); + } else if (receipt.length === 1) total = receipt[0].cost; - /** - * This method should optimise for sellers with the largest inventory when filling an order - * if the quantity is greater than any single seller can accomodate - * then the next largest seller should be used. - * if multiple sellers have the same amount of inventory - * you should use the cheaper of the two. - */ - fillWithLargestSellers(product, quantity) { - throw Error("Not Implemented"); - } + return total; + + } } module.exports = {Buyer} diff --git a/Seller.js b/Seller.js index 67d9134..fd90440 100644 --- a/Seller.js +++ b/Seller.js @@ -36,6 +36,7 @@ class Seller { const ec = getExpectedChange(this.random_generator); const alpha = inventory.startingQuantity const beta = inventory.quantity + // console.log(`${this.id} alpha: ${alpha} // beta: ${beta} // ec: ${ec}`); const inv_based_change = Math.log10(beta / alpha) * (-v); const sentimentChange = inv_based_change + ((ec - 0.5)*v) return sentimentChange; @@ -53,7 +54,6 @@ class Seller { tick() { - console.log('tick', this); for (let [product, value] of Object.entries(this.inventory)) { let inventory = value; const isReadyForDelivery = (inventory.priceHistory.length % this.deliveryWait) == 0; diff --git a/main.js b/main.js index 31ce18c..563cbf3 100644 --- a/main.js +++ b/main.js @@ -9,12 +9,12 @@ function main(){ let product = "Apples"; let quantity = 10; buyerFunctions(product, quantity, buyer); - // observeMarket(market); + observeMarket(market); }; function buyerFunctions(product, quantity, buyer){ console.log(`The best price for ${product} is ${buyer.getBestPrice(product)}`) ; - console.log(`To completely fill a order of ${quantity} ${product} costs ${buyer.completelyFill(product,quantity)}`) ; + console.log(`To completely fill a order of ${quantity} ${product} costs ${buyer.fillWithBestPrices(product,quantity)}`) ; console.log(`To buy as quickly as possible ${quantity} ${product} costs ${buyer.quicklyFill(product,quantity)}`) ; } diff --git a/tests/buyer.test.js b/tests/buyer.test.js index 0ae2140..5693e71 100644 --- a/tests/buyer.test.js +++ b/tests/buyer.test.js @@ -47,19 +47,19 @@ describe("Buyer", function () { it("fill 50 apples", () => { let buyer = new Buyer(market); - expect(buyer.fillWithBestPrices('Apples', 50)).toEqual(0); + expect(buyer.fillWithBestPrices('Apples', 50)).toEqual(233.60268569487857); }); - it("fill 100 apples", () => { + it("unable to fill 10 Kumquat", () => { let buyer = new Buyer(market); - expect(buyer.fillWithBestPrices('Apples', 100)).toEqual(0); + expect(buyer.fillWithBestPrices('Kumquat', 10)).toEqual(0); }); - it("fill 1000 apples", () => { + it("Large fill 50 apples", () => { let buyer = new Buyer(market); - expect(buyer.fillWithBestPrices('Apples', 100)).toEqual(0); + expect(buyer. fillWithLargestSellers('Apples', 50)).toEqual(312.5); });