NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Nutmeg // @namespace http://www.galloway.me.uk/ // @version 0.2.1 // @description Augment Nutmeg portfolio page // @author Matt Galloway // @match https://app.nutmeg.com/client/portfolio // @match https://app.nutmeg.com/client/portfolio/dig_deeper // @grant none // @license MIT // ==/UserScript== installAnnualisedRate(); // Converts an array of arrays in [key, value] form, to a dictionary function dataArrayToDictionary(dataArray) { var dictionary = {}; for(i = 0; i < dataArray.length; i++) { var value = dataArray[i]; dictionary[value[0]] = value[1]; } return dictionary; } function calculateAnnualisedRateSeries(data) { var fundValues = data.performance; var contributions = data.contributions; // For calculating YTD and past year rates var now = new Date(); var nowMinusYear = new Date(now); nowMinusYear.setFullYear(nowMinusYear.getFullYear() - 1); var nowStartOfYear = new Date(now.getFullYear(), 0, 1); // This will contain all of the data points as an array of arrays in the form [date, fundValue, contributions] var zippedData = []; var contributionsDictionary = dataArrayToDictionary(contributions); var lastDate = 0; for (i = 0; i < fundValues.length; i++) { fundValue = fundValues[i]; date = fundValue[0]; fundValueToday = fundValue[1]; contributionsToday = contributionsDictionary[date]; // Check it's the next day if (lastDate !== 0 && date != lastDate + 86400000) { // If it isn't the next day, let's start our array again // This must be consecutive days for the maths to work zippedData = []; } lastDate = date; zippedData.push([date, fundValueToday, contributionsToday]); } var summedContribution = 0; var dataRows = []; var hitMinusYearDate = false; var summedContributionMinusYear = 0; var minusYearPct = 0; var minusYearFundValueStart = 0; var minusYearContributionsTodayStart = 0; var hitYTDDate = false; var summedContributionYTD = 0; var ytdPct = 0; var ytdFundValueStart = 0; var ytdContributionsTodayStart = 0; for (i = 0; i < zippedData.length; i++) { thisData = zippedData[i]; date = thisData[0]; fundValueToday = thisData[1]; contributionsToday = thisData[2]; summedContribution += contributionsToday; if (date > nowMinusYear) { if (!hitMinusYearDate) { minusYearFundValueStart = fundValueToday; minusYearContributionsTodayStart = contributionsToday; hitMinusYearDate = true; } summedContributionMinusYear += contributionsToday; minusYearPct = 100 * (Math.pow((1 + ((fundValueToday - minusYearFundValueStart) - (contributionsToday - minusYearContributionsTodayStart)) / summedContributionMinusYear), 365.25) - 1); } if (date > nowStartOfYear) { if (!hitYTDDate) { ytdFundValueStart = fundValueToday; ytdContributionsTodayStart = contributionsToday; hitYTDDate = true; } summedContributionYTD += contributionsToday; ytdPct = 100 * (Math.pow((1 + ((fundValueToday - ytdFundValueStart) - (contributionsToday - ytdContributionsTodayStart)) / summedContributionYTD), 365.25) - 1); } dailyPct = 100 * (Math.pow((1 + (fundValueToday - contributionsToday) / summedContribution), 365.25) - 1); dataRows.push([date, dailyPct]); } return [dataRows, minusYearPct, ytdPct]; } function installAnnualisedRate() { var originalPerformanceChart = window.PerformanceChart; var originalMethod = originalPerformanceChart.prototype.chart_config; var newMethod = function(a, b, c){ console.log('starting'); var data = calculateAnnualisedRateSeries(b); var dataRows = data[0]; var minusYearPct = data[1]; var ytdPct = data[2]; console.log('finished with data:'); console.log(dataRows); var output = originalMethod(a, b, c); // Add a new series to the chart to show the annualised rate var newSeries = { threshold: null , type: "area", name: "Annualised Rate", connectNulls: !1, data: dataRows, marker: { fillColor: "white", symbol: "circle", lineColor: "#ff0000", lineWidth: 1 }, lineColor: "#ff0000", fillColor: "rgba(255, 0, 0, 0.2)" }; output.series.push(newSeries); // Replace the tooltip for hover with one that adds the annualised rate var tooltip = output.tooltip; var originalFormatter = tooltip.formatter; var newFormatter = function() { var originalTooltip = originalFormatter.call(this); var i = this.points.length - 1; return originalTooltip + "<br/>" + "Annualised Rate: " + this.points[i].y.toFixed(2) + "%"; }; tooltip.formatter = newFormatter; var chart = $('#' + a)[0]; var closestChartData = chart.closest('[id^="charts-data-"]'); var latestAnnualisedRate = ""; latestAnnualisedRate += "Latest annualised rate = " + dataRows[dataRows.length-1][1].toFixed(2) + "%"; latestAnnualisedRate += "<br/>"; latestAnnualisedRate += "-1 year annualised rate = " + minusYearPct.toFixed(2) + "%"; latestAnnualisedRate += "<br/>"; latestAnnualisedRate += "YTD annualised rate = " + ytdPct.toFixed(2) + "%"; var textNode = document.createElement("p"); textNode.innerHTML = latestAnnualisedRate; closestChartData.parentNode.insertBefore(textNode, closestChartData); return output; }; // Swizzle the chart_config method to our new one originalPerformanceChart.prototype.chart_config = function(a, b, c) { try { return newMethod(a, b, c); } catch (error) { console.log(error + "\nBut continuing with old behaviour."); return originalMethod(a, b, c); } }; }