From e9bad8c29cba0a41957bde9c2976ad721e4c3230 Mon Sep 17 00:00:00 2001 From: Adrian Gallagher Date: Mon, 27 Feb 2017 17:01:41 +1100 Subject: [PATCH] Added basic portfolio summary code --- common.go | 1 + main.go | 1 + portfolio.go | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 portfolio.go diff --git a/common.go b/common.go index cb231f0d..50f459cb 100644 --- a/common.go +++ b/common.go @@ -30,6 +30,7 @@ const ( HASH_SHA512_384 SATOSHIS_PER_BTC = 100000000 SATOSHIS_PER_LTC = 100000000 + WEI_PER_ETHER = 1000000000000000000 ) func GetMD5(input []byte) []byte { diff --git a/main.go b/main.go index 42357d1a..1dde04d8 100644 --- a/main.go +++ b/main.go @@ -36,6 +36,7 @@ type Bot struct { exchange Exchange exchanges []IBotExchange tickers []Ticker + portfolio Portfolio tickerChan chan Ticker shutdown chan bool } diff --git a/portfolio.go b/portfolio.go new file mode 100644 index 00000000..5930efdb --- /dev/null +++ b/portfolio.go @@ -0,0 +1,168 @@ +package main + +import ( + "errors" + "fmt" +) + +const ( + BLOCKR_API_URL = "blockr.io/api" + BLOCKR_API_VERSION = "1" + BLOCKR_ADDRESS_BALANCE = "address/balance" + + ETHERCHAIN_API_URL = "https://etherchain.org/api" + ETHERCHAIN_ACCOUNT_MULTIPLE = "account/multiple" +) + +type PortfolioAddress struct { + Address string + CoinType string + Balance float64 +} + +type Portfolio struct { + Addresses []PortfolioAddress +} + +type BlockrAddress struct { + Address string `json:"address"` + Balance float64 `json:"balance"` + BalanceMultisig float64 `json:"balance_multisig"` +} + +type BlockrAddressBalanceSingle struct { + Status string `json:"status"` + Data BlockrAddress `json:"data"` + Code int `json:"code"` + Message string `json:"message"` +} + +type BlockrAddressBalanceMulti struct { + Status string `json:"status"` + Data []BlockrAddress `json:"data"` + Code int `json:"code"` + Message string `json:"message"` +} + +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"` +} + +func GetEthereumBalance(address []string) (EtherchainBalanceResponse, error) { + addresses := JoinStrings(address, ",") + url := fmt.Sprintf("%s/%s/%s", ETHERCHAIN_API_URL, ETHERCHAIN_ACCOUNT_MULTIPLE, addresses) + result := EtherchainBalanceResponse{} + err := SendHTTPGetRequest(url, true, &result) + if err != nil { + return result, err + } + if result.Status != 1 { + return result, errors.New("Status was not 1") + } + return result, nil +} + +func GetBlockrBalanceSingle(address string, coinType string) (BlockrAddressBalanceSingle, error) { + url := fmt.Sprintf("https://%s.%s/v%s/%s/%s", StringToLower(coinType), BLOCKR_API_URL, BLOCKR_API_VERSION, BLOCKR_ADDRESS_BALANCE, address) + result := BlockrAddressBalanceSingle{} + err := SendHTTPGetRequest(url, true, &result) + if err != nil { + return result, err + } + if result.Status != "success" { + return result, errors.New(result.Message) + } + return result, nil +} + +func GetBlockrAddressMulti(addresses []string, coinType string) (BlockrAddressBalanceMulti, error) { + addressesStr := JoinStrings(addresses, ",") + url := fmt.Sprintf("https://%s.%s/v%s/%s/%s", StringToLower(coinType), BLOCKR_API_URL, BLOCKR_API_VERSION, BLOCKR_ADDRESS_BALANCE, addressesStr) + result := BlockrAddressBalanceMulti{} + err := SendHTTPGetRequest(url, true, &result) + if err != nil { + return result, err + } + if result.Status != "success" { + return result, errors.New(result.Message) + } + return result, nil +} + +func GetAddressBalance(address string) (float64, bool) { + for _, x := range bot.portfolio.Addresses { + if x.Address == address { + return x.Balance, true + } + } + return 0, false +} + +func AddressExists(address string) bool { + for _, x := range bot.portfolio.Addresses { + if x.Address == address { + return true + } + } + return false +} + +func UpdatePortfolio(addresses []string, coinType string) bool { + if coinType == "ETH" { + result, err := GetEthereumBalance(addresses) + if err != nil { + return false + } + for _, x := range result.Data { + if !AddressExists(x.Address) { + bot.portfolio.Addresses = append(bot.portfolio.Addresses, PortfolioAddress{Address: x.Address, CoinType: coinType, Balance: x.Balance / WEI_PER_ETHER}) + } + } + return true + } + if len(addresses) > 1 { + result, err := GetBlockrAddressMulti(addresses, coinType) + if err != nil { + return false + } + for _, x := range result.Data { + if !AddressExists(x.Address) { + bot.portfolio.Addresses = append(bot.portfolio.Addresses, PortfolioAddress{Address: x.Address, CoinType: coinType, Balance: x.Balance}) + } + } + } else { + result, err := GetBlockrBalanceSingle(addresses[0], coinType) + if err != nil { + return false + } + if !AddressExists(result.Data.Address) { + bot.portfolio.Addresses = append(bot.portfolio.Addresses, PortfolioAddress{Address: result.Data.Address, CoinType: coinType, Balance: result.Data.Balance}) + } + } + return true +} + +func GetPortfolioSummary(coinFilter string) map[string]float64 { + result := make(map[string]float64) + for _, x := range bot.portfolio.Addresses { + if coinFilter != "" && coinFilter != x.CoinType { + continue + } + balance, ok := result[x.CoinType] + if !ok { + result[x.CoinType] = x.Balance + } else { + result[x.CoinType] = x.Balance + balance + } + } + return result +}