From d097b01288200e029be55b32a943feb01fa615f4 Mon Sep 17 00:00:00 2001 From: Natercio Moniz Date: Tue, 18 Nov 2025 16:07:41 +0000 Subject: [PATCH] print a pretty table with correct country coder --- cmd/any2anexoj-cli/main.go | 4 +- go.mod | 6 ++ go.sum | 14 ++++ internal/mocks/mocks_gen.go | 76 ++++++++++++++++++++ internal/record.go | 17 ----- internal/report.go | 44 ++++++++---- internal/report_test.go | 5 +- internal/stdout.go | 34 --------- internal/stdout_test.go | 93 ------------------------- internal/table_writer.go | 53 ++++++++++++++ internal/table_writer_test.go | 116 +++++++++++++++++++++++++++++++ internal/trading212/constants.go | 7 ++ internal/trading212/record.go | 9 +++ 13 files changed, 318 insertions(+), 160 deletions(-) delete mode 100644 internal/record.go delete mode 100644 internal/stdout.go delete mode 100644 internal/stdout_test.go create mode 100644 internal/table_writer.go create mode 100644 internal/table_writer_test.go create mode 100644 internal/trading212/constants.go diff --git a/cmd/any2anexoj-cli/main.go b/cmd/any2anexoj-cli/main.go index 74e92bc..9fba3cf 100644 --- a/cmd/any2anexoj-cli/main.go +++ b/cmd/any2anexoj-cli/main.go @@ -51,7 +51,7 @@ func run(ctx context.Context, platform string) error { reader := factory() - writer := internal.NewStdOutLogger() + writer := internal.NewTableWriter(os.Stdout) eg.Go(func() error { return internal.BuildReport(ctx, reader, writer) @@ -62,7 +62,7 @@ func run(ctx context.Context, platform string) error { return err } - slog.Info("Finish processing statement") + writer.Render() return nil } diff --git a/go.mod b/go.mod index 86d9488..d775733 100644 --- a/go.mod +++ b/go.mod @@ -5,12 +5,18 @@ go 1.25.3 require ( go.uber.org/mock v0.6.0 golang.org/x/sync v0.18.0 + github.com/biter777/countries v1.7.5 ) require ( + github.com/jedib0t/go-pretty/v6 v6.7.2 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/spf13/pflag v1.0.10 // indirect golang.org/x/mod v0.27.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.22.0 // indirect golang.org/x/tools v0.36.0 // indirect ) diff --git a/go.sum b/go.sum index 101ec92..84d147b 100644 --- a/go.sum +++ b/go.sum @@ -1,21 +1,35 @@ +github.com/biter777/countries v1.7.5 h1:MJ+n3+rSxWQdqVJU8eBy9RqcdH6ePPn4PJHocVWUa+Q= +github.com/biter777/countries v1.7.5/go.mod h1:1HSpZ526mYqKJcpT5Ti1kcGQ0L0SrXWIaptUWjFfv2E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/jedib0t/go-pretty/v6 v6.7.2 h1:EYWgQNIH/+JsyHki7ns9OHyBKuHPkzrBo02uYjran7w= +github.com/jedib0t/go-pretty/v6 v6.7.2/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/mocks/mocks_gen.go b/internal/mocks/mocks_gen.go index 104d0bf..0c7df4a 100644 --- a/internal/mocks/mocks_gen.go +++ b/internal/mocks/mocks_gen.go @@ -106,6 +106,82 @@ func (m *MockRecord) EXPECT() *MockRecordMockRecorder { return m.recorder } +// AssetCountry mocks base method. +func (m *MockRecord) AssetCountry() int64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AssetCountry") + ret0, _ := ret[0].(int64) + return ret0 +} + +// AssetCountry indicates an expected call of AssetCountry. +func (mr *MockRecordMockRecorder) AssetCountry() *MockRecordAssetCountryCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AssetCountry", reflect.TypeOf((*MockRecord)(nil).AssetCountry)) + return &MockRecordAssetCountryCall{Call: call} +} + +// MockRecordAssetCountryCall wrap *gomock.Call +type MockRecordAssetCountryCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockRecordAssetCountryCall) Return(arg0 int64) *MockRecordAssetCountryCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockRecordAssetCountryCall) Do(f func() int64) *MockRecordAssetCountryCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockRecordAssetCountryCall) DoAndReturn(f func() int64) *MockRecordAssetCountryCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// BrokerCountry mocks base method. +func (m *MockRecord) BrokerCountry() int64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BrokerCountry") + ret0, _ := ret[0].(int64) + return ret0 +} + +// BrokerCountry indicates an expected call of BrokerCountry. +func (mr *MockRecordMockRecorder) BrokerCountry() *MockRecordBrokerCountryCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BrokerCountry", reflect.TypeOf((*MockRecord)(nil).BrokerCountry)) + return &MockRecordBrokerCountryCall{Call: call} +} + +// MockRecordBrokerCountryCall wrap *gomock.Call +type MockRecordBrokerCountryCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockRecordBrokerCountryCall) Return(arg0 int64) *MockRecordBrokerCountryCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockRecordBrokerCountryCall) Do(f func() int64) *MockRecordBrokerCountryCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockRecordBrokerCountryCall) DoAndReturn(f func() int64) *MockRecordBrokerCountryCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // Fees mocks base method. func (m *MockRecord) Fees() decimal.Decimal { m.ctrl.T.Helper() diff --git a/internal/record.go b/internal/record.go deleted file mode 100644 index 1fe43be..0000000 --- a/internal/record.go +++ /dev/null @@ -1,17 +0,0 @@ -package internal - -import ( - "time" - - "github.com/shopspring/decimal" -) - -type Record interface { - Symbol() string - Side() Side - Price() decimal.Decimal - Quantity() decimal.Decimal - Timestamp() time.Time - Fees() decimal.Decimal - Taxes() decimal.Decimal -} diff --git a/internal/report.go b/internal/report.go index 753bc88..226ac0d 100644 --- a/internal/report.go +++ b/internal/report.go @@ -10,11 +10,39 @@ import ( "github.com/shopspring/decimal" ) +type Record interface { + Symbol() string + BrokerCountry() int64 + AssetCountry() int64 + Side() Side + Price() decimal.Decimal + Quantity() decimal.Decimal + Timestamp() time.Time + Fees() decimal.Decimal + Taxes() decimal.Decimal +} + type RecordReader interface { // ReadRecord should return Records until an error is found. ReadRecord(context.Context) (Record, error) } +type ReportItem struct { + Symbol string + BrokerCountry int64 + AssetCountry int64 + BuyValue decimal.Decimal + BuyTimestamp time.Time + SellValue decimal.Decimal + SellTimestamp time.Time + Fees decimal.Decimal + Taxes decimal.Decimal +} + +func (ri ReportItem) RealisedPnL() decimal.Decimal { + return ri.SellValue.Sub(ri.BuyValue) +} + type ReportWriter interface { // ReportWriter writes report items Write(context.Context, ReportItem) error @@ -79,6 +107,9 @@ func processRecord(ctx context.Context, q *FillerQueue, rec Record, writer Repor sellValue := matchedQty.Mul(rec.Price()) err := writer.Write(ctx, ReportItem{ + Symbol: rec.Symbol(), + BrokerCountry: rec.BrokerCountry(), + AssetCountry: rec.AssetCountry(), BuyValue: buyValue, BuyTimestamp: buy.Timestamp(), SellValue: sellValue, @@ -97,16 +128,3 @@ func processRecord(ctx context.Context, q *FillerQueue, rec Record, writer Repor return nil } - -type ReportItem struct { - BuyValue decimal.Decimal - BuyTimestamp time.Time - SellValue decimal.Decimal - SellTimestamp time.Time - Fees decimal.Decimal - Taxes decimal.Decimal -} - -func (ri ReportItem) RealisedPnL() decimal.Decimal { - return ri.SellValue.Sub(ri.BuyValue) -} diff --git a/internal/report_test.go b/internal/report_test.go index 70b64c9..d9edfe4 100644 --- a/internal/report_test.go +++ b/internal/report_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/biter777/countries" "github.com/nmoniz/any2anexoj/internal" "github.com/nmoniz/any2anexoj/internal/mocks" "github.com/shopspring/decimal" @@ -50,10 +51,12 @@ func TestBuildReport(t *testing.T) { func mockRecord(ctrl *gomock.Controller, price, quantity float64, side internal.Side, ts time.Time) *mocks.MockRecord { rec := mocks.NewMockRecord(ctrl) + rec.EXPECT().Symbol().Return("TEST").AnyTimes() + rec.EXPECT().BrokerCountry().Return(int64(countries.PT)).AnyTimes() + rec.EXPECT().AssetCountry().Return(int64(countries.USA)).AnyTimes() rec.EXPECT().Price().Return(decimal.NewFromFloat(price)).AnyTimes() rec.EXPECT().Quantity().Return(decimal.NewFromFloat(quantity)).AnyTimes() rec.EXPECT().Side().Return(side).AnyTimes() - rec.EXPECT().Symbol().Return("TEST").AnyTimes() rec.EXPECT().Timestamp().Return(ts).AnyTimes() rec.EXPECT().Fees().Return(decimal.Decimal{}).AnyTimes() rec.EXPECT().Taxes().Return(decimal.Decimal{}).AnyTimes() diff --git a/internal/stdout.go b/internal/stdout.go deleted file mode 100644 index 0ced987..0000000 --- a/internal/stdout.go +++ /dev/null @@ -1,34 +0,0 @@ -package internal - -import ( - "context" - "fmt" - "io" - "os" - "time" -) - -// ReportLogger writes a simple, human readable, line to the provided io.Writer for each -// ReportItem received. -type ReportLogger struct { - counter int - writer io.Writer -} - -func NewStdOutLogger() *ReportLogger { - return &ReportLogger{ - writer: os.Stdout, - } -} - -func NewReportLogger(w io.Writer) *ReportLogger { - return &ReportLogger{ - writer: w, - } -} - -func (rl *ReportLogger) Write(_ context.Context, ri ReportItem) error { - rl.counter++ - _, err := fmt.Fprintf(rl.writer, "%6d: realised %s on %s\n", rl.counter, ri.RealisedPnL().String(), ri.SellTimestamp.Format(time.RFC3339)) - return err -} diff --git a/internal/stdout_test.go b/internal/stdout_test.go deleted file mode 100644 index fd181e9..0000000 --- a/internal/stdout_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package internal_test - -import ( - "bytes" - "fmt" - "testing" - "time" - - "github.com/nmoniz/any2anexoj/internal" - "github.com/shopspring/decimal" -) - -func TestReportLogger_Write(t *testing.T) { - tNow := time.Now() - - tests := []struct { - name string - items []internal.ReportItem - want []string - }{ - { - name: "empty", - }, - { - name: "single item positive", - items: []internal.ReportItem{ - { - BuyValue: decimal.NewFromFloat(100.0), - SellValue: decimal.NewFromFloat(200.0), - SellTimestamp: tNow, - }, - }, - want: []string{ - fmt.Sprintf("%6d: realised 100 on %s\n", 1, tNow.Format(time.RFC3339)), - }, - }, - { - name: "single item negative", - items: []internal.ReportItem{ - { - BuyValue: decimal.NewFromFloat(200.0), - SellValue: decimal.NewFromFloat(150.0), - SellTimestamp: tNow, - }, - }, - want: []string{ - fmt.Sprintf("%6d: realised -50 on %s\n", 1, tNow.Format(time.RFC3339)), - }, - }, - { - name: "multiple items", - items: []internal.ReportItem{ - { - BuyValue: decimal.NewFromFloat(100.0), - SellValue: decimal.NewFromFloat(200.0), - SellTimestamp: tNow, - }, - { - BuyValue: decimal.NewFromFloat(200.0), - SellValue: decimal.NewFromFloat(150.0), - SellTimestamp: tNow.Add(1), - }, - }, - want: []string{ - fmt.Sprintf("%6d: realised 100 on %s\n", 1, tNow.Format(time.RFC3339)), - fmt.Sprintf("%6d: realised -50 on %s\n", 2, tNow.Add(1).Format(time.RFC3339)), - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - buf := new(bytes.Buffer) - rw := internal.NewReportLogger(buf) - - for _, item := range tt.items { - err := rw.Write(t.Context(), item) - if err != nil { - t.Fatalf("unexpected error on write: %v", err) - } - } - - for _, wantLine := range tt.want { - gotLine, err := buf.ReadString(byte('\n')) - if err != nil { - t.Fatalf("unexpected error on buffer read: %v", err) - } - if wantLine != gotLine { - t.Fatalf("want line %q but got %q", wantLine, gotLine) - } - } - }) - } -} diff --git a/internal/table_writer.go b/internal/table_writer.go new file mode 100644 index 0000000..b5fa831 --- /dev/null +++ b/internal/table_writer.go @@ -0,0 +1,53 @@ +package internal + +import ( + "context" + "io" + + "github.com/jedib0t/go-pretty/v6/table" + "github.com/jedib0t/go-pretty/v6/text" + "github.com/shopspring/decimal" +) + +// TableWriter writes a simple, human readable, table row to the provided io.Writer for each +// ReportItem received. +type TableWriter struct { + table table.Writer + output io.Writer + + totalEarned decimal.Decimal + totalSpent decimal.Decimal + totalFees decimal.Decimal + totalTaxes decimal.Decimal +} + +func NewTableWriter(w io.Writer) *TableWriter { + t := table.NewWriter() + t.SetOutputMirror(w) + t.SetAutoIndex(true) + t.SetStyle(table.StyleLight) + + t.AppendHeader(table.Row{"", "", "Realisation", "Realisation", "Realisation", "Realisation", "Aquisition", "Aquisition", "Aquisition", "Aquisition", "", "", ""}, table.RowConfig{AutoMerge: true}) + t.AppendHeader(table.Row{"Source Country", "Code", "Year", "Month", "Day", "Value", "Year", "Month", "Day", "Value", "Expenses", "Payed Taxes", "Counter Country"}) + + return &TableWriter{ + table: t, + output: w, + } +} + +func (tw *TableWriter) Write(_ context.Context, ri ReportItem) error { + tw.totalEarned = tw.totalEarned.Add(ri.SellValue) + tw.totalSpent = tw.totalSpent.Add(ri.BuyValue) + tw.totalFees = tw.totalFees.Add(ri.Fees) + tw.totalTaxes = tw.totalTaxes.Add(ri.Taxes) + + tw.table.AppendRow(table.Row{ri.AssetCountry, ri.Symbol, ri.SellTimestamp.Year(), int(ri.SellTimestamp.Month()), ri.SellTimestamp.Day(), ri.SellValue, ri.BuyTimestamp.Year(), ri.BuyTimestamp.Month(), ri.BuyTimestamp.Day(), ri.BuyValue, ri.Fees, ri.Taxes, ri.BrokerCountry}) + + return nil +} + +func (tw *TableWriter) Render() { + tw.table.AppendFooter(table.Row{"SUM", "SUM", "SUM", "SUM", "SUM", tw.totalEarned, "", "", "", tw.totalSpent, tw.totalFees, tw.totalTaxes}, table.RowConfig{AutoMerge: true, AutoMergeAlign: text.AlignRight}) + tw.table.Render() +} diff --git a/internal/table_writer_test.go b/internal/table_writer_test.go new file mode 100644 index 0000000..2d642b3 --- /dev/null +++ b/internal/table_writer_test.go @@ -0,0 +1,116 @@ +package internal + +import ( + "bytes" + "testing" + "time" + + "github.com/shopspring/decimal" +) + +func TestTableWriter_Write(t *testing.T) { + tNow := time.Now() + + tests := []struct { + name string + items []ReportItem + wantTotalSpent decimal.Decimal + wantTotalEarned decimal.Decimal + wantTotalTaxes decimal.Decimal + wantTotalFees decimal.Decimal + }{ + { + name: "empty", + }, + { + name: "single item positive", + items: []ReportItem{ + { + BuyValue: decimal.NewFromFloat(100.0), + SellValue: decimal.NewFromFloat(200.0), + SellTimestamp: tNow, + Taxes: decimal.NewFromFloat(2.5), + Fees: decimal.NewFromFloat(2.5), + }, + }, + wantTotalSpent: decimal.NewFromFloat(100.0), + wantTotalEarned: decimal.NewFromFloat(200.0), + wantTotalTaxes: decimal.NewFromFloat(2.5), + wantTotalFees: decimal.NewFromFloat(2.5), + }, + { + name: "single item negative", + items: []ReportItem{ + { + BuyValue: decimal.NewFromFloat(200.0), + SellValue: decimal.NewFromFloat(150.0), + SellTimestamp: tNow, + Taxes: decimal.NewFromFloat(2.5), + Fees: decimal.NewFromFloat(2.5), + }, + }, + wantTotalSpent: decimal.NewFromFloat(200.0), + wantTotalEarned: decimal.NewFromFloat(150.0), + wantTotalTaxes: decimal.NewFromFloat(2.5), + wantTotalFees: decimal.NewFromFloat(2.5), + }, + { + name: "multiple items", + items: []ReportItem{ + { + Symbol: "US1912161007", + BuyValue: decimal.NewFromFloat(100.0), + SellValue: decimal.NewFromFloat(200.0), + SellTimestamp: tNow, + Taxes: decimal.NewFromFloat(2.5), + Fees: decimal.NewFromFloat(2.5), + }, + { + Symbol: "US1912161007", + BuyValue: decimal.NewFromFloat(200.0), + SellValue: decimal.NewFromFloat(150.0), + SellTimestamp: tNow.Add(1), + Taxes: decimal.NewFromFloat(2.5), + Fees: decimal.NewFromFloat(2.5), + }, + }, + wantTotalSpent: decimal.NewFromFloat(300.0), + wantTotalEarned: decimal.NewFromFloat(350.0), + wantTotalTaxes: decimal.NewFromFloat(5.0), + wantTotalFees: decimal.NewFromFloat(5.0), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := new(bytes.Buffer) + tw := NewTableWriter(buf) + + for _, item := range tt.items { + err := tw.Write(t.Context(), item) + if err != nil { + t.Fatalf("unexpected error on write: %v", err) + } + } + + if tw.table.Length() != len(tt.items) { + t.Fatalf("want %d items in table but got %d", len(tt.items), tw.table.Length()) + } + + if !tw.totalSpent.Equal(tt.wantTotalSpent) { + t.Errorf("want totalSpent to be %v but got %v", tt.wantTotalSpent, tw.totalSpent) + } + + if !tw.totalEarned.Equal(tt.wantTotalEarned) { + t.Errorf("want totalEarned to be %v but got %v", tt.wantTotalEarned, tw.totalEarned) + } + + if !tw.totalTaxes.Equal(tt.wantTotalTaxes) { + t.Errorf("want totalTaxes to be %v but got %v", tt.wantTotalTaxes, tw.totalTaxes) + } + + if !tw.totalFees.Equal(tt.wantTotalFees) { + t.Errorf("want totalFees to be %v but got %v", tt.wantTotalFees, tw.totalFees) + } + }) + } +} diff --git a/internal/trading212/constants.go b/internal/trading212/constants.go new file mode 100644 index 0000000..5a0f282 --- /dev/null +++ b/internal/trading212/constants.go @@ -0,0 +1,7 @@ +package trading212 + +import ( + "github.com/biter777/countries" +) + +const Country = countries.Cyprus diff --git a/internal/trading212/record.go b/internal/trading212/record.go index cae8db5..1c74f44 100644 --- a/internal/trading212/record.go +++ b/internal/trading212/record.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "github.com/biter777/countries" "github.com/nmoniz/any2anexoj/internal" "github.com/shopspring/decimal" ) @@ -26,6 +27,14 @@ func (r Record) Symbol() string { return r.symbol } +func (r Record) BrokerCountry() int64 { + return int64(Country) +} + +func (r Record) AssetCountry() int64 { + return int64(countries.ByName(r.Symbol()[:2]).Info().Code) +} + func (r Record) Side() internal.Side { return r.side }