GDax Display Gross Order Costs/Yields

Adds a button to the GDax crypto-exchange main view that calculates gross costs and yields of existing orders.

As of 2018-01-23. See the latest version.

// ==UserScript==
// @name         GDax Display Gross Order Costs/Yields
// @version      1.10
// @author       Simonthebrit
// @description  Adds a button to the GDax crypto-exchange main view that calculates gross costs and yields of existing orders.
// @namespace    GDaxTweaks
// @grant        none
// @include      *.gdax.com*
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js
// ==/UserScript==

// Specs
var delay = 5000; // 5-second delay while gdax interface loads
var grossPreText = 'GRS'; // Label to use to indicate our added gross values
var buttonText = 'CALCULATE GROSS (' + grossPreText + ')';
var buttonCSS = {
  "display" : "flex",
  "justify-content" : "center",
  "align-items" : "center",
  "margin-left" : "20px",
  "padding" : "3px 7px",
  "border" : "2px solid #788085",
  "border-radius" : "40px",
  "font-size" : "9px",
  "font-weight" : "bold",
  "color" : "#ced2d5",
  "cursor" : "pointer",
  "transition" : "background .2s ease-out"
};

// Start when main view is loaded
var checkExist = setInterval(function() {
   if ($('div:acp("PanelHeader_title-and-children")').length) {
      // Button for main view
      $(':acp("UserPanel_user-panel") :acp("UserPanel_user-history") :acp("PanelHeader_title-and-children")')
        .append('<div class="calcGrossButton">' + buttonText + '</div>');
      $('.calcGrossButton').css(buttonCSS);
      doIt();
      clearInterval(checkExist);
   }
}, 100); // check every 100ms

// Start when order/fill view is loaded
var checkExist2 = setInterval(function() {
   if ($('div:acp("ListView_header")').length) {
      // Button for "All Order" view
      $(':acp("ListView_header") :acp("OrderListHeader_title")')
        .after('<div class="calcGrossButton">' + buttonText + '</div>');
      $('.calcGrossButton').css(buttonCSS);

      // Button for "All Fills" page (this doesn't work yet)
      $(':acp("FillListHeader_fill-list-header") :acp("FillListHeader_download")')
        .after('<div class="calcGrossButton">' + buttonText + '</div>');
      $('.calcGrossButton').css(buttonCSS);
      doIt();
      clearInterval(checkExist2);
   }
}, 100); // check every 100ms

