Created
January 6, 2025 01:26
-
-
Save gtracy/a82347d3821df6021cb9d4f18cf5d562 to your computer and use it in GitHub Desktop.
Aggregate multiple CSV files from brokerage accounts
This file contains 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
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