Last active
October 10, 2022 04:25
-
-
Save zetavg/3069787e24718add60d3d730d46bbfd4 to your computer and use it in GitHub Desktop.
Script to import Firstrade positions into Yahoo Finance as Portfolio.
This file contains hidden or 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
/* | |
* 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(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The result would have issue if
unitCost
andquantity
are more than 1000.It's because the text would be
1,000
.This would leads to two cell due to the extra
,
.