Skip to content

Instantly share code, notes, and snippets.

@shashank291
Last active January 5, 2022 14:42
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shashank291/196165bd37ba89c266aeac79b122b31b to your computer and use it in GitHub Desktop.
Save shashank291/196165bd37ba89c266aeac79b122b31b to your computer and use it in GitHub Desktop.
Export Fidelity Stock Lots to TSV file

Description

I used the below script to export my Fidelity share lot information in one file. By default, Fidelity requires going to each individual stock page to get this info which can be painstaking.

Instructions

Fair warning that you should understand the code you're running, and I make no guarantees as to the correctness of the script or the data downloaded, and I assume no liabilities if you decide to run this code. Now that that's out of the way, to get your aggregate share lot information:

Go to your Individual account on Fidelity, under Positions tab, and open the console(Ctrl+Shift+J on most browsers). Then you can run the following script that will gather all your stock lot information and download it in a TSV file. I suggest you read through it and verify it as a common precaution. Here's the script:

const domParser = new DOMParser();
const stockInfo = {}
const separator = '\t';
const newline = '\n';
// Simple function to clean the text from any spaces and extra characters
const cleanText = text => text.replace(/\n+/g, " ").replace(/\t+/g, " ").replace(/<br>/g, " ").replace(/\s\s+/g, ' ').trim();

const headers = {'Stock Symbol': 0};
let lastHeaderIndex = 0;

// Function to generate the information for a single lot position page
function generateLotPositionInfo(response) {
    const html = response.substring(response.indexOf('<!DOCTYPE'),response.indexOf('</html>')+7);
    const doc = domParser.parseFromString(html, "text/html");
    const stockInfoTables = Array.from(doc.getElementsByClassName('uma-tla-table'));
    const stockSymbol = cleanText(doc.getElementsByClassName('symbol-info-table')[0].rows[1].children[0].innerText);
    const parsedRows = [];

    // One table for long term, one for short term
    stockInfoTables.forEach(stockInfoTable => {
        const dataHeaders = Array.from(stockInfoTable.rows[1].children).map(a=>cleanText(a.innerHTML));
        const dataIndexes = dataHeaders.map(headerText=> {
          if (headers[headerText]) {
            return headers[headerText];
          }
          headers[headerText] = ++lastHeaderIndex;
          return headers[headerText];
        });
        const lineLength = Object.keys(headers).length;
        Array.from(stockInfoTable.rows).slice(2).forEach(dataRow=> {
            const line = new Array(lineLength).fill('');
            line[0] = stockSymbol;
            Array.from(dataRow.children).forEach((a, index)=>{
              line[dataIndexes[index]] = cleanText(a.innerText);
            });
            parsedRows.push(line.join(separator));
        });
    });
    stockInfo[stockSymbol] = parsedRows.join(newline);
}

function downloadFileWithResults(filename, text) {
  const element = document.createElement('a');
  element.setAttribute('href', 'data:text/tsv;charset=utf-8,' + encodeURIComponent(text));
  element.setAttribute('download', filename);
  element.style.display = 'none';
  document.body.appendChild(element);
  element.click();
  document.body.removeChild(element);
}

// Click to expand all stock rows
Array.from(document.getElementsByClassName('normal-row')).forEach(a=>a.click())
// Click to open the "Purchase History/Lots" tab for each stock
Array.from(document.querySelectorAll('[id=positions-tabs-lots]')).forEach(a=>a.click())
// Get all the links for "Purchase Lots"
const lotPositionLinks = Array.from(document.querySelectorAll('.positions--lots-viewLotsInClassic a')).map(a=>a.href);
// Fetch all purchase lots links one by one and parse them
const allOperations = lotPositionLinks.reduce((lastOperation, link) => lastOperation.then(()=>fetch(link)).then(response => response.text()).then(response => generateLotPositionInfo(response)), Promise.resolve());

// Wait until all operations are done and print the results
allOperations.then(()=>{
    console.log('All done.');
    const headerRow = Object.keys(headers).sort((a, b) => headers[a] < headers[b]).join(separator);
    const output =Object.values(stockInfo).join(newline);
    const outputWithHeader=`${headerRow}${newline}${output}`;
    downloadFileWithResults("stocklots.tsv", outputWithHeader);
    console.log(outputWithHeader);
});

After you've run the script, it will download a file called "stocklots.tsv" that has all the lot information. You can optionally paste that data(from column A-D) into this Google Sheets template(make a clone of it) that has pivot tables that let you compare your short term and long term obligations for different holdings, as well as filter to stocks you've held for different periods of time. This can be helpful to calculate tax obligation if sold at end of year, for example, or just to know your tax obligations for each position(short term and long term) across different points in time.

As before, I make no claims regarding the correctness of this sheet and take no liabilities if you decide to use it. Feel free to inspect it yourself. Sheet link:

https://docs.google.com/spreadsheets/d/1Mn3r-TmfCI1htt4JzUN5q5UCGoImDegFftHd1x_QG0Y/edit?usp=sharing

Output

Sample row from TSV output: image

Sample Pivot Table in Sheets template: image

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