From 278fc8bcbdd9ca8d7f4fe34c0b4964ff954189a3 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Thu, 2 Dec 2021 05:26:01 +0530 Subject: [PATCH] Price updation - Update current_price when no seller or buyer available. - Inc price only when sell-orders from rated sellers are available. - User must buy a minimum FLO before placing a sell order unless they are a 'Miner'. --- src/group.js | 18 +++++--- src/market.js | 124 ++++++++++++++++++++++++-------------------------- src/price.js | 121 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 70 deletions(-) create mode 100644 src/price.js diff --git a/src/group.js b/src/group.js index f6da65f..33e1015 100644 --- a/src/group.js +++ b/src/group.js @@ -40,12 +40,18 @@ const bestPair = function(cur_rate, tags_buy, tags_sell) { }); this.get = () => new Promise((resolve, reject) => { - Promise.all([getBuyOrder(), getSellOrder()]).then(results => { - resolve({ - buyOrder: results[0], - sellOrder: results[1], - null_base: getSellOrder.cache.mode_null - }) + Promise.allSettled([getBuyOrder(), getSellOrder()]).then(results => { + if (results[0].status === "fulfilled" && results[1].status === "fulfilled") + resolve({ + buyOrder: results[0].value, + sellOrder: results[1].value, + null_base: getSellOrder.cache.mode_null + }) + else + reject({ + buy: results[0].reason, + sell: results[1].reason + }) }).catch(error => reject(error)) }); diff --git a/src/market.js b/src/market.js index 3044b70..bc913cc 100644 --- a/src/market.js +++ b/src/market.js @@ -1,6 +1,7 @@ const group = require("./group"); +const price = require("./price"); +const MINIMUM_BUY_REQUIREMENT = 0.1; -var net_FLO_price; //container for FLO price (from API or by model) var DB; //container for database const tokenAPI = { @@ -52,46 +53,8 @@ const tokenAPI = { } } -function getRates() { - return new Promise((resolve, reject) => { - getRates.FLO_USD().then(FLO_rate => { - getRates.USD_INR().then(INR_rate => { - net_FLO_price = FLO_rate * INR_rate; - console.debug('Rates:', FLO_rate, INR_rate, net_FLO_price); - resolve(net_FLO_price); - }).catch(error => reject(error)) - }).catch(error => reject(error)) - }); -} - -getRates.FLO_USD = function() { - return new Promise((resolve, reject) => { - fetch('https://api.coinlore.net/api/ticker/?id=67').then(response => { - if (response.ok) { - response.json() - .then(result => resolve(result[0].price_usd)) - .catch(error => reject(error)); - } else - reject(response.status); - }).catch(error => reject(error)); - }); -} - -getRates.USD_INR = function() { - return new Promise((resolve, reject) => { - fetch('https://api.exchangerate-api.com/v4/latest/usd').then(response => { - if (response.ok) { - response.json() - .then(result => resolve(result.rates['INR'])) - .catch(error => reject(error)); - } else - reject(response.status); - }).catch(error => reject(error)); - }); -} - function returnRates() { - return net_FLO_price; + return price.currentRate; } function addSellOrder(floID, quantity, min_price) { @@ -102,23 +65,40 @@ function addSellOrder(floID, quantity, min_price) { return reject(INVALID(`Invalid quantity (${quantity})`)); else if (typeof min_price !== "number" || min_price <= 0) return reject(INVALID(`Invalid min_price (${min_price})`)); - DB.query("SELECT SUM(quantity) AS total FROM Vault WHERE floID=?", [floID]).then(result => { - let total = result.pop()["total"] || 0; - if (total < quantity) - return reject(INVALID("Insufficient FLO")); - DB.query("SELECT SUM(quantity) AS locked FROM SellOrder WHERE floID=?", [floID]).then(result => { - let locked = result.pop()["locked"] || 0; - let available = total - locked; - if (available < quantity) - return reject(INVALID("Insufficient FLO (Some FLO are locked in another sell order)")); - DB.query("INSERT INTO SellOrder(floID, quantity, minPrice) VALUES (?, ?, ?)", [floID, quantity, min_price]) - .then(result => resolve("Added SellOrder to DB")) - .catch(error => reject(error)); + checkSellRequirement().then(_ => { + DB.query("SELECT SUM(quantity) AS total FROM Vault WHERE floID=?", [floID]).then(result => { + let total = result.pop()["total"] || 0; + if (total < quantity) + return reject(INVALID("Insufficient FLO")); + DB.query("SELECT SUM(quantity) AS locked FROM SellOrder WHERE floID=?", [floID]).then(result => { + let locked = result.pop()["locked"] || 0; + let available = total - locked; + if (available < quantity) + return reject(INVALID("Insufficient FLO (Some FLO are locked in another sell order)")); + DB.query("INSERT INTO SellOrder(floID, quantity, minPrice) VALUES (?, ?, ?)", [floID, quantity, min_price]) + .then(result => resolve("Added SellOrder to DB")) + .catch(error => reject(error)); + }).catch(error => reject(error)); }).catch(error => reject(error)); }).catch(error => reject(error)); }); } +function checkSellRequirement(floID) { + return new Promise((resolve, reject) => { + DB.query("SELECT * FROM Tags WHERE floID=? AND tag=?", [floID, "MINER"]).then(result => { + if (result.length) + return resolve(true); + DB.query("SELECT SUM(quantity) AS brought FROM Transactions WHERE floID=?", [floID]).then(result => { + if (result[0].brought >= MINIMUM_BUY_REQUIREMENT) + resolve(true); + else + reject(INVALID(`Sellers required to buy atleast ${MINIMUM_BUY_REQUIREMENT} FLO before placing a sell order unless they are a Miner`)); + }).catch(error => reject(error)) + }).catch(error => reject(error)) + }) +} + function addBuyOrder(floID, quantity, max_price) { return new Promise((resolve, reject) => { if (!floID || !floCrypto.validateAddr(floID)) @@ -171,9 +151,11 @@ function cancelOrder(type, id, floID) { } function initiateCoupling() { - group.getBestPairs(net_FLO_price) - .then(bestPairQueue => processCoupling(bestPairQueue)) - .catch(error => console.error("initiateCoupling", error)) + price.getRates().then(cur_rate => { + group.getBestPairs(cur_rate) + .then(bestPairQueue => processCoupling(bestPairQueue)) + .catch(error => console.error("initiateCoupling", error)) + }).catch(error => reject(error)); } function processCoupling(bestPairQueue) { @@ -197,10 +179,26 @@ function processCoupling(bestPairQueue) { }).catch(error => console.error(error)); }).catch(error => console.error(error)); }).catch(error => { - if (error !== false) - console.error(error); - else - console.log("No valid orders."); + let noBuy, noSell; + if (error.buy === undefined) + noBuy = false; + else if (error.buy !== false) { + console.error(error.buy); + noBuy = null; + } else { + console.log("No valid buyOrders."); + noBuy = true; + } + if (error.sell === undefined) + noSell = false; + if (error.sell !== false) { + console.error(error.sell); + noSell = null; + } else { + console.log("No valid sellOrders."); + noSell = true; + } + price.noOrder(noBuy, noSell); }); } @@ -597,11 +595,8 @@ function confirmWithdrawalRupee() { } function periodicProcess() { - let old_rate = net_FLO_price; - getRates().then(cur_rate => { - transactionReCheck(); - initiateCoupling(); - }).catch(error => console.error(error)); + transactionReCheck(); + initiateCoupling(); } function transactionReCheck() { @@ -628,5 +623,6 @@ module.exports = { set DB(db) { DB = db; group.DB = db; + price.DB = db; } }; \ No newline at end of file diff --git a/src/price.js b/src/price.js new file mode 100644 index 0000000..dac5807 --- /dev/null +++ b/src/price.js @@ -0,0 +1,121 @@ +const MIN_TIME = 1 * 60 * 60 * 1000, + DOWN_RATE = 0.2 / 100, + UP_RATE = 0.5 / 100, + MAX_DOWN_PER_DAY = 4.8 / 100, + MAX_UP_PER_DAY = 12 / 100, + TOP_RANGE = 10 / 100; + +var DB; //container for database + +var netValue, //container for FLO price (from API or by model) + lastTxTime, //container for timestamp of the last tx + noBuyOrder, + noSellOrder + +var dayInitRate; +setInterval(() => dayInitRate = netValue, 24 * 60 * 60 * 1000); //reset the day price every 24 hrs + +//store FLO price in DB every 1 hr +setInterval(function storeRate() { + DB.query("INSERT INTO priceHistory (price) VALUE (?)", netValue) + .then(_ => null).catch(error => console.error(error)) +}) + +/* OLD FUNCTION +function getRates() { + return new Promise((resolve, reject) => { + getRates.FLO_USD().then(FLO_rate => { + getRates.USD_INR().then(INR_rate => { + net_FLO_price = FLO_rate * INR_rate; + console.debug('Rates:', FLO_rate, INR_rate, net_FLO_price); + resolve(net_FLO_price); + }).catch(error => reject(error)) + }).catch(error => reject(error)) + }); +} + +getRates.FLO_USD = function() { + return new Promise((resolve, reject) => { + fetch('https://api.coinlore.net/api/ticker/?id=67').then(response => { + if (response.ok) { + response.json() + .then(result => resolve(result[0].price_usd)) + .catch(error => reject(error)); + } else + reject(response.status); + }).catch(error => reject(error)); + }); +} + +getRates.USD_INR = function() { + return new Promise((resolve, reject) => { + fetch('https://api.exchangerate-api.com/v4/latest/usd').then(response => { + if (response.ok) { + response.json() + .then(result => resolve(result.rates['INR'])) + .catch(error => reject(error)); + } else + reject(response.status); + }).catch(error => reject(error)); + }); +} +*/ + +function getRates() { + return new Promise((resolve, reject) => { + let cur_time = Date.now(); + if (cur_time - lastTxTime < MIN_TIME) //Minimum time to update not crossed: No update required + resolve(netValue); + else if (noBuyOrder && noSellOrder) //Both are not available: No update required + resolve(netValue); + else if (noBuyOrder === null || noSellOrder === null) //An error has occured during last process: No update (might cause price to crash/jump) + resolve(netValue); + else if (noBuyOrder) { + //No Buy, But Sell available: Decrease the price + let tmp_val = netValue * (1 - DOWN_RATE); + if (tmp_val >= dayInitRate * (1 - MAX_DOWN_PER_DAY)) + netValue *= tmp_val; + resolve(netValue); + } else if (noSellOrder) { + //No Sell, But Buy available: Increase the price + checkForRatedSellers().then(result => { + if (result) { + let tmp_val = netValue * (1 + UP_RATE) + if (tmp_val >= dayInitRate * (1 + MAX_UP_PER_DAY)) + netValue *= tmp_val; + } + }).catch(error => console.error(error)).finally(_ => resolve(netValue)); + } + }) +} + +function checkForRatedSellers() { + //Check if there are best rated sellers? + return new Promise((resolve, reject) => { + DB.query("SELECT MAX(sellPriority) as maxValue FROM TagList").then(result => { + let ratedMin = result[0].maxValue * (1 - TOP_RANGE); + DB.query("SELECT COUNT(*) as value FROM SellOrder WHERE floID IN (" + + " SELECT Tags.floID FROM Tags INNER JOIN TagList ON Tags.tag = TagList.tag" + + " WHERE TagList.sellPriority > ?)", [ratedMin]).then(result => { + resolve(result[0].value > 0); + }).catch(error => reject(error)) + }).catch(error => reject(error)) + }) +} + +module.exports = { + getRates, + set lastTxTime(t) { + lastTxTime = t; + }, + set noOrder(buy, sell) { + noBuyOrder = buy; + noSellOrder = sell; + }, + set DB(db) { + DB = db; + }, + get currentRate() { + return netValue + } +} \ No newline at end of file