Last active
August 29, 2015 14:14
-
-
Save geekman/2f06e2a58490253e42d9 to your computer and use it in GitHub Desktop.
user script to export Yahoo finance portfolio
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Yahoo! Finance Portfolio Export | |
// @description Adds a button that lets you export your portfolio | |
// @author Darell Tan | |
// @version 1.2 | |
// @namespace https://gist.github.com/geekman | |
// @match https://finance.yahoo.com/portfolio/pf_*/holdings/edit* | |
// @match https://*.finance.yahoo.com/portfolio/pf_*/holdings/edit* | |
// ==/UserScript== | |
(function() { | |
function isnum(str, char) { | |
return /^\d+$/.test(str.replace(char, '')) | |
} | |
function addcol(rowA, rowB, key) { | |
if (! rowB) | |
return rowA[key]; | |
return rowA[key] + rowB[key]; | |
} | |
function avgcol(rowA, rowB, key, weightKey) { | |
if (! rowB) | |
return rowA[key]; | |
return ((rowA[key] * rowA[weightKey]) + | |
(rowB[key] * rowB[weightKey])) / | |
(rowA[weightKey] + rowB[weightKey]); | |
} | |
function toCsv(rows) { | |
var csv = ''; | |
if (rows.length == 0) | |
return csv; | |
var keys = Object.keys(rows[0]); | |
rows.forEach(function(row) { | |
var rowData = []; | |
for (var i = 0; i < keys.length; i++) { | |
var v = row[keys[i]] || ''; | |
if (typeof v == 'string' && v.indexOf(',') > -1) | |
v = '"' + v + '"' | |
rowData.push(v); | |
} | |
csv += rowData.join() + '\n'; | |
}); | |
return csv; | |
} | |
function doExport() { | |
var rows = []; | |
[].forEach.call(document.querySelectorAll('.yfi-quotes-table table tr'), function(row) { | |
var rowObj = {}; | |
for (var i = 0, col; col = row.cells[i]; i++) { | |
var t = null; | |
var colname = col.className.replace(/^col-/g, ''); | |
// is cell a date? | |
[].forEach.call(col.querySelectorAll('select'), function(e) { | |
var sel = e.options[e.selectedIndex]; | |
t = (t || '') + sel.text + '-'; | |
}); | |
if (t) | |
t = t.substring(0, t.length - 1); | |
// is cell a textfield? | |
for (var j = 0, e; e = col.children[j]; j++) { | |
if (e.tagName == 'INPUT') { | |
var inpTyp = e.getAttribute('type') || 'text'; | |
if (inpTyp == 'hidden') { | |
continue; | |
} else if (inpTyp == 'text') { | |
t = e.value; | |
break; | |
} | |
} | |
} | |
// just grab text from cell | |
t = t || col.textContent; | |
// massage data | |
t = t.trim(); | |
if (isnum(t, ',')) { | |
t = parseInt(t.replace(',', '')); | |
} else if ((t.match(/\./g) || []).length == 1 && isnum(t, '.')) { | |
t = parseFloat(t); | |
} | |
rowObj[colname] = t; | |
}; | |
rows.push(rowObj); | |
}); | |
// aggregate rows based on symbol | |
var aggRows = {}; | |
var aggRowsArr = []; | |
rows.forEach(function(row, i) { | |
var sym = row['symbol']; | |
// don't bother including those not owned | |
if (row['shares_owned'] == '') | |
return; | |
aggRows[sym] = { | |
'symbol': sym, | |
'price_paid': avgcol(row, aggRows[sym], 'price_paid', 'shares_owned'), | |
'shares_owned': addcol(row, aggRows[sym], 'shares_owned'), | |
'commission': addcol(row, aggRows[sym], 'commission') | |
}; | |
// header row | |
if (i == 0) { | |
aggRowsArr.push(aggRows[sym]); | |
delete aggRows[sym]; | |
} | |
}); | |
for (var sym in aggRows) | |
aggRowsArr.push(aggRows[sym]); | |
// convert to CSV | |
var data = (document.querySelector('#export_consolidated').checked) ? | |
toCsv(aggRowsArr) : | |
toCsv(rows); | |
var dl = document.createElement('a'); | |
dl.setAttribute('href', 'data:text/csv;charset=utf-8,' + encodeURIComponent(data)); | |
dl.setAttribute('download', 'portfolio.csv'); | |
document.body.appendChild(dl); | |
dl.click(); | |
document.body.removeChild(dl); | |
} | |
var actionBar = document.querySelector('.actionbar'); | |
var btn = document.createElement('input'); | |
btn.setAttribute('type', 'button'); | |
btn.setAttribute('class', 'submit'); | |
btn.setAttribute('value', 'Export'); | |
btn.addEventListener('click', doExport, false); | |
actionBar.appendChild(btn); | |
var cbx = document.createElement('label'); | |
cbx.setAttribute('title', 'Consolidate entries of the same symbol into a single entry'); | |
cbx.innerHTML = '<input id="export_consolidated" type="checkbox">Consolidated'; | |
actionBar.appendChild(cbx); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment