NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name FTX Custom Theme // @namespace http://github.com/tradersamwise/ // @version 1.1.1 // @description Custom theme for FTX // @author @TraderSamwise // @match https://ftx.com/* // @icon https://www.google.com/s2/favicons?domain=tampermonkey.net // @grant none // @license MIT // ==/UserScript== const COMPACT_TABLE = true; const SQUARE_BUTTONS = true; const SQUARE_CARDS = true; const DISABLE_CELL_WRAPPING = true; const BACKGROUND_COLOR = '#587369'; const TABLE_BODY_COLOR = '#273a3a'; const TABLE_HEADER_COLOR = '#0a1f1f'; const SHOW_BTC_PNL = true; const BTC_PNL_PRECISION = 4; const BTC_SUFFIX = "₿"; const SHOW_PNL_PERCENT = true; const PNL_PERCENT_PRECISION = 2; // set specific style function addGlobalStyle(css) { var head, style; head = document.getElementsByTagName('head')[0]; if (!head) { return; } style = document.createElement('style'); style.type = 'text/css'; style.innerHTML = css; head.appendChild(style); } // reference btc price let btcPrice; // fetch the last btc price function fetchBtcPrice() { fetch('https://ftx.com/api/markets/BTC-PERP') .then(response => response.json()) .then(data => { btcPrice = data.result.price; }).catch((error) => { console.log(error); }); // recheck every 10 minutes setTimeout(fetchBtcPrice, 1000 * 60 * 10); } // US Number Formatter var formatter = new Intl.NumberFormat(undefined, { style: 'currency', currency: 'USD', // These options are needed to round to whole numbers if that's what you want. //minimumFractionDigits: 0, // (this suffices for whole numbers, but will print 2500.10 as $2,500.1) //maximumFractionDigits: 0, // (causes 2500.99 to be printed as $2,501) }); // function to parse USD notional string value for all possible locals and formats function parseNotionalString(val) { val = val.replace(/\./g, ''); val = val.replace(/,/g, ''); val = val.replace(/US\$/g, ''); val = val.replace(/USD/g, ''); val = val.replace(/ /g, ''); val = val.replace(/\$/g, ''); val = val.trim() val = val.substr(0, val.length - 2) + "." + val.substr(val.length - 2, val.length); val = parseFloat(val); return val; } // whether we are currently updating the table let updating = false; // converts pnl for all rows and updates the cells and the top header row function convertPnlHelper() { let rows = document.getElementsByClassName("MuiTableBody-root")[3].children; let totalUsdPnl = 0; let totalNotional = 0; for (var i = 0; i < rows.length; i++) { let pnlCell = rows[i].children[6].children[0]; let formattedPnl = pnlCell.innerText; // USED FOR TESTING! DO NOT DELETE // if (!formattedPnl.includes("|")) { // formattedPnl = formattedPnl.replace(/\./g, '_'); // formattedPnl = formattedPnl.replace(/,/g, '.'); // formattedPnl = formattedPnl.replace(/_/g, ','); // formattedPnl = formattedPnl.replace(/\$/g, 'US$ '); // pnlCell.innerText = formattedPnl; // } let rawPnl = formattedPnl.split("|")[0]; rawPnl = parseNotionalString(rawPnl) totalUsdPnl += rawPnl; let percentagePnl; if (SHOW_PNL_PERCENT) { let formattedNotionalSize = rows[i].children[3].innerText; let rawNotionalSize = parseNotionalString(formattedNotionalSize); percentagePnl = (rawPnl / rawNotionalSize * 100).toFixed(PNL_PERCENT_PRECISION); totalNotional += rawNotionalSize; } if (!formattedPnl.includes("|")) { let btcPnl = (rawPnl / btcPrice); let formattedPnlBtc = btcPnl.toFixed(BTC_PNL_PRECISION) pnlCell.innerHTML = pnlCell.innerText + "  |  " + formattedPnlBtc + " " + BTC_SUFFIX; if (SHOW_PNL_PERCENT) { pnlCell.innerHTML += "  |  " + percentagePnl + '%'; } pnlCell.style["white-space"] = "nowrap"; } } // if we have any open positions, add total to row header if (rows.length > 0) { let formattedBtcTotal = (totalUsdPnl / btcPrice).toFixed(BTC_PNL_PRECISION) + " " + BTC_SUFFIX; let formattedUsdTotal = formatter.format(totalUsdPnl); let pnlRowHeader = document.getElementsByClassName("MuiTableRow-head")[3].children[6]; pnlRowHeader.style["padding-top"] = "5px"; pnlRowHeader.style["padding-bottom"] = "5px"; let pnlColor = "#02C77A"; if (totalUsdPnl < 0) { pnlColor = "#FF3B69" } if (SHOW_PNL_PERCENT) { let totalPercentagePnl = (totalUsdPnl / totalNotional * 100).toFixed(PNL_PERCENT_PRECISION); pnlRowHeader.innerHTML = "<span style=\"white-space: nowrap; font-size: 0.875rem; font-weight: 700; color: " + pnlColor + "; \"> " + formattedUsdTotal + "  |  " + formattedBtcTotal + "  |  " + totalPercentagePnl + "% </span>"; } else { pnlRowHeader.innerHTML = "<span style=\"white-space: nowrap; font-size: 0.875rem; font-weight: 700; color: " + pnlColor + "; \"> " + formattedUsdTotal + "  |  " + formattedBtcTotal + "</span>"; } } } // iterate over the rows and convert pnl function convertPnl() { // only update once we have fetched btc price if (!updating && btcPrice) { updating = true; const table = document.getElementsByClassName("MuiTableBody-root")[3]; table.removeEventListener("DOMSubtreeModified", convertPnl); try { convertPnlHelper(); } catch (error) { console.log(error); } table.addEventListener("DOMSubtreeModified", convertPnl); updating = false; } } // set the styles according to preferences (function () { 'use strict'; COMPACT_TABLE && addGlobalStyle('.MuiTableCell-root {padding-top: 0px; padding-bottom: 0px;}'); SQUARE_BUTTONS && addGlobalStyle('.MuiButton-root {border-radius: 0px;}'); SQUARE_CARDS && addGlobalStyle('.MuiPaper-rounded {border-radius: 0px;}'); DISABLE_CELL_WRAPPING && addGlobalStyle('.MuiTableCell-body {white-space: nowrap;}'); // set background color addGlobalStyle('.react-grid-layout {background-color: ' + BACKGROUND_COLOR + ';}'); addGlobalStyle('.jss11 {background-color: ' + BACKGROUND_COLOR + ';}'); // set table body color addGlobalStyle('.MuiPaper-root {background-color: ' + TABLE_BODY_COLOR + ';}'); // set table header color addGlobalStyle('.MuiAppBar-root {background-color: ' + TABLE_HEADER_COLOR + ';}'); addGlobalStyle('.jss305 {background-color: ' + TABLE_HEADER_COLOR + ';}'); // fix toastr font color addGlobalStyle('.MuiSnackbarContent-message {color: white;}'); // show btc pnl if (SHOW_BTC_PNL) { fetchBtcPrice(); setTimeout(function () { const table = document.getElementsByClassName("MuiTableBody-root")[3]; table.addEventListener("DOMSubtreeModified", convertPnl); convertPnl(); document.getElementsByClassName("MuiButtonBase-root MuiTab-root MuiTab-textColorInherit Mui-selected MuiTab-fullWidth")[3].children[0].addEventListener("click", function () { setTimeout(function () { convertPnl(); }, 200) }); }, 3000); } })();