Formatting, linter and test for portfolio

This commit is contained in:
Ryan O'Hara-Reid
2017-07-26 19:17:29 +10:00
committed by Adrian Gallagher
parent 3e4fb1660d
commit f11f83fe50
7 changed files with 396 additions and 136 deletions

View File

@@ -80,7 +80,7 @@ type Config struct {
Name string
EncryptConfig int
Cryptocurrencies string
Portfolio portfolio.PortfolioBase `json:"PortfolioAddresses"`
Portfolio portfolio.Base `json:"PortfolioAddresses"`
SMS SMSGlobalConfig `json:"SMSGlobal"`
Webserver WebserverConfig `json:"Webserver"`
Exchanges []ExchangeConfig `json:"Exchanges"`

View File

@@ -5,19 +5,19 @@ import (
)
func TestAddEvent(t *testing.T) {
eventID, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", ACTION_TEST)
eventID, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", actionTest)
if err != nil && eventID != 0 {
t.Errorf("Test Failed. AddEvent: Error, %s", err)
}
eventID, err = AddEvent("ANXX", "price", ">,==", "BTC", "LTC", ACTION_TEST)
eventID, err = AddEvent("ANXX", "price", ">,==", "BTC", "LTC", actionTest)
if err == nil && eventID == 0 {
t.Error("Test Failed. AddEvent: Error, error not captured in Exchange")
}
eventID, err = AddEvent("ANX", "prices", ">,==", "BTC", "LTC", ACTION_TEST)
eventID, err = AddEvent("ANX", "prices", ">,==", "BTC", "LTC", actionTest)
if err == nil && eventID == 0 {
t.Error("Test Failed. AddEvent: Error, error not captured in Item")
}
eventID, err = AddEvent("ANX", "price", "3===D", "BTC", "LTC", ACTION_TEST)
eventID, err = AddEvent("ANX", "price", "3===D", "BTC", "LTC", actionTest)
if err == nil && eventID == 0 {
t.Error("Test Failed. AddEvent: Error, error not captured in Condition")
}
@@ -25,7 +25,7 @@ func TestAddEvent(t *testing.T) {
if err == nil && eventID == 0 {
t.Error("Test Failed. AddEvent: Error, error not captured in Action")
}
eventID, err = AddEvent("ANX", "price", ">,==", "BATMAN", "ROBIN", ACTION_TEST)
eventID, err = AddEvent("ANX", "price", ">,==", "BATMAN", "ROBIN", actionTest)
if err == nil && eventID == 0 {
t.Error("Test Failed. AddEvent: Error, error not captured in Action")
}
@@ -35,34 +35,38 @@ func TestAddEvent(t *testing.T) {
}
func TestRemoveEvent(t *testing.T) {
eventID, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", ACTION_TEST)
eventID, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", actionTest)
if err != nil && eventID != 0 {
t.Errorf("Test Failed. RemoveEvent: Error, %s", err)
}
if !RemoveEvent(eventID) {
t.Error("Test Failed. RemoveEvent: Error, error removing event")
}
if RemoveEvent(1234) {
t.Error("Test Failed. RemoveEvent: Error, error removing event")
}
}
func TestGetEventCounter(t *testing.T) {
one, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", ACTION_TEST)
one, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", actionTest)
if err != nil {
t.Errorf("Test Failed. GetEventCounter: Error, %s", err)
}
two, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", ACTION_TEST)
two, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", actionTest)
if err != nil {
t.Errorf("Test Failed. GetEventCounter: Error, %s", err)
}
three, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", ACTION_TEST)
three, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", actionTest)
if err != nil {
t.Errorf("Test Failed. GetEventCounter: Error, %s", err)
}
Events[three-1].Executed = true
total, _ := GetEventCounter()
if total <= 0 {
t.Errorf("Test Failed. GetEventCounter: Total = %d", total)
}
if !RemoveEvent(one) {
t.Error("Test Failed. GetEventCounter: Error, error removing event")
}
@@ -80,7 +84,7 @@ func TestGetEventCounter(t *testing.T) {
}
func TestExecuteAction(t *testing.T) {
one, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", ACTION_TEST)
one, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", actionTest)
if err != nil {
t.Errorf("Test Failed. ExecuteAction: Error, %s", err)
}
@@ -88,14 +92,15 @@ func TestExecuteAction(t *testing.T) {
if !isExecuted {
t.Error("Test Failed. ExecuteAction: Error, error removing event")
}
if !RemoveEvent(one) {
t.Error("Test Failed. ExecuteAction: Error, error removing event")
}
// More tests when ExecuteAction is expanded
}
func TestEventToString(t *testing.T) {
one, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", ACTION_TEST)
one, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", actionTest)
if err != nil {
t.Errorf("Test Failed. EventToString: Error, %s", err)
}
@@ -108,11 +113,10 @@ func TestEventToString(t *testing.T) {
if !RemoveEvent(one) {
t.Error("Test Failed. EventToString: Error, error removing event")
}
}
func TestCheckCondition(t *testing.T) { //error handling needs to be implemented
one, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", ACTION_TEST)
one, err := AddEvent("ANX", "price", ">,==", "BTC", "LTC", actionTest)
if err != nil {
t.Errorf("Test Failed. EventToString: Error, %s", err)
}
@@ -125,26 +129,58 @@ func TestCheckCondition(t *testing.T) { //error handling needs to be implemented
if !RemoveEvent(one) {
t.Error("Test Failed. EventToString: Error, error removing event")
}
}
func TestIsValidEvent(t *testing.T) {
err := IsValidEvent("ANX", "price", ">,==", ACTION_TEST)
err := IsValidEvent("ANX", "price", ">,==", actionTest)
if err != nil {
t.Errorf("Test Failed. IsValidExchange: Error %s", err)
}
err = IsValidEvent("ANX", "price", ">,", actionTest)
if err == nil {
t.Errorf("Test Failed. IsValidExchange: Error")
}
err = IsValidEvent("ANX", "Testy", ">,==", actionTest)
if err == nil {
t.Errorf("Test Failed. IsValidExchange: Error")
}
err = IsValidEvent("Testys", "price", ">,==", actionTest)
if err == nil {
t.Errorf("Test Failed. IsValidExchange: Error")
}
//Function tests need to appended to this function when more actions are
//implemented
}
func TestCheckEvents(t *testing.T) { //Add error handling
//CheckEvents() //check once error handling is implemented
}
func TestIsValidCurrency(t *testing.T) {
if !IsValidCurrency("BTC") {
t.Error("Test Failed - Event_test.go TestIsValidCurrency Error")
}
if !IsValidCurrency("USD") {
t.Error("Test Failed - Event_test.go TestIsValidCurrency Error")
}
if IsValidCurrency("testy") {
t.Error("Test Failed - Event_test.go TestIsValidCurrency Error")
}
if !IsValidCurrency("USD", "BTC", "USD") {
t.Error("Test Failed - Event_test.go TestIsValidCurrency Error")
}
if IsValidCurrency("USD", "USD", "Wigwham") {
t.Error("Test Failed - Event_test.go TestIsValidCurrency Error")
}
}
func TestIsValidExchange(t *testing.T) {
boolean := IsValidExchange("ANX", CONFIG_PATH_TEST)
boolean := IsValidExchange("ANX", configPathTest)
if !boolean {
t.Error("Test Failed. IsValidExchange: Error, incorrect Exchange")
}
boolean = IsValidExchange("OBTUSE", CONFIG_PATH_TEST)
boolean = IsValidExchange("OBTUSE", configPathTest)
if boolean {
t.Error("Test Failed. IsValidExchange: Error, incorrect return")
}
@@ -182,7 +218,7 @@ func TestIsValidAction(t *testing.T) {
if !boolean {
t.Error("Test Failed. IsValidAction: Error, incorrect Action")
}
boolean = IsValidAction(ACTION_TEST)
boolean = IsValidAction(actionTest)
if !boolean {
t.Error("Test Failed. IsValidAction: Error, incorrect Action")
}

View File

@@ -15,26 +15,27 @@ import (
)
const (
ITEM_PRICE = "PRICE"
GREATER_THAN = ">"
GREATER_THAN_OR_EQUAL = ">="
LESS_THAN = "<"
LESS_THAN_OR_EQUAL = "<="
IS_EQUAL = "=="
ACTION_SMS_NOTIFY = "SMS"
ACTION_CONSOLE_PRINT = "CONSOLE_PRINT"
ACTION_TEST = "ACTION_TEST"
CONFIG_PATH_TEST = config.ConfigTestFile
itemPrice = "PRICE"
greaterThan = ">"
greaterThanOrEqual = ">="
lessThan = "<"
lessThanOrEqual = "<="
isEqual = "=="
actionSMSNotify = "SMS"
actionConsolePrint = "CONSOLE_PRINT"
actionTest = "ACTION_TEST"
configPathTest = config.ConfigTestFile
)
var (
ErrInvalidItem = errors.New("Invalid item.")
ErrInvalidCondition = errors.New("Invalid conditional option.")
ErrInvalidAction = errors.New("Invalid action.")
ErrExchangeDisabled = errors.New("Desired exchange is disabled.")
ErrCurrencyInvalid = errors.New("Invalid currency.")
errInvalidItem = errors.New("invalid item")
errInvalidCondition = errors.New("invalid conditional option")
errInvalidAction = errors.New("invalid action")
errExchangeDisabled = errors.New("desired exchange is disabled")
errCurrencyInvalid = errors.New("invalid currency")
)
// Event struct holds the event variables
type Event struct {
ID int
Exchange string
@@ -46,8 +47,12 @@ type Event struct {
Executed bool
}
// Events variable is a pointer array to the event structures that will be
// appended
var Events []*Event
// AddEvent adds an event to the Events chain and returns an index/eventID
// and an error
func AddEvent(Exchange, Item, Condition, FirstCurrency, SecondCurrency, Action string) (int, error) {
err := IsValidEvent(Exchange, Item, Condition, Action)
if err != nil {
@@ -55,7 +60,7 @@ func AddEvent(Exchange, Item, Condition, FirstCurrency, SecondCurrency, Action s
}
if !IsValidCurrency(FirstCurrency, SecondCurrency) {
return 0, ErrCurrencyInvalid
return 0, errCurrencyInvalid
}
Event := &Event{}
@@ -77,6 +82,7 @@ func AddEvent(Exchange, Item, Condition, FirstCurrency, SecondCurrency, Action s
return Event.ID, nil
}
// RemoveEvent deletes and event by its ID
func RemoveEvent(EventID int) bool {
for i, x := range Events {
if x.ID == EventID {
@@ -87,6 +93,8 @@ func RemoveEvent(EventID int) bool {
return false
}
// GetEventCounter displays the emount of total events on the chain and the
// events that have been executed.
func GetEventCounter() (int, int) {
total := len(Events)
executed := 0
@@ -99,15 +107,18 @@ func GetEventCounter() (int, int) {
return total, executed
}
// ExecuteAction will execute the action pending on the chain
func (e *Event) ExecuteAction() bool {
if common.StringContains(e.Action, ",") {
action := common.SplitStrings(e.Action, ",")
if action[0] == ACTION_SMS_NOTIFY {
if action[0] == actionSMSNotify {
message := fmt.Sprintf("Event triggered: %s", e.EventToString())
if action[1] == "ALL" {
smsglobal.SMSSendToAll(message, config.Cfg)
} else {
smsglobal.SMSNotify(smsglobal.SMSGetNumberByName(action[1], config.Cfg.SMS), message, config.Cfg)
smsglobal.SMSNotify(smsglobal.SMSGetNumberByName(action[1],
config.Cfg.SMS), message, config.Cfg,
)
}
}
} else {
@@ -116,13 +127,18 @@ func (e *Event) ExecuteAction() bool {
return true
}
// EventToString turns the structure event into a string
func (e *Event) EventToString() string {
condition := common.SplitStrings(e.Condition, ",")
return fmt.Sprintf("If the %s%s %s on %s is %s then %s.", e.FirstCurrency, e.SecondCurrency, e.Item, e.Exchange, condition[0]+" "+condition[1], e.Action)
return fmt.Sprintf(
"If the %s%s %s on %s is %s then %s.", e.FirstCurrency, e.SecondCurrency,
e.Item, e.Exchange, condition[0]+" "+condition[1], e.Action,
)
}
func (e *Event) CheckCondition() bool { //Add error handling
lastPrice := 0.00
// CheckCondition will check the event structure to see if there is a condition
// met
func (e *Event) CheckCondition() bool {
condition := common.SplitStrings(e.Condition, ",")
targetPrice, _ := strconv.ParseFloat(condition[1], 64)
@@ -131,38 +147,38 @@ func (e *Event) CheckCondition() bool { //Add error handling
return false
}
lastPrice = ticker.Price[pair.CurrencyItem(e.FirstCurrency)][pair.CurrencyItem(e.SecondCurrency)].Last
lastPrice := ticker.Price[pair.CurrencyItem(e.FirstCurrency)][pair.CurrencyItem(e.SecondCurrency)].Last
if lastPrice == 0 {
return false
}
switch condition[0] {
case GREATER_THAN:
case greaterThan:
{
if lastPrice > targetPrice {
return e.ExecuteAction()
}
}
case GREATER_THAN_OR_EQUAL:
case greaterThanOrEqual:
{
if lastPrice >= targetPrice {
return e.ExecuteAction()
}
}
case LESS_THAN:
case lessThan:
{
if lastPrice < targetPrice {
return e.ExecuteAction()
}
}
case LESS_THAN_OR_EQUAL:
case lessThanOrEqual:
{
if lastPrice <= targetPrice {
return e.ExecuteAction()
}
}
case IS_EQUAL:
case isEqual:
{
if lastPrice == targetPrice {
return e.ExecuteAction()
@@ -172,52 +188,56 @@ func (e *Event) CheckCondition() bool { //Add error handling
return false
}
// IsValidEvent checks the actions to be taken and returns an error if incorrect
func IsValidEvent(Exchange, Item, Condition, Action string) error {
Exchange = common.StringToUpper(Exchange)
Item = common.StringToUpper(Item)
Action = common.StringToUpper(Action)
configPath := ""
if Action == ACTION_TEST {
configPath = CONFIG_PATH_TEST
if Action == actionTest {
configPath = configPathTest
}
if !IsValidExchange(Exchange, configPath) {
return ErrExchangeDisabled
return errExchangeDisabled
}
if !IsValidItem(Item) {
return ErrInvalidItem
return errInvalidItem
}
if !common.StringContains(Condition, ",") {
return ErrInvalidCondition
return errInvalidCondition
}
condition := common.SplitStrings(Condition, ",")
if !IsValidCondition(condition[0]) || len(condition[1]) == 0 {
return ErrInvalidCondition
return errInvalidCondition
}
if common.StringContains(Action, ",") {
action := common.SplitStrings(Action, ",")
if action[0] != ACTION_SMS_NOTIFY {
return ErrInvalidAction
if action[0] != actionSMSNotify {
return errInvalidAction
}
if action[1] != "ALL" && smsglobal.SMSGetNumberByName(action[1], config.Cfg.SMS) == smsglobal.ErrSMSContactNotFound {
return ErrInvalidAction
if action[1] != "ALL" && smsglobal.SMSGetNumberByName(
action[1], config.Cfg.SMS) == smsglobal.ErrSMSContactNotFound {
return errInvalidAction
}
} else {
if Action != ACTION_CONSOLE_PRINT && Action != ACTION_TEST {
return ErrInvalidAction
if Action != actionConsolePrint && Action != actionTest {
return errInvalidAction
}
}
return nil
}
// CheckEvents is the overarching routine that will iterate through the Events
// chain
func CheckEvents() {
for {
total, executed := GetEventCounter()
@@ -226,7 +246,10 @@ func CheckEvents() {
if !event.Executed {
success := event.CheckCondition()
if success {
log.Printf("Event %d triggered on %s successfully.\n", event.ID, event.Exchange)
log.Printf(
"Event %d triggered on %s successfully.\n", event.ID,
event.Exchange,
)
event.Executed = true
}
}
@@ -235,19 +258,21 @@ func CheckEvents() {
}
}
// IsValidCurrency takes in CRYPTO or FIAT currency strings and returns if valid
func IsValidCurrency(currencies ...string) bool {
for _, whatIsIt := range currencies {
for index, whatIsIt := range currencies {
whatIsIt = common.StringToUpper(whatIsIt)
if currency.IsDefaultCryptocurrency(whatIsIt) {
return true
}
if currency.IsDefaultCurrency(whatIsIt) {
return true
if currency.IsDefaultCryptocurrency(whatIsIt) || currency.IsDefaultCurrency(whatIsIt) {
if len(currencies)-1 == index {
return true
}
continue
}
}
return false
}
// IsValidExchange validates the exchange
func IsValidExchange(Exchange, configPath string) bool {
Exchange = common.StringToUpper(Exchange)
@@ -264,27 +289,30 @@ func IsValidExchange(Exchange, configPath string) bool {
return false
}
// IsValidCondition validates passed in condition
func IsValidCondition(Condition string) bool {
switch Condition {
case GREATER_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL, IS_EQUAL:
case greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual, isEqual:
return true
}
return false
}
// IsValidAction validates passed in action
func IsValidAction(Action string) bool {
Action = common.StringToUpper(Action)
switch Action {
case ACTION_SMS_NOTIFY, ACTION_CONSOLE_PRINT, ACTION_TEST:
case actionSMSNotify, actionConsolePrint, actionTest:
return true
}
return false
}
// IsValidItem validates passed in Item
func IsValidItem(Item string) bool {
Item = common.StringToUpper(Item)
switch Item {
case ITEM_PRICE:
case itemPrice:
return true
}
return false

View File

@@ -58,7 +58,7 @@ type ExchangeMain struct {
type Bot struct {
config *config.Config
portfolio *portfolio.PortfolioBase
portfolio *portfolio.Base
exchange ExchangeMain
exchanges []exchange.IBotExchange
tickers []ticker.Ticker
@@ -242,7 +242,7 @@ func SeedExchangeAccountInfo(data []exchange.ExchangeAccountInfo) {
}
if !port.ExchangeAddressExists(exchangeName, currencyName) {
port.Addresses = append(port.Addresses, portfolio.PortfolioAddress{Address: exchangeName, CoinType: currencyName, Balance: total, Decscription: portfolio.PORTFOLIO_ADDRESS_EXCHANGE})
port.Addresses = append(port.Addresses, portfolio.Address{Address: exchangeName, CoinType: currencyName, Balance: total, Decscription: portfolio.PortfolioAddressExchange})
} else {
port.UpdateExchangeAddressBalance(exchangeName, currencyName, total)
}

View File

@@ -10,35 +10,43 @@ import (
)
const (
BLOCKR_API_URL = "blockr.io/api"
BLOCKR_API_VERSION = "1"
BLOCKR_ADDRESS_BALANCE = "address/balance"
blockrAPIURL = "blockr.io/api"
blockrAPIVersion = "1"
blockrAddressBalance = "address/balance"
ETHERCHAIN_API_URL = "https://etherchain.org/api"
ETHERCHAIN_ACCOUNT_MULTIPLE = "account/multiple"
PORTFOLIO_ADDRESS_EXCHANGE = "Exchange"
PORTFOLIO_ADDRESS_PERSONAL = "Personal"
etherchainAPIURL = "https://etherchain.org/api"
etherchainAccountMultiple = "account/multiple"
// PortfolioAddressExchange holds the current portfolio address
PortfolioAddressExchange = "Exchange"
portfolioAddressPersonal = "Personal"
)
var Portfolio PortfolioBase
// Portfolio is variable store holding an array of portfolioAddress
var Portfolio Base
type PortfolioAddress struct {
// 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
Decscription string
}
type PortfolioBase struct {
Addresses []PortfolioAddress
}
// BlockrAddress holds JSON incoming and outgoing data for BLOCKR with address
// information
type BlockrAddress struct {
Address string `json:"address"`
Balance float64 `json:"balance"`
BalanceMultisig float64 `json:"balance_multisig"`
}
// BlockrAddressBalanceSingle holds JSON incoming and outgoing data for BLOCKR
// with address balance information
type BlockrAddressBalanceSingle struct {
Status string `json:"status"`
Data BlockrAddress `json:"data"`
@@ -46,6 +54,8 @@ type BlockrAddressBalanceSingle struct {
Message string `json:"message"`
}
// BlockrAddressBalanceMulti holds JSON incoming and outgoing data for BLOCKR
// with address balance information for multiple wallets
type BlockrAddressBalanceMulti struct {
Status string `json:"status"`
Data []BlockrAddress `json:"data"`
@@ -53,6 +63,8 @@ type BlockrAddressBalanceMulti struct {
Message string `json:"message"`
}
// EtherchainBalanceResponse holds JSON incoming and outgoing data for
// Etherchain
type EtherchainBalanceResponse struct {
Status int `json:"status"`
Data []struct {
@@ -66,7 +78,8 @@ type EtherchainBalanceResponse struct {
} `json:"data"`
}
// ExchangeAccountInfo : Generic type to hold each exchange's holdings in all enabled currencies
// ExchangeAccountInfo : Generic type to hold each exchange's holdings in all
// enabled currencies
type ExchangeAccountInfo struct {
ExchangeName string
Currencies []ExchangeAccountCurrencyInfo
@@ -79,6 +92,8 @@ type ExchangeAccountCurrencyInfo struct {
Hold float64
}
// GetEthereumBalance single or multiple address information as
// EtherchainBalanceResponse
func GetEthereumBalance(address []string) (EtherchainBalanceResponse, error) {
for _, add := range address {
valid, _ := common.IsValidCryptoAddress(add, "eth")
@@ -88,7 +103,9 @@ func GetEthereumBalance(address []string) (EtherchainBalanceResponse, error) {
}
addresses := common.JoinStrings(address, ",")
url := fmt.Sprintf("%s/%s/%s", ETHERCHAIN_API_URL, ETHERCHAIN_ACCOUNT_MULTIPLE, addresses)
url := fmt.Sprintf(
"%s/%s/%s", etherchainAPIURL, etherchainAccountMultiple, addresses,
)
result := EtherchainBalanceResponse{}
err := common.SendHTTPGetRequest(url, true, &result)
if err != nil {
@@ -100,13 +117,20 @@ func GetEthereumBalance(address []string) (EtherchainBalanceResponse, error) {
return result, nil
}
// GetBlockrBalanceSingle queries Blockr for an address balance for either a
// LTC or a BTC single address
func GetBlockrBalanceSingle(address string, coinType string) (BlockrAddressBalanceSingle, error) {
valid, _ := common.IsValidCryptoAddress(address, coinType)
if !valid {
return BlockrAddressBalanceSingle{}, errors.New(fmt.Sprintf("Not a %s address", common.StringToUpper(coinType)))
return BlockrAddressBalanceSingle{}, fmt.Errorf(
"Not a %s address", common.StringToUpper(coinType),
)
}
url := fmt.Sprintf("https://%s.%s/v%s/%s/%s", common.StringToLower(coinType), BLOCKR_API_URL, BLOCKR_API_VERSION, BLOCKR_ADDRESS_BALANCE, address)
url := fmt.Sprintf(
"https://%s.%s/v%s/%s/%s", common.StringToLower(coinType), blockrAPIURL,
blockrAPIVersion, blockrAddressBalance, address,
)
result := BlockrAddressBalanceSingle{}
err := common.SendHTTPGetRequest(url, true, &result)
if err != nil {
@@ -118,15 +142,22 @@ func GetBlockrBalanceSingle(address string, coinType string) (BlockrAddressBalan
return result, nil
}
// GetBlockrAddressMulti queries Blockr for an address balance for either a LTC
// or a BTC multiple addresses
func GetBlockrAddressMulti(addresses []string, coinType string) (BlockrAddressBalanceMulti, error) {
for _, add := range addresses {
valid, _ := common.IsValidCryptoAddress(add, coinType)
if !valid {
return BlockrAddressBalanceMulti{}, errors.New(fmt.Sprintf("Not a %s address", common.StringToUpper(coinType)))
return BlockrAddressBalanceMulti{}, fmt.Errorf(
"Not a %s address", common.StringToUpper(coinType),
)
}
}
addressesStr := common.JoinStrings(addresses, ",")
url := fmt.Sprintf("https://%s.%s/v%s/%s/%s", common.StringToLower(coinType), BLOCKR_API_URL, BLOCKR_API_VERSION, BLOCKR_ADDRESS_BALANCE, addressesStr)
url := fmt.Sprintf(
"https://%s.%s/v%s/%s/%s", common.StringToLower(coinType), blockrAPIURL,
blockrAPIVersion, blockrAddressBalance, addressesStr,
)
result := BlockrAddressBalanceMulti{}
err := common.SendHTTPGetRequest(url, true, &result)
if err != nil {
@@ -138,7 +169,9 @@ func GetBlockrAddressMulti(addresses []string, coinType string) (BlockrAddressBa
return result, nil
}
func (p *PortfolioBase) GetAddressBalance(address string) (float64, bool) {
// GetAddressBalance acceses the portfolio base and returns the balance by passed
// in address
func (p *Base) GetAddressBalance(address string) (float64, bool) {
for _, x := range p.Addresses {
if x.Address == address {
return x.Balance, true
@@ -147,7 +180,8 @@ func (p *PortfolioBase) GetAddressBalance(address string) (float64, bool) {
return 0, false
}
func (p *PortfolioBase) ExchangeExists(exchangeName string) bool {
// ExchangeExists checks to see if an exchange exists in the portfolio base
func (p *Base) ExchangeExists(exchangeName string) bool {
for _, x := range p.Addresses {
if x.Address == exchangeName {
return true
@@ -156,7 +190,9 @@ func (p *PortfolioBase) ExchangeExists(exchangeName string) bool {
return false
}
func (p *PortfolioBase) AddressExists(address string) bool {
// AddressExists checks to see if there is an address associated with the
// portfolio base
func (p *Base) AddressExists(address string) bool {
for _, x := range p.Addresses {
if x.Address == address {
return true
@@ -165,7 +201,9 @@ func (p *PortfolioBase) AddressExists(address string) bool {
return false
}
func (p *PortfolioBase) ExchangeAddressExists(exchangeName, coinType string) bool {
// ExchangeAddressExists checks to see if there is an exchange address
// associated with the portfolio base
func (p *Base) ExchangeAddressExists(exchangeName, coinType string) bool {
for _, x := range p.Addresses {
if x.Address == exchangeName && x.CoinType == coinType {
return true
@@ -174,32 +212,40 @@ func (p *PortfolioBase) ExchangeAddressExists(exchangeName, coinType string) boo
return false
}
func (p *PortfolioBase) UpdateAddressBalance(address string, amount float64) {
for x, _ := range p.Addresses {
// UpdateAddressBalance updates the portfolio base balance
func (p *Base) UpdateAddressBalance(address string, amount float64) {
for x := range p.Addresses {
if p.Addresses[x].Address == address {
p.Addresses[x].Balance = amount
}
}
}
func (p *PortfolioBase) UpdateExchangeAddressBalance(exchangeName, coinType string, balance float64) {
for x, _ := range p.Addresses {
// UpdateExchangeAddressBalance updates the portfolio balance when checked
// against correct exchangeName and coinType.
func (p *Base) UpdateExchangeAddressBalance(exchangeName, coinType string, balance float64) {
for x := range p.Addresses {
if p.Addresses[x].Address == exchangeName && p.Addresses[x].CoinType == coinType {
p.Addresses[x].Balance = balance
}
}
}
func (p *PortfolioBase) AddAddress(address, coinType, description string, balance float64) {
// AddAddress adds an address to the portfolio base
func (p *Base) AddAddress(address, coinType, description string, balance float64) {
if !p.AddressExists(address) {
p.Addresses = append(p.Addresses, PortfolioAddress{Address: address, CoinType: coinType, Balance: balance, Decscription: description})
p.Addresses = append(
p.Addresses, Address{Address: address, CoinType: coinType,
Balance: balance, Decscription: description},
)
} else {
p.UpdateAddressBalance(address, balance)
}
}
func (p *PortfolioBase) UpdatePortfolio(addresses []string, coinType string) bool {
if common.StringContains(common.JoinStrings(addresses, ","), PORTFOLIO_ADDRESS_EXCHANGE) || common.StringContains(common.JoinStrings(addresses, ","), PORTFOLIO_ADDRESS_PERSONAL) {
// UpdatePortfolio adds to the portfolio addresses by coin type
func (p *Base) UpdatePortfolio(addresses []string, coinType string) bool {
if common.StringContains(common.JoinStrings(addresses, ","), PortfolioAddressExchange) || common.StringContains(common.JoinStrings(addresses, ","), portfolioAddressPersonal) {
return true
}
@@ -210,7 +256,7 @@ func (p *PortfolioBase) UpdatePortfolio(addresses []string, coinType string) boo
}
for _, x := range result.Data {
p.AddAddress(x.Address, coinType, PORTFOLIO_ADDRESS_PERSONAL, x.Balance)
p.AddAddress(x.Address, coinType, portfolioAddressPersonal, x.Balance)
}
return true
}
@@ -220,22 +266,25 @@ func (p *PortfolioBase) UpdatePortfolio(addresses []string, coinType string) boo
return false
}
for _, x := range result.Data {
p.AddAddress(x.Address, coinType, PORTFOLIO_ADDRESS_PERSONAL, x.Balance)
p.AddAddress(x.Address, coinType, portfolioAddressPersonal, x.Balance)
}
} else {
result, err := GetBlockrBalanceSingle(addresses[0], coinType)
if err != nil {
return false
}
p.AddAddress(addresses[0], coinType, PORTFOLIO_ADDRESS_PERSONAL, result.Data.Balance)
p.AddAddress(
addresses[0], coinType, portfolioAddressPersonal, result.Data.Balance,
)
}
return true
}
func (p *PortfolioBase) GetExchangePortfolio() map[string]float64 {
// GetExchangePortfolio returns current portfolio base information
func (p *Base) GetExchangePortfolio() map[string]float64 {
result := make(map[string]float64)
for _, x := range p.Addresses {
if x.Decscription != PORTFOLIO_ADDRESS_EXCHANGE {
if x.Decscription != PortfolioAddressExchange {
continue
}
balance, ok := result[x.CoinType]
@@ -248,10 +297,11 @@ func (p *PortfolioBase) GetExchangePortfolio() map[string]float64 {
return result
}
func (p *PortfolioBase) GetPersonalPortfolio() map[string]float64 {
// GetPersonalPortfolio returns current portfolio base information
func (p *Base) GetPersonalPortfolio() map[string]float64 {
result := make(map[string]float64)
for _, x := range p.Addresses {
if x.Decscription == PORTFOLIO_ADDRESS_EXCHANGE {
if x.Decscription == PortfolioAddressExchange {
continue
}
balance, ok := result[x.CoinType]
@@ -264,7 +314,8 @@ func (p *PortfolioBase) GetPersonalPortfolio() map[string]float64 {
return result
}
func (p *PortfolioBase) GetPortfolioSummary(coinFilter string) map[string]float64 {
// GetPortfolioSummary rpoves a summary for your portfolio base
func (p *Base) GetPortfolioSummary(coinFilter string) map[string]float64 {
result := make(map[string]float64)
for _, x := range p.Addresses {
if coinFilter != "" && coinFilter != x.CoinType {
@@ -280,10 +331,11 @@ func (p *PortfolioBase) GetPortfolioSummary(coinFilter string) map[string]float6
return result
}
func (p *PortfolioBase) GetPortfolioGroupedCoin() map[string][]string {
// GetPortfolioGroupedCoin returns portfolio base information grouped by coin
func (p *Base) GetPortfolioGroupedCoin() map[string][]string {
result := make(map[string][]string)
for _, x := range p.Addresses {
if common.StringContains(x.Decscription, PORTFOLIO_ADDRESS_EXCHANGE) || common.StringContains(x.Decscription, PORTFOLIO_ADDRESS_PERSONAL) {
if common.StringContains(x.Decscription, PortfolioAddressExchange) || common.StringContains(x.Decscription, portfolioAddressPersonal) {
continue
}
result[x.CoinType] = append(result[x.CoinType], x.Address)
@@ -291,25 +343,34 @@ func (p *PortfolioBase) GetPortfolioGroupedCoin() map[string][]string {
return result
}
func (p *PortfolioBase) SeedPortfolio(port PortfolioBase) {
// SeedPortfolio appends a portfolio base object with another base portfolio
// addresses
func (p *Base) SeedPortfolio(port Base) {
p.Addresses = port.Addresses
}
// StartPortfolioWatcher observes the portfolio object
func StartPortfolioWatcher() {
addrCount := len(Portfolio.Addresses)
log.Printf("PortfolioWatcher started: Have %d entries in portfolio.\n", addrCount)
log.Printf(
"PortfolioWatcher started: Have %d entries in portfolio.\n", addrCount,
)
for {
data := Portfolio.GetPortfolioGroupedCoin()
for key, value := range data {
success := Portfolio.UpdatePortfolio(value, key)
if success {
log.Printf("PortfolioWatcher: Successfully updated address balance for %s address(es) %s\n", key, value)
log.Printf(
"PortfolioWatcher: Successfully updated address balance for %s address(es) %s\n",
key, value,
)
}
}
time.Sleep(time.Minute * 10)
}
}
func GetPortfolio() *PortfolioBase {
// GetPortfolio returns a pointer to the portfolio base
func GetPortfolio() *Base {
return &Portfolio
}

View File

@@ -1,20 +1,26 @@
package portfolio
import (
"reflect"
"testing"
)
func TestGetEthereumBalance(t *testing.T) {
addresses := []string{"0xb794f5ea0ba39494ce839613fffba74279579268",
"0xe853c56864a2ebe4576a807d26fdc4a0ada51919"}
nonsenseAddress := []string{"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, 0xe853c56864a2ebe4576a807d26fdc4a0ada51919"}
nonsenseAddress := []string{
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"0xe853c56864a2ebe4576a807d26fdc4a0ada51919",
}
response, err := GetEthereumBalance(addresses)
if err != nil {
t.Errorf("Test Failed - Portfolio GetEthereumBalance() Error: %s", err)
}
if len(response.Data) != 2 {
t.Error("Test Failed - Portfolio GetEthereumBalance() Error: Incorrect address")
t.Error(
"Test Failed - Portfolio GetEthereumBalance() Error: Incorrect address",
)
}
response, err = GetEthereumBalance(nonsenseAddress)
@@ -54,13 +60,19 @@ func TestGetBlockrBalanceSingle(t *testing.T) {
t.Errorf("Test Failed - Portfolio GetBlockrBalanceSingle() Error: %s", err)
}
if response.Status == "success" {
t.Error("Test Failed - Portfolio GetBlockrBalanceSingle() Error: Incorrect status")
t.Error(
"Test Failed - Portfolio GetBlockrBalanceSingle() Error: Incorrect status",
)
}
}
func TestGetBlockrAddressMulti(t *testing.T) {
litecoinAddresses := []string{"LdP8Qox1VAhCzLJNqrr74YovaWYyNBUWvL", "LVa8wZ983PvWtdwXZ8viK6SocMENLCXkEy"}
bitcoinAddresses := []string{"3D2oetdNuZUqQHPJmcMDDHYoqkyNVsFk9r", "3Nxwenay9Z8Lc9JBiywExpnEFiLp6Afp8v"}
litecoinAddresses := []string{
"LdP8Qox1VAhCzLJNqrr74YovaWYyNBUWvL", "LVa8wZ983PvWtdwXZ8viK6SocMENLCXkEy",
}
bitcoinAddresses := []string{
"3D2oetdNuZUqQHPJmcMDDHYoqkyNVsFk9r", "3Nxwenay9Z8Lc9JBiywExpnEFiLp6Afp8v",
}
nonsenseAddresses := []string{"DingDong", "ningNang"}
ltc := "LtC"
btc := "bTc"
@@ -85,7 +97,7 @@ func TestGetAddressBalance(t *testing.T) {
description := "Description of Wallet"
balance := float64(1000)
portfolio := PortfolioBase{}
portfolio := Base{}
portfolio.AddAddress(ltcAddress, ltc, description, balance)
addBalance, _ := portfolio.GetAddressBalance("LdP8Qox1VAhCzLJNqrr74YovaWYyNBUWvL")
@@ -102,59 +114,182 @@ func TestGetAddressBalance(t *testing.T) {
}
}
// func TestExchangeExists(t *testing.T) {
// portfolio := PortfolioBase{}
// portfolio.SeedPortfolio(port)
// }
func TestExchangeExists(t *testing.T) {
newBase := Base{}
newBase.AddAddress("someaddress", "LTC", "LTCWALLETTEST", 0.02)
if !newBase.ExchangeExists("someaddress") {
t.Error("Test Failed - portfolio_test.go - AddressExists error")
}
if newBase.ExchangeExists("bla") {
t.Error("Test Failed - portfolio_test.go - AddressExists error")
}
}
func TestAddressExists(t *testing.T) {
newbase := Base{}
newbase.AddAddress("someaddress", "LTC", "LTCWALLETTEST", 0.02)
if !newbase.AddressExists("someaddress") {
t.Error("Test Failed - portfolio_test.go - AddressExists error")
}
if newbase.AddressExists("bla") {
t.Error("Test Failed - portfolio_test.go - AddressExists error")
}
}
func TestExchangeAddressExists(t *testing.T) {
newbase := Base{}
newbase.AddAddress("someaddress", "LTC", "LTCWALLETTEST", 0.02)
if !newbase.ExchangeAddressExists("someaddress", "LTC") {
t.Error("Test Failed - portfolio_test.go - ExchangeAddressExists error")
}
if newbase.ExchangeAddressExists("TEST", "LTC") {
t.Error("Test Failed - portfolio_test.go - ExchangeAddressExists error")
}
}
func TestUpdateAddressBalance(t *testing.T) {
newbase := Base{}
newbase.AddAddress("someaddress", "LTC", "LTCWALLETTEST", 0.02)
newbase.UpdateAddressBalance("someaddress", 0.03)
value := newbase.GetPortfolioSummary("LTC")
if value["LTC"] != 0.03 {
t.Error("Test Failed - portfolio_test.go - UpdateUpdateAddressBalance error")
}
}
func TestUpdateExchangeAddressBalance(t *testing.T) {
newbase := Base{}
newbase.AddAddress("someaddress", "LTC", "LTCWALLETTEST", 0.02)
portfolio := GetPortfolio()
portfolio.SeedPortfolio(newbase)
portfolio.UpdateExchangeAddressBalance("someaddress", "LTC", 0.04)
value := portfolio.GetPortfolioSummary("LTC")
if value["LTC"] != 0.04 {
t.Error("Test Failed - portfolio_test.go - UpdateExchangeAddressBalance error")
}
}
func TestAddAddress(t *testing.T) {
newbase := Base{}
newbase.AddAddress("someaddress", "LTC", "LTCWALLETTEST", 0.02)
portfolio := GetPortfolio()
portfolio.SeedPortfolio(newbase)
if !portfolio.AddressExists("someaddress") {
t.Error("Test Failed - portfolio_test.go - AddAddress error")
}
}
func TestUpdatePortfolio(t *testing.T) {
newbase := Base{}
newbase.AddAddress("someaddress", "LTC", "LTCWALLETTEST", 0.02)
portfolio := GetPortfolio()
portfolio.SeedPortfolio(newbase)
value := portfolio.UpdatePortfolio(
[]string{"LdP8Qox1VAhCzLJNqrr74YovaWYyNBUWvL"}, "LTC",
)
if !value {
t.Error("Test Failed - portfolio_test.go - UpdatePortfolio error")
}
value = portfolio.UpdatePortfolio([]string{"Testy"}, "LTC")
if value {
t.Error("Test Failed - portfolio_test.go - UpdatePortfolio error")
}
value = portfolio.UpdatePortfolio(
[]string{"LdP8Qox1VAhCzLJNqrr74YovaWYyNBUWvL", "LVa8wZ983PvWtdwXZ8viK6SocMENLCXkEy"},
"LTC",
)
if !value {
t.Error("Test Failed - portfolio_test.go - UpdatePortfolio error")
}
value = portfolio.UpdatePortfolio(
[]string{"LdP8Qox1VAhCzLJNqrr74YovaWYyNBUWvL", "Testy"}, "LTC",
)
if value {
t.Error("Test Failed - portfolio_test.go - UpdatePortfolio error")
}
value = portfolio.UpdatePortfolio(
[]string{"0xb794f5ea0ba39494ce839613fffba74279579268",
"0xe853c56864a2ebe4576a807d26fdc4a0ada51919"}, "ETH",
)
if !value {
t.Error("Test Failed - portfolio_test.go - UpdatePortfolio error")
}
value = portfolio.UpdatePortfolio(
[]string{"0xb794f5ea0ba39494ce839613fffba74279579268", "TESTY"}, "ETH",
)
if value {
t.Error("Test Failed - portfolio_test.go - UpdatePortfolio error")
}
}
func TestGetExchangePortfolio(t *testing.T) {
newbase := Base{}
newbase.AddAddress("someaddress", "LTC", "LTCWALLETTEST", 0.02)
portfolio := GetPortfolio()
portfolio.SeedPortfolio(newbase)
value := portfolio.GetExchangePortfolio()
_, ok := value["ANX"]
if ok {
t.Error("Test Failed - portfolio_test.go - GetExchangePortfolio error")
}
}
func TestGetPersonalPortfolio(t *testing.T) {
newbase := Base{}
newbase.AddAddress("someaddress", "LTC", "LTCWALLETTEST", 0.02)
portfolio := GetPortfolio()
portfolio.SeedPortfolio(newbase)
value := portfolio.GetPersonalPortfolio()
_, ok := value["LTC"]
if !ok {
t.Error("Test Failed - portfolio_test.go - GetPersonalPortfolio error")
}
}
func TestGetPortfolioSummary(t *testing.T) {
newbase := Base{}
newbase.AddAddress("someaddress", "LTC", "LTCWALLETTEST", 0.02)
portfolio := GetPortfolio()
portfolio.SeedPortfolio(newbase)
value := portfolio.GetPortfolioSummary("LTC")
if value["LTC"] != 0.02 {
t.Error("Test Failed - portfolio_test.go - GetPortfolioGroupedCoin error")
}
}
func TestGetPortfolioGroupedCoin(t *testing.T) {
newbase := Base{}
newbase.AddAddress("someaddress", "LTC", "LTCWALLETTEST", 0.02)
portfolio := GetPortfolio()
portfolio.SeedPortfolio(newbase)
value := portfolio.GetPortfolioGroupedCoin()
if value["LTC"][0] != "someaddress" {
t.Error("Test Failed - portfolio_test.go - GetPortfolioGroupedCoin error")
}
}
func TestSeedPortfolio(t *testing.T) {
newbase := Base{}
newbase.AddAddress("someaddress", "LTC", "LTCWALLETTEST", 0.02)
portfolio := GetPortfolio()
portfolio.SeedPortfolio(newbase)
if !portfolio.AddressExists("someaddress") {
t.Error("Test Failed - portfolio_test.go - SeedPortfolio error")
}
}
func TestStartPortfolioWatcher(t *testing.T) {
//Not until testTimeoutFeature and errors
}
func TestGetPortfolio(t *testing.T) {
ptrBASE := GetPortfolio()
if reflect.TypeOf(ptrBASE).String() != "*portfolio.Base" {
t.Error("Test Failed - portfolio_test.go - GetoPortfolio error")
}
}

View File

@@ -48,7 +48,7 @@ func main() {
log.Fatal("File isn't in JSON format")
}
port := portfolio.PortfolioBase{}
port := portfolio.Base{}
port.SeedPortfolio(cfg.Portfolio)
result := port.GetPortfolioSummary("")