Robinhood Trading History Markers

Provide trading history markers for Robinhood

// ==UserScript==
// @name         Robinhood Trading History Markers
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  Provide trading history markers for Robinhood
// @author       Alan Xu
// @match        https://robinhood.com/*
// @grant        window.onurlchange
// @require      http://code.jquery.com/jquery-3.5.1.min.js
// @require      https://cdn.jsdelivr.net/npm/js-cookie@rc/dist/js.cookie.min.js
// @require      https://gitcdn.xyz/repo/axemclion/jquery-indexeddb/8556df7fe15fdff8d1e4d55c2ebbbebec0032ed6/dist/jquery.indexeddb.min.js
// @require      https://gitcdn.xyz/repo/AlanDelip/svg-parser/618931881bb77621a1248a9c89863056f68a228e/svg-parser.min.js
// ==/UserScript==

!function(){"use strict";var t=window.jQuery;const e=["black","white"],i="rgb(0,200,5)",s="rgba(0,127,245,0.8)",n="rgba(255,80,0,0.8)",a=["1D","1W","1M","3M","1Y","5Y"],l="https://api.robinhood.com/marketdata/historicals",r='<div id="history-btn" style="display:flex;padding-bottom:10px;margin-left:10px;cursor:pointer;"><span style="font-weight:700;">History</span>\n<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"width="24" height="24" viewBox="0 0 510 510" fill="#fill" style="padding:2px;padding-bottom:7px;margin-left:3px;" xml:space="preserve"><g><g id="history"><path style="opacity:0.9;enable-background:new;" d="M267.75,12.75c-89.25,0-168.3,48.45-209.1,122.4L0,76.5v165.75h165.75l-71.4-71.4c33.15-63.75,96.9-107.1,173.4-107.1C372.3,63.75,459,150.45,459,255s-86.7,191.25-191.25,191.25c-84.15,0-153-53.55-181.05-127.5H33.15c28.05,102,122.4,178.5,234.6,178.5C402.9,497.25,510,387.6,510,255C510,122.4,400.35,12.75,267.75,12.75z M229.5,140.25V270.3l119.85,71.4l20.4-33.15l-102-61.2v-107.1H229.5z"/></g></svg></div>',o='<div id="no-history-btn" style="display:flex;padding-bottom:10px;margin-left:10px;"><span style="font-weight:700;">No History Registered</span>\n<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"width="24" height="24" viewBox="0 0 510 510" fill="#fill" style="padding:2px;padding-bottom:7px;margin-left:3px;" xml:space="preserve"><g><g id="history"><path style="opacity:0.9;enable-background:new;" d="M267.75,12.75c-89.25,0-168.3,48.45-209.1,122.4L0,76.5v165.75h165.75l-71.4-71.4c33.15-63.75,96.9-107.1,173.4-107.1C372.3,63.75,459,150.45,459,255s-86.7,191.25-191.25,191.25c-84.15,0-153-53.55-181.05-127.5H33.15c28.05,102,122.4,178.5,234.6,178.5C402.9,497.25,510,387.6,510,255C510,122.4,400.35,12.75,267.75,12.75z M229.5,140.25V270.3l119.85,71.4l20.4-33.15l-102-61.2v-107.1H229.5z"/></g></svg></div>',c='<div id="register-btn" style="display:flex;align-items:center;transform:translateY(-40px);cursor:pointer;"><span style="font-weight:700;">Register History</span>\n<svg version="1.1" id="_x31_" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" fill="#fill" style="padding:3px;margin-left:3px;"><path d="m15.5 24h-13c-1.379 0-2.5-1.122-2.5-2.5v-16c0-1.378 1.121-2.5 2.5-2.5h2c.276 0 .5.224.5.5s-.224.5-.5.5h-2c-.827 0-1.5.673-1.5 1.5v16c0 .827.673 1.5 1.5 1.5h13c.827 0 1.5-.673 1.5-1.5v-16c0-.827-.673-1.5-1.5-1.5h-2c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h2c1.379 0 2.5 1.122 2.5 2.5v16c0 1.378-1.121 2.5-2.5 2.5z"/><path d="m12.5 6h-7c-.827 0-1.5-.673-1.5-1.5v-2c0-.276.224-.5.5-.5h2.05c.232-1.14 1.243-2 2.45-2s2.218.86 2.45 2h2.05c.276 0 .5.224.5.5v2c0 .827-.673 1.5-1.5 1.5zm-7.5-3v1.5c0 .276.225.5.5.5h7c.275 0 .5-.224.5-.5v-1.5h-2c-.276 0-.5-.224-.5-.5 0-.827-.673-1.5-1.5-1.5s-1.5.673-1.5 1.5c0 .276-.224.5-.5.5z"/><path d="m22 21c-.189 0-.362-.107-.447-.276l-1.289-2.578c-.173-.346-.264-.732-.264-1.118v-9.028c0-1.103.897-2 2-2s2 .897 2 2v9.028c0 .386-.091.772-.264 1.118l-1.289 2.578c-.085.169-.258.276-.447.276zm0-14c-.552 0-1 .449-1 1v9.028c0 .231.055.464.158.671l.842 1.683.842-1.683c.103-.207.158-.44.158-.671v-9.028c0-.551-.448-1-1-1zm1.289 10.922h.01z"/><path d="m14.5 10h-11c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h11c.276 0 .5.224.5.5s-.224.5-.5.5z"/><path d="m14.5 13h-11c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h11c.276 0 .5.224.5.5s-.224.5-.5.5z"/><path d="m14.5 16h-11c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h11c.276 0 .5.224.5.5s-.224.5-.5.5z"/><path d="m14.5 19h-11c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h11c.276 0 .5.224.5.5s-.224.5-.5.5z"/></svg></div>',d='<div id="registered-btn" style="display:flex;align-items:center;transform:translateY(-40px);"><span style="font-weight:700;">History Registered</span>\n<svg id="_x31_" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" fill="#fill" style="padding:3px;margin-left:3px;"><path d="m17.5 24c-3.584 0-6.5-2.916-6.5-6.5s2.916-6.5 6.5-6.5 6.5 2.916 6.5 6.5-2.916 6.5-6.5 6.5zm0-12c-3.033 0-5.5 2.468-5.5 5.5s2.467 5.5 5.5 5.5 5.5-2.468 5.5-5.5-2.467-5.5-5.5-5.5z"/><path d="m16.5 20c-.132 0-.26-.053-.354-.146l-2-2c-.195-.195-.195-.512 0-.707s.512-.195.707 0l1.622 1.622 3.148-3.598c.182-.209.498-.229.706-.047s.229.497.047.705l-3.5 4c-.091.104-.222.166-.36.171-.005 0-.011 0-.016 0z"/><path d="m9.5 21h-7c-1.378 0-2.5-1.121-2.5-2.5v-13c0-1.379 1.122-2.5 2.5-2.5h2c.276 0 .5.224.5.5s-.224.5-.5.5h-2c-.827 0-1.5.673-1.5 1.5v13c0 .827.673 1.5 1.5 1.5h7c.276 0 .5.224.5.5s-.224.5-.5.5z"/><path d="m16.5 9c-.276 0-.5-.224-.5-.5v-3c0-.827-.673-1.5-1.5-1.5h-2c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h2c1.378 0 2.5 1.121 2.5 2.5v3c0 .276-.224.5-.5.5z"/><path d="m11.5 6h-6c-.827 0-1.5-.673-1.5-1.5v-2c0-.276.224-.5.5-.5h1.55c.233-1.14 1.242-2 2.45-2s2.217.86 2.45 2h1.55c.276 0 .5.224.5.5v2c0 .827-.673 1.5-1.5 1.5zm-6.5-3v1.5c0 .275.224.5.5.5h6c.276 0 .5-.225.5-.5v-1.5h-1.5c-.276 0-.5-.224-.5-.5 0-.827-.673-1.5-1.5-1.5s-1.5.673-1.5 1.5c0 .276-.224.5-.5.5z"/><path d="m13.5 9h-10c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h10c.276 0 .5.224.5.5s-.224.5-.5.5z"/><path d="m10.5 12h-7c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h7c.276 0 .5.224.5.5s-.224.5-.5.5z"/><path d="m8.5 15h-5c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h5c.276 0 .5.224.5.5s-.224.5-.5.5z"/></svg></div>';let h="",p=0,g=!1;function v(){let s=location.pathname.split("/")[1];switch(p=t("body").attr("class").includes("is-dark-theme")?1:0,s){case"stocks":var n=t("section[data-testid=ChartSection] nav div button");if(0==n.length){console.log("page not ready, re-analyze in 3s..."),setTimeout(v,3e3);break}var l=function(e){var i=0,s={};return e.each((t,e)=>{let i=function(t){let e=0,i=t.length,s=0;if(i>0)for(;s<i;)e=(e<<5)-e+t.charCodeAt(s++)|0;return e}(e.className);i in s?s[i].cnt+=1:(s[i]={cnt:0,idx:0},s[i].cnt=1),s[i].idx=t}),t.each(s,(t,e)=>{1==e.cnt&&(i=e.idx)}),console.log("current active tab: "+a[i]),a[i]}(n);if("1Y"==l&&0==t("#history-btn").length&&(t("section[data-testid=ChartSection] nav div a").before(r.replace("#fill",e[p])),t("#history-btn").click(()=>{t("#history-btn").css("color").replace(/ /g,"")!=i&&(f(),t("#history-btn").css("color",i),t("#history-btn svg").css("fill",i))})),g)break;n.click(function(){g=!0,l=t(this).find("span span").text(),console.log("current active tab: "+l),0==t("#no-history-btn").length&&("1Y"==l?0==t("#history-btn").length?(t("section[data-testid=ChartSection] nav div a").before(r.replace("#fill",e[p])),t("#history-btn").click(()=>{t("#history-btn").css("color").replace(/ /g,"")!=i&&(f(),t("#history-btn").css("color",i),t("#history-btn svg").css("fill",i))})):(t("#history-btn svg").css("fill",e[p]),t("#history-btn").css("color",e[p]).fadeIn()):(0!=t("#history-btn").length&&t("#history-btn").fadeOut(),t("g[data-type=marker]").remove(),t("defs[data-type=marker]").remove()))});break;case"history":t("header.main-container").append(c.replace("#fill",e[p])),t("#register-btn").click(()=>{m(),t("#register-btn").fadeOut(100,()=>{t("header.main-container").append(d.replace("#fill",e[p]))})})}}function f(){let i=localStorage.getItem(`${location.pathname.split("/")[2]}-history`);i?(i=JSON.parse(i),console.log("loaded history",i)):(console.log("no history to show"),t("#history-btn").fadeOut(100,()=>{t("section[data-testid=ChartSection] nav div a").before(o.replace("#fill",e[p]))}));var a=window.indexedDB.open("localforage",2);a.onerror=function(t){console.log("indexedDB error: "+t)},a.onsuccess=function(e){let r=a.result.transaction(["keyvaluepairs"]).objectStore("keyvaluepairs").get("reduxPersist:auth");r.onerror=function(t){console.log(t)},r.onsuccess=function(e){if(r.result){let e=JSON.parse(JSON.parse(r.result));h=e[1][17],t.ajax({type:"GET",url:l+`/${location.pathname.split("/")[2]}/?bounds=regular&interval=day&span=year`,dataType:"json",headers:{Authorization:"Bearer "+h},success:function(e){let a=e.historicals;t.each(a,(e,s)=>{let n=new Date(s.begins_at.replace("Z","-0800")).toLocaleDateString("en-us");if(i){let s="",a=1;t.each(i,(t,i)=>{let l=new Date(i.filledTs).toLocaleDateString("en-us");n==l&&(i.svg_index=e,l==s?i.level_index=a++:(s=l,a=1))})}}),function(e){let i=t("section[data-testid=ChartSection] svg g path[name=sparkline]"),a=[],l=2,r=i.eq(0).attr("d"),o=i.eq(1).attr("d");a=parse(r).concat(parse(o)),i.eq(0).attr("d").length>26&&(l=1);let c=`<defs data-type="marker"><filter id="shadowbuy"><feDropShadow dx="0" dy="0" stdDeviation="1" flood-color="${s}"/></filter><filter id="shadowsell"><feDropShadow dx="0" dy="0" stdDeviation="1" flood-color="${n}"/></filter></defs>`;t("section[data-testid=ChartSection] svg")[0].prepend(y(c)),t.each(e,(e,i)=>{if(!i.svg_index)return;let r=a[i.svg_index+l],o=i.cost,c=4;o>3e3&&o<=5e3?c=5:o>5e3&&o<=1e4?c=6:o>1e4&&(c=7);let d=i.direction,h="shadowbuy",p=s;"Sell"==d&&(p=n,h="shadowsell");let g=i.level_index,v=0;g&&(v=7*g);let f=y(`<g data-type="marker" id="${e}" transform="translate(${r.x}, ${r.y-v})"><circle cx="0" cy="0" r="${c}" fill="${p}" filter="url(#${h})"></circle></g>`);t("section[data-testid=ChartSection] svg g")[0].appendChild(f);let m=`marker_panel_${e}`;t(`section[data-testid=ChartSection] svg g[id=${e}]`).hover(function(){if(t(this).children("circle").css("transform","scale(1.7)"),0!=t(`#${m}`).length)return void t(`#${m}`).fadeIn(100);let e=`<div id="${m}" style="\n                       background: rgba(255, 255, 255,0.9);\n                       position: absolute;\n                       color: black;\n                       z-index:10;\n                       top: ${r.y-v}px;\n                       left: ${r.x+20}px;\n                       width: 230px;\n                       padding: 15px;\n                       box-shadow: rgba(0, 0, 0, 0.3) 5px 6px 11px;\n                      "><div><div>Type</div><div style="font-weight:700;font-size:1.2em">${i.type} ${d}</div>\n                             <div>Filled Time</div><div style="font-weight:700;font-size:1.2em">${new Date(i.filledTs).toLocaleString("en-us")}</div>\n                             <div>Quantity</div><div style="font-weight:700;font-size:1.2em">${i.quantity}</div>\n                             <div>Price</div><div style="font-weight:700;font-size:1.2em">${i.price}</div>\n                             <div>Total Cost</div><div style="font-weight:700;font-size:1.2em">${i.cost}</div></div></div>`;t("section[data-testid=ChartSection] header").siblings().eq(0).append(e)},function(){t(this).children("circle").css("transform","scale(1)"),t(`#${m}`).fadeOut(100)})})}(i)}})}}}}function y(t){var e=document.createElementNS("http://www.w3.org/1999/xhtml","div");e.innerHTML='<svg xmlns="http://www.w3.org/2000/svg">'+t+"</svg>";for(var i=document.createDocumentFragment();e.firstChild.firstChild;)i.appendChild(e.firstChild.firstChild);return i}function m(){var e=t("div.main-container .col-12 section div.grid-3"),i=[],s="";t.each(e,function(e,n){let a={},l=t(n).children();if(l.length>=9){s=l.eq(0).children().eq(2).text();let t=l.eq(1).children().eq(2).text(),e=t.split(" ")[0],n=t.split(" ")[1],r=l.eq(3).children().eq(2).text(),o=0,c=0,d=0,h=0,p=8,g=7,v=6;"market"!=e.toLowerCase()&&(p=9,g=8,v=7),d=parseFloat(l.eq(p).children().eq(2).text().substr(1).replace(/,/g,""));let f=l.eq(g).children().eq(2).text().split(" ");o=parseFloat(f[0].replace(/,/g,"")),c=parseFloat(f[3].substr(1).replace(/,/g,"")),h=new Date(`${r} ${l.eq(v).children().eq(2).text()}`).getTime(),a={symbol:s,type:e,direction:n,filledTs:h,quantity:o,price:c,cost:d},i.push(a)}}),s?(localStorage.setItem(`${s}-history`,JSON.stringify(i)),console.log("history updated!")):(console.log("page not ready, re-analyze in 3s..."),setTimeout(m,3e3))}let x=location.href;window.addEventListener("urlchange",t=>{if(x!=t.url){x=t.url;let e=t.url.split("/")[t.url.split("/").length-2];"stocks"!=e&&"history"!=e||setTimeout(v,2e3),"stocks"!=e&&(g=!1)}}),setTimeout(v,3e3)}();