portfolio: clean up fixes #243

pull/250/head
Miguel Mota 3 years ago committed by Simon Roberts
parent b5b68833f5
commit 0e956d6358
No known key found for this signature in database
GPG Key ID: 0F30F99E6B771FD4

@ -647,10 +647,8 @@ func (ct *Cointop) loadPortfolioHoldingsFromConfig(holdingsIfc []interface{}) er
buyPrice := 0.0 buyPrice := 0.0
if len(tupleIfc) >= 3 { if len(tupleIfc) >= 3 {
if parsePrice, err := ct.InterfaceToFloat64(tupleIfc[2]); err != nil { if buyPrice, err = ct.InterfaceToFloat64(tupleIfc[2]); err != nil {
return err return err
} else {
buyPrice = parsePrice
} }
} }

@ -302,19 +302,19 @@ func CurrencySymbol(currency string) string {
return "?" return "?"
} }
func (ct *Cointop) Convert(convertFrom string, convertTo string, amount float64) (float64, error) { // Convert converts an amount to another currency type
func (ct *Cointop) Convert(convertFrom, convertTo string, amount float64) (float64, error) {
convertFrom = strings.ToLower(convertFrom) convertFrom = strings.ToLower(convertFrom)
convertTo = strings.ToLower(convertTo) convertTo = strings.ToLower(convertTo)
var rate float64
if convertFrom == convertTo { if convertFrom == convertTo {
rate = 1.0 return amount, nil
} else { }
crate, err := ct.api.GetExchangeRate(convertFrom, convertTo, true)
if err != nil { rate, err := ct.api.GetExchangeRate(convertFrom, convertTo, true)
return 0, err if err != nil {
} return 0, err
rate = crate
} }
return rate * amount, nil return rate * amount, nil
} }

@ -328,9 +328,9 @@ func (ct *Cointop) SetKeybindingAction(shortcutKey string, action string) error
case "sort_column_cost": case "sort_column_cost":
fn = ct.Sortfn("cost", true) fn = ct.Sortfn("cost", true)
case "sort_column_pnl": case "sort_column_pnl":
fn = ct.Sortfn("profit", true) fn = ct.Sortfn("pnl", true)
case "sort_column_pnl_percent": case "sort_column_pnl_percent":
fn = ct.Sortfn("profit_percent", true) fn = ct.Sortfn("pnl_percent", true)
default: default:
fn = ct.Keyfn(ct.Noop) fn = ct.Keyfn(ct.Noop)
} }

