Skip to content

Instantly share code, notes, and snippets.

@zetavg
Last active October 10, 2022 04:25
Show Gist options
  • Save zetavg/3069787e24718add60d3d730d46bbfd4 to your computer and use it in GitHub Desktop.
Save zetavg/3069787e24718add60d3d730d46bbfd4 to your computer and use it in GitHub Desktop.
Script to import Firstrade positions into Yahoo Finance as Portfolio.
/*
* 1. Sign in to Firstrade's positions page (https://invest.firstrade.com/cgi-bin/main#/cgi-bin/acctpositions).
* 2. Open DevTools (Command + Option + i) JS console (Esc), paste the code below. Then stuff will be copied.
* 3. Open a new text file, paste the copied content into it and save it as *.csv.
* 4. Go to the Portfolios page on Yahoo Finance (https://finance.yahoo.com/portfolios), click "Import" and upload the file created on step 3.
* 5. You now have a new portfolio imported from your Firstrade positions data (you might want to rename it).
*/
function getPositionTable() {
return document.getElementById('positiontable');
};
// TODO: More languages
const COLUMN_NAME_MAP = {
symbol: ['Symbol', '代號'],
quantity: ['Quantity', '股數'],
unitCost: ['Unit Cost', '單位成本'],
};
function getDataIndexMap(positionTable) {
const columnNames = Array
.from(
positionTable
.getElementsByTagName('thead')[0]
.getElementsByTagName('tr')[0]
.children
)
.map(e => e.innerText);
return columnNames.reduce((map, displayName, index) => {
const [name] = Object.entries(COLUMN_NAME_MAP)
.find(([, displayNames]) => displayNames.includes(displayName)) || [];
if (name) {
map[name] = index;
}
return map;
}, {});
}
function objMap(obj, func) {
return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, func(k, v)]));
}
function getPositions(positionTable) {
const dataIndexMap = getDataIndexMap(positionTable);
const dataRows = Array
.from(
positionTable
.getElementsByTagName('tbody')[0]
.children
)
.map(tr => Array.from(tr.getElementsByTagName('td')).map(td => td.innerText.trim()));
return dataRows.map(row => objMap(dataIndexMap, (name, index) => row[index]));
}
function getPositionsCsv(positionTable) {
const data = getPositions(positionTable);
const csvLines = data.map(d => `${d.symbol},,,,,,,,,,${d.unitCost.replace(/,/mg, '')},${d.quantity.replace(/,/mg, '')},,,,Imported`);
const csvHead = 'Symbol,Current Price,Date,Time,Change,Open,High,Low,Volume,Trade Date,Purchase Price,Quantity,Commission,High Limit,Low Limit,Comment';
return csvHead + '\n' + csvLines.join('\n');
}
function copyPositionsCsv() {
copy(getPositionsCsv(getPositionTable()));
}
copyPositionsCsv();
@jimytc
Copy link

jimytc commented Dec 5, 2021

The result would have issue if unitCost and quantity are more than 1000.
It's because the text would be 1,000.
This would leads to two cell due to the extra ,.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment