From 87633c21425792a80846f8352d4625ecebd6d195 Mon Sep 17 00:00:00 2001 From: Adrian Gallagher Date: Sat, 2 Sep 2017 16:31:08 +1000 Subject: [PATCH] Link up orderbook websocket code, improve exchange test coverage and various other fixes --- common/common_test.go | 37 +- currency/currency.go | 33 +- currency/currency_test.go | 98 +++-- currency/pair/pair_test.go | 14 + exchanges/alphapoint/alphapoint_wrapper.go | 30 +- exchanges/anx/anx_wrapper.go | 10 +- exchanges/bitfinex/bitfinex_wrapper.go | 18 +- exchanges/bitfinex/bitfinex_wrapper_test.go | 6 +- exchanges/bitstamp/bitstamp_wrapper.go | 18 +- exchanges/bittrex/bittrex_wrapper.go | 18 +- exchanges/btcc/btcc_wrapper.go | 18 +- exchanges/btce/btce_wrapper.go | 18 +- exchanges/btcmarkets/btcmarkets_wrapper.go | 18 +- exchanges/coinut/coinut_wrapper.go | 18 +- exchanges/exchange.go | 4 +- exchanges/exchange_test.go | 389 +++++++++++++++--- exchanges/gdax/gdax_wrapper.go | 18 +- exchanges/gemini/gemini_wrapper.go | 18 +- exchanges/huobi/huobi_wrapper.go | 18 +- exchanges/itbit/itbit_wrapper.go | 18 +- exchanges/kraken/kraken_wrapper.go | 18 +- exchanges/lakebtc/lakebtc_wrapper.go | 18 +- exchanges/liqui/liqui_wrapper.go | 18 +- .../localbitcoins/localbitcoins_wrapper.go | 18 +- exchanges/okcoin/okcoin_wrapper.go | 18 +- exchanges/orderbook/orderbook.go | 106 +++-- exchanges/orderbook/orderbook_test.go | 266 ++++++++++++ exchanges/poloniex/poloniex_wrapper.go | 18 +- exchanges/ticker/ticker_test.go | 41 ++ main.go | 2 +- orderbook_routes.go | 141 +++++++ restful_router.go | 1 + routines.go | 84 ++-- websocket.go | 1 - 34 files changed, 1215 insertions(+), 354 deletions(-) create mode 100644 exchanges/orderbook/orderbook_test.go create mode 100644 orderbook_routes.go diff --git a/common/common_test.go b/common/common_test.go index e7328612..31b17a0c 100644 --- a/common/common_test.go +++ b/common/common_test.go @@ -209,6 +209,11 @@ func TestBase64Decode(t *testing.T) { expectedOutput, actualResult, err), ) } + + _, err = Base64Decode("-") + if err == nil { + t.Error("Test failed. Bad base64 string failed returned nil error") + } } func TestBase64Encode(t *testing.T) { @@ -226,7 +231,7 @@ func TestBase64Encode(t *testing.T) { func TestStringSliceDifference(t *testing.T) { t.Parallel() originalInputOne := []string{"hello"} - originalInputTwo := []string{"moto"} + originalInputTwo := []string{"hello", "moto"} expectedOutput := []string{"hello moto"} actualResult := StringSliceDifference(originalInputOne, originalInputTwo) if reflect.DeepEqual(expectedOutput, actualResult) { @@ -334,14 +339,17 @@ func TestReplaceString(t *testing.T) { func TestRoundFloat(t *testing.T) { t.Parallel() - originalInput := float64(1.4545445445) - precisionInput := 2 - expectedOutput := float64(1.45) - actualResult := RoundFloat(originalInput, precisionInput) - if expectedOutput != actualResult { - t.Error(fmt.Sprintf( - "Test failed. Expected '%f'. Actual '%f'.", expectedOutput, actualResult), - ) + // mapping of input vs expected result + testTable := map[float64]float64{ + 2.3232323: 2.32, + -2.3232323: -2.32, + } + for testInput, expectedOutput := range testTable { + actualOutput := RoundFloat(testInput, 2) + if actualOutput != expectedOutput { + t.Error(fmt.Sprintf("Test failed. RoundFloat Expected '%f'. Actual '%f'.", + expectedOutput, actualOutput)) + } } } @@ -650,6 +658,11 @@ func TestWriteFile(t *testing.T) { if err != nil { t.Errorf("Test failed. Common WriteFile error: %s", err) } + + err = WriteFile("", nil) + if err == nil { + t.Error("Test failed. Common WriteFile allowed bad path") + } } func TestRemoveFile(t *testing.T) { @@ -672,9 +685,9 @@ func TestGetURIPath(t *testing.T) { t.Parallel() // mapping of input vs expected result testTable := map[string]string{ - "https://api.gdax.com/accounts": "/accounts", - "https://api.gdax.com/accounts?a=1&b=2": "/accounts?a=1&b=2", - "ht:tp:/invalidurl": "", + "https://api.gdax.com/accounts": "/accounts", + "https://api.gdax.com/accounts?a=1&b=2": "/accounts?a=1&b=2", + "http://www.google.com/accounts?!@#$%;^^": "", } for testInput, expectedOutput := range testTable { actualOutput := GetURIPath(testInput) diff --git a/currency/currency.go b/currency/currency.go index 32cfe22d..e29d8d7c 100644 --- a/currency/currency.go +++ b/currency/currency.go @@ -50,7 +50,6 @@ const ( maxCurrencyPairsPerRequest = 350 yahooYQLURL = "https://query.yahooapis.com/v1/public/yql?" yahooDatabase = "store://datatables.org/alltableswithkeys" - yahooEnabled = false fixerAPI = "http://api.fixer.io/latest" // DefaultCurrencies has the default minimum of FIAT values DefaultCurrencies = "USD,AUD,EUR,CNY" @@ -69,6 +68,7 @@ var ( ErrCurrencyNotFound = errors.New("unable to find specified currency") ErrQueryingYahoo = errors.New("unable to query Yahoo currency values") ErrQueryingYahooZeroCount = errors.New("yahoo returned zero currency data") + yahooEnabled = false ) // IsDefaultCurrency checks if the currency passed in matches the default @@ -91,6 +91,7 @@ func IsDefaultCryptocurrency(currency string) bool { func IsFiatCurrency(currency string) bool { if BaseCurrencies == "" { log.Println("IsFiatCurrency: BaseCurrencies string variable not populated") + return false } return common.StringContains(BaseCurrencies, common.StringToUpper(currency)) } @@ -101,6 +102,7 @@ func IsCryptocurrency(currency string) bool { log.Println( "IsCryptocurrency: CryptoCurrencies string variable not populated", ) + return false } return common.StringContains(CryptoCurrencies, common.StringToUpper(currency)) } @@ -313,6 +315,8 @@ func FetchYahooCurrencyData(currencyPairs []string) error { return err } + log.Printf("Currency recv: %s", resp) + yahooResp := YahooJSONResponse{} err = common.JSONDecode([]byte(resp), &yahooResp) if err != nil { @@ -338,30 +342,5 @@ func QueryYahooCurrencyValues(currencies string) error { "%d fiat currency pairs generated. Fetching Yahoo currency data (this may take a minute)..\n", len(currencyPairs), ) - var err error - var pairs []string - index := 0 - - if len(currencyPairs) > maxCurrencyPairsPerRequest { - for index < len(currencyPairs) { - if len(currencyPairs)-index > maxCurrencyPairsPerRequest { - pairs = currencyPairs[index : index+maxCurrencyPairsPerRequest] - index += maxCurrencyPairsPerRequest - } else { - pairs = currencyPairs[index:] - index += (len(currencyPairs) - index) - } - err = FetchYahooCurrencyData(pairs) - if err != nil { - return err - } - } - } else { - pairs = currencyPairs[index:] - err = FetchYahooCurrencyData(pairs) - if err != nil { - return err - } - } - return nil + return FetchYahooCurrencyData(currencyPairs) } diff --git a/currency/currency_test.go b/currency/currency_test.go index 17a081d1..e7a0ad00 100644 --- a/currency/currency_test.go +++ b/currency/currency_test.go @@ -58,6 +58,10 @@ func TestIsDefaultCryptocurrency(t *testing.T) { func TestIsFiatCurrency(t *testing.T) { t.Parallel() + if IsFiatCurrency("") { + t.Error("Test failed. TestIsFiatCurrency returned true on an empty string") + } + BaseCurrencies = "USD,AUD" var str1, str2, str3 string = "BTC", "USD", "birds123" @@ -81,6 +85,10 @@ func TestIsFiatCurrency(t *testing.T) { func TestIsCryptocurrency(t *testing.T) { t.Parallel() + if IsCryptocurrency("") { + t.Error("Test failed. TestIsCryptocurrency returned true on an empty string") + } + CryptoCurrencies = "BTC,LTC,DASH" var str1, str2, str3 string = "USD", "BTC", "pterodactyl123" @@ -256,25 +264,25 @@ func TestCheckAndAddCurrency(t *testing.T) { } func TestSeedCurrencyData(t *testing.T) { - currencyRequestDefault := "" - currencyRequestUSDAUD := "USD,AUD" - currencyRequestObtuse := "WigWham" - - err := SeedCurrencyData(currencyRequestDefault) - if err != nil { - t.Errorf( - "Test Failed. SeedCurrencyData: Error %s with currency as %s.", - err, currencyRequestDefault, - ) - } - err2 := SeedCurrencyData(currencyRequestUSDAUD) - if err2 != nil { - t.Errorf( - "Test Failed. SeedCurrencyData: Error %s with currency as %s.", - err2, currencyRequestUSDAUD, - ) - } if yahooEnabled { + currencyRequestDefault := "" + currencyRequestUSDAUD := "USD,AUD" + currencyRequestObtuse := "WigWham" + + err := SeedCurrencyData(currencyRequestDefault) + if err != nil { + t.Errorf( + "Test Failed. SeedCurrencyData: Error %s with currency as %s.", + err, currencyRequestDefault, + ) + } + err2 := SeedCurrencyData(currencyRequestUSDAUD) + if err2 != nil { + t.Errorf( + "Test Failed. SeedCurrencyData: Error %s with currency as %s.", + err2, currencyRequestUSDAUD, + ) + } err3 := SeedCurrencyData(currencyRequestObtuse) if err3 == nil { t.Errorf( @@ -283,6 +291,12 @@ func TestSeedCurrencyData(t *testing.T) { ) } } + + yahooEnabled = false + err := SeedCurrencyData("") + if err != nil { + t.Errorf("Test failed. SeedCurrencyData via Fixer. Error: %s", err) + } } func TestMakecurrencyPairs(t *testing.T) { @@ -299,12 +313,10 @@ func TestMakecurrencyPairs(t *testing.T) { } func TestConvertCurrency(t *testing.T) { - fiatCurrencies := DefaultCurrencies - for _, currencyFrom := range common.SplitStrings(fiatCurrencies, ",") { - for _, currencyTo := range common.SplitStrings(fiatCurrencies, ",") { - if currencyFrom == currencyTo { - continue - } else { + if yahooEnabled { + fiatCurrencies := DefaultCurrencies + for _, currencyFrom := range common.SplitStrings(fiatCurrencies, ",") { + for _, currencyTo := range common.SplitStrings(fiatCurrencies, ",") { floatyMcfloat, err := ConvertCurrency(1000, currencyFrom, currencyTo) if err != nil { t.Errorf( @@ -323,6 +335,44 @@ func TestConvertCurrency(t *testing.T) { } } } + + yahooEnabled = false + _, err := ConvertCurrency(1000, "USD", "AUD") + if err != nil { + t.Errorf("Test failed. ConvertCurrency USD -> AUD. Error %s", err) + } + + _, err = ConvertCurrency(1000, "AUD", "USD") + if err != nil { + t.Errorf("Test failed. ConvertCurrency AUD -> AUD. Error %s", err) + } + + _, err = ConvertCurrency(1000, "CNY", "AUD") + if err != nil { + t.Errorf("Test failed. ConvertCurrency USD -> AUD. Error %s", err) + } + + // Test non-existant currencies + + _, err = ConvertCurrency(1000, "ASDF", "USD") + if err == nil { + t.Errorf("Test failed. ConvertCurrency non-existant currency -> USD. Error %s", err) + } + + _, err = ConvertCurrency(1000, "USD", "ASDF") + if err == nil { + t.Errorf("Test failed. ConvertCurrency USD -> non-existant currency. Error %s", err) + } + + _, err = ConvertCurrency(1000, "CNY", "UAHF") + if err == nil { + t.Errorf("Test failed. ConvertCurrency non-USD currency CNY -> non-existant currency. Error %s", err) + } + + _, err = ConvertCurrency(1000, "UASF", "UAHF") + if err == nil { + t.Errorf("Test failed. ConvertCurrency non-existant currency -> non-existant currency. Error %s", err) + } } func TestFetchFixerCurrencyData(t *testing.T) { diff --git a/currency/pair/pair_test.go b/currency/pair/pair_test.go index e787bba2..736abd01 100644 --- a/currency/pair/pair_test.go +++ b/currency/pair/pair_test.go @@ -158,6 +158,20 @@ func TestNewCurrencyPairFromIndex(t *testing.T) { actual, expected, ) } + + currency = "DOGEBTC" + + pair = NewCurrencyPairFromIndex(currency, index) + pair.Delimiter = "-" + actual = pair.Pair() + + expected = CurrencyItem("DOGE-BTC") + if actual != expected { + t.Errorf( + "Test failed. Pair(): %s was not equal to expected value: %s", + actual, expected, + ) + } } func TestNewCurrencyPairFromString(t *testing.T) { diff --git a/exchanges/alphapoint/alphapoint_wrapper.go b/exchanges/alphapoint/alphapoint_wrapper.go index b93b0968..8ccbdfab 100644 --- a/exchanges/alphapoint/alphapoint_wrapper.go +++ b/exchanges/alphapoint/alphapoint_wrapper.go @@ -29,7 +29,7 @@ func (a *Alphapoint) GetExchangeAccountInfo() (exchange.AccountInfo, error) { } // UpdateTicker updates and returns the ticker for a currency pair -func (a *Alphapoint) UpdateTicker(p pair.CurrencyPair) (ticker.Price, error) { +func (a *Alphapoint) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) { var tickerPrice ticker.Price tick, err := a.GetTicker(p.Pair().String()) if err != nil { @@ -43,22 +43,22 @@ func (a *Alphapoint) UpdateTicker(p pair.CurrencyPair) (ticker.Price, error) { tickerPrice.High = tick.High tickerPrice.Volume = tick.Volume tickerPrice.Last = tick.Last - ticker.ProcessTicker(a.GetName(), p, tickerPrice, ticker.Spot) - return ticker.GetTicker(a.Name, p, ticker.Spot) + ticker.ProcessTicker(a.GetName(), p, tickerPrice, assetType) + return ticker.GetTicker(a.Name, p, assetType) } // GetTickerPrice returns the ticker for a currency pair -func (a *Alphapoint) GetTickerPrice(p pair.CurrencyPair) (ticker.Price, error) { - tick, err := ticker.GetTicker(a.GetName(), p, ticker.Spot) +func (a *Alphapoint) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) { + tick, err := ticker.GetTicker(a.GetName(), p, assetType) if err != nil { - return a.UpdateTicker(p) + return a.UpdateTicker(p, assetType) } return tick, nil } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (a *Alphapoint) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - var orderBook orderbook.OrderbookBase +func (a *Alphapoint) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base orderbookNew, err := a.GetOrderbook(p.Pair().String()) if err != nil { return orderBook, err @@ -66,23 +66,23 @@ func (a *Alphapoint) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBa for x := range orderbookNew.Bids { data := orderbookNew.Bids[x] - orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: data.Quantity, Price: data.Price}) + orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data.Quantity, Price: data.Price}) } for x := range orderbookNew.Asks { data := orderbookNew.Asks[x] - orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: data.Quantity, Price: data.Price}) + orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data.Quantity, Price: data.Price}) } - orderbook.ProcessOrderbook(a.GetName(), p, orderBook) - return orderbook.GetOrderbook(a.Name, p) + orderbook.ProcessOrderbook(a.GetName(), p, orderBook, assetType) + return orderbook.GetOrderbook(a.Name, p, assetType) } // GetOrderbookEx returns the orderbook for a currency pair -func (a *Alphapoint) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - ob, err := orderbook.GetOrderbook(a.GetName(), p) +func (a *Alphapoint) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(a.GetName(), p, assetType) if err == nil { - return a.UpdateOrderbook(p) + return a.UpdateOrderbook(p, assetType) } return ob, nil } diff --git a/exchanges/anx/anx_wrapper.go b/exchanges/anx/anx_wrapper.go index 8c6e9c8c..cf70d97b 100644 --- a/exchanges/anx/anx_wrapper.go +++ b/exchanges/anx/anx_wrapper.go @@ -100,17 +100,17 @@ func (a *ANX) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Pric } // GetOrderbookEx returns the orderbook for a currency pair -func (a *ANX) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - ob, err := orderbook.GetOrderbook(a.GetName(), p) +func (a *ANX) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(a.GetName(), p, assetType) if err == nil { - return a.UpdateOrderbook(p) + return a.UpdateOrderbook(p, assetType) } return ob, nil } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (a *ANX) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - var orderBook orderbook.OrderbookBase +func (a *ANX) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base return orderBook, nil } diff --git a/exchanges/bitfinex/bitfinex_wrapper.go b/exchanges/bitfinex/bitfinex_wrapper.go index 8a817e52..a2033f31 100644 --- a/exchanges/bitfinex/bitfinex_wrapper.go +++ b/exchanges/bitfinex/bitfinex_wrapper.go @@ -67,32 +67,32 @@ func (b *Bitfinex) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker } // GetOrderbookEx returns the orderbook for a currency pair -func (b *Bitfinex) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - ob, err := orderbook.GetOrderbook(b.GetName(), p) +func (b *Bitfinex) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(b.GetName(), p, assetType) if err == nil { - return b.UpdateOrderbook(p) + return b.UpdateOrderbook(p, assetType) } return ob, nil } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (b *Bitfinex) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - var orderBook orderbook.OrderbookBase +func (b *Bitfinex) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base orderbookNew, err := b.GetOrderbook(p.Pair().String(), nil) if err != nil { return orderBook, err } for x := range orderbookNew.Asks { - orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Price: orderbookNew.Asks[x].Price, Amount: orderbookNew.Asks[x].Amount}) + orderBook.Asks = append(orderBook.Asks, orderbook.Item{Price: orderbookNew.Asks[x].Price, Amount: orderbookNew.Asks[x].Amount}) } for x := range orderbookNew.Bids { - orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Price: orderbookNew.Bids[x].Price, Amount: orderbookNew.Bids[x].Amount}) + orderBook.Bids = append(orderBook.Bids, orderbook.Item{Price: orderbookNew.Bids[x].Price, Amount: orderbookNew.Bids[x].Amount}) } - orderbook.ProcessOrderbook(b.GetName(), p, orderBook) - return orderbook.GetOrderbook(b.Name, p) + orderbook.ProcessOrderbook(b.GetName(), p, orderBook, assetType) + return orderbook.GetOrderbook(b.Name, p, assetType) } // GetExchangeAccountInfo retrieves balances for all enabled currencies on the diff --git a/exchanges/bitfinex/bitfinex_wrapper_test.go b/exchanges/bitfinex/bitfinex_wrapper_test.go index 4b614392..a565c0ba 100644 --- a/exchanges/bitfinex/bitfinex_wrapper_test.go +++ b/exchanges/bitfinex/bitfinex_wrapper_test.go @@ -19,7 +19,8 @@ func TestRun(t *testing.T) { func TestGetTickerPrice(t *testing.T) { getTickerPrice := Bitfinex{} - _, err := getTickerPrice.GetTickerPrice(pair.NewCurrencyPair("BTC", "USD"), ticker.Spot) + _, err := getTickerPrice.GetTickerPrice(pair.NewCurrencyPair("BTC", "USD"), + ticker.Spot) if err != nil { t.Errorf("Test Failed - Bitfinex GetTickerPrice() error: %s", err) } @@ -27,7 +28,8 @@ func TestGetTickerPrice(t *testing.T) { func TestGetOrderbookEx(t *testing.T) { getOrderBookEx := Bitfinex{} - _, err := getOrderBookEx.GetOrderbookEx(pair.NewCurrencyPair("BTC", "USD")) + _, err := getOrderBookEx.GetOrderbookEx(pair.NewCurrencyPair("BTC", "USD"), + ticker.Spot) if err != nil { t.Errorf("Test Failed - Bitfinex GetOrderbookEx() error: %s", err) } diff --git a/exchanges/bitstamp/bitstamp_wrapper.go b/exchanges/bitstamp/bitstamp_wrapper.go index 31c44b53..b8183e99 100644 --- a/exchanges/bitstamp/bitstamp_wrapper.go +++ b/exchanges/bitstamp/bitstamp_wrapper.go @@ -57,17 +57,17 @@ func (b *Bitstamp) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker } // GetOrderbookEx returns the orderbook for a currency pair -func (b *Bitstamp) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - ob, err := orderbook.GetOrderbook(b.GetName(), p) +func (b *Bitstamp) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(b.GetName(), p, assetType) if err == nil { - return b.UpdateOrderbook(p) + return b.UpdateOrderbook(p, assetType) } return ob, nil } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (b *Bitstamp) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - var orderBook orderbook.OrderbookBase +func (b *Bitstamp) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base orderbookNew, err := b.GetOrderbook(p.Pair().String()) if err != nil { return orderBook, err @@ -75,16 +75,16 @@ func (b *Bitstamp) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase for x := range orderbookNew.Bids { data := orderbookNew.Bids[x] - orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: data.Amount, Price: data.Price}) + orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data.Amount, Price: data.Price}) } for x := range orderbookNew.Asks { data := orderbookNew.Asks[x] - orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: data.Amount, Price: data.Price}) + orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data.Amount, Price: data.Price}) } - orderbook.ProcessOrderbook(b.GetName(), p, orderBook) - return orderbook.GetOrderbook(b.Name, p) + orderbook.ProcessOrderbook(b.GetName(), p, orderBook, assetType) + return orderbook.GetOrderbook(b.Name, p, assetType) } // GetExchangeAccountInfo retrieves balances for all enabled currencies for the diff --git a/exchanges/bittrex/bittrex_wrapper.go b/exchanges/bittrex/bittrex_wrapper.go index 00392a32..843cc733 100644 --- a/exchanges/bittrex/bittrex_wrapper.go +++ b/exchanges/bittrex/bittrex_wrapper.go @@ -100,17 +100,17 @@ func (b *Bittrex) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker. } // GetOrderbookEx returns the orderbook for a currency pair -func (b *Bittrex) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - ob, err := orderbook.GetOrderbook(b.GetName(), p) +func (b *Bittrex) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(b.GetName(), p, assetType) if err == nil { - return b.UpdateOrderbook(p) + return b.UpdateOrderbook(p, assetType) } return ob, nil } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (b *Bittrex) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - var orderBook orderbook.OrderbookBase +func (b *Bittrex) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base orderbookNew, err := b.GetOrderbook(exchange.FormatExchangeCurrency(b.GetName(), p).String()) if err != nil { return orderBook, err @@ -118,7 +118,7 @@ func (b *Bittrex) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, for x := range orderbookNew.Buy { orderBook.Bids = append(orderBook.Bids, - orderbook.OrderbookItem{ + orderbook.Item{ Amount: orderbookNew.Buy[x].Quantity, Price: orderbookNew.Buy[x].Rate, }, @@ -127,13 +127,13 @@ func (b *Bittrex) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, for x := range orderbookNew.Sell { orderBook.Asks = append(orderBook.Asks, - orderbook.OrderbookItem{ + orderbook.Item{ Amount: orderbookNew.Sell[x].Quantity, Price: orderbookNew.Sell[x].Rate, }, ) } - orderbook.ProcessOrderbook(b.GetName(), p, orderBook) - return orderbook.GetOrderbook(b.Name, p) + orderbook.ProcessOrderbook(b.GetName(), p, orderBook, assetType) + return orderbook.GetOrderbook(b.Name, p, assetType) } diff --git a/exchanges/btcc/btcc_wrapper.go b/exchanges/btcc/btcc_wrapper.go index bbab1953..a3dcf97b 100644 --- a/exchanges/btcc/btcc_wrapper.go +++ b/exchanges/btcc/btcc_wrapper.go @@ -56,17 +56,17 @@ func (b *BTCC) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Pri } // GetOrderbookEx returns the orderbook for a currency pair -func (b *BTCC) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - ob, err := orderbook.GetOrderbook(b.GetName(), p) +func (b *BTCC) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(b.GetName(), p, assetType) if err == nil { - return b.UpdateOrderbook(p) + return b.UpdateOrderbook(p, assetType) } return ob, nil } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (b *BTCC) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - var orderBook orderbook.OrderbookBase +func (b *BTCC) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base orderbookNew, err := b.GetOrderBook(exchange.FormatExchangeCurrency(b.GetName(), p).String(), 100) if err != nil { return orderBook, err @@ -74,16 +74,16 @@ func (b *BTCC) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, er for x := range orderbookNew.Bids { data := orderbookNew.Bids[x] - orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Price: data[0], Amount: data[1]}) + orderBook.Bids = append(orderBook.Bids, orderbook.Item{Price: data[0], Amount: data[1]}) } for x := range orderbookNew.Asks { data := orderbookNew.Asks[x] - orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Price: data[0], Amount: data[1]}) + orderBook.Asks = append(orderBook.Asks, orderbook.Item{Price: data[0], Amount: data[1]}) } - orderbook.ProcessOrderbook(b.GetName(), p, orderBook) - return orderbook.GetOrderbook(b.Name, p) + orderbook.ProcessOrderbook(b.GetName(), p, orderBook, assetType) + return orderbook.GetOrderbook(b.Name, p, assetType) } // GetExchangeAccountInfo : Retrieves balances for all enabled currencies for diff --git a/exchanges/btce/btce_wrapper.go b/exchanges/btce/btce_wrapper.go index 0b8e3eed..ff180858 100644 --- a/exchanges/btce/btce_wrapper.go +++ b/exchanges/btce/btce_wrapper.go @@ -85,17 +85,17 @@ func (b *BTCE) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Pri } // GetOrderbookEx returns the orderbook for a currency pair -func (b *BTCE) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - ob, err := orderbook.GetOrderbook(b.GetName(), p) +func (b *BTCE) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(b.GetName(), p, assetType) if err == nil { - return b.UpdateOrderbook(p) + return b.UpdateOrderbook(p, assetType) } return ob, nil } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (b *BTCE) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - var orderBook orderbook.OrderbookBase +func (b *BTCE) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base orderbookNew, err := b.GetDepth(exchange.FormatExchangeCurrency(b.Name, p).String()) if err != nil { return orderBook, err @@ -103,16 +103,16 @@ func (b *BTCE) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, er for x := range orderbookNew.Bids { data := orderbookNew.Bids[x] - orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Price: data[0], Amount: data[1]}) + orderBook.Bids = append(orderBook.Bids, orderbook.Item{Price: data[0], Amount: data[1]}) } for x := range orderbookNew.Asks { data := orderbookNew.Asks[x] - orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Price: data[0], Amount: data[1]}) + orderBook.Asks = append(orderBook.Asks, orderbook.Item{Price: data[0], Amount: data[1]}) } - orderbook.ProcessOrderbook(b.GetName(), p, orderBook) - return orderbook.GetOrderbook(b.Name, p) + orderbook.ProcessOrderbook(b.GetName(), p, orderBook, assetType) + return orderbook.GetOrderbook(b.Name, p, assetType) } // GetExchangeAccountInfo retrieves balances for all enabled currencies for the diff --git a/exchanges/btcmarkets/btcmarkets_wrapper.go b/exchanges/btcmarkets/btcmarkets_wrapper.go index 841b316b..d9fde312 100644 --- a/exchanges/btcmarkets/btcmarkets_wrapper.go +++ b/exchanges/btcmarkets/btcmarkets_wrapper.go @@ -75,17 +75,17 @@ func (b *BTCMarkets) GetTickerPrice(p pair.CurrencyPair, assetType string) (tick } // GetOrderbookEx returns orderbook base on the currency pair -func (b *BTCMarkets) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - ob, err := orderbook.GetOrderbook(b.GetName(), p) +func (b *BTCMarkets) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(b.GetName(), p, assetType) if err == nil { - return b.UpdateOrderbook(p) + return b.UpdateOrderbook(p, assetType) } return ob, nil } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (b *BTCMarkets) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - var orderBook orderbook.OrderbookBase +func (b *BTCMarkets) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base orderbookNew, err := b.GetOrderbook(p.GetFirstCurrency().String()) if err != nil { return orderBook, err @@ -93,16 +93,16 @@ func (b *BTCMarkets) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBa for x := range orderbookNew.Bids { data := orderbookNew.Bids[x] - orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: data[1], Price: data[0]}) + orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data[1], Price: data[0]}) } for x := range orderbookNew.Asks { data := orderbookNew.Asks[x] - orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: data[1], Price: data[0]}) + orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data[1], Price: data[0]}) } - orderbook.ProcessOrderbook(b.GetName(), p, orderBook) - return orderbook.GetOrderbook(b.Name, p) + orderbook.ProcessOrderbook(b.GetName(), p, orderBook, assetType) + return orderbook.GetOrderbook(b.Name, p, assetType) } // GetExchangeAccountInfo retrieves balances for all enabled currencies for the diff --git a/exchanges/coinut/coinut_wrapper.go b/exchanges/coinut/coinut_wrapper.go index bb60982e..0035e049 100644 --- a/exchanges/coinut/coinut_wrapper.go +++ b/exchanges/coinut/coinut_wrapper.go @@ -96,30 +96,30 @@ func (c *COINUT) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.P } // GetOrderbookEx returns orderbook base on the currency pair -func (c *COINUT) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - ob, err := orderbook.GetOrderbook(c.GetName(), p) +func (c *COINUT) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(c.GetName(), p, assetType) if err == nil { - return c.UpdateOrderbook(p) + return c.UpdateOrderbook(p, assetType) } return ob, nil } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (c *COINUT) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - var orderBook orderbook.OrderbookBase +func (c *COINUT) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base orderbookNew, err := c.GetInstrumentOrderbook(c.InstrumentMap[p.Pair().String()], 200) if err != nil { return orderBook, err } for x := range orderbookNew.Buy { - orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: orderbookNew.Buy[x].Quantity, Price: orderbookNew.Buy[x].Price}) + orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: orderbookNew.Buy[x].Quantity, Price: orderbookNew.Buy[x].Price}) } for x := range orderbookNew.Sell { - orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: orderbookNew.Sell[x].Quantity, Price: orderbookNew.Sell[x].Price}) + orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: orderbookNew.Sell[x].Quantity, Price: orderbookNew.Sell[x].Price}) } - orderbook.ProcessOrderbook(c.GetName(), p, orderBook) - return orderbook.GetOrderbook(c.Name, p) + orderbook.ProcessOrderbook(c.GetName(), p, orderBook, assetType) + return orderbook.GetOrderbook(c.Name, p, assetType) } diff --git a/exchanges/exchange.go b/exchanges/exchange.go index 89c859e2..f66e382b 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -66,8 +66,8 @@ type IBotExchange interface { IsEnabled() bool GetTickerPrice(currency pair.CurrencyPair, assetType string) (ticker.Price, error) UpdateTicker(currency pair.CurrencyPair, assetType string) (ticker.Price, error) - GetOrderbookEx(currency pair.CurrencyPair) (orderbook.OrderbookBase, error) - UpdateOrderbook(currency pair.CurrencyPair) (orderbook.OrderbookBase, error) + GetOrderbookEx(currency pair.CurrencyPair, assetType string) (orderbook.Base, error) + UpdateOrderbook(currency pair.CurrencyPair, assetType string) (orderbook.Base, error) GetEnabledCurrencies() []pair.CurrencyPair GetExchangeAccountInfo() (AccountInfo, error) GetAuthenticatedAPISupport() bool diff --git a/exchanges/exchange_test.go b/exchanges/exchange_test.go index 37039d60..2e5e71d1 100644 --- a/exchanges/exchange_test.go +++ b/exchanges/exchange_test.go @@ -3,10 +3,157 @@ package exchange import ( "testing" + "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" "github.com/thrasher-/gocryptotrader/currency/pair" + "github.com/thrasher-/gocryptotrader/exchanges/ticker" ) +func TestSetAssetTypes(t *testing.T) { + cfg := config.GetConfig() + err := cfg.LoadConfig(config.ConfigTestFile) + if err != nil { + t.Fatalf("Test failed. TestSetAssetTypes failed to load config file. Error: %s", err) + } + + b := Base{ + Name: "TESTNAME", + } + + err = b.SetAssetTypes() + if err == nil { + t.Fatal("Test failed. TestSetAssetTypes returned nil error for a non-existant exchange") + } + + b.Name = "ANX" + err = b.SetAssetTypes() + if err != nil { + t.Fatalf("Test failed. TestSetAssetTypes. Error %s", err) + } + + exch, err := cfg.GetExchangeConfig(b.Name) + if err != nil { + t.Fatalf("Test failed. TestSetAssetTypes load config failed. Error %s", err) + } + + exch.AssetTypes = "" + err = cfg.UpdateExchangeConfig(exch) + if err != nil { + t.Fatalf("Test failed. TestSetAssetTypes update config failed. Error %s", err) + } + + exch, err = cfg.GetExchangeConfig(b.Name) + if err != nil { + t.Fatalf("Test failed. TestSetAssetTypes load config failed. Error %s", err) + } + + if exch.AssetTypes != "" { + t.Fatal("Test failed. TestSetAssetTypes assetTypes != ''") + } + + err = b.SetAssetTypes() + if err != nil { + t.Fatalf("Test failed. TestSetAssetTypes. Error %s", err) + } + + if !common.DataContains(b.AssetTypes, ticker.Spot) { + t.Fatal("Test failed. TestSetAssetTypes assetTypes is not set") + } +} + +func TestGetExchangeAssetTypes(t *testing.T) { + cfg := config.GetConfig() + err := cfg.LoadConfig(config.ConfigTestFile) + if err != nil { + t.Fatalf("Failed to load config file. Error: %s", err) + } + + result, err := GetExchangeAssetTypes("Bitfinex") + if err != nil { + t.Fatal("Test failed. Unable to obtain Bitfinex asset types") + } + + if !common.DataContains(result, ticker.Spot) { + t.Fatal("Test failed. Bitfinex does not contain default asset type 'SPOT'") + } + + _, err = GetExchangeAssetTypes("non-existant-exchange") + if err == nil { + t.Fatal("Test failed. Got asset types for non-existant exchange") + } +} + +func TestSetCurrencyPairFormat(t *testing.T) { + cfg := config.GetConfig() + err := cfg.LoadConfig(config.ConfigTestFile) + if err != nil { + t.Fatalf("Test failed. TestSetCurrencyPairFormat failed to load config file. Error: %s", err) + } + + b := Base{ + Name: "TESTNAME", + } + + err = b.SetCurrencyPairFormat() + if err == nil { + t.Fatal("Test failed. TestSetCurrencyPairFormat returned nil error for a non-existant exchange") + } + + b.Name = "ANX" + err = b.SetCurrencyPairFormat() + if err != nil { + t.Fatalf("Test failed. TestSetCurrencyPairFormat. Error %s", err) + } + + exch, err := cfg.GetExchangeConfig(b.Name) + if err != nil { + t.Fatalf("Test failed. TestSetCurrencyPairFormat load config failed. Error %s", err) + } + + exch.ConfigCurrencyPairFormat = nil + exch.RequestCurrencyPairFormat = nil + err = cfg.UpdateExchangeConfig(exch) + if err != nil { + t.Fatalf("Test failed. TestSetCurrencyPairFormat update config failed. Error %s", err) + } + + exch, err = cfg.GetExchangeConfig(b.Name) + if err != nil { + t.Fatalf("Test failed. TestSetCurrencyPairFormat load config failed. Error %s", err) + } + + if exch.ConfigCurrencyPairFormat != nil && exch.RequestCurrencyPairFormat != nil { + t.Fatal("Test failed. TestSetCurrencyPairFormat exch values are not nil") + } + + err = b.SetCurrencyPairFormat() + if err != nil { + t.Fatalf("Test failed. TestSetCurrencyPairFormat. Error %s", err) + } + + if b.ConfigCurrencyPairFormat.Delimiter != "" && + b.ConfigCurrencyPairFormat.Index != "BTC" && + b.ConfigCurrencyPairFormat.Uppercase { + t.Fatal("Test failed. TestSetCurrencyPairFormat ConfigCurrencyPairFormat values are incorrect") + } + + if b.RequestCurrencyPairFormat.Delimiter != "" && + b.RequestCurrencyPairFormat.Index != "BTC" && + b.RequestCurrencyPairFormat.Uppercase { + t.Fatal("Test failed. TestSetCurrencyPairFormat RequestCurrencyPairFormat values are incorrect") + } +} + +func TestGetAuthenticatedAPISupport(t *testing.T) { + base := Base{ + AuthenticatedAPISupport: false, + } + + if base.GetAuthenticatedAPISupport() { + t.Fatal("Test failed. TestGetAuthenticatedAPISupport returned true when it should of been false.") + } +} + func TestGetName(t *testing.T) { GetName := Base{ Name: "TESTNAME", @@ -18,51 +165,132 @@ func TestGetName(t *testing.T) { } } -func TestSetCurrencyPairFormat(t *testing.T) { - cfg := config.GetConfig() - err := cfg.LoadConfig(config.ConfigTestFile) - if err != nil { - t.Fatalf("Failed to load config file. Error: %s", err) - } - - exch, err := cfg.GetExchangeConfig("GDAX") - if err != nil { - t.Fatalf("Failed to load GDAX exchange config. Error: %s", err) - } - - exch.RequestCurrencyPairFormat = nil - exch.ConfigCurrencyPairFormat = nil - - err = cfg.UpdateExchangeConfig(exch) - if err != nil { - t.Fatalf("Failed to update GDAX config. Error: %s", err) - } - - // to-do -} - func TestGetEnabledCurrencies(t *testing.T) { - enabledPairs := []string{"BTCUSD", "BTCAUD", "LTCUSD", "LTCAUD"} - GetEnabledCurrencies := Base{ - Name: "TESTNAME", - EnabledPairs: enabledPairs, + b := Base{ + Name: "TESTNAME", } - enCurr := GetEnabledCurrencies.GetEnabledCurrencies() - if enCurr[0].Pair().String() != "BTCUSD" { - t.Error("Test Failed - Exchange GetEnabledCurrencies() incorrect string") + b.EnabledPairs = []string{"BTC-USD"} + format := config.CurrencyPairFormatConfig{ + Delimiter: "-", + Index: "", + } + + b.RequestCurrencyPairFormat = format + b.ConfigCurrencyPairFormat = format + c := b.GetEnabledCurrencies() + if c[0].Pair().String() != "BTC-USD" { + t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string") + } + + format.Delimiter = "~" + b.RequestCurrencyPairFormat = format + c = b.GetEnabledCurrencies() + if c[0].Pair().String() != "BTC-USD" { + t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string") + } + + format.Delimiter = "" + b.ConfigCurrencyPairFormat = format + c = b.GetEnabledCurrencies() + if c[0].Pair().String() != "BTC-USD" { + t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string") + } + + b.EnabledPairs = []string{"BTCDOGE"} + format.Index = "BTC" + b.ConfigCurrencyPairFormat = format + c = b.GetEnabledCurrencies() + if c[0].FirstCurrency.String() != "BTC" && c[0].SecondCurrency.String() != "DOGE" { + t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string") + } + + b.EnabledPairs = []string{"BTC_USD"} + b.RequestCurrencyPairFormat.Delimiter = "" + b.ConfigCurrencyPairFormat.Delimiter = "_" + c = b.GetEnabledCurrencies() + if c[0].FirstCurrency.String() != "BTC" && c[0].SecondCurrency.String() != "USD" { + t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string") + } + + b.EnabledPairs = []string{"BTCDOGE"} + b.RequestCurrencyPairFormat.Delimiter = "" + b.ConfigCurrencyPairFormat.Delimiter = "" + b.ConfigCurrencyPairFormat.Index = "BTC" + c = b.GetEnabledCurrencies() + if c[0].FirstCurrency.String() != "BTC" && c[0].SecondCurrency.String() != "DOGE" { + t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string") + } + + b.EnabledPairs = []string{"BTCUSD"} + b.ConfigCurrencyPairFormat.Index = "" + c = b.GetEnabledCurrencies() + if c[0].FirstCurrency.String() != "BTC" && c[0].SecondCurrency.String() != "USD" { + t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string") } } func TestGetAvailableCurrencies(t *testing.T) { - availablePairs := []string{"BTCUSD", "BTCAUD", "LTCUSD", "LTCAUD"} - GetEnabledCurrencies := Base{ - Name: "TESTNAME", - AvailablePairs: availablePairs, + b := Base{ + Name: "TESTNAME", } - enCurr := GetEnabledCurrencies.GetAvailableCurrencies() - if enCurr[0].Pair().String() != "BTCUSD" { + b.AvailablePairs = []string{"BTC-USD"} + format := config.CurrencyPairFormatConfig{ + Delimiter: "-", + Index: "", + } + + b.RequestCurrencyPairFormat = format + b.ConfigCurrencyPairFormat = format + c := b.GetAvailableCurrencies() + if c[0].Pair().String() != "BTC-USD" { + t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string") + } + + format.Delimiter = "~" + b.RequestCurrencyPairFormat = format + c = b.GetAvailableCurrencies() + if c[0].Pair().String() != "BTC-USD" { + t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string") + } + + format.Delimiter = "" + b.ConfigCurrencyPairFormat = format + c = b.GetAvailableCurrencies() + if c[0].Pair().String() != "BTC-USD" { + t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string") + } + + b.AvailablePairs = []string{"BTCDOGE"} + format.Index = "BTC" + b.ConfigCurrencyPairFormat = format + c = b.GetAvailableCurrencies() + if c[0].FirstCurrency.String() != "BTC" && c[0].SecondCurrency.String() != "DOGE" { + t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string") + } + + b.AvailablePairs = []string{"BTC_USD"} + b.RequestCurrencyPairFormat.Delimiter = "" + b.ConfigCurrencyPairFormat.Delimiter = "_" + c = b.GetAvailableCurrencies() + if c[0].FirstCurrency.String() != "BTC" && c[0].SecondCurrency.String() != "USD" { + t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string") + } + + b.AvailablePairs = []string{"BTCDOGE"} + b.RequestCurrencyPairFormat.Delimiter = "" + b.ConfigCurrencyPairFormat.Delimiter = "" + b.ConfigCurrencyPairFormat.Index = "BTC" + c = b.GetAvailableCurrencies() + if c[0].FirstCurrency.String() != "BTC" && c[0].SecondCurrency.String() != "DOGE" { + t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string") + } + + b.AvailablePairs = []string{"BTCUSD"} + b.ConfigCurrencyPairFormat.Index = "" + c = b.GetAvailableCurrencies() + if c[0].FirstCurrency.String() != "BTC" && c[0].SecondCurrency.String() != "USD" { t.Error("Test Failed - Exchange GetAvailableCurrencies() incorrect string") } } @@ -89,6 +317,14 @@ func TestGetExchangeFormatCurrencySeperator(t *testing.T) { t.Errorf("Test failed - TestGetExchangeFormatCurrencySeperator expected %v != actual %v", expected, actual) } + + expected = false + actual = GetExchangeFormatCurrencySeperator("blah") + + if expected != actual { + t.Errorf("Test failed - TestGetExchangeFormatCurrencySeperator expected %v != actual %v", + expected, actual) + } } func TestGetAndFormatExchangeCurrencies(t *testing.T) { @@ -112,6 +348,11 @@ func TestGetAndFormatExchangeCurrencies(t *testing.T) { t.Errorf("Test failed - Exchange TestGetAndFormatExchangeCurrencies %s != %s", actual, expected) } + + _, err = GetAndFormatExchangeCurrencies("non-existant", pairs) + if err == nil { + t.Errorf("Test failed - Exchange TestGetAndFormatExchangeCurrencies returned nil error on non-existant exchange") + } } func TestFormatExchangeCurrency(t *testing.T) { @@ -193,33 +434,77 @@ func TestSetAPIKeys(t *testing.T) { func TestUpdateEnabledCurrencies(t *testing.T) { cfg := config.GetConfig() err := cfg.LoadConfig(config.ConfigTestFile) - UAC := Base{Name: "ANX"} - enabledCurrencies := []string{"ltc", "btc", "usd", "aud"} - if err != nil { - t.Error( - "Test Failed - Exchange UpdateEnabledCurrencies() did not set correct values", - ) + t.Fatal("Test failed. TestUpdateEnabledCurrencies failed to load config") } - err2 := UAC.UpdateEnabledCurrencies(enabledCurrencies, false) - if err2 != nil { - t.Errorf("Test Failed - Exchange UpdateEnabledCurrencies() error: %s", err2) + + UAC := Base{Name: "ANX"} + exchangeProducts := []string{"ltc", "btc", "usd", "aud"} + + // Test updating exchange products for an exchange which doesn't exist + UAC.Name = "Blah" + err = UAC.UpdateEnabledCurrencies(exchangeProducts, false) + if err == nil { + t.Errorf("Test Failed - Exchange TestUpdateEnabledCurrencies succeeded on an exchange which doesn't exist") + } + + // Test updating exchange products + UAC.Name = "ANX" + err = UAC.UpdateEnabledCurrencies(exchangeProducts, false) + if err != nil { + t.Errorf("Test Failed - Exchange TestUpdateEnabledCurrencies error: %s", err) + } + + // Test updating the same new products, diff should be 0 + UAC.Name = "ANX" + err = UAC.UpdateEnabledCurrencies(exchangeProducts, false) + if err != nil { + t.Errorf("Test Failed - Exchange TestUpdateEnabledCurrencies error: %s", err) + } + + // Test force updating to only one product + exchangeProducts = []string{"btc"} + err = UAC.UpdateEnabledCurrencies(exchangeProducts, true) + if err != nil { + t.Errorf("Test Failed - Forced Exchange TestUpdateEnabledCurrencies error: %s", err) } } func TestUpdateAvailableCurrencies(t *testing.T) { cfg := config.GetConfig() err := cfg.LoadConfig(config.ConfigTestFile) + if err != nil { + t.Fatal("Test failed. TestUpdateAvailableCurrencies failed to load config") + } + UAC := Base{Name: "ANX"} exchangeProducts := []string{"ltc", "btc", "usd", "aud"} - if err != nil { - t.Error( - "Test Failed - Exchange UpdateAvailableCurrencies() did not set correct values", - ) + // Test updating exchange products for an exchange which doesn't exist + UAC.Name = "Blah" + err = UAC.UpdateAvailableCurrencies(exchangeProducts, false) + if err == nil { + t.Errorf("Test Failed - Exchange UpdateAvailableCurrencies() succeeded on an exchange which doesn't exist") } - err2 := UAC.UpdateAvailableCurrencies(exchangeProducts, false) - if err2 != nil { - t.Errorf("Test Failed - Exchange UpdateAvailableCurrencies() error: %s", err2) + + // Test updating exchange products + UAC.Name = "ANX" + err = UAC.UpdateAvailableCurrencies(exchangeProducts, false) + if err != nil { + t.Errorf("Test Failed - Exchange UpdateAvailableCurrencies() error: %s", err) + } + + // Test updating the same new products, diff should be 0 + UAC.Name = "ANX" + err = UAC.UpdateAvailableCurrencies(exchangeProducts, false) + if err != nil { + t.Errorf("Test Failed - Exchange UpdateAvailableCurrencies() error: %s", err) + } + + // Test force updating to only one product + exchangeProducts = []string{"btc"} + err = UAC.UpdateAvailableCurrencies(exchangeProducts, true) + if err != nil { + t.Errorf("Test Failed - Forced Exchange UpdateAvailableCurrencies() error: %s", err) } } diff --git a/exchanges/gdax/gdax_wrapper.go b/exchanges/gdax/gdax_wrapper.go index f6705edc..8ad7b404 100644 --- a/exchanges/gdax/gdax_wrapper.go +++ b/exchanges/gdax/gdax_wrapper.go @@ -97,17 +97,17 @@ func (g *GDAX) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Pri } // GetOrderbookEx returns orderbook base on the currency pair -func (g *GDAX) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - ob, err := orderbook.GetOrderbook(g.GetName(), p) +func (g *GDAX) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(g.GetName(), p, assetType) if err == nil { - return g.UpdateOrderbook(p) + return g.UpdateOrderbook(p, assetType) } return ob, nil } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (g *GDAX) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - var orderBook orderbook.OrderbookBase +func (g *GDAX) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base orderbookNew, err := g.GetOrderbook(exchange.FormatExchangeCurrency(g.Name, p).String(), 2) if err != nil { return orderBook, err @@ -116,13 +116,13 @@ func (g *GDAX) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, er obNew := orderbookNew.(GDAXOrderbookL1L2) for x := range obNew.Bids { - orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: obNew.Bids[x].Amount, Price: obNew.Bids[x].Price}) + orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: obNew.Bids[x].Amount, Price: obNew.Bids[x].Price}) } for x := range obNew.Asks { - orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: obNew.Bids[x].Amount, Price: obNew.Bids[x].Price}) + orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: obNew.Bids[x].Amount, Price: obNew.Bids[x].Price}) } - orderbook.ProcessOrderbook(g.GetName(), p, orderBook) - return orderbook.GetOrderbook(g.Name, p) + orderbook.ProcessOrderbook(g.GetName(), p, orderBook, assetType) + return orderbook.GetOrderbook(g.Name, p, assetType) } diff --git a/exchanges/gemini/gemini_wrapper.go b/exchanges/gemini/gemini_wrapper.go index 19ee46d9..143c8e88 100644 --- a/exchanges/gemini/gemini_wrapper.go +++ b/exchanges/gemini/gemini_wrapper.go @@ -78,30 +78,30 @@ func (g *Gemini) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.P } // GetOrderbookEx returns orderbook base on the currency pair -func (g *Gemini) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - ob, err := orderbook.GetOrderbook(g.GetName(), p) +func (g *Gemini) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(g.GetName(), p, assetType) if err == nil { - return g.UpdateOrderbook(p) + return g.UpdateOrderbook(p, assetType) } return ob, nil } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (g *Gemini) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - var orderBook orderbook.OrderbookBase +func (g *Gemini) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base orderbookNew, err := g.GetOrderbook(p.Pair().String(), url.Values{}) if err != nil { return orderBook, err } for x := range orderbookNew.Bids { - orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: orderbookNew.Bids[x].Amount, Price: orderbookNew.Bids[x].Price}) + orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: orderbookNew.Bids[x].Amount, Price: orderbookNew.Bids[x].Price}) } for x := range orderbookNew.Asks { - orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: orderbookNew.Asks[x].Amount, Price: orderbookNew.Asks[x].Price}) + orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: orderbookNew.Asks[x].Amount, Price: orderbookNew.Asks[x].Price}) } - orderbook.ProcessOrderbook(g.GetName(), p, orderBook) - return orderbook.GetOrderbook(g.Name, p) + orderbook.ProcessOrderbook(g.GetName(), p, orderBook, assetType) + return orderbook.GetOrderbook(g.Name, p, assetType) } diff --git a/exchanges/huobi/huobi_wrapper.go b/exchanges/huobi/huobi_wrapper.go index 10f7d5a4..31042c6d 100644 --- a/exchanges/huobi/huobi_wrapper.go +++ b/exchanges/huobi/huobi_wrapper.go @@ -56,17 +56,17 @@ func (h *HUOBI) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Pr } // GetOrderbookEx returns orderbook base on the currency pair -func (h *HUOBI) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - ob, err := orderbook.GetOrderbook(h.GetName(), p) +func (h *HUOBI) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(h.GetName(), p, assetType) if err == nil { - return h.UpdateOrderbook(p) + return h.UpdateOrderbook(p, assetType) } return ob, nil } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (h *HUOBI) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - var orderBook orderbook.OrderbookBase +func (h *HUOBI) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base orderbookNew, err := h.GetOrderBook(p.GetFirstCurrency().Lower().String()) if err != nil { return orderBook, err @@ -74,16 +74,16 @@ func (h *HUOBI) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, e for x := range orderbookNew.Bids { data := orderbookNew.Bids[x] - orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: data[1], Price: data[0]}) + orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data[1], Price: data[0]}) } for x := range orderbookNew.Asks { data := orderbookNew.Asks[x] - orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: data[1], Price: data[0]}) + orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data[1], Price: data[0]}) } - orderbook.ProcessOrderbook(h.GetName(), p, orderBook) - return orderbook.GetOrderbook(h.Name, p) + orderbook.ProcessOrderbook(h.GetName(), p, orderBook, assetType) + return orderbook.GetOrderbook(h.Name, p, assetType) } //GetExchangeAccountInfo retrieves balances for all enabled currencies for the diff --git a/exchanges/itbit/itbit_wrapper.go b/exchanges/itbit/itbit_wrapper.go index 871e923e..30335616 100644 --- a/exchanges/itbit/itbit_wrapper.go +++ b/exchanges/itbit/itbit_wrapper.go @@ -53,17 +53,17 @@ func (i *ItBit) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Pr } // GetOrderbookEx returns orderbook base on the currency pair -func (i *ItBit) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - ob, err := orderbook.GetOrderbook(i.GetName(), p) +func (i *ItBit) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(i.GetName(), p, assetType) if err == nil { - return i.UpdateOrderbook(p) + return i.UpdateOrderbook(p, assetType) } return ob, nil } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (i *ItBit) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - var orderBook orderbook.OrderbookBase +func (i *ItBit) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base orderbookNew, err := i.GetOrderbook(exchange.FormatExchangeCurrency(i.Name, p).String()) if err != nil { @@ -80,7 +80,7 @@ func (i *ItBit) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, e if err != nil { log.Println(err) } - orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: amount, Price: price}) + orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: amount, Price: price}) } for x := range orderbookNew.Asks { @@ -93,11 +93,11 @@ func (i *ItBit) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, e if err != nil { log.Println(err) } - orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: amount, Price: price}) + orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: amount, Price: price}) } - orderbook.ProcessOrderbook(i.GetName(), p, orderBook) - return orderbook.GetOrderbook(i.Name, p) + orderbook.ProcessOrderbook(i.GetName(), p, orderBook, assetType) + return orderbook.GetOrderbook(i.Name, p, assetType) } // GetExchangeAccountInfo retrieves balances for all enabled currencies for the diff --git a/exchanges/kraken/kraken_wrapper.go b/exchanges/kraken/kraken_wrapper.go index c880dafc..30410649 100644 --- a/exchanges/kraken/kraken_wrapper.go +++ b/exchanges/kraken/kraken_wrapper.go @@ -78,32 +78,32 @@ func (k *Kraken) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.P } // GetOrderbookEx returns orderbook base on the currency pair -func (k *Kraken) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - ob, err := orderbook.GetOrderbook(k.GetName(), p) +func (k *Kraken) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(k.GetName(), p, assetType) if err == nil { - return k.UpdateOrderbook(p) + return k.UpdateOrderbook(p, assetType) } return ob, nil } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (k *Kraken) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - var orderBook orderbook.OrderbookBase +func (k *Kraken) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base orderbookNew, err := k.GetDepth(exchange.FormatExchangeCurrency(k.GetName(), p).String()) if err != nil { return orderBook, err } for x := range orderbookNew.Bids { - orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: orderbookNew.Bids[x].Amount, Price: orderbookNew.Bids[x].Price}) + orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: orderbookNew.Bids[x].Amount, Price: orderbookNew.Bids[x].Price}) } for x := range orderbookNew.Asks { - orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: orderbookNew.Asks[x].Amount, Price: orderbookNew.Asks[x].Price}) + orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: orderbookNew.Asks[x].Amount, Price: orderbookNew.Asks[x].Price}) } - orderbook.ProcessOrderbook(k.GetName(), p, orderBook) - return orderbook.GetOrderbook(k.Name, p) + orderbook.ProcessOrderbook(k.GetName(), p, orderBook, assetType) + return orderbook.GetOrderbook(k.Name, p, assetType) } // GetExchangeAccountInfo retrieves balances for all enabled currencies for the diff --git a/exchanges/lakebtc/lakebtc_wrapper.go b/exchanges/lakebtc/lakebtc_wrapper.go index df01c9fa..4066e2dc 100644 --- a/exchanges/lakebtc/lakebtc_wrapper.go +++ b/exchanges/lakebtc/lakebtc_wrapper.go @@ -56,32 +56,32 @@ func (l *LakeBTC) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker. } // GetOrderbookEx returns orderbook base on the currency pair -func (l *LakeBTC) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - ob, err := orderbook.GetOrderbook(l.GetName(), p) +func (l *LakeBTC) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(l.GetName(), p, assetType) if err == nil { - return l.UpdateOrderbook(p) + return l.UpdateOrderbook(p, assetType) } return ob, nil } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (l *LakeBTC) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - var orderBook orderbook.OrderbookBase +func (l *LakeBTC) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base orderbookNew, err := l.GetOrderBook(p.Pair().String()) if err != nil { return orderBook, err } for x := range orderbookNew.Bids { - orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: orderbookNew.Bids[x].Amount, Price: orderbookNew.Bids[x].Price}) + orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: orderbookNew.Bids[x].Amount, Price: orderbookNew.Bids[x].Price}) } for x := range orderbookNew.Asks { - orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: orderbookNew.Asks[x].Amount, Price: orderbookNew.Asks[x].Price}) + orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: orderbookNew.Asks[x].Amount, Price: orderbookNew.Asks[x].Price}) } - orderbook.ProcessOrderbook(l.GetName(), p, orderBook) - return orderbook.GetOrderbook(l.Name, p) + orderbook.ProcessOrderbook(l.GetName(), p, orderBook, assetType) + return orderbook.GetOrderbook(l.Name, p, assetType) } // GetExchangeAccountInfo retrieves balances for all enabled currencies for the diff --git a/exchanges/liqui/liqui_wrapper.go b/exchanges/liqui/liqui_wrapper.go index ba934d99..8e22e8ba 100644 --- a/exchanges/liqui/liqui_wrapper.go +++ b/exchanges/liqui/liqui_wrapper.go @@ -75,17 +75,17 @@ func (l *Liqui) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Pr } // GetOrderbookEx returns orderbook base on the currency pair -func (l *Liqui) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - ob, err := orderbook.GetOrderbook(l.Name, p) +func (l *Liqui) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(l.Name, p, assetType) if err == nil { - return l.UpdateOrderbook(p) + return l.UpdateOrderbook(p, assetType) } return ob, nil } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (l *Liqui) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - var orderBook orderbook.OrderbookBase +func (l *Liqui) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base orderbookNew, err := l.GetDepth(exchange.FormatExchangeCurrency(l.Name, p).String()) if err != nil { return orderBook, err @@ -93,16 +93,16 @@ func (l *Liqui) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, e for x := range orderbookNew.Bids { data := orderbookNew.Bids[x] - orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: data[1], Price: data[0]}) + orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data[1], Price: data[0]}) } for x := range orderbookNew.Asks { data := orderbookNew.Asks[x] - orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: data[1], Price: data[0]}) + orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data[1], Price: data[0]}) } - orderbook.ProcessOrderbook(l.Name, p, orderBook) - return orderbook.GetOrderbook(l.Name, p) + orderbook.ProcessOrderbook(l.Name, p, orderBook, assetType) + return orderbook.GetOrderbook(l.Name, p, assetType) } // GetExchangeAccountInfo retrieves balances for all enabled currencies for the diff --git a/exchanges/localbitcoins/localbitcoins_wrapper.go b/exchanges/localbitcoins/localbitcoins_wrapper.go index 06685353..9cfc99e9 100644 --- a/exchanges/localbitcoins/localbitcoins_wrapper.go +++ b/exchanges/localbitcoins/localbitcoins_wrapper.go @@ -52,17 +52,17 @@ func (l *LocalBitcoins) GetTickerPrice(p pair.CurrencyPair, assetType string) (t } // GetOrderbookEx returns orderbook base on the currency pair -func (l *LocalBitcoins) GetOrderbookEx(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - ob, err := orderbook.GetOrderbook(l.GetName(), p) +func (l *LocalBitcoins) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(l.GetName(), p, assetType) if err == nil { - return l.UpdateOrderbook(p) + return l.UpdateOrderbook(p, assetType) } return ob, nil } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (l *LocalBitcoins) UpdateOrderbook(p pair.CurrencyPair) (orderbook.OrderbookBase, error) { - var orderBook orderbook.OrderbookBase +func (l *LocalBitcoins) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base orderbookNew, err := l.GetOrderbook(p.GetSecondCurrency().String()) if err != nil { return orderBook, err @@ -70,16 +70,16 @@ func (l *LocalBitcoins) UpdateOrderbook(p pair.CurrencyPair) (orderbook.Orderboo for x := range orderbookNew.Bids { data := orderbookNew.Bids[x] - orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: data.Amount, Price: data.Price}) + orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data.Amount, Price: data.Price}) } for x := range orderbookNew.Asks { data := orderbookNew.Asks[x] - orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: data.Amount, Price: data.Price}) + orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data.Amount, Price: data.Price}) } - orderbook.ProcessOrderbook(l.GetName(), p, orderBook) - return orderbook.GetOrderbook(l.Name, p) + orderbook.ProcessOrderbook(l.GetName(), p, orderBook, assetType) + return orderbook.GetOrderbook(l.Name, p, assetType) } // GetExchangeAccountInfo retrieves balances for all enabled currencies for the diff --git a/exchanges/okcoin/okcoin_wrapper.go b/exchanges/okcoin/okcoin_wrapper.go index a1a4807f..9b155bfc 100644 --- a/exchanges/okcoin/okcoin_wrapper.go +++ b/exchanges/okcoin/okcoin_wrapper.go @@ -74,17 +74,17 @@ func (o *OKCoin) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.P } // GetOrderbookEx returns orderbook base on the currency pair -func (o *OKCoin) GetOrderbookEx(currency pair.CurrencyPair) (orderbook.OrderbookBase, error) { - ob, err := orderbook.GetOrderbook(o.GetName(), currency) +func (o *OKCoin) GetOrderbookEx(currency pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(o.GetName(), currency, assetType) if err == nil { - return o.UpdateOrderbook(currency) + return o.UpdateOrderbook(currency, assetType) } return ob, nil } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (o *OKCoin) UpdateOrderbook(currency pair.CurrencyPair) (orderbook.OrderbookBase, error) { - var orderBook orderbook.OrderbookBase +func (o *OKCoin) UpdateOrderbook(currency pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base orderbookNew, err := o.GetOrderBook(exchange.FormatExchangeCurrency(o.Name, currency).String(), 200, false) if err != nil { return orderBook, err @@ -92,16 +92,16 @@ func (o *OKCoin) UpdateOrderbook(currency pair.CurrencyPair) (orderbook.Orderboo for x := range orderbookNew.Bids { data := orderbookNew.Bids[x] - orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: data[1], Price: data[0]}) + orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data[1], Price: data[0]}) } for x := range orderbookNew.Asks { data := orderbookNew.Asks[x] - orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: data[1], Price: data[0]}) + orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data[1], Price: data[0]}) } - orderbook.ProcessOrderbook(o.GetName(), currency, orderBook) - return orderbook.GetOrderbook(o.Name, currency) + orderbook.ProcessOrderbook(o.GetName(), currency, orderBook, assetType) + return orderbook.GetOrderbook(o.Name, currency, assetType) } // GetExchangeAccountInfo retrieves balances for all enabled currencies for the diff --git a/exchanges/orderbook/orderbook.go b/exchanges/orderbook/orderbook.go index 50528dac..0236bc87 100644 --- a/exchanges/orderbook/orderbook.go +++ b/exchanges/orderbook/orderbook.go @@ -7,33 +7,44 @@ import ( "github.com/thrasher-/gocryptotrader/currency/pair" ) -var ( +// Const values for orderbook package +const ( ErrOrderbookForExchangeNotFound = "Ticker for exchange does not exist." ErrPrimaryCurrencyNotFound = "Error primary currency for orderbook not found." ErrSecondaryCurrencyNotFound = "Error secondary currency for orderbook not found." + Spot = "SPOT" +) + +// Vars for the orderbook package +var ( Orderbooks []Orderbook ) -type OrderbookItem struct { +// Item stores the amount and price values +type Item struct { Amount float64 Price float64 } -type OrderbookBase struct { +// Base holds the fields for the orderbook base +type Base struct { Pair pair.CurrencyPair `json:"pair"` CurrencyPair string `json:"CurrencyPair"` - Bids []OrderbookItem `json:"bids"` - Asks []OrderbookItem `json:"asks"` + Bids []Item `json:"bids"` + Asks []Item `json:"asks"` LastUpdated time.Time `json:"last_updated"` } +// Orderbook holds the orderbook information for a currency pair and type type Orderbook struct { - Orderbook map[pair.CurrencyItem]map[pair.CurrencyItem]OrderbookBase + Orderbook map[pair.CurrencyItem]map[pair.CurrencyItem]map[string]Base ExchangeName string } -func (o *OrderbookBase) CalculateTotalBids() (float64, float64) { +// CalculateTotalBids returns the total amount of bids and the total orderbook +// bids value +func (o *Base) CalculateTotalBids() (float64, float64) { amountCollated := float64(0) total := float64(0) for _, x := range o.Bids { @@ -43,7 +54,9 @@ func (o *OrderbookBase) CalculateTotalBids() (float64, float64) { return amountCollated, total } -func (o *OrderbookBase) CalculateTotalAsks() (float64, float64) { +// CalculateTotalAsks returns the total amount of asks and the total orderbook +// asks value +func (o *Base) CalculateTotalAsks() (float64, float64) { amountCollated := float64(0) total := float64(0) for _, x := range o.Asks { @@ -53,29 +66,33 @@ func (o *OrderbookBase) CalculateTotalAsks() (float64, float64) { return amountCollated, total } -func (o *OrderbookBase) Update(Bids, Asks []OrderbookItem) { +// Update updates the bids and asks +func (o *Base) Update(Bids, Asks []Item) { o.Bids = Bids o.Asks = Asks o.LastUpdated = time.Now() } -func GetOrderbook(exchange string, p pair.CurrencyPair) (OrderbookBase, error) { +// GetOrderbook checks and returns the orderbook given an exchange name and +// currency pair if it exists +func GetOrderbook(exchange string, p pair.CurrencyPair, orderbookType string) (Base, error) { orderbook, err := GetOrderbookByExchange(exchange) if err != nil { - return OrderbookBase{}, err + return Base{}, err } if !FirstCurrencyExists(exchange, p.GetFirstCurrency()) { - return OrderbookBase{}, errors.New(ErrPrimaryCurrencyNotFound) + return Base{}, errors.New(ErrPrimaryCurrencyNotFound) } if !SecondCurrencyExists(exchange, p) { - return OrderbookBase{}, errors.New(ErrSecondaryCurrencyNotFound) + return Base{}, errors.New(ErrSecondaryCurrencyNotFound) } - return orderbook.Orderbook[p.GetFirstCurrency()][p.GetSecondCurrency()], nil + return orderbook.Orderbook[p.GetFirstCurrency()][p.GetSecondCurrency()][orderbookType], nil } +// GetOrderbookByExchange returns an exchange orderbook func GetOrderbookByExchange(exchange string) (*Orderbook, error) { for _, y := range Orderbooks { if y.ExchangeName == exchange { @@ -85,6 +102,8 @@ func GetOrderbookByExchange(exchange string) (*Orderbook, error) { return nil, errors.New(ErrOrderbookForExchangeNotFound) } +// FirstCurrencyExists checks to see if the first currency of the orderbook map +// exists func FirstCurrencyExists(exchange string, currency pair.CurrencyItem) bool { for _, y := range Orderbooks { if y.ExchangeName == exchange { @@ -96,6 +115,8 @@ func FirstCurrencyExists(exchange string, currency pair.CurrencyItem) bool { return false } +// SecondCurrencyExists checks to see if the second currency of the orderbook +// map exists func SecondCurrencyExists(exchange string, p pair.CurrencyPair) bool { for _, y := range Orderbooks { if y.ExchangeName == exchange { @@ -109,40 +130,51 @@ func SecondCurrencyExists(exchange string, p pair.CurrencyPair) bool { return false } -func CreateNewOrderbook(exchangeName string, p pair.CurrencyPair, orderbookNew OrderbookBase) Orderbook { +// CreateNewOrderbook creates a new orderbook +func CreateNewOrderbook(exchangeName string, p pair.CurrencyPair, orderbookNew Base, orderbookType string) Orderbook { orderbook := Orderbook{} orderbook.ExchangeName = exchangeName - orderbook.Orderbook = make(map[pair.CurrencyItem]map[pair.CurrencyItem]OrderbookBase) - sMap := make(map[pair.CurrencyItem]OrderbookBase) - sMap[p.GetSecondCurrency()] = orderbookNew - orderbook.Orderbook[p.GetFirstCurrency()] = sMap + orderbook.Orderbook = make(map[pair.CurrencyItem]map[pair.CurrencyItem]map[string]Base) + a := make(map[pair.CurrencyItem]map[string]Base) + b := make(map[string]Base) + b[orderbookType] = orderbookNew + a[p.SecondCurrency] = b + orderbook.Orderbook[p.FirstCurrency] = a Orderbooks = append(Orderbooks, orderbook) return orderbook } -func ProcessOrderbook(exchangeName string, p pair.CurrencyPair, orderbookNew OrderbookBase) { +// ProcessOrderbook processes incoming orderbooks, creating or updating the +// Orderbook list +func ProcessOrderbook(exchangeName string, p pair.CurrencyPair, orderbookNew Base, orderbookType string) { orderbookNew.CurrencyPair = p.Pair().String() orderbookNew.LastUpdated = time.Now() + if len(Orderbooks) == 0 { - CreateNewOrderbook(exchangeName, p, orderbookNew) + CreateNewOrderbook(exchangeName, p, orderbookNew, orderbookType) return - } else { - orderbook, err := GetOrderbookByExchange(exchangeName) - if err != nil { - CreateNewOrderbook(exchangeName, p, orderbookNew) + } + + orderbook, err := GetOrderbookByExchange(exchangeName) + if err != nil { + CreateNewOrderbook(exchangeName, p, orderbookNew, orderbookType) + return + } + + if FirstCurrencyExists(exchangeName, p.GetFirstCurrency()) { + if !SecondCurrencyExists(exchangeName, p) { + a := orderbook.Orderbook[p.FirstCurrency] + b := make(map[string]Base) + b[orderbookType] = orderbookNew + a[p.SecondCurrency] = b + orderbook.Orderbook[p.FirstCurrency] = a return } - - if FirstCurrencyExists(exchangeName, p.GetFirstCurrency()) { - if !SecondCurrencyExists(exchangeName, p) { - second := orderbook.Orderbook[p.GetFirstCurrency()] - second[p.GetSecondCurrency()] = orderbookNew - orderbook.Orderbook[p.GetFirstCurrency()] = second - return - } - } - sMap := make(map[pair.CurrencyItem]OrderbookBase) - sMap[p.GetSecondCurrency()] = orderbookNew - orderbook.Orderbook[p.GetFirstCurrency()] = sMap } + + a := make(map[pair.CurrencyItem]map[string]Base) + b := make(map[string]Base) + b[orderbookType] = orderbookNew + a[p.SecondCurrency] = b + orderbook.Orderbook[p.FirstCurrency] = a } diff --git a/exchanges/orderbook/orderbook_test.go b/exchanges/orderbook/orderbook_test.go new file mode 100644 index 00000000..d03e4c26 --- /dev/null +++ b/exchanges/orderbook/orderbook_test.go @@ -0,0 +1,266 @@ +package orderbook + +import ( + "testing" + "time" + + "github.com/thrasher-/gocryptotrader/currency/pair" +) + +func TestCalculateTotalBids(t *testing.T) { + t.Parallel() + currency := pair.NewCurrencyPair("BTC", "USD") + base := Base{ + Pair: currency, + CurrencyPair: currency.Pair().String(), + Bids: []Item{Item{Price: 100, Amount: 10}}, + LastUpdated: time.Now(), + } + + a, b := base.CalculateTotalBids() + if a != 10 && b != 1000 { + t.Fatal("Test failed. TestCalculateTotalBids expected a = 10 and b = 1000") + } +} + +func TestCalculateTotaAsks(t *testing.T) { + t.Parallel() + currency := pair.NewCurrencyPair("BTC", "USD") + base := Base{ + Pair: currency, + CurrencyPair: currency.Pair().String(), + Asks: []Item{Item{Price: 100, Amount: 10}}, + LastUpdated: time.Now(), + } + + a, b := base.CalculateTotalAsks() + if a != 10 && b != 1000 { + t.Fatal("Test failed. TestCalculateTotalAsks expected a = 10 and b = 1000") + } +} + +func TestUpdate(t *testing.T) { + t.Parallel() + currency := pair.NewCurrencyPair("BTC", "USD") + timeNow := time.Now() + base := Base{ + Pair: currency, + CurrencyPair: currency.Pair().String(), + Asks: []Item{Item{Price: 100, Amount: 10}}, + Bids: []Item{Item{Price: 200, Amount: 10}}, + LastUpdated: timeNow, + } + + asks := []Item{Item{Price: 200, Amount: 101}} + bids := []Item{Item{Price: 201, Amount: 100}} + time.Sleep(time.Millisecond * 50) + base.Update(bids, asks) + + if !base.LastUpdated.After(timeNow) { + t.Fatal("test failed. TestUpdate expected LastUpdated to be greater then original time") + } + + a, b := base.CalculateTotalAsks() + if a != 100 && b != 20200 { + t.Fatal("Test failed. TestUpdate expected a = 100 and b = 20100") + } + + a, b = base.CalculateTotalBids() + if a != 100 && b != 20100 { + t.Fatal("Test failed. TestUpdate expected a = 100 and b = 20100") + } +} + +func TestGetOrderbook(t *testing.T) { + currency := pair.NewCurrencyPair("BTC", "USD") + base := Base{ + Pair: currency, + CurrencyPair: currency.Pair().String(), + Asks: []Item{Item{Price: 100, Amount: 10}}, + Bids: []Item{Item{Price: 200, Amount: 10}}, + } + + CreateNewOrderbook("Exchange", currency, base, Spot) + + result, err := GetOrderbook("Exchange", currency, Spot) + if err != nil { + t.Fatalf("Test failed. TestGetOrderbook failed to get orderbook. Error %s", + err) + } + + if result.Pair.Pair() != currency.Pair() { + t.Fatal("Test failed. TestGetOrderbook failed. Mismatched pairs") + } + + _, err = GetOrderbook("nonexistant", currency, Spot) + if err == nil { + t.Fatal("Test failed. TestGetOrderbook retrieved non-existant orderbook") + } + + currency.FirstCurrency = "blah" + _, err = GetOrderbook("Exchange", currency, Spot) + if err == nil { + t.Fatal("Test failed. TestGetOrderbook retrieved non-existant orderbook using invalid first currency") + } + + newCurrency := pair.NewCurrencyPair("BTC", "AUD") + _, err = GetOrderbook("Exchange", newCurrency, Spot) + if err == nil { + t.Fatal("Test failed. TestGetOrderbook retrieved non-existant orderbook using invalid second currency") + } +} + +func TestGetOrderbookByExchange(t *testing.T) { + currency := pair.NewCurrencyPair("BTC", "USD") + base := Base{ + Pair: currency, + CurrencyPair: currency.Pair().String(), + Asks: []Item{Item{Price: 100, Amount: 10}}, + Bids: []Item{Item{Price: 200, Amount: 10}}, + } + + CreateNewOrderbook("Exchange", currency, base, Spot) + + _, err := GetOrderbookByExchange("Exchange") + if err != nil { + t.Fatalf("Test failed. TestGetOrderbookByExchange failed to get orderbook. Error %s", + err) + } + + _, err = GetOrderbookByExchange("nonexistant") + if err == nil { + t.Fatal("Test failed. TestGetOrderbookByExchange retrieved non-existant orderbook") + } +} + +func TestFirstCurrencyExists(t *testing.T) { + currency := pair.NewCurrencyPair("BTC", "AUD") + base := Base{ + Pair: currency, + CurrencyPair: currency.Pair().String(), + Asks: []Item{Item{Price: 100, Amount: 10}}, + Bids: []Item{Item{Price: 200, Amount: 10}}, + } + + CreateNewOrderbook("Exchange", currency, base, Spot) + + if !FirstCurrencyExists("Exchange", currency.FirstCurrency) { + t.Fatal("Test failed. TestFirstCurrencyExists expected first currency doesn't exist") + } + + var item pair.CurrencyItem = "blah" + if FirstCurrencyExists("Exchange", item) { + t.Fatal("Test failed. TestFirstCurrencyExists unexpected first currency exists") + } +} + +func TestSecondCurrencyExists(t *testing.T) { + currency := pair.NewCurrencyPair("BTC", "USD") + base := Base{ + Pair: currency, + CurrencyPair: currency.Pair().String(), + Asks: []Item{Item{Price: 100, Amount: 10}}, + Bids: []Item{Item{Price: 200, Amount: 10}}, + } + + CreateNewOrderbook("Exchange", currency, base, Spot) + + if !SecondCurrencyExists("Exchange", currency) { + t.Fatal("Test failed. TestSecondCurrencyExists expected first currency doesn't exist") + } + + currency.SecondCurrency = "blah" + if SecondCurrencyExists("Exchange", currency) { + t.Fatal("Test failed. TestSecondCurrencyExists unexpected first currency exists") + } +} + +func TestCreateNewOrderbook(t *testing.T) { + currency := pair.NewCurrencyPair("BTC", "USD") + base := Base{ + Pair: currency, + CurrencyPair: currency.Pair().String(), + Asks: []Item{Item{Price: 100, Amount: 10}}, + Bids: []Item{Item{Price: 200, Amount: 10}}, + } + + CreateNewOrderbook("Exchange", currency, base, Spot) + + result, err := GetOrderbook("Exchange", currency, Spot) + if err != nil { + t.Fatal("Test failed. TestCreateNewOrderbook failed to create new orderbook") + } + + if result.Pair.Pair() != currency.Pair() { + t.Fatal("Test failed. TestCreateNewOrderbook result pair is incorrect") + } + + a, b := result.CalculateTotalAsks() + if a != 10 && b != 1000 { + t.Fatal("Test failed. TestCreateNewOrderbook CalculateTotalAsks value is incorrect") + } + + a, b = result.CalculateTotalBids() + if a != 10 && b != 2000 { + t.Fatal("Test failed. TestCreateNewOrderbook CalculateTotalBids value is incorrect") + } +} + +func TestProcessOrderbook(t *testing.T) { + Orderbooks = []Orderbook{} + currency := pair.NewCurrencyPair("BTC", "USD") + base := Base{ + Pair: currency, + CurrencyPair: currency.Pair().String(), + Asks: []Item{Item{Price: 100, Amount: 10}}, + Bids: []Item{Item{Price: 200, Amount: 10}}, + } + + ProcessOrderbook("Exchange", currency, base, Spot) + + result, err := GetOrderbook("Exchange", currency, Spot) + if err != nil { + t.Fatal("Test failed. TestProcessOrderbook failed to create new orderbook") + } + + if result.Pair.Pair() != currency.Pair() { + t.Fatal("Test failed. TestProcessOrderbook result pair is incorrect") + } + + currency = pair.NewCurrencyPair("BTC", "GBP") + base.Pair = currency + ProcessOrderbook("Exchange", currency, base, Spot) + + result, err = GetOrderbook("Exchange", currency, Spot) + if err != nil { + t.Fatal("Test failed. TestProcessOrderbook failed to retrieve new orderbook") + } + + if result.Pair.Pair() != currency.Pair() { + t.Fatal("Test failed. TestProcessOrderbook result pair is incorrect") + } + + base.Asks = []Item{Item{Price: 200, Amount: 200}} + ProcessOrderbook("Exchange", currency, base, "monthly") + + result, err = GetOrderbook("Exchange", currency, "monthly") + if err != nil { + t.Fatal("Test failed. TestProcessOrderbook failed to retrieve new orderbook") + } + + a, b := result.CalculateTotalAsks() + if a != 200 && b != 40000 { + t.Fatal("Test failed. TestProcessOrderbook CalculateTotalsAsks incorrect values") + } + + base.Bids = []Item{Item{Price: 420, Amount: 200}} + ProcessOrderbook("Blah", currency, base, "quarterly") + result, err = GetOrderbook("Blah", currency, "quarterly") + if err != nil { + t.Fatal("Test failed. TestProcessOrderbook failed to create new orderbook") + } + + if a != 200 && b != 84000 { + t.Fatal("Test failed. TestProcessOrderbook CalculateTotalsBids incorrect values") + } +} diff --git a/exchanges/poloniex/poloniex_wrapper.go b/exchanges/poloniex/poloniex_wrapper.go index 10896e50..78d7a480 100644 --- a/exchanges/poloniex/poloniex_wrapper.go +++ b/exchanges/poloniex/poloniex_wrapper.go @@ -61,17 +61,17 @@ func (p *Poloniex) GetTickerPrice(currencyPair pair.CurrencyPair, assetType stri } // GetOrderbookEx returns orderbook base on the currency pair -func (p *Poloniex) GetOrderbookEx(currencyPair pair.CurrencyPair) (orderbook.OrderbookBase, error) { - ob, err := orderbook.GetOrderbook(p.GetName(), currencyPair) +func (p *Poloniex) GetOrderbookEx(currencyPair pair.CurrencyPair, assetType string) (orderbook.Base, error) { + ob, err := orderbook.GetOrderbook(p.GetName(), currencyPair, assetType) if err == nil { - return p.UpdateOrderbook(currencyPair) + return p.UpdateOrderbook(currencyPair, assetType) } return ob, nil } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (p *Poloniex) UpdateOrderbook(currencyPair pair.CurrencyPair) (orderbook.OrderbookBase, error) { - var orderBook orderbook.OrderbookBase +func (p *Poloniex) UpdateOrderbook(currencyPair pair.CurrencyPair, assetType string) (orderbook.Base, error) { + var orderBook orderbook.Base orderbookNew, err := p.GetOrderbook(exchange.FormatExchangeCurrency(p.GetName(), currencyPair).String(), 1000) if err != nil { return orderBook, err @@ -79,16 +79,16 @@ func (p *Poloniex) UpdateOrderbook(currencyPair pair.CurrencyPair) (orderbook.Or for x := range orderbookNew.Bids { data := orderbookNew.Bids[x] - orderBook.Bids = append(orderBook.Bids, orderbook.OrderbookItem{Amount: data.Amount, Price: data.Price}) + orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: data.Amount, Price: data.Price}) } for x := range orderbookNew.Asks { data := orderbookNew.Asks[x] - orderBook.Asks = append(orderBook.Asks, orderbook.OrderbookItem{Amount: data.Amount, Price: data.Price}) + orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: data.Amount, Price: data.Price}) } - orderbook.ProcessOrderbook(p.GetName(), currencyPair, orderBook) - return orderbook.GetOrderbook(p.Name, currencyPair) + orderbook.ProcessOrderbook(p.GetName(), currencyPair, orderBook, assetType) + return orderbook.GetOrderbook(p.Name, currencyPair, assetType) } // GetExchangeAccountInfo retrieves balances for all enabled currencies for the diff --git a/exchanges/ticker/ticker_test.go b/exchanges/ticker/ticker_test.go index eb7c8b91..7f5d29ed 100644 --- a/exchanges/ticker/ticker_test.go +++ b/exchanges/ticker/ticker_test.go @@ -72,6 +72,23 @@ func TestGetTicker(t *testing.T) { t.Error("Test Failed - ticker tickerPrice.CurrencyPair value is incorrect") } + _, err = GetTicker("blah", newPair, Spot) + if err == nil { + t.Fatal("Test Failed. TestGetTicker returned nil error on invalid exchange") + } + + newPair.FirstCurrency = "ETH" + _, err = GetTicker("bitfinex", newPair, Spot) + if err == nil { + t.Fatal("Test Failed. TestGetTicker returned ticker for invalid first currency") + } + + btcltcPair := pair.NewCurrencyPair("BTC", "LTC") + _, err = GetTicker("bitfinex", btcltcPair, Spot) + if err == nil { + t.Fatal("Test Failed. TestGetTicker returned ticker for invalid second currency") + } + priceStruct.PriceATH = 9001 ProcessTicker("bitfinex", newPair, priceStruct, "futures_3m") tickerPrice, err = GetTicker("bitfinex", newPair, "futures_3m") @@ -220,6 +237,7 @@ func TestCreateNewTicker(t *testing.T) { } func TestProcessTicker(t *testing.T) { //non-appending function to tickers + Tickers = []Ticker{} newPair := pair.NewCurrencyPair("BTC", "USD") priceStruct := Price{ Pair: newPair, @@ -234,4 +252,27 @@ func TestProcessTicker(t *testing.T) { //non-appending function to tickers } ProcessTicker("btcc", newPair, priceStruct, Spot) + + result, err := GetTicker("btcc", newPair, Spot) + if err != nil { + t.Fatal("Test failed. TestProcessTicker failed to create and return a new ticker") + } + + if result.Pair.Pair() != newPair.Pair() { + t.Fatal("Test failed. TestProcessTicker pair mismatch") + } + + secondPair := pair.NewCurrencyPair("BTC", "AUD") + priceStruct.Pair = secondPair + ProcessTicker("btcc", secondPair, priceStruct, Spot) + + result, err = GetTicker("btcc", secondPair, Spot) + if err != nil { + t.Fatal("Test failed. TestProcessTicker failed to create and return a new ticker") + } + + result, err = GetTicker("btcc", newPair, Spot) + if err != nil { + t.Fatal("Test failed. TestProcessTicker failed to return an existing ticker") + } } diff --git a/main.go b/main.go index 97b9dbfd..08a6b9d8 100644 --- a/main.go +++ b/main.go @@ -191,7 +191,7 @@ func main() { go WebsocketHandler() go TickerUpdaterRoutine() - //go OrderbookUpdaterRoutine() + go OrderbookUpdaterRoutine() if bot.config.Webserver.Enabled { err := bot.config.CheckWebserverConfigValues() diff --git a/orderbook_routes.go b/orderbook_routes.go new file mode 100644 index 00000000..6a708739 --- /dev/null +++ b/orderbook_routes.go @@ -0,0 +1,141 @@ +package main + +import ( + "encoding/json" + "log" + "net/http" + + "github.com/gorilla/mux" + "github.com/thrasher-/gocryptotrader/currency/pair" + exchange "github.com/thrasher-/gocryptotrader/exchanges" + "github.com/thrasher-/gocryptotrader/exchanges/orderbook" +) + +// GetSpecificOrderbook returns a specific orderbook given the currency, +// exchangeName and assetType +func GetSpecificOrderbook(currency, exchangeName, assetType string) (orderbook.Base, error) { + var specificOrderbook orderbook.Base + var err error + for i := 0; i < len(bot.exchanges); i++ { + if bot.exchanges[i] != nil { + if bot.exchanges[i].IsEnabled() && bot.exchanges[i].GetName() == exchangeName { + specificOrderbook, err = bot.exchanges[i].GetOrderbookEx( + pair.NewCurrencyPairFromString(currency), + assetType, + ) + break + } + } + } + return specificOrderbook, err +} + +func jsonOrderbookResponse(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + currency := vars["currency"] + exchange := vars["exchangeName"] + assetType := vars["assetType"] + + if assetType == "" { + assetType = orderbook.Spot + } + + response, err := GetSpecificOrderbook(currency, exchange, assetType) + if err != nil { + log.Printf("Failed to fetch orderbook for %s currency: %s\n", exchange, + currency) + return + } + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + w.WriteHeader(http.StatusOK) + if err := json.NewEncoder(w).Encode(response); err != nil { + panic(err) + } +} + +// AllEnabledExchangeOrderbooks holds the enabled exchange orderbooks +type AllEnabledExchangeOrderbooks struct { + Data []EnabledExchangeOrderbooks `json:"data"` +} + +// EnabledExchangeOrderbooks is a sub type for singular exchanges and respective +// orderbooks +type EnabledExchangeOrderbooks struct { + ExchangeName string `json:"exchangeName"` + ExchangeValues []orderbook.Base `json:"exchangeValues"` +} + +// GetAllActiveOrderbooks returns all enabled exchanges orderbooks +func GetAllActiveOrderbooks() []EnabledExchangeOrderbooks { + var orderbookData []EnabledExchangeOrderbooks + + for _, individualBot := range bot.exchanges { + if individualBot != nil && individualBot.IsEnabled() { + var individualExchange EnabledExchangeOrderbooks + exchangeName := individualBot.GetName() + individualExchange.ExchangeName = exchangeName + currencies := individualBot.GetEnabledCurrencies() + assetTypes, err := exchange.GetExchangeAssetTypes(exchangeName) + if err != nil { + log.Printf("failed to get %s exchange asset types. Error: %s", + exchangeName, err) + continue + } + for _, x := range currencies { + currency := x + + var ob orderbook.Base + if len(assetTypes) > 1 { + for y := range assetTypes { + ob, err = individualBot.UpdateOrderbook(currency, + assetTypes[y]) + } + } else { + ob, err = individualBot.UpdateOrderbook(currency, + assetTypes[0]) + } + + if err != nil { + log.Printf("failed to get %s %s orderbook. Error: %s", + currency.Pair().String(), + exchangeName, + err) + continue + } + + individualExchange.ExchangeValues = append( + individualExchange.ExchangeValues, ob, + ) + } + orderbookData = append(orderbookData, individualExchange) + } + } + return orderbookData +} + +func getAllActiveOrderbooksResponse(w http.ResponseWriter, r *http.Request) { + var response AllEnabledExchangeOrderbooks + response.Data = GetAllActiveOrderbooks() + + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + w.WriteHeader(http.StatusOK) + if err := json.NewEncoder(w).Encode(response); err != nil { + panic(err) + } +} + +// OrderbookRoutes denotes the current exchange orderbook routes +var OrderbookRoutes = Routes{ + Route{ + "AllActiveExchangesAndOrderbooks", + "GET", + "/exchanges/orderbook/latest/all", + getAllActiveOrderbooksResponse, + }, + Route{ + "IndividualExchangeOrderbook", + "GET", + "/exchanges/{exchangeName}/orderbook/latest/{currency}", + jsonOrderbookResponse, + }, +} diff --git a/restful_router.go b/restful_router.go index 467dd05f..f151bb3d 100644 --- a/restful_router.go +++ b/restful_router.go @@ -18,6 +18,7 @@ func NewRouter(exchanges []exchange.IBotExchange) *mux.Router { allRoutes = append(allRoutes, WalletRoutes...) allRoutes = append(allRoutes, IndexRoute...) allRoutes = append(allRoutes, WebsocketRoutes...) + allRoutes = append(allRoutes, OrderbookRoutes...) for _, route := range allRoutes { var handler http.Handler handler = route.HandlerFunc diff --git a/routines.go b/routines.go index 0da929fe..3a20b24b 100644 --- a/routines.go +++ b/routines.go @@ -5,6 +5,8 @@ import ( "log" "time" + "github.com/thrasher-/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-/gocryptotrader/currency/pair" exchange "github.com/thrasher-/gocryptotrader/exchanges" "github.com/thrasher-/gocryptotrader/exchanges/ticker" @@ -31,6 +33,31 @@ func printSummary(result ticker.Price, p pair.CurrencyPair, assetType, exchangeN result.Volume) } +func printOrderbookSummary(result orderbook.Base, p pair.CurrencyPair, assetType, exchangeName string, err error) { + if err != nil { + log.Printf("failed to get %s %s orderbook. Error: %s", + p.Pair().String(), + exchangeName, + err) + return + } + + bidsAmount, bidsValue := result.CalculateTotalBids() + asksAmount, asksValue := result.CalculateTotalAsks() + + log.Printf("%s %s %s: Orderbook Bids len: %d amount: %f total value: %f Asks len: %d amount: %f total value: %f", + exchangeName, + exchange.FormatCurrency(p).String(), + assetType, + len(result.Bids), + bidsAmount, + bidsValue, + len(result.Asks), + asksAmount, + asksValue, + ) +} + func relayWebsocketEvent(result interface{}, event, assetType, exchangeName string) { evt := WebsocketEvent{ Data: result, @@ -57,13 +84,15 @@ func TickerUpdaterRoutine() { var err error var assetTypes []string + assetTypes, err = exchange.GetExchangeAssetTypes(exchangeName) + if err != nil { + log.Printf("failed to get %s exchange asset types. Error: %s", + exchangeName, err) + } + for y := range enabledCurrencies { currency := enabledCurrencies[y] - assetTypes, err = exchange.GetExchangeAssetTypes(exchangeName) - if err != nil { - log.Printf("failed to get %s exchange asset types. Error: %s", - exchangeName, err) - } + if len(assetTypes) > 1 { for z := range assetTypes { result, err = bot.exchanges[x].UpdateTicker(currency, @@ -93,33 +122,42 @@ func OrderbookUpdaterRoutine() { for { for x := range bot.exchanges { if bot.exchanges[x].IsEnabled() { - exchangeName := bot.exchanges[x].GetName() - - if exchangeName == "ANX" { + if bot.exchanges[x].GetName() == "ANX" { continue } + exchangeName := bot.exchanges[x].GetName() enabledCurrencies := bot.exchanges[x].GetEnabledCurrencies() + var result orderbook.Base + var err error + var assetTypes []string + + assetTypes, err = exchange.GetExchangeAssetTypes(exchangeName) + if err != nil { + log.Printf("failed to get %s exchange asset types. Error: %s", + exchangeName, err) + } for y := range enabledCurrencies { currency := enabledCurrencies[y] - result, err := bot.exchanges[x].UpdateOrderbook(currency) - if err != nil { - log.Printf("failed to get %s orderbook", currency.Pair().String()) - continue - } - log.Printf("%s %s %v", - exchangeName, - exchange.FormatCurrency(currency).String(), - result) - - evt := WebsocketEvent{ - Data: result, - Event: "orderbook_update", - Exchange: exchangeName, + if len(assetTypes) > 1 { + for z := range assetTypes { + result, err = bot.exchanges[x].UpdateOrderbook(currency, + assetTypes[z]) + printOrderbookSummary(result, currency, assetTypes[z], exchangeName, err) + if err == nil { + relayWebsocketEvent(result, "orderbook_update", assetTypes[z], exchangeName) + } + } + } else { + result, err = bot.exchanges[x].UpdateOrderbook(currency, + assetTypes[0]) + printOrderbookSummary(result, currency, assetTypes[0], exchangeName, err) + if err == nil { + relayWebsocketEvent(result, "orderbook_update", assetTypes[0], exchangeName) + } } - BroadcastWebsocketMessage(evt) } } } diff --git a/websocket.go b/websocket.go index 3086216a..8c697e57 100644 --- a/websocket.go +++ b/websocket.go @@ -94,7 +94,6 @@ func SendWebsocketMessage(id int, data interface{}) error { } func BroadcastWebsocketMessage(evt WebsocketEvent) error { - log.Println(evt) for _, x := range WebsocketClientHub { x.Conn.WriteJSON(evt) }