Reorganise portfolio and prevent saving nil portfolio if not seeded

This commit is contained in:
Adrian Gallagher
2018-05-31 14:07:23 +10:00
parent 02dbab03ce
commit d3edd2845f
6 changed files with 187 additions and 180 deletions

File diff suppressed because one or more lines are too long

View File

@@ -3,6 +3,7 @@ package main
import (
"errors"
"fmt"
"log"
"github.com/thrasher-/gocryptotrader/currency"
"github.com/thrasher-/gocryptotrader/currency/pair"
@@ -11,6 +12,7 @@ import (
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-/gocryptotrader/exchanges/stats"
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
"github.com/thrasher-/gocryptotrader/portfolio"
)
// GetAllAvailablePairs returns a list of all available pairs on either enabled
@@ -311,3 +313,51 @@ func GetExchangeLowestPriceByCurrencyPair(p pair.CurrencyPair, assetType string)
return result[0].Exchange, nil
}
// SeedExchangeAccountInfo seeds account info
func SeedExchangeAccountInfo(data []exchange.AccountInfo) {
if len(data) == 0 {
return
}
port := portfolio.GetPortfolio()
for i := 0; i < len(data); i++ {
exchangeName := data[i].ExchangeName
for j := 0; j < len(data[i].Currencies); j++ {
currencyName := data[i].Currencies[j].CurrencyName
onHold := data[i].Currencies[j].Hold
avail := data[i].Currencies[j].TotalValue
total := onHold + avail
if !port.ExchangeAddressExists(exchangeName, currencyName) {
if total <= 0 {
continue
}
log.Printf("Portfolio: Adding new exchange address: %s, %s, %f, %s\n",
exchangeName, currencyName, total, portfolio.PortfolioAddressExchange)
port.Addresses = append(
port.Addresses,
portfolio.Address{Address: exchangeName, CoinType: currencyName,
Balance: total, Description: portfolio.PortfolioAddressExchange},
)
} else {
if total <= 0 {
log.Printf("Portfolio: Removing %s %s entry.\n", exchangeName,
currencyName)
port.RemoveExchangeAddress(exchangeName, currencyName)
} else {
balance, ok := port.GetAddressBalance(exchangeName, currencyName, portfolio.PortfolioAddressExchange)
if !ok {
continue
}
if balance != total {
log.Printf("Portfolio: Updating %s %s entry with balance %f.\n",
exchangeName, currencyName, total)
port.UpdateExchangeAddressBalance(exchangeName, currencyName, total)
}
}
}
}
}
}

60
main.go
View File

