diff --git a/cointop/cointop.go b/cointop/cointop.go index 3a72b6d..72c2bad 100644 --- a/cointop/cointop.go +++ b/cointop/cointop.go @@ -17,6 +17,8 @@ import ( "github.com/miguelmota/cointop/pkg/termui" ) +// TODO: clean up and optimize codebase + // Cointop cointop type Cointop struct { g *gocui.Gui @@ -156,6 +158,7 @@ func New() *Cointop { "7dchange", "totalsupply", "availablesupply", + "percentholdings", "lastupdated", }, statusbarviewname: "statusbar", diff --git a/cointop/headers.go b/cointop/headers.go index b20f5dc..9125e1a 100644 --- a/cointop/headers.go +++ b/cointop/headers.go @@ -32,6 +32,7 @@ func (ct *Cointop) updateHeaders() { "7dchange": &t{color.Black, "[7]D%", 4, 0, " "}, "totalsupply": &t{color.Black, "[t]otal supply", 7, 0, " "}, "availablesupply": &t{color.Black, "[a]vailable supply", 0, 0, " "}, + "percentholdings": &t{color.Black, "%holdings", 2, 0, " "}, "lastupdated": &t{color.Black, "last [u]pdated", 3, 0, " "}, } @@ -49,7 +50,7 @@ func (ct *Cointop) updateHeaders() { if ct.portfoliovisible { cols = []string{"rank", "name", "symbol", "price", - "holdings", "balance", "24hchange", "lastupdated"} + "holdings", "balance", "24hchange", "percentholdings", "lastupdated"} } else { cols = []string{"rank", "name", "symbol", "price", "marketcap", "24hvolume", "1hchange", "24hchange", diff --git a/cointop/marketbar.go b/cointop/marketbar.go index 5e5834e..be47269 100644 --- a/cointop/marketbar.go +++ b/cointop/marketbar.go @@ -19,8 +19,10 @@ func (ct *Cointop) updateMarketbar() error { if ct.portfoliovisible { total := ct.getPortfolioTotal() + totalstr := humanize.Commaf(total) if !(ct.currencyconversion == "BTC" || ct.currencyconversion == "ETH" || total < 1) { total = math.Round(total*1e2) / 1e2 + totalstr = humanize.Commaf2(total) } timeframe := ct.selectedchartrange @@ -33,12 +35,32 @@ func (ct *Cointop) updateMarketbar() error { charttitle = fmt.Sprintf("Portfolio - %s", color.Cyan(chartname)) } + var percentChange24H float64 + for _, p := range ct.getPortfolioSlice() { + n := ((p.Balance / total) * p.PercentChange24H) + if math.IsNaN(n) { + continue + } + percentChange24H += n + } + + color24h := color.White + arrow := "" + if percentChange24H > 0 { + color24h = color.Green + arrow = "▲" + } + if percentChange24H < 0 { + color24h = color.Red + arrow = "▼" + } + content = fmt.Sprintf( - "[ Chart: %s %s ] Current Portfolio Value: %s%s", + "[ Chart: %s %s ] Total Portfolio Value: %s • 24H: %s", charttitle, timeframe, - ct.currencySymbol(), - humanize.Commaf(total), + color.Cyan(fmt.Sprintf("%s%s", ct.currencySymbol(), totalstr)), + color24h(fmt.Sprintf("%.2f%%%s", percentChange24H, arrow)), ) } else { var market types.GlobalMarketData diff --git a/cointop/portfolio.go b/cointop/portfolio.go index 776df90..d86c92b 100644 --- a/cointop/portfolio.go +++ b/cointop/portfolio.go @@ -62,6 +62,12 @@ func (ct *Cointop) updatePortfolioUpdateMenu() { } func (ct *Cointop) showPortfolioUpdateMenu() error { + coin := ct.highlightedRowCoin() + if coin == nil { + ct.togglePortfolio() + return nil + } + ct.portfolioupdatemenuvisible = true ct.updatePortfolioUpdateMenu() ct.setActiveView(ct.portfolioupdatemenuviewname) diff --git a/cointop/search.go b/cointop/search.go index 081149e..e70cb78 100644 --- a/cointop/search.go +++ b/cointop/search.go @@ -23,6 +23,11 @@ func (ct *Cointop) doSearch() error { ct.searchfield.Rewind() b := make([]byte, 100) n, err := ct.searchfield.Read(b) + + // TODO: do this a better way (SoC) + ct.filterByFavorites = false + ct.portfoliovisible = false + defer ct.setActiveView(ct.tableviewname) if err != nil { return nil diff --git a/cointop/table.go b/cointop/table.go index 7b9a5d4..1250c11 100644 --- a/cointop/table.go +++ b/cointop/table.go @@ -27,6 +27,10 @@ func (ct *Cointop) refreshTable() error { ct.table.AddCol("") ct.table.AddCol("") ct.table.AddCol("") + ct.table.AddCol("") + + total := ct.getPortfolioTotal() + for _, coin := range ct.coins { unix, _ := strconv.ParseInt(coin.LastUpdated, 10, 64) lastUpdated := time.Unix(unix, 0).Format("15:04:05 Jan 02") @@ -48,6 +52,11 @@ func (ct *Cointop) refreshTable() error { name = fmt.Sprintf("%s%s", name[0:18], dots) } + percentHoldings := (coin.Balance / total) * 1e2 + if math.IsNaN(percentHoldings) { + percentHoldings = 0 + } + ct.table.AddRow( rank, namecolor(pad.Right(fmt.Sprintf("%.22s", name), 21, " ")), @@ -56,6 +65,7 @@ func (ct *Cointop) refreshTable() error { color.White(fmt.Sprintf("%15s", strconv.FormatFloat(coin.Holdings, 'f', -1, 64))), colorbalance(fmt.Sprintf("%15s", humanize.Commaf(coin.Balance))), color24h(fmt.Sprintf("%8.2f%%", coin.PercentChange24H)), + color.White(fmt.Sprintf("%10.2f%%", percentHoldings)), color.White(pad.Right(fmt.Sprintf("%17s", lastUpdated), 80, " ")), ) } @@ -111,10 +121,17 @@ func (ct *Cointop) refreshTable() error { if len(name) > 20 { name = fmt.Sprintf("%s%s", name[0:18], dots) } + + symbolpadding := 5 + // NOTE: this is to adjust padding by 1 because when all name rows are + // yellow it messes the spacing (need to debug) + if ct.filterByFavorites { + symbolpadding = 6 + } ct.table.AddRow( rank, namecolor(pad.Right(fmt.Sprintf("%.22s", name), 21, " ")), - color.White(pad.Right(fmt.Sprintf("%.6s", coin.Symbol), 5, " ")), + color.White(pad.Right(fmt.Sprintf("%.6s", coin.Symbol), symbolpadding, " ")), colorprice(fmt.Sprintf("%12s", humanize.Commaf(coin.Price))), color.White(fmt.Sprintf("%17s", humanize.Commaf(coin.MarketCap))), color.White(fmt.Sprintf("%15s", humanize.Commaf(coin.Volume24H))), diff --git a/pkg/humanize/humanize.go b/pkg/humanize/humanize.go index 3ef17c5..8daf95d 100644 --- a/pkg/humanize/humanize.go +++ b/pkg/humanize/humanize.go @@ -4,6 +4,9 @@ import ( "bytes" "strconv" "strings" + + "golang.org/x/text/language" + "golang.org/x/text/message" ) // Commaf produces a string form of the given number in base 10 with @@ -38,3 +41,9 @@ func Commaf(v float64) string { } return buf.String() } + +// Commaf2 ... +func Commaf2(v float64) string { + p := message.NewPrinter(language.English) + return p.Sprintf("%.2f", v) +}