Skip to content

Instantly share code, notes, and snippets.

@jkereako
Created February 22, 2025 17:43
Show Gist options
  • Save jkereako/76dbb9e0f1dc56f35209d9a93fa3e64d to your computer and use it in GitHub Desktop.
Save jkereako/76dbb9e0f1dc56f35209d9a93fa3e64d to your computer and use it in GitHub Desktop.
Convert a Fidelity CSV to a TaxSlayer CSV for import
// Generated by Copilot
package main
import (
"bufio"
"encoding/csv"
"fmt"
"os"
"strconv"
"strings"
"time"
)
func main() {
// Open the fidelity.csv file
inputFile, err := os.Open("fidelity.csv")
if err != nil {
fmt.Println("Error opening fidelity.csv:", err)
return
}
defer inputFile.Close()
// Create a new file to save the normalized data
normalizedFile, err := os.Create("fidelity-normalized.csv")
if err != nil {
fmt.Println("Error creating fidelity-normalized.csv:", err)
return
}
defer normalizedFile.Close()
writer := bufio.NewWriter(normalizedFile)
scanner := bufio.NewScanner(inputFile)
// Read the file line by line and save relevant lines
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, "1099-B-Detail") && (strings.Contains(line, "BUY") || strings.Contains(line, "SALE")) && !strings.Contains(line, "NONCOVERED") {
_, err := writer.WriteString(line + "\n")
if err != nil {
fmt.Println("Error writing to fidelity-normalized.csv:", err)
return
}
}
}
if err := scanner.Err(); err != nil {
fmt.Println("Error reading fidelity.csv:", err)
return
}
writer.Flush()
// Open the normalized file for reading
normalizedFile, err = os.Open("fidelity-normalized.csv")
if err != nil {
fmt.Println("Error opening fidelity-normalized.csv:", err)
return
}
defer normalizedFile.Close()
reader := csv.NewReader(normalizedFile)
reader.LazyQuotes = true
// Read all records from the normalized file
records, err := reader.ReadAll()
if err != nil {
fmt.Println("Error reading fidelity-normalized.csv:", err)
return
}
// Open the taxslayer.csv file for writing
outputFile, err := os.Create("taxslayer.csv")
if err != nil {
fmt.Println("Error creating taxslayer.csv:", err)
return
}
defer outputFile.Close()
csvWriter := csv.NewWriter(outputFile)
defer csvWriter.Flush()
// Write the header row
header := []string{"Owner", "Description", "DtAcquired", "DtSold", "SalesPrice", "Cost"}
if err := csvWriter.Write(header); err != nil {
fmt.Println("Error writing header:", err)
return
}
// Process each record and map to taxslayer.csv format
batchSize := 500
rowCount := 0
for _, record := range records {
if len(record) < 20 {
continue // Skip rows that don't have enough columns
}
// Extract and map the required fields
description := extractDescription(record[8])
dtAcquired := formatDate(record[11])
dtSold := formatDate(record[12])
cost := roundToNearestDollar(record[14])
salesPrice := roundToNearestDollar(record[17])
if salesPrice == "0" {
salesPrice = roundToNearestDollar(record[18])
}
// Create the new row for taxslayer.csv
newRow := []string{"T", description, dtAcquired, dtSold, salesPrice, cost}
if err := csvWriter.Write(newRow); err != nil {
fmt.Println("Error writing row:", err)
return
}
rowCount++
// Flush the writer every batchSize rows
if rowCount%batchSize == 0 {
csvWriter.Flush()
}
}
// Final flush to ensure all data is written
csvWriter.Flush()
fmt.Println("CSV mapping complete. Output written to taxslayer.csv")
}
func extractDescription(field string) string {
parts := strings.Fields(strings.TrimSpace(field))
if len(parts) > 1 {
return parts[len(parts)-2]
}
return ""
}
func formatDate(field string) string {
date, err := time.Parse("01/02/06", strings.TrimSpace(field))
if err != nil {
fmt.Println("Error parsing date:", err)
return ""
}
return date.Format("01/02/2006")
}
func roundToNearestDollar(field string) string {
value, err := strconv.ParseFloat(strings.TrimSpace(field), 64)
if err != nil {
fmt.Println("Error parsing float:", err)
return "0"
}
return strconv.Itoa(int(value + 0.5))
}
@jkereako
Copy link
Author

jkereako commented Feb 22, 2025

Description

Converts an CSV exported from Fidelity, named fidelity.csv, to the format TaxSlayer expects. This was written by Copilot.

Fidelity's CSV is malformed, at least mine was, so the first thing this script does is normalize the CSV and extracts the rows that actually matter. It saves that as a new CSV, opens it, and then converts it to the format TaxSlayer expects.

Prompt

Below is the prompt I used. It was a series of trial and error.

Write a program in Go that maps the values in fidelity.csv, which is record of financial exchanges, to the columns in taxslayer.csv.

The columns in taxslayer.csv are "Owner", "Description", "DtAcquired", "DtSold", "SalesPrice", and "Cost". The value for the column "Owner" is always "T". The column "Description" is the ticker symbol of the exchanged equity. The columns "DtAcquired" and "DtSold" are dates with the formats MM/DD/YYYY. The column "SalesPrice" indicates the realized gain or loss and "Cost" is the cost basis. Both of these columns must contain whole numbers, so round to the nearest dollar.

Below are instructions on how to parse fidelity.csv.

Read fidelity.csv as a regular file, line by line. Save every line that contains "1099-B-Detail" and "BUY" or "SALE" and doesn't contain "NONCOVERED". Store this in a collection. Repeat the process until EOF. Save this collection as "fidelity-normalized.csv".

Open "fidelity-normalized.csv" with csv.ReadAll() and set LazyQuotes to true. Start at the eighth index. This contains the name of the exchanged equity. First trim this string and split it by ' '. The ticker symbol is the penultimate index. Map this to the column "Description".

Skip three columns ahead to find a date in the format MM/DD/YY, this is the acquisition date of the equity and maps to "DtAcquired". The next column maps to "DtSold" and is the date sold and has the same format.

Skip two columns ahead to find the cost basis and map this to the column "Cost".

Skip three columns ahead to find the realized gain. If the value of this row is zero, then inspect the next column which is the realized loss. Map this value to the column "SalesPrice"

Batch the number of rows in taxslayer.csv to 500.

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