@@ -43,6 +43,7 @@ const banner = `
var bot Bot
func main() {
bot.shutdown = make(chan bool)
HandleInterrupt()
//Handle flags
@@ -181,15 +182,18 @@ func HandleInterrupt() {
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
sig := <-c
log.Printf("Captured %v.", sig)
Shutdown()
log.Printf("Captured %v, shutdown requested.", sig)
bot.shutdown <- true
}()
}
// Shutdown correctly shuts down bot saving configuration files
func Shutdown() {
log.Println("Bot shutting down..")
bot.config.Portfolio = portfolio.Portfolio
if len(portfolio.Portfolio.Addresses) != 0 {
bot.config.Portfolio = portfolio.Portfolio
}
if !bot.dryRun {
err := bot.config.SaveConfig(bot.configFile)
@@ -202,53 +206,5 @@ func Shutdown() {
}
log.Println("Exiting.")
os.Exit(1)
}
// SeedExchangeAccountInfo seeds account info
func SeedExchangeAccountInfo(data []exchange.AccountInfo) {
if len(data) == 0 {
return
}
port := portfolio.GetPortfolio()
for i := 0; i < len(data); i++ {
exchangeName := data[i].ExchangeName
for j := 0; j < len(data[i].Currencies); j++ {
currencyName := data[i].Currencies[j].CurrencyName
onHold := data[i].Currencies[j].Hold
avail := data[i].Currencies[j].TotalValue
total := onHold + avail
if !port.ExchangeAddressExists(exchangeName, currencyName) {
if total <= 0 {
continue
}
log.Printf("Portfolio: Adding new exchange address: %s, %s, %f, %s\n",
exchangeName, currencyName, total, portfolio.PortfolioAddressExchange)
port.Addresses = append(
port.Addresses,
portfolio.Address{Address: exchangeName, CoinType: currencyName,
Balance: total, Description: portfolio.PortfolioAddressExchange},
)
} else {
if total <= 0 {
log.Printf("Portfolio: Removing %s %s entry.\n", exchangeName,
currencyName)
port.RemoveExchangeAddress(exchangeName, currencyName)
} else {
balance, ok := port.GetAddressBalance(exchangeName, currencyName, portfolio.PortfolioAddressExchange)
if !ok {
continue
}
if balance != total {
log.Printf("Portfolio: Updating %s %s entry with balance %f.\n",
exchangeName, currencyName, total)
port.UpdateExchangeAddressBalance(exchangeName, currencyName, total)
}
}
}
}
}
os.Exit(0)
}

View File

@@ -12,9 +12,6 @@ import (
const (
cryptoIDAPIURL = "https://chainz.cryptoid.info"
etherchainAPIURL = "https://etherchain.org/api"
etherchainAccountMultiple = "account/multiple"
ethplorerAPIURL = "https://api.ethplorer.io"
ethplorerAddressInfo = "getAddressInfo"
@@ -27,89 +24,6 @@ const (
// Portfolio is variable store holding an array of portfolioAddress
var Portfolio Base
// Base holds the portfolio base addresses
type Base struct {
Addresses []Address
}
// Address sub type holding address information for portfolio
type Address struct {
Address string
CoinType string
Balance float64
Description string
}
// EtherchainBalanceResponse holds JSON incoming and outgoing data for
// Etherchain
type EtherchainBalanceResponse struct {
Status int `json:"status"`
Data []struct {
Address string `json:"address"`
Balance float64 `json:"balance"`
Nonce interface{} `json:"nonce"`
Code string `json:"code"`
Name interface{} `json:"name"`
Storage interface{} `json:"storage"`
FirstSeen interface{} `json:"firstSeen"`
} `json:"data"`
}
// EthplorerResponse holds JSON address data for Ethplorer
type EthplorerResponse struct {
Address string `json:"address"`
ETH struct {
Balance float64 `json:"balance"`
TotalIn float64 `json:"totalIn"`
TotalOut float64 `json:"totalOut"`
} `json:"ETH"`
CountTxs int `json:"countTxs"`
ContractInfo struct {
CreatorAddress string `json:"creatorAddress"`
TransactionHash string `json:"transactionHash"`
Timestamp int `json:"timestamp"`
} `json:"contractInfo"`
TokenInfo struct {
Address string `json:"address"`
Name string `json:"name"`
Decimals int `json:"decimals"`
Symbol string `json:"symbol"`
TotalSupply string `json:"totalSupply"`
Owner string `json:"owner"`
LastUpdated int `json:"lastUpdated"`
TotalIn int64 `json:"totalIn"`
TotalOut int64 `json:"totalOut"`
IssuancesCount int `json:"issuancesCount"`
HoldersCount int `json:"holdersCount"`
Image string `json:"image"`
Description string `json:"description"`
Price struct {
Rate int `json:"rate"`
Diff int `json:"diff"`
Ts int `json:"ts"`
Currency string `json:"currency"`
} `json:"price"`
} `json:"tokenInfo"`
Error struct {
Code int `json:"code"`
Message string `json:"message"`
} `json:"error"`
}
// ExchangeAccountInfo : Generic type to hold each exchange's holdings in all
// enabled currencies
type ExchangeAccountInfo struct {
ExchangeName string
Currencies []ExchangeAccountCurrencyInfo
}
// ExchangeAccountCurrencyInfo : Sub type to store currency name and value
type ExchangeAccountCurrencyInfo struct {
CurrencyName string
TotalValue float64
Hold float64
}
// GetEthereumBalance single or multiple address information as
// EtherchainBalanceResponse
func GetEthereumBalance(address string) (EthplorerResponse, error) {
@@ -361,39 +275,6 @@ func getPercentageSpecific(input float64, target string, totals map[string]float
return percentage
}
// Coin stores a coin type, balance, address and percentage relative to the total
// amount.
type Coin struct {
Coin string `json:"coin"`
Balance float64 `json:"balance"`
Address string `json:"address,omitempty"`
Percentage float64 `json:"percentage,omitempty"`
}
// OfflineCoinSummary stores a coin types address, balance and percentage
// relative to the total amount.
type OfflineCoinSummary struct {
Address string `json:"address"`
Balance float64 `json:"balance"`
Percentage float64 `json:"percentage,omitempty"`
}
// OnlineCoinSummary stores a coin types balance and percentage relative to the
// total amount.
type OnlineCoinSummary struct {
Balance float64 `json:"balance"`
Percentage float64 `json:"percentage,omitempty"`
}
// Summary Stores the entire portfolio summary
type Summary struct {
Totals []Coin `json:"coin_totals"`
Offline []Coin `json:"coins_offline"`
OfflineSummary map[string][]OfflineCoinSummary `json:"offline_summary"`
Online []Coin `json:"coins_online"`
OnlineSummary map[string]map[string]OnlineCoinSummary `json:"online_summary"`
}
// GetPortfolioSummary returns the complete portfolio summary, showing
// coin totals, offline and online summaries with their relative percentages.
func (p *Base) GetPortfolioSummary() Summary {

View File

@@ -3,6 +3,7 @@ package portfolio
import (
"reflect"
"testing"
"time"
)
func TestGetEthereumBalance(t *testing.T) {
@@ -201,6 +202,8 @@ func TestUpdatePortfolio(t *testing.T) {
if value {
t.Error("Test Failed - portfolio_test.go - UpdatePortfolio error")
}
time.Sleep(time.Second * 5)
value = portfolio.UpdatePortfolio(
[]string{"0xb794f5ea0ba39494ce839613fffba74279579268",
"0xe853c56864a2ebe4576a807d26fdc4a0ada51919"}, "ETH",

View File

@@ -0,0 +1,117 @@
package portfolio
// Base holds the portfolio base addresses
type Base struct {
Addresses []Address
}
// Address sub type holding address information for portfolio
type Address struct {
Address string
CoinType string
Balance float64
Description string
}
// EtherchainBalanceResponse holds JSON incoming and outgoing data for
// Etherchain
type EtherchainBalanceResponse struct {
Status int `json:"status"`
Data []struct {
Address string `json:"address"`
Balance float64 `json:"balance"`
Nonce interface{} `json:"nonce"`
Code string `json:"code"`
Name interface{} `json:"name"`
Storage interface{} `json:"storage"`
FirstSeen interface{} `json:"firstSeen"`
} `json:"data"`
}
// EthplorerResponse holds JSON address data for Ethplorer
type EthplorerResponse struct {
Address string `json:"address"`
ETH struct {
Balance float64 `json:"balance"`
TotalIn float64 `json:"totalIn"`
TotalOut float64 `json:"totalOut"`
} `json:"ETH"`
CountTxs int `json:"countTxs"`
ContractInfo struct {
CreatorAddress string `json:"creatorAddress"`
TransactionHash string `json:"transactionHash"`
Timestamp int `json:"timestamp"`
} `json:"contractInfo"`
TokenInfo struct {
Address string `json:"address"`
Name string `json:"name"`
Decimals int `json:"decimals"`
Symbol string `json:"symbol"`
TotalSupply string `json:"totalSupply"`
Owner string `json:"owner"`
LastUpdated int `json:"lastUpdated"`
TotalIn int64 `json:"totalIn"`
TotalOut int64 `json:"totalOut"`
IssuancesCount int `json:"issuancesCount"`
HoldersCount int `json:"holdersCount"`
Image string `json:"image"`
Description string `json:"description"`
Price struct {
Rate int `json:"rate"`
Diff int `json:"diff"`
Ts int `json:"ts"`
Currency string `json:"currency"`
} `json:"price"`
} `json:"tokenInfo"`
Error struct {
Code int `json:"code"`
Message string `json:"message"`
} `json:"error"`
}
// ExchangeAccountInfo : Generic type to hold each exchange's holdings in all
// enabled currencies
type ExchangeAccountInfo struct {
ExchangeName string
Currencies []ExchangeAccountCurrencyInfo
}
// ExchangeAccountCurrencyInfo : Sub type to store currency name and value
type ExchangeAccountCurrencyInfo struct {
CurrencyName string
TotalValue float64
Hold float64
}
// Coin stores a coin type, balance, address and percentage relative to the total
// amount.
type Coin struct {
Coin string `json:"coin"`
Balance float64 `json:"balance"`
Address string `json:"address,omitempty"`
Percentage float64 `json:"percentage,omitempty"`
}
// OfflineCoinSummary stores a coin types address, balance and percentage
// relative to the total amount.
type OfflineCoinSummary struct {
Address string `json:"address"`
Balance float64 `json:"balance"`
Percentage float64 `json:"percentage,omitempty"`
}
// OnlineCoinSummary stores a coin types balance and percentage relative to the
// total amount.
type OnlineCoinSummary struct {
Balance float64 `json:"balance"`
Percentage float64 `json:"percentage,omitempty"`
}
// Summary Stores the entire portfolio summary
type Summary struct {
Totals []Coin `json:"coin_totals"`
Offline []Coin `json:"coins_offline"`
OfflineSummary map[string][]OfflineCoinSummary `json:"offline_summary"`
Online []Coin `json:"coins_online"`
OnlineSummary map[string]map[string]OnlineCoinSummary `json:"online_summary"`
}