Skip to content

Commit

Permalink
portfolio: clean up fixes #243
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelmota authored and lyricnz committed Oct 24, 2021
1 parent b5b6883 commit 0e956d6
Show file tree
Hide file tree
Showing 10 changed files with 68 additions and 42 deletions.
4 changes: 1 addition & 3 deletions cointop/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand Down
18 changes: 9 additions & 9 deletions cointop/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
4 changes: 2 additions & 2 deletions cointop/keybindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
42 changes: 35 additions & 7 deletions cointop/portfolio.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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 != "" {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions cointop/sort.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 8 additions & 8 deletions cointop/table_header.go
Original file line number Diff line number Diff line change
Expand Up @@ -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%",
},
}
Expand Down Expand Up @@ -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 {
Expand Down
16 changes: 8 additions & 8 deletions docs/content/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"]]
Expand All @@ -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)?

Expand Down Expand Up @@ -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.

Expand Down
2 changes: 1 addition & 1 deletion pkg/api/impl/coingecko/coingecko.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/impl/coinmarketcap/coinmarketcap.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

0 comments on commit 0e956d6

Please sign in to comment.