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
if len(tupleIfc) >= 3 {
if parsePrice, err := ct.InterfaceToFloat64(tupleIfc[2]); err != nil {
if buyPrice, err = ct.InterfaceToFloat64(tupleIfc[2]); err != nil {
return err
} else {
buyPrice = parsePrice
}
}

@ -302,19 +302,19 @@ func CurrencySymbol(currency string) string {
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)
convertTo = strings.ToLower(convertTo)
var rate float64
if convertFrom == convertTo {
rate = 1.0
} else {
crate, err := ct.api.GetExchangeRate(convertFrom, convertTo, true)
if err != nil {
return 0, err
}
rate = crate
return amount, nil
}
rate, err := ct.api.GetExchangeRate(convertFrom, convertTo, true)
if err != nil {
return 0, err
}
return rate * amount, nil
}

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

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

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

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

@ -186,11 +186,11 @@ draft: false
## 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
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` the cost (in the current currency) of the coins
- `profit` 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` the PNL of the coins (current value vs 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:
![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)?
@ -520,7 +520,7 @@ draft: false
## 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
`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
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
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)
convertTo = strings.ToLower(convertTo)
if convertFrom == convertTo {

@ -432,7 +432,7 @@ func getChartInterval(start, end int64) string {
}
// 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 {
return 1.0, nil
}

@ -16,5 +16,5 @@ type Interface interface {
CoinLink(name string) string
SupportedCurrencies() []string
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