diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 907db755..1bd5d584 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -2,8 +2,8 @@
Thanks to the following contributors:
thrasher- | https://github.com/thrasher-
shazbert | https://github.com/shazbert
-gloriousCode | https://github.com/gloriousCode
dependabot[bot] | https://github.com/apps/dependabot
+gloriousCode | https://github.com/gloriousCode
dependabot-preview[bot] | https://github.com/apps/dependabot-preview
xtda | https://github.com/xtda
gbjk | https://github.com/gbjk
@@ -13,13 +13,13 @@ vazha | https://github.com/vazha
ydm | https://github.com/ydm
ermalguni | https://github.com/ermalguni
MadCozBadd | https://github.com/MadCozBadd
+Beadko | https://github.com/Beadko
vadimzhukck | https://github.com/vadimzhukck
140am | https://github.com/140am
marcofranssen | https://github.com/marcofranssen
geseq | https://github.com/geseq
-Beadko | https://github.com/Beadko
-TaltaM | https://github.com/TaltaM
samuael | https://github.com/samuael
+TaltaM | https://github.com/TaltaM
dackroyd | https://github.com/dackroyd
cranktakular | https://github.com/cranktakular
khcchiu | https://github.com/khcchiu
diff --git a/README.md b/README.md
index 68c8bc5c..9a53d1cb 100644
--- a/README.md
+++ b/README.md
@@ -144,25 +144,25 @@ Binaries will be published once the codebase reaches a stable condition.
|User|Contribution Amount|
|--|--|
| [thrasher-](https://github.com/thrasher-) | 683 |
-| [shazbert](https://github.com/shazbert) | 301 |
-| [gloriousCode](https://github.com/gloriousCode) | 219 |
-| [dependabot[bot]](https://github.com/apps/dependabot) | 207 |
+| [shazbert](https://github.com/shazbert) | 313 |
+| [dependabot[bot]](https://github.com/apps/dependabot) | 227 |
+| [gloriousCode](https://github.com/gloriousCode) | 224 |
| [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) | 88 |
| [xtda](https://github.com/xtda) | 47 |
-| [gbjk](https://github.com/gbjk) | 35 |
+| [gbjk](https://github.com/gbjk) | 40 |
| [lrascao](https://github.com/lrascao) | 27 |
| [Rots](https://github.com/Rots) | 15 |
| [vazha](https://github.com/vazha) | 15 |
| [ydm](https://github.com/ydm) | 15 |
| [ermalguni](https://github.com/ermalguni) | 14 |
| [MadCozBadd](https://github.com/MadCozBadd) | 13 |
+| [Beadko](https://github.com/Beadko) | 10 |
| [vadimzhukck](https://github.com/vadimzhukck) | 10 |
| [140am](https://github.com/140am) | 8 |
| [marcofranssen](https://github.com/marcofranssen) | 8 |
| [geseq](https://github.com/geseq) | 8 |
-| [Beadko](https://github.com/Beadko) | 6 |
+| [samuael](https://github.com/samuael) | 7 |
| [TaltaM](https://github.com/TaltaM) | 6 |
-| [samuael](https://github.com/samuael) | 6 |
| [dackroyd](https://github.com/dackroyd) | 5 |
| [cranktakular](https://github.com/cranktakular) | 5 |
| [khcchiu](https://github.com/khcchiu) | 5 |
diff --git a/cmd/documentation/currency_templates/fx.tmpl b/cmd/documentation/currency_templates/fx.tmpl
index 14a0fba9..6e9e55f1 100644
--- a/cmd/documentation/currency_templates/fx.tmpl
+++ b/cmd/documentation/currency_templates/fx.tmpl
@@ -7,7 +7,6 @@
+ Exchange Rates support
+ Fixer.io support
+ Open Exchange Rates support
-+ ExchangeRate.host support
### Please click GoDocs chevron above to view current GoDoc information for this package
{{template "contributions"}}
diff --git a/cmd/documentation/currency_templates/fx_exchangeratehost.tmpl b/cmd/documentation/currency_templates/fx_exchangeratehost.tmpl
deleted file mode 100644
index 24a9c900..00000000
--- a/cmd/documentation/currency_templates/fx_exchangeratehost.tmpl
+++ /dev/null
@@ -1,35 +0,0 @@
-{{define "currency forexprovider exchangerate.host" -}}
-{{template "header" .}}
-## Current Features for {{.Name}}
-
-+ Fetches up to date currency data from [ExchangeRate.host API]("https://exchangerate.host")
-
-### How to enable
-
-+ [Enable via configuration](https://github.com/thrasher-corp/gocryptotrader/tree/master/config#enable-currency-via-config-example)
-
-+ Individual package example below:
-```go
-import (
- "github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
- "github.com/thrasher-corp/gocryptotrader/currency/forexprovider/exchangerate.host"
-)
-
-var c exchangeratehost.ExchangeRateHost
-
-// Define configuration
-newSettings := base.Settings{
- Name: "ExchangeRateHost",
- // ...
-}
-
-c.Setup(newSettings)
-
-rates, err := c.GetRates("USD", "EUR,AUD")
-// Handle error
-```
-
-### Please click GoDocs chevron above to view current GoDoc information for this package
-{{template "contributions"}}
-{{template "donations" .}}
-{{- end}}
\ No newline at end of file
diff --git a/config/config_types.go b/config/config_types.go
index 657cabbe..c69a942e 100644
--- a/config/config_types.go
+++ b/config/config_types.go
@@ -68,12 +68,11 @@ const (
// Constants here define unset default values displayed in the config.json
// file
const (
- APIURLNonDefaultMessage = "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API"
- WebsocketURLNonDefaultMessage = "NON_DEFAULT_HTTP_LINK_TO_WEBSOCKET_EXCHANGE_API"
- DefaultUnsetAPIKey = "Key"
- DefaultUnsetAPISecret = "Secret"
- DefaultUnsetAccountPlan = "accountPlan"
- DefaultForexProviderExchangeRatesAPI = "ExchangeRateHost"
+ APIURLNonDefaultMessage = "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API"
+ WebsocketURLNonDefaultMessage = "NON_DEFAULT_HTTP_LINK_TO_WEBSOCKET_EXCHANGE_API"
+ DefaultUnsetAPIKey = "Key"
+ DefaultUnsetAPISecret = "Secret"
+ DefaultUnsetAccountPlan = "accountPlan"
)
// Variables here are used for configuration
diff --git a/currency/conversion_test.go b/currency/conversion_test.go
index 189b18f2..ca97648d 100644
--- a/currency/conversion_test.go
+++ b/currency/conversion_test.go
@@ -2,32 +2,25 @@ package currency
import (
"fmt"
- "strings"
"testing"
+
+ "github.com/stretchr/testify/assert"
)
func TestNewConversionFromString(t *testing.T) {
- expected := "AUDUSD"
- conv, err := NewConversionFromString(expected)
- if err != nil {
- t.Error(err)
- }
- if conv.String() != expected {
- t.Errorf("NewConversion() error expected %s but received %s",
- expected,
- conv)
- }
+ conv, err := NewConversionFromString("AUDUSD")
+ assert.NoError(t, err, "NewConversionFromString should not error")
+ assert.Equal(t, "AUDUSD", conv.String(), "Should provide correct conversion currency")
+ r, err := conv.GetRate()
+ assert.NoError(t, err, "GetRate should not error")
+ assert.Positive(t, r, "Should provide correct conversion rate")
- newexpected := strings.ToLower(expected)
- conv, err = NewConversionFromString(newexpected)
- if err != nil {
- t.Error(err)
- }
- if conv.String() != newexpected {
- t.Errorf("NewConversion() error expected %s but received %s",
- newexpected,
- conv)
- }
+ conv, err = NewConversionFromString("audusd")
+ assert.NoError(t, err, "NewConversionFromString should not error")
+ assert.Equal(t, "audusd", conv.String(), "Should provide correct conversion for lowercase")
+ r, err = conv.GetRate()
+ assert.NoError(t, err, "GetRate should not error")
+ assert.Positive(t, r, "Should provide correct conversion rate")
}
func TestNewConversionFromStrings(t *testing.T) {
diff --git a/currency/currency_types.go b/currency/currency_types.go
index de9d1aa4..97575ed8 100644
--- a/currency/currency_types.go
+++ b/currency/currency_types.go
@@ -34,7 +34,6 @@ type BotOverrides struct {
ExchangeRates bool
Fixer bool
OpenExchangeRates bool
- ExchangeRateHost bool
}
// CoinmarketcapSettings refers to settings
diff --git a/currency/forexprovider/README.md b/currency/forexprovider/README.md
index 25118167..eca205a7 100644
--- a/currency/forexprovider/README.md
+++ b/currency/forexprovider/README.md
@@ -25,7 +25,6 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
+ Exchange Rates support
+ Fixer.io support
+ Open Exchange Rates support
-+ ExchangeRate.host support
### Please click GoDocs chevron above to view current GoDoc information for this package
diff --git a/currency/forexprovider/currencylayer/currencylayer_test.go b/currency/forexprovider/currencylayer/currencylayer_test.go
index 1105a306..bbcb1f40 100644
--- a/currency/forexprovider/currencylayer/currencylayer_test.go
+++ b/currency/forexprovider/currencylayer/currencylayer_test.go
@@ -1,137 +1,129 @@
package currencylayer
import (
+ "log"
+ "os"
+ "strings"
"testing"
+ "github.com/stretchr/testify/assert"
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
)
var c CurrencyLayer
-// please set your API key here for due diligence testing NOTE be aware you will
-// minimize your API calls using this test.
-const (
- APIkey = ""
- Apilevel = 0
+// Either set your API key here or in env var for integration testing
+var (
+ apiKey = ""
+ apiKeyLevel = 0
)
var isSet bool
-func setup() error {
- if !isSet {
- defaultCfg := base.Settings{
- Name: "CurrencyLayer",
- Enabled: true,
- }
-
- if APIkey != "" {
- defaultCfg.APIKey = APIkey
- }
-
- if Apilevel > -2 && Apilevel < 4 {
- defaultCfg.APIKeyLvl = Apilevel
- }
-
- err := c.Setup(defaultCfg)
- if err != nil {
- return err
- }
- isSet = true
+func TestMain(m *testing.M) {
+ if apiKey == "" {
+ apiKey = os.Getenv("CURRENCYLAYER_APIKEY")
}
- return nil
+
+ cfg := base.Settings{
+ Name: "CurrencyLayer",
+ Enabled: true,
+ APIKeyLvl: apiKeyLevel,
+ APIKey: apiKey,
+ }
+
+ if err := c.Setup(cfg); err != nil {
+ log.Fatal(err)
+ }
+ os.Exit(m.Run())
}
-func areAPIKeysSet() bool {
- return APIkey != "" && Apilevel != -1
+func skipIfNoAPIKey(tb testing.TB) {
+ tb.Helper()
+ if apiKey == "" {
+ tb.Skip("No API Key configured. Set env var CURRENCYLAYER_APIKEY")
+ }
}
-func TestGetRates(t *testing.T) {
- err := setup()
- if err != nil {
- t.Skip("CurrencyLayer GetRates error", err)
- }
- _, err = c.GetRates("USD", "AUD")
- if areAPIKeysSet() && err != nil {
- t.Error("test error - currencylayer GetRates() error", err)
- } else if !areAPIKeysSet() && err == nil {
- t.Error("test error - currencylayer GetRates() error cannot be nil")
- }
+func TestNoAPIKey(t *testing.T) {
+ t.Parallel()
+ n := c
+ n.APIKey = ""
+ _, err := n.GetSupportedCurrencies()
+ assert.ErrorContains(t, err, "You have not supplied an API Access Key", "Should error APIKeyRequired")
}
func TestGetSupportedCurrencies(t *testing.T) {
- err := setup()
- if err != nil {
- t.Fatal("CurrencyLayer GetSupportedCurrencies error", err)
+ t.Parallel()
+ skipIfNoAPIKey(t)
+ currs, err := c.GetSupportedCurrencies()
+ if assert.NoError(t, err, "GetSupportedCurrencies should not error") {
+ assert.Contains(t, currs, "AUD", "AUD is a valid currency")
+ assert.Contains(t, currs, "USD", "USD is a valid currency") // Might fail in the near future
}
- _, err = c.GetSupportedCurrencies()
- if areAPIKeysSet() && err != nil {
- t.Error("test error - currencylayer GetSupportedCurrencies() error", err)
- } else if !areAPIKeysSet() && err == nil {
- t.Error("test error - currencylayer GetSupportedCurrencies() error cannot be nil")
+}
+
+func TestGetRates(t *testing.T) {
+ t.Parallel()
+ skipIfNoAPIKey(t)
+ r, err := c.GetRates("USD", "AUD")
+ if assert.NoError(t, err, "GetRates should not error") {
+ assert.Contains(t, r, "USDAUD", "Should find a USDAUD rate")
+ assert.Positive(t, r["USDAUD"], "Rate should be positive")
}
}
func TestGetliveData(t *testing.T) {
- err := setup()
- if err != nil {
- t.Fatal("CurrencyLayer GetliveData error", err)
- }
- _, err = c.GetliveData("AUD", "USD")
- if areAPIKeysSet() && err != nil {
- t.Error("test error - currencylayer GetliveData() error", err)
- } else if !areAPIKeysSet() && err == nil {
- t.Error("test error - currencylayer GetliveData() error cannot be nil")
+ t.Parallel()
+ skipIfNoAPIKey(t)
+ r, err := c.GetliveData("EUR", "GBP")
+ if assert.NoError(t, err, "GetliveData should not error") {
+ assert.Contains(t, r, "GBPEUR", "Should find rate")
+ assert.Positive(t, r["GBPEUR"], "Rate should be positive")
}
}
func TestGetHistoricalData(t *testing.T) {
- err := setup()
- if err != nil {
- t.Fatal("CurrencyLayer GetHistoricalData error", err)
- }
- _, err = c.GetHistoricalData("2016-12-15", []string{"AUD"}, "USD")
- if areAPIKeysSet() && err != nil {
- t.Error("test error - currencylayer GetHistoricalData() error", err)
- } else if !areAPIKeysSet() && err == nil {
- t.Error("test error - currencylayer GetHistoricalData() error cannot be nil")
+ t.Parallel()
+ skipIfNoAPIKey(t)
+ r, err := c.GetHistoricalData("2022-09-26", []string{"USD"}, "EUR")
+ if assert.NoError(t, err, "GetHistoricalData should not error") {
+ assert.Contains(t, r, "EURUSD", "Should find rate")
+ assert.Equal(t, r["EURUSD"], 0.962232, "Rate should be exactly correct")
}
}
func TestConvert(t *testing.T) {
- err := setup()
- if err != nil {
- t.Fatal("CurrencyLayer Convert error", err)
- }
- _, err = c.Convert("USD", "AUD", "", 1)
- if areAPIKeysSet() && err != nil && c.APIKeyLvl >= AccountBasic {
- t.Error("test error - currencylayer Convert() error", err)
- } else if !areAPIKeysSet() && err == nil {
- t.Error("test error - currencylayer Convert() error cannot be nil")
+ t.Parallel()
+ skipIfNoAPIKey(t)
+ r, err := c.Convert("USD", "AUD", "", 1)
+ if assert.NoError(t, err, "Convert should not error") {
+ assert.Positive(t, r, "Should get a positive rate")
}
}
func TestQueryTimeFrame(t *testing.T) {
- err := setup()
- if err != nil {
- t.Fatal("CurrencyLayer QueryTimeFrame error", err)
- }
- _, err = c.QueryTimeFrame("2010-12-0", "2010-12-5", "USD", []string{"AUD"})
- if areAPIKeysSet() && err != nil && c.APIKeyLvl >= AccountPro {
- t.Error("test error - currencylayer QueryTimeFrame() error", err)
- } else if !areAPIKeysSet() && err == nil {
- t.Error("test error - currencylayer QueryTimeFrame() error cannot be nil")
+ t.Parallel()
+ skipIfNoAPIKey(t)
+ r, err := c.QueryTimeFrame("2020-03-12", "2020-03-16", "USD", []string{"AUD"})
+ if assert.NoError(t, err, "QueryTimeFrame should not error") {
+ assert.Len(t, r, 5, "Should get correct number of days")
+ a, ok := r["2020-03-16"].(map[string]any)
+ assert.True(t, ok, "Has final date entry")
+ assert.Equal(t, a["USDAUD"], 1.6397, "And it was a bad week")
}
}
func TestQueryCurrencyChange(t *testing.T) {
- err := setup()
- if err != nil {
- t.Fatal("CurrencyLayer QueryCurrencyChange() error", err)
- }
- _, err = c.QueryCurrencyChange("2010-12-0", "2010-12-5", "USD", []string{"AUD"})
- if areAPIKeysSet() && err != nil && c.APIKeyLvl == AccountEnterprise {
- t.Error("test error - currencylayer QueryCurrencyChange() error", err)
- } else if !areAPIKeysSet() && err == nil {
- t.Error("test error - currencylayer QueryCurrencyChange() error cannot be nil")
+ t.Parallel()
+ skipIfNoAPIKey(t)
+ r, err := c.QueryCurrencyChange("2030-03-12", "2030-03-16", "USD", []string{"AUD"})
+ switch {
+ case err != nil && strings.Contains(err.Error(), "insufficient API privileges, upgrade to basic to use this function"):
+ t.Skip("Upgrade to Basic API plan to test Currency Change")
+ case assert.NoError(t, err, "QueryCurrencyChange should not error"):
+ assert.Contains(t, r, "USDAUD", "Should find change")
+ assert.Positive(t, r["USDAUD"].Change, "Change should be positive")
+ assert.Positive(t, r["USDAUD"].ChangePCT, "Change PCT should be positive")
}
}
diff --git a/currency/forexprovider/currencylayer/currencylayer_types.go b/currency/forexprovider/currencylayer/currencylayer_types.go
index 43848c7b..6d6351fa 100644
--- a/currency/forexprovider/currencylayer/currencylayer_types.go
+++ b/currency/forexprovider/currencylayer/currencylayer_types.go
@@ -33,6 +33,7 @@ type CurrencyLayer struct {
// Error Defines the response error if an error occurred
type Error struct {
Code int `json:"code"`
+ Type string `json:"type"`
Info string `json:"info"`
}
diff --git a/currency/forexprovider/exchangerate.host/README.md b/currency/forexprovider/exchangerate.host/README.md
deleted file mode 100644
index 114f5909..00000000
--- a/currency/forexprovider/exchangerate.host/README.md
+++ /dev/null
@@ -1,69 +0,0 @@
-# GoCryptoTrader package Exchangerate.host
-
-
-
-
-[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
-[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
-[](https://godoc.org/github.com/thrasher-corp/gocryptotrader/currency/forexprovider/exchangerate.host)
-[](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
-[](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
-
-
-This exchangerate.host package is part of the GoCryptoTrader codebase.
-
-## This is still in active development
-
-You can track ideas, planned features and what's in progress on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader).
-
-Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTc5ZDE1ZTNiOGM3ZGMyMmY1NTAxYWZhODE0MWM5N2JlZDk1NDU0YTViYzk4NTk3OTRiMDQzNGQ1YTc4YmRlMTk)
-
-## Current Features for exchangerate.host
-
-+ Fetches up to date currency data from [ExchangeRate.host API]("https://exchangerate.host")
-
-### How to enable
-
-+ [Enable via configuration](https://github.com/thrasher-corp/gocryptotrader/tree/master/config#enable-currency-via-config-example)
-
-+ Individual package example below:
-```go
-import (
- "github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
- "github.com/thrasher-corp/gocryptotrader/currency/forexprovider/exchangerate.host"
-)
-
-var c exchangeratehost.ExchangeRateHost
-
-// Define configuration
-newSettings := base.Settings{
- Name: "ExchangeRateHost",
- // ...
-}
-
-c.Setup(newSettings)
-
-rates, err := c.GetRates("USD", "EUR,AUD")
-// Handle error
-```
-
-### Please click GoDocs chevron above to view current GoDoc information for this package
-
-## Contribution
-
-Please feel free to submit any pull requests or suggest any desired features to be added.
-
-When submitting a PR, please abide by our coding guidelines:
-
-+ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
-+ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
-+ Code must adhere to our [coding style](https://github.com/thrasher-corp/gocryptotrader/blob/master/doc/coding_style.md).
-+ Pull requests need to be based on and opened against the `master` branch.
-
-## Donations
-
-
-
-If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
-
-***bc1qk0jareu4jytc0cfrhr5wgshsq8282awpavfahc***
\ No newline at end of file
diff --git a/currency/forexprovider/exchangerate.host/exchangerate.go b/currency/forexprovider/exchangerate.host/exchangerate.go
deleted file mode 100644
index 420b1213..00000000
--- a/currency/forexprovider/exchangerate.host/exchangerate.go
+++ /dev/null
@@ -1,265 +0,0 @@
-package exchangeratehost
-
-import (
- "context"
- "errors"
- "net/http"
- "net/url"
- "strconv"
- "time"
-
- "github.com/thrasher-corp/gocryptotrader/common"
- "github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
- "github.com/thrasher-corp/gocryptotrader/exchanges/request"
-)
-
-// A client for the exchangerate.host API. NOTE: The format and callback
-// parameters aren't supported as they're not needed for this implementation.
-// Furthermore, the source is set to "ECB" as default
-
-const (
- timeLayout = "2006-01-02"
- exchangeRateHostURL = "https://api.exchangerate.host"
-)
-
-var (
- // DefaultSource uses the ecb for forex rates
- DefaultSource = "ecb"
-)
-
-// Setup sets up the ExchangeRateHost config
-func (e *ExchangeRateHost) Setup(config base.Settings) error {
- e.Name = config.Name
- e.Enabled = config.Enabled
- e.Verbose = config.Verbose
- e.PrimaryProvider = config.PrimaryProvider
- var err error
- e.Requester, err = request.New(e.Name,
- common.NewHTTPClientWithTimeout(base.DefaultTimeOut))
- return err
-}
-
-// GetLatestRates returns a list of forex rates based on the supplied params
-func (e *ExchangeRateHost) GetLatestRates(baseCurrency, symbols string, amount float64, places int64, source string) (*LatestRates, error) {
- v := url.Values{}
- if baseCurrency != "" {
- v.Set("base", baseCurrency)
- }
-
- if symbols != "" {
- v.Set("symbols", symbols)
- }
-
- if amount != 0 {
- v.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
- }
-
- if places != 0 {
- v.Set("places", strconv.FormatInt(places, 10))
- }
-
- targetSource := DefaultSource
- if source != "" {
- targetSource = source
- }
- v.Set("source", targetSource)
-
- var l LatestRates
- return &l, e.SendHTTPRequest("latest", v, &l)
-}
-
-// ConvertCurrency converts a currency based on the supplied params
-func (e *ExchangeRateHost) ConvertCurrency(from, to, baseCurrency, symbols, source string, date time.Time, amount float64, places int64) (*ConvertCurrency, error) {
- v := url.Values{}
- if from != "" {
- v.Set("from", from)
- }
- if to != "" {
- v.Set("to", to)
- }
- if !date.IsZero() {
- v.Set("date", date.UTC().Format(timeLayout))
- }
- if baseCurrency != "" {
- v.Set("base", baseCurrency)
- }
- if symbols != "" {
- v.Set("symbols", symbols)
- }
- if amount != 0 {
- v.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
- }
- if places != 0 {
- v.Set("places", strconv.FormatInt(places, 10))
- }
- targetSource := DefaultSource
- if source != "" {
- targetSource = source
- }
- v.Set("source", targetSource)
-
- var c ConvertCurrency
- return &c, e.SendHTTPRequest("convert", v, &c)
-}
-
-// GetHistoricalRates returns a list of historical rates based on the supplied params
-func (e *ExchangeRateHost) GetHistoricalRates(date time.Time, baseCurrency, symbols string, amount float64, places int64, source string) (*HistoricRates, error) {
- v := url.Values{}
- if date.IsZero() {
- date = time.Now()
- }
- fmtDate := date.UTC().Format(timeLayout)
- v.Set("date", fmtDate)
- if baseCurrency != "" {
- v.Set("base", baseCurrency)
- }
-
- if symbols != "" {
- v.Set("symbols", symbols)
- }
-
- if amount != 0 {
- v.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
- }
-
- if places != 0 {
- v.Set("places", strconv.FormatInt(places, 10))
- }
-
- targetSource := DefaultSource
- if source != "" {
- targetSource = source
- }
- v.Set("source", targetSource)
-
- var h HistoricRates
- return &h, e.SendHTTPRequest(fmtDate, v, &h)
-}
-
-// GetTimeSeries returns time series forex data based on the supplied params
-func (e *ExchangeRateHost) GetTimeSeries(startDate, endDate time.Time, baseCurrency, symbols string, amount float64, places int64, source string) (*TimeSeries, error) {
- if startDate.IsZero() || endDate.IsZero() {
- return nil, errors.New("startDate and endDate must be set")
- }
-
- if startDate.After(endDate) || startDate.Equal(endDate) {
- return nil, errors.New("startDate and endDate must be set correctly")
- }
-
- v := url.Values{}
- v.Set("start_date", startDate.UTC().Format(timeLayout))
- v.Set("end_date", endDate.UTC().Format(timeLayout))
-
- if baseCurrency != "" {
- v.Set("base", baseCurrency)
- }
-
- if symbols != "" {
- v.Set("symbols", symbols)
- }
-
- if amount != 0 {
- v.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
- }
-
- if places != 0 {
- v.Set("places", strconv.FormatInt(places, 10))
- }
-
- targetSource := DefaultSource
- if source != "" {
- targetSource = source
- }
- v.Set("source", targetSource)
-
- var t TimeSeries
- return &t, e.SendHTTPRequest("timeseries", v, &t)
-}
-
-// GetFluctuations returns a list of forex price fluctuations based on the supplied params
-func (e *ExchangeRateHost) GetFluctuations(startDate, endDate time.Time, baseCurrency, symbols string, amount float64, places int64, source string) (*Fluctuations, error) {
- if startDate.IsZero() || endDate.IsZero() {
- return nil, errors.New("startDate and endDate must be set")
- }
-
- if startDate.After(endDate) || startDate.Equal(endDate) {
- return nil, errors.New("startDate and endDate must be set correctly")
- }
-
- v := url.Values{}
- v.Set("start_date", startDate.UTC().Format(timeLayout))
- v.Set("end_date", endDate.UTC().Format(timeLayout))
-
- if baseCurrency != "" {
- v.Set("base", baseCurrency)
- }
-
- if symbols != "" {
- v.Set("symbols", symbols)
- }
-
- if amount != 0 {
- v.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
- }
-
- if places != 0 {
- v.Set("places", strconv.FormatInt(places, 10))
- }
-
- targetSource := DefaultSource
- if source != "" {
- targetSource = source
- }
- v.Set("source", targetSource)
-
- var f Fluctuations
- return &f, e.SendHTTPRequest("fluctuation", v, &f)
-}
-
-// GetSupportedSymbols returns a list of supported symbols
-func (e *ExchangeRateHost) GetSupportedSymbols() (*SupportedSymbols, error) {
- var s SupportedSymbols
- return &s, e.SendHTTPRequest("symbols", url.Values{}, &s)
-}
-
-// GetSupportedCurrencies returns a list of supported currencies
-func (e *ExchangeRateHost) GetSupportedCurrencies() ([]string, error) {
- s, err := e.GetSupportedSymbols()
- if err != nil {
- return nil, err
- }
-
- symbols := make([]string, 0, len(s.Symbols))
- for x := range s.Symbols {
- symbols = append(symbols, x)
- }
- return symbols, nil
-}
-
-// GetRates returns the forex rates based on the supplied base currency and symbols
-func (e *ExchangeRateHost) GetRates(baseCurrency, symbols string) (map[string]float64, error) {
- l, err := e.GetLatestRates(baseCurrency, symbols, 0, 0, "")
- if err != nil {
- return nil, err
- }
-
- rates := make(map[string]float64)
- for k, v := range l.Rates {
- rates[baseCurrency+k] = v
- }
- return rates, nil
-}
-
-// SendHTTPRequest sends a typical get request
-func (e *ExchangeRateHost) SendHTTPRequest(endpoint string, v url.Values, result interface{}) error {
- path := common.EncodeURLValues(exchangeRateHostURL+"/"+endpoint, v)
- item := &request.Item{
- Method: http.MethodGet,
- Path: path,
- Result: &result,
- Verbose: e.Verbose,
- }
- return e.Requester.SendPayload(context.TODO(), request.Unset, func() (*request.Item, error) {
- return item, nil
- }, request.UnauthenticatedRequest)
-}
diff --git a/currency/forexprovider/exchangerate.host/exchangerate_test.go b/currency/forexprovider/exchangerate.host/exchangerate_test.go
deleted file mode 100644
index c5535909..00000000
--- a/currency/forexprovider/exchangerate.host/exchangerate_test.go
+++ /dev/null
@@ -1,110 +0,0 @@
-package exchangeratehost
-
-import (
- "log"
- "os"
- "testing"
- "time"
-
- "github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
-)
-
-var (
- e ExchangeRateHost
- testCurrencies = "USD,EUR,CZK"
-)
-
-func TestMain(t *testing.M) {
- err := e.Setup(base.Settings{
- Name: "ExchangeRateHost",
- })
- if err != nil {
- log.Fatal(err)
- }
- os.Exit(t.Run())
-}
-
-func TestGetLatestRates(t *testing.T) {
- _, err := e.GetLatestRates("USD", testCurrencies, 1200, 2, "")
- if err != nil {
- t.Error(err)
- }
-}
-
-func TestConvertCurrency(t *testing.T) {
- _, err := e.ConvertCurrency("USD", "EUR", "", testCurrencies, "", time.Now(), 1200, 2)
- if err != nil {
- t.Error(err)
- }
-}
-
-func TestGetHistoricRates(t *testing.T) {
- _, err := e.GetHistoricalRates(time.Time{}, "AUD", testCurrencies, 1200, 2, "")
- if err != nil {
- t.Error(err)
- }
-}
-
-func TestGetTimeSeriesRates(t *testing.T) {
- _, err := e.GetTimeSeries(time.Time{}, time.Now(), "USD", testCurrencies, 1200, 2, "")
- if err == nil {
- t.Error("empty start time show throw an error")
- }
- tmNow := time.Now()
- _, err = e.GetTimeSeries(tmNow, tmNow, "USD", testCurrencies, 1200, 2, "")
- if err == nil {
- t.Error("equal times show throw an error")
- }
- tmStart := tmNow.AddDate(0, -3, 0)
- _, err = e.GetTimeSeries(tmStart, tmNow, "USD", testCurrencies, 1200, 2, "")
- if err != nil {
- t.Error(err)
- }
-}
-
-func TestGetFluctuationData(t *testing.T) {
- _, err := e.GetFluctuations(time.Time{}, time.Now(), "USD", testCurrencies, 1200, 2, "")
- if err == nil {
- t.Error("empty start time show throw an error")
- }
- tmNow := time.Now()
- _, err = e.GetFluctuations(tmNow, tmNow, "USD", testCurrencies, 1200, 2, "")
- if err == nil {
- t.Error("equal times show throw an error")
- }
- tmStart := tmNow.AddDate(0, -3, 0)
- _, err = e.GetFluctuations(tmStart, tmNow, "USD", testCurrencies, 1200, 2, "")
- if err != nil {
- t.Error(err)
- }
-}
-
-func TestGetSupportedSymbols(t *testing.T) {
- r, err := e.GetSupportedSymbols()
- if err != nil {
- t.Fatal(err)
- }
- if _, ok := r.Symbols["AUD"]; !ok {
- t.Error("should contain AUD")
- }
-}
-
-func TestGetGetSupportedCurrencies(t *testing.T) {
- s, err := e.GetSupportedCurrencies()
- if err != nil {
- t.Fatal(err)
- }
- if len(s) == 0 {
- t.Error("supported currencies should be greater than 0")
- }
-}
-
-func TestGetRates(t *testing.T) {
- r, err := e.GetRates("USD", "")
- if err != nil {
- t.Fatal(err)
- }
- if rate := r["USDAUD"]; rate == 0 {
- t.Error("rate of USDAUD should be set")
- }
-}
diff --git a/currency/forexprovider/exchangerate.host/exchangerate_types.go b/currency/forexprovider/exchangerate.host/exchangerate_types.go
deleted file mode 100644
index 0a56ac98..00000000
--- a/currency/forexprovider/exchangerate.host/exchangerate_types.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package exchangeratehost
-
-import (
- "github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
- "github.com/thrasher-corp/gocryptotrader/exchanges/request"
-)
-
-// ExchangeRateHost stores the struct for the exchangerate.host API
-type ExchangeRateHost struct {
- base.Base
- Requester *request.Requester
-}
-
-// MessageOfTheDay stores the message of the day
-type MessageOfTheDay struct {
- Message string `json:"msg"`
- DonationURL string `json:"url"`
-}
-
-// LatestRates stores the latest forex rates
-type LatestRates struct {
- MessageOfTheDay MessageOfTheDay `json:"motd"`
- Success bool `json:"success"`
- Base string `json:"base"`
- Date string `json:"date"`
- Rates map[string]float64 `json:"rates"`
-}
-
-// ConvertCurrency stores currency conversion data
-type ConvertCurrency struct {
- MessageOfTheDay MessageOfTheDay `json:"motd"`
- Query struct {
- From string `json:"from"`
- To string `json:"to"`
- Amount float64 `json:"amount"`
- } `json:"query"`
- Info struct {
- Rate float64 `json:"rate"`
- } `json:"info"`
- Historical bool `json:"historical"`
- Date string `json:"date"`
- Result float64 `json:"result"`
-}
-
-// HistoricRates stores the hostoric rates
-type HistoricRates struct {
- LatestRates
- Historical bool `json:"historical"`
-}
-
-// TimeSeries stores time series data
-type TimeSeries struct {
- MessageOfTheDay MessageOfTheDay `json:"motd"`
- Success bool `json:"success"`
- TimeSeries bool `json:"timeseries"`
- Base string `json:"base"`
- StartDate string `json:"start_date"`
- EndDate string `json:"end_date"`
- Rates map[string]map[string]float64 `json:"rates"`
-}
-
-// Fluctuation stores an individual rate flucutation
-type Fluctuation struct {
- StartRate float64 `json:"start_rate"`
- EndRate float64 `json:"end_rate"`
- Change float64 `json:"change"`
- ChangePercentage float64 `json:"change_pct"`
-}
-
-// Fluctuations stores a collection of rate fluctuations
-type Fluctuations struct {
- MessageOfTheDay MessageOfTheDay `json:"motd"`
- Success bool `json:"success"`
- Flucutation bool `json:"fluctuation"`
- StartDate string `json:"start_date"`
- EndDate string `json:"end_date"`
- Rates map[string]Fluctuation `json:"rate"`
-}
-
-// Symbol stores an individual symbol
-type Symbol struct {
- Description string `json:"description"`
- Code string `json:"code"`
-}
-
-// SupportedSymbols store a collection of supported symbols
-type SupportedSymbols struct {
- MessageOfTheDay MessageOfTheDay `json:"motd"`
- Success bool `json:"success"`
- Symbols map[string]Symbol `json:"symbols"`
-}
diff --git a/currency/forexprovider/forexprovider.go b/currency/forexprovider/forexprovider.go
index 393a01fa..0be87517 100644
--- a/currency/forexprovider/forexprovider.go
+++ b/currency/forexprovider/forexprovider.go
@@ -9,7 +9,6 @@ import (
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
currencyconverter "github.com/thrasher-corp/gocryptotrader/currency/forexprovider/currencyconverterapi"
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/currencylayer"
- exchangeratehost "github.com/thrasher-corp/gocryptotrader/currency/forexprovider/exchangerate.host"
exchangerates "github.com/thrasher-corp/gocryptotrader/currency/forexprovider/exchangeratesapi.io"
fixer "github.com/thrasher-corp/gocryptotrader/currency/forexprovider/fixer.io"
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/openexchangerates"
@@ -33,36 +32,9 @@ func GetSupportedForexProviders() []string {
"ExchangeRates",
"Fixer",
"OpenExchangeRates",
- "ExchangeRateHost",
}
}
-// NewDefaultFXProvider returns the default forex provider (currencyconverterAPI)
-func NewDefaultFXProvider() *ForexProviders {
- handler := new(ForexProviders)
- provider := new(exchangeratehost.ExchangeRateHost)
- err := provider.Setup(base.Settings{
- PrimaryProvider: true,
- Enabled: true,
- Name: "ExchangeRateHost",
- })
- if err != nil {
- panic(err)
- }
-
- currencies, _ := provider.GetSupportedCurrencies()
- providerBase := base.Provider{
- Provider: provider,
- SupportedCurrencies: currencies,
- }
-
- handler.FXHandler = base.FXHandler{
- Primary: providerBase,
- }
-
- return handler
-}
-
// SetProvider sets provider to the FX handler
func (f *ForexProviders) SetProvider(b base.IFXProvider) error {
currencies, err := b.GetSupportedCurrencies()
@@ -102,8 +74,6 @@ func StartFXService(fxProviders []base.Settings) (*ForexProviders, error) {
provider = new(fixer.Fixer)
case "OpenExchangeRates":
provider = new(openexchangerates.OXR)
- case "ExchangeRateHost":
- provider = new(exchangeratehost.ExchangeRateHost)
default:
return nil, fmt.Errorf("%s %w", fxProviders[i].Name,
errUnhandledForeignExchangeProvider)
diff --git a/currency/mock_provider_test.go b/currency/mock_provider_test.go
new file mode 100644
index 00000000..3e1431ef
--- /dev/null
+++ b/currency/mock_provider_test.go
@@ -0,0 +1,40 @@
+package currency
+
+import (
+ "math/rand"
+ "strings"
+
+ "github.com/thrasher-corp/gocryptotrader/currency/forexprovider"
+ "github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
+)
+
+type MockProvider struct{}
+
+func newMockProvider() *forexprovider.ForexProviders {
+ p := &MockProvider{}
+ c, _ := p.GetSupportedCurrencies()
+ return &forexprovider.ForexProviders{
+ FXHandler: base.FXHandler{
+ Primary: base.Provider{
+ Provider: p,
+ SupportedCurrencies: c,
+ },
+ },
+ }
+}
+
+func (m *MockProvider) GetName() string { return "MockProvider" }
+func (m *MockProvider) Setup(base.Settings) error { return nil }
+func (m *MockProvider) IsEnabled() bool { return true }
+func (m *MockProvider) IsPrimaryProvider() bool { return true }
+func (m *MockProvider) GetSupportedCurrencies() ([]string, error) {
+ return storage.defaultFiatCurrencies.Strings(), nil
+}
+func (m *MockProvider) GetRates(baseCurrency, symbols string) (map[string]float64, error) {
+ c := map[string]float64{}
+ for _, s := range strings.Split(symbols, ",") {
+ // The year is 2027; The USD is nearly worthless. The world reserve currency is eggs.
+ c[baseCurrency+s] = 1 / (1 + rand.Float64()) //nolint:gosec // Doesn't need to be a strong random number
+ }
+ return c, nil
+}
diff --git a/currency/storage.go b/currency/storage.go
index e43d7b35..930533f1 100644
--- a/currency/storage.go
+++ b/currency/storage.go
@@ -26,10 +26,9 @@ func init() {
// CurrencyFileUpdateDelay defines the rate at which the currency.json file is
// updated
const (
- DefaultCurrencyFileDelay = 168 * time.Hour
- DefaultForeignExchangeDelay = 1 * time.Minute
- DefaultStorageFile = "currency.json"
- DefaultForexProviderExchangeRatesAPI = "ExchangeRateHost"
+ DefaultCurrencyFileDelay = 168 * time.Hour
+ DefaultForeignExchangeDelay = 1 * time.Minute
+ DefaultStorageFile = "currency.json"
)
var (
@@ -74,7 +73,12 @@ func (s *Storage) SetDefaults() {
log.Errorf(log.Currency, "Currency Storage: Setting default cryptocurrencies error: %s", err)
}
s.SetupConversionRates()
- s.fiatExchangeMarkets = forexprovider.NewDefaultFXProvider()
+ s.fiatExchangeMarkets = nil
+}
+
+// ForexEnabled returns whether the currency system has any available forex providers enabled
+func ForexEnabled() bool {
+ return storage.fiatExchangeMarkets != nil
}
// RunUpdater runs the foreign exchange updater service. This will set up a JSON
@@ -103,6 +107,10 @@ func (s *Storage) RunUpdater(overrides BotOverrides, settings *Config, filePath
}
s.mtx.Lock()
+
+ // Ensure the forex provider is unset in cases we exit early
+ s.fiatExchangeMarkets = nil
+
s.shutdown = make(chan struct{})
s.baseCurrency = settings.FiatDisplayCurrency
s.path = filepath.Join(filePath, DefaultStorageFile)
@@ -132,26 +140,23 @@ func (s *Storage) RunUpdater(overrides BotOverrides, settings *Config, filePath
(settings.ForexProviders[i].Name == "CurrencyLayer" && overrides.CurrencyLayer) ||
(settings.ForexProviders[i].Name == "Fixer" && overrides.Fixer) ||
(settings.ForexProviders[i].Name == "OpenExchangeRates" && overrides.OpenExchangeRates) ||
- (settings.ForexProviders[i].Name == "ExchangeRates" && overrides.ExchangeRates) ||
- (settings.ForexProviders[i].Name == "ExchangeRateHost" && overrides.ExchangeRateHost)
+ (settings.ForexProviders[i].Name == "ExchangeRates" && overrides.ExchangeRates)
if !enabled {
continue
}
- if settings.ForexProviders[i].Name != DefaultForexProviderExchangeRatesAPI {
- if settings.ForexProviders[i].APIKey == "" || settings.ForexProviders[i].APIKey == "Key" {
- log.Warnf(log.Currency, "%s forex provider API key not set, disabling. Please set this in your config.json file\n",
- settings.ForexProviders[i].Name)
- settings.ForexProviders[i].Enabled = false
- settings.ForexProviders[i].PrimaryProvider = false
- continue
- }
+ if settings.ForexProviders[i].APIKey == "" || settings.ForexProviders[i].APIKey == "Key" {
+ log.Warnf(log.Currency, "%s forex provider API key not set, disabling. Please set this in your config.json file\n",
+ settings.ForexProviders[i].Name)
+ settings.ForexProviders[i].Enabled = false
+ settings.ForexProviders[i].PrimaryProvider = false
+ continue
+ }
- if settings.ForexProviders[i].APIKeyLvl == -1 && settings.ForexProviders[i].Name != "ExchangeRates" {
- log.Warnf(log.Currency, "%s APIKey level not set, functionality is limited. Please review this in your config.json file\n",
- settings.ForexProviders[i].Name)
- }
+ if settings.ForexProviders[i].APIKeyLvl == -1 && settings.ForexProviders[i].Name != "ExchangeRates" {
+ log.Warnf(log.Currency, "%s APIKey level not set, functionality is limited. Please review this in your config.json file\n",
+ settings.ForexProviders[i].Name)
}
if settings.ForexProviders[i].PrimaryProvider {
@@ -166,23 +171,10 @@ func (s *Storage) RunUpdater(overrides BotOverrides, settings *Config, filePath
fxSettings = append(fxSettings, base.Settings(settings.ForexProviders[i]))
}
- if len(fxSettings) == 0 {
- log.Warnln(log.Currency, "No foreign exchange providers enabled, setting default provider...")
- for x := range settings.ForexProviders {
- if settings.ForexProviders[x].Name != DefaultForexProviderExchangeRatesAPI {
- continue
- }
- settings.ForexProviders[x].Enabled = true
- settings.ForexProviders[x].PrimaryProvider = true
- primaryProvider = true
- log.Warnf(log.Currency, "No valid foreign exchange providers configured. Defaulting to %s.", DefaultForexProviderExchangeRatesAPI)
- fxSettings = append(fxSettings, base.Settings(settings.ForexProviders[x]))
- }
- }
-
if len(fxSettings) == 0 {
s.mtx.Unlock()
- return errNoForeignExchangeProvidersEnabled
+ log.Warnln(log.Currency, "No foreign exchange providers enabled, currency conversion will not be available")
+ return nil
}
if !primaryProvider {
@@ -313,8 +305,7 @@ func (s *Storage) ForeignExchangeUpdater() {
log.Errorln(log.Currency, err)
}
- // Unlock main rate retrieval mutex so all routines waiting can get access
- // to data
+ // Unlock main rate retrieval mutex so all routines waiting can get access to data
s.mtx.Unlock()
// Set tickers to client defined rates or defaults
@@ -516,6 +507,9 @@ func (s *Storage) UpdateCurrencies() error {
func (s *Storage) SeedForeignExchangeRatesByCurrencies(c Currencies) error {
s.fxRates.mtx.Lock()
defer s.fxRates.mtx.Unlock()
+ if s.fiatExchangeMarkets == nil {
+ return nil
+ }
rates, err := s.fiatExchangeMarkets.GetCurrencyData(s.baseCurrency.String(),
c.Strings())
if err != nil {
@@ -526,6 +520,9 @@ func (s *Storage) SeedForeignExchangeRatesByCurrencies(c Currencies) error {
// SeedForeignExchangeRate returns a singular exchange rate
func (s *Storage) SeedForeignExchangeRate(from, to Code) (map[string]float64, error) {
+ if s.fiatExchangeMarkets == nil {
+ return nil, nil
+ }
return s.fiatExchangeMarkets.GetCurrencyData(from.String(),
[]string{to.String()})
}
@@ -546,6 +543,9 @@ func (s *Storage) GetDefaultForeignExchangeRates() (Conversions, error) {
func (s *Storage) SeedDefaultForeignExchangeRates() error {
s.fxRates.mtx.Lock()
defer s.fxRates.mtx.Unlock()
+ if s.fiatExchangeMarkets == nil {
+ return errNoForeignExchangeProvidersEnabled
+ }
rates, err := s.fiatExchangeMarkets.GetCurrencyData(
s.defaultBaseCurrency.String(),
s.defaultFiatCurrencies.Strings())
@@ -566,11 +566,13 @@ func (s *Storage) GetExchangeRates() (Conversions, error) {
return s.fxRates.GetFullRates(), nil
}
-// SeedForeignExchangeRates seeds the foreign exchange rates from storage config
-// currencies
+// SeedForeignExchangeRates seeds the foreign exchange rates from storage config currencies
func (s *Storage) SeedForeignExchangeRates() error {
s.fxRates.mtx.Lock()
defer s.fxRates.mtx.Unlock()
+ if s.fiatExchangeMarkets == nil {
+ return errNoForeignExchangeProvidersEnabled
+ }
rates, err := s.fiatExchangeMarkets.GetCurrencyData(s.baseCurrency.String(),
s.fiatCurrencies.Strings())
if err != nil {
@@ -695,6 +697,9 @@ func (s *Storage) UpdateEnabledFiatCurrencies(c Currencies) {
// ConvertCurrency for example converts $1 USD to the equivalent Japanese Yen
// or vice versa.
func (s *Storage) ConvertCurrency(amount float64, from, to Code) (float64, error) {
+ if s.fiatExchangeMarkets == nil {
+ return 0, errNoForeignExchangeProvidersEnabled
+ }
if amount <= 0 {
return 0, fmt.Errorf("%f %w", amount, errInvalidAmount)
}
diff --git a/currency/storage_test.go b/currency/storage_test.go
index 8481ac4a..a35b60dd 100644
--- a/currency/storage_test.go
+++ b/currency/storage_test.go
@@ -1,11 +1,11 @@
package currency
import (
- "errors"
"fmt"
"os"
"testing"
+ "github.com/stretchr/testify/assert"
"github.com/thrasher-corp/gocryptotrader/database/testhelpers"
)
@@ -17,6 +17,8 @@ func TestMain(m *testing.M) {
os.Exit(1)
}
+ storage.fiatExchangeMarkets = newMockProvider()
+
t := m.Run()
err = os.RemoveAll(testhelpers.TempDir)
@@ -30,219 +32,88 @@ func TestMain(m *testing.M) {
func TestRunUpdater(t *testing.T) {
var newStorage Storage
- emptyMainConfig := Config{}
- err := newStorage.RunUpdater(BotOverrides{}, &emptyMainConfig, "")
- if err == nil {
- t.Fatal("storage RunUpdater() error cannot be nil")
- }
+ err := newStorage.RunUpdater(BotOverrides{}, &Config{}, "")
+ assert.ErrorIs(t, err, errFiatDisplayCurrencyUnset, "No currency should error correctly")
- mainConfig := Config{}
- err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, "")
- if !errors.Is(err, errFiatDisplayCurrencyUnset) {
- t.Fatalf("received: '%v' but expected: '%v'", err, errFiatDisplayCurrencyUnset)
- }
+ err = newStorage.RunUpdater(BotOverrides{}, &Config{FiatDisplayCurrency: BTC}, "")
+ assert.ErrorIs(t, err, ErrFiatDisplayCurrencyIsNotFiat, "Crypto currency should error as not fiat")
- mainConfig.FiatDisplayCurrency = BTC
- err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, "")
- if !errors.Is(err, ErrFiatDisplayCurrencyIsNotFiat) {
- t.Fatalf("received: '%v' but expected: '%v'", err, ErrFiatDisplayCurrencyIsNotFiat)
- }
-
- mainConfig.FiatDisplayCurrency = AUD
- err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, "")
- if !errors.Is(err, errNoFilePathSet) {
- t.Fatalf("received: '%v' but expected: '%v'", err, errNoFilePathSet)
- }
+ c := &Config{FiatDisplayCurrency: AUD}
+ err = newStorage.RunUpdater(BotOverrides{}, c, "")
+ assert.ErrorIs(t, err, errNoFilePathSet, "Should error with no path set")
tempDir := testhelpers.TempDir
+ err = newStorage.RunUpdater(BotOverrides{}, c, tempDir)
+ assert.ErrorIs(t, err, errInvalidCurrencyFileUpdateDuration, "Should error invalid file update duration")
- err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, tempDir)
- if !errors.Is(err, errInvalidCurrencyFileUpdateDuration) {
- t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidCurrencyFileUpdateDuration)
- }
+ c.CurrencyFileUpdateDuration = DefaultCurrencyFileDelay
+ err = newStorage.RunUpdater(BotOverrides{}, c, tempDir)
+ assert.ErrorIs(t, err, errInvalidForeignExchangeUpdateDuration, "Should error invalid forex update duration")
- mainConfig.CurrencyFileUpdateDuration = DefaultCurrencyFileDelay
- err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, tempDir)
- if !errors.Is(err, errInvalidForeignExchangeUpdateDuration) {
- t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidForeignExchangeUpdateDuration)
- }
+ c.ForeignExchangeUpdateDuration = DefaultForeignExchangeDelay
+ err = newStorage.RunUpdater(BotOverrides{}, c, tempDir)
- mainConfig.ForeignExchangeUpdateDuration = DefaultForeignExchangeDelay
- err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, tempDir)
- if !errors.Is(err, errNoForeignExchangeProvidersEnabled) {
- t.Fatalf("received: '%v' but expected: '%v'", err, errNoForeignExchangeProvidersEnabled)
- }
-
- settings := FXSettings{
- Name: "Fixer",
- Enabled: true,
- APIKey: "wo",
- }
-
- mainConfig.ForexProviders = AllFXSettings{settings}
- err = newStorage.RunUpdater(BotOverrides{Fixer: true}, &mainConfig, tempDir)
- if errors.Is(err, nil) {
- t.Fatalf("received: '%v' but expected: '%v'", err, "an error")
- }
+ assert.NoError(t, err, "Storage should not error with no forex providers enabled")
+ assert.Nil(t, newStorage.fiatExchangeMarkets, "Forex should not be enabled with no providers") // Proxy for testing ForexEnabled
err = newStorage.Shutdown()
- if err != nil {
- t.Fatal(err)
+ assert.NoError(t, err, "Shutdown should not error evne though it silently aborted the RunUpdater early")
+
+ // Exchanges which reject a bad APIKey
+ for _, n := range []string{"Fixer", "CurrencyConverter", "CurrencyLayer", "ExchangeRates"} {
+ c.ForexProviders = AllFXSettings{{Name: n, Enabled: true, APIKey: ""}}
+ err = newStorage.RunUpdater(overrideForProvider(n), c, tempDir)
+ assert.NoErrorf(t, err, "%s should not error and silently exit without running with no api keys", n)
+ assert.Falsef(t, c.ForexProviders[0].Enabled, "%s should not be marked enabled with no api keys", n)
+ assert.Nil(t, newStorage.fiatExchangeMarkets, "Forex should not be enabled with no providers")
+ c.ForexProviders = AllFXSettings{{Name: n, Enabled: true, APIKey: "sudo shazam!"}}
+ err = newStorage.RunUpdater(overrideForProvider(n), c, tempDir)
+ assert.Errorf(t, err, "%s should throw some provider originating error with a (hopefully) invalid api key", n)
+ assert.Truef(t, c.ForexProviders[0].Enabled, "%s should still be enabled after being chosen but failing", n)
+ assert.Nil(t, newStorage.fiatExchangeMarkets, "Forex should not be enabled when provider errored during startup")
+ err = newStorage.Shutdown()
+ assert.NoError(t, err, "Shutdown should not error")
}
- settings.Name = "CurrencyConverter"
- mainConfig.ForexProviders = AllFXSettings{settings}
- err = newStorage.RunUpdater(BotOverrides{CurrencyConverter: true}, &mainConfig, tempDir)
- if errors.Is(err, nil) {
- t.Fatalf("received: '%v' but expected: '%v'", err, "an error")
+ // Exchanges which do not error with a bad APIKey on startup
+ for _, n := range []string{"OpenExchangeRates"} {
+ c.ForexProviders = AllFXSettings{{Name: n, Enabled: true, APIKey: ""}}
+ err = newStorage.RunUpdater(overrideForProvider(n), c, tempDir)
+ assert.NoErrorf(t, err, "%s should not error and silently exit without running with no api keys", n)
+ assert.Nil(t, newStorage.fiatExchangeMarkets, "Forex should not be enabled with no providers")
+ c.ForexProviders = AllFXSettings{{Name: n, Enabled: true, APIKey: "sudo shazam!"}}
+ err = newStorage.RunUpdater(overrideForProvider(n), c, tempDir)
+ assert.NoError(t, err, "%s should not error on Setup with a bad apikey", n)
+ assert.NotNil(t, newStorage.fiatExchangeMarkets, "Forex should be enabled now we have a provider with a key")
+ err = newStorage.Shutdown()
+ assert.NoError(t, err, "Shutdown should not error")
}
- err = newStorage.Shutdown()
- if err != nil {
- t.Fatal(err)
+ c.ForexProviders = AllFXSettings{
+ {Name: "ExchangeRates"}, // Old Default
+ {Name: "OpenExchangeRates", APIKey: "shazam?"},
}
- settings.Name = "CurrencyLayer"
- mainConfig.ForexProviders = AllFXSettings{settings}
- err = newStorage.RunUpdater(BotOverrides{CurrencyLayer: true}, &mainConfig, tempDir)
- if errors.Is(err, nil) {
- t.Fatalf("received: '%v' but expected: '%v'", err, "an error")
- }
-
- err = newStorage.Shutdown()
- if err != nil {
- t.Fatal(err)
- }
-
- settings.Name = "OpenExchangeRates"
- mainConfig.ForexProviders = AllFXSettings{settings}
- err = newStorage.RunUpdater(BotOverrides{OpenExchangeRates: true}, &mainConfig, tempDir)
- if !errors.Is(err, nil) {
- t.Fatalf("received: '%v' but expected: '%v'", err, nil)
- }
-
- err = newStorage.Shutdown()
- if err != nil {
- t.Fatal(err)
- }
-
- settings.Name = "ExchangeRates"
- mainConfig.ForexProviders = AllFXSettings{settings}
- err = newStorage.RunUpdater(BotOverrides{ExchangeRates: true}, &mainConfig, tempDir)
- if errors.Is(err, nil) {
- t.Fatalf("received: '%v' but expected: '%v'", err, "an error")
- }
-
- err = newStorage.Shutdown()
- if err != nil {
- t.Fatal(err)
- }
-
- settings.Name = "ExchangeRateHost"
- mainConfig.ForexProviders = AllFXSettings{settings}
- err = newStorage.RunUpdater(BotOverrides{ExchangeRateHost: true}, &mainConfig, tempDir)
- if !errors.Is(err, nil) {
- t.Fatalf("received: '%v' but expected: '%v'", err, nil)
- }
-
- err = newStorage.Shutdown()
- if err != nil {
- t.Fatal(err)
- }
-
- // old config where two providers enabled
- oldDefault := FXSettings{
- Name: "ExchangeRates",
- Enabled: true,
- APIKey: "", // old default provider which did not need api keys.
- PrimaryProvider: true,
- }
- other := FXSettings{
- Name: "OpenExchangeRates",
- Enabled: true,
- APIKey: "enabled-keys", // Has keys enabled and will fall over to primary
- }
- defaultProvider := FXSettings{
- // From config this will be included but not necessarily enabled.
- Name: "ExchangeRateHost",
- Enabled: false,
- }
-
- mainConfig.ForexProviders = AllFXSettings{oldDefault, other, defaultProvider}
- err = newStorage.RunUpdater(BotOverrides{ExchangeRates: true, OpenExchangeRates: true}, &mainConfig, tempDir)
- if !errors.Is(err, nil) {
- t.Fatalf("received: '%v' but expected: '%v'", err, nil)
- }
-
- if mainConfig.ForexProviders[0].Enabled {
- t.Fatal("old default provider should not be enabled due to unset keys")
- }
-
- if mainConfig.ForexProviders[0].PrimaryProvider {
- t.Fatal("old default provider should not be a primary provider anymore")
- }
-
- if !mainConfig.ForexProviders[1].Enabled {
- t.Fatal("open exchange rates provider with keys set should be enabled")
- }
-
- if !mainConfig.ForexProviders[1].PrimaryProvider {
- t.Fatal("open exchange rates provider with keys set should be set as primary provider")
- }
-
- if mainConfig.ForexProviders[2].Enabled {
- t.Fatal("actual default provider should not be enabled")
- }
-
- if mainConfig.ForexProviders[2].PrimaryProvider {
- t.Fatal("actual default provider should not be designated as primary provider")
- }
-
- err = newStorage.Shutdown()
- if err != nil {
- t.Fatal(err)
- }
-
- // old config where two providers enabled
- oldDefault = FXSettings{
- Name: "ExchangeRates",
- Enabled: true,
- APIKey: "", // old default provider which did not need api keys.
- PrimaryProvider: true,
- }
- other = FXSettings{
- Name: "OpenExchangeRates",
- Enabled: true,
- APIKey: "", // Has no keys enabled will fall over to new default provider.
- }
-
- mainConfig.ForexProviders = AllFXSettings{oldDefault, other, defaultProvider}
- err = newStorage.RunUpdater(BotOverrides{ExchangeRates: true, OpenExchangeRates: true}, &mainConfig, tempDir)
- if !errors.Is(err, nil) {
- t.Fatalf("received: '%v' but expected: '%v'", err, nil)
- }
-
- if mainConfig.ForexProviders[0].Enabled {
- t.Fatal("old default provider should not be enabled due to unset keys")
- }
-
- if mainConfig.ForexProviders[0].PrimaryProvider {
- t.Fatal("old default provider should not be a primary provider anymore")
- }
-
- if mainConfig.ForexProviders[1].Enabled {
- t.Fatal("open exchange rates provider with keys unset should not be enabled")
- }
-
- if mainConfig.ForexProviders[1].PrimaryProvider {
- t.Fatal("open exchange rates provider with keys unset should not be set as primary provider")
- }
-
- if !mainConfig.ForexProviders[2].Enabled {
- t.Fatal("actual default provider should not be disabled")
- }
-
- if !mainConfig.ForexProviders[2].PrimaryProvider {
- t.Fatal("actual default provider should be designated as primary provider")
- }
+ // Regression test for old defaults which were enabled when in settings and nothing else was enabled and configured
+ err = newStorage.RunUpdater(BotOverrides{}, c, tempDir)
+ assert.NoError(t, err, "RunUpdater should not error")
+ assert.Nil(t, newStorage.fiatExchangeMarkets, "Forex should not be enabled with no providers") // Proxy for testing ForexEnabled
+ assert.False(t, c.ForexProviders[0].Enabled, "Old Default ExchangeRates should not have defaulted to enabled with no enabled overrides")
+}
+
+func overrideForProvider(n string) BotOverrides {
+ b := BotOverrides{}
+ switch n {
+ case "Fixer":
+ b.Fixer = true
+ case "CurrencyConverter":
+ b.CurrencyConverter = true
+ case "CurrencyLayer":
+ b.CurrencyLayer = true
+ case "OpenExchangeRates":
+ b.OpenExchangeRates = true
+ case "ExchangeRates":
+ b.ExchangeRates = true
+ }
+ return b
}
diff --git a/engine/engine.go b/engine/engine.go
index d279afa8..08bb6041 100644
--- a/engine/engine.go
+++ b/engine/engine.go
@@ -173,7 +173,6 @@ func validateSettings(b *Engine, s *Settings, flagSet FlagSet) {
flagSet.WithBool("exchangerates", &b.Settings.EnableExchangeRates, b.Config.Currency.ForexProviders.IsEnabled("exchangerates"))
flagSet.WithBool("fixer", &b.Settings.EnableFixer, b.Config.Currency.ForexProviders.IsEnabled("fixer"))
flagSet.WithBool("openexchangerates", &b.Settings.EnableOpenExchangeRates, b.Config.Currency.ForexProviders.IsEnabled("openexchangerates"))
- flagSet.WithBool("exchangeratehost", &b.Settings.EnableExchangeRateHost, b.Config.Currency.ForexProviders.IsEnabled("exchangeratehost"))
flagSet.WithBool("datahistorymanager", &b.Settings.EnableDataHistoryManager, b.Config.DataHistoryManager.Enabled)
flagSet.WithBool("currencystatemanager", &b.Settings.EnableCurrencyStateManager, b.Config.CurrencyStateManager.Enabled != nil && *b.Config.CurrencyStateManager.Enabled)
@@ -403,12 +402,11 @@ func (bot *Engine) Start() error {
ExchangeRates: bot.Settings.EnableExchangeRates,
Fixer: bot.Settings.EnableFixer,
OpenExchangeRates: bot.Settings.EnableOpenExchangeRates,
- ExchangeRateHost: bot.Settings.EnableExchangeRateHost,
},
&bot.Config.Currency,
bot.Settings.DataDir,
); err != nil {
- gctlog.Errorf(gctlog.Global, "ExchangeSettings updater system failed to start %s", err)
+ gctlog.Errorf(gctlog.Global, "Currency Converter system failed to start %s", err)
}
if bot.Settings.EnableGRPC {
@@ -677,7 +675,7 @@ func (bot *Engine) Stop() {
err = currency.ShutdownStorageUpdater()
if err != nil {
- gctlog.Errorf(gctlog.Global, "ExchangeSettings storage system. Error: %v", err)
+ gctlog.Errorf(gctlog.Global, "Currency Converter unable to stop. Error: %v", err)
}
if !bot.Settings.EnableDryRun {
diff --git a/engine/engine_types.go b/engine/engine_types.go
index 211c7140..c84b940e 100644
--- a/engine/engine_types.go
+++ b/engine/engine_types.go
@@ -80,7 +80,6 @@ type ForexSettings struct {
EnableExchangeRates bool
EnableFixer bool
EnableOpenExchangeRates bool
- EnableExchangeRateHost bool
}
// ExchangeTuningSettings defines settings related to an exchange
diff --git a/engine/sync_manager.go b/engine/sync_manager.go
index 52dd075e..22db7095 100644
--- a/engine/sync_manager.go
+++ b/engine/sync_manager.go
@@ -697,13 +697,15 @@ func printCurrencyFormat(price float64, displayCurrency currency.Code) string {
}
func printConvertCurrencyFormat(origPrice float64, origCurrency, displayCurrency currency.Code) string {
- var conv float64
+ var conv string
if origPrice > 0 {
- var err error
- conv, err = currency.ConvertFiat(origPrice, origCurrency, displayCurrency)
- if err != nil {
- log.Errorf(log.SyncMgr, "Failed to convert currency: %s", err)
+ if convFloat, err := currency.ConvertFiat(origPrice, origCurrency, displayCurrency); err != nil {
+ conv = "?.??"
+ } else {
+ conv = fmt.Sprintf("%.2f", convFloat)
}
+ } else {
+ conv = "0.00"
}
displaySymbol, err := currency.GetSymbolByCurrencyName(displayCurrency)
@@ -718,7 +720,7 @@ func printConvertCurrencyFormat(origPrice float64, origCurrency, displayCurrency
err)
}
- return fmt.Sprintf("%s%.2f %s (%s%.2f %s)",
+ return fmt.Sprintf("%s%s %s (%s%.2f %s)",
displaySymbol,
conv,
displayCurrency,
@@ -755,7 +757,8 @@ func (m *SyncManager) PrintTickerSummary(result *ticker.Price, protocol string,
return
}
- if result.Pair.Quote.IsFiatCurrency() &&
+ if currency.ForexEnabled() &&
+ result.Pair.Quote.IsFiatCurrency() &&
!result.Pair.Quote.Equal(m.fiatDisplayCurrency) &&
!m.fiatDisplayCurrency.IsEmpty() {
origCurrency := result.Pair.Quote.Upper()
@@ -854,7 +857,7 @@ func (m *SyncManager) PrintOrderbookSummary(result *orderbook.Base, protocol str
var bidValueResult, askValueResult string
switch {
- case result.Pair.Quote.IsFiatCurrency() && !result.Pair.Quote.Equal(m.fiatDisplayCurrency) && !m.fiatDisplayCurrency.IsEmpty():
+ case currency.ForexEnabled() && result.Pair.Quote.IsFiatCurrency() && !result.Pair.Quote.Equal(m.fiatDisplayCurrency) && !m.fiatDisplayCurrency.IsEmpty():
origCurrency := result.Pair.Quote.Upper()
if bidsValue > 0 {
bidValueResult = printConvertCurrencyFormat(bidsValue, origCurrency, m.fiatDisplayCurrency)
diff --git a/main.go b/main.go
index fd0900ee..a6085d4f 100644
--- a/main.go
+++ b/main.go
@@ -79,7 +79,6 @@ func main() {
flag.BoolVar(&settings.EnableExchangeRates, "exchangerates", false, "overrides config and sets up foreign exchange exchangeratesapi.io")
flag.BoolVar(&settings.EnableFixer, "fixer", false, "overrides config and sets up foreign exchange Fixer.io")
flag.BoolVar(&settings.EnableOpenExchangeRates, "openexchangerates", false, "overrides config and sets up foreign exchange Open Exchange Rates")
- flag.BoolVar(&settings.EnableExchangeRateHost, "exchangeratehost", false, "overrides config and sets up foreign exchange ExchangeRate.host")
// Exchange tuning settings
flag.BoolVar(&settings.EnableExchangeAutoPairUpdates, "exchangeautopairupdates", false, "enables automatic available currency pair updates for supported exchanges")