function doIt() {

  // When clicked
  $('.calcGrossButton').click(function(){
    
    var grs = [], addVal = 0, currentUSD = 0, net = 0;
    
    // Do 'orders' list ------------------
    $('ul:acp("OrderList_list") li:acp("OrderList_row")').each( function() {
      
        var $this = $(this);
        var market = $this.find(':acp("OrderList_order-price") span.whole').text() + 
            '.' + $this.find(':acp("OrderList_order-price") span.part').text();
        var wholeBTC = $this.find(':acp("OrderList_split-number") span.whole:first').text();
        var subNum = (wholeBTC.substr(0,1) == '≈') ? 2 : 0;
        var btc = $this.find(':acp("OrderList_order-size") :acp("OrderList_split-number") span.whole:first').text().substr(subNum) + 
            '.' + $this.find(':acp("OrderList_order-size") :acp("OrderList_split-number") span.part').text();
        var net = roundTo(market * btc, 2);
        //alert('market: ' + market + ', btc: ' + btc + ', multiplied: ' + (market * btc) + ', net: ' + net); //debug
        
        var color = $this.find(':acp("OrderList_order-tag")').is('[class*="buy"]') ? "rgba(255,105,57,.85)" : "rgba(132,247,102,.85)";
        var buyOrSell = $this.find(':acp("OrderList_order-tag")').is('[class*="buy"]') ? "-" : "+";
      
        if (!$this.hasClass('NetDone')){
            $this.find('div:acp("OrderList_order-price"):acp("OrderList_column") :acp("OrderList_split-number")')
              .append(" <br>(" + grossPreText + " <span style='color:" + color + ";'>" + buyOrSell + "</span> $<span style='color:" + color + "'>" + net + "</span>)");
        }
      
        $this.addClass('NetDone');
      
        net = parseFloat(net); 
        var addVal = (buyOrSell == '+') ? net : (net - (net*2)); 
      
        grs.push(addVal);
      
    });
    
    // Do 'fills' list ------------------
    $('ul:acp("FillList_list") li:acp("FillList_row")').each( function() {
      
        var $this = $(this);
        var market = $this.find(':acp("FillList_fill-price") span.whole').text() + 
            '.' + $this.find(':acp("FillList_fill-price") span.part').text();
        var wholeBTC = $this.find(':acp("FillList_split-number") span.whole:first').text();
        var subNum = (wholeBTC.substr(0,1) == '≈') ? 2 : 0;
        var btc = $this.find(':acp("FillList_fill-size") :acp("FillList_split-number") span.whole:first').text().substr(subNum) + 
            '.' + $this.find(':acp("FillList_fill-size") :acp("FillList_split-number") span.part').text();
        $this.find(':acp("FillList_fill-price")').css('white-space','inherit');
        var net = roundTo(market * btc, 2); 
        //alert('market: ' + market + ', btc: ' + btc + ', multiplied: ' + (market * btc) + ', net: ' + net); //debug
        
        var color = $this.find(':acp("FillList_fill-tag")').is('[class*="buy"]') ? "rgba(255,105,57,.85)" : "rgba(132,247,102,.85)";
        var buyOrSell = $this.find(':acp("FillList_fill-tag")').is('[class*="buy"]') ? "-" : "+";
      
        if (!$this.hasClass('NetDone')){
            $this.find(':acp("FillList_fill-price"):acp("FillList_column") :acp("FillList_split-number")')
              .append(" <br>(" + grossPreText + " <span style='color:" + color + ";'>" + buyOrSell + "</span> $<span style='color:" + color + ";'>" + net + "</span>)");
        }

        $this.addClass('NetDone');
      
    });
    
    // Tweak some styles for better display with the added info
    $(':acp("FillList_fill-list") :acp("FillList_row") :acp("FillList_fill-price")')
        .css('width','25%');
    $(':acp("FillList_fill-list") :acp("FillList_row") :acp("FillList_fill-time")')
        .css('width','20%');
    $(':acp("OrderList_order-list") :acp("OrderList_list") :acp("OrderList_order") :acp("OrderList_order-tag") :acp("OrderList_tag")')
        .css('height','27px');
    $(':acp("FillList_fill-list") :acp("FillList_list") :acp("FillList_fill") :acp("FillList_fill-tag") span')
        .css('height','27px');
    
    // Do total gross
    if (!$('.totalGRS').length) {
        $(':acp("BalanceInfo_currencies") :acp("BalanceInfo_spacer")').clone().appendTo(':acp("BalanceInfo_currencies")'); 
        $(':acp("BalanceInfo_currencies")').append('<div class="totalLabel" style="color:yellow">+ ORDERS</div>'); 
        $(':acp("BalanceInfo_balances") :acp("BalanceInfo_spacer")').clone().appendTo(':acp("BalanceInfo_balances")'); 
        $(':acp("BalanceInfo_balances") :acp("Tooltip_wrapper"):first').clone().addClass('totalGRS').appendTo(':acp("BalanceInfo_balances")');
    }
    var currentUSD = $(':acp("BalanceInfo_balances") span:acp("BalanceInfo_term-description"):first').text();
    currentUSD = parseFloat(currentUSD);
    $('.totalGRS span:acp("BalanceInfo_term-description")').css('color','yellow').html(currentUSD + grs.reduce(add, 0));
    
  });
  
}

function add(a, b) {
    return a + b;
}

// Declare :acp() custom jQuery selector. It finds individual class names beginning with 'string'
$(function(){
    $.expr[":"].acp = function(elem, index, m){
          var regString = '\\b' + m[3];
          var reg = new RegExp(regString, "g");
          return elem.className.match(reg);
    }
});

// Rounding
function roundTo(n, digits) {
    var negative = false;
    if (digits === undefined) {
        digits = 0;
    }
    if ( n < 0) {
      negative = true;
      n = n * -1;
    }
    var multiplicator = Math.pow(10, digits);
    n = parseFloat((n * multiplicator).toFixed(11));
    n = (Math.round(n) / multiplicator).toFixed(2);
    if ( negative ) {    
        n = (n * -1).toFixed(2);
    }
    return n;
}