Skip to content

Instantly share code, notes, and snippets.

@geekman
Last active August 29, 2015 14:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save geekman/2f06e2a58490253e42d9 to your computer and use it in GitHub Desktop.
Save geekman/2f06e2a58490253e42d9 to your computer and use it in GitHub Desktop.
user script to export Yahoo finance portfolio
// ==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