mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 15:09:42 +00:00
Engine: Remove Default Forex Provider (#1395)
* Currency: Do not use a default forex provider exchangerate.host now requires an API key. Instead of finding a new Free (for now) default, this change simply disables the currency exchange when nothing is enabled. * SyncManager: Report ?.?? for an unknown forex amount In a situation where we thought forex was available but we got an error, this avoids showing 0.00 when there was actually an error. * Currency: Tests for no default forex * Currency: Use mock provider for tests * Currency: Add API key to exchangerate.host * Currency: Remove Exchangerate.host Exchangerate.host was bought by apilayer, the old API deprecated, and replaced with a proxy to the apilayer api. We already have currencylayer support, so ther's no reason to keep exh. Worth noting: New ERH keys actually work on currencylayer * Currencies: Add test coverage for currency layer * fixup! Currency: Tests for no default forex Remove duplicate assignment Fixes [review comment](https://github.com/thrasher-corp/gocryptotrader/pull/1395#discussion_r1395178513) * fixup! Currency: Add API key to exchangerate.host Remove unused ErrVar Fixes [review comment](https://github.com/thrasher-corp/gocryptotrader/pull/1395#discussion_r1396647418) * fixup! Currency: Tests for no default forex Fix spelling of override in test Fixes [review comment](https://github.com/thrasher-corp/gocryptotrader/pull/1395#discussion_r1396701476) * fixup! SyncManager: Report ?.?? for an unknown forex amount Fix display of non-positive currency conversions. Fixes [review comment](https://github.com/thrasher-corp/gocryptotrader/pull/1395/files#r1398527134)
This commit is contained in:
@@ -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
|
||||
|
||||
12
README.md
12
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 |
|
||||
|
||||
@@ -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"}}
|
||||
|
||||
@@ -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}}
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -34,7 +34,6 @@ type BotOverrides struct {
|
||||
ExchangeRates bool
|
||||
Fixer bool
|
||||
OpenExchangeRates bool
|
||||
ExchangeRateHost bool
|
||||
}
|
||||
|
||||
// CoinmarketcapSettings refers to settings
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
# GoCryptoTrader package Exchangerate.host
|
||||
|
||||
<img src="/common/gctlogo.png?raw=true" width="350px" height="350px" hspace="70">
|
||||
|
||||
|
||||
[](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
|
||||
|
||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
||||
|
||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||
|
||||
***bc1qk0jareu4jytc0cfrhr5wgshsq8282awpavfahc***
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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"`
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
40
currency/mock_provider_test.go
Normal file
40
currency/mock_provider_test.go
Normal file
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -80,7 +80,6 @@ type ForexSettings struct {
|
||||
EnableExchangeRates bool
|
||||
EnableFixer bool
|
||||
EnableOpenExchangeRates bool
|
||||
EnableExchangeRateHost bool
|
||||
}
|
||||
|
||||
// ExchangeTuningSettings defines settings related to an exchange
|
||||
|
||||
@@ -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)
|
||||
|
||||
1
main.go
1
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")
|
||||
|
||||
Reference in New Issue
Block a user