diff --git a/config/config.go b/config/config.go index e3d3162f..2f9468be 100644 --- a/config/config.go +++ b/config/config.go @@ -124,7 +124,7 @@ func (c *Config) SupportsPair(exchName string, p pair.CurrencyPair) (bool, error if err != nil { return false, err } - return pair.Contains(pairs, p), nil + return pair.Contains(pairs, p, false), nil } // GetAvailablePairs returns a list of currency pairs for a specifc exchange diff --git a/currency/pair/pair.go b/currency/pair/pair.go index 9148a442..f1a68e55 100644 --- a/currency/pair/pair.go +++ b/currency/pair/pair.go @@ -2,6 +2,8 @@ package pair import ( "strings" + + "github.com/thrasher-/gocryptotrader/common" ) // CurrencyItem is an exported string with methods to manipulate the data instead @@ -63,16 +65,31 @@ func (c CurrencyPair) Display(delimiter string, uppercase bool) CurrencyItem { } // Equal compares two currency pairs and returns whether or not they are equal -func (c CurrencyPair) Equal(p CurrencyPair) bool { - if c.FirstCurrency.Upper() == p.FirstCurrency.Upper() && - c.SecondCurrency.Upper() == p.SecondCurrency.Upper() || - c.FirstCurrency.Upper() == p.SecondCurrency.Upper() && - c.SecondCurrency.Upper() == p.FirstCurrency.Upper() { - return true +func (c CurrencyPair) Equal(p CurrencyPair, exact bool) bool { + if !exact { + if c.FirstCurrency.Upper() == p.FirstCurrency.Upper() && + c.SecondCurrency.Upper() == p.SecondCurrency.Upper() || + c.FirstCurrency.Upper() == p.SecondCurrency.Upper() && + c.SecondCurrency.Upper() == p.FirstCurrency.Upper() { + return true + } + } else { + if c.FirstCurrency.Upper() == p.FirstCurrency.Upper() && + c.SecondCurrency.Upper() == p.SecondCurrency.Upper() { + return true + } } return false } +// Swap swaps the pairs first and second currencies +func (c CurrencyPair) Swap() CurrencyPair { + p := c + p.FirstCurrency = c.SecondCurrency + p.SecondCurrency = c.FirstCurrency + return p +} + // NewCurrencyPairDelimiter splits the desired currency string at delimeter, // the returns a CurrencyPair struct func NewCurrencyPairDelimiter(currency, delimiter string) CurrencyPair { @@ -118,15 +135,34 @@ func NewCurrencyPairFromString(currency string) CurrencyPair { // Contains checks to see if a specified pair exists inside a currency pair // array -func Contains(pairs []CurrencyPair, p CurrencyPair) bool { +func Contains(pairs []CurrencyPair, p CurrencyPair, exact bool) bool { for x := range pairs { - if pairs[x].Equal(p) { + if pairs[x].Equal(p, exact) { return true } } return false } +// ContainsCurrency checks to see if a pair contains a specific currency +func ContainsCurrency(p CurrencyPair, c string) bool { + return p.FirstCurrency.Upper().String() == common.StringToUpper(c) || + p.SecondCurrency.Upper().String() == common.StringToUpper(c) +} + +// RemovePairsByFilter checks to see if a pair contains a specific currency +// and removes it from the list of pairs +func RemovePairsByFilter(p []CurrencyPair, filter string) []CurrencyPair { + var pairs []CurrencyPair + for x := range p { + if ContainsCurrency(p[x], filter) { + continue + } + pairs = append(pairs, p[x]) + } + return pairs +} + // FormatPairs formats a string array to a list of currency pairs with the // supplied currency pair format func FormatPairs(pairs []string, delimiter, index string) []CurrencyPair { @@ -151,9 +187,9 @@ func FormatPairs(pairs []string, delimiter, index string) []CurrencyPair { } // CopyPairFormat copies the pair format from a list of pairs once matched -func CopyPairFormat(p CurrencyPair, pairs []CurrencyPair) CurrencyPair { +func CopyPairFormat(p CurrencyPair, pairs []CurrencyPair, exact bool) CurrencyPair { for x := range pairs { - if p.Equal(pairs[x]) { + if p.Equal(pairs[x], exact) { return pairs[x] } } diff --git a/currency/pair/pair_test.go b/currency/pair/pair_test.go index f568c125..158f7497 100644 --- a/currency/pair/pair_test.go +++ b/currency/pair/pair_test.go @@ -111,7 +111,7 @@ func TestEqual(t *testing.T) { t.Parallel() pair := NewCurrencyPair("BTC", "USD") secondPair := NewCurrencyPair("btc", "uSd") - actual := pair.Equal(secondPair) + actual := pair.Equal(secondPair, false) expected := true if actual != expected { t.Errorf( @@ -121,7 +121,7 @@ func TestEqual(t *testing.T) { } secondPair.SecondCurrency = "ETH" - actual = pair.Equal(secondPair) + actual = pair.Equal(secondPair, false) expected = false if actual != expected { t.Errorf( @@ -131,7 +131,7 @@ func TestEqual(t *testing.T) { } secondPair = NewCurrencyPair("USD", "BTC") - actual = pair.Equal(secondPair) + actual = pair.Equal(secondPair, false) expected = true if actual != expected { t.Errorf( @@ -141,6 +141,19 @@ func TestEqual(t *testing.T) { } } +func TestSwap(t *testing.T) { + t.Parallel() + pair := NewCurrencyPair("BTC", "USD") + actual := pair.Swap().Pair() + expected := CurrencyItem("USDBTC") + if actual != expected { + t.Errorf( + "Test failed. TestSwap: %s was not equal to expected value: %s", + actual, expected, + ) + } +} + func TestNewCurrencyPair(t *testing.T) { t.Parallel() pair := NewCurrencyPair("BTC", "USD") @@ -243,15 +256,39 @@ func TestContains(t *testing.T) { pairs = append(pairs, pairOne) pairs = append(pairs, pairTwo) - if !Contains(pairs, pairOne) { + if !Contains(pairs, pairOne, true) { t.Errorf("Test failed. TestContains: Expected pair was not found") } - if Contains(pairs, NewCurrencyPair("ETH", "USD")) { + if Contains(pairs, NewCurrencyPair("ETH", "USD"), false) { t.Errorf("Test failed. TestContains: Non-existant pair was found") } } +func TestContainsCurrency(t *testing.T) { + p := NewCurrencyPair("BTC", "USD") + + if !ContainsCurrency(p, "BTC") { + t.Error("Test failed. TestContainsCurrency: Expected currency was not found") + } + + if ContainsCurrency(p, "ETH") { + t.Error("Test failed. TestContainsCurrency: Non-existant currency was found") + } +} + +func TestRemovePairsByFilter(t *testing.T) { + var pairs []CurrencyPair + pairs = append(pairs, NewCurrencyPair("BTC", "USD")) + pairs = append(pairs, NewCurrencyPair("LTC", "USD")) + pairs = append(pairs, NewCurrencyPair("LTC", "USDT")) + + pairs = RemovePairsByFilter(pairs, "USDT") + if Contains(pairs, NewCurrencyPair("LTC", "USDT"), true) { + t.Error("Test failed. TestRemovePairsByFilter unexpected result") + } +} + func TestFormatPairs(t *testing.T) { if len(FormatPairs([]string{""}, "-", "")) > 0 { t.Error("Test failed. TestFormatPairs: Empty string returned a valid pair") @@ -282,12 +319,12 @@ func TestCopyPairFormat(t *testing.T) { testPair := NewCurrencyPair("BTC", "USD") testPair.Delimiter = "~" - result := CopyPairFormat(testPair, pairs) + result := CopyPairFormat(testPair, pairs, false) if result.Pair().String() != "BTC-USD" { t.Error("Test failed. TestCopyPairFormat: Expected pair was not found") } - result = CopyPairFormat(NewCurrencyPair("ETH", "USD"), pairs) + result = CopyPairFormat(NewCurrencyPair("ETH", "USD"), pairs, true) if result.Pair().String() != "" { t.Error("Test failed. TestCopyPairFormat: Unexpected non empty pair returned") } diff --git a/currency/translation/translation.go b/currency/translation/translation.go index f3c2001a..65948ae2 100644 --- a/currency/translation/translation.go +++ b/currency/translation/translation.go @@ -15,18 +15,22 @@ var translations = map[pair.CurrencyItem]pair.CurrencyItem{ // GetTranslation returns similar strings for a particular currency func GetTranslation(currency pair.CurrencyItem) (pair.CurrencyItem, error) { - result, ok := translations[currency] - if !ok { - return "", errors.New("no translation found for specified currency") - } + for k, v := range translations { + if k == currency { + return v, nil + } - return result, nil + if v == currency { + return k, nil + } + } + return "", errors.New("no translation found for specified currency") } // HasTranslation returns whether or not a particular currency has a translation func HasTranslation(currency pair.CurrencyItem) bool { - _, ok := translations[currency] - if !ok { + _, err := GetTranslation(currency) + if err != nil { return false } return true diff --git a/currency/translation/translation_test.go b/currency/translation/translation_test.go index 0fb32767..de50fa7e 100644 --- a/currency/translation/translation_test.go +++ b/currency/translation/translation_test.go @@ -23,6 +23,18 @@ func TestGetTranslation(t *testing.T) { if err == nil { t.Error("GetTranslation: no error on non translatable currency") } + + expected = "BTC" + currencyPair.FirstCurrency = "XBT" + + actual, err = GetTranslation(currencyPair.FirstCurrency) + if err != nil { + t.Error("GetTranslation: failed to retrieve translation for BTC") + } + + if expected != actual { + t.Error("GetTranslation: translation result was different to expected result") + } } func TestHasTranslation(t *testing.T) { @@ -33,6 +45,13 @@ func TestHasTranslation(t *testing.T) { t.Error("HasTranslation: translation result was different to expected result") } + currencyPair.FirstCurrency = "XBT" + expected = true + actual = HasTranslation(currencyPair.FirstCurrency) + if expected != actual { + t.Error("HasTranslation: translation result was different to expected result") + } + currencyPair.FirstCurrency = "NEO" expected = false actual = HasTranslation(currencyPair.FirstCurrency) diff --git a/exchanges/coinut/coinut.go b/exchanges/coinut/coinut.go index 999de2b2..8f152ab6 100644 --- a/exchanges/coinut/coinut.go +++ b/exchanges/coinut/coinut.go @@ -354,6 +354,9 @@ func (c *COINUT) SendHTTPRequest(apiRequest string, params map[string]interface{ return errors.New("unable to JSON Unmarshal generic response") } + if len(genResp.Status) < 1 { + return errors.New("genResp.Status was empty") + } if genResp.Status[0] != "OK" { return errors.New("status is not OK") } diff --git a/exchanges/exchange.go b/exchanges/exchange.go index 2dc502de..df5a421d 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -218,9 +218,9 @@ func (e *Base) GetAvailableCurrencies() []pair.CurrencyPair { // exchange available currencies or not func (e *Base) SupportsCurrency(p pair.CurrencyPair, enabledPairs bool) bool { if enabledPairs { - return pair.Contains(e.GetEnabledCurrencies(), p) + return pair.Contains(e.GetEnabledCurrencies(), p, false) } - return pair.Contains(e.GetAvailableCurrencies(), p) + return pair.Contains(e.GetAvailableCurrencies(), p, false) } // GetExchangeFormatCurrencySeperator returns whether or not a specific diff --git a/exchanges/exchange_test.go b/exchanges/exchange_test.go index 8563682b..5524beb1 100644 --- a/exchanges/exchange_test.go +++ b/exchanges/exchange_test.go @@ -503,12 +503,12 @@ func TestSetCurrencies(t *testing.T) { UAC.Name = "ANX" UAC.SetCurrencies([]pair.CurrencyPair{newPair}, true) - if !pair.Contains(UAC.GetEnabledCurrencies(), newPair) { + if !pair.Contains(UAC.GetEnabledCurrencies(), newPair, true) { t.Fatal("Test failed. TestSetCurrencies failed to set currencies") } UAC.SetCurrencies([]pair.CurrencyPair{newPair}, false) - if !pair.Contains(UAC.GetAvailableCurrencies(), newPair) { + if !pair.Contains(UAC.GetAvailableCurrencies(), newPair, true) { t.Fatal("Test failed. TestSetCurrencies failed to set currencies") } } diff --git a/exchanges/stats/stats.go b/exchanges/stats/stats.go index 200a35fa..4abf01af 100644 --- a/exchanges/stats/stats.go +++ b/exchanges/stats/stats.go @@ -89,7 +89,7 @@ func Append(exchange string, p pair.CurrencyPair, assetType string, price, volum // for a specific currency pair and asset type func AlreadyExists(exchange string, p pair.CurrencyPair, assetType string, price, volume float64) bool { for i := range Items { - if Items[i].Exchange == exchange && Items[i].Pair.Equal(p) && Items[i].AssetType == assetType { + if Items[i].Exchange == exchange && Items[i].Pair.Equal(p, false) && Items[i].AssetType == assetType { Items[i].Price, Items[i].Volume = price, volume return true } @@ -103,7 +103,7 @@ func AlreadyExists(exchange string, p pair.CurrencyPair, assetType string, price func SortExchangesByVolume(p pair.CurrencyPair, assetType string, reverse bool) []Item { var result []Item for x := range Items { - if Items[x].Pair.Equal(p) && Items[x].AssetType == assetType { + if Items[x].Pair.Equal(p, false) && Items[x].AssetType == assetType { result = append(result, Items[x]) } } @@ -122,7 +122,7 @@ func SortExchangesByVolume(p pair.CurrencyPair, assetType string, reverse bool) func SortExchangesByPrice(p pair.CurrencyPair, assetType string, reverse bool) []Item { var result []Item for x := range Items { - if Items[x].Pair.Equal(p) && Items[x].AssetType == assetType { + if Items[x].Pair.Equal(p, false) && Items[x].AssetType == assetType { result = append(result, Items[x]) } } diff --git a/helpers.go b/helpers.go index ab88e1c8..8c982f3b 100644 --- a/helpers.go +++ b/helpers.go @@ -13,12 +13,82 @@ import ( "github.com/thrasher-/gocryptotrader/exchanges/ticker" ) +// GetAllAvailablePairs returns a list of all available pairs on either enabled +// or disabled exchanges +func GetAllAvailablePairs(enabledExchangesOnly bool) []pair.CurrencyPair { + var pairList []pair.CurrencyPair + for x := range bot.config.Exchanges { + if enabledExchangesOnly && !bot.config.Exchanges[x].Enabled { + continue + } + + exchName := bot.config.Exchanges[x].Name + pairs, err := bot.config.GetAvailablePairs(exchName) + if err != nil { + continue + } + + for y := range pairs { + if pair.Contains(pairList, pairs[y]) { + continue + } + pairList = append(pairList, pairs[y]) + } + } + return pairList +} + +// GetSpecificAvailablePairs returns a list of supported pairs based on specific +// parameters +func GetSpecificAvailablePairs(enabledExchangesOnly, fiatPairs, includeUSDT, cryptoPairs bool) []pair.CurrencyPair { + var pairList []pair.CurrencyPair + supportedPairs := GetAllAvailablePairs(enabledExchangesOnly) + + for x := range supportedPairs { + if fiatPairs { + if currency.IsCryptoFiatPair(supportedPairs[x]) || currency.IsFiatPair(supportedPairs[x]) || (includeUSDT && pair.ContainsCurrency(supportedPairs[x], "USDT")) { + if pair.Contains(pairList, supportedPairs[x]) { + continue + } + pairList = append(pairList, supportedPairs[x]) + } + } + if cryptoPairs { + if currency.IsCryptoPair(supportedPairs[x]) { + if pair.Contains(pairList, supportedPairs[x]) { + continue + } + pairList = append(pairList, supportedPairs[x]) + } + } + } + return pairList +} + +// IsRelatablePairs checks to see if the two pairs are relatable +func IsRelatablePairs(p1, p2 pair.CurrencyPair) bool { + if currency.IsCryptoPair(p1) && currency.IsCryptoPair(p2) { + relatablePairs := GetRelatableCurrencies(p1, false) + return pair.Contains(relatablePairs, p2) + } + + if currency.IsCryptoFiatPair(p1) && currency.IsCryptoFiatPair(p2) { + relatablePairs := GetRelatableFiatCurrencies(p1) + relatablePairs = append(relatablePairs, GetRelatableCurrencies(p1, false)...) + return pair.Contains(relatablePairs, p2) + } + return false +} + // MapCurrenciesByExchange returns a list of currency pairs mapped to an // exchange -func MapCurrenciesByExchange(p []pair.CurrencyPair) map[string][]pair.CurrencyPair { +func MapCurrenciesByExchange(p []pair.CurrencyPair, enabledExchangesOnly bool) map[string][]pair.CurrencyPair { currencyExchange := make(map[string][]pair.CurrencyPair) for x := range p { for y := range bot.config.Exchanges { + if enabledExchangesOnly && !bot.config.Exchanges[y].Enabled { + continue + } exchName := bot.config.Exchanges[y].Name success, err := bot.config.SupportsPair(exchName, p[x]) if err != nil || !success { @@ -31,6 +101,9 @@ func MapCurrenciesByExchange(p []pair.CurrencyPair) map[string][]pair.CurrencyPa pairs = append(pairs, p[x]) currencyExchange[exchName] = pairs } else { + if pair.Contains(result, p[x]) { + continue + } result = append(result, p[x]) currencyExchange[exchName] = result }