@ -37,8 +37,8 @@ var SupportedPortfolioTableHeaders = []string{
"last_updated", "last_updated",
"cost_price", "cost_price",
"cost", "cost",
"profit", "pnl",
"profit_percent", "pnl_percent",
} }
// DefaultPortfolioTableHeaders are the default portfolio table header columns // DefaultPortfolioTableHeaders are the default portfolio table header columns
@ -53,12 +53,23 @@ var DefaultPortfolioTableHeaders = []string{
"24h_change", "24h_change",
"7d_change", "7d_change",
"percent_holdings", "percent_holdings",
"cost_price",
"cost",
"pnl",
"pnl_percent",
"last_updated", "last_updated",
} }
// HiddenBalanceChars are the characters to show when hidding balances // HiddenBalanceChars are the characters to show when hidding balances
var HiddenBalanceChars = "********" var HiddenBalanceChars = "********"
var costColumns = map[string]bool{
"cost_price": true,
"cost": true,
"pnl": true,
"pnl_percent": true,
}
// ValidPortfolioTableHeader returns the portfolio table headers // ValidPortfolioTableHeader returns the portfolio table headers
func (ct *Cointop) ValidPortfolioTableHeader(name string) bool { func (ct *Cointop) ValidPortfolioTableHeader(name string) bool {
for _, v := range SupportedPortfolioTableHeaders { for _, v := range SupportedPortfolioTableHeaders {
@ -84,6 +95,25 @@ func (ct *Cointop) GetPortfolioTable() *table.Table {
headers := ct.GetPortfolioTableHeaders() headers := ct.GetPortfolioTableHeaders()
ct.ClearSyncMap(&ct.State.tableColumnWidths) ct.ClearSyncMap(&ct.State.tableColumnWidths)
ct.ClearSyncMap(&ct.State.tableColumnAlignLeft) ct.ClearSyncMap(&ct.State.tableColumnAlignLeft)
displayCostColumns := false
for _, coin := range ct.State.coins {
if coin.BuyPrice > 0 && coin.BuyCurrency != "" {
displayCostColumns = true
break
}
}
if !displayCostColumns {
filtered := make([]string, 0)
for _, header := range headers {
if _, ok := costColumns[header]; !ok {
filtered = append(filtered, header)
}
}
headers = filtered
}
for _, coin := range ct.State.coins { for _, coin := range ct.State.coins {
leftMargin := 1 leftMargin := 1
rightMargin := 1 rightMargin := 1
@ -332,7 +362,6 @@ func (ct *Cointop) GetPortfolioTable() *table.Table {
cost = costPrice * coin.Holdings cost = costPrice * coin.Holdings
} }
} }
// text := ct.FormatPrice(cost)
text := humanize.FixedMonetaryf(cost, 2) text := humanize.FixedMonetaryf(cost, 2)
if ct.State.hidePortfolioBalances { if ct.State.hidePortfolioBalances {
text = HiddenBalanceChars text = HiddenBalanceChars
@ -352,7 +381,7 @@ func (ct *Cointop) GetPortfolioTable() *table.Table {
Color: ct.colorscheme.TableColumnPrice, Color: ct.colorscheme.TableColumnPrice,
Text: text, Text: text,
}) })
case "profit": case "pnl":
text := "" text := ""
colorProfit := ct.colorscheme.TableColumnChange colorProfit := ct.colorscheme.TableColumnChange
if coin.BuyPrice > 0 && coin.BuyCurrency != "" { if coin.BuyPrice > 0 && coin.BuyCurrency != "" {
@ -385,7 +414,7 @@ func (ct *Cointop) GetPortfolioTable() *table.Table {
Color: colorProfit, Color: colorProfit,
Text: text, Text: text,
}) })
case "profit_percent": case "pnl_percent":
profitPercent := 0.0 profitPercent := 0.0
if coin.BuyPrice > 0 && coin.BuyCurrency != "" { if coin.BuyPrice > 0 && coin.BuyCurrency != "" {
costPrice, err := ct.Convert(coin.BuyCurrency, ct.State.currencyConversion, coin.BuyPrice) costPrice, err := ct.Convert(coin.BuyCurrency, ct.State.currencyConversion, coin.BuyPrice)
@ -812,8 +841,7 @@ func (ct *Cointop) PrintHoldingsTable(options *TablePrintOptions) error {
records := make([][]string, len(holdings)) records := make([][]string, len(holdings))
symbol := ct.CurrencySymbol() symbol := ct.CurrencySymbol()
// TODO: buy_price, buy_currency, profit, profit_percent, etc headers := []string{"name", "symbol", "price", "holdings", "balance", "24h%", "%holdings", "buy_price", "buy_currency", "pnl", "pnl_percent"}
headers := []string{"name", "symbol", "price", "holdings", "balance", "24h%", "%holdings"}
if len(filterCols) > 0 { if len(filterCols) > 0 {
for _, col := range filterCols { for _, col := range filterCols {
valid := false valid := false

@ -72,9 +72,9 @@ func (ct *Cointop) Sort(sortBy string, desc bool, list []*Coin, renderHeaders bo
return a.BuyPrice < b.BuyPrice return a.BuyPrice < b.BuyPrice
case "cost": case "cost":
return (a.BuyPrice * a.Holdings) < (b.BuyPrice * b.Holdings) // TODO: convert? return (a.BuyPrice * a.Holdings) < (b.BuyPrice * b.Holdings) // TODO: convert?
case "profit": case "pnl":
return (a.Price - a.BuyPrice) < (b.Price - b.BuyPrice) return (a.Price - a.BuyPrice) < (b.Price - b.BuyPrice)
case "profit_percent": case "pnl_percent":
return (a.Price - a.BuyPrice) < (b.Price - b.BuyPrice) return (a.Price - a.BuyPrice) < (b.Price - b.BuyPrice)
default: default:
return a.Rank < b.Rank return a.Rank < b.Rank

@ -133,17 +133,17 @@ var HeaderColumns = map[string]*HeaderColumn{
}, },
"cost": { "cost": {
Slug: "cost", Slug: "cost",
Label: "cost[!]", Label: "[!]cost",
PlainLabel: "cost", PlainLabel: "cost",
}, },
"profit": { "pnl": {
Slug: "profit", Slug: "pnl",
Label: "PNL[@]", Label: "[@]PNL",
PlainLabel: "PNL", PlainLabel: "PNL",
}, },
"profit_percent": { "pnl_percent": {
Slug: "profit_percent", Slug: "pnl_percent",
Label: "PNL%[#]", Label: "[#]PNL%",
PlainLabel: "PNL%", PlainLabel: "PNL%",
}, },
} }
@ -231,7 +231,7 @@ func (ct *Cointop) UpdateTableHeader() error {
} }
leftAlign := ct.GetTableColumnAlignLeft(col) leftAlign := ct.GetTableColumnAlignLeft(col)
switch col { switch col {
case "price", "balance", "profit", "cost": case "price", "balance", "pnl", "cost":
label = fmt.Sprintf("%s%s", ct.CurrencySymbol(), label) label = fmt.Sprintf("%s%s", ct.CurrencySymbol(), label)
} }
if leftAlign { if leftAlign {

@ -186,11 +186,11 @@ draft: false
## How do I include buy/cost price in my portfolio? ## How do I include buy/cost price in my portfolio?
Currently there is no UI for this. If you want to include the cost of your coins in the Portfolio screen, you will need to edit your config.toml Currently there is no UI for this. If you want to include the cost of your coins in the Portfolio screen, you will need to edit your config.toml
Each coin consists of four values: coin name, coin amount, cost-price, cost-currency. Each coin consists of four values: coin name, coin amount, cost-price, cost-currency.
For example, the following configuration includes 100 ALGO at USD1.95 each; and 0.1 BTC at AUD50100.83 each. For example, the following configuration includes 100 ALGO at USD1.95 each; and 0.1 BTC at AUD50100.83 each.
```toml ```toml
holdings = [["Algorand", "100", "1.95", "USD"], ["Bitcoin", "0.1", "50100.83", "AUD"]] holdings = [["Algorand", "100", "1.95", "USD"], ["Bitcoin", "0.1", "50100.83", "AUD"]]
@ -200,12 +200,12 @@ draft: false
- `cost_price` the price and currency that the coins were purchased at - `cost_price` the price and currency that the coins were purchased at
- `cost` the cost (in the current currency) of the coins - `cost` the cost (in the current currency) of the coins
- `profit` the PNL of the coins (current value vs original cost) - `pnl` the PNL of the coins (current value vs original cost)
- `profit_percent` the PNL of the coins as a fraction of the original cost - `pnl_percent` the PNL of the coins as a fraction of the original cost
With the holdings above, and the currency set to GBP (British Pounds) cointop will look something like this: With the holdings above, and the currency set to GBP (British Pounds) cointop will look something like this:
![Screen Shot 2021-10-22 at 8 41 21 am](https://user-images.githubusercontent.com/122371/138361142-8e1f32b5-ca24-471d-a628-06968f07c65f.png) ![portfolio profit and loss](https://user-images.githubusercontent.com/122371/138361142-8e1f32b5-ca24-471d-a628-06968f07c65f.png)
## How do I hide my portfolio balances (private mode)? ## How do I hide my portfolio balances (private mode)?
@ -520,7 +520,7 @@ draft: false
## How can I get more information when something is going wrong? ## How can I get more information when something is going wrong?
Cointop creates a logfile at `/tmp/cointop.log`. Normally nothing is written to this, but if you set the environment variable Cointop creates a logfile at `/tmp/cointop.log`. Normally nothing is written to this, but if you set the environment variable
`DEBUG=1` cointop will write a lot of output describing its operation. Furthermore, if you also set `DEBUG_HTTP=1` it will `DEBUG=1` cointop will write a lot of output describing its operation. Furthermore, if you also set `DEBUG_HTTP=1` it will
emit lots about every HTTP request that cointop makes to coingecko (backend). Developers may ask for this information emit lots about every HTTP request that cointop makes to coingecko (backend). Developers may ask for this information
to help diagnose any problems you may experience. to help diagnose any problems you may experience.

@ -161,7 +161,7 @@ func (s *Service) GetExchangeRates(cached bool) (*types.ExchangeRatesItem, error
} }
// GetExchangeRate gets the current excange rate between two currencies // GetExchangeRate gets the current excange rate between two currencies
func (s *Service) GetExchangeRate(convertFrom string, convertTo string, cached bool) (float64, error) { func (s *Service) GetExchangeRate(convertFrom, convertTo string, cached bool) (float64, error) {
convertFrom = strings.ToLower(convertFrom) convertFrom = strings.ToLower(convertFrom)
convertTo = strings.ToLower(convertTo) convertTo = strings.ToLower(convertTo)
if convertFrom == convertTo { if convertFrom == convertTo {

@ -432,7 +432,7 @@ func getChartInterval(start, end int64) string {
} }
// GetExchangeRate gets the current excange rate between two currencies // GetExchangeRate gets the current excange rate between two currencies
func (s *Service) GetExchangeRate(convertFrom string, convertTo string, cached bool) (float64, error) { func (s *Service) GetExchangeRate(convertFrom, convertTo string, cached bool) (float64, error) {
if convertFrom == convertTo { if convertFrom == convertTo {
return 1.0, nil return 1.0, nil
} }

@ -16,5 +16,5 @@ type Interface interface {
CoinLink(name string) string CoinLink(name string) string
SupportedCurrencies() []string SupportedCurrencies() []string
Price(name string, convert string) (float64, error) Price(name string, convert string) (float64, error)
GetExchangeRate(convertFrom string, convertTo string, cached bool) (float64, error) // I don't love this caching GetExchangeRate(convertFrom, convertTo string, cached bool) (float64, error) // I don't love this caching
} }

Loading…
Cancel
Save