From 2f4e3cc9413125bc12a7169c71ec52eecf32b2a0 Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sat, 30 Mar 2024 11:29:56 -0400 Subject: [PATCH 01/20] support for open trades data (WIP) --- cmd/transaction_reader.go | 9 +++- parse/journal.go | 39 ++++++++++++-- parse/journal_test.go | 108 +++++++++++++++++++++++++++----------- 3 files changed, 122 insertions(+), 34 deletions(-) diff --git a/cmd/transaction_reader.go b/cmd/transaction_reader.go index c101824..fe21af4 100644 --- a/cmd/transaction_reader.go +++ b/cmd/transaction_reader.go @@ -7,9 +7,10 @@ import ( "os" ) -// usage: go run cmd/transaction_reader.go --data "./testdata/input/1-dmc.csv" +// usage: go run cmd/transaction_reader.go --data "./testdata/input/1-dmc.csv" --open "./testdata/input/open-trades.json" func main() { dataFlag := flag.String("data", "", "Path to CSV data.") + openFlag := flag.String("open", "", "Path to open trades JSON file.") flag.Parse() @@ -18,8 +19,14 @@ func main() { os.Exit(1) } + if *openFlag == "" { + flag.PrintDefaults() + os.Exit(1) + } + journal := parse.NewJournal() transactions := journal.ReadTransactions(*dataFlag) + transactions = journal.AddOpenTradesData(*openFlag, transactions) journal.ToCsv(transactions) for i, transaction := range transactions { diff --git a/parse/journal.go b/parse/journal.go index 2a98fa1..559ac08 100644 --- a/parse/journal.go +++ b/parse/journal.go @@ -2,6 +2,7 @@ package parse import ( "encoding/csv" + "encoding/json" "fmt" "io" "log" @@ -12,6 +13,7 @@ import ( ) type Transaction struct { + id string // trade id will be imported from a separate JSON file ticker string account string date string @@ -45,6 +47,14 @@ type Journal struct { trades map[string][]Transaction } +// OpenTicker represents an existing trade with properties that we want to add transactions we're importing +// This will eliminate the need to copy / paste properties of open trades such as the trade ID and notes +type OpenTicker struct { + ID int `json:"id"` + Ticker string `json:"ticker"` + Notes string `json:"notes"` +} + func NewJournal() Journal { return Journal{} } @@ -411,6 +421,25 @@ func (j *Journal) findSingleTransaction(ticker string, action string) *Transacti return &(matchedTransactions)[0] } +func (j *Journal) loadTickerMap() map[string]*OpenTicker { + data, err := os.ReadFile("tickers.json") + if err != nil { + log.Fatal("Error reading file:", err) + } + + // Create a map to store openTickers by their ticker symbols + tickerMap := make(map[string]*OpenTicker) + + // Unmarshal the JSON data into a slice of Ticker structs + var openTickers []OpenTicker + err = json.Unmarshal(data, &openTickers) + if err != nil { + log.Fatal("Error unmarshaling JSON:", err) + } + + return tickerMap +} + // update updateSingleTransaction to take another parameter of the Action to match so that can have multiple transactions for the same ticker func (j *Journal) updateSingleTransaction(ticker string, transaction Transaction) { if j.trades == nil { @@ -421,9 +450,6 @@ func (j *Journal) updateSingleTransaction(ticker string, transaction Transaction if transactions == nil { panic(fmt.Sprintf("no transactions for ticker %s", ticker)) } - //if len(transactions) > 1 { - // panic(fmt.Sprintf("expected only 1 transaction for ticker %s but have %d", ticker, len(transactions))) - //} // loop over transactions and find the one with the action matchedTransactions := make([]Transaction, 0) @@ -504,3 +530,10 @@ func (j *Journal) ToCsv(txs []Transaction) { writer := csv.NewWriter(f) writer.WriteAll(txsStr) } + +// AddOpen +func (j *Journal) AddOpenTradesData(openTradesPath string, transactions []Transaction) []Transaction { + //transactions = AddOpenTradesData(openTradesPath, transactions) + // func (j *Journal) ReadTransactions(csvPath string) []Transaction { + return transactions +} diff --git a/parse/journal_test.go b/parse/journal_test.go index d19bf88..48d4af9 100644 --- a/parse/journal_test.go +++ b/parse/journal_test.go @@ -6,13 +6,20 @@ import ( "github.com/stretchr/testify/require" ) -type TestData struct { - expectedTransactions []Transaction - filePath string +type TestDataL1 struct { + expectedL1Transactions []Transaction + csvExportPath string + //openTradesPath string +} + +type TestDataL2 struct { + openTradesPath string + l1Transactions []Transaction + expectedL2Transactions []Transaction } func TestReadTransactions(t *testing.T) { - expectedTransactions1 := []Transaction{ + expectedL1Transactions1 := []Transaction{ { date: "2022-11-25", account: "TFSA", @@ -60,8 +67,8 @@ func TestReadTransactions(t *testing.T) { { date: "2023-06-08", account: "RRSP", - action: "Dividend", ticker: "MSFT", + action: "Dividend", dividend: "136", notes: "MSFT(US5949181045) Cash Dividend USD 0.68 per Share (Ordinary Dividend)", }, @@ -320,50 +327,50 @@ func TestReadTransactions(t *testing.T) { // should be an empty array because the put option expired out of the money } - testDataMap := map[string]TestData{ + testDataMap := map[string]TestDataL1{ "stock, short call, long put": { - expectedTransactions: expectedTransactions1, - filePath: "../testdata/input/1-dmc.csv", + expectedL1Transactions: expectedL1Transactions1, + csvExportPath: "../testdata/input/1-dmc.csv", }, "dividend": { - expectedTransactions: expectedTransactions2, - filePath: "../testdata/input/2-dividend.csv", + expectedL1Transactions: expectedTransactions2, + csvExportPath: "../testdata/input/2-dividend.csv", }, "forex": { - expectedTransactions: expectedTransactions3, - filePath: "../testdata/input/3-forex.csv", + expectedL1Transactions: expectedTransactions3, + csvExportPath: "../testdata/input/3-forex.csv", }, "dividend - withholding tax": { - expectedTransactions: expectedTransactions4, - filePath: "../testdata/input/4-dividend-withholding-tax.csv", + expectedL1Transactions: expectedTransactions4, + csvExportPath: "../testdata/input/4-dividend-withholding-tax.csv", }, "call assignment": { - expectedTransactions: expectedTransactions5, - filePath: "../testdata/input/5-call-assignment.csv", + expectedL1Transactions: expectedTransactions5, + csvExportPath: "../testdata/input/5-call-assignment.csv", }, "hit target": { - expectedTransactions: expectedTransactions6, - filePath: "../testdata/input/6-hit-target.csv", + expectedL1Transactions: expectedTransactions6, + csvExportPath: "../testdata/input/6-hit-target.csv", }, "roll out call, roll down call": { - expectedTransactions: expectedTransactions7, - filePath: "../testdata/input/7-call-roll-out-roll-down.csv", + expectedL1Transactions: expectedTransactions7, + csvExportPath: "../testdata/input/7-call-roll-out-roll-down.csv", }, "dividend - withholding tax + other transactions same ticker": { - expectedTransactions: expectedTransactions8, - filePath: "../testdata/input/8-dividend-withholding-tax-other-tx.csv", + expectedL1Transactions: expectedTransactions8, + csvExportPath: "../testdata/input/8-dividend-withholding-tax-other-tx.csv", }, "expired OTM put": { - expectedTransactions: expectedEmptyTransactions, - filePath: "../testdata/input/9-lapsed-put.csv", + expectedL1Transactions: expectedEmptyTransactions, + csvExportPath: "../testdata/input/9-lapsed-put.csv", }, "expired OTM call, OTM puts": { - expectedTransactions: expectedEmptyTransactions, - filePath: "../testdata/input/10-lapsed-call-puts.csv", + expectedL1Transactions: expectedEmptyTransactions, + csvExportPath: "../testdata/input/10-lapsed-call-puts.csv", }, "exercise put, lapsed call for same ticker": { - expectedTransactions: expectedTransactions9, - filePath: "../testdata/input/11-exercise-put.csv", + expectedL1Transactions: expectedTransactions9, + csvExportPath: "../testdata/input/11-exercise-put.csv", }, } @@ -371,9 +378,50 @@ func TestReadTransactions(t *testing.T) { t.Run(k, func(t *testing.T) { // read original csv file trade data journal := NewJournal() - actualTransactions := journal.ReadTransactions(testData.filePath) + actualTransactions := journal.ReadTransactions(testData.csvExportPath) - require.ElementsMatch(t, testData.expectedTransactions, actualTransactions) + require.ElementsMatch(t, testData.expectedL1Transactions, actualTransactions) }) } } + +func TestAddOpenTickers(t *testing.T) { + expectedTransactions1 := []Transaction{ + { + date: "2022-11-25", + account: "TFSA", + action: "Trade", + ticker: "PR", + buySell: "Buy", + shares: "600", + price: "10.588333333", + proceeds: "-6353.00", + costBasisBuyOrOption: "-6355.999999799999", + costBasisTotal: "-6355.999999799999", + commission: "-3", + }, + } + + testDataMap := map[string]TestDataL2{ + //"stock, short call, long put": { + // expectedL1Transactions: expectedTransactions1, + // csvExportPath: "../testdata/input/1-dmc.csv", + //}, + "dividend": { + openTradesPath: "../testdata/input/open-trades.json", + l1Transactions: expectedTransactions1, + expectedL2Transactions: expectedTransactions1, + }, + } + + for k, testData := range testDataMap { + t.Run(k, func(t *testing.T) { + // read original csv file trade data + journal := NewJournal() + actualTransactions := journal.AddOpenTradesData(testData.openTradesPath, expectedTransactions1) + + require.ElementsMatch(t, testData.expectedL2Transactions, actualTransactions) + }) + } + +} From 6b5cdb75bac294a3c88f85b51202577abe4232be Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sat, 30 Mar 2024 11:47:09 -0400 Subject: [PATCH 02/20] testing - moved expected transactions to test data file --- parse/journal_test.go | 308 ----------------------------------------- parse/test_data.go | 309 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 309 insertions(+), 308 deletions(-) create mode 100644 parse/test_data.go diff --git a/parse/journal_test.go b/parse/journal_test.go index 48d4af9..31aab2f 100644 --- a/parse/journal_test.go +++ b/parse/journal_test.go @@ -19,314 +19,6 @@ type TestDataL2 struct { } func TestReadTransactions(t *testing.T) { - expectedL1Transactions1 := []Transaction{ - { - date: "2022-11-25", - account: "TFSA", - action: "Trade", - ticker: "PR", - buySell: "Buy", - shares: "600", - price: "10.588333333", - proceeds: "-6353.00", - costBasisBuyOrOption: "-6355.999999799999", - costBasisTotal: "-6355.999999799999", - commission: "-3", - }, - { - date: "2022-11-25", - account: "TFSA", - action: "Trade - Option", - ticker: "PR", - optionContract: "20JAN23 9 C", - buySell: "Sell", - optionContracts: "-6", - price: "1.971666667", - proceeds: "1183.00", - costBasisShare: "0", - costBasisBuyOrOption: "1179.9809295", - commission: "-3.0190707", - }, - { - date: "2022-11-25", - account: "TFSA", - action: "Trade - Option", - ticker: "PR", - optionContract: "20JAN23 5 P", - buySell: "Buy", - optionContracts: "6", - price: "0.053333333", - proceeds: "-32.00", - costBasisShare: "0", - costBasisBuyOrOption: "-32.9788998", - commission: "-0.9789", - }, - } - - expectedTransactions2 := []Transaction{ - { - date: "2023-06-08", - account: "RRSP", - ticker: "MSFT", - action: "Dividend", - dividend: "136", - notes: "MSFT(US5949181045) Cash Dividend USD 0.68 per Share (Ordinary Dividend)", - }, - } - - expectedTransactions3 := []Transaction{ - { - date: "2023-06-05", - account: "Margin", - action: "Forex", - commission: "-2", - forexUSDBuy: "4,838.82", - forexUSDCAD: "1.3433", - forexCADSell: "-6499.986906", - notes: "converted all CAD to USD", - }, - { - date: "2023-06-05", - account: "Margin", - action: "Trade", - ticker: "TECK", - buySell: "Buy", - shares: "100", - price: "42.09", - proceeds: "-4209.00", - costBasisBuyOrOption: "-4209.370257250001", - costBasisTotal: "-4209.370257250001", - commission: "-0.37025725", - }, - { - date: "2023-06-05", - account: "Margin", - action: "Trade - Option", - ticker: "TECK", - optionContract: "21JUL23 38 C", - buySell: "Sell", - optionContracts: "-1", - price: "5.07", - proceeds: "507.00", - costBasisShare: "0", - costBasisBuyOrOption: "505.944454", - commission: "-1.055546", - }, - } - - expectedTransactions4 := []Transaction{ - { - date: "2023-06-09", - account: "Margin", - action: "Dividend", - ticker: "SMG", - dividend: "66", - fee: "-9.9", - notes: "SMG(US8101861065) Payment in Lieu of Dividend (Ordinary Dividend)\n15% tax withdrawn", - }, - } - - expectedTransactions5 := []Transaction{ - { - date: "2023-06-08", - account: "RRSP", - action: "Trade - Option - Assignment", - actionModified: "Trade - Option - Assignment", - ticker: "FDX", - optionContract: "16JUN23 155 C", - buySell: "Sell", - shares: "-100", - price: "155", - proceeds: "15500.00", - costBasisShare: "-172.67370257", - costBasisTotal: "17267.370257", // import IBKR value and multiply by -1 - realizedPL: "3873.744617", // imports IBKR value - commission: "-0.1385", - notes: "called away for profit", - }, - } - - expectedTransactions6 := []Transaction{ - { - date: "2023-06-08", - account: "TFSA", - action: "Trade - Close", - actionModified: "Trade - Close", - ticker: "BBWI", - buySell: "Sell", - shares: "-100", - price: "41.44", - proceeds: "4144.00", - costBasisShare: "-38.17000000", - costBasisBuyOrOption: "", - costBasisTotal: "3817", // imports IBKR value and multiply by -1 - realizedPL: "326.482091", // imports IBKR value - commission: "-0.51790925", - notes: "hit GTC target", - }, - { - date: "2023-06-08", - account: "TFSA", - action: "Trade - Option", - ticker: "BBWI", - optionContract: "16JUN23 35 C", - buySell: "Buy", - optionContracts: "1", - price: "6.53", - proceeds: "-653.00", - costBasisShare: "0", - costBasisBuyOrOption: "-654.05155", - commission: "-1.05155", - notes: "hit GTC target", - }, - } - - expectedTransactions7 := []Transaction{ - // HPQ roll out - { - date: "2023-06-12", - account: "RRSP", - action: "Trade - Option", - ticker: "HPQ", - optionContract: "16JUN23 27 C", - buySell: "Buy", - optionContracts: "2", - price: "3.32", - proceeds: "-664.00", - costBasisShare: "0", - costBasisBuyOrOption: "-664.6581", - commission: "-0.6581", - }, - { - date: "2023-06-12", - account: "RRSP", - action: "Trade - Option", - ticker: "HPQ", - optionContract: "18AUG23 27 C", - buySell: "Sell", - optionContracts: "-2", - price: "3.62", - proceeds: "724.00", - costBasisShare: "0", - costBasisBuyOrOption: "723.331228", - commission: "-0.668772", - }, - - // STNG roll down - { - date: "2023-06-12", - account: "RRSP", - action: "Trade - Option", - ticker: "STNG", - optionContract: "21JUL23 46 C", - buySell: "Buy", - optionContracts: "1", - price: "1.97", - proceeds: "-197.00", - costBasisShare: "0", - costBasisBuyOrOption: "-197.64905", - commission: "-0.64905", - }, - { - date: "2023-06-12", - account: "RRSP", - action: "Trade - Option", - ticker: "STNG", - optionContract: "21JUL23 44 C", - buySell: "Sell", - optionContracts: "-1", - price: "2.79", - proceeds: "279.00", - costBasisShare: "0", - costBasisBuyOrOption: "278.346278", - commission: "-0.653722", - }, - } - - expectedTransactions8 := []Transaction{ - { - date: "2023-06-15", - account: "TFSA", - action: "Trade - Option", - ticker: "MOS", - optionContract: "16JUN23 32.5 C", - buySell: "Buy", - optionContracts: "2", - price: "3.125", - proceeds: "-625.00", - costBasisShare: "0", - costBasisBuyOrOption: "-625.6581", - commission: "-0.6581", - }, - - { - date: "2023-06-15", - account: "TFSA", - action: "Trade - Option", - ticker: "MOS", - optionContract: "21JUL23 32.5 C", - buySell: "Sell", - optionContracts: "-2", - price: "3.825", - proceeds: "765.00", - costBasisShare: "0", - costBasisBuyOrOption: "764.3309", - commission: "-0.6691", - }, - { - date: "2023-06-15", - account: "TFSA", - action: "Dividend", - ticker: "MOS", - dividend: "40", - fee: "-6", - notes: "MOS(US61945C1036) Cash Dividend USD 0.20 per Share (Ordinary Dividend)\n15% tax withdrawn", - }, - } - - expectedTransactions9 := []Transaction{ - { - date: "2023-07-21", - account: "RRSP", - action: "Trade - Option - Exercise", - actionModified: "Trade - Option - Exercise", - ticker: "STNG", - optionContract: "21JUL23 50 P", - buySell: "Sell", - shares: "-100", - price: "50", - proceeds: "5000.00", - costBasisShare: "-62.61370257", - costBasisBuyOrOption: "", - costBasisTotal: "6261.370257", // imports IBKR value and multiply by -1 - realizedPL: "-1791.673807", // imports IBKR value - commission: "-0.0545", - notes: "exercised long put", - }, - { - date: "2023-07-21", - account: "RRSP", - action: "Trade - Option - Exercise", - actionModified: "Trade - Option - Exercise", - ticker: "TGT", - optionContract: "21JUL23 140 P", - buySell: "Sell", - shares: "-100", - price: "140", - proceeds: "14000.00", - costBasisShare: "-163.60370257", - costBasisBuyOrOption: "", - costBasisTotal: "16360.370257", // imports IBKR value and multiply by -1 - realizedPL: "-3011.535807", // imports IBKR value - commission: "-0.1265", - notes: "exercised long put", - }, - } - - expectedEmptyTransactions := []Transaction{ - // should be an empty array because the put option expired out of the money - } - testDataMap := map[string]TestDataL1{ "stock, short call, long put": { expectedL1Transactions: expectedL1Transactions1, diff --git a/parse/test_data.go b/parse/test_data.go new file mode 100644 index 0000000..bb27a79 --- /dev/null +++ b/parse/test_data.go @@ -0,0 +1,309 @@ +package parse + +var expectedL1Transactions1 = []Transaction{ + { + date: "2022-11-25", + account: "TFSA", + action: "Trade", + ticker: "PR", + buySell: "Buy", + shares: "600", + price: "10.588333333", + proceeds: "-6353.00", + costBasisBuyOrOption: "-6355.999999799999", + costBasisTotal: "-6355.999999799999", + commission: "-3", + }, + { + date: "2022-11-25", + account: "TFSA", + action: "Trade - Option", + ticker: "PR", + optionContract: "20JAN23 9 C", + buySell: "Sell", + optionContracts: "-6", + price: "1.971666667", + proceeds: "1183.00", + costBasisShare: "0", + costBasisBuyOrOption: "1179.9809295", + commission: "-3.0190707", + }, + { + date: "2022-11-25", + account: "TFSA", + action: "Trade - Option", + ticker: "PR", + optionContract: "20JAN23 5 P", + buySell: "Buy", + optionContracts: "6", + price: "0.053333333", + proceeds: "-32.00", + costBasisShare: "0", + costBasisBuyOrOption: "-32.9788998", + commission: "-0.9789", + }, +} + +var expectedTransactions2 = []Transaction{ + { + date: "2023-06-08", + account: "RRSP", + ticker: "MSFT", + action: "Dividend", + dividend: "136", + notes: "MSFT(US5949181045) Cash Dividend USD 0.68 per Share (Ordinary Dividend)", + }, +} + +var expectedTransactions3 = []Transaction{ + { + date: "2023-06-05", + account: "Margin", + action: "Forex", + commission: "-2", + forexUSDBuy: "4,838.82", + forexUSDCAD: "1.3433", + forexCADSell: "-6499.986906", + notes: "converted all CAD to USD", + }, + { + date: "2023-06-05", + account: "Margin", + action: "Trade", + ticker: "TECK", + buySell: "Buy", + shares: "100", + price: "42.09", + proceeds: "-4209.00", + costBasisBuyOrOption: "-4209.370257250001", + costBasisTotal: "-4209.370257250001", + commission: "-0.37025725", + }, + { + date: "2023-06-05", + account: "Margin", + action: "Trade - Option", + ticker: "TECK", + optionContract: "21JUL23 38 C", + buySell: "Sell", + optionContracts: "-1", + price: "5.07", + proceeds: "507.00", + costBasisShare: "0", + costBasisBuyOrOption: "505.944454", + commission: "-1.055546", + }, +} + +var expectedTransactions4 = []Transaction{ + { + date: "2023-06-09", + account: "Margin", + action: "Dividend", + ticker: "SMG", + dividend: "66", + fee: "-9.9", + notes: "SMG(US8101861065) Payment in Lieu of Dividend (Ordinary Dividend)\n15% tax withdrawn", + }, +} + +var expectedTransactions5 = []Transaction{ + { + date: "2023-06-08", + account: "RRSP", + action: "Trade - Option - Assignment", + actionModified: "Trade - Option - Assignment", + ticker: "FDX", + optionContract: "16JUN23 155 C", + buySell: "Sell", + shares: "-100", + price: "155", + proceeds: "15500.00", + costBasisShare: "-172.67370257", + costBasisTotal: "17267.370257", // import IBKR value and multiply by -1 + realizedPL: "3873.744617", // imports IBKR value + commission: "-0.1385", + notes: "called away for profit", + }, +} + +var expectedTransactions6 = []Transaction{ + { + date: "2023-06-08", + account: "TFSA", + action: "Trade - Close", + actionModified: "Trade - Close", + ticker: "BBWI", + buySell: "Sell", + shares: "-100", + price: "41.44", + proceeds: "4144.00", + costBasisShare: "-38.17000000", + costBasisBuyOrOption: "", + costBasisTotal: "3817", // imports IBKR value and multiply by -1 + realizedPL: "326.482091", // imports IBKR value + commission: "-0.51790925", + notes: "hit GTC target", + }, + { + date: "2023-06-08", + account: "TFSA", + action: "Trade - Option", + ticker: "BBWI", + optionContract: "16JUN23 35 C", + buySell: "Buy", + optionContracts: "1", + price: "6.53", + proceeds: "-653.00", + costBasisShare: "0", + costBasisBuyOrOption: "-654.05155", + commission: "-1.05155", + notes: "hit GTC target", + }, +} + +var expectedTransactions7 = []Transaction{ + // HPQ roll out + { + date: "2023-06-12", + account: "RRSP", + action: "Trade - Option", + ticker: "HPQ", + optionContract: "16JUN23 27 C", + buySell: "Buy", + optionContracts: "2", + price: "3.32", + proceeds: "-664.00", + costBasisShare: "0", + costBasisBuyOrOption: "-664.6581", + commission: "-0.6581", + }, + { + date: "2023-06-12", + account: "RRSP", + action: "Trade - Option", + ticker: "HPQ", + optionContract: "18AUG23 27 C", + buySell: "Sell", + optionContracts: "-2", + price: "3.62", + proceeds: "724.00", + costBasisShare: "0", + costBasisBuyOrOption: "723.331228", + commission: "-0.668772", + }, + + // STNG roll down + { + date: "2023-06-12", + account: "RRSP", + action: "Trade - Option", + ticker: "STNG", + optionContract: "21JUL23 46 C", + buySell: "Buy", + optionContracts: "1", + price: "1.97", + proceeds: "-197.00", + costBasisShare: "0", + costBasisBuyOrOption: "-197.64905", + commission: "-0.64905", + }, + { + date: "2023-06-12", + account: "RRSP", + action: "Trade - Option", + ticker: "STNG", + optionContract: "21JUL23 44 C", + buySell: "Sell", + optionContracts: "-1", + price: "2.79", + proceeds: "279.00", + costBasisShare: "0", + costBasisBuyOrOption: "278.346278", + commission: "-0.653722", + }, +} + +var expectedTransactions8 = []Transaction{ + { + date: "2023-06-15", + account: "TFSA", + action: "Trade - Option", + ticker: "MOS", + optionContract: "16JUN23 32.5 C", + buySell: "Buy", + optionContracts: "2", + price: "3.125", + proceeds: "-625.00", + costBasisShare: "0", + costBasisBuyOrOption: "-625.6581", + commission: "-0.6581", + }, + + { + date: "2023-06-15", + account: "TFSA", + action: "Trade - Option", + ticker: "MOS", + optionContract: "21JUL23 32.5 C", + buySell: "Sell", + optionContracts: "-2", + price: "3.825", + proceeds: "765.00", + costBasisShare: "0", + costBasisBuyOrOption: "764.3309", + commission: "-0.6691", + }, + { + date: "2023-06-15", + account: "TFSA", + action: "Dividend", + ticker: "MOS", + dividend: "40", + fee: "-6", + notes: "MOS(US61945C1036) Cash Dividend USD 0.20 per Share (Ordinary Dividend)\n15% tax withdrawn", + }, +} + +var expectedTransactions9 = []Transaction{ + { + date: "2023-07-21", + account: "RRSP", + action: "Trade - Option - Exercise", + actionModified: "Trade - Option - Exercise", + ticker: "STNG", + optionContract: "21JUL23 50 P", + buySell: "Sell", + shares: "-100", + price: "50", + proceeds: "5000.00", + costBasisShare: "-62.61370257", + costBasisBuyOrOption: "", + costBasisTotal: "6261.370257", // imports IBKR value and multiply by -1 + realizedPL: "-1791.673807", // imports IBKR value + commission: "-0.0545", + notes: "exercised long put", + }, + { + date: "2023-07-21", + account: "RRSP", + action: "Trade - Option - Exercise", + actionModified: "Trade - Option - Exercise", + ticker: "TGT", + optionContract: "21JUL23 140 P", + buySell: "Sell", + shares: "-100", + price: "140", + proceeds: "14000.00", + costBasisShare: "-163.60370257", + costBasisBuyOrOption: "", + costBasisTotal: "16360.370257", // imports IBKR value and multiply by -1 + realizedPL: "-3011.535807", // imports IBKR value + commission: "-0.1265", + notes: "exercised long put", + }, +} + +var expectedEmptyTransactions = []Transaction{ + // should be an empty array because the put option expired out of the money +} From 242097ce9d463822ad8d4e6c3152cc5417ab9b37 Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sat, 30 Mar 2024 12:06:44 -0400 Subject: [PATCH 03/20] testing - adding L2 expected transactions (WIP) --- parse/journal_test.go | 39 ++++++++------------------------- parse/test_data.go | 50 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 30 deletions(-) diff --git a/parse/journal_test.go b/parse/journal_test.go index 31aab2f..53e0919 100644 --- a/parse/journal_test.go +++ b/parse/journal_test.go @@ -7,15 +7,14 @@ import ( ) type TestDataL1 struct { - expectedL1Transactions []Transaction - csvExportPath string - //openTradesPath string + csvExportPath string // input + expectedL1Transactions []Transaction // expected output } type TestDataL2 struct { - openTradesPath string - l1Transactions []Transaction - expectedL2Transactions []Transaction + openTradesPath string // input + l1Transactions []Transaction // input - based on output from L1 process + expectedL2Transactions []Transaction // expected output } func TestReadTransactions(t *testing.T) { @@ -78,31 +77,11 @@ func TestReadTransactions(t *testing.T) { } func TestAddOpenTickers(t *testing.T) { - expectedTransactions1 := []Transaction{ - { - date: "2022-11-25", - account: "TFSA", - action: "Trade", - ticker: "PR", - buySell: "Buy", - shares: "600", - price: "10.588333333", - proceeds: "-6353.00", - costBasisBuyOrOption: "-6355.999999799999", - costBasisTotal: "-6355.999999799999", - commission: "-3", - }, - } - testDataMap := map[string]TestDataL2{ - //"stock, short call, long put": { - // expectedL1Transactions: expectedTransactions1, - // csvExportPath: "../testdata/input/1-dmc.csv", - //}, - "dividend": { + "stock, short call, long put": { openTradesPath: "../testdata/input/open-trades.json", - l1Transactions: expectedTransactions1, - expectedL2Transactions: expectedTransactions1, + l1Transactions: expectedL1Transactions1, + expectedL2Transactions: expectedL2Transactions1, }, } @@ -110,7 +89,7 @@ func TestAddOpenTickers(t *testing.T) { t.Run(k, func(t *testing.T) { // read original csv file trade data journal := NewJournal() - actualTransactions := journal.AddOpenTradesData(testData.openTradesPath, expectedTransactions1) + actualTransactions := journal.AddOpenTradesData(testData.openTradesPath, testData.l1Transactions) require.ElementsMatch(t, testData.expectedL2Transactions, actualTransactions) }) diff --git a/parse/test_data.go b/parse/test_data.go index bb27a79..ae044eb 100644 --- a/parse/test_data.go +++ b/parse/test_data.go @@ -44,6 +44,56 @@ var expectedL1Transactions1 = []Transaction{ }, } +var expectedL2Transactions1 = []Transaction{ + { + id: "MCDA278", + date: "2022-11-25", + account: "TFSA", + action: "Trade", + ticker: "PR", + buySell: "Buy", + shares: "600", + price: "10.588333333", + proceeds: "-6353.00", + costBasisBuyOrOption: "-6355.999999799999", + costBasisTotal: "-6355.999999799999", + commission: "-3", + notes: "\"stock screen: Div Upcoming (15D)\ncc screen: OTM-60 Day 0.1 4+%\nex-div 3/22 0.20 earn 5/1\n3/25 48.69 50GTCp5c\"", + }, + { + id: "MCDA278", + date: "2022-11-25", + account: "TFSA", + action: "Trade - Option", + ticker: "PR", + optionContract: "20JAN23 9 C", + buySell: "Sell", + optionContracts: "-6", + price: "1.971666667", + proceeds: "1183.00", + costBasisShare: "0", + costBasisBuyOrOption: "1179.9809295", + commission: "-3.0190707", + notes: "", + }, + { + id: "MCDA278", + date: "2022-11-25", + account: "TFSA", + action: "Trade - Option", + ticker: "PR", + optionContract: "20JAN23 5 P", + buySell: "Buy", + optionContracts: "6", + price: "0.053333333", + proceeds: "-32.00", + costBasisShare: "0", + costBasisBuyOrOption: "-32.9788998", + commission: "-0.9789", + notes: "", + }, +} + var expectedTransactions2 = []Transaction{ { date: "2023-06-08", From cc14dc2faf5f72517e0b05e089590234fb09ade0 Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sat, 30 Mar 2024 12:10:13 -0400 Subject: [PATCH 04/20] testing - updated open-trades.json with expected L2 data --- testdata/input/open-trades.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 testdata/input/open-trades.json diff --git a/testdata/input/open-trades.json b/testdata/input/open-trades.json new file mode 100644 index 0000000..d28ca56 --- /dev/null +++ b/testdata/input/open-trades.json @@ -0,0 +1,20 @@ +[ + { + "id": "MCDA278", + "ticker": "PR", + "account": "TFSA", + "notes": "\"stock screen: Div Upcoming (15D)\ncc screen: OTM-60 Day 0.1 4+%\nex-div 3/22 0.20 earn 5/1\n3/25 48.69 50GTCp5c\"" + }, + { + "id": "2", + "ticker": "MSFT", + "account": "TFSA", + "notes": "Microsoft Corporation" + }, + { + "id": "3", + "ticker": "GOOGL", + "account": "TFSA", + "notes": "Alphabet Inc." + } +] From 74c26f5e77795e70b74c63a4101eb5bea7261820 Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sat, 30 Mar 2024 14:43:53 -0400 Subject: [PATCH 05/20] initial implementation, 1st test passing --- parse/journal.go | 111 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 100 insertions(+), 11 deletions(-) diff --git a/parse/journal.go b/parse/journal.go index 559ac08..8e0747e 100644 --- a/parse/journal.go +++ b/parse/journal.go @@ -47,12 +47,14 @@ type Journal struct { trades map[string][]Transaction } -// OpenTicker represents an existing trade with properties that we want to add transactions we're importing +// OpenTrade represents the data associated with each open trade that we want to add to an exported transaction. // This will eliminate the need to copy / paste properties of open trades such as the trade ID and notes -type OpenTicker struct { - ID int `json:"id"` - Ticker string `json:"ticker"` - Notes string `json:"notes"` +type OpenTrade struct { + ID string `json:"ID"` + Ticker string `json:"ticker"` + Account string `json:"account"` + Notes string `json:"notes"` + Matched bool // keeps track if this entry was previously matched so we don't duplicate Notes property for subsequent entries. } func NewJournal() Journal { @@ -421,17 +423,17 @@ func (j *Journal) findSingleTransaction(ticker string, action string) *Transacti return &(matchedTransactions)[0] } -func (j *Journal) loadTickerMap() map[string]*OpenTicker { +func (j *Journal) loadTickerMap() map[string]*OpenTrade { data, err := os.ReadFile("tickers.json") if err != nil { log.Fatal("Error reading file:", err) } // Create a map to store openTickers by their ticker symbols - tickerMap := make(map[string]*OpenTicker) + tickerMap := make(map[string]*OpenTrade) // Unmarshal the JSON data into a slice of Ticker structs - var openTickers []OpenTicker + var openTickers []OpenTrade err = json.Unmarshal(data, &openTickers) if err != nil { log.Fatal("Error unmarshaling JSON:", err) @@ -531,9 +533,96 @@ func (j *Journal) ToCsv(txs []Transaction) { writer.WriteAll(txsStr) } -// AddOpen +// AddOpenTradesData appends to the list of transactions that will be imported by adding more data. This is to eliminate +// the need for copying and pasting data in the spreadsheet, so it's already there when transactions are imported. +// For example, the trade ID and notes are properties that aren't present in the original exported CSV from Interactive Brokers, +// but they need to be added to the general ledger of trades. func (j *Journal) AddOpenTradesData(openTradesPath string, transactions []Transaction) []Transaction { - //transactions = AddOpenTradesData(openTradesPath, transactions) - // func (j *Journal) ReadTransactions(csvPath string) []Transaction { + // load JSON data of open trades into a map + jsonData, err := os.ReadFile(openTradesPath) + if err != nil { + fmt.Println("Error reading file:", err) + return nil + } + + // parse the JSON data + var openTrades []OpenTrade + if err := json.Unmarshal(jsonData, &openTrades); err != nil { + fmt.Println("Error parsing JSON:", err) + return nil + } + + // initialize an empty map to store the openTrades + openTradesMap := make(map[[2]interface{}]OpenTrade) + + // create a unique key for each openTrade (ticker + account) + for _, openTrade := range openTrades { + key := [2]interface{}{openTrade.Ticker, openTrade.Account} + openTradesMap[key] = openTrade + } + + // loop over all L1 transactions + for i, transaction := range transactions { + // for each L1 transaction, lookup that { ticker, account } in openTradesMap + lookupKey := [2]interface{}{transaction.ticker, transaction.account} + if foundEntry, exists := openTradesMap[lookupKey]; exists { + // update transaction with additional properties - always update the ID property + transaction.id = foundEntry.ID + + // only update notes property for the first {ticker / account } match - don't want notes duplicated on subsequent matches + if !foundEntry.Matched { + transaction.notes = foundEntry.Notes + foundEntry.Matched = true + // save updated entry in map + openTradesMap[lookupKey] = foundEntry + } + // update slice with new transaction value + transactions[i] = transaction + } + } + + // if found a match: + // - update transaction ID + // - check table of matched L1 transactions: + // - if that { ticker / account } combo does NOT exit, update transaction notes AND add that { ticker / account } combo to table + // - if that { ticker / account } combo DOES exit, skip updating notes (this means notes was already updated for the first occurrence of that combo + // and we only want to update notes for the first transaction of the {ticker / account} combo + + /* + Map + - ticker1 + - account1 + - notes1 + + - account2 + - notes2 + + - ticker 2 + - account 2 + - notes 2 + + - ticker 3 + - account 3 + - notes 3 + + + Map + - ticker 1, account 1 + - ID + - notes + + - ticker 1, account 2 + - ID + - notes + + - ticker 2, account 2 + - ID + - notes + + - ticker 3, account 3 + - ID + - notes + */ + return transactions } From 2865f65a153a23fae06cd9e7036d467b9821367f Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sat, 30 Mar 2024 14:52:32 -0400 Subject: [PATCH 06/20] clean up --- parse/journal.go | 79 +++++++++++++++++++----------------------------- 1 file changed, 31 insertions(+), 48 deletions(-) diff --git a/parse/journal.go b/parse/journal.go index 8e0747e..fdfdd79 100644 --- a/parse/journal.go +++ b/parse/journal.go @@ -552,7 +552,33 @@ func (j *Journal) AddOpenTradesData(openTradesPath string, transactions []Transa return nil } - // initialize an empty map to store the openTrades + // initialize an empty map to store the openTrades. + /* + Map structure: + - ticker 1, account 1 + - ticker + - ID + - account + - notes + + - ticker 1, account 2 + - ticker + - ID + - account + - notes + + - ticker 2, account 2 + - ticker + - ID + - account + - notes + + - ticker 3, account 3 + - ticker + - ID + - account + - notes + */ openTradesMap := make(map[[2]interface{}]OpenTrade) // create a unique key for each openTrade (ticker + account) @@ -561,10 +587,11 @@ func (j *Journal) AddOpenTradesData(openTradesPath string, transactions []Transa openTradesMap[key] = openTrade } - // loop over all L1 transactions + // loop over all L1 transactions and update the transactions that have matches in openTradesMap for i, transaction := range transactions { // for each L1 transaction, lookup that { ticker, account } in openTradesMap lookupKey := [2]interface{}{transaction.ticker, transaction.account} + if foundEntry, exists := openTradesMap[lookupKey]; exists { // update transaction with additional properties - always update the ID property transaction.id = foundEntry.ID @@ -573,56 +600,12 @@ func (j *Journal) AddOpenTradesData(openTradesPath string, transactions []Transa if !foundEntry.Matched { transaction.notes = foundEntry.Notes foundEntry.Matched = true - // save updated entry in map + // save updated entry in map, so won't update the notes on subsequent matches openTradesMap[lookupKey] = foundEntry } - // update slice with new transaction value + // update transactions slice with new transaction values transactions[i] = transaction } } - - // if found a match: - // - update transaction ID - // - check table of matched L1 transactions: - // - if that { ticker / account } combo does NOT exit, update transaction notes AND add that { ticker / account } combo to table - // - if that { ticker / account } combo DOES exit, skip updating notes (this means notes was already updated for the first occurrence of that combo - // and we only want to update notes for the first transaction of the {ticker / account} combo - - /* - Map - - ticker1 - - account1 - - notes1 - - - account2 - - notes2 - - - ticker 2 - - account 2 - - notes 2 - - - ticker 3 - - account 3 - - notes 3 - - - Map - - ticker 1, account 1 - - ID - - notes - - - ticker 1, account 2 - - ID - - notes - - - ticker 2, account 2 - - ID - - notes - - - ticker 3, account 3 - - ID - - notes - */ - return transactions } From f38c032b5b2c72b31ddcb2b26a00b412303b8c0b Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sat, 30 Mar 2024 15:03:54 -0400 Subject: [PATCH 07/20] 2nd test passing --- parse/journal.go | 5 ++++- parse/journal_test.go | 7 ++++++- parse/test_data.go | 14 +++++++++++++- testdata/input/open-trades.json | 4 ++-- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/parse/journal.go b/parse/journal.go index fdfdd79..6f80462 100644 --- a/parse/journal.go +++ b/parse/journal.go @@ -598,7 +598,10 @@ func (j *Journal) AddOpenTradesData(openTradesPath string, transactions []Transa // only update notes property for the first {ticker / account } match - don't want notes duplicated on subsequent matches if !foundEntry.Matched { - transaction.notes = foundEntry.Notes + // don't overwrite existing notes (e.g. dividend payment) + if transaction.notes == "" { + transaction.notes = foundEntry.Notes + } foundEntry.Matched = true // save updated entry in map, so won't update the notes on subsequent matches openTradesMap[lookupKey] = foundEntry diff --git a/parse/journal_test.go b/parse/journal_test.go index 53e0919..08d4616 100644 --- a/parse/journal_test.go +++ b/parse/journal_test.go @@ -24,7 +24,7 @@ func TestReadTransactions(t *testing.T) { csvExportPath: "../testdata/input/1-dmc.csv", }, "dividend": { - expectedL1Transactions: expectedTransactions2, + expectedL1Transactions: expectedL1Transactions2, csvExportPath: "../testdata/input/2-dividend.csv", }, "forex": { @@ -83,6 +83,11 @@ func TestAddOpenTickers(t *testing.T) { l1Transactions: expectedL1Transactions1, expectedL2Transactions: expectedL2Transactions1, }, + "dividend": { + openTradesPath: "../testdata/input/open-trades.json", + l1Transactions: expectedL1Transactions2, + expectedL2Transactions: expectedL2Transactions2, + }, } for k, testData := range testDataMap { diff --git a/parse/test_data.go b/parse/test_data.go index ae044eb..3931a5f 100644 --- a/parse/test_data.go +++ b/parse/test_data.go @@ -94,7 +94,7 @@ var expectedL2Transactions1 = []Transaction{ }, } -var expectedTransactions2 = []Transaction{ +var expectedL1Transactions2 = []Transaction{ { date: "2023-06-08", account: "RRSP", @@ -105,6 +105,18 @@ var expectedTransactions2 = []Transaction{ }, } +var expectedL2Transactions2 = []Transaction{ + { + id: "MCDA279", + date: "2023-06-08", + account: "RRSP", + ticker: "MSFT", + action: "Dividend", + dividend: "136", + notes: "MSFT(US5949181045) Cash Dividend USD 0.68 per Share (Ordinary Dividend)", + }, +} + var expectedTransactions3 = []Transaction{ { date: "2023-06-05", diff --git a/testdata/input/open-trades.json b/testdata/input/open-trades.json index d28ca56..e386805 100644 --- a/testdata/input/open-trades.json +++ b/testdata/input/open-trades.json @@ -6,9 +6,9 @@ "notes": "\"stock screen: Div Upcoming (15D)\ncc screen: OTM-60 Day 0.1 4+%\nex-div 3/22 0.20 earn 5/1\n3/25 48.69 50GTCp5c\"" }, { - "id": "2", + "id": "MCDA279", "ticker": "MSFT", - "account": "TFSA", + "account": "RRSP", "notes": "Microsoft Corporation" }, { From a6b9f7abee0916eb08b654255e4c67d9c022853f Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sat, 30 Mar 2024 23:44:09 -0400 Subject: [PATCH 08/20] 3rd test passing --- parse/journal_test.go | 23 ++++++++----- parse/test_data.go | 60 ++++++++++++++++++++++++++++----- testdata/input/open-trades.json | 8 ++--- 3 files changed, 70 insertions(+), 21 deletions(-) diff --git a/parse/journal_test.go b/parse/journal_test.go index 08d4616..b19ffc2 100644 --- a/parse/journal_test.go +++ b/parse/journal_test.go @@ -28,39 +28,39 @@ func TestReadTransactions(t *testing.T) { csvExportPath: "../testdata/input/2-dividend.csv", }, "forex": { - expectedL1Transactions: expectedTransactions3, + expectedL1Transactions: expectedL1Transactions3, csvExportPath: "../testdata/input/3-forex.csv", }, "dividend - withholding tax": { - expectedL1Transactions: expectedTransactions4, + expectedL1Transactions: expectedL1Transactions4, csvExportPath: "../testdata/input/4-dividend-withholding-tax.csv", }, "call assignment": { - expectedL1Transactions: expectedTransactions5, + expectedL1Transactions: expectedL1Transactions5, csvExportPath: "../testdata/input/5-call-assignment.csv", }, "hit target": { - expectedL1Transactions: expectedTransactions6, + expectedL1Transactions: expectedL1Transactions6, csvExportPath: "../testdata/input/6-hit-target.csv", }, "roll out call, roll down call": { - expectedL1Transactions: expectedTransactions7, + expectedL1Transactions: expectedL1Transactions7, csvExportPath: "../testdata/input/7-call-roll-out-roll-down.csv", }, "dividend - withholding tax + other transactions same ticker": { - expectedL1Transactions: expectedTransactions8, + expectedL1Transactions: expectedL1Transactions8, csvExportPath: "../testdata/input/8-dividend-withholding-tax-other-tx.csv", }, "expired OTM put": { - expectedL1Transactions: expectedEmptyTransactions, + expectedL1Transactions: expectedL1EmptyTransactions, csvExportPath: "../testdata/input/9-lapsed-put.csv", }, "expired OTM call, OTM puts": { - expectedL1Transactions: expectedEmptyTransactions, + expectedL1Transactions: expectedL1EmptyTransactions, csvExportPath: "../testdata/input/10-lapsed-call-puts.csv", }, "exercise put, lapsed call for same ticker": { - expectedL1Transactions: expectedTransactions9, + expectedL1Transactions: expectedL1Transactions9, csvExportPath: "../testdata/input/11-exercise-put.csv", }, } @@ -88,6 +88,11 @@ func TestAddOpenTickers(t *testing.T) { l1Transactions: expectedL1Transactions2, expectedL2Transactions: expectedL2Transactions2, }, + "forex": { + openTradesPath: "../testdata/input/open-trades.json", + l1Transactions: expectedL1Transactions3, + expectedL2Transactions: expectedL2Transactions3, + }, } for k, testData := range testDataMap { diff --git a/parse/test_data.go b/parse/test_data.go index 3931a5f..ce11f2d 100644 --- a/parse/test_data.go +++ b/parse/test_data.go @@ -117,7 +117,7 @@ var expectedL2Transactions2 = []Transaction{ }, } -var expectedTransactions3 = []Transaction{ +var expectedL1Transactions3 = []Transaction{ { date: "2023-06-05", account: "Margin", @@ -157,7 +157,51 @@ var expectedTransactions3 = []Transaction{ }, } -var expectedTransactions4 = []Transaction{ +var expectedL2Transactions3 = []Transaction{ + { + date: "2023-06-05", + account: "Margin", + action: "Forex", + commission: "-2", + forexUSDBuy: "4,838.82", + forexUSDCAD: "1.3433", + forexCADSell: "-6499.986906", + notes: "converted all CAD to USD", + }, + { + id: "ABC288", + date: "2023-06-05", + account: "Margin", + action: "Trade", + ticker: "TECK", + buySell: "Buy", + shares: "100", + price: "42.09", + proceeds: "-4209.00", + costBasisBuyOrOption: "-4209.370257250001", + costBasisTotal: "-4209.370257250001", + commission: "-0.37025725", + notes: "\"stock screen: Div Upcoming (15D)\ncc screen: OTM-60 Day 0.1 4+%\nex-div 3/27 0.19 earn 5/2\n3/27 50GTCp5c 10.80\"", + }, + { + id: "ABC288", + date: "2023-06-05", + account: "Margin", + action: "Trade - Option", + ticker: "TECK", + optionContract: "21JUL23 38 C", + buySell: "Sell", + optionContracts: "-1", + price: "5.07", + proceeds: "507.00", + costBasisShare: "0", + costBasisBuyOrOption: "505.944454", + commission: "-1.055546", + notes: "", + }, +} + +var expectedL1Transactions4 = []Transaction{ { date: "2023-06-09", account: "Margin", @@ -169,7 +213,7 @@ var expectedTransactions4 = []Transaction{ }, } -var expectedTransactions5 = []Transaction{ +var expectedL1Transactions5 = []Transaction{ { date: "2023-06-08", account: "RRSP", @@ -189,7 +233,7 @@ var expectedTransactions5 = []Transaction{ }, } -var expectedTransactions6 = []Transaction{ +var expectedL1Transactions6 = []Transaction{ { date: "2023-06-08", account: "TFSA", @@ -224,7 +268,7 @@ var expectedTransactions6 = []Transaction{ }, } -var expectedTransactions7 = []Transaction{ +var expectedL1Transactions7 = []Transaction{ // HPQ roll out { date: "2023-06-12", @@ -286,7 +330,7 @@ var expectedTransactions7 = []Transaction{ }, } -var expectedTransactions8 = []Transaction{ +var expectedL1Transactions8 = []Transaction{ { date: "2023-06-15", account: "TFSA", @@ -327,7 +371,7 @@ var expectedTransactions8 = []Transaction{ }, } -var expectedTransactions9 = []Transaction{ +var expectedL1Transactions9 = []Transaction{ { date: "2023-07-21", account: "RRSP", @@ -366,6 +410,6 @@ var expectedTransactions9 = []Transaction{ }, } -var expectedEmptyTransactions = []Transaction{ +var expectedL1EmptyTransactions = []Transaction{ // should be an empty array because the put option expired out of the money } diff --git a/testdata/input/open-trades.json b/testdata/input/open-trades.json index e386805..43fb64f 100644 --- a/testdata/input/open-trades.json +++ b/testdata/input/open-trades.json @@ -12,9 +12,9 @@ "notes": "Microsoft Corporation" }, { - "id": "3", - "ticker": "GOOGL", - "account": "TFSA", - "notes": "Alphabet Inc." + "id": "ABC288", + "ticker": "TECK", + "account": "Margin", + "notes": "\"stock screen: Div Upcoming (15D)\ncc screen: OTM-60 Day 0.1 4+%\nex-div 3/27 0.19 earn 5/2\n3/27 50GTCp5c 10.80\"" } ] From cf41c016205abfe326748d07f8b36252bd4ab33a Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sat, 30 Mar 2024 23:48:28 -0400 Subject: [PATCH 09/20] 4th test passing --- parse/journal_test.go | 5 +++++ parse/test_data.go | 13 +++++++++++++ testdata/input/open-trades.json | 6 ++++++ 3 files changed, 24 insertions(+) diff --git a/parse/journal_test.go b/parse/journal_test.go index b19ffc2..4f3f609 100644 --- a/parse/journal_test.go +++ b/parse/journal_test.go @@ -93,6 +93,11 @@ func TestAddOpenTickers(t *testing.T) { l1Transactions: expectedL1Transactions3, expectedL2Transactions: expectedL2Transactions3, }, + "dividend - withholding tax": { + openTradesPath: "../testdata/input/open-trades.json", + l1Transactions: expectedL1Transactions4, + expectedL2Transactions: expectedL2Transactions4, + }, } for k, testData := range testDataMap { diff --git a/parse/test_data.go b/parse/test_data.go index ce11f2d..62feee6 100644 --- a/parse/test_data.go +++ b/parse/test_data.go @@ -213,6 +213,19 @@ var expectedL1Transactions4 = []Transaction{ }, } +var expectedL2Transactions4 = []Transaction{ + { + id: "DEF985", + date: "2023-06-09", + account: "Margin", + action: "Dividend", + ticker: "SMG", + dividend: "66", + fee: "-9.9", + notes: "SMG(US8101861065) Payment in Lieu of Dividend (Ordinary Dividend)\n15% tax withdrawn", + }, +} + var expectedL1Transactions5 = []Transaction{ { date: "2023-06-08", diff --git a/testdata/input/open-trades.json b/testdata/input/open-trades.json index 43fb64f..5887acf 100644 --- a/testdata/input/open-trades.json +++ b/testdata/input/open-trades.json @@ -16,5 +16,11 @@ "ticker": "TECK", "account": "Margin", "notes": "\"stock screen: Div Upcoming (15D)\ncc screen: OTM-60 Day 0.1 4+%\nex-div 3/27 0.19 earn 5/2\n3/27 50GTCp5c 10.80\"" + }, + { + "id": "DEF985", + "ticker": "SMG", + "account": "Margin", + "notes": "foobar - should not replace dividend" } ] From 98328491274aa8cb3f38fecccb17bfd5a76a4dc7 Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sat, 30 Mar 2024 23:59:03 -0400 Subject: [PATCH 10/20] 5th test passing --- parse/journal_test.go | 5 +++++ parse/test_data.go | 21 +++++++++++++++++++++ testdata/input/open-trades.json | 8 +++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/parse/journal_test.go b/parse/journal_test.go index 4f3f609..273bca5 100644 --- a/parse/journal_test.go +++ b/parse/journal_test.go @@ -98,6 +98,11 @@ func TestAddOpenTickers(t *testing.T) { l1Transactions: expectedL1Transactions4, expectedL2Transactions: expectedL2Transactions4, }, + "call assignment": { + openTradesPath: "../testdata/input/open-trades.json", + l1Transactions: expectedL1Transactions5, + expectedL2Transactions: expectedL2Transactions5, + }, } for k, testData := range testDataMap { diff --git a/parse/test_data.go b/parse/test_data.go index 62feee6..95535ee 100644 --- a/parse/test_data.go +++ b/parse/test_data.go @@ -246,6 +246,27 @@ var expectedL1Transactions5 = []Transaction{ }, } +var expectedL2Transactions5 = []Transaction{ + { + id: "ZXYK999", + date: "2023-06-08", + account: "RRSP", + action: "Trade - Option - Assignment", + actionModified: "Trade - Option - Assignment", + ticker: "FDX", + optionContract: "16JUN23 155 C", + buySell: "Sell", + shares: "-100", + price: "155", + proceeds: "15500.00", + costBasisShare: "-172.67370257", + costBasisTotal: "17267.370257", // import IBKR value and multiply by -1 + realizedPL: "3873.744617", // imports IBKR value + commission: "-0.1385", + notes: "called away for profit", + }, +} + var expectedL1Transactions6 = []Transaction{ { date: "2023-06-08", diff --git a/testdata/input/open-trades.json b/testdata/input/open-trades.json index 5887acf..9980d93 100644 --- a/testdata/input/open-trades.json +++ b/testdata/input/open-trades.json @@ -21,6 +21,12 @@ "id": "DEF985", "ticker": "SMG", "account": "Margin", - "notes": "foobar - should not replace dividend" + "notes": "foobar - should not replace notes" + }, + { + "id": "ZXYK999", + "ticker": "FDX", + "account": "RRSP", + "notes": "foobar - should not replace notes" } ] From f5d0fb4c8b8506cdecf23f8e9111621634f7097a Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sun, 31 Mar 2024 00:06:36 -0400 Subject: [PATCH 11/20] 6th test passing --- parse/journal_test.go | 5 +++++ parse/test_data.go | 37 +++++++++++++++++++++++++++++++++ testdata/input/open-trades.json | 6 ++++++ 3 files changed, 48 insertions(+) diff --git a/parse/journal_test.go b/parse/journal_test.go index 273bca5..02695e8 100644 --- a/parse/journal_test.go +++ b/parse/journal_test.go @@ -103,6 +103,11 @@ func TestAddOpenTickers(t *testing.T) { l1Transactions: expectedL1Transactions5, expectedL2Transactions: expectedL2Transactions5, }, + "hit target": { + openTradesPath: "../testdata/input/open-trades.json", + l1Transactions: expectedL1Transactions6, + expectedL2Transactions: expectedL2Transactions6, + }, } for k, testData := range testDataMap { diff --git a/parse/test_data.go b/parse/test_data.go index 95535ee..8b232f0 100644 --- a/parse/test_data.go +++ b/parse/test_data.go @@ -302,6 +302,43 @@ var expectedL1Transactions6 = []Transaction{ }, } +var expectedL2Transactions6 = []Transaction{ + { + id: "1234", + date: "2023-06-08", + account: "TFSA", + action: "Trade - Close", + actionModified: "Trade - Close", + ticker: "BBWI", + buySell: "Sell", + shares: "-100", + price: "41.44", + proceeds: "4144.00", + costBasisShare: "-38.17000000", + costBasisBuyOrOption: "", + costBasisTotal: "3817", // imports IBKR value and multiply by -1 + realizedPL: "326.482091", // imports IBKR value + commission: "-0.51790925", + notes: "hit GTC target", + }, + { + id: "1234", + date: "2023-06-08", + account: "TFSA", + action: "Trade - Option", + ticker: "BBWI", + optionContract: "16JUN23 35 C", + buySell: "Buy", + optionContracts: "1", + price: "6.53", + proceeds: "-653.00", + costBasisShare: "0", + costBasisBuyOrOption: "-654.05155", + commission: "-1.05155", + notes: "hit GTC target", + }, +} + var expectedL1Transactions7 = []Transaction{ // HPQ roll out { diff --git a/testdata/input/open-trades.json b/testdata/input/open-trades.json index 9980d93..83925d1 100644 --- a/testdata/input/open-trades.json +++ b/testdata/input/open-trades.json @@ -28,5 +28,11 @@ "ticker": "FDX", "account": "RRSP", "notes": "foobar - should not replace notes" + }, + { + "id": "1234", + "ticker": "BBWI", + "account": "TFSA", + "notes": "foobar - should not replace notes" } ] From 0b8831b857664851884444dfa26fae6094338a99 Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sun, 31 Mar 2024 00:17:57 -0400 Subject: [PATCH 12/20] 7th test passing --- parse/journal_test.go | 5 +++ parse/test_data.go | 68 +++++++++++++++++++++++++++++++++ testdata/input/open-trades.json | 13 +++++++ 3 files changed, 86 insertions(+) diff --git a/parse/journal_test.go b/parse/journal_test.go index 02695e8..6104a17 100644 --- a/parse/journal_test.go +++ b/parse/journal_test.go @@ -108,6 +108,11 @@ func TestAddOpenTickers(t *testing.T) { l1Transactions: expectedL1Transactions6, expectedL2Transactions: expectedL2Transactions6, }, + "roll out call, roll down call": { + openTradesPath: "../testdata/input/open-trades.json", + l1Transactions: expectedL1Transactions7, + expectedL2Transactions: expectedL2Transactions7, + }, } for k, testData := range testDataMap { diff --git a/parse/test_data.go b/parse/test_data.go index 8b232f0..56d773e 100644 --- a/parse/test_data.go +++ b/parse/test_data.go @@ -401,6 +401,74 @@ var expectedL1Transactions7 = []Transaction{ }, } +var expectedL2Transactions7 = []Transaction{ + // HPQ roll out + { + id: "MR06", + date: "2023-06-12", + account: "RRSP", + action: "Trade - Option", + ticker: "HPQ", + optionContract: "16JUN23 27 C", + buySell: "Buy", + optionContracts: "2", + price: "3.32", + proceeds: "-664.00", + costBasisShare: "0", + costBasisBuyOrOption: "-664.6581", + commission: "-0.6581", + notes: "Rolled out to 18Aug23 for 0.30CR", + }, + { + id: "MR06", + date: "2023-06-12", + account: "RRSP", + action: "Trade - Option", + ticker: "HPQ", + optionContract: "18AUG23 27 C", + buySell: "Sell", + optionContracts: "-2", + price: "3.62", + proceeds: "724.00", + costBasisShare: "0", + costBasisBuyOrOption: "723.331228", + commission: "-0.668772", + }, + + // STNG roll down + { + id: "MCDO653", + date: "2023-06-12", + account: "RRSP", + action: "Trade - Option", + ticker: "STNG", + optionContract: "21JUL23 46 C", + buySell: "Buy", + optionContracts: "1", + price: "1.97", + proceeds: "-197.00", + costBasisShare: "0", + costBasisBuyOrOption: "-197.64905", + commission: "-0.64905", + notes: "6/12 40.59 rolled down to 21Jul23 44C for 0.82CR", + }, + { + id: "MCDO653", + date: "2023-06-12", + account: "RRSP", + action: "Trade - Option", + ticker: "STNG", + optionContract: "21JUL23 44 C", + buySell: "Sell", + optionContracts: "-1", + price: "2.79", + proceeds: "279.00", + costBasisShare: "0", + costBasisBuyOrOption: "278.346278", + commission: "-0.653722", + }, +} + var expectedL1Transactions8 = []Transaction{ { date: "2023-06-15", diff --git a/testdata/input/open-trades.json b/testdata/input/open-trades.json index 83925d1..7acb06f 100644 --- a/testdata/input/open-trades.json +++ b/testdata/input/open-trades.json @@ -34,5 +34,18 @@ "ticker": "BBWI", "account": "TFSA", "notes": "foobar - should not replace notes" + }, + { + "id": "MR06", + "ticker": "HPQ", + "account": "RRSP", + "notes": "Rolled out to 18Aug23 for 0.30CR" + }, + { + "id": "MCDO653", + "ticker": "STNG", + "account": "RRSP", + "notes": "6/12 40.59 rolled down to 21Jul23 44C for 0.82CR" } + ] From ff96e96bd93009e5b27527fb0f673e3334ccf6a7 Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sun, 31 Mar 2024 00:27:02 -0400 Subject: [PATCH 13/20] 8th test passing --- parse/journal_test.go | 5 ++++ parse/test_data.go | 44 +++++++++++++++++++++++++++++++++ testdata/input/open-trades.json | 7 +++++- 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/parse/journal_test.go b/parse/journal_test.go index 6104a17..86b61c7 100644 --- a/parse/journal_test.go +++ b/parse/journal_test.go @@ -113,6 +113,11 @@ func TestAddOpenTickers(t *testing.T) { l1Transactions: expectedL1Transactions7, expectedL2Transactions: expectedL2Transactions7, }, + "dividend - withholding tax + other transactions same ticker": { + openTradesPath: "../testdata/input/open-trades.json", + l1Transactions: expectedL1Transactions8, + expectedL2Transactions: expectedL2Transactions8, + }, } for k, testData := range testDataMap { diff --git a/parse/test_data.go b/parse/test_data.go index 56d773e..a9e3ab6 100644 --- a/parse/test_data.go +++ b/parse/test_data.go @@ -510,6 +510,50 @@ var expectedL1Transactions8 = []Transaction{ }, } +var expectedL2Transactions8 = []Transaction{ + { + id: "MCDA462", + date: "2023-06-15", + account: "TFSA", + action: "Trade - Option", + ticker: "MOS", + optionContract: "16JUN23 32.5 C", + buySell: "Buy", + optionContracts: "2", + price: "3.125", + proceeds: "-625.00", + costBasisShare: "0", + costBasisBuyOrOption: "-625.6581", + commission: "-0.6581", + notes: "6/15 33.92, -180 if CA, rolled out to 21Jul 32.5C 2x0.70CR, no 31.5C strike so rolled up", + }, + { + id: "MCDA462", + date: "2023-06-15", + account: "TFSA", + action: "Trade - Option", + ticker: "MOS", + optionContract: "21JUL23 32.5 C", + buySell: "Sell", + optionContracts: "-2", + price: "3.825", + proceeds: "765.00", + costBasisShare: "0", + costBasisBuyOrOption: "764.3309", + commission: "-0.6691", + }, + { + id: "MCDA462", + date: "2023-06-15", + account: "TFSA", + action: "Dividend", + ticker: "MOS", + dividend: "40", + fee: "-6", + notes: "MOS(US61945C1036) Cash Dividend USD 0.20 per Share (Ordinary Dividend)\n15% tax withdrawn", + }, +} + var expectedL1Transactions9 = []Transaction{ { date: "2023-07-21", diff --git a/testdata/input/open-trades.json b/testdata/input/open-trades.json index 7acb06f..c4d1771 100644 --- a/testdata/input/open-trades.json +++ b/testdata/input/open-trades.json @@ -46,6 +46,11 @@ "ticker": "STNG", "account": "RRSP", "notes": "6/12 40.59 rolled down to 21Jul23 44C for 0.82CR" + }, + { + "id": "MCDA462", + "ticker": "MOS", + "account": "TFSA", + "notes": "6/15 33.92, -180 if CA, rolled out to 21Jul 32.5C 2x0.70CR, no 31.5C strike so rolled up" } - ] From c0fbb15d0b075319fa89d69909d8c40a2dc275bc Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sun, 31 Mar 2024 00:44:25 -0400 Subject: [PATCH 14/20] 9th test passing --- parse/journal_test.go | 5 ++++ parse/test_data.go | 41 +++++++++++++++++++++++++++++++++ testdata/input/open-trades.json | 7 ++++++ 3 files changed, 53 insertions(+) diff --git a/parse/journal_test.go b/parse/journal_test.go index 86b61c7..9ceb267 100644 --- a/parse/journal_test.go +++ b/parse/journal_test.go @@ -118,6 +118,11 @@ func TestAddOpenTickers(t *testing.T) { l1Transactions: expectedL1Transactions8, expectedL2Transactions: expectedL2Transactions8, }, + "expired OTM put": { + openTradesPath: "../testdata/input/open-trades.json", + l1Transactions: expectedL1Transactions9, + expectedL2Transactions: expectedL2Transactions9, + }, } for k, testData := range testDataMap { diff --git a/parse/test_data.go b/parse/test_data.go index a9e3ab6..46ee810 100644 --- a/parse/test_data.go +++ b/parse/test_data.go @@ -593,6 +593,47 @@ var expectedL1Transactions9 = []Transaction{ }, } +var expectedL2Transactions9 = []Transaction{ + { + id: "MCDO653", + date: "2023-07-21", + account: "RRSP", + action: "Trade - Option - Exercise", + actionModified: "Trade - Option - Exercise", + ticker: "STNG", + optionContract: "21JUL23 50 P", + buySell: "Sell", + shares: "-100", + price: "50", + proceeds: "5000.00", + costBasisShare: "-62.61370257", + costBasisBuyOrOption: "", + costBasisTotal: "6261.370257", // imports IBKR value and multiply by -1 + realizedPL: "-1791.673807", // imports IBKR value + commission: "-0.0545", + notes: "exercised long put", + }, + { + id: "MCDA419", + date: "2023-07-21", + account: "RRSP", + action: "Trade - Option - Exercise", + actionModified: "Trade - Option - Exercise", + ticker: "TGT", + optionContract: "21JUL23 140 P", + buySell: "Sell", + shares: "-100", + price: "140", + proceeds: "14000.00", + costBasisShare: "-163.60370257", + costBasisBuyOrOption: "", + costBasisTotal: "16360.370257", // imports IBKR value and multiply by -1 + realizedPL: "-3011.535807", // imports IBKR value + commission: "-0.1265", + notes: "exercised long put", + }, +} + var expectedL1EmptyTransactions = []Transaction{ // should be an empty array because the put option expired out of the money } diff --git a/testdata/input/open-trades.json b/testdata/input/open-trades.json index c4d1771..9636aaf 100644 --- a/testdata/input/open-trades.json +++ b/testdata/input/open-trades.json @@ -52,5 +52,12 @@ "ticker": "MOS", "account": "TFSA", "notes": "6/15 33.92, -180 if CA, rolled out to 21Jul 32.5C 2x0.70CR, no 31.5C strike so rolled up" + }, + { + "id": "MCDA419", + "ticker": "TGT", + "account": "RRSP", + "notes": "6/15 33.92, -180 if CA, rolled out to 21Jul 32.5C 2x0.70CR, no 31.5C strike so rolled up" } + ] From a4b9f82dc02fde142bf8db77d13eebbe904f58d2 Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sun, 31 Mar 2024 00:47:49 -0400 Subject: [PATCH 15/20] 10th test passing --- parse/journal_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/parse/journal_test.go b/parse/journal_test.go index 9ceb267..7b0dd3b 100644 --- a/parse/journal_test.go +++ b/parse/journal_test.go @@ -123,6 +123,11 @@ func TestAddOpenTickers(t *testing.T) { l1Transactions: expectedL1Transactions9, expectedL2Transactions: expectedL2Transactions9, }, + "expired OTM call, OTM puts": { + openTradesPath: "../testdata/input/open-trades.json", + l1Transactions: expectedL1EmptyTransactions, + expectedL2Transactions: expectedL1EmptyTransactions, + }, } for k, testData := range testDataMap { From 50c9c08b5004c17183e5a9e0ac324c9d2ffcce25 Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sun, 31 Mar 2024 00:59:42 -0400 Subject: [PATCH 16/20] clean up --- parse/journal_test.go | 2 +- testdata/input/open-trades.json | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/parse/journal_test.go b/parse/journal_test.go index 7b0dd3b..9371143 100644 --- a/parse/journal_test.go +++ b/parse/journal_test.go @@ -118,7 +118,7 @@ func TestAddOpenTickers(t *testing.T) { l1Transactions: expectedL1Transactions8, expectedL2Transactions: expectedL2Transactions8, }, - "expired OTM put": { + "exercise put, lapsed call for same ticker": { openTradesPath: "../testdata/input/open-trades.json", l1Transactions: expectedL1Transactions9, expectedL2Transactions: expectedL2Transactions9, diff --git a/testdata/input/open-trades.json b/testdata/input/open-trades.json index 9636aaf..c490dcc 100644 --- a/testdata/input/open-trades.json +++ b/testdata/input/open-trades.json @@ -59,5 +59,4 @@ "account": "RRSP", "notes": "6/15 33.92, -180 if CA, rolled out to 21Jul 32.5C 2x0.70CR, no 31.5C strike so rolled up" } - ] From 97a3dadc201d23ef9e71be752f101d9c36444295 Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sun, 31 Mar 2024 04:03:20 -0400 Subject: [PATCH 17/20] support for appending notes to called away calls --- parse/journal.go | 7 ++++++- parse/test_data.go | 2 +- testdata/input/open-trades.json | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/parse/journal.go b/parse/journal.go index 6f80462..c0f94cc 100644 --- a/parse/journal.go +++ b/parse/journal.go @@ -12,6 +12,8 @@ import ( "strings" ) +const CALLED_AWAY_NOTE = "called away for profit" + type Transaction struct { id string // trade id will be imported from a separate JSON file ticker string @@ -266,7 +268,7 @@ func (j *Journal) ReadTransactions(csvPath string) []Transaction { // short call option assignments (i.e. short calls called away) will have a price of 0 case "C": singleTransaction.actionModified = "Trade - Option - Assignment" - singleTransaction.notes = "called away for profit" + singleTransaction.notes = CALLED_AWAY_NOTE // long put option exercises will have a price of 0 case "P": @@ -601,6 +603,9 @@ func (j *Journal) AddOpenTradesData(openTradesPath string, transactions []Transa // don't overwrite existing notes (e.g. dividend payment) if transaction.notes == "" { transaction.notes = foundEntry.Notes + } else if transaction.notes == CALLED_AWAY_NOTE { + // append to the note if called away + transaction.notes = foundEntry.Notes + "\n" + CALLED_AWAY_NOTE } foundEntry.Matched = true // save updated entry in map, so won't update the notes on subsequent matches diff --git a/parse/test_data.go b/parse/test_data.go index 46ee810..af327bc 100644 --- a/parse/test_data.go +++ b/parse/test_data.go @@ -263,7 +263,7 @@ var expectedL2Transactions5 = []Transaction{ costBasisTotal: "17267.370257", // import IBKR value and multiply by -1 realizedPL: "3873.744617", // imports IBKR value commission: "-0.1385", - notes: "called away for profit", + notes: "195.00 ex-div tom, 1/31 187.90 - price jumped sharply up yesterday, created GTC roll up to 19Apr 145C 2.50DB to squeeze out 250 more (currently 147 profit if called away, CA)\n3/26 195.00 ex-div tom, 0 TP; ok to get called away for small profit (147)\ncalled away for profit", }, } diff --git a/testdata/input/open-trades.json b/testdata/input/open-trades.json index c490dcc..f133bd7 100644 --- a/testdata/input/open-trades.json +++ b/testdata/input/open-trades.json @@ -27,7 +27,7 @@ "id": "ZXYK999", "ticker": "FDX", "account": "RRSP", - "notes": "foobar - should not replace notes" + "notes": "195.00 ex-div tom, 1/31 187.90 - price jumped sharply up yesterday, created GTC roll up to 19Apr 145C 2.50DB to squeeze out 250 more (currently 147 profit if called away, CA)\n3/26 195.00 ex-div tom, 0 TP; ok to get called away for small profit (147)" }, { "id": "1234", From 80e8314cc01b0e55c52f4ddd3deb672b7103e1f1 Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sun, 31 Mar 2024 04:05:41 -0400 Subject: [PATCH 18/20] Update .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index d1baad6..e508b9b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +transactions.csv +open-trades.json + # Binaries for programs and plugins *.exe *.exe~ From 48b85c8873685925c2703e70339a0818cbfee496 Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sun, 31 Mar 2024 04:32:49 -0400 Subject: [PATCH 19/20] support for appending notes to hitting GTC target --- parse/journal.go | 8 ++++++-- parse/test_data.go | 2 +- testdata/input/open-trades.json | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/parse/journal.go b/parse/journal.go index c0f94cc..e528327 100644 --- a/parse/journal.go +++ b/parse/journal.go @@ -13,6 +13,7 @@ import ( ) const CALLED_AWAY_NOTE = "called away for profit" +const HIT_GTC_TARGET_NOTE = "hit GTC target" type Transaction struct { id string // trade id will be imported from a separate JSON file @@ -306,8 +307,8 @@ func (j *Journal) ReadTransactions(csvPath string) []Transaction { costBasisPerShare := costBasisTotal / shares // always round to 8 decimal places - Go sometimes is slightly off in decimal calculations singleTransaction.costBasisShare = fmt.Sprintf("%.8f", costBasisPerShare) - singleTransaction.notes = "hit GTC target" - transaction.notes = "hit GTC target" + singleTransaction.notes = HIT_GTC_TARGET_NOTE + transaction.notes = HIT_GTC_TARGET_NOTE j.updateSingleTransaction(transaction.ticker, *singleTransaction) } @@ -606,6 +607,9 @@ func (j *Journal) AddOpenTradesData(openTradesPath string, transactions []Transa } else if transaction.notes == CALLED_AWAY_NOTE { // append to the note if called away transaction.notes = foundEntry.Notes + "\n" + CALLED_AWAY_NOTE + } else if transaction.notes == HIT_GTC_TARGET_NOTE { + // append to the note if hit GTC target + transaction.notes = foundEntry.Notes + "\n" + HIT_GTC_TARGET_NOTE } foundEntry.Matched = true // save updated entry in map, so won't update the notes on subsequent matches diff --git a/parse/test_data.go b/parse/test_data.go index af327bc..3b8c269 100644 --- a/parse/test_data.go +++ b/parse/test_data.go @@ -319,7 +319,7 @@ var expectedL2Transactions6 = []Transaction{ costBasisTotal: "3817", // imports IBKR value and multiply by -1 realizedPL: "326.482091", // imports IBKR value commission: "-0.51790925", - notes: "hit GTC target", + notes: "3/25 50GTCp5c 86.87\nhit GTC target", }, { id: "1234", diff --git a/testdata/input/open-trades.json b/testdata/input/open-trades.json index f133bd7..28ba7a0 100644 --- a/testdata/input/open-trades.json +++ b/testdata/input/open-trades.json @@ -33,7 +33,7 @@ "id": "1234", "ticker": "BBWI", "account": "TFSA", - "notes": "foobar - should not replace notes" + "notes": "3/25 50GTCp5c 86.87" }, { "id": "MR06", From ef9546212032f95c609a0e48bfe7c849cd2aea83 Mon Sep 17 00:00:00 2001 From: Misha <15269764+gomisha@users.noreply.github.com> Date: Sun, 31 Mar 2024 11:59:27 -0400 Subject: [PATCH 20/20] added trade ID to exported CSV --- parse/journal.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parse/journal.go b/parse/journal.go index e528327..cb34f1d 100644 --- a/parse/journal.go +++ b/parse/journal.go @@ -493,7 +493,7 @@ func (j *Journal) ToCsv(txs []Transaction) { row = append(row, "") row = append(row, "") row = append(row, tx.ticker) - row = append(row, "") + row = append(row, tx.id) row = append(row, "") row = append(row, tx.optionContract) row = append(row, tx.buySell)