Skip to content

Instantly share code, notes, and snippets.

@gtracy
Created January 6, 2025 01:26
Show Gist options
  • Save gtracy/a82347d3821df6021cb9d4f18cf5d562 to your computer and use it in GitHub Desktop.
Save gtracy/a82347d3821df6021cb9d4f18cf5d562 to your computer and use it in GitHub Desktop.
Aggregate multiple CSV files from brokerage accounts
const fs = require('fs');const csv = require('csv-parser');
const createCsvWriter = require('csv-writer').createObjectCsvWriter;
const { argv } = require('process');
async function processCSV(filePath) {
const results = [];
return new Promise((resolve, reject) => {
fs.createReadStream(filePath) // Correct usage of createReadStream
.pipe(csv())
.on('data', (data) => {
try {
const { Symbol, Quantity, 'Cost Basis Total': costBasisTotal } = data;
if (Symbol && Quantity && costBasisTotal) {
results.push({
Symbol: Symbol.trim(),
Quantity: parseFloat(Quantity.replace(/,/g, '')),
'Cost Basis Total': parseFloat(costBasisTotal.replace(/[$,]/g, '')), // Remove $ and ,
});
}
} catch (error) {
console.error(`Error processing row in ${filePath}:`, data, error);
}
})
.on('end', () => resolve(results))
.on('error', reject);
});
}
async function aggregateData(filePaths) {
const aggregatedData = {};
for (const filePath of filePaths) {
try {
const data = await processCSV(filePath);
for (const item of data) {
if (!aggregatedData[item.Symbol]) {
aggregatedData[item.Symbol] = { ...item };
} else {
aggregatedData[item.Symbol].Quantity += item.Quantity;
aggregatedData[item.Symbol]['Cost Basis Total'] += item['Cost Basis Total'];
}
}
} catch (error) {
console.error(`Error processing file ${filePath}:`, error);
}
}
return Object.values(aggregatedData);
}
async function writeToCSV(data, outputPath) {
if (data.length === 0) {
console.log("No data to write to CSV.");
return;
}
// Sort the data alphabetically by Symbol
data.sort((a, b) => {
const symbolA = a.Symbol.toUpperCase(); // Ignore case during sorting
const symbolB = b.Symbol.toUpperCase();
if (symbolA < symbolB) {
return -1;
}
if (symbolA > symbolB) {
return 1;
}
return 0; // symbols are equal
});
const csvWriter = createCsvWriter({
path: outputPath,
header: [
{ id: 'Symbol', title: 'Symbol' },
{ id: 'Quantity', title: 'Quantity' },
{ id: 'Cost Basis Total', title: 'Cost Basis Total' },
],
});
try {
await csvWriter.writeRecords(data);
console.log(`Data written to ${outputPath}`);
} catch (err) {
console.error('Error writing CSV:', err);
}
}
async function main() {
const inputFiles = argv.slice(2); // Get file paths from command line arguments
const outputFile = 'aggregated_holdings.csv';
if (inputFiles.length === 0) {
console.error('Please provide input file paths as command line arguments.');
process.exit(1);
}
const aggregated = await aggregateData(inputFiles);
await writeToCSV(aggregated, outputFile);
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment