Skip to content

Commit db9d160

Browse files
committed
commit
1 parent 2871eb0 commit db9d160

File tree

4 files changed

+22
-311
lines changed

4 files changed

+22
-311
lines changed

cmd/ibctl/internal/command/holding/category/categorylist/categorylist.go

Lines changed: 6 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,12 @@ import (
99
"context"
1010
"os"
1111
"strings"
12-
"time"
1312

1413
"buf.build/go/app/appcmd"
1514
"buf.build/go/app/appext"
1615
"github.com/bufdev/ibctl/cmd/ibctl/internal/ibctlcmd"
17-
"github.com/bufdev/ibctl/internal/ibctl/ibctlconfig"
18-
"github.com/bufdev/ibctl/internal/ibctl/ibctlfxrates"
1916
"github.com/bufdev/ibctl/internal/ibctl/ibctlholdings"
20-
"github.com/bufdev/ibctl/internal/ibctl/ibctlmerge"
21-
"github.com/bufdev/ibctl/internal/ibctl/ibctlpath"
22-
"github.com/bufdev/ibctl/internal/ibctl/ibctlrealtime"
2317
"github.com/bufdev/ibctl/internal/pkg/cliio"
24-
"github.com/bufdev/ibctl/internal/pkg/yahoofinance"
2518
"github.com/spf13/pflag"
2619
)
2720

@@ -97,91 +90,29 @@ func run(ctx context.Context, container appext.Container, flags *flags) error {
9790
if err != nil {
9891
return appcmd.NewInvalidArgumentError(err.Error())
9992
}
100-
// Normalize base currency to uppercase for case-insensitive matching.
101-
baseCurrency := strings.ToUpper(flags.BaseCurrency)
102-
// Select the formatting function based on the base currency.
103-
formatBase := formatBaseFunc(baseCurrency)
104-
// Resolve the ibctl directory from the IBKR_DIR environment variable.
105-
dirPath, err := ibctlcmd.DirPath(container)
93+
// Load data through the common pipeline (config, merge, FX, optional download/realtime).
94+
data, err := ibctlcmd.LoadHoldingsData(ctx, container, flags.Download, flags.Realtime, flags.BaseCurrency)
10695
if err != nil {
10796
return err
10897
}
109-
// Read and validate the configuration file from the base directory.
110-
config, err := ibctlconfig.ReadConfig(dirPath)
111-
if err != nil {
112-
return err
113-
}
114-
// Download fresh data if --download is set.
115-
if flags.Download {
116-
downloader, err := ibctlcmd.NewDownloader(container, dirPath)
117-
if err != nil {
118-
return err
119-
}
120-
if err := downloader.Download(ctx); err != nil {
121-
return err
122-
}
123-
}
124-
// Merge trade data from all sources.
125-
mergedData, err := ibctlmerge.Merge(
126-
ibctlpath.DataAccountsDirPath(config.DirPath),
127-
ibctlpath.CacheAccountsDirPath(config.DirPath),
128-
ibctlpath.ActivityStatementsDirPath(config.DirPath),
129-
ibctlpath.SeedDirPath(config.DirPath),
130-
config.AccountAliases,
131-
config.Additions,
132-
)
133-
if err != nil {
134-
return err
135-
}
136-
// Load FX rates for base currency conversion.
137-
fxStore := ibctlfxrates.NewStore(ibctlpath.CacheFXDirPath(config.DirPath))
138-
// Override market prices and FX rates with real-time data from Yahoo Finance.
139-
if flags.Realtime {
140-
todayDate := time.Now().Format("2006-01-02")
141-
if err := ibctlrealtime.ApplyOverrides(ctx, container.Logger(), yahoofinance.NewClient(), mergedData.Positions, fxStore, config, baseCurrency, todayDate); err != nil {
142-
return err
143-
}
144-
}
14598
// Compute holdings via FIFO from all trade data, converted to the base currency.
146-
result, err := ibctlholdings.GetHoldingsOverview(mergedData.Trades, mergedData.Positions, mergedData.CashPositions, config, fxStore, baseCurrency)
99+
result, err := ibctlholdings.GetHoldingsOverview(data.MergedData.Trades, data.MergedData.Positions, data.MergedData.CashPositions, data.Config, data.FxStore, data.BaseCurrency)
147100
if err != nil {
148101
return err
149102
}
150103
// Aggregate holdings by category, optionally filtered by geo (case-insensitive).
151104
categories := ibctlholdings.GetCategoryList(result.Holdings, strings.ToUpper(flags.Geo))
152105
// Write output in the requested format.
106+
formatBase := ibctlcmd.FormatBaseFunc(data.BaseCurrency)
153107
writer := os.Stdout
154108
switch format {
155109
case cliio.FormatTable:
156-
headers := ibctlholdings.CategoryListHeaders(baseCurrency)
157-
rows := make([][]string, 0, len(categories))
158-
for _, c := range categories {
159-
rows = append(rows, ibctlholdings.CategoryOverviewToTableRow(c, formatBase))
160-
}
161-
return cliio.WriteTable(writer, headers, rows)
110+
return ibctlcmd.WriteCategoryListTable(writer, categories, data.BaseCurrency, formatBase)
162111
case cliio.FormatCSV:
163-
headers := ibctlholdings.CategoryListHeaders(baseCurrency)
164-
records := make([][]string, 0, len(categories)+1)
165-
records = append(records, headers)
166-
for _, c := range categories {
167-
records = append(records, ibctlholdings.CategoryOverviewToRow(c))
168-
}
169-
return cliio.WriteCSVRecords(writer, records)
112+
return ibctlcmd.WriteCategoryListCSV(writer, categories, data.BaseCurrency)
170113
case cliio.FormatJSON:
171114
return cliio.WriteJSON(writer, categories...)
172115
default:
173116
return appcmd.NewInvalidArgumentErrorf("unsupported format: %s", format)
174117
}
175118
}
176-
177-
// formatBaseFunc returns a formatting function for display values in the given currency.
178-
func formatBaseFunc(baseCurrency string) func(string) string {
179-
switch baseCurrency {
180-
case "USD":
181-
return cliio.FormatUSD
182-
case "CAD":
183-
return cliio.FormatCAD
184-
default:
185-
return func(v string) string { return v }
186-
}
187-
}

cmd/ibctl/internal/command/holding/geo/geolist/geolist.go

Lines changed: 6 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,12 @@ import (
99
"context"
1010
"os"
1111
"strings"
12-
"time"
1312

1413
"buf.build/go/app/appcmd"
1514
"buf.build/go/app/appext"
1615
"github.com/bufdev/ibctl/cmd/ibctl/internal/ibctlcmd"
17-
"github.com/bufdev/ibctl/internal/ibctl/ibctlconfig"
18-
"github.com/bufdev/ibctl/internal/ibctl/ibctlfxrates"
1916
"github.com/bufdev/ibctl/internal/ibctl/ibctlholdings"
20-
"github.com/bufdev/ibctl/internal/ibctl/ibctlmerge"
21-
"github.com/bufdev/ibctl/internal/ibctl/ibctlpath"
22-
"github.com/bufdev/ibctl/internal/ibctl/ibctlrealtime"
2317
"github.com/bufdev/ibctl/internal/pkg/cliio"
24-
"github.com/bufdev/ibctl/internal/pkg/yahoofinance"
2518
"github.com/spf13/pflag"
2619
)
2720

@@ -98,91 +91,29 @@ func run(ctx context.Context, container appext.Container, flags *flags) error {
9891
if err != nil {
9992
return appcmd.NewInvalidArgumentError(err.Error())
10093
}
101-
// Normalize base currency to uppercase for case-insensitive matching.
102-
baseCurrency := strings.ToUpper(flags.BaseCurrency)
103-
// Select the formatting function based on the base currency.
104-
formatBase := formatBaseFunc(baseCurrency)
105-
// Resolve the ibctl directory from the IBKR_DIR environment variable.
106-
dirPath, err := ibctlcmd.DirPath(container)
94+
// Load data through the common pipeline (config, merge, FX, optional download/realtime).
95+
data, err := ibctlcmd.LoadHoldingsData(ctx, container, flags.Download, flags.Realtime, flags.BaseCurrency)
10796
if err != nil {
10897
return err
10998
}
110-
// Read and validate the configuration file from the base directory.
111-
config, err := ibctlconfig.ReadConfig(dirPath)
112-
if err != nil {
113-
return err
114-
}
115-
// Download fresh data if --download is set.
116-
if flags.Download {
117-
downloader, err := ibctlcmd.NewDownloader(container, dirPath)
118-
if err != nil {
119-
return err
120-
}
121-
if err := downloader.Download(ctx); err != nil {
122-
return err
123-
}
124-
}
125-
// Merge trade data from all sources.
126-
mergedData, err := ibctlmerge.Merge(
127-
ibctlpath.DataAccountsDirPath(config.DirPath),
128-
ibctlpath.CacheAccountsDirPath(config.DirPath),
129-
ibctlpath.ActivityStatementsDirPath(config.DirPath),
130-
ibctlpath.SeedDirPath(config.DirPath),
131-
config.AccountAliases,
132-
config.Additions,
133-
)
134-
if err != nil {
135-
return err
136-
}
137-
// Load FX rates for base currency conversion.
138-
fxStore := ibctlfxrates.NewStore(ibctlpath.CacheFXDirPath(config.DirPath))
139-
// Override market prices and FX rates with real-time data from Yahoo Finance.
140-
if flags.Realtime {
141-
todayDate := time.Now().Format("2006-01-02")
142-
if err := ibctlrealtime.ApplyOverrides(ctx, container.Logger(), yahoofinance.NewClient(), mergedData.Positions, fxStore, config, baseCurrency, todayDate); err != nil {
143-
return err
144-
}
145-
}
14699
// Compute holdings via FIFO from all trade data, converted to the base currency.
147-
result, err := ibctlholdings.GetHoldingsOverview(mergedData.Trades, mergedData.Positions, mergedData.CashPositions, config, fxStore, baseCurrency)
100+
result, err := ibctlholdings.GetHoldingsOverview(data.MergedData.Trades, data.MergedData.Positions, data.MergedData.CashPositions, data.Config, data.FxStore, data.BaseCurrency)
148101
if err != nil {
149102
return err
150103
}
151104
// Aggregate holdings by geo, optionally filtered by category (case-insensitive).
152105
geos := ibctlholdings.GetGeoList(result.Holdings, strings.ToUpper(flags.Category))
153106
// Write output in the requested format.
107+
formatBase := ibctlcmd.FormatBaseFunc(data.BaseCurrency)
154108
writer := os.Stdout
155109
switch format {
156110
case cliio.FormatTable:
157-
headers := ibctlholdings.GeoListHeaders(baseCurrency)
158-
rows := make([][]string, 0, len(geos))
159-
for _, g := range geos {
160-
rows = append(rows, ibctlholdings.GeoOverviewToTableRow(g, formatBase))
161-
}
162-
return cliio.WriteTable(writer, headers, rows)
111+
return ibctlcmd.WriteGeoListTable(writer, geos, data.BaseCurrency, formatBase)
163112
case cliio.FormatCSV:
164-
headers := ibctlholdings.GeoListHeaders(baseCurrency)
165-
records := make([][]string, 0, len(geos)+1)
166-
records = append(records, headers)
167-
for _, g := range geos {
168-
records = append(records, ibctlholdings.GeoOverviewToRow(g))
169-
}
170-
return cliio.WriteCSVRecords(writer, records)
113+
return ibctlcmd.WriteGeoListCSV(writer, geos, data.BaseCurrency)
171114
case cliio.FormatJSON:
172115
return cliio.WriteJSON(writer, geos...)
173116
default:
174117
return appcmd.NewInvalidArgumentErrorf("unsupported format: %s", format)
175118
}
176119
}
177-
178-
// formatBaseFunc returns a formatting function for display values in the given currency.
179-
func formatBaseFunc(baseCurrency string) func(string) string {
180-
switch baseCurrency {
181-
case "USD":
182-
return cliio.FormatUSD
183-
case "CAD":
184-
return cliio.FormatCAD
185-
default:
186-
return func(v string) string { return v }
187-
}
188-
}

cmd/ibctl/internal/command/holding/holding.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/bufdev/ibctl/cmd/ibctl/internal/command/holding/coveredcall"
1313
"github.com/bufdev/ibctl/cmd/ibctl/internal/command/holding/geo"
1414
"github.com/bufdev/ibctl/cmd/ibctl/internal/command/holding/holdinglist"
15+
"github.com/bufdev/ibctl/cmd/ibctl/internal/command/holding/holdingoverview"
1516
"github.com/bufdev/ibctl/cmd/ibctl/internal/command/holding/holdingvalue"
1617
"github.com/bufdev/ibctl/cmd/ibctl/internal/command/holding/lot"
1718
"github.com/bufdev/ibctl/cmd/ibctl/internal/command/holding/projectedincome"
@@ -28,6 +29,7 @@ func NewCommand(name string, builder appext.SubCommandBuilder) *appcmd.Command {
2829
geo.NewCommand("geo", builder),
2930
holdinglist.NewCommand("list", builder),
3031
lot.NewCommand("lot", builder),
32+
holdingoverview.NewCommand("overview", builder),
3133
projectedincome.NewCommand("projected-income", builder),
3234
holdingvalue.NewCommand("value", builder),
3335
},

0 commit comments

Comments
 (0)