diff --git a/BINANCE.gs b/BINANCE.gs index 789bb29..8b8c2cb 100644 --- a/BINANCE.gs +++ b/BINANCE.gs @@ -49,9 +49,6 @@ function BINANCE(operation, range_or_cell, opts, force_refresh_cell) { if (BinDoOrdersTable().is(operation)) { return BinDoOrdersTable().run(range_or_cell, options); } - if (BinDoOrdersTableStats().is(operation)) { - return BinDoOrdersTableStats().run(range_or_cell, options); - } throw new Error("Unsupported operation given: '"+operation+"'"); } diff --git a/README.md b/README.md index 30f857b..52bb98e 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,8 @@ By using the `BINANCE()` formula in your spreadsheet, you can get data fetched f * Last [24h stats](#operation-stats24h-public) * Total [account assets](#operation-account-private) from Binance wallets (SPOT + CROSS + ISOLATED for now) * All current [open orders](#operation-ordersopen-private) (SPOT + CROSS + ISOLATED for now) -* Latest [done/finished orders](#operation-ordersdone-private) (SPOT for now) -* Historical [orders table + stats](#operation-orderstable-private) (SPOT + CROSS + ISOLATED for now) +* Latest [done/finished orders](#operation-ordersdone-private) (SPOT + CROSS + ISOLATED for now) +* Historical [orders table](#operation-orderstable-private) (SPOT + CROSS + ISOLATED for now) * Last data [update time](#operation-last_update-public) and current [add-on version](#operation-version-public) being used * ..and many more to come! @@ -136,13 +136,6 @@ Some operations are **private**, meaning they **do require a Binance API key** t * `=BINANCE("orders/open", "BTCUSDT")` Optionally you can give a **full ticker** to filter the results. * `=BINANCE("orders/open", "BTCUSDT", "headers: false")` Optionally you can give more options like not returning table headers. -### Operation: `"orders/done"` (private) -`=BINANCE("orders/done", A1:A3)` will return a list with your most recent (`10` per symbol by default) done/finished orders for given symbols from Binance. -* A single value like `"BTC"` or a range of values is **required**. Values must be simple symbols like `A1="BTC"`, `A2="ETH"` and `A3="LTC"`. -* `=BINANCE("orders/done", A1:A3, "BTC")` Optionally you can give a ticker to match against (defaults to `USDT`). -* `=BINANCE("orders/done", A1:A3, "ticker: BTC, headers: false, max: 100")` Optionally you can give more options like not returning table headers and fetching latest `100` orders per given symbol. -* Values for `max` allowed between `1` and `1000` (defaults to `10`). - ### Operation: `"orders/table"` (private) `=BINANCE("orders/table", MySheet!A1:A3)` will **transform** the current sheet into a **"table"** in where ALL historic done/finished orders will be periodically polled and stored for each given symbol from Binance (SPOT + CROSS + ISOLATED for now). * This formula **must always** be placed at `A1` in any new blank sheet into your spreadsheet. @@ -170,8 +163,15 @@ Some operations are **private**, meaning they **do require a Binance API key** t But if you have _MANY_ trading symbols, I don't know how well it could behave! The best option is to try to "merge" as much as you can in a single sheet with JUST the traded symbols for each one. -### Operation: `"orders/table/stats"` (private) -`=BINANCE("orders/table/stats", 'Orders Table'!A1)` _coming soon_ +### Operation: `"orders/done"` (private) +**IMPORTANT:** It now requires at least **ONE sheet** in the spreadsheet with the **`"orders/table"`** operation in it! +It will take **ALL** your sheets that have the **`"orders/table"`** operation (aka: "order table sheets") and it will summarize them for you in a single table. +* `=BINANCE("orders/done")` Will return a list with your most recent (`100` by default) done/finished orders from ALL `"orders/table"` sheets in the spreadsheet. + +* `=BINANCE("orders/done", "", "headers: false, max: 0")` Optionally you can give more options like not returning table headers and displaying ALL orders. +* Values for `max` allowed between `0` (unlimited/all) and `1000` (defaults to `100`). ## See it working live! diff --git a/tasks/do-account-info.gs b/tasks/do-account-info.gs index 5b87a7c..bfa50ee 100644 --- a/tasks/do-account-info.gs +++ b/tasks/do-account-info.gs @@ -99,10 +99,8 @@ function BinDoAccountInfo() { } function parseOverview(show_headers) { - const wallet = BinWallet(); const headers = ["Asset", "Free", "Locked", "Borrowed", "Interest", "Total", "Net"]; - - const assets = wallet.calculateAssets(); + const assets = BinWallet().calculateAssets(); const balances = Object.keys(assets).map(function (symbol) { const asset = assets[symbol]; return [ diff --git a/tasks/do-orders-done.gs b/tasks/do-orders-done.gs index d096cf5..c45db8d 100644 --- a/tasks/do-orders-done.gs +++ b/tasks/do-orders-done.gs @@ -2,9 +2,7 @@ * Runs the done orders script. */ function BinDoOrdersDone() { - const max_items = 10; // How many items to be fetched for each symbol by default - const delay = 250; // Delay between API calls in milliseconds - let lock_retries = 5; // Max retries to acquire lock + const max_items = 100; // How many items to be displayed by default /** * Returns this function tag (the one that's used for BINANCE function 1st parameter) @@ -28,10 +26,12 @@ function BinDoOrdersDone() { } /** - * Returns the most recent filled/done orders for given symbols (10 per symbol by default). + * Returns the most recent filled/done orders (100 by default) from ALL sheets that are "order tables" in the spreadsheet + * NOTE: It requires at least ONE sheet with the 'orders/table' operation in it! + * @TODO Add support to filter by `range_or_cell` and/or `ticker` option! * - * @param {["BTC","ETH"..]} range_or_cell REQUIRED! Will fetch recent done orders for given symbols only. - * @param options Ticker to match against (USDT by default) or an option list like "ticker: USDT, headers: false, max: 100" + * @param {["BTC","ETH"..]} range_or_cell Will fetch recent done orders for given symbols only. + * @param options Ticker to match against (USDT by default) or an option list like "ticker: USDT, headers: false, max: 0" * @return The list of all current done orders for all or given symbols/tickers. */ function run(range_or_cell, options) { @@ -45,65 +45,43 @@ function BinDoOrdersDone() { } } + // @TODO Implement `range_or_cell` and/or `ticker` filtering function execute(range_or_cell, options) { - const ticker_against = options["ticker"]; - const limit = _getMaxItems(options); // Get max items limit + // const ticker_against = options["ticker"]; Logger.log("[BinDoOrdersDone] Running.."); - if (!range_or_cell) { - throw new Error("A range with crypto names must be given!"); - } - const lock = BinUtils().getUserLock(lock_retries--); - if (!lock) { // Could not acquire lock! => Retry - return execute(range_or_cell, options); - } - const range = BinUtils().getRangeOrCell(range_or_cell) || []; - const opts = { - CACHE_TTL: 55, - "retries": range.length - }; - const data = range.reduce(function(rows, crypto) { - const qs = "limit="+limit+"&symbol="+crypto+ticker_against; - Utilities.sleep(delay); // Add some waiting time to avoid 418 responses! - const crypto_data = BinRequest(opts).get("api/v3/myTrades", qs, ""); - return rows.concat(crypto_data); - }, []); - - BinUtils().releaseLock(lock); + // const range = BinUtils().getRangeOrCell(range_or_cell) || []; + // const data = range.reduce(function(rows, asset) { + // return rows.concat(asset); + // }, []); + // const parsed = parse(data, options); + + // Get ALL the rows contained in ALL defined sheets as order tables! + const data = BinDoOrdersTable().getRows(); + if (!data.length) { + console.error("[BinDoOrdersDone] It seems that we didn't find any sheet in the spreadsheet with the 'orders/table' operation in it!"); + return [["- no results to display - WARNING: This operation requires at least ONE sheet in the spreadsheet with the 'orders/table' operation in it!"]]; + } const parsed = parse(data, options); + Logger.log("[BinDoOrdersDone] Returning "+data.length+" orders.."); Logger.log("[BinDoOrdersDone] Done!"); return parsed; } function parse(data, options) { - const header = ["#ID", "Date", "Pair", "Type", "Side", "Price", "Amount", "Commission", "Total"]; - const parsed = data.reduce(function(rows, order) { - const price = BinUtils().parsePrice(order.price); - const amount = parseFloat(order.qty); - const commission = BinUtils().parsePrice(order.commission); - const row = [ - order.orderId, - new Date(parseInt(order.time)), - order.symbol, - order.isMaker ? "LIMIT" : "STOP-LIMIT", - order.isBuyer ? "BUY" : "SELL", - price, - amount, - commission, - price*amount - ]; - rows.push(row); - return rows; - }, []); - - const sorted = BinUtils().sortResults(parsed, 1, true); + const header = ["Order #ID", "Date", "Pair", "Type", "Side", "Price", "Amount", "Commission", "Total"]; + const parsed = data.map(function(order) { + order.shift(); // Remove the first column (Trade #ID) + return order; + }); + let sorted = BinUtils().sortResults(parsed, 1, true); + const limit = parseInt(options["max"]||max_items); // Max items to display + if (limit > 0 && limit < sorted.length) { + sorted = sorted.slice(0, limit); + } return BinUtils().parseBool(options["headers"]) ? [header, ...sorted] : sorted; } - function _getMaxItems(options) { - return Math.max(1, Math.min(1000, parseInt(options["max"]||max_items))); // Cap between 1 and 1000 items per symbol - } - // Return just what's needed from outside! return { tag, diff --git a/tasks/do-orders-table-stats.gs b/tasks/do-orders-table-stats.gs deleted file mode 100644 index ece9cba..0000000 --- a/tasks/do-orders-table-stats.gs +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Runs the table orders stats script. - */ -function BinDoOrdersTableStats() { - /** - * Returns this function tag (the one that's used for BINANCE function 1st parameter) - */ - function tag() { - return "orders/table/stats"; - } - - /** - * Returns true if the given operation belongs to this code - */ - function is(operation) { - return operation === tag(); - } - - /** - * Returns this function period (the one that's used by the refresh triggers) - */ - function period() { - return BinScheduler().getSchedule(tag()) || "10m"; - } - - /** - * @TODO WIP! - */ - function run(orders_table_cell, options) { - Logger.log("[BinDoOrdersTableStats] Running.."); - // @TODO WIP! - Logger.log("[BinDoOrdersTableStats] Done!"); - - return [ - ["coming soon..!"] - ]; - } - - // Return just what's needed from outside! - return { - tag, - is, - period, - run - }; -} \ No newline at end of file diff --git a/tasks/do-orders-table.gs b/tasks/do-orders-table.gs index 7719180..11356f6 100644 --- a/tasks/do-orders-table.gs +++ b/tasks/do-orders-table.gs @@ -37,8 +37,7 @@ function BinDoOrdersTable() { * You may ONLY REMOVE records from the bottom of the sheet (as many as you want, even all of them). * * @param {["BTC","ETH"..]} range_or_cell REQUIRED! Will fetch ALL historic orders for given symbols only. - * @param options Ticker to match against (USDT by default) or an option list like "ticker: USDT, stats: false" - * @TODO Implement "stats: false" option + * @param options Ticker to match against (USDT by default) or an option list like "ticker: USDT" * @return The list of all orders for all or given symbols/tickers. */ function run(range_or_cell, options) { @@ -263,7 +262,7 @@ function BinDoOrdersTable() { sheet.getRange("E2:F2").mergeAcross(); // Set the table headers - const header = ["#ID", "Order #ID", "Date", "Pair", "Type", "Side", "Price", "Amount", "Commission", "Total"]; + const header = ["Trade #ID", "Order #ID", "Date", "Pair", "Type", "Side", "Price", "Amount", "Commission", "Total"]; sheet.getRange("A3:J3").setValues([header]); sheet.getRange("A2").setValue("Last poll:"); sheet.getRange("D2").setValue("Status:"); @@ -392,8 +391,8 @@ function BinDoOrdersTable() { const range = sheet.getRange(last_row+1, 1, dlen, last_col); range.setValues(data); - // Sort ALL sheet rows! - sheet.getRange(header_size+1, 1, sheet.getLastRow()-header_size, last_col).sort(3); + // Sort ALL sheet's rows! + _getSheetDataRange(sheet).sort(3); } function _setStatus(sheet, status) { @@ -441,6 +440,23 @@ function BinDoOrdersTable() { return PropertiesService.getScriptProperties().setProperty(ASSETS_PROP_NAME, JSON.stringify(updated_assets)); } + /** + * Get the FULL data range for given sheet + */ + function _getSheetDataRange(sheet) { + return sheet.getRange(header_size+1, 1, sheet.getLastRow()-header_size, sheet.getLastColumn()); + } + + /** + * Returns ALL the rows contained in ALL defined sheets as order tables + */ + function getRows() { + return _findSheets().reduce(function(rows, sheet) { // Go through each sheet found + const values = _getSheetDataRange(sheet).getValues(); + return values && values.length ? rows.concat(values) : rows; + }, []); + } + // Return just what's needed from outside! return { tag, @@ -448,6 +464,7 @@ function BinDoOrdersTable() { period, run, init, - execute + execute, + getRows }; } \ No newline at end of file diff --git a/ui/setup.gs b/ui/setup.gs index c9be8ad..9820932 100644 --- a/ui/setup.gs +++ b/ui/setup.gs @@ -167,14 +167,14 @@ function BinSetup() { if (!api_key) { ui.alert("Binance API Key is not set!", - "You just need a Binance API Key if you want open/done orders list.\n\n"+ - "It's NOT needed to get market prices and 24hr stats!", + "You just need a Binance API Key if you want to use private operations.\n\n"+ + "It's NOT needed to use public operations like current market prices and 24hr stats!", ui.ButtonSet.OK); } if (!api_secret) { ui.alert("Binance API Secret Key is not set!", - "You just need a Binance API Secret Key if you want open/done orders.\n\n"+ - "It's NOT needed to get market prices and 24hr stats!", + "You just need a Binance API Secret Key if you to use private operations.\n\n"+ + "It's NOT needed to use public operations like current market prices and 24hr stats!", ui.ButtonSet.OK); } }