Currency: Add additional functionality, refactor and improvements (#881)

* currency: Add method to derive pair

* currency: Add method to lower entire charset but used the slice copy and returned that. This will change the original, just gotta see if this is an issue, but the slice usually goes out of scope anyway.

* currency/pairs: add filter method

* currency: add function to derive select currencies from currency pairs

* currency/engine: slight adjustments

* currency: fix linter issue also shift burden of proof to caller instead of repair, more performant.

* currency: more linter

* pairs: optimize; reduce allocs/op and B/op

* currency: Add in function 'NewPairsFromString' for testing purposes

* currency: don't suppress error

* currency: stop panic on empty currency code

* currency: Add helper method to match currencies between exchanges

* currency: fixed my bad spelling

* currency: Implement stable coin checks, refactored base code methods, optimized upper and lower case strings for currency code/pairs

* currency: add pairs method to derive stable coins from internal list.

* Currency: Cleanup, fix tests.

* engine/exchanges/currency: fix whoops

* Currency: force govet no copy on Item datatype

* Currency: fix naughty linter issues

* exchange: revert change

* currency/config: fix config upgrade mistake

* currency: re-implement currency sub-systems

* *RetrieveConfigCurrencyPairs removed
*CheckCurrencyConfigValues to only provide warnings, add additional support when, disable when support is lost or not available and set default values.
*Drop Cryptocurrencies from configuration as this is not needed.
*Drop REST Poll delay field as this was unused.
*Update default values for currencyFileUpdateDuration & foreignExchangeUpdateDuration.
*Allow Role to be marshalled for file type.
*Refactor RunUpdater to verify and check config values and set default running foreign exchange provider.

* currency: cleanup

* currency: change match -> equal for comparison which is more of a standard and little easier to find

* currency: address nits

* currency: fix whoops

* currency: Add some more pairs methods

* currency: linter issues

* currency: RM unused field

* currency: rm verbose

* currency: fix word

* currency: gocritic

* currency: fix another whoopsie

* example_config: default to show log system name

* Currency: Force all support packages to use Equal method for comparison as there is a small comparison bug when checking upper and lower casing, this has a more of a pronounced impact between exchanges and client instances of currency generation

* currency: fix log name

* ordermanager: fix potential panic

* currency: small optim.

* engine: display correct bool and force shutdown

* currency: add function and fix regression
* Change ConvertCurrency -> ConvertFiat to be more precise
* ADD GetForeignExchangeRate to get specific exchange rate for fiat pair
* Fix currency display and formatting regression and tied in with config.Currency fields

* engine: fix tests

* currency: return the amount when no conversion needs to take place

* currency: reduce method name

* currency: Address nits glorious nits

* currency: fix linter

* currency: addr nits

* currency: check underlying role in test

* gct: change to EMPTYCODE and EMPTYPAIR across codebase

* currency: fix nits

* currency: this fixes test race but this issue has not been resolved. Please see: https://trello.com/c/54eizOIo/143-currency-package-upgrades

* currency: Add temp dir for testing

* Update engine/engine.go

Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>

* documentation: update and regen

* currency: Address niterinos

* currency: Add test case for config upgrade when falling over to exchange rate host as default from exchangeRates provider

* currency: addr nits

* currency: fix whoops

Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>
Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>
This commit is contained in:
Ryan O'Hara-Reid
2022-02-17 16:24:57 +11:00
committed by GitHub
parent 2f353a78f8
commit 11da520dc8
102 changed files with 5199 additions and 3095 deletions

View File

@@ -68,7 +68,7 @@ However, we welcome pull requests for any exchange which does not match this cri
+ Connection monitor package.
+ gRPC service and JSON RPC proxy. See [gRPC service](/gctrpc/README.md).
+ gRPC client. See [gctcli](/cmd/gctcli/README.md).
+ Forex currency converter packages (CurrencyConverterAPI, CurrencyLayer, Fixer.io, OpenExchangeRates).
+ Forex currency converter packages (CurrencyConverterAPI, CurrencyLayer, Exchange Rates, Fixer.io, OpenExchangeRates, Exchange Rate Host).
+ Packages for handling currency pairs, tickers and orderbooks.
+ Portfolio management tool; fetches balances from supported exchanges and allows for custom address tracking.
+ Basic event trigger system.
@@ -143,13 +143,13 @@ Binaries will be published once the codebase reaches a stable condition.
|User|Contribution Amount|
|--|--|
| [thrasher-](https://github.com/thrasher-) | 662 |
| [shazbert](https://github.com/shazbert) | 231 |
| [thrasher-](https://github.com/thrasher-) | 664 |
| [shazbert](https://github.com/shazbert) | 232 |
| [gloriousCode](https://github.com/gloriousCode) | 194 |
| [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) | 88 |
| [dependabot[bot]](https://github.com/apps/dependabot) | 50 |
| [dependabot[bot]](https://github.com/apps/dependabot) | 57 |
| [xtda](https://github.com/xtda) | 47 |
| [lrascao](https://github.com/lrascao) | 21 |
| [lrascao](https://github.com/lrascao) | 27 |
| [Rots](https://github.com/Rots) | 15 |
| [vazha](https://github.com/vazha) | 15 |
| [ydm](https://github.com/ydm) | 15 |

View File

@@ -525,19 +525,19 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange
func (bt *BackTest) loadExchangePairAssetBase(exch, base, quote, ass string) (gctexchange.IBotExchange, currency.Pair, asset.Item, error) {
e, err := bt.exchangeManager.GetExchangeByName(exch)
if err != nil {
return nil, currency.Pair{}, "", err
return nil, currency.EMPTYPAIR, "", err
}
var cp, fPair currency.Pair
cp, err = currency.NewPairFromStrings(base, quote)
if err != nil {
return nil, currency.Pair{}, "", err
return nil, currency.EMPTYPAIR, "", err
}
var a asset.Item
a, err = asset.New(ass)
if err != nil {
return nil, currency.Pair{}, "", err
return nil, currency.EMPTYPAIR, "", err
}
exchangeBase := e.GetBase()
@@ -547,7 +547,7 @@ func (bt *BackTest) loadExchangePairAssetBase(exch, base, quote, ass string) (gc
fPair, err = exchangeBase.FormatExchangeCurrency(cp, a)
if err != nil {
return nil, currency.Pair{}, "", err
return nil, currency.EMPTYPAIR, "", err
}
return e, fPair, a, nil
}

View File

@@ -320,7 +320,7 @@ func (e *Exchange) SetExchangeAssetCurrencySettings(exch string, a asset.Item, c
}
for i := range e.CurrencySettings {
if e.CurrencySettings[i].Pair == cp &&
if e.CurrencySettings[i].Pair.Equal(cp) &&
e.CurrencySettings[i].Asset == a &&
exch == e.CurrencySettings[i].Exchange {
e.CurrencySettings[i] = *c

View File

@@ -44,7 +44,7 @@ func TestReset(t *testing.T) {
func TestSetCurrency(t *testing.T) {
t.Parallel()
e := Exchange{}
e.SetExchangeAssetCurrencySettings("", "", currency.Pair{}, &Settings{})
e.SetExchangeAssetCurrencySettings("", "", currency.EMPTYPAIR, &Settings{})
if len(e.CurrencySettings) != 0 {
t.Error("expected 0")
}
@@ -264,7 +264,7 @@ func TestExecuteOrder(t *testing.T) {
d := &kline.DataFromKline{
Item: gctkline.Item{
Exchange: "",
Pair: currency.Pair{},
Pair: currency.EMPTYPAIR,
Asset: "",
Interval: 0,
Candles: []gctkline.Candle{
@@ -385,7 +385,7 @@ func TestExecuteOrderBuySellSizeLimit(t *testing.T) {
d := &kline.DataFromKline{
Item: gctkline.Item{
Exchange: "",
Pair: currency.Pair{},
Pair: currency.EMPTYPAIR,
Asset: "",
Interval: 0,
Candles: []gctkline.Candle{

View File

@@ -303,7 +303,7 @@ func TestUpdate(t *testing.T) {
func TestGetFee(t *testing.T) {
t.Parallel()
p := Portfolio{}
f := p.GetFee("", "", currency.Pair{})
f := p.GetFee("", "", currency.EMPTYPAIR)
if !f.IsZero() {
t.Error("expected 0")
}
@@ -323,7 +323,7 @@ func TestGetFee(t *testing.T) {
func TestGetComplianceManager(t *testing.T) {
t.Parallel()
p := Portfolio{}
_, err := p.GetComplianceManager("", "", currency.Pair{})
_, err := p.GetComplianceManager("", "", currency.EMPTYPAIR)
if !errors.Is(err, errNoPortfolioSettings) {
t.Errorf("received: %v, expected: %v", err, errNoPortfolioSettings)
}

View File

@@ -35,11 +35,11 @@ func CalculateFundingStatistics(funds funding.IFundingManager, currStats map[str
}
var relevantStats []relatedCurrencyPairStatistics
for k, v := range exchangeAssetStats {
if k.Base == report.Items[i].Currency {
if k.Base.Equal(report.Items[i].Currency) {
relevantStats = append(relevantStats, relatedCurrencyPairStatistics{isBaseCurrency: true, stat: v})
continue
}
if k.Quote == report.Items[i].Currency {
if k.Quote.Equal(report.Items[i].Currency) {
relevantStats = append(relevantStats, relatedCurrencyPairStatistics{stat: v})
}
}

View File

@@ -112,7 +112,7 @@ func (f *FundManager) AddUSDTrackingData(k *kline.DataFromKline) error {
}
if strings.EqualFold(f.items[i].exchange, k.Item.Exchange) &&
f.items[i].asset == k.Item.Asset {
if f.items[i].currency == k.Item.Pair.Base {
if f.items[i].currency.Equal(k.Item.Pair.Base) {
if f.items[i].usdTrackingCandles == nil &&
trackingcurrencies.CurrencyIsUSDTracked(k.Item.Pair.Quote) {
f.items[i].usdTrackingCandles = k
@@ -123,7 +123,7 @@ func (f *FundManager) AddUSDTrackingData(k *kline.DataFromKline) error {
baseSet = true
}
if trackingcurrencies.CurrencyIsUSDTracked(f.items[i].currency) {
if f.items[i].pairedWith != nil && f.items[i].currency != basePairedWith {
if f.items[i].pairedWith != nil && !f.items[i].currency.Equal(basePairedWith) {
continue
}
if f.items[i].usdTrackingCandles == nil {
@@ -267,10 +267,10 @@ func (f *FundManager) Transfer(amount decimal.Decimal, sender, receiver *Item, i
}
}
if sender.currency != receiver.currency {
if !sender.currency.Equal(receiver.currency) {
return errTransferMustBeSameCurrency
}
if sender.currency == receiver.currency &&
if sender.currency.Equal(receiver.currency) &&
sender.exchange == receiver.exchange &&
sender.asset == receiver.asset {
return fmt.Errorf("%v %v %v %w", sender.exchange, sender.asset, sender.currency, errCannotTransferToSameFunds)
@@ -336,7 +336,7 @@ func (f *FundManager) GetFundingForEvent(ev common.EventHandler) (*Pair, error)
// GetFundingForEAC This will construct a funding based on the exchange, asset, currency code
func (f *FundManager) GetFundingForEAC(exch string, a asset.Item, c currency.Code) (*Item, error) {
for i := range f.items {
if f.items[i].BasicEqual(exch, a, c, currency.Code{}) {
if f.items[i].BasicEqual(exch, a, c, currency.EMPTYCODE) {
return f.items[i], nil
}
}
@@ -514,7 +514,7 @@ func (i *Item) Equal(item *Item) bool {
if item == nil || i == nil {
return false
}
if i.currency == item.currency &&
if i.currency.Equal(item.currency) &&
i.asset == item.asset &&
i.exchange == item.exchange {
if i.pairedWith == nil && item.pairedWith == nil {
@@ -523,7 +523,7 @@ func (i *Item) Equal(item *Item) bool {
if i.pairedWith == nil || item.pairedWith == nil {
return false
}
if i.pairedWith.currency == item.pairedWith.currency &&
if i.pairedWith.currency.Equal(item.pairedWith.currency) &&
i.pairedWith.asset == item.pairedWith.asset &&
i.pairedWith.exchange == item.pairedWith.exchange {
return true
@@ -537,19 +537,19 @@ func (i *Item) BasicEqual(exch string, a asset.Item, currency, pairedCurrency cu
return i != nil &&
i.exchange == exch &&
i.asset == a &&
i.currency == currency &&
i.currency.Equal(currency) &&
(i.pairedWith == nil ||
(i.pairedWith != nil && i.pairedWith.currency == pairedCurrency))
(i.pairedWith != nil && i.pairedWith.currency.Equal(pairedCurrency)))
}
// MatchesCurrency checks that an item's currency is equal
func (i *Item) MatchesCurrency(c currency.Code) bool {
return i != nil && i.currency == c
return i != nil && i.currency.Equal(c)
}
// MatchesItemCurrency checks that an item's currency is equal
func (i *Item) MatchesItemCurrency(item *Item) bool {
return i != nil && item != nil && i.currency == item.currency
return i != nil && item != nil && i.currency.Equal(item.currency)
}
// MatchesExchange checks that an item's exchange is equal

View File

@@ -247,12 +247,12 @@ func TestAddPair(t *testing.T) {
}
if resp.Base.exchange != exch ||
resp.Base.asset != a ||
resp.Base.currency != pair.Base {
!resp.Base.currency.Equal(pair.Base) {
t.Error("woah nelly")
}
if resp.Quote.exchange != exch ||
resp.Quote.asset != a ||
resp.Quote.currency != pair.Quote {
!resp.Quote.currency.Equal(pair.Quote) {
t.Error("woah nelly")
}
if resp.Quote.pairedWith != resp.Base {
@@ -841,7 +841,7 @@ func TestMatchesCurrency(t *testing.T) {
if !i.MatchesCurrency(currency.BTC) {
t.Error("expected true")
}
if i.MatchesCurrency(currency.Code{}) {
if i.MatchesCurrency(currency.EMPTYCODE) {
t.Error("expected false")
}
if i.MatchesCurrency(currency.NewCode("")) {
@@ -855,7 +855,7 @@ func TestCreateSnapshot(t *testing.T) {
f.items = append(f.items, &Item{
exchange: "",
asset: "",
currency: currency.Code{},
currency: currency.EMPTYCODE,
initialFunds: decimal.Decimal{},
available: decimal.Decimal{},
reserved: decimal.Decimal{},

View File

@@ -121,13 +121,13 @@ func pairContainsUSD(pair currency.Pair) bool {
// this will allow for data retrieval and total tracking on backtesting runs
func findMatchingUSDPairs(pair currency.Pair, pairs *currency.PairStore) (basePair, quotePair currency.Pair, err error) {
if pairs == nil {
return currency.Pair{}, currency.Pair{}, errNilPairs
return currency.EMPTYPAIR, currency.EMPTYPAIR, errNilPairs
}
if pairContainsUSD(pair) {
return currency.Pair{}, currency.Pair{}, ErrCurrencyContainsUSD
return currency.EMPTYPAIR, currency.EMPTYPAIR, ErrCurrencyContainsUSD
}
if !pairs.Available.Contains(pair, true) {
return currency.Pair{}, currency.Pair{}, fmt.Errorf("%v %w", pair, errCurrencyNotFoundInPairs)
return currency.EMPTYPAIR, currency.EMPTYPAIR, fmt.Errorf("%v %w", pair, errCurrencyNotFoundInPairs)
}
var baseFound, quoteFound bool

View File

@@ -82,8 +82,8 @@ func TestFindMatchingUSDPairs(t *testing.T) {
description: "already has USD",
initialPair: currency.NewPair(currency.BTC, currency.USDT),
availablePairs: &currency.PairStore{Available: currency.Pairs{currency.NewPair(currency.BTC, currency.USDT)}},
basePair: currency.Pair{},
quotePair: currency.Pair{},
basePair: currency.EMPTYPAIR,
quotePair: currency.EMPTYPAIR,
expectedErr: ErrCurrencyContainsUSD,
},
{
@@ -99,14 +99,14 @@ func TestFindMatchingUSDPairs(t *testing.T) {
initialPair: currency.NewPair(currency.BTC, currency.LTC),
availablePairs: &currency.PairStore{Available: currency.Pairs{currency.NewPair(currency.BTC, currency.LTC), currency.NewPair(currency.BTC, currency.DAI)}},
basePair: currency.NewPair(currency.BTC, currency.DAI),
quotePair: currency.Pair{},
quotePair: currency.EMPTYPAIR,
expectedErr: errNoMatchingQuoteUSDFound,
},
{
description: "base currency has no matching USD pair",
initialPair: currency.NewPair(currency.BTC, currency.LTC),
availablePairs: &currency.PairStore{Available: currency.Pairs{currency.NewPair(currency.BTC, currency.LTC), currency.NewPair(currency.LTC, currency.USDT)}},
basePair: currency.Pair{},
basePair: currency.EMPTYPAIR,
quotePair: currency.NewPair(currency.LTC, currency.USDT),
expectedErr: errNoMatchingBaseUSDFound,
},
@@ -114,16 +114,16 @@ func TestFindMatchingUSDPairs(t *testing.T) {
description: "both base and quote don't have USD pairs",
initialPair: currency.NewPair(currency.BTC, currency.LTC),
availablePairs: &currency.PairStore{Available: currency.Pairs{currency.NewPair(currency.BTC, currency.LTC)}},
basePair: currency.Pair{},
quotePair: currency.Pair{},
basePair: currency.EMPTYPAIR,
quotePair: currency.EMPTYPAIR,
expectedErr: errNoMatchingPairUSDFound,
},
{
description: "currency doesnt exist in available pairs",
initialPair: currency.NewPair(currency.BTC, currency.LTC),
availablePairs: &currency.PairStore{Available: currency.Pairs{currency.NewPair(currency.BTC, currency.DOGE)}},
basePair: currency.Pair{},
quotePair: currency.Pair{},
basePair: currency.EMPTYPAIR,
quotePair: currency.EMPTYPAIR,
expectedErr: errCurrencyNotFoundInPairs,
},
}

View File

@@ -4,6 +4,7 @@
+ Currency Converter API support
+ Currency Layer support
+ Exchange Rates support
+ Fixer.io support
+ Open Exchange Rates support
+ ExchangeRate.host support

View File

@@ -69,7 +69,7 @@ However, we welcome pull requests for any exchange which does not match this cri
+ Connection monitor package.
+ gRPC service and JSON RPC proxy. See [gRPC service](/gctrpc/README.md).
+ gRPC client. See [gctcli](/cmd/gctcli/README.md).
+ Forex currency converter packages (CurrencyConverterAPI, CurrencyLayer, Fixer.io, OpenExchangeRates).
+ Forex currency converter packages (CurrencyConverterAPI, CurrencyLayer, Exchange Rates, Fixer.io, OpenExchangeRates, Exchange Rate Host).
+ Packages for handling currency pairs, tickers and orderbooks.
+ Portfolio management tool; fetches balances from supported exchanges and allows for custom address tracking.
+ Basic event trigger system.

View File

@@ -7,7 +7,7 @@ import (
)
func TestDisruptFormatting(t *testing.T) {
_, err := disruptFormatting(currency.Pair{})
_, err := disruptFormatting(currency.EMPTYPAIR)
if err == nil {
t.Fatal("error cannot be nil")
}

View File

@@ -953,10 +953,10 @@ func outputToConsole(exchangeResponses []ExchangeResponses) {
// ensure format currency pair is used throughout the code base.
func disruptFormatting(p currency.Pair) (currency.Pair, error) {
if p.Base.IsEmpty() {
return currency.Pair{}, errors.New("cannot disrupt formatting as base is not populated")
return currency.EMPTYPAIR, errors.New("cannot disrupt formatting as base is not populated")
}
if p.Quote.IsEmpty() {
return currency.Pair{}, errors.New("cannot disrupt formatting as quote is not populated")
return currency.EMPTYPAIR, errors.New("cannot disrupt formatting as quote is not populated")
}
return currency.Pair{

View File

@@ -9,13 +9,12 @@ import (
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/bitfinex"
"github.com/thrasher-corp/gocryptotrader/portfolio"
)
var (
priceMap map[currency.Code]float64
priceMap map[*currency.Item]float64
displayCurrency currency.Code
)
@@ -23,10 +22,8 @@ func printSummary(msg string, amount float64) {
log.Println()
log.Println(fmt.Sprintf("%s in USD: $%.2f", msg, amount))
if displayCurrency != currency.USD {
conv, err := currency.ConvertCurrency(amount,
currency.USD,
displayCurrency)
if !displayCurrency.Equal(currency.USD) {
conv, err := currency.ConvertFiat(amount, currency.USD, displayCurrency)
if err != nil {
log.Println(err)
} else {
@@ -50,7 +47,7 @@ func printSummary(msg string, amount float64) {
func getOnlineOfflinePortfolio(coins []portfolio.Coin, online bool) {
var totals float64
for _, x := range coins {
value := priceMap[x.Coin] * x.Balance
value := priceMap[x.Coin.Item] * x.Balance
totals += value
log.Printf("\t%v %v Subtotal: $%.2f Coin percentage: %.2f%%\n", x.Coin,
x.Balance, value, x.Percentage)
@@ -90,13 +87,7 @@ func main() {
Subtotal float64
}
err = cfg.RetrieveConfigCurrencyPairs(true, asset.Spot)
if err != nil {
log.Printf("Failed to retrieve config currency pairs %v\n", err)
os.Exit(1)
}
portfolioMap := make(map[currency.Code]PortfolioTemp)
portfolioMap := make(map[*currency.Item]PortfolioTemp)
total := float64(0)
log.Println("Fetching currency data..")
@@ -114,21 +105,21 @@ func main() {
log.Println("Fetched currency data.")
log.Println("Fetching ticker data and calculating totals..")
priceMap = make(map[currency.Code]float64)
priceMap[currency.USD] = 1
priceMap = make(map[*currency.Item]float64)
priceMap[currency.USD.Item] = 1
for _, y := range result.Totals {
pf := PortfolioTemp{}
pf.Balance = y.Balance
pf.Subtotal = 0
if y.Coin.IsDefaultFiatCurrency() {
if y.Coin != currency.USD {
conv, err := currency.ConvertCurrency(y.Balance, y.Coin, currency.USD)
if y.Coin.IsFiatCurrency() {
if !y.Coin.Equal(currency.USD) {
conv, err := currency.ConvertFiat(y.Balance, y.Coin, currency.USD)
if err != nil {
log.Println(err)
} else {
priceMap[y.Coin] = conv / y.Balance
priceMap[y.Coin.Item] = conv / y.Balance
pf.Subtotal = conv
}
} else {
@@ -143,18 +134,25 @@ func main() {
if errf != nil {
log.Println(errf)
} else {
priceMap[y.Coin] = ticker.Last
priceMap[y.Coin.Item] = ticker.Last
pf.Subtotal = ticker.Last * y.Balance
}
}
portfolioMap[y.Coin] = pf
portfolioMap[y.Coin.Item] = pf
total += pf.Subtotal
}
log.Println("Done.")
log.Println()
log.Println("PORTFOLIO TOTALS:")
for x, y := range portfolioMap {
log.Printf("\t%s Amount: %f Subtotal: $%.2f USD (1 %s = $%.2f USD). Percentage of portfolio %.3f%%", x, y.Balance, y.Subtotal, x, y.Subtotal/y.Balance, y.Subtotal/total*100/1)
code := currency.Code{Item: x}
log.Printf("\t%s Amount: %f Subtotal: $%.2f USD (1 %s = $%.2f USD). Percentage of portfolio %.3f%%",
code,
y.Balance,
y.Subtotal,
code,
y.Subtotal/y.Balance,
y.Subtotal/total*100/1)
}
printSummary("\tTotal balance", total)
@@ -170,7 +168,7 @@ func main() {
log.Printf("\t%s:", x)
totals = 0
for z := range y {
value := priceMap[x] * y[z].Balance
value := priceMap[x.Item] * y[z].Balance
totals += value
log.Printf("\t %s Amount: %f Subtotal: $%.2f Coin percentage: %.2f%%\n",
y[z].Address, y[z].Balance, value, y[z].Percentage)
@@ -183,7 +181,7 @@ func main() {
log.Printf("\t%s:", x)
totals = 0
for z, w := range y {
value := priceMap[z] * w.Balance
value := priceMap[z.Item] * w.Balance
totals += value
log.Printf("\t %s Amount: %f Subtotal $%.2f Coin percentage: %.2f%%",
z, w.Balance, value, w.Percentage)

View File

@@ -1,7 +0,0 @@
package main
import "testing"
func TestMain(t *testing.T) {
}

View File

@@ -33,7 +33,7 @@ import (
var errExchangeConfigIsNil = errors.New("exchange config is nil")
// GetCurrencyConfig returns currency configurations
func (c *Config) GetCurrencyConfig() CurrencyConfig {
func (c *Config) GetCurrencyConfig() currency.Config {
return c.Currency
}
@@ -191,7 +191,7 @@ func (c *Config) UpdateCommunicationsConfig(config *base.CommunicationsConfig) {
}
// GetCryptocurrencyProviderConfig returns the communications configuration
func (c *Config) GetCryptocurrencyProviderConfig() CryptocurrencyProvider {
func (c *Config) GetCryptocurrencyProviderConfig() currency.Provider {
m.Lock()
provider := c.Currency.CryptocurrencyProvider
m.Unlock()
@@ -199,7 +199,7 @@ func (c *Config) GetCryptocurrencyProviderConfig() CryptocurrencyProvider {
}
// UpdateCryptocurrencyProviderConfig returns the communications configuration
func (c *Config) UpdateCryptocurrencyProviderConfig(config CryptocurrencyProvider) {
func (c *Config) UpdateCryptocurrencyProviderConfig(config currency.Provider) {
m.Lock()
c.Currency.CryptocurrencyProvider = config
m.Unlock()
@@ -732,7 +732,7 @@ func (c *Config) CountEnabledExchanges() int {
}
// GetCurrencyPairDisplayConfig retrieves the currency pair display preference
func (c *Config) GetCurrencyPairDisplayConfig() *CurrencyPairFormatConfig {
func (c *Config) GetCurrencyPairDisplayConfig() *currency.PairFormat {
return c.Currency.CurrencyPairFormat
}
@@ -756,38 +756,6 @@ func (c *Config) GetExchangeConfig(name string) (*Exchange, error) {
return nil, fmt.Errorf("%s %w", name, ErrExchangeNotFound)
}
// GetForexProvider returns a forex provider configuration by its name
func (c *Config) GetForexProvider(name string) (currency.FXSettings, error) {
m.Lock()
defer m.Unlock()
for i := range c.Currency.ForexProviders {
if strings.EqualFold(c.Currency.ForexProviders[i].Name, name) {
return c.Currency.ForexProviders[i], nil
}
}
return currency.FXSettings{}, errors.New("provider not found")
}
// GetForexProviders returns a list of available forex providers
func (c *Config) GetForexProviders() []currency.FXSettings {
m.Lock()
fxProviders := c.Currency.ForexProviders
m.Unlock()
return fxProviders
}
// GetPrimaryForexProvider returns the primary forex provider
func (c *Config) GetPrimaryForexProvider() string {
m.Lock()
defer m.Unlock()
for i := range c.Currency.ForexProviders {
if c.Currency.ForexProviders[i].PrimaryProvider {
return c.Currency.ForexProviders[i].Name
}
}
return ""
}
// UpdateExchangeConfig updates exchange configurations
func (c *Config) UpdateExchangeConfig(e *Exchange) error {
m.Lock()
@@ -903,13 +871,19 @@ func (c *Config) CheckExchangeConfigValues() error {
c.Exchanges[i].EnabledPairs = nil
} else {
assets := c.Exchanges[i].CurrencyPairs.GetAssetTypes(false)
if len(assets) == 0 {
c.Exchanges[i].Enabled = false
log.Warnf(log.ConfigMgr, "%s no assets found, disabling...", c.Exchanges[i].Name)
continue
}
var atLeastOne bool
for index := range assets {
err := c.Exchanges[i].CurrencyPairs.IsAssetEnabled(assets[index])
if err != nil {
// Checks if we have an old config without the ability to
// enable disable the entire asset
if err.Error() == "cannot ascertain if asset is enabled, variable is nil" {
if errors.Is(err, currency.ErrAssetIsNil) {
// Checks if we have an old config without the ability to
// enable disable the entire asset
log.Warnf(log.ConfigMgr,
"Exchange %s: upgrading config for asset type %s and setting enabled.\n",
c.Exchanges[i].Name,
@@ -926,14 +900,6 @@ func (c *Config) CheckExchangeConfigValues() error {
}
if !atLeastOne {
if len(assets) == 0 {
c.Exchanges[i].Enabled = false
log.Warnf(log.ConfigMgr,
"%s no assets found, disabling...",
c.Exchanges[i].Name)
continue
}
// turn on an asset if all disabled
log.Warnf(log.ConfigMgr,
"%s assets disabled, turning on asset %s",
@@ -1076,100 +1042,74 @@ func (c *Config) CheckBankAccountConfig() {
banking.SetAccounts(c.BankAccounts...)
}
// CheckCurrencyConfigValues checks to see if the currency config values are correct or not
func (c *Config) CheckCurrencyConfigValues() error {
fxProviders := forexprovider.GetSupportedForexProviders()
// GetForexProviders returns a list of available forex providers
func (c *Config) GetForexProviders() []currency.FXSettings {
m.Lock()
fxProviders := c.Currency.ForexProviders
m.Unlock()
return fxProviders
}
if len(fxProviders) != len(c.Currency.ForexProviders) {
for x := range fxProviders {
_, err := c.GetForexProvider(fxProviders[x])
if err != nil {
log.Warnf(log.Global, "%s forex provider not found, adding to config..\n", fxProviders[x])
c.Currency.ForexProviders = append(c.Currency.ForexProviders, currency.FXSettings{
Name: fxProviders[x],
RESTPollingDelay: 600,
APIKey: DefaultUnsetAPIKey,
APIKeyLvl: -1,
})
}
}
}
count := 0
// GetPrimaryForexProvider returns the primary forex provider
func (c *Config) GetPrimaryForexProvider() string {
m.Lock()
defer m.Unlock()
for i := range c.Currency.ForexProviders {
if c.Currency.ForexProviders[i].Enabled {
if (c.Currency.ForexProviders[i].Name == "CurrencyConverter" || c.Currency.ForexProviders[i].Name == "ExchangeRates") &&
c.Currency.ForexProviders[i].PrimaryProvider &&
(c.Currency.ForexProviders[i].APIKey == "" ||
c.Currency.ForexProviders[i].APIKey == DefaultUnsetAPIKey) {
log.Warnf(log.Global, "%s forex provider no longer supports unset API key requests. Switching to %s FX provider..",
c.Currency.ForexProviders[i].Name, DefaultForexProviderExchangeRatesAPI)
c.Currency.ForexProviders[i].Enabled = false
c.Currency.ForexProviders[i].PrimaryProvider = false
c.Currency.ForexProviders[i].APIKey = DefaultUnsetAPIKey
c.Currency.ForexProviders[i].APIKeyLvl = -1
continue
}
if c.Currency.ForexProviders[i].APIKey == DefaultUnsetAPIKey &&
c.Currency.ForexProviders[i].Name != DefaultForexProviderExchangeRatesAPI {
log.Warnf(log.Global, "%s enabled forex provider API key not set. Please set this in your config.json file\n", c.Currency.ForexProviders[i].Name)
c.Currency.ForexProviders[i].Enabled = false
c.Currency.ForexProviders[i].PrimaryProvider = false
continue
}
if c.Currency.ForexProviders[i].PrimaryProvider {
return c.Currency.ForexProviders[i].Name
}
}
return ""
}
if c.Currency.ForexProviders[i].APIKeyLvl == -1 && c.Currency.ForexProviders[i].Name != DefaultForexProviderExchangeRatesAPI {
log.Warnf(log.Global, "%s APIKey Level not set, functions limited. Please set this in your config.json file\n",
c.Currency.ForexProviders[i].Name)
}
count++
// forexProviderExists checks to see if the provider exist.
func (c *Config) forexProviderExists(name string) bool {
for i := range c.Currency.ForexProviders {
if strings.EqualFold(c.Currency.ForexProviders[i].Name, name) {
return true
}
}
return false
}
// CheckCurrencyConfigValues checks to see if the currency config values are
// correct or not
func (c *Config) CheckCurrencyConfigValues() error {
supported := forexprovider.GetSupportedForexProviders()
for x := range supported {
if !c.forexProviderExists(supported[x]) {
log.Warnf(log.ConfigMgr, "%s forex provider not found, adding to config...\n", supported[x])
c.Currency.ForexProviders = append(c.Currency.ForexProviders,
currency.FXSettings{
Name: supported[x],
APIKey: DefaultUnsetAPIKey,
APIKeyLvl: -1,
})
}
}
if count == 0 {
for x := range c.Currency.ForexProviders {
if c.Currency.ForexProviders[x].Name == DefaultForexProviderExchangeRatesAPI {
c.Currency.ForexProviders[x].Enabled = true
c.Currency.ForexProviders[x].PrimaryProvider = true
log.Warnf(log.ConfigMgr, "No valid forex providers configured. Defaulting to %s.",
DefaultForexProviderExchangeRatesAPI)
}
for i := range c.Currency.ForexProviders {
if !common.StringDataContainsInsensitive(supported, c.Currency.ForexProviders[i].Name) {
log.Warnf(log.ConfigMgr,
"%s forex provider not supported, please remove from config.\n",
c.Currency.ForexProviders[i].Name)
c.Currency.ForexProviders[i].Enabled = false
}
}
if c.Currency.CryptocurrencyProvider == (CryptocurrencyProvider{}) {
if c.Currency.CryptocurrencyProvider == (currency.Provider{}) {
c.Currency.CryptocurrencyProvider.Name = "CoinMarketCap"
c.Currency.CryptocurrencyProvider.Enabled = false
c.Currency.CryptocurrencyProvider.Verbose = false
c.Currency.CryptocurrencyProvider.AccountPlan = DefaultUnsetAccountPlan
c.Currency.CryptocurrencyProvider.APIkey = DefaultUnsetAPIKey
c.Currency.CryptocurrencyProvider.APIKey = DefaultUnsetAPIKey
}
if c.Currency.CryptocurrencyProvider.Enabled {
if c.Currency.CryptocurrencyProvider.APIkey == "" ||
c.Currency.CryptocurrencyProvider.APIkey == DefaultUnsetAPIKey {
log.Warnln(log.ConfigMgr, "CryptocurrencyProvider enabled but api key is unset please set this in your config.json file")
}
if c.Currency.CryptocurrencyProvider.AccountPlan == "" ||
c.Currency.CryptocurrencyProvider.AccountPlan == DefaultUnsetAccountPlan {
log.Warnln(log.ConfigMgr, "CryptocurrencyProvider enabled but account plan is unset please set this in your config.json file")
}
} else {
if c.Currency.CryptocurrencyProvider.APIkey == "" {
c.Currency.CryptocurrencyProvider.APIkey = DefaultUnsetAPIKey
}
if c.Currency.CryptocurrencyProvider.AccountPlan == "" {
c.Currency.CryptocurrencyProvider.AccountPlan = DefaultUnsetAccountPlan
}
if c.Currency.CryptocurrencyProvider.APIKey == "" {
c.Currency.CryptocurrencyProvider.APIKey = DefaultUnsetAPIKey
}
if c.Currency.Cryptocurrencies.Join() == "" {
if c.Cryptocurrencies != nil {
c.Currency.Cryptocurrencies = *c.Cryptocurrencies
c.Cryptocurrencies = nil
} else {
c.Currency.Cryptocurrencies = currency.GetDefaultCryptocurrencies()
}
if c.Currency.CryptocurrencyProvider.AccountPlan == "" {
c.Currency.CryptocurrencyProvider.AccountPlan = DefaultUnsetAccountPlan
}
if c.Currency.CurrencyPairFormat == nil {
@@ -1177,7 +1117,7 @@ func (c *Config) CheckCurrencyConfigValues() error {
c.Currency.CurrencyPairFormat = c.CurrencyPairFormat
c.CurrencyPairFormat = nil
} else {
c.Currency.CurrencyPairFormat = &CurrencyPairFormatConfig{
c.Currency.CurrencyPairFormat = &currency.PairFormat{
Delimiter: "-",
Uppercase: true,
}
@@ -1198,65 +1138,16 @@ func (c *Config) CheckCurrencyConfigValues() error {
c.FiatDisplayCurrency = nil
}
return nil
}
// RetrieveConfigCurrencyPairs splits, assigns and verifies enabled currency
// pairs either cryptoCurrencies or fiatCurrencies
func (c *Config) RetrieveConfigCurrencyPairs(enabledOnly bool, assetType asset.Item) error {
cryptoCurrencies := c.Currency.Cryptocurrencies
fiatCurrencies := currency.GetFiatCurrencies()
for x := range c.Exchanges {
if !c.Exchanges[x].Enabled && enabledOnly {
continue
}
err := c.SupportsExchangeAssetType(c.Exchanges[x].Name, assetType)
if err != nil {
continue
}
baseCurrencies := c.Exchanges[x].BaseCurrencies
for y := range baseCurrencies {
if !fiatCurrencies.Contains(baseCurrencies[y]) {
fiatCurrencies = append(fiatCurrencies, baseCurrencies[y])
}
}
if c.Currency.CurrencyFileUpdateDuration <= 0 {
log.Warnf(log.ConfigMgr, "Currency file update duration invalid, defaulting to %s", currency.DefaultCurrencyFileDelay)
c.Currency.CurrencyFileUpdateDuration = currency.DefaultCurrencyFileDelay
}
for x := range c.Exchanges {
err := c.SupportsExchangeAssetType(c.Exchanges[x].Name, assetType)
if err != nil {
continue
}
var pairs []currency.Pair
if !c.Exchanges[x].Enabled && enabledOnly {
pairs, err = c.GetEnabledPairs(c.Exchanges[x].Name, assetType)
} else {
pairs, err = c.GetAvailablePairs(c.Exchanges[x].Name, assetType)
}
if err != nil {
return err
}
for y := range pairs {
if !fiatCurrencies.Contains(pairs[y].Base) &&
!cryptoCurrencies.Contains(pairs[y].Base) {
cryptoCurrencies = append(cryptoCurrencies, pairs[y].Base)
}
if !fiatCurrencies.Contains(pairs[y].Quote) &&
!cryptoCurrencies.Contains(pairs[y].Quote) {
cryptoCurrencies = append(cryptoCurrencies, pairs[y].Quote)
}
}
if c.Currency.ForeignExchangeUpdateDuration <= 0 {
log.Warnf(log.ConfigMgr, "Currency foreign exchange update duration invalid, defaulting to %s", currency.DefaultForeignExchangeDelay)
c.Currency.ForeignExchangeUpdateDuration = currency.DefaultForeignExchangeDelay
}
currency.UpdateCurrencies(fiatCurrencies, false)
currency.UpdateCurrencies(cryptoCurrencies, true)
return nil
}
@@ -1282,7 +1173,7 @@ func (c *Config) CheckLoggerConfig() error {
c.Logging.LoggerFileConfig.Rotate = convert.BoolPtr(false)
}
if c.Logging.LoggerFileConfig.MaxSize <= 0 {
log.Warnf(log.Global, "Logger rotation size invalid, defaulting to %v", log.DefaultMaxFileSize)
log.Warnf(log.ConfigMgr, "Logger rotation size invalid, defaulting to %v", log.DefaultMaxFileSize)
c.Logging.LoggerFileConfig.MaxSize = log.DefaultMaxFileSize
}
log.FileLoggingConfiguredCorrectly = true
@@ -1676,7 +1567,7 @@ func (c *Config) SaveConfigToFile(configPath string) error {
if writer != nil {
err = writer.Close()
if err != nil {
log.Error(log.Global, err)
log.Error(log.ConfigMgr, err)
}
}
}()
@@ -1786,7 +1677,7 @@ func (c *Config) CheckConfig() error {
err = c.checkGCTScriptConfig()
if err != nil {
log.Errorf(log.Global,
log.Errorf(log.ConfigMgr,
"Failed to configure gctscript, feature has been disabled: %s\n",
err)
}

View File

@@ -46,7 +46,7 @@ func TestGetNonExistentDefaultFilePathDoesNotCreateDefaultDir(t *testing.T) {
func TestGetCurrencyConfig(t *testing.T) {
t.Parallel()
cfg := &Config{
Currency: CurrencyConfig{
Currency: currency.Config{
ForeignExchangeUpdateDuration: time.Second,
},
}
@@ -361,8 +361,8 @@ func TestUpdateCommunicationsConfig(t *testing.T) {
func TestGetCryptocurrencyProviderConfig(t *testing.T) {
t.Parallel()
cfg := &Config{
Currency: CurrencyConfig{
CryptocurrencyProvider: CryptocurrencyProvider{
Currency: currency.Config{
CryptocurrencyProvider: currency.Provider{
Name: "hellomoto",
},
},
@@ -376,13 +376,13 @@ func TestGetCryptocurrencyProviderConfig(t *testing.T) {
func TestUpdateCryptocurrencyProviderConfig(t *testing.T) {
t.Parallel()
cfg := &Config{
Currency: CurrencyConfig{
CryptocurrencyProvider: CryptocurrencyProvider{
Currency: currency.Config{
CryptocurrencyProvider: currency.Provider{
Name: "hellomoto",
},
},
}
cfg.UpdateCryptocurrencyProviderConfig(CryptocurrencyProvider{Name: "SERIOUS TESTING PROCEDURE!"})
cfg.UpdateCryptocurrencyProviderConfig(currency.Provider{Name: "SERIOUS TESTING PROCEDURE!"})
if cfg.Currency.CryptocurrencyProvider.Name != "SERIOUS TESTING PROCEDURE!" {
t.Error("UpdateCurrencyProviderConfig LoadConfig error")
}
@@ -1194,8 +1194,8 @@ func TestCountEnabledExchanges(t *testing.T) {
func TestGetCurrencyPairDisplayConfig(t *testing.T) {
t.Parallel()
cfg := &Config{
Currency: CurrencyConfig{
CurrencyPairFormat: &CurrencyPairFormatConfig{
Currency: currency.Config{
CurrencyPairFormat: &currency.PairFormat{
Delimiter: "-",
Uppercase: true,
},
@@ -1241,34 +1241,11 @@ func TestGetExchangeConfig(t *testing.T) {
}
}
func TestGetForexProviderConfig(t *testing.T) {
t.Parallel()
fxr := "Fixer"
cfg := &Config{
Currency: CurrencyConfig{
ForexProviders: []currency.FXSettings{
{
Name: fxr,
},
},
},
}
_, err := cfg.GetForexProvider(fxr)
if err != nil {
t.Error("GetForexProviderConfig error", err)
}
_, err = cfg.GetForexProvider("this is not a forex provider")
if err == nil {
t.Error("GetForexProviderConfig no error for invalid provider")
}
}
func TestGetForexProviders(t *testing.T) {
t.Parallel()
fxr := "Fixer"
cfg := &Config{
Currency: CurrencyConfig{
Currency: currency.Config{
ForexProviders: []currency.FXSettings{
{
Name: fxr,
@@ -1285,7 +1262,7 @@ func TestGetPrimaryForexProvider(t *testing.T) {
t.Parallel()
fxr := "Fixer" // nolint:ifshort,nolintlint // false positive and triggers only on Windows
cfg := &Config{
Currency: CurrencyConfig{
Currency: currency.Config{
ForexProviders: []currency.FXSettings{
{
Name: fxr,
@@ -1700,52 +1677,6 @@ func TestCheckExchangeConfigValues(t *testing.T) {
}
}
func TestRetrieveConfigCurrencyPairs(t *testing.T) {
t.Parallel()
cp1 := currency.NewPair(currency.DOGE, currency.XRP)
cp2 := currency.NewPair(currency.DOGE, currency.USD)
cfg := &Config{
Exchanges: []Exchange{
{
Enabled: true,
BaseCurrencies: currency.Currencies{
currency.USD,
},
CurrencyPairs: &currency.PairsManager{
RequestFormat: nil,
ConfigFormat: nil,
UseGlobalFormat: false,
LastUpdated: 0,
Pairs: map[asset.Item]*currency.PairStore{
asset.Spot: {
AssetEnabled: convert.BoolPtr(true),
Available: currency.Pairs{cp1, cp2},
Enabled: currency.Pairs{cp1},
ConfigFormat: &currency.PairFormat{},
RequestFormat: &currency.PairFormat{},
},
},
},
},
},
}
err := cfg.RetrieveConfigCurrencyPairs(true, asset.Spot)
if err != nil {
t.Errorf(
"TestRetrieveConfigCurrencyPairs.RetrieveConfigCurrencyPairs: %s",
err.Error(),
)
}
err = cfg.RetrieveConfigCurrencyPairs(false, asset.Spot)
if err != nil {
t.Errorf(
"TestRetrieveConfigCurrencyPairs.RetrieveConfigCurrencyPairs: %s",
err.Error(),
)
}
}
func TestReadConfigFromFile(t *testing.T) {
cfg := &Config{}
err := cfg.ReadConfigFromFile(TestFile, true)
@@ -1959,14 +1890,10 @@ func TestUpdateConfig(t *testing.T) {
t.Fatalf("Error should have been thrown for invalid path")
}
newCfg.Currency.Cryptocurrencies = currency.NewCurrenciesFromStringArray([]string{""})
err = c.UpdateConfig(TestFile, &newCfg, true)
if err != nil {
t.Errorf("%s", err)
}
if c.Currency.Cryptocurrencies.Join() == "" {
t.Fatalf("Cryptocurrencies should have been repopulated")
}
}
func BenchmarkUpdateConfig(b *testing.B) {
@@ -2112,10 +2039,10 @@ func TestCheckNTPConfig(t *testing.T) {
func TestCheckCurrencyConfigValues(t *testing.T) {
t.Parallel()
cfg := &Config{
Currency: CurrencyConfig{},
Currency: currency.Config{},
}
cfg.Currency.ForexProviders = nil
cfg.Currency.CryptocurrencyProvider = CryptocurrencyProvider{}
cfg.Currency.CryptocurrencyProvider = currency.Provider{}
err := cfg.CheckCurrencyConfigValues()
if err != nil {
t.Error(err)
@@ -2123,7 +2050,7 @@ func TestCheckCurrencyConfigValues(t *testing.T) {
if cfg.Currency.ForexProviders == nil {
t.Error("Failed to populate c.Currency.ForexProviders")
}
if cfg.Currency.CryptocurrencyProvider.APIkey != DefaultUnsetAPIKey {
if cfg.Currency.CryptocurrencyProvider.APIKey != DefaultUnsetAPIKey {
t.Error("Failed to set the api key to the default key")
}
if cfg.Currency.CryptocurrencyProvider.Name != "CoinMarketCap" {
@@ -2133,34 +2060,29 @@ func TestCheckCurrencyConfigValues(t *testing.T) {
cfg.Currency.ForexProviders[0].Enabled = true
cfg.Currency.ForexProviders[0].Name = "CurrencyConverter"
cfg.Currency.ForexProviders[0].PrimaryProvider = true
cfg.Currency.Cryptocurrencies = nil
cfg.Cryptocurrencies = nil
cfg.Currency.CurrencyPairFormat = nil
cfg.CurrencyPairFormat = &CurrencyPairFormatConfig{
cfg.CurrencyPairFormat = &currency.PairFormat{
Uppercase: true,
}
cfg.Currency.FiatDisplayCurrency = currency.Code{}
cfg.Currency.FiatDisplayCurrency = currency.EMPTYCODE
cfg.FiatDisplayCurrency = &currency.BTC
cfg.Currency.CryptocurrencyProvider.Enabled = true
err = cfg.CheckCurrencyConfigValues()
if err != nil {
t.Error(err)
}
if cfg.Currency.ForexProviders[0].Enabled {
t.Error("Failed to disable invalid forex provider")
}
if !cfg.Currency.CurrencyPairFormat.Uppercase {
t.Error("Failed to apply c.CurrencyPairFormat format to c.Currency.CurrencyPairFormat")
}
cfg.Currency.CryptocurrencyProvider.Enabled = false
cfg.Currency.CryptocurrencyProvider.APIkey = ""
cfg.Currency.CryptocurrencyProvider.APIKey = ""
cfg.Currency.CryptocurrencyProvider.AccountPlan = ""
cfg.FiatDisplayCurrency = &currency.BTC
cfg.Currency.ForexProviders[0].Enabled = true
cfg.Currency.ForexProviders[0].Name = "Name"
cfg.Currency.ForexProviders[0].PrimaryProvider = true
cfg.Currency.Cryptocurrencies = currency.Currencies{}
cfg.Cryptocurrencies = &currency.Currencies{}
err = cfg.CheckCurrencyConfigValues()
if err != nil {
@@ -2169,7 +2091,7 @@ func TestCheckCurrencyConfigValues(t *testing.T) {
if cfg.FiatDisplayCurrency != nil {
t.Error("Failed to clear c.FiatDisplayCurrency")
}
if cfg.Currency.CryptocurrencyProvider.APIkey != DefaultUnsetAPIKey ||
if cfg.Currency.CryptocurrencyProvider.APIKey != DefaultUnsetAPIKey ||
cfg.Currency.CryptocurrencyProvider.AccountPlan != DefaultUnsetAccountPlan {
t.Error("Failed to set CryptocurrencyProvider.APIkey and AccountPlan")
}

View File

@@ -86,7 +86,7 @@ type Config struct {
Profiler Profiler `json:"profiler"`
NTPClient NTPClientConfig `json:"ntpclient"`
GCTScript gctscript.Config `json:"gctscript"`
Currency CurrencyConfig `json:"currencyConfig"`
Currency currency.Config `json:"currencyConfig"`
Communications base.CommunicationsConfig `json:"communications"`
RemoteControl RemoteControlConfig `json:"remoteControl"`
Portfolio portfolio.Base `json:"portfolioAddresses"`
@@ -94,11 +94,11 @@ type Config struct {
BankAccounts []banking.Account `json:"bankAccounts"`
// Deprecated config settings, will be removed at a future date
Webserver *WebserverConfig `json:"webserver,omitempty"`
CurrencyPairFormat *CurrencyPairFormatConfig `json:"currencyPairFormat,omitempty"`
FiatDisplayCurrency *currency.Code `json:"fiatDispayCurrency,omitempty"`
Cryptocurrencies *currency.Currencies `json:"cryptocurrencies,omitempty"`
SMS *base.SMSGlobalConfig `json:"smsGlobal,omitempty"`
Webserver *WebserverConfig `json:"webserver,omitempty"`
CurrencyPairFormat *currency.PairFormat `json:"currencyPairFormat,omitempty"`
FiatDisplayCurrency *currency.Code `json:"fiatDispayCurrency,omitempty"`
Cryptocurrencies *currency.Currencies `json:"cryptocurrencies,omitempty"`
SMS *base.SMSGlobalConfig `json:"smsGlobal,omitempty"`
// encryption session values
storedSalt []byte
sessionDK []byte
@@ -249,26 +249,6 @@ type BankTransaction struct {
PaymentInstructions string `json:"paymentInstructions"`
}
// CurrencyConfig holds all the information needed for currency related manipulation
type CurrencyConfig struct {
ForexProviders []currency.FXSettings `json:"forexProviders"`
CryptocurrencyProvider CryptocurrencyProvider `json:"cryptocurrencyProvider"`
Cryptocurrencies currency.Currencies `json:"cryptocurrencies"`
CurrencyPairFormat *CurrencyPairFormatConfig `json:"currencyPairFormat"`
FiatDisplayCurrency currency.Code `json:"fiatDisplayCurrency"`
CurrencyFileUpdateDuration time.Duration `json:"currencyFileUpdateDuration"`
ForeignExchangeUpdateDuration time.Duration `json:"foreignExchangeUpdateDuration"`
}
// CryptocurrencyProvider defines coinmarketcap tools
type CryptocurrencyProvider struct {
Name string `json:"name"`
Enabled bool `json:"enabled"`
Verbose bool `json:"verbose"`
APIkey string `json:"apiKey"`
AccountPlan string `json:"accountPlan"`
}
// FeaturesSupportedConfig stores the exchanges supported features
type FeaturesSupportedConfig struct {
REST bool `json:"restAPI"`

View File

@@ -26,7 +26,7 @@
"maxsize": 250
},
"advancedSettings": {
"showLogSystemName": false,
"showLogSystemName": true,
"spacer": " | ",
"timeStampFormat": "02/01/2006 15:04:05",
"headers": {

View File

@@ -8,10 +8,21 @@ import (
"unicode"
)
var (
// ErrCurrencyCodeEmpty defines an error if the currency code is empty
ErrCurrencyCodeEmpty = errors.New("currency code is empty")
errItemIsNil = errors.New("item is nil")
errItemIsEmpty = errors.New("item is empty")
errRoleUnset = errors.New("role unset")
// EMPTYCODE is an empty currency code
EMPTYCODE = Code{}
// EMPTYPAIR is an empty currency pair
EMPTYPAIR = Pair{}
)
func (r Role) String() string {
switch r {
case Unset:
return UnsetRoleString
case Fiat:
return FiatCurrencyString
case Cryptocurrency:
@@ -20,8 +31,10 @@ func (r Role) String() string {
return TokenString
case Contract:
return ContractString
case Stable:
return StableString
default:
return "UNKNOWN"
return UnsetRoleString
}
}
@@ -49,6 +62,8 @@ func (r *Role) UnmarshalJSON(d []byte) error {
*r = Token
case ContractString:
*r = Contract
case StableString:
*r = Stable
default:
return fmt.Errorf("unmarshal error role type %s unsupported for currency",
incoming)
@@ -69,20 +84,21 @@ func (b *BaseCodes) GetFullCurrencyData() (File, error) {
for i := range b.Items {
switch b.Items[i].Role {
case Unset:
file.UnsetCurrency = append(file.UnsetCurrency, *b.Items[i])
file.UnsetCurrency = append(file.UnsetCurrency, b.Items[i])
case Fiat:
file.FiatCurrency = append(file.FiatCurrency, *b.Items[i])
file.FiatCurrency = append(file.FiatCurrency, b.Items[i])
case Cryptocurrency:
file.Cryptocurrency = append(file.Cryptocurrency, *b.Items[i])
file.Cryptocurrency = append(file.Cryptocurrency, b.Items[i])
case Token:
file.Token = append(file.Token, *b.Items[i])
file.Token = append(file.Token, b.Items[i])
case Contract:
file.Contracts = append(file.Contracts, *b.Items[i])
file.Contracts = append(file.Contracts, b.Items[i])
case Stable:
file.Stable = append(file.Stable, b.Items[i])
default:
return file, errors.New("role undefined")
}
}
file.LastMainUpdate = b.LastMainUpdate.Unix()
return file, nil
}
@@ -90,12 +106,10 @@ func (b *BaseCodes) GetFullCurrencyData() (File, error) {
// GetCurrencies gets the full currency list from the base code type available
// from the currency system
func (b *BaseCodes) GetCurrencies() Currencies {
var currencies Currencies
b.mtx.Lock()
currencies := make(Currencies, len(b.Items))
for i := range b.Items {
currencies = append(currencies, Code{
Item: b.Items[i],
})
currencies[i] = Code{Item: b.Items[i]}
}
b.mtx.Unlock()
return currencies
@@ -104,31 +118,27 @@ func (b *BaseCodes) GetCurrencies() Currencies {
// UpdateCurrency updates or registers a currency/contract
func (b *BaseCodes) UpdateCurrency(fullName, symbol, blockchain string, id int, r Role) error {
if r == Unset {
return fmt.Errorf("role cannot be unset in update currency for %s", symbol)
return fmt.Errorf("cannot update currency %w for %s", errRoleUnset, symbol)
}
b.mtx.Lock()
defer b.mtx.Unlock()
for i := range b.Items {
if b.Items[i].Symbol != symbol {
if b.Items[i].Symbol != symbol || (b.Items[i].Role != Unset && b.Items[i].Role != r) {
continue
}
if b.Items[i].Role == Unset {
if fullName != "" {
b.Items[i].FullName = fullName
}
if r != Unset {
b.Items[i].Role = r
}
if blockchain != "" {
b.Items[i].AssocChain = blockchain
}
if id != 0 {
b.Items[i].ID = id
return nil
}
if b.Items[i].Role != r {
// Captures same name currencies and duplicates to different roles
break
}
b.Items[i].FullName = fullName
b.Items[i].AssocChain = blockchain
b.Items[i].ID = id
return nil
}
@@ -142,75 +152,70 @@ func (b *BaseCodes) UpdateCurrency(fullName, symbol, blockchain string, id int,
return nil
}
// Register registers a currency from a string and returns a currency code
func (b *BaseCodes) Register(c string) Code {
var format bool
if c != "" {
format = unicode.IsUpper(rune(c[0]))
// Register registers a currency from a string and returns a currency code, this
// can optionally include a role when it is known.
func (b *BaseCodes) Register(c string, newRole Role) Code {
if c == "" {
return EMPTYCODE
}
var format bool
// Digits fool upper and lower casing. So find first letter and check case.
for x := range c {
if !unicode.IsDigit(rune(c[x])) {
format = unicode.IsUpper(rune(c[x]))
break
}
}
// Force upper string storage and matching
c = strings.ToUpper(c)
b.mtx.Lock()
defer b.mtx.Unlock()
for i := range b.Items {
if b.Items[i].Symbol == c {
return Code{
Item: b.Items[i],
UpperCase: format,
}
if b.Items[i].Symbol != c {
continue
}
}
newItem := &Item{Symbol: c}
b.Items = append(b.Items, newItem)
return Code{
Item: newItem,
UpperCase: format,
}
}
// RegisterFiat registers a fiat currency from a string and returns a currency
// code
func (b *BaseCodes) RegisterFiat(c string) Code {
c = strings.ToUpper(c)
b.mtx.Lock()
defer b.mtx.Unlock()
for i := range b.Items {
if b.Items[i].Symbol == c {
if newRole != Unset {
if b.Items[i].Role == Unset {
b.Items[i].Role = Fiat
}
if b.Items[i].Role != Fiat {
b.Items[i].Role = newRole
} else if b.Items[i].Role != newRole {
// This will duplicate item with same name but different role.
// TODO: This will need a specific update to NewCode to add in
// a specific param to find the exact name and role.
continue
}
return Code{Item: b.Items[i], UpperCase: true}
}
}
item := &Item{Symbol: c, Role: Fiat}
b.Items = append(b.Items, item)
return Code{Item: item, UpperCase: true}
return Code{Item: b.Items[i], UpperCase: format}
}
newItem := &Item{Symbol: c, Lower: strings.ToLower(c), Role: newRole}
b.Items = append(b.Items, newItem)
return Code{Item: newItem, UpperCase: format}
}
// LoadItem sets item data
func (b *BaseCodes) LoadItem(item *Item) error {
if item == nil {
return errItemIsNil
}
if *item == (Item{}) {
return errItemIsEmpty
}
item.Symbol = strings.ToUpper(item.Symbol)
item.Lower = strings.ToLower(item.Symbol)
b.mtx.Lock()
defer b.mtx.Unlock()
for i := range b.Items {
if b.Items[i].Symbol != item.Symbol ||
(b.Items[i].Role != Unset &&
item.Role != Unset &&
b.Items[i].Role != item.Role) {
(b.Items[i].Role != Unset && item.Role != Unset && b.Items[i].Role != item.Role) {
continue
}
b.Items[i].AssocChain = item.AssocChain
b.Items[i].ID = item.ID
b.Items[i].Role = item.Role
b.Items[i].FullName = item.FullName
return nil
}
b.Items = append(b.Items, item)
@@ -224,7 +229,7 @@ func NewCode(c string) Code {
// String conforms to the stringer interface
func (i *Item) String() string {
return i.FullName
return i.Symbol
}
// String converts the code to string
@@ -232,11 +237,10 @@ func (c Code) String() string {
if c.Item == nil {
return ""
}
if c.UpperCase {
return strings.ToUpper(c.Item.Symbol)
return c.Item.Symbol
}
return strings.ToLower(c.Item.Symbol)
return c.Item.Lower
}
// Lower converts the code to lowercase formatting
@@ -272,35 +276,27 @@ func (c Code) MarshalJSON() ([]byte, error) {
// IsEmpty returns true if the code is empty
func (c Code) IsEmpty() bool {
if c.Item == nil {
return true
}
return c.Item.Symbol == ""
return c.Item == nil || c.Item.Symbol == ""
}
// Match returns if the code supplied is the same as the corresponding code
func (c Code) Match(check Code) bool {
// Equal returns if the code supplied is the same as the corresponding code
func (c Code) Equal(check Code) bool {
return c.Item == check.Item
}
// IsDefaultFiatCurrency checks if the currency passed in matches the default
// fiat currency
func (c Code) IsDefaultFiatCurrency() bool {
return storage.IsDefaultCurrency(c)
}
// IsDefaultCryptocurrency checks if the currency passed in matches the default
// cryptocurrency
func (c Code) IsDefaultCryptocurrency() bool {
return storage.IsDefaultCryptocurrency(c)
}
// IsFiatCurrency checks if the currency passed is an enabled fiat currency
func (c Code) IsFiatCurrency() bool {
return storage.IsFiatCurrency(c)
return c.Item != nil && c.Item.Role == Fiat
}
// IsCryptocurrency checks if the currency passed is an enabled CRYPTO currency.
// NOTE: All unset currencies will default to cryptocurrencies and stable coins
// are cryptocurrencies as well.
func (c Code) IsCryptocurrency() bool {
return storage.IsCryptocurrency(c)
return c.Item != nil && c.Item.Role&(Cryptocurrency|Stable) == c.Item.Role
}
// IsStableCurrency checks if the currency is a stable currency.
func (c Code) IsStableCurrency() bool {
return c.Item != nil && c.Item.Role == Stable
}

View File

@@ -2,6 +2,7 @@ package currency
import (
"encoding/json"
"errors"
"testing"
)
@@ -38,7 +39,7 @@ func TestRoleString(t *testing.T) {
var random Role = 1 << 7
if random.String() != "UNKNOWN" {
if random.String() != UnsetRoleString {
t.Errorf("Role String() error expected %s but received %s",
"UNKNOWN",
random)
@@ -51,7 +52,7 @@ func TestRoleMarshalJSON(t *testing.T) {
t.Error("Role MarshalJSON() error", err)
}
if expected := `"fiatCurrency"`; string(d) != expected {
if expected := `"fiatcurrency"`; string(d) != expected {
t.Errorf("Role MarshalJSON() error expected %s but received %s",
expected,
string(d))
@@ -66,6 +67,7 @@ func TestRoleUnmarshalJSON(t *testing.T) {
RoleThree Role `json:"RoleThree"`
RoleFour Role `json:"RoleFour"`
RoleFive Role `json:"RoleFive"`
RoleSix Role `json:"RoleSix"`
RoleUnknown Role `json:"RoleUnknown"`
}
@@ -75,6 +77,7 @@ func TestRoleUnmarshalJSON(t *testing.T) {
RoleThree: Fiat,
RoleFour: Token,
RoleFive: Contract,
RoleSix: Stable,
}
e, err := json.Marshal(1337)
@@ -138,6 +141,27 @@ func TestRoleUnmarshalJSON(t *testing.T) {
if err == nil {
t.Error("Expected unmarshall error")
}
err = unhandled.UnmarshalJSON([]byte(`1336`))
if err == nil {
t.Error("Expected unmarshall error")
}
}
func (b *BaseCodes) assertRole(t *testing.T, c Code, r Role) {
t.Helper()
b.mtx.Lock()
defer b.mtx.Unlock()
for x := range b.Items {
if b.Items[x] != c.Item {
continue
}
if b.Items[x].Role != r {
t.Fatal("unexpected role")
}
return
}
t.Fatal("code pointer not found")
}
func TestBaseCode(t *testing.T) {
@@ -147,35 +171,66 @@ func TestBaseCode(t *testing.T) {
main.HasData())
}
catsCode := main.Register("CATS")
catsUnset := main.Register("CATS", Unset)
main.assertRole(t, catsUnset, Unset)
if !main.HasData() {
t.Errorf("BaseCode HasData() error expected true but received %v",
main.HasData())
}
if !main.Register("CATS").Match(catsCode) {
// Changes unset to fiat
catsFiat := main.Register("CATS", Fiat)
main.assertRole(t, catsUnset, Fiat)
// Register as unset, will return first match.
otherFiatCat := main.Register("CATS", Unset)
main.assertRole(t, otherFiatCat, Fiat)
if !otherFiatCat.Equal(catsFiat) {
t.Errorf("BaseCode Match() error expected true but received %v",
false)
}
if main.Register("DOGS").Match(catsCode) {
// Register as fiat, will return fiat match.
thatOtherFiatCat := main.Register("CATS", Fiat)
main.assertRole(t, otherFiatCat, Fiat)
if !thatOtherFiatCat.Equal(catsFiat) {
t.Errorf("BaseCode Match() error expected true but received %v",
false)
}
// Register as stable, will return a different currency with the same
// currency code.
superStableCatNoShakes := main.Register("CATS", Stable)
main.assertRole(t, superStableCatNoShakes, Stable)
if superStableCatNoShakes.Equal(catsFiat) {
t.Errorf("BaseCode Match() error expected true but received %v",
true)
}
// Due to the role being unset originally, this will be set to Fiat when
// explicitly set.
if !catsUnset.Equal(catsFiat) {
t.Fatal("both should be the same")
}
if main.Register("DOGS", Unset).Equal(catsUnset) {
t.Errorf("BaseCode Match() error expected false but received %v",
true)
}
loadedCurrencies := main.GetCurrencies()
if loadedCurrencies.Contains(main.Register("OWLS")) {
if loadedCurrencies.Contains(main.Register("OWLS", Unset)) {
t.Errorf("BaseCode Contains() error expected false but received %v",
true)
}
if !loadedCurrencies.Contains(catsCode) {
if !loadedCurrencies.Contains(catsFiat) {
t.Errorf("BaseCode Contains() error expected true but received %v",
false)
}
main.Register("XBTUSD")
main.Register("XBTUSD", Unset)
err := main.UpdateCurrency("Bitcoin Perpetual",
"XBTUSD",
@@ -186,19 +241,24 @@ func TestBaseCode(t *testing.T) {
t.Fatal(err)
}
main.Register("BTC")
main.Register("BTC", Unset)
err = main.UpdateCurrency("Bitcoin", "BTC", "", 1337, Unset)
if !errors.Is(err, errRoleUnset) {
t.Fatalf("received: '%v' but expected: '%v'", err, errRoleUnset)
}
err = main.UpdateCurrency("Bitcoin", "BTC", "", 1337, Cryptocurrency)
if err != nil {
t.Fatal(err)
}
main.Register("AUD")
aud := main.Register("AUD", Unset)
err = main.UpdateCurrency("Unreal Dollar", "AUD", "", 1111, Fiat)
if err != nil {
t.Fatal(err)
}
if main.Items[5].FullName != "Unreal Dollar" {
if aud.Item.FullName != "Unreal Dollar" {
t.Error("Expected fullname to update for AUD")
}
@@ -207,22 +267,22 @@ func TestBaseCode(t *testing.T) {
t.Fatal(err)
}
main.Items[5].Role = Unset
aud.Item.Role = Unset
err = main.UpdateCurrency("Australian Dollar", "AUD", "", 1336, Fiat)
if err != nil {
t.Fatal(err)
}
if main.Items[5].Role != Fiat {
if aud.Item.Role != Fiat {
t.Error("Expected role to change to Fiat")
}
main.Register("PPT")
main.Register("PPT", Unset)
err = main.UpdateCurrency("Populous", "PPT", "ETH", 1335, Token)
if err != nil {
t.Fatal(err)
}
contract := main.Register("XBTUSD")
contract := main.Register("XBTUSD", Unset)
if contract.IsFiatCurrency() {
t.Errorf("BaseCode IsFiatCurrency() error expected false but received %v",
@@ -230,18 +290,28 @@ func TestBaseCode(t *testing.T) {
}
if contract.IsCryptocurrency() {
t.Errorf("BaseCode IsFiatCurrency() error expected false but received %v",
t.Errorf("BaseCode IsCryptocurrency() error expected false but received %v",
true)
}
if contract.IsDefaultFiatCurrency() {
t.Errorf("BaseCode IsDefaultFiatCurrency() error expected false but received %v",
true)
err = main.LoadItem(nil)
if !errors.Is(err, errItemIsNil) {
t.Fatalf("received: '%v' but expected: '%v'", err, errItemIsNil)
}
if contract.IsDefaultFiatCurrency() {
t.Errorf("BaseCode IsFiatCurrency() error expected false but received %v",
true)
err = main.LoadItem(&Item{})
if !errors.Is(err, errItemIsEmpty) {
t.Fatalf("received: '%v' but expected: '%v'", err, errItemIsEmpty)
}
err = main.LoadItem(&Item{
ID: 0,
FullName: "Cardano",
Role: Cryptocurrency,
Symbol: "ADA",
})
if err != nil {
t.Fatal(err)
}
err = main.LoadItem(&Item{
@@ -269,7 +339,7 @@ func TestBaseCode(t *testing.T) {
len(full.Cryptocurrency))
}
if len(full.FiatCurrency) != 1 {
if len(full.FiatCurrency) != 2 {
t.Errorf("BaseCode GetFullCurrencyData() error expected 1 but received %v",
len(full.FiatCurrency))
}
@@ -279,7 +349,7 @@ func TestBaseCode(t *testing.T) {
len(full.Token))
}
if len(full.UnsetCurrency) != 3 {
if len(full.UnsetCurrency) != 2 {
t.Errorf("BaseCode GetFullCurrencyData() error expected 3 but received %v",
len(full.UnsetCurrency))
}
@@ -304,24 +374,25 @@ func TestBaseCode(t *testing.T) {
}
main.Items[0].FullName = "Hello"
err = main.UpdateCurrency("MEWOW", "CATS", "", 1338, Cryptocurrency)
err = main.UpdateCurrency("MEWOW", "CATS", "", 1338, Fiat)
if err != nil {
t.Fatal(err)
}
if main.Items[0].FullName != "MEWOW" {
t.Error("Fullname not updated")
}
err = main.UpdateCurrency("MEWOW", "CATS", "", 1338, Cryptocurrency)
err = main.UpdateCurrency("MEWOW", "CATS", "", 1338, Fiat)
if err != nil {
t.Fatal(err)
}
err = main.UpdateCurrency("WOWCATS", "CATS", "", 3, Token)
err = main.UpdateCurrency("WOWCATS", "CATS", "", 3, Fiat)
if err != nil {
t.Fatal(err)
}
// Creates a new item under a different currency role
if main.Items[9].ID != 3 {
if main.Items[0].ID != 3 {
t.Error("ID not updated")
}
@@ -381,6 +452,15 @@ func TestCodeUnmarshalJSON(t *testing.T) {
expected,
unmarshalHere)
}
encoded, err = json.Marshal(1336) // :'(
if err != nil {
t.Fatal(err)
}
err = json.Unmarshal(encoded, &unmarshalHere)
if err == nil {
t.Fatal("expected error")
}
}
func TestCodeMarshalJSON(t *testing.T) {
@@ -406,7 +486,7 @@ func TestCodeMarshalJSON(t *testing.T) {
quickstruct = struct {
Codey Code `json:"sweetCodes"`
}{
Codey: Code{}, // nil code
Codey: EMPTYCODE, // nil code
}
encoded, err = json.Marshal(quickstruct)
@@ -421,37 +501,11 @@ func TestCodeMarshalJSON(t *testing.T) {
}
}
func TestIsDefaultCurrency(t *testing.T) {
if !USD.IsDefaultFiatCurrency() {
t.Errorf("TestIsDefaultCurrency Cannot match currency %s.",
USD)
}
if !AUD.IsDefaultFiatCurrency() {
t.Errorf("TestIsDefaultCurrency Cannot match currency, %s.",
AUD)
}
if LTC.IsDefaultFiatCurrency() {
t.Errorf("TestIsDefaultCurrency Function return is incorrect with, %s.",
LTC)
}
}
func TestIsDefaultCryptocurrency(t *testing.T) {
if !BTC.IsDefaultCryptocurrency() {
t.Errorf("TestIsDefaultCryptocurrency cannot match currency, %s.",
BTC)
}
if !LTC.IsDefaultCryptocurrency() {
t.Errorf("TestIsDefaultCryptocurrency cannot match currency, %s.",
LTC)
}
if AUD.IsDefaultCryptocurrency() {
t.Errorf("TestIsDefaultCryptocurrency function return is incorrect with, %s.",
AUD)
}
}
func TestIsFiatCurrency(t *testing.T) {
if EMPTYCODE.IsFiatCurrency() {
t.Errorf("TestIsFiatCurrency cannot match currency, %s.",
EMPTYCODE)
}
if !USD.IsFiatCurrency() {
t.Errorf(
"TestIsFiatCurrency cannot match currency, %s.", USD)
@@ -465,31 +519,74 @@ func TestIsFiatCurrency(t *testing.T) {
"TestIsFiatCurrency cannot match currency, %s.", LINO,
)
}
if USDT.IsFiatCurrency() {
t.Errorf(
"TestIsFiatCurrency cannot match currency, %s.", USD)
}
if DAI.IsFiatCurrency() {
t.Errorf(
"TestIsFiatCurrency cannot match currency, %s.", USD)
}
}
func TestIsCryptocurrency(t *testing.T) {
if EMPTYCODE.IsCryptocurrency() {
t.Errorf("TestIsCryptocurrency cannot match currency, %s.",
EMPTYCODE)
}
if !BTC.IsCryptocurrency() {
t.Errorf("TestIsFiatCurrency cannot match currency, %s.",
t.Errorf("TestIsCryptocurrency cannot match currency, %s.",
BTC)
}
if !LTC.IsCryptocurrency() {
t.Errorf("TestIsFiatCurrency cannot match currency, %s.",
t.Errorf("TestIsCryptocurrency cannot match currency, %s.",
LTC)
}
if AUD.IsCryptocurrency() {
t.Errorf("TestIsFiatCurrency cannot match currency, %s.",
t.Errorf("TestIsCryptocurrency cannot match currency, %s.",
AUD)
}
if !USDT.IsCryptocurrency() {
t.Errorf(
"TestIsCryptocurrency cannot match currency, %s.", USD)
}
if !DAI.IsCryptocurrency() {
t.Errorf(
"TestIsCryptocurrency cannot match currency, %s.", USD)
}
}
func TestIsStableCurrency(t *testing.T) {
if EMPTYCODE.IsStableCurrency() {
t.Errorf("TestIsStableCurrency cannot match currency, %s.", EMPTYCODE)
}
if BTC.IsStableCurrency() {
t.Errorf("TestIsStableCurrency cannot match currency, %s.", BTC)
}
if LTC.IsStableCurrency() {
t.Errorf("TestIsStableCurrency cannot match currency, %s.", LTC)
}
if AUD.IsStableCurrency() {
t.Errorf("TestIsStableCurrency cannot match currency, %s.", AUD)
}
if !USDT.IsStableCurrency() {
t.Errorf("TestIsStableCurrency cannot match currency, %s.", USDT)
}
if !DAI.IsStableCurrency() {
t.Errorf("TestIsStableCurrency cannot match currency, %s.", DAI)
}
}
func TestItemString(t *testing.T) {
expected := "Hello,World"
newItem := Item{
FullName: expected,
ID: 1337,
FullName: "Hello,World",
Symbol: "HWORLD",
AssocChain: "Silly",
}
if newItem.String() != expected {
t.Errorf("Currency String() error expected %s but received %s",
if expected := "HWORLD"; newItem.String() != expected {
t.Errorf("Currency String() error expected '%s' but received '%s'",
expected,
&newItem)
}

File diff suppressed because it is too large Load Diff

View File

@@ -19,6 +19,16 @@ import (
"github.com/thrasher-corp/gocryptotrader/log"
)
// NewFromSettings returns a new coin market cap instance with supplied settings
func NewFromSettings(cfg Settings) (*Coinmarketcap, error) {
c := &Coinmarketcap{}
c.SetDefaults()
if err := c.Setup(cfg); err != nil {
return nil, err
}
return c, nil
}
// SetDefaults sets default values for the exchange
func (c *Coinmarketcap) SetDefaults() {
c.Name = "CoinMarketCap"
@@ -41,7 +51,7 @@ func (c *Coinmarketcap) Setup(conf Settings) error {
c.Enabled = true
c.Verbose = conf.Verbose
c.APIkey = conf.APIkey
c.APIkey = conf.APIKey
return c.SetAccountPlan(conf.AccountPlan)
}
@@ -714,7 +724,7 @@ func (c *Coinmarketcap) SetAccountPlan(s string) error {
case "enterprise":
c.Plan = Enterprise
default:
log.Warnf(log.Global, "account plan %s not found, defaulting to basic", s)
log.Warnf(log.Currency, "account plan %s not found, defaulting to basic", s)
c.Plan = Basic
}
return nil

View File

@@ -36,7 +36,7 @@ func TestSetup(t *testing.T) {
c.SetDefaults()
cfg := Settings{}
cfg.APIkey = apikey
cfg.APIKey = apikey
cfg.AccountPlan = apiAccountPlanLevel
cfg.Enabled = true
cfg.AccountPlan = "basic"

View File

@@ -70,7 +70,7 @@ type Settings struct {
Name string `json:"name"`
Enabled bool `json:"enabled"`
Verbose bool `json:"verbose"`
APIkey string `json:"apiKey"`
APIKey string `json:"apiKey"`
AccountPlan string `json:"accountPlan"`
}

View File

@@ -19,9 +19,6 @@ type ConversionRates struct {
func (c *ConversionRates) HasData() bool {
c.mtx.Lock()
defer c.mtx.Unlock()
if c.m == nil {
return false
}
return len(c.m) != 0
}
@@ -105,7 +102,7 @@ func (c *ConversionRates) Update(m map[string]float64) error {
log.Debugln(log.Global, "Conversion rates are being updated.")
}
solidvalues := make(map[Code]map[Code]float64)
solidvalues := make(map[*Item]map[*Item]float64)
var list []Code // Verification list, cross check all currencies coming in
@@ -113,26 +110,26 @@ func (c *ConversionRates) Update(m map[string]float64) error {
for key, val := range m {
code1 := storage.ValidateFiatCode(key[:3])
if mainBaseCurrency == (Code{}) {
if mainBaseCurrency.Equal(EMPTYCODE) {
mainBaseCurrency = code1
}
code2 := storage.ValidateFiatCode(key[3:])
if code1 == code2 { // Get rid of same conversions
if code1.Equal(code2) { // Get rid of same conversions
continue
}
var codeOneFound, codeTwoFound bool
// Check and add to our funky list
for i := range list {
if list[i] == code1 {
if list[i].Equal(code1) {
codeOneFound = true
if codeTwoFound {
break
}
}
if list[i] == code2 {
if list[i].Equal(code2) {
codeTwoFound = true
if codeOneFound {
break
@@ -148,39 +145,39 @@ func (c *ConversionRates) Update(m map[string]float64) error {
list = append(list, code2)
}
if solidvalues[code1] == nil {
solidvalues[code1] = make(map[Code]float64)
if solidvalues[code1.Item] == nil {
solidvalues[code1.Item] = make(map[*Item]float64)
}
solidvalues[code1][code2] = val
solidvalues[code1.Item][code2.Item] = val
// Input inverse values 1/val to swap from -> to and vice versa
if solidvalues[code2] == nil {
solidvalues[code2] = make(map[Code]float64)
if solidvalues[code2.Item] == nil {
solidvalues[code2.Item] = make(map[*Item]float64)
}
solidvalues[code2][code1] = 1 / val
solidvalues[code2.Item][code1.Item] = 1 / val
}
for _, base := range list {
for _, term := range list {
if base == term {
if base.Equal(term) {
continue
}
_, ok := solidvalues[base][term]
_, ok := solidvalues[base.Item][term.Item]
if !ok {
var crossRate float64
// Check inversion to speed things up
v, ok := solidvalues[term][base]
v, ok := solidvalues[term.Item][base.Item]
if !ok {
v1, ok := solidvalues[mainBaseCurrency][base]
v1, ok := solidvalues[mainBaseCurrency.Item][base.Item]
if !ok {
return fmt.Errorf("value not found base %s term %s",
mainBaseCurrency,
base)
}
v2, ok := solidvalues[mainBaseCurrency][term]
v2, ok := solidvalues[mainBaseCurrency.Item][term.Item]
if !ok {
return fmt.Errorf("value not found base %s term %s",
mainBaseCurrency,
@@ -197,7 +194,7 @@ func (c *ConversionRates) Update(m map[string]float64) error {
term,
crossRate)
}
solidvalues[base][term] = crossRate
solidvalues[base.Item][term.Item] = crossRate
}
}
}
@@ -209,14 +206,14 @@ func (c *ConversionRates) Update(m map[string]float64) error {
c.m = make(map[*Item]map[*Item]*float64)
}
if c.m[key.Item] == nil {
c.m[key.Item] = make(map[*Item]*float64)
if c.m[key] == nil {
c.m[key] = make(map[*Item]*float64)
}
p := c.m[key.Item][key2.Item]
p := c.m[key][key2]
if p == nil {
newPalsAndFriends := val2
c.m[key.Item][key2.Item] = &newPalsAndFriends
c.m[key][key2] = &newPalsAndFriends
} else {
*p = val2
}
@@ -281,7 +278,7 @@ func (c Conversion) IsInvalid() bool {
// IsFiat checks to see if the from and to currency is a fiat e.g. EURUSD
func (c Conversion) IsFiat() bool {
return storage.IsFiatCurrency(c.From) && storage.IsFiatCurrency(c.To)
return c.From.IsFiatCurrency() && c.To.IsFiatCurrency()
}
// String returns the stringed fields

View File

@@ -7,7 +7,7 @@ import (
// NewCurrenciesFromStringArray returns a Currencies object from strings
func NewCurrenciesFromStringArray(currencies []string) Currencies {
var list Currencies
list := make(Currencies, 0, len(currencies))
for i := range currencies {
if currencies[i] == "" {
continue
@@ -22,17 +22,17 @@ type Currencies []Code
// Strings returns an array of currency strings
func (c Currencies) Strings() []string {
var list []string
list := make([]string, len(c))
for i := range c {
list = append(list, c[i].String())
list[i] = c[i].String()
}
return list
}
// Contains checks to see if a currency code is contained in the currency list
func (c Currencies) Contains(cc Code) bool {
func (c Currencies) Contains(check Code) bool {
for i := range c {
if c[i].Item == cc.Item {
if c[i].Equal(check) {
return true
}
}
@@ -52,10 +52,10 @@ func (c *Currencies) UnmarshalJSON(d []byte) error {
return err
}
var allTheCurrencies Currencies
curr := strings.Split(configCurrencies, ",")
allTheCurrencies := make(Currencies, len(curr))
for i := range curr {
allTheCurrencies = append(allTheCurrencies, NewCode(curr[i]))
allTheCurrencies[i] = NewCode(curr[i])
}
*c = allTheCurrencies
@@ -76,7 +76,7 @@ func (c Currencies) Match(other Currencies) bool {
match:
for x := range c {
for y := range other {
if c[x] == other[y] {
if c[x].Equal(other[y]) {
continue match
}
}

View File

@@ -1,5 +1,13 @@
package currency
import (
"errors"
"fmt"
"strings"
)
var errEmptyPairString = errors.New("empty pair string")
// GetDefaultExchangeRates returns the currency exchange rates based off the
// default fiat values
func GetDefaultExchangeRates() (Conversions, error) {
@@ -56,11 +64,16 @@ func UpdateCurrencies(c Currencies, isCryptocurrency bool) {
storage.UpdateEnabledFiatCurrencies(c)
}
// ConvertCurrency converts an amount from one currency to another
func ConvertCurrency(amount float64, from, to Code) (float64, error) {
// ConvertFiat converts an fiat amount from one currency to another
func ConvertFiat(amount float64, from, to Code) (float64, error) {
return storage.ConvertCurrency(amount, from, to)
}
// GetForeignExchangeRate returns the foreign exchange rate for a fiat pair.
func GetForeignExchangeRate(quotation Pair) (float64, error) {
return storage.ConvertCurrency(1, quotation.Base, quotation.Quote)
}
// SeedForeignExchangeData seeds FX data with the currencies supplied
func SeedForeignExchangeData(c Currencies) error {
return storage.SeedForeignExchangeRatesByCurrencies(c)
@@ -72,7 +85,7 @@ func GetTotalMarketCryptocurrencies() ([]Code, error) {
}
// RunStorageUpdater runs a new foreign exchange updater instance
func RunStorageUpdater(o BotOverrides, m *MainConfiguration, filepath string) error {
func RunStorageUpdater(o BotOverrides, m *Config, filepath string) error {
return storage.RunUpdater(o, m, filepath)
}
@@ -88,43 +101,47 @@ func CopyPairFormat(p Pair, pairs []Pair, exact bool) Pair {
if p.Equal(pairs[x]) {
return pairs[x]
}
continue
}
if p.EqualIncludeReciprocal(pairs[x]) {
return pairs[x]
}
}
return Pair{}
return EMPTYPAIR
}
// FormatPairs formats a string array to a list of currency pairs with the
// supplied currency pair format
func FormatPairs(pairs []string, delimiter, index string) (Pairs, error) {
var result Pairs
var result = make(Pairs, len(pairs))
for x := range pairs {
if pairs[x] == "" {
continue
return nil, fmt.Errorf("%w in slice %v", errEmptyPairString, pairs)
}
var p Pair
var err error
if delimiter != "" {
p, err = NewPairDelimiter(pairs[x], delimiter)
if err != nil {
return nil, err
}
} else {
if index != "" {
p, err = NewPairFromIndex(pairs[x], index)
if err != nil {
return Pairs{}, err
}
} else {
p, err = NewPairFromStrings(pairs[x][0:3], pairs[x][3:])
if err != nil {
return Pairs{}, err
}
}
switch {
case delimiter != "":
result[x], err = NewPairDelimiter(pairs[x], delimiter)
case index != "":
result[x], err = NewPairFromIndex(pairs[x], index)
default:
result[x], err = NewPairFromStrings(pairs[x][:3], pairs[x][3:])
}
if err != nil {
return nil, err
}
result = append(result, p)
}
return result, nil
}
// IsEnabled returns if the individual foreign exchange config setting is
// enabled
func (settings AllFXSettings) IsEnabled(name string) bool {
for x := range settings {
if !strings.EqualFold(settings[x].Name, name) {
continue
}
return settings[x].Enabled
}
return false
}

View File

@@ -1,6 +1,7 @@
package currency
import (
"errors"
"testing"
)
@@ -43,14 +44,14 @@ func TestUpdateBaseCurrency(t *testing.T) {
t.Error("UpdateBaseCurrency() error cannot be nil")
}
if GetBaseCurrency() != AUD {
if !GetBaseCurrency().Equal(AUD) {
t.Errorf("GetBaseCurrency() expected %s but received %s",
AUD, GetBaseCurrency())
}
}
func TestGetDefaultBaseCurrency(t *testing.T) {
if GetDefaultBaseCurrency() != USD {
if !GetDefaultBaseCurrency().Equal(USD) {
t.Errorf("GetDefaultBaseCurrency() expected %s but received %s",
USD, GetDefaultBaseCurrency())
}
@@ -88,13 +89,28 @@ func TestUpdateCurrencies(t *testing.T) {
}
}
func TestConvertCurrency(t *testing.T) {
_, err := ConvertCurrency(100, AUD, USD)
func TestConvertFiat(t *testing.T) {
_, err := ConvertFiat(0, LTC, USD)
if !errors.Is(err, errInvalidAmount) {
t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidAmount)
}
_, err = ConvertFiat(100, LTC, USD)
if !errors.Is(err, errNotFiatCurrency) {
t.Fatalf("received: '%v' but expected: '%v'", err, errNotFiatCurrency)
}
_, err = ConvertFiat(100, USD, LTC)
if !errors.Is(err, errNotFiatCurrency) {
t.Fatalf("received: '%v' but expected: '%v'", err, errNotFiatCurrency)
}
_, err = ConvertFiat(100, AUD, USD)
if err != nil {
t.Fatal(err)
}
r, err := ConvertCurrency(100, AUD, AUD)
r, err := ConvertFiat(100, AUD, AUD)
if err != nil {
t.Fatal(err)
}
@@ -104,23 +120,64 @@ func TestConvertCurrency(t *testing.T) {
100.00, r)
}
_, err = ConvertCurrency(100, USD, AUD)
_, err = ConvertFiat(100, USD, AUD)
if err != nil {
t.Fatal(err)
}
_, err = ConvertCurrency(100, CNY, AUD)
_, err = ConvertFiat(100, CNY, AUD)
if err != nil {
t.Fatal(err)
}
_, err = ConvertCurrency(100, LTC, USD)
if err == nil {
t.Fatal("Expected err on non-existent currency")
}
_, err = ConvertCurrency(100, USD, LTC)
if err == nil {
t.Fatal("Expected err on non-existent currency")
}
}
func TestGetForeignExchangeRate(t *testing.T) {
_, err := GetForeignExchangeRate(NewPair(EMPTYCODE, EMPTYCODE))
if !errors.Is(err, errNotFiatCurrency) {
t.Fatalf("received: '%v' but expected: '%v'", err, errNotFiatCurrency)
}
_, err = GetForeignExchangeRate(NewPair(USD, EMPTYCODE))
if !errors.Is(err, errNotFiatCurrency) {
t.Fatalf("received: '%v' but expected: '%v'", err, errNotFiatCurrency)
}
one, err := GetForeignExchangeRate(NewPair(USD, USD))
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
if one != 1 {
t.Fatal("unexpected value")
}
rate, err := GetForeignExchangeRate(NewPair(AUD, USD))
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
if rate <= 0 {
t.Fatal("unexpected value")
}
}
func TestAllFXSettingsIsEnabled(t *testing.T) {
var settings AllFXSettings
if received := settings.IsEnabled("wow"); received {
t.Fatalf("received: '%v' but expected: '%v'", received, false)
}
settings = []FXSettings{
{
Name: "wOow",
},
{
Name: "amICool?",
Enabled: true,
},
}
if received := settings.IsEnabled("AMICOOL?"); !received {
t.Fatalf("received: '%v' but expected: '%v'", received, true)
}
}

View File

@@ -6,26 +6,35 @@ import (
"github.com/thrasher-corp/gocryptotrader/currency/coinmarketcap"
)
// MainConfiguration is the main configuration from the config.json file
type MainConfiguration struct {
ForexProviders []FXSettings
CryptocurrencyProvider coinmarketcap.Settings
Cryptocurrencies Currencies
CurrencyPairFormat interface{}
FiatDisplayCurrency Code
CurrencyDelay time.Duration
FxRateDelay time.Duration
// Config holds all the information needed for currency related manipulation
type Config struct {
ForexProviders AllFXSettings `json:"forexProviders"`
CryptocurrencyProvider Provider `json:"cryptocurrencyProvider"`
CurrencyPairFormat *PairFormat `json:"currencyPairFormat"`
FiatDisplayCurrency Code `json:"fiatDisplayCurrency"`
CurrencyFileUpdateDuration time.Duration `json:"currencyFileUpdateDuration"`
ForeignExchangeUpdateDuration time.Duration `json:"foreignExchangeUpdateDuration"`
}
// Provider defines coinmarketcap tools
type Provider struct {
Name string `json:"name"`
Enabled bool `json:"enabled"`
Verbose bool `json:"verbose"`
APIKey string `json:"apiKey"`
AccountPlan string `json:"accountPlan"`
}
// BotOverrides defines a bot overriding factor for quick running currency
// subsystems
type BotOverrides struct {
Coinmarketcap bool
FxCurrencyConverter bool
FxCurrencyLayer bool
FxFixer bool
FxOpenExchangeRates bool
FxExchangeRateHost bool
Coinmarketcap bool
CurrencyConverter bool
CurrencyLayer bool
ExchangeRates bool
Fixer bool
OpenExchangeRates bool
ExchangeRateHost bool
}
// CoinmarketcapSettings refers to settings
@@ -40,26 +49,29 @@ type SystemsSettings struct {
Openexchangerates FXSettings
}
// AllFXSettings defines all the foreign exchange settings
type AllFXSettings []FXSettings
// FXSettings defines foreign exchange requester settings
type FXSettings struct {
Name string `json:"name"`
Enabled bool `json:"enabled"`
Verbose bool `json:"verbose"`
RESTPollingDelay time.Duration `json:"restPollingDelay"`
APIKey string `json:"apiKey"`
APIKeyLvl int `json:"apiKeyLvl"`
PrimaryProvider bool `json:"primaryProvider"`
Name string `json:"name"`
Enabled bool `json:"enabled"`
Verbose bool `json:"verbose"`
APIKey string `json:"apiKey"`
APIKeyLvl int `json:"apiKeyLvl"`
PrimaryProvider bool `json:"primaryProvider"`
}
// File defines a full currency file generated by the currency storage
// analysis system
type File struct {
LastMainUpdate interface{} `json:"lastMainUpdate"`
Cryptocurrency []Item `json:"cryptocurrencies"`
FiatCurrency []Item `json:"fiatCurrencies"`
UnsetCurrency []Item `json:"unsetCurrencies"`
Contracts []Item `json:"contracts"`
Token []Item `json:"tokens"`
Cryptocurrency []*Item `json:"cryptocurrencies"`
FiatCurrency []*Item `json:"fiatCurrencies"`
UnsetCurrency []*Item `json:"unsetCurrencies"`
Contracts []*Item `json:"contracts"`
Token []*Item `json:"tokens"`
Stable []*Item `json:"stableCurrencies"`
}
// Const here are packaged defined delimiters

View File

@@ -22,6 +22,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
+ Currency Converter API support
+ Currency Layer support
+ Exchange Rates support
+ Fixer.io support
+ Open Exchange Rates support
+ ExchangeRate.host support

View File

@@ -29,11 +29,10 @@ const DefaultTimeOut = time.Second * 15
// Settings enforces standard variables across the provider packages
type Settings struct {
Name string `json:"name"`
Enabled bool `json:"enabled"`
Verbose bool `json:"verbose"`
RESTPollingDelay time.Duration `json:"restPollingDelay"`
APIKey string `json:"apiKey"`
APIKeyLvl int `json:"apiKeyLvl"`
PrimaryProvider bool `json:"primaryProvider"`
Name string `json:"name"`
Enabled bool `json:"enabled"`
Verbose bool `json:"verbose"`
APIKey string `json:"apiKey"`
APIKeyLvl int `json:"apiKeyLvl"`
PrimaryProvider bool `json:"primaryProvider"`
}

View File

@@ -21,7 +21,6 @@ func (c *CurrencyConverter) Setup(config base.Settings) error {
c.APIKey = config.APIKey
c.APIKeyLvl = config.APIKeyLvl
c.Enabled = config.Enabled
c.RESTPollingDelay = config.RESTPollingDelay
c.Verbose = config.Verbose
c.PrimaryProvider = config.PrimaryProvider
c.Requester = request.New(c.Name,

View File

@@ -40,7 +40,6 @@ func (c *CurrencyLayer) Setup(config base.Settings) error {
c.APIKey = config.APIKey
c.APIKeyLvl = config.APIKeyLvl
c.Enabled = config.Enabled
c.RESTPollingDelay = config.RESTPollingDelay
c.Verbose = config.Verbose
c.PrimaryProvider = config.PrimaryProvider
// Rate limit is based off a monthly counter - Open limit used.

View File

@@ -20,9 +20,6 @@ const (
APIEndpointConversion = "convert"
APIEndpointTimeframe = "timeframe"
APIEndpointChange = "change"
authRate = 0
unAuthRate = 0
)
// CurrencyLayer is a foreign exchange rate provider at

View File

@@ -31,7 +31,6 @@ var (
func (e *ExchangeRateHost) Setup(config base.Settings) error {
e.Name = config.Name
e.Enabled = config.Enabled
e.RESTPollingDelay = config.RESTPollingDelay
e.Verbose = config.Verbose
e.PrimaryProvider = config.PrimaryProvider
e.Requester = request.New(e.Name,

View File

@@ -25,7 +25,6 @@ func (e *ExchangeRates) Setup(config base.Settings) error {
}
e.Name = config.Name
e.Enabled = config.Enabled
e.RESTPollingDelay = config.RESTPollingDelay
e.Verbose = config.Verbose
e.PrimaryProvider = config.PrimaryProvider
e.APIKey = config.APIKey

View File

@@ -34,7 +34,6 @@ func (f *Fixer) Setup(config base.Settings) error {
f.APIKeyLvl = config.APIKeyLvl
f.Enabled = config.Enabled
f.Name = config.Name
f.RESTPollingDelay = config.RESTPollingDelay
f.Verbose = config.Verbose
f.PrimaryProvider = config.PrimaryProvider
f.Requester = request.New(f.Name,
@@ -220,10 +219,13 @@ func (f *Fixer) GetFluctuationData(startDate, endDate, baseCurrency string, symb
// SendOpenHTTPRequest sends a typical get request
func (f *Fixer) SendOpenHTTPRequest(endpoint string, v url.Values, result interface{}) error {
var path string
if v == nil {
v = url.Values{}
}
v.Set("access_key", f.APIKey)
var auth bool
var path string
if f.APIKeyLvl == fixerAPIFree {
path = fixerAPI + endpoint + "?" + v.Encode()
} else {

View File

@@ -9,11 +9,6 @@ import (
// Please set API key and apikey subscription level for correct due diligence
// testing - NOTE please be aware tests will diminish your monthly API calls
const (
apikey = ""
apiKeyLvl = 3
)
var f Fixer
var isSetup bool

View File

@@ -4,6 +4,7 @@ package forexprovider
import (
"errors"
"fmt"
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/base"
currencyconverter "github.com/thrasher-corp/gocryptotrader/currency/forexprovider/currencyconverterapi"
@@ -14,6 +15,11 @@ import (
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider/openexchangerates"
)
var (
errUnhandledForeignExchangeProvider = errors.New("unhandled foreign exchange provider")
errNoPrimaryForexProviderEnabled = errors.New("no primary forex provider enabled")
)
// ForexProviders is a foreign exchange handler type
type ForexProviders struct {
base.FXHandler
@@ -83,69 +89,37 @@ func (f *ForexProviders) SetProvider(b base.IFXProvider) error {
// StartFXService starts the forex provider service and returns a pointer to it
func StartFXService(fxProviders []base.Settings) (*ForexProviders, error) {
handler := new(ForexProviders)
for i := range fxProviders {
switch {
case fxProviders[i].Name == "CurrencyConverter" && fxProviders[i].Enabled:
provider := new(currencyconverter.CurrencyConverter)
err := provider.Setup(fxProviders[i])
if err != nil {
return nil, err
}
err = handler.SetProvider(provider)
if err != nil {
return nil, err
}
case fxProviders[i].Name == "CurrencyLayer" && fxProviders[i].Enabled:
provider := new(currencylayer.CurrencyLayer)
err := provider.Setup(fxProviders[i])
if err != nil {
return nil, err
}
err = handler.SetProvider(provider)
if err != nil {
return nil, err
}
case fxProviders[i].Name == "ExchangeRates" && fxProviders[i].Enabled:
provider := new(exchangerates.ExchangeRates)
err := provider.Setup(fxProviders[i])
if err != nil {
return nil, err
}
err = handler.SetProvider(provider)
if err != nil {
return nil, err
}
case fxProviders[i].Name == "Fixer" && fxProviders[i].Enabled:
provider := new(fixer.Fixer)
err := provider.Setup(fxProviders[i])
if err != nil {
return nil, err
}
err = handler.SetProvider(provider)
if err != nil {
return nil, err
}
case fxProviders[i].Name == "OpenExchangeRates" && fxProviders[i].Enabled:
provider := new(openexchangerates.OXR)
err := provider.Setup(fxProviders[i])
if err != nil {
return nil, err
}
err = handler.SetProvider(provider)
if err != nil {
return nil, err
}
var provider base.IFXProvider
switch fxProviders[i].Name {
case "CurrencyConverter":
provider = new(currencyconverter.CurrencyConverter)
case "CurrencyLayer":
provider = new(currencylayer.CurrencyLayer)
case "ExchangeRates":
provider = new(exchangerates.ExchangeRates)
case "Fixer":
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)
}
err := provider.Setup(fxProviders[i])
if err != nil {
return nil, err
}
err = handler.SetProvider(provider)
if err != nil {
return nil, err
}
}
if handler.Primary.Provider == nil {
return nil, errors.New("no primary forex provider enabled")
return nil, errNoPrimaryForexProviderEnabled
}
return handler, nil

View File

@@ -35,7 +35,6 @@ func (o *OXR) Setup(config base.Settings) error {
o.APIKeyLvl = config.APIKeyLvl
o.Enabled = config.Enabled
o.Name = config.Name
o.RESTPollingDelay = config.RESTPollingDelay
o.Verbose = config.Verbose
o.PrimaryProvider = config.PrimaryProvider
o.Requester = request.New(o.Name,

View File

@@ -16,6 +16,12 @@ var (
ErrPairAlreadyEnabled = errors.New("pair already enabled")
// ErrPairNotFound is returned when a currency pair is not found
ErrPairNotFound = errors.New("pair not found")
// errAssetNotEnabled defines an error for the pairs management system
// that declares the asset is not enabled.
errAssetNotEnabled = errors.New("asset not enabled")
// ErrAssetIsNil is an error when the asset has not been populated by the
// configuration
ErrAssetIsNil = errors.New("asset is nil")
)
// GetAssetTypes returns a list of stored asset types
@@ -168,11 +174,11 @@ func (p *PairsManager) IsAssetEnabled(a asset.Item) error {
}
if c.AssetEnabled == nil {
return errors.New("cannot ascertain if asset is enabled, variable is nil")
return fmt.Errorf("%s %w", a, ErrAssetIsNil)
}
if !*c.AssetEnabled {
return fmt.Errorf("asset %s not enabled", a)
return fmt.Errorf("%s %w", a, errAssetNotEnabled)
}
return nil
}

View File

@@ -250,13 +250,13 @@ func TestDisablePair(t *testing.T) {
// Test asset type which doesn't exist
initTest(t)
if err := p.DisablePair(asset.Futures, Pair{}); err == nil {
if err := p.DisablePair(asset.Futures, EMPTYPAIR); err == nil {
t.Error("unexpected result")
}
// Test asset type which has an empty pair store
p.Pairs[asset.Spot] = nil
if err := p.DisablePair(asset.Spot, Pair{}); err == nil {
if err := p.DisablePair(asset.Spot, EMPTYPAIR); err == nil {
t.Error("unexpected result")
}
@@ -281,13 +281,13 @@ func TestEnablePair(t *testing.T) {
// Test asset type which doesn't exist
initTest(t)
if err := p.EnablePair(asset.Futures, Pair{}); err == nil {
if err := p.EnablePair(asset.Futures, EMPTYPAIR); err == nil {
t.Error("unexpected result")
}
// Test asset type which has an empty pair store
p.Pairs[asset.Spot] = nil
if err := p.EnablePair(asset.Spot, Pair{}); err == nil {
if err := p.EnablePair(asset.Spot, EMPTYPAIR); err == nil {
t.Error("unexpected result")
}

View File

@@ -1,20 +1,23 @@
package currency
import (
"errors"
"fmt"
"strings"
)
var errCannotCreatePair = errors.New("cannot create currency pair")
// NewPairDelimiter splits the desired currency string at delimeter, the returns
// a Pair struct
func NewPairDelimiter(currencyPair, delimiter string) (Pair, error) {
if !strings.Contains(currencyPair, delimiter) {
return Pair{},
return EMPTYPAIR,
fmt.Errorf("delimiter: [%s] not found in currencypair string", delimiter)
}
result := strings.Split(currencyPair, delimiter)
if len(result) < 2 {
return Pair{},
return EMPTYPAIR,
fmt.Errorf("supplied pair: [%s] cannot be split with %s",
currencyPair,
delimiter)
@@ -32,13 +35,13 @@ func NewPairDelimiter(currencyPair, delimiter string) (Pair, error) {
// NewPairFromStrings returns a CurrencyPair without a delimiter
func NewPairFromStrings(base, quote string) (Pair, error) {
if strings.Contains(base, " ") {
return Pair{},
return EMPTYPAIR,
fmt.Errorf("cannot create pair, invalid base currency string [%s]",
base)
}
if strings.Contains(quote, " ") {
return Pair{},
return EMPTYPAIR,
fmt.Errorf("cannot create pair, invalid quote currency string [%s]",
quote)
}
@@ -68,7 +71,7 @@ func NewPairWithDelimiter(base, quote, delimiter string) Pair {
func NewPairFromIndex(currencyPair, index string) (Pair, error) {
i := strings.Index(currencyPair, index)
if i == -1 {
return Pair{},
return EMPTYPAIR,
fmt.Errorf("index %s not found in currency pair string", index)
}
if i == 0 {
@@ -87,8 +90,9 @@ func NewPairFromString(currencyPair string) (Pair, error) {
}
}
if len(currencyPair) < 3 {
return Pair{},
fmt.Errorf("cannot create pair from %s string",
return EMPTYPAIR,
fmt.Errorf("%w from %s string too short to be a current pair",
errCannotCreatePair,
currencyPair)
}
return NewPairFromStrings(currencyPair[0:3], currencyPair[3:])
@@ -100,8 +104,7 @@ func NewPairFromString(currencyPair string) (Pair, error) {
// apply the same format
func NewPairFromFormattedPairs(currencyPair string, pairs Pairs, pairFmt PairFormat) (Pair, error) {
for x := range pairs {
fPair := pairFmt.Format(pairs[x])
if strings.EqualFold(fPair, currencyPair) {
if strings.EqualFold(pairFmt.Format(pairs[x]), currencyPair) {
return pairs[x], nil
}
}
@@ -132,5 +135,5 @@ func MatchPairsWithNoDelimiter(currencyPair string, pairs Pairs, pairFmt PairFor
}
}
}
return Pair{}, fmt.Errorf("currency %v not found in supplied pairs", currencyPair)
return EMPTYPAIR, fmt.Errorf("currency %v not found in supplied pairs", currencyPair)
}

View File

@@ -2,7 +2,6 @@ package currency
import (
"encoding/json"
"strings"
)
// String returns a currency pair string
@@ -12,20 +11,16 @@ func (p Pair) String() string {
// Lower converts the pair object to lowercase
func (p Pair) Lower() Pair {
return Pair{
Delimiter: p.Delimiter,
Base: p.Base.Lower(),
Quote: p.Quote.Lower(),
}
p.Base = p.Base.Lower()
p.Quote = p.Quote.Lower()
return p
}
// Upper converts the pair object to uppercase
func (p Pair) Upper() Pair {
return Pair{
Delimiter: p.Delimiter,
Base: p.Base.Upper(),
Quote: p.Quote.Upper(),
}
p.Base = p.Base.Upper()
p.Quote = p.Quote.Upper()
return p
}
// UnmarshalJSON comforms type to the umarshaler interface
@@ -41,9 +36,7 @@ func (p *Pair) UnmarshalJSON(d []byte) error {
return err
}
p.Base = newPair.Base
p.Quote = newPair.Quote
p.Delimiter = newPair.Delimiter
*p = newPair
return nil
}
@@ -55,49 +48,56 @@ func (p Pair) MarshalJSON() ([]byte, error) {
// Format changes the currency based on user preferences overriding the default
// String() display
func (p Pair) Format(delimiter string, uppercase bool) Pair {
newP := Pair{Base: p.Base, Quote: p.Quote, Delimiter: delimiter}
p.Delimiter = delimiter
if uppercase {
return newP.Upper()
return p.Upper()
}
return newP.Lower()
return p.Lower()
}
// Equal compares two currency pairs and returns whether or not they are equal
func (p Pair) Equal(cPair Pair) bool {
return strings.EqualFold(p.Base.String(), cPair.Base.String()) &&
strings.EqualFold(p.Quote.String(), cPair.Quote.String())
return p.Base.Equal(cPair.Base) && p.Quote.Equal(cPair.Quote)
}
// EqualIncludeReciprocal compares two currency pairs and returns whether or not
// they are the same including reciprocal currencies.
func (p Pair) EqualIncludeReciprocal(cPair Pair) bool {
if (p.Base.Item == cPair.Base.Item && p.Quote.Item == cPair.Quote.Item) ||
(p.Base.Item == cPair.Quote.Item && p.Quote.Item == cPair.Base.Item) {
return true
}
return false
return (p.Base.Equal(cPair.Base) && p.Quote.Equal(cPair.Quote)) ||
(p.Base.Equal(cPair.Quote) && p.Quote.Equal(cPair.Base))
}
// IsCryptoPair checks to see if the pair is a crypto pair e.g. BTCLTC
func (p Pair) IsCryptoPair() bool {
return storage.IsCryptocurrency(p.Base) &&
storage.IsCryptocurrency(p.Quote)
return p.Base.IsCryptocurrency() && p.Quote.IsCryptocurrency()
}
// IsCryptoFiatPair checks to see if the pair is a crypto fiat pair e.g. BTCUSD
func (p Pair) IsCryptoFiatPair() bool {
return (storage.IsCryptocurrency(p.Base) && storage.IsFiatCurrency(p.Quote)) ||
(storage.IsFiatCurrency(p.Base) && storage.IsCryptocurrency(p.Quote))
return (p.Base.IsCryptocurrency() && p.Quote.IsFiatCurrency()) ||
(p.Base.IsFiatCurrency() && p.Quote.IsCryptocurrency())
}
// IsFiatPair checks to see if the pair is a fiat pair e.g. EURUSD
func (p Pair) IsFiatPair() bool {
return storage.IsFiatCurrency(p.Base) && storage.IsFiatCurrency(p.Quote)
return p.Base.IsFiatCurrency() && p.Quote.IsFiatCurrency()
}
// IsCryptoStablePair checks to see if the pair is a crypto stable pair e.g.
// LTC-USDT
func (p Pair) IsCryptoStablePair() bool {
return (p.Base.IsCryptocurrency() && p.Quote.IsStableCurrency()) ||
(p.Base.IsStableCurrency() && p.Quote.IsCryptocurrency())
}
// IsStablePair checks to see if the pair is a stable pair e.g. USDT-DAI
func (p Pair) IsStablePair() bool {
return p.Base.IsStableCurrency() && p.Quote.IsStableCurrency()
}
// IsInvalid checks invalid pair if base and quote are the same
func (p Pair) IsInvalid() bool {
return p.Base.Item == p.Quote.Item
return p.Base.Equal(p.Quote)
}
// Swap turns the currency pair into its reciprocal
@@ -111,7 +111,23 @@ func (p Pair) IsEmpty() bool {
return p.Base.IsEmpty() && p.Quote.IsEmpty()
}
// ContainsCurrency checks to see if a pair contains a specific currency
func (p Pair) ContainsCurrency(c Code) bool {
return p.Base.Item == c.Item || p.Quote.Item == c.Item
// Contains checks to see if a pair contains a specific currency
func (p Pair) Contains(c Code) bool {
return p.Base.Equal(c) || p.Quote.Equal(c)
}
// Len derives full length for match exclusion.
func (p Pair) Len() int {
return len(p.Base.String()) + len(p.Quote.String())
}
// Other returns the other currency from pair, if not matched returns empty code.
func (p Pair) Other(c Code) (Code, error) {
if p.Base.Equal(c) {
return p.Quote, nil
}
if p.Quote.Equal(c) {
return p.Base, nil
}
return EMPTYCODE, ErrCurrencyCodeEmpty
}

View File

@@ -2,6 +2,7 @@ package currency
import (
"encoding/json"
"errors"
"testing"
)
@@ -120,6 +121,34 @@ func TestIsFiatPair(t *testing.T) {
}
}
func TestIsCryptoStablePair(t *testing.T) {
if !NewPair(BTC, USDT).IsCryptoStablePair() {
t.Error("TestIsCryptoStablePair. Expected true result")
}
if !NewPair(DAI, USDT).IsCryptoStablePair() {
t.Error("TestIsCryptoStablePair. Expected true result")
}
if NewPair(AUD, USDT).IsCryptoStablePair() {
t.Error("TestIsCryptoStablePair. Expected false result")
}
}
func TestIsStablePair(t *testing.T) {
if !NewPair(USDT, DAI).IsStablePair() {
t.Error("TestIsStablePair. Expected true result")
}
if NewPair(USDT, AUD).IsStablePair() {
t.Error("TestIsStablePair. Expected false result")
}
if NewPair(USDT, LTC).IsStablePair() {
t.Error("TestIsStablePair. Expected false result")
}
}
func TestString(t *testing.T) {
t.Parallel()
pair := NewPair(BTC, USD)
@@ -132,7 +161,7 @@ func TestString(t *testing.T) {
func TestFirstCurrency(t *testing.T) {
t.Parallel()
pair := NewPair(BTC, USD)
if actual, expected := pair.Base, BTC; actual != expected {
if actual, expected := pair.Base, BTC; !actual.Equal(expected) {
t.Errorf(
"GetFirstCurrency(): %s was not equal to expected value: %s",
actual, expected,
@@ -143,7 +172,7 @@ func TestFirstCurrency(t *testing.T) {
func TestSecondCurrency(t *testing.T) {
t.Parallel()
pair := NewPair(BTC, USD)
if actual, expected := pair.Quote, USD; actual != expected {
if actual, expected := pair.Quote, USD; !actual.Equal(expected) {
t.Errorf(
"GetSecondCurrency(): %s was not equal to expected value: %s",
actual, expected,
@@ -513,26 +542,22 @@ func TestNewPairFromFormattedPairs(t *testing.T) {
func TestContainsCurrency(t *testing.T) {
p := NewPair(BTC, USD)
if !p.ContainsCurrency(BTC) {
t.Error("TestContainsCurrency: Expected currency was not found")
if !p.Contains(BTC) {
t.Error("TestContains: Expected currency was not found")
}
if p.ContainsCurrency(ETH) {
t.Error("TestContainsCurrency: Non-existent currency was found")
if p.Contains(ETH) {
t.Error("TestContains: Non-existent currency was found")
}
}
func TestFormatPairs(t *testing.T) {
newP, err := FormatPairs([]string{""}, "-", "")
if err != nil {
t.Error("FormatPairs() error", err)
_, err := FormatPairs([]string{""}, "-", "")
if !errors.Is(err, errEmptyPairString) {
t.Fatalf("received: '%v' but expected: '%v'", err, errEmptyPairString)
}
if len(newP) > 0 {
t.Error("TestFormatPairs: Empty string returned a valid pair")
}
newP, err = FormatPairs([]string{defaultPairWDelimiter}, "-", "")
newP, err := FormatPairs([]string{defaultPairWDelimiter}, "-", "")
if err != nil {
t.Error("FormatPairs() error", err)
}
@@ -599,8 +624,8 @@ func TestFindPairDifferences(t *testing.T) {
}
emptyPairsList, err := NewPairsFromStrings([]string{""})
if err != nil {
t.Fatal(err)
if !errors.Is(err, errCannotCreatePair) {
t.Fatalf("received: '%v' but expected: '%v'", err, errCannotCreatePair)
}
// Test that we don't allow empty strings for new pairs
@@ -778,7 +803,7 @@ func TestPairFormat_Format(t *testing.T) {
{
name: "empty",
fields: fields{},
arg: Pair{},
arg: EMPTYPAIR,
want: "",
},
{
@@ -820,3 +845,23 @@ func TestPairFormat_Format(t *testing.T) {
})
}
}
func TestOther(t *testing.T) {
received, err := NewPair(DAI, XRP).Other(DAI)
if err != nil {
t.Fatal(err)
}
if !received.Equal(XRP) {
t.Fatal("unexpected value")
}
received, err = NewPair(DAI, XRP).Other(XRP)
if err != nil {
t.Fatal(err)
}
if !received.Equal(DAI) {
t.Fatal("unexpected value")
}
if _, err := NewPair(DAI, XRP).Other(BTC); !errors.Is(err, ErrCurrencyCodeEmpty) {
t.Fatal("unexpected value")
}
}

View File

@@ -2,36 +2,48 @@ package currency
import (
"encoding/json"
"errors"
"fmt"
"math/rand"
"strings"
"github.com/thrasher-corp/gocryptotrader/log"
)
var (
errSymbolEmpty = errors.New("symbol is empty")
errPairsEmpty = errors.New("pairs are empty")
errNoDelimiter = errors.New("no delimiter was supplied")
)
// NewPairsFromStrings takes in currency pair strings and returns a currency
// pair list
func NewPairsFromStrings(pairs []string) (Pairs, error) {
var newPairs Pairs
allThePairs := make(Pairs, len(pairs))
var err error
for i := range pairs {
if pairs[i] == "" {
continue
}
newPair, err := NewPairFromString(pairs[i])
allThePairs[i], err = NewPairFromString(pairs[i])
if err != nil {
return nil, err
}
newPairs = append(newPairs, newPair)
}
return newPairs, nil
return allThePairs, nil
}
// NewPairsFromString takes in a delimiter string and returns a Pairs
// type
func NewPairsFromString(pairs, delimiter string) (Pairs, error) {
if delimiter == "" {
return nil, errNoDelimiter
}
return NewPairsFromStrings(strings.Split(pairs, delimiter))
}
// Strings returns a slice of strings referring to each currency pair
func (p Pairs) Strings() []string {
var list []string
list := make([]string, len(p))
for i := range p {
list = append(list, p[i].String())
list[i] = p[i].String()
}
return list
}
@@ -43,28 +55,22 @@ func (p Pairs) Join() string {
// Format formats the pair list to the exchange format configuration
func (p Pairs) Format(delimiter, index string, uppercase bool) Pairs {
var pairs Pairs
for i := range p {
var formattedPair = Pair{
Delimiter: delimiter,
Base: p[i].Base,
Quote: p[i].Quote,
}
pairs := make(Pairs, 0, len(p))
var err error
for _, format := range p {
if index != "" {
newP, err := NewPairFromIndex(p[i].String(), index)
format, err = NewPairFromIndex(format.String(), index)
if err != nil {
log.Errorf(log.Global,
"failed to create NewPairFromIndex. Err: %s\n", err)
continue
}
formattedPair.Base = newP.Base
formattedPair.Quote = newP.Quote
}
format.Delimiter = delimiter
if uppercase {
pairs = append(pairs, formattedPair.Upper())
pairs = append(pairs, format.Upper())
} else {
pairs = append(pairs, formattedPair.Lower())
pairs = append(pairs, format.Lower())
}
}
return pairs
@@ -83,18 +89,8 @@ func (p *Pairs) UnmarshalJSON(d []byte) error {
return nil
}
var allThePairs Pairs
oldPairs := strings.Split(pairs, ",")
for i := range oldPairs {
pair, err := NewPairFromString(oldPairs[i])
if err != nil {
return err
}
allThePairs = append(allThePairs, pair)
}
*p = allThePairs
return nil
*p, err = NewPairsFromString(pairs, ",")
return err
}
// MarshalJSON conforms type to the marshaler interface
@@ -102,13 +98,22 @@ func (p Pairs) MarshalJSON() ([]byte, error) {
return json.Marshal(p.Join())
}
// Upper returns an upper formatted pair list
// Upper updates the original pairs and returns the pairs for convenience if
// needed.
func (p Pairs) Upper() Pairs {
var upper Pairs
for i := range p {
upper = append(upper, p[i].Upper())
p[i] = p[i].Upper()
}
return upper
return p
}
// Lower updates the original pairs and returns the pairs for convenience if
// needed.
func (p Pairs) Lower() Pairs {
for i := range p {
p[i] = p[i].Lower()
}
return p
}
// Contains checks to see if a specified pair exists inside a currency pair
@@ -131,9 +136,22 @@ func (p Pairs) Contains(check Pair, exact bool) bool {
// RemovePairsByFilter checks to see if a pair contains a specific currency
// and removes it from the list of pairs
func (p Pairs) RemovePairsByFilter(filter Code) Pairs {
var pairs Pairs
pairs := make(Pairs, 0, len(p))
for i := range p {
if p[i].ContainsCurrency(filter) {
if p[i].Contains(filter) {
continue
}
pairs = append(pairs, p[i])
}
return pairs
}
// GetPairsByFilter returns all pairs that have at least one match base or quote
// to the filter code.
func (p Pairs) GetPairsByFilter(filter Code) Pairs {
pairs := make(Pairs, 0, len(p))
for i := range p {
if !p[i].Contains(filter) {
continue
}
pairs = append(pairs, p[i])
@@ -143,7 +161,7 @@ func (p Pairs) RemovePairsByFilter(filter Code) Pairs {
// Remove removes the specified pair from the list of pairs if it exists
func (p Pairs) Remove(pair Pair) Pairs {
var pairs Pairs
pairs := make(Pairs, 0, len(p))
for x := range p {
if p[x].Equal(pair) {
continue
@@ -162,6 +180,18 @@ func (p Pairs) Add(pair Pair) Pairs {
return p
}
// GetMatch returns either the pair that is equal including the reciprocal for
// when currencies are constructed from different exchange pairs e.g. Exchange
// one USDT-DAI to exchange two DAI-USDT enabled/available pairs.
func (p Pairs) GetMatch(pair Pair) (Pair, error) {
for x := range p {
if p[x].EqualIncludeReciprocal(pair) {
return p[x], nil
}
}
return EMPTYPAIR, ErrPairNotFound
}
// FindDifferences returns pairs which are new or have been removed
func (p Pairs) FindDifferences(pairs Pairs) (newPairs, removedPairs Pairs) {
for x := range pairs {
@@ -188,5 +218,116 @@ func (p Pairs) GetRandomPair() Pair {
if pairsLen := len(p); pairsLen != 0 {
return p[rand.Intn(pairsLen)] // nolint:gosec // basic number generation required, no need for crypo/rand
}
return Pair{}
return EMPTYPAIR
}
// DeriveFrom matches symbol string to the available pairs list when no
// delimiter is supplied.
func (p Pairs) DeriveFrom(symbol string) (Pair, error) {
if len(p) == 0 {
return EMPTYPAIR, errPairsEmpty
}
if symbol == "" {
return EMPTYPAIR, errSymbolEmpty
}
symbol = strings.ToLower(symbol)
pairs:
for x := range p {
if p[x].Len() != len(symbol) {
continue
}
base := p[x].Base.Lower().String()
baseLength := len(base)
for y := 0; y < baseLength; y++ {
if base[y] != symbol[y] {
continue pairs
}
}
quote := p[x].Quote.Lower().String()
for y := 0; y < len(quote); y++ {
if quote[y] != symbol[baseLength+y] {
continue pairs
}
}
return p[x], nil
}
return EMPTYPAIR, fmt.Errorf("%w for symbol string %s", ErrPairNotFound, symbol)
}
// GetCrypto returns all the cryptos contained in the list.
func (p Pairs) GetCrypto() Currencies {
m := make(map[*Item]bool)
for x := range p {
if p[x].Base.IsCryptocurrency() {
m[p[x].Base.Item] = p[x].Base.UpperCase
}
if p[x].Quote.IsCryptocurrency() {
m[p[x].Quote.Item] = p[x].Quote.UpperCase
}
}
return currencyConstructor(m)
}
// GetFiat returns all the cryptos contained in the list.
func (p Pairs) GetFiat() Currencies {
m := make(map[*Item]bool)
for x := range p {
if p[x].Base.IsFiatCurrency() {
m[p[x].Base.Item] = p[x].Base.UpperCase
}
if p[x].Quote.IsFiatCurrency() {
m[p[x].Quote.Item] = p[x].Quote.UpperCase
}
}
return currencyConstructor(m)
}
// GetCurrencies returns the full currency code list contained derived from the
// pairs list.
func (p Pairs) GetCurrencies() Currencies {
m := make(map[*Item]bool)
for x := range p {
m[p[x].Base.Item] = p[x].Base.UpperCase
m[p[x].Quote.Item] = p[x].Quote.UpperCase
}
return currencyConstructor(m)
}
// GetStables returns the stable currency code list derived from the pairs list.
func (p Pairs) GetStables() Currencies {
m := make(map[*Item]bool)
for x := range p {
if p[x].Base.IsStableCurrency() {
m[p[x].Base.Item] = p[x].Base.UpperCase
}
if p[x].Quote.IsStableCurrency() {
m[p[x].Quote.Item] = p[x].Quote.UpperCase
}
}
return currencyConstructor(m)
}
// currencyConstructor takes in an item map and returns the currencies with
// the same formatting.
func currencyConstructor(m map[*Item]bool) Currencies {
var cryptos = make([]Code, len(m))
var target int
for code, upper := range m {
cryptos[target].Item = code
cryptos[target].UpperCase = upper
target++
}
return cryptos
}
// GetStablesMatch returns all stable pairs matched with code
func (p Pairs) GetStablesMatch(code Code) Pairs {
stablePairs := make([]Pair, 0, len(p))
for x := range p {
if p[x].Base.IsStableCurrency() && p[x].Quote.Equal(code) ||
p[x].Quote.IsStableCurrency() && p[x].Base.Equal(code) {
stablePairs = append(stablePairs, p[x])
}
}
return stablePairs
}

View File

@@ -2,6 +2,7 @@ package currency
import (
"encoding/json"
"errors"
"testing"
)
@@ -10,9 +11,18 @@ func TestPairsUpper(t *testing.T) {
if err != nil {
t.Fatal(err)
}
expected := "BTC_USD,BTC_AUD,BTC_LTC"
if expected := "BTC_USD,BTC_AUD,BTC_LTC"; pairs.Upper().Join() != expected {
t.Errorf("Pairs Join() error expected %s but received %s",
expected, pairs.Join())
}
}
if pairs.Upper().Join() != expected {
func TestPairsLower(t *testing.T) {
pairs, err := NewPairsFromStrings([]string{"BTC_USD", "BTC_AUD", "BTC_LTC"})
if err != nil {
t.Fatal(err)
}
if expected := "btc_usd,btc_aud,btc_ltc"; pairs.Lower().Join() != expected {
t.Errorf("Pairs Join() error expected %s but received %s",
expected, pairs.Join())
}
@@ -33,6 +43,33 @@ func TestPairsString(t *testing.T) {
}
}
func TestPairsFromString(t *testing.T) {
if _, err := NewPairsFromString("", ""); !errors.Is(err, errNoDelimiter) {
t.Fatalf("received: '%v' but expected: '%v'", err, errNoDelimiter)
}
if _, err := NewPairsFromString("", ","); !errors.Is(err, errCannotCreatePair) {
t.Fatalf("received: '%v' but expected: '%v'", err, errCannotCreatePair)
}
pairs, err := NewPairsFromString("ALGO-AUD,BAT-AUD,BCH-AUD,BSV-AUD,BTC-AUD,COMP-AUD,ENJ-AUD,ETC-AUD,ETH-AUD,ETH-BTC,GNT-AUD,LINK-AUD,LTC-AUD,LTC-BTC,MCAU-AUD,OMG-AUD,POWR-AUD,UNI-AUD,USDT-AUD,XLM-AUD,XRP-AUD,XRP-BTC", ",")
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
expected := []string{"ALGO-AUD", "BAT-AUD", "BCH-AUD", "BSV-AUD", "BTC-AUD",
"COMP-AUD", "ENJ-AUD", "ETC-AUD", "ETH-AUD", "ETH-BTC", "GNT-AUD",
"LINK-AUD", "LTC-AUD", "LTC-BTC", "MCAU-AUD", "OMG-AUD", "POWR-AUD",
"UNI-AUD", "USDT-AUD", "XLM-AUD", "XRP-AUD", "XRP-BTC"}
returned := pairs.Strings()
for x := range returned {
if returned[x] != expected[x] {
t.Fatalf("received: '%v' but expected: '%v'", returned[x], expected[x])
}
}
}
func TestPairsJoin(t *testing.T) {
pairs, err := NewPairsFromStrings([]string{"btc_usd", "btc_aud", "btc_ltc"})
if err != nil {
@@ -157,6 +194,20 @@ func TestRemovePairsByFilter(t *testing.T) {
}
}
func TestGetPairsByFilter(t *testing.T) {
var pairs = Pairs{
NewPair(BTC, USD),
NewPair(LTC, USD),
NewPair(LTC, USDT),
}
filtered := pairs.GetPairsByFilter(LTC)
if !filtered.Contains(NewPair(LTC, USDT), true) &&
!filtered.Contains(NewPair(LTC, USD), true) {
t.Error("TestRemovePairsByFilter unexpected result")
}
}
func TestRemove(t *testing.T) {
var pairs = Pairs{
NewPair(BTC, USD),
@@ -215,3 +266,242 @@ func TestContains(t *testing.T) {
t.Errorf("TestContains: Non-existent pair was found")
}
}
func TestDeriveFrom(t *testing.T) {
t.Parallel()
_, err := Pairs{}.DeriveFrom("")
if !errors.Is(err, errPairsEmpty) {
t.Fatalf("received: '%v' but expected: '%v'", err, errPairsEmpty)
}
var testCases = Pairs{
NewPair(BTC, USDT),
NewPair(USDC, USDT),
NewPair(USDC, USD),
NewPair(BTC, LTC),
NewPair(LTC, SAFEMARS),
}
_, err = testCases.DeriveFrom("")
if !errors.Is(err, errSymbolEmpty) {
t.Fatalf("received: '%v' but expected: '%v'", err, errSymbolEmpty)
}
_, err = testCases.DeriveFrom("btcUSD")
if !errors.Is(err, ErrPairNotFound) {
t.Fatalf("received: '%v' but expected: '%v'", err, ErrPairNotFound)
}
got, err := testCases.DeriveFrom("USDCUSD")
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
if got.Upper().String() != "USDCUSD" {
t.Fatalf("received: '%v' but expected: '%v'", got.Upper().String(), "USDCUSD")
}
}
func TestGetCrypto(t *testing.T) {
pairs := Pairs{
NewPair(BTC, USD),
NewPair(LTC, USD),
NewPair(USD, NZD),
NewPair(LTC, USDT),
}
contains(t, []Code{BTC, LTC, USDT}, pairs.GetCrypto())
}
func TestGetFiat(t *testing.T) {
pairs := Pairs{
NewPair(BTC, USD),
NewPair(LTC, USD),
NewPair(USD, NZD),
NewPair(LTC, USDT),
}
contains(t, []Code{USD, NZD}, pairs.GetFiat())
}
func TestGetCurrencies(t *testing.T) {
pairs := Pairs{
NewPair(BTC, USD),
NewPair(LTC, USD),
NewPair(USD, NZD),
NewPair(LTC, USDT),
}
contains(t, []Code{BTC, USD, LTC, NZD, USDT}, pairs.GetCurrencies())
}
func TestGetStables(t *testing.T) {
pairs := Pairs{
NewPair(BTC, USD),
NewPair(LTC, USD),
NewPair(USD, NZD),
NewPair(LTC, USDT),
NewPair(DAI, USDT),
NewPair(LTC, USDC),
NewPair(USDP, USDT),
}
contains(t, []Code{USDT, USDP, USDC, DAI}, pairs.GetStables())
}
func contains(t *testing.T, c1, c2 []Code) {
t.Helper()
codes:
for x := range c1 {
for y := range c2 {
if c1[x].Equal(c2[y]) {
continue codes
}
}
t.Fatalf("cannot find currency %s in returned currency list %v", c1[x], c2)
}
}
// Current: 6176922 260.0 ns/op 48 B/op 1 allocs/op
// Prior: 2575473 474.2 ns/op 112 B/op 3 allocs/op
func BenchmarkGetCrypto(b *testing.B) {
pairs := Pairs{
NewPair(BTC, USD),
NewPair(LTC, USD),
NewPair(USD, NZD),
NewPair(LTC, USDT),
}
for x := 0; x < b.N; x++ {
_ = pairs.GetCrypto()
}
}
func TestGetMatch(t *testing.T) {
pairs := Pairs{
NewPair(BTC, USD),
NewPair(LTC, USD),
NewPair(USD, NZD),
NewPair(LTC, USDT),
}
_, err := pairs.GetMatch(NewPair(BTC, WABI))
if !errors.Is(err, ErrPairNotFound) {
t.Fatalf("received: '%v' but expected '%v'", err, ErrPairNotFound)
}
expected := NewPair(BTC, USD)
match, err := pairs.GetMatch(expected)
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected '%v'", err, nil)
}
if !match.Equal(expected) {
t.Fatalf("received: '%v' but expected '%v'", match, expected)
}
match, err = pairs.GetMatch(NewPair(USD, BTC))
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected '%v'", err, nil)
}
if !match.Equal(expected) {
t.Fatalf("received: '%v' but expected '%v'", match, expected)
}
}
func TestGetStablesMatch(t *testing.T) {
pairs := Pairs{
NewPair(BTC, USD),
NewPair(LTC, USD),
NewPair(USD, NZD),
NewPair(LTC, USDT),
NewPair(LTC, DAI),
NewPair(USDT, XRP),
NewPair(DAI, XRP),
}
stablePairs := pairs.GetStablesMatch(BTC)
if len(stablePairs) != 0 {
t.Fatal("unexpected value")
}
stablePairs = pairs.GetStablesMatch(USD)
if len(stablePairs) != 0 {
t.Fatal("unexpected value")
}
stablePairs = pairs.GetStablesMatch(LTC)
if len(stablePairs) != 2 {
t.Fatal("unexpected value")
}
if !stablePairs[0].Equal(NewPair(LTC, USDT)) {
t.Fatal("unexpected value")
}
if !stablePairs[1].Equal(NewPair(LTC, DAI)) {
t.Fatal("unexpected value")
}
stablePairs = pairs.GetStablesMatch(XRP)
if len(stablePairs) != 2 {
t.Fatal("unexpected value")
}
if !stablePairs[0].Equal(NewPair(USDT, XRP)) {
t.Fatal("unexpected value")
}
if !stablePairs[1].Equal(NewPair(DAI, XRP)) {
t.Fatal("unexpected value")
}
}
// Current: 5594431 217.4 ns/op 168 B/op 8 allocs/op
// Prev: 3490366 373.4 ns/op 296 B/op 11 allocs/op
func BenchmarkPairsString(b *testing.B) {
pairs := Pairs{
NewPair(BTC, USD),
NewPair(LTC, USD),
NewPair(USD, NZD),
NewPair(LTC, USDT),
NewPair(LTC, DAI),
NewPair(USDT, XRP),
NewPair(DAI, XRP),
}
for x := 0; x < b.N; x++ {
_ = pairs.Strings()
}
}
// Current: 6691011 184.6 ns/op 352 B/op 1 allocs/op
// Prev: 3746151 317.1 ns/op 720 B/op 4 allocs/op
func BenchmarkPairsFormat(b *testing.B) {
pairs := Pairs{
NewPair(BTC, USD),
NewPair(LTC, USD),
NewPair(USD, NZD),
NewPair(LTC, USDT),
NewPair(LTC, DAI),
NewPair(USDT, XRP),
NewPair(DAI, XRP),
}
for x := 0; x < b.N; x++ {
_ = pairs.Format("/", "", false)
}
}
// current: 13075897 100.4 ns/op 352 B/op 1 allocs/o
// prev: 8188616 148.0 ns/op 336 B/op 3 allocs/op
func BenchmarkRemovePairsByFilter(b *testing.B) {
pairs := Pairs{
NewPair(BTC, USD),
NewPair(LTC, USD),
NewPair(USD, NZD),
NewPair(LTC, USDT),
NewPair(LTC, DAI),
NewPair(USDT, XRP),
NewPair(DAI, XRP),
}
for x := 0; x < b.N; x++ {
_ = pairs.RemovePairsByFilter(USD)
}
}

View File

@@ -15,10 +15,38 @@ import (
"github.com/thrasher-corp/gocryptotrader/log"
)
// storage is an overarching type that keeps track of and updates currency,
// currency exchange rates and pairs
var storage Storage
func init() {
storage.SetDefaults()
}
// 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"
)
var (
// ErrFiatDisplayCurrencyIsNotFiat defines an error for when the fiat
// display currency is not set as a fiat currency.
ErrFiatDisplayCurrencyIsNotFiat = errors.New("fiat display currency is not a fiat currency")
errUnexpectedRole = errors.New("unexpected currency role")
errFiatDisplayCurrencyUnset = errors.New("fiat display currency is unset")
errNoFilePathSet = errors.New("no file path set")
errInvalidCurrencyFileUpdateDuration = errors.New("invalid currency file update duration")
errInvalidForeignExchangeUpdateDuration = errors.New("invalid foreign exchange update duration")
errNoForeignExchangeProvidersEnabled = errors.New("no foreign exchange providers enabled")
errNotFiatCurrency = errors.New("not a fiat currency")
errInvalidAmount = errors.New("invalid amount")
)
// SetDefaults sets storage defaults for basic package functionality
func (s *Storage) SetDefaults() {
s.defaultBaseCurrency = USD
@@ -31,14 +59,19 @@ func (s *Storage) SetDefaults() {
fiatCurrencies = append(fiatCurrencies, Code{Item: item, UpperCase: true})
}
err := s.SetDefaultFiatCurrencies(fiatCurrencies...)
err := s.SetDefaultFiatCurrencies(fiatCurrencies)
if err != nil {
log.Errorf(log.Global, "Currency Storage: Setting default fiat currencies error: %s", err)
log.Errorf(log.Currency, "Currency Storage: Setting default fiat currencies error: %s", err)
}
err = s.SetDefaultCryptocurrencies(BTC, LTC, ETH, DOGE, DASH, XRP, XMR, USDT, UST)
err = s.SetStableCoins(stables)
if err != nil {
log.Errorf(log.Global, "Currency Storage: Setting default cryptocurrencies error: %s", err)
log.Errorf(log.Currency, "Currency Storage: Setting default stable currencies error: %s", err)
}
err = s.SetDefaultCryptocurrencies(Currencies{BTC, LTC, ETH, DOGE, DASH, XRP, XMR, USDT, UST})
if err != nil {
log.Errorf(log.Currency, "Currency Storage: Setting default cryptocurrencies error: %s", err)
}
s.SetupConversionRates()
s.fiatExchangeMarkets = forexprovider.NewDefaultFXProvider()
@@ -48,142 +81,139 @@ func (s *Storage) SetDefaults() {
// dump file and keep foreign exchange rates updated as fast as possible without
// triggering rate limiters, it will also run a full cryptocurrency check
// through coin market cap and expose analytics for exchange services
func (s *Storage) RunUpdater(overrides BotOverrides, settings *MainConfiguration, filePath string) error {
s.mtx.Lock()
s.shutdown = make(chan struct{})
if !settings.Cryptocurrencies.HasData() {
s.mtx.Unlock()
return errors.New("currency storage error, no cryptocurrencies loaded")
}
s.cryptocurrencies = settings.Cryptocurrencies
func (s *Storage) RunUpdater(overrides BotOverrides, settings *Config, filePath string) error {
if settings.FiatDisplayCurrency.IsEmpty() {
s.mtx.Unlock()
return errors.New("currency storage error, no fiat display currency set in config")
return errFiatDisplayCurrencyUnset
}
s.baseCurrency = settings.FiatDisplayCurrency
log.Debugf(log.Global,
"Fiat display currency: %s.\n",
s.baseCurrency)
if settings.CryptocurrencyProvider.Enabled {
log.Debugln(log.Global,
"Setting up currency analysis system with Coinmarketcap...")
c := &coinmarketcap.Coinmarketcap{}
c.SetDefaults()
err := c.Setup(coinmarketcap.Settings{
Name: settings.CryptocurrencyProvider.Name,
Enabled: settings.CryptocurrencyProvider.Enabled,
AccountPlan: settings.CryptocurrencyProvider.AccountPlan,
APIkey: settings.CryptocurrencyProvider.APIkey,
Verbose: settings.CryptocurrencyProvider.Verbose,
})
if err != nil {
log.Errorf(log.Global,
"Unable to setup CoinMarketCap analysis. Error: %s", err)
settings.CryptocurrencyProvider.Enabled = false
} else {
s.currencyAnalysis = c
}
if !settings.FiatDisplayCurrency.IsFiatCurrency() {
return fmt.Errorf("%s: %w", settings.FiatDisplayCurrency, ErrFiatDisplayCurrencyIsNotFiat)
}
if filePath == "" {
s.mtx.Unlock()
return errors.New("currency package runUpdater error filepath not set")
return errNoFilePathSet
}
if settings.CurrencyFileUpdateDuration <= 0 {
return errInvalidCurrencyFileUpdateDuration
}
if settings.ForeignExchangeUpdateDuration <= 0 {
return errInvalidForeignExchangeUpdateDuration
}
s.mtx.Lock()
s.shutdown = make(chan struct{})
s.baseCurrency = settings.FiatDisplayCurrency
s.path = filepath.Join(filePath, DefaultStorageFile)
s.currencyFileUpdateDelay = settings.CurrencyFileUpdateDuration
s.foreignExchangeUpdateDelay = settings.ForeignExchangeUpdateDuration
if settings.CurrencyDelay.Nanoseconds() == 0 {
s.currencyFileUpdateDelay = DefaultCurrencyFileDelay
} else {
s.currencyFileUpdateDelay = settings.CurrencyDelay
}
if settings.FxRateDelay.Nanoseconds() == 0 {
s.foreignExchangeUpdateDelay = DefaultForeignExchangeDelay
} else {
s.foreignExchangeUpdateDelay = settings.FxRateDelay
log.Debugf(log.Currency, "Fiat display currency: %s.\n", s.baseCurrency)
var err error
if overrides.Coinmarketcap {
if settings.CryptocurrencyProvider.APIKey != "" &&
settings.CryptocurrencyProvider.APIKey != "Key" {
log.Debugln(log.Currency, "Setting up currency analysis system with Coinmarketcap...")
s.currencyAnalysis, err = coinmarketcap.NewFromSettings(coinmarketcap.Settings(settings.CryptocurrencyProvider))
if err != nil {
log.Errorf(log.Currency, "Unable to setup CoinMarketCap analysis. Error: %s", err)
}
} else {
log.Warnf(log.Currency, "%s API key not set, disabling. Please set this in your config.json file\n",
settings.CryptocurrencyProvider.Name)
}
}
var fxSettings []base.Settings
var primaryProvider bool
for i := range settings.ForexProviders {
switch settings.ForexProviders[i].Name {
case "CurrencyConverter":
if overrides.FxCurrencyConverter ||
settings.ForexProviders[i].Enabled {
settings.ForexProviders[i].Enabled = true
fxSettings = append(fxSettings,
base.Settings(settings.ForexProviders[i]))
enabled := (settings.ForexProviders[i].Name == "CurrencyConverter" && overrides.CurrencyConverter) ||
(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)
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
}
case "CurrencyLayer":
if overrides.FxCurrencyLayer || settings.ForexProviders[i].Enabled {
settings.ForexProviders[i].Enabled = true
fxSettings = append(fxSettings,
base.Settings(settings.ForexProviders[i]))
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)
}
}
case "Fixer":
if overrides.FxFixer || settings.ForexProviders[i].Enabled {
settings.ForexProviders[i].Enabled = true
fxSettings = append(fxSettings,
base.Settings(settings.ForexProviders[i]))
if settings.ForexProviders[i].PrimaryProvider {
if primaryProvider {
log.Warnf(log.Currency, "%s disabling primary provider, multiple primarys found. Please review providers in your config.json file\n",
settings.ForexProviders[i].Name)
settings.ForexProviders[i].PrimaryProvider = false
} else {
primaryProvider = true
}
}
fxSettings = append(fxSettings, base.Settings(settings.ForexProviders[i]))
}
case "OpenExchangeRates":
if overrides.FxOpenExchangeRates ||
settings.ForexProviders[i].Enabled {
settings.ForexProviders[i].Enabled = true
fxSettings = append(fxSettings,
base.Settings(settings.ForexProviders[i]))
}
case "ExchangeRates":
// TODO ADD OVERRIDE
if settings.ForexProviders[i].Enabled {
settings.ForexProviders[i].Enabled = true
fxSettings = append(fxSettings,
base.Settings(settings.ForexProviders[i]))
}
case "ExchangeRateHost":
if overrides.FxExchangeRateHost || settings.ForexProviders[i].Enabled {
settings.ForexProviders[i].Enabled = true
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 {
var err error
s.fiatExchangeMarkets, err = forexprovider.StartFXService(fxSettings)
if err != nil {
s.mtx.Unlock()
return err
}
log.Debugf(log.Global,
"Primary foreign exchange conversion provider %s enabled\n",
s.fiatExchangeMarkets.Primary.Provider.GetName())
for i := range s.fiatExchangeMarkets.Support {
log.Debugf(log.Global,
"Support forex conversion provider %s enabled\n",
s.fiatExchangeMarkets.Support[i].Provider.GetName())
}
// Mutex present in this go routine to lock down retrieving rate data
// until this system initially updates
go s.ForeignExchangeUpdater()
} else {
log.Warnln(log.Global,
"No foreign exchange providers enabled in config.json")
if len(fxSettings) == 0 {
s.mtx.Unlock()
return errNoForeignExchangeProvidersEnabled
}
if !primaryProvider {
for x := range settings.ForexProviders {
if settings.ForexProviders[x].Name == fxSettings[0].Name {
settings.ForexProviders[x].PrimaryProvider = true
fxSettings[0].PrimaryProvider = true
log.Warnf(log.Currency, "No primary foreign exchange provider set. Defaulting to %s.", fxSettings[0].Name)
break
}
}
}
s.fiatExchangeMarkets, err = forexprovider.StartFXService(fxSettings)
if err != nil {
s.mtx.Unlock()
return err
}
log.Debugf(log.Currency, "Using primary foreign exchange provider %s\n",
s.fiatExchangeMarkets.Primary.Provider.GetName())
for i := range s.fiatExchangeMarkets.Support {
log.Debugf(log.Currency, "Supporting foreign exchange provider %s\n",
s.fiatExchangeMarkets.Support[i].Provider.GetName())
}
// Mutex present in this go routine to lock down retrieving rate data
// until this system initially updates
s.wg.Add(1)
go s.ForeignExchangeUpdater()
return nil
}
@@ -196,7 +226,7 @@ func (s *Storage) SetupConversionRates() {
// SetDefaultFiatCurrencies assigns the default fiat currency list and adds it
// to the running list
func (s *Storage) SetDefaultFiatCurrencies(c ...Code) error {
func (s *Storage) SetDefaultFiatCurrencies(c Currencies) error {
for i := range c {
err := s.currencyCodes.UpdateCurrency("", c[i].String(), "", 0, Fiat)
if err != nil {
@@ -208,9 +238,22 @@ func (s *Storage) SetDefaultFiatCurrencies(c ...Code) error {
return nil
}
// SetStableCoins assigns the stable currency list and adds it to the running
// list
func (s *Storage) SetStableCoins(c Currencies) error {
for i := range c {
err := s.currencyCodes.UpdateCurrency("", c[i].String(), "", 0, Stable)
if err != nil {
return err
}
}
s.stableCurrencies = append(s.stableCurrencies, c...)
return nil
}
// SetDefaultCryptocurrencies assigns the default cryptocurrency list and adds
// it to the running list
func (s *Storage) SetDefaultCryptocurrencies(c ...Code) error {
func (s *Storage) SetDefaultCryptocurrencies(c Currencies) error {
for i := range c {
err := s.currencyCodes.UpdateCurrency("",
c[i].String(),
@@ -240,20 +283,17 @@ func (s *Storage) SetupForexProviders(setting ...base.Settings) error {
// ForeignExchangeUpdater is a routine that seeds foreign exchange rate and keeps
// updated as fast as possible
func (s *Storage) ForeignExchangeUpdater() {
log.Debugln(log.Global,
"Foreign exchange updater started, seeding FX rate list..")
s.wg.Add(1)
defer s.wg.Done()
log.Debugln(log.Currency, "Foreign exchange updater started, seeding FX rate list...")
err := s.SeedCurrencyAnalysisData()
if err != nil {
log.Errorln(log.Global, err)
log.Errorln(log.Currency, err)
}
err = s.SeedForeignExchangeRates()
if err != nil {
log.Errorln(log.Global, err)
log.Errorln(log.Currency, err)
}
// Unlock main rate retrieval mutex so all routines waiting can get access
@@ -270,20 +310,18 @@ func (s *Storage) ForeignExchangeUpdater() {
select {
case <-s.shutdown:
return
case <-SeedForeignExchangeTick.C:
go func() {
err := s.SeedForeignExchangeRates()
if err != nil {
log.Errorln(log.Global, err)
log.Errorln(log.Currency, err)
}
}()
case <-SeedCurrencyAnalysisTick.C:
go func() {
err := s.SeedCurrencyAnalysisData()
if err != nil {
log.Errorln(log.Global, err)
log.Errorln(log.Currency, err)
}
}()
}
@@ -324,7 +362,7 @@ func (s *Storage) SeedCurrencyAnalysisData() error {
// loads it into memory
func (s *Storage) FetchCurrencyAnalysisData() error {
if s.currencyAnalysis == nil {
log.Warnln(log.Global,
log.Warnln(log.Currency,
"Currency analysis system offline, please set api keys for coinmarketcap if you wish to use this feature.")
return errors.New("currency analysis system offline")
}
@@ -354,48 +392,55 @@ func (s *Storage) WriteCurrencyDataToFile(path string, mainUpdate bool) error {
return file.Write(path, encoded)
}
func (s *Storage) checkFileCurrencyData(item *Item, role Role) error {
if item.Role == Unset {
item.Role = role
}
if item.Role != role {
return fmt.Errorf("%w %s expecting: %s", errUnexpectedRole, item.Role, role)
}
return s.currencyCodes.LoadItem(item)
}
// LoadFileCurrencyData loads currencies into the currency codes
func (s *Storage) LoadFileCurrencyData(f *File) error {
for i := range f.Contracts {
contract := f.Contracts[i]
contract.Role = Contract
err := s.currencyCodes.LoadItem(&contract)
err := s.checkFileCurrencyData(f.Contracts[i], Contract)
if err != nil {
return err
}
}
for i := range f.Cryptocurrency {
crypto := f.Cryptocurrency[i]
crypto.Role = Cryptocurrency
err := s.currencyCodes.LoadItem(&crypto)
err := s.checkFileCurrencyData(f.Cryptocurrency[i], Cryptocurrency)
if err != nil {
return err
}
}
for i := range f.Token {
token := f.Token[i]
token.Role = Token
err := s.currencyCodes.LoadItem(&token)
err := s.checkFileCurrencyData(f.Token[i], Token)
if err != nil {
return err
}
}
for i := range f.FiatCurrency {
fiat := f.FiatCurrency[i]
fiat.Role = Fiat
err := s.currencyCodes.LoadItem(&fiat)
err := s.checkFileCurrencyData(f.FiatCurrency[i], Fiat)
if err != nil {
return err
}
}
for i := range f.UnsetCurrency {
unset := f.UnsetCurrency[i]
unset.Role = Unset
err := s.currencyCodes.LoadItem(&unset)
err := s.checkFileCurrencyData(f.UnsetCurrency[i], Unset)
if err != nil {
return err
}
}
for i := range f.Stable {
err := s.checkFileCurrencyData(f.Stable[i], Stable)
if err != nil {
return err
}
@@ -526,21 +571,6 @@ func (s *Storage) updateExchangeRates(m map[string]float64) error {
return s.fxRates.Update(m)
}
// SetupCryptoProvider sets congiguration parameters and starts a new instance
// of the currency analyser
func (s *Storage) SetupCryptoProvider(settings coinmarketcap.Settings) error {
if settings.APIkey == "" ||
settings.APIkey == "key" ||
settings.AccountPlan == "" ||
settings.AccountPlan == "accountPlan" {
return errors.New("currencyprovider error api key or plan not set in config.json")
}
s.currencyAnalysis = new(coinmarketcap.Coinmarketcap)
s.currencyAnalysis.SetDefaults()
return s.currencyAnalysis.Setup(settings)
}
// GetTotalMarketCryptocurrencies returns the total seeded market
// cryptocurrencies
func (s *Storage) GetTotalMarketCryptocurrencies() (Currencies, error) {
@@ -553,8 +583,8 @@ func (s *Storage) GetTotalMarketCryptocurrencies() (Currencies, error) {
// IsDefaultCurrency returns if a currency is a default currency
func (s *Storage) IsDefaultCurrency(c Code) bool {
for i := range s.defaultFiatCurrencies {
if s.defaultFiatCurrencies[i].Match(c) ||
s.defaultFiatCurrencies[i].Match(GetTranslation(c)) {
if s.defaultFiatCurrencies[i].Equal(c) ||
s.defaultFiatCurrencies[i].Equal(GetTranslation(c)) {
return true
}
}
@@ -565,83 +595,30 @@ func (s *Storage) IsDefaultCurrency(c Code) bool {
// cryptocurrency
func (s *Storage) IsDefaultCryptocurrency(c Code) bool {
for i := range s.defaultCryptoCurrencies {
if s.defaultCryptoCurrencies[i].Match(c) ||
s.defaultCryptoCurrencies[i].Match(GetTranslation(c)) {
if s.defaultCryptoCurrencies[i].Equal(c) ||
s.defaultCryptoCurrencies[i].Equal(GetTranslation(c)) {
return true
}
}
return false
}
// IsFiatCurrency returns if a currency is part of the enabled fiat currency
// list
func (s *Storage) IsFiatCurrency(c Code) bool {
if c.Item.Role != Unset {
return c.Item.Role == Fiat
}
if c == USDT {
return false
}
for i := range s.fiatCurrencies {
if s.fiatCurrencies[i].Match(c) ||
s.fiatCurrencies[i].Match(GetTranslation(c)) {
return true
}
}
return false
}
// IsCryptocurrency returns if a cryptocurrency is part of the enabled
// cryptocurrency list
func (s *Storage) IsCryptocurrency(c Code) bool {
if c.Item.Role != Unset {
return c.Item.Role == Cryptocurrency
}
if c == USD {
return false
}
for i := range s.cryptocurrencies {
if s.cryptocurrencies[i].Match(c) ||
s.cryptocurrencies[i].Match(GetTranslation(c)) {
return true
}
}
return false
}
// ValidateCode validates string against currency list and returns a currency
// code
func (s *Storage) ValidateCode(newCode string) Code {
return s.currencyCodes.Register(newCode)
return s.currencyCodes.Register(newCode, Unset)
}
// ValidateFiatCode validates a fiat currency string and returns a currency
// code
func (s *Storage) ValidateFiatCode(newCode string) Code {
c := s.currencyCodes.RegisterFiat(newCode)
c := s.currencyCodes.Register(newCode, Fiat)
if !s.fiatCurrencies.Contains(c) {
s.fiatCurrencies = append(s.fiatCurrencies, c)
}
return c
}
// ValidateCryptoCode validates a cryptocurrency string and returns a currency
// code
// TODO: Update and add in RegisterCrypto member func
func (s *Storage) ValidateCryptoCode(newCode string) Code {
c := s.currencyCodes.Register(newCode)
if !s.cryptocurrencies.Contains(c) {
s.cryptocurrencies = append(s.cryptocurrencies, c)
}
return c
}
// UpdateBaseCurrency changes base currency
func (s *Storage) UpdateBaseCurrency(c Code) error {
if c.IsFiatCurrency() {
@@ -705,9 +682,22 @@ 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 amount <= 0 {
return 0, fmt.Errorf("%f %w", amount, errInvalidAmount)
}
if !from.IsFiatCurrency() {
return 0, fmt.Errorf("%s %w", from, errNotFiatCurrency)
}
if !to.IsFiatCurrency() {
return 0, fmt.Errorf("%s %w", to, errNotFiatCurrency)
}
if from.Equal(to) { // No need to lock down storage for this rate.
return amount, nil
}
s.mtx.Lock()
defer s.mtx.Unlock()
if !s.fxRates.HasData() {
err := s.SeedDefaultForeignExchangeRates()
if err != nil {
@@ -762,7 +752,11 @@ func (s *Storage) IsVerbose() bool {
func (s *Storage) Shutdown() error {
s.mtx.Lock()
defer s.mtx.Unlock()
if s.shutdown == nil {
return nil
}
close(s.shutdown)
s.wg.Wait()
s.shutdown = nil
return s.WriteCurrencyDataToFile(s.path, true)
}

View File

@@ -1,28 +1,249 @@
package currency
import "testing"
import (
"errors"
"fmt"
"io/ioutil"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/database/testhelpers"
)
func TestMain(m *testing.M) {
var err error
testhelpers.TempDir, err = ioutil.TempDir("", "gct-temp")
if err != nil {
fmt.Printf("failed to create temp file: %v", err)
os.Exit(1)
}
t := m.Run()
err = os.RemoveAll(testhelpers.TempDir)
if err != nil {
fmt.Printf("Failed to remove temp db file: %v", err)
}
os.Exit(t)
}
func TestRunUpdater(t *testing.T) {
var newStorage Storage
emptyMainConfig := MainConfiguration{}
emptyMainConfig := Config{}
err := newStorage.RunUpdater(BotOverrides{}, &emptyMainConfig, "")
if err == nil {
t.Fatal("storage RunUpdater() error cannot be nil")
}
mainConfig := MainConfiguration{
Cryptocurrencies: NewCurrenciesFromStringArray([]string{"BTC"}),
FiatDisplayCurrency: USD,
}
mainConfig := Config{}
err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, "")
if err == nil {
t.Fatal("storage RunUpdater() error cannot be nil")
if !errors.Is(err, errFiatDisplayCurrencyUnset) {
t.Fatalf("received: '%v' but expected: '%v'", err, errFiatDisplayCurrencyUnset)
}
err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, "/bla")
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)
}
tempDir := testhelpers.TempDir
err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, tempDir)
if !errors.Is(err, errInvalidCurrencyFileUpdateDuration) {
t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidCurrencyFileUpdateDuration)
}
mainConfig.CurrencyFileUpdateDuration = DefaultCurrencyFileDelay
err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, tempDir)
if !errors.Is(err, errInvalidForeignExchangeUpdateDuration) {
t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidForeignExchangeUpdateDuration)
}
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")
}
err = newStorage.Shutdown()
if err != nil {
t.Fatal("storage RunUpdater() error", err)
t.Fatal(err)
}
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")
}
err = newStorage.Shutdown()
if err != nil {
t.Fatal(err)
}
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")
}
}

View File

@@ -8,29 +8,19 @@ import (
"github.com/thrasher-corp/gocryptotrader/currency/forexprovider"
)
// CurrencyFileUpdateDelay defines the rate at which the currency.json file is
// updated
const (
DefaultCurrencyFileDelay = 168 * time.Hour
DefaultForeignExchangeDelay = 1 * time.Minute
DefaultStorageFile = "currency.json"
)
// storage is an overarching type that keeps track of and updates currency,
// currency exchange rates and pairs
var storage Storage
// Storage contains the loaded storage currencies supported on available crypto
// or fiat marketplaces
// NOTE: All internal currencies are upper case
type Storage struct {
// FiatCurrencies defines the running fiat currencies in the currency
// fiatCurrencies defines the running fiat currencies in the currency
// storage
fiatCurrencies Currencies
// Cryptocurrencies defines the running cryptocurrencies in the currency
// cryptocurrencies defines the running cryptocurrencies in the currency
// storage
cryptocurrencies Currencies
// CurrencyCodes is a full basket of currencies either crypto, fiat, ico or
// stableCurrencies defines the running stable currencies
stableCurrencies Currencies
// currencyCodes is a full basket of currencies either crypto, fiat, ico or
// contract being tracked by the currency storage system
currencyCodes BaseCodes
// Main converting currency

View File

@@ -12,7 +12,7 @@ func TestGetSymbolByCurrencyName(t *testing.T) {
t.Errorf("TestGetSymbolByCurrencyName differing values")
}
_, err = GetSymbolByCurrencyName(Code{})
_, err = GetSymbolByCurrencyName(EMPTYCODE)
if err == nil {
t.Errorf("TestGetSymbolByCurrencyNam returned nil on non-existent currency")
}

View File

@@ -3,20 +3,20 @@ package currency
// GetTranslation returns similar strings for a particular currency if not found
// returns the code back
func GetTranslation(currency Code) Code {
val, ok := translations[currency]
val, ok := translations[currency.Item]
if !ok {
return currency
}
return val
}
var translations = map[Code]Code{
BTC: XBT,
ETH: XETH,
DOGE: XDG,
USD: USDT,
XBT: BTC,
XETH: ETH,
XDG: DOGE,
USDT: USD,
var translations = map[*Item]Code{
BTC.Item: XBT,
ETH.Item: XETH,
DOGE.Item: XDG,
USD.Item: USDT,
XBT.Item: BTC,
XETH.Item: ETH,
XDG.Item: DOGE,
USDT.Item: USD,
}

View File

@@ -1,18 +1,20 @@
package currency
import "testing"
import (
"testing"
)
func TestGetTranslation(t *testing.T) {
currencyPair := NewPair(BTC, USD)
expected := XBT
actual := GetTranslation(currencyPair.Base)
if expected != actual {
if !expected.Equal(actual) {
t.Error("GetTranslation: translation result was different to expected result")
}
currencyPair.Base = NEO
actual = GetTranslation(currencyPair.Base)
if actual != currencyPair.Base {
if !actual.Equal(currencyPair.Base) {
t.Error("GetTranslation: no error on non translatable currency")
}
@@ -20,7 +22,15 @@ func TestGetTranslation(t *testing.T) {
currencyPair.Base = XBT
actual = GetTranslation(currencyPair.Base)
if expected != actual {
if !expected.Equal(actual) {
t.Error("GetTranslation: translation result was different to expected result")
}
// This test accentuates the issue of comparing code types as this will
// not match for lower and upper differences and a key (*Item) needs to be
// used.
// Code{Item: 0xc000094140, Upper: true} != Code{Item: 0xc000094140, Upper: false}
if actual = GetTranslation(NewCode("btc")); !XBT.Equal(actual) {
t.Errorf("received: '%v', but expected: '%v'", actual, XBT)
}
}

View File

@@ -240,7 +240,7 @@ func TestGetAllRPC(t *testing.T) {
func TestCanWithdrawRPC(t *testing.T) {
t.Parallel()
_, err := (*CurrencyStateManager)(nil).CanWithdrawRPC("", currency.Code{}, "")
_, err := (*CurrencyStateManager)(nil).CanWithdrawRPC("", currency.EMPTYCODE, "")
if !errors.Is(err, ErrSubSystemNotStarted) {
t.Fatalf("received: '%v' but expected: '%v'", err, ErrSubSystemNotStarted)
}
@@ -248,7 +248,7 @@ func TestCanWithdrawRPC(t *testing.T) {
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{ErrorMeOne: true},
}).CanWithdrawRPC("", currency.Code{}, "")
}).CanWithdrawRPC("", currency.EMPTYCODE, "")
if !errors.Is(err, errManager) {
t.Fatalf("received: '%v' but expected: '%v'", err, errManager)
}
@@ -256,7 +256,7 @@ func TestCanWithdrawRPC(t *testing.T) {
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{ErrorMeTwo: true},
}).CanWithdrawRPC("", currency.Code{}, "")
}).CanWithdrawRPC("", currency.EMPTYCODE, "")
if !errors.Is(err, errExchange) {
t.Fatalf("received: '%v' but expected: '%v'", err, errExchange)
}
@@ -264,7 +264,7 @@ func TestCanWithdrawRPC(t *testing.T) {
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{},
}).CanWithdrawRPC("", currency.Code{}, "")
}).CanWithdrawRPC("", currency.EMPTYCODE, "")
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
@@ -272,7 +272,7 @@ func TestCanWithdrawRPC(t *testing.T) {
func TestCanDepositRPC(t *testing.T) {
t.Parallel()
_, err := (*CurrencyStateManager)(nil).CanDepositRPC("", currency.Code{}, "")
_, err := (*CurrencyStateManager)(nil).CanDepositRPC("", currency.EMPTYCODE, "")
if !errors.Is(err, ErrSubSystemNotStarted) {
t.Fatalf("received: '%v' but expected: '%v'", err, ErrSubSystemNotStarted)
}
@@ -280,7 +280,7 @@ func TestCanDepositRPC(t *testing.T) {
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{ErrorMeOne: true},
}).CanDepositRPC("", currency.Code{}, "")
}).CanDepositRPC("", currency.EMPTYCODE, "")
if !errors.Is(err, errManager) {
t.Fatalf("received: '%v' but expected: '%v'", err, errManager)
}
@@ -288,7 +288,7 @@ func TestCanDepositRPC(t *testing.T) {
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{ErrorMeTwo: true},
}).CanDepositRPC("", currency.Code{}, "")
}).CanDepositRPC("", currency.EMPTYCODE, "")
if !errors.Is(err, errExchange) {
t.Fatalf("received: '%v' but expected: '%v'", err, errExchange)
}
@@ -296,7 +296,7 @@ func TestCanDepositRPC(t *testing.T) {
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{},
}).CanDepositRPC("", currency.Code{}, "")
}).CanDepositRPC("", currency.EMPTYCODE, "")
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
@@ -304,7 +304,7 @@ func TestCanDepositRPC(t *testing.T) {
func TestCanTradeRPC(t *testing.T) {
t.Parallel()
_, err := (*CurrencyStateManager)(nil).CanTradeRPC("", currency.Code{}, "")
_, err := (*CurrencyStateManager)(nil).CanTradeRPC("", currency.EMPTYCODE, "")
if !errors.Is(err, ErrSubSystemNotStarted) {
t.Fatalf("received: '%v' but expected: '%v'", err, ErrSubSystemNotStarted)
}
@@ -312,7 +312,7 @@ func TestCanTradeRPC(t *testing.T) {
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{ErrorMeOne: true},
}).CanTradeRPC("", currency.Code{}, "")
}).CanTradeRPC("", currency.EMPTYCODE, "")
if !errors.Is(err, errManager) {
t.Fatalf("received: '%v' but expected: '%v'", err, errManager)
}
@@ -320,7 +320,7 @@ func TestCanTradeRPC(t *testing.T) {
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{ErrorMeTwo: true},
}).CanTradeRPC("", currency.Code{}, "")
}).CanTradeRPC("", currency.EMPTYCODE, "")
if !errors.Is(err, errExchange) {
t.Fatalf("received: '%v' but expected: '%v'", err, errExchange)
}
@@ -328,7 +328,7 @@ func TestCanTradeRPC(t *testing.T) {
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{},
}).CanTradeRPC("", currency.Code{}, "")
}).CanTradeRPC("", currency.EMPTYCODE, "")
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
@@ -336,7 +336,7 @@ func TestCanTradeRPC(t *testing.T) {
func TestCanTradePairRPC(t *testing.T) {
t.Parallel()
_, err := (*CurrencyStateManager)(nil).CanTradePairRPC("", currency.Pair{}, "")
_, err := (*CurrencyStateManager)(nil).CanTradePairRPC("", currency.EMPTYPAIR, "")
if !errors.Is(err, ErrSubSystemNotStarted) {
t.Fatalf("received: '%v' but expected: '%v'", err, ErrSubSystemNotStarted)
}
@@ -344,7 +344,7 @@ func TestCanTradePairRPC(t *testing.T) {
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{ErrorMeOne: true},
}).CanTradePairRPC("", currency.Pair{}, "")
}).CanTradePairRPC("", currency.EMPTYPAIR, "")
if !errors.Is(err, errManager) {
t.Fatalf("received: '%v' but expected: '%v'", err, errManager)
}
@@ -352,7 +352,7 @@ func TestCanTradePairRPC(t *testing.T) {
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{ErrorMeTwo: true},
}).CanTradePairRPC("", currency.Pair{}, "")
}).CanTradePairRPC("", currency.EMPTYPAIR, "")
if !errors.Is(err, errExchange) {
t.Fatalf("received: '%v' but expected: '%v'", err, errExchange)
}
@@ -360,7 +360,7 @@ func TestCanTradePairRPC(t *testing.T) {
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{},
}).CanTradePairRPC("", currency.Pair{}, "")
}).CanTradePairRPC("", currency.EMPTYPAIR, "")
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}

View File

@@ -15,7 +15,6 @@ import (
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/currency/coinmarketcap"
"github.com/thrasher-corp/gocryptotrader/dispatch"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
@@ -146,40 +145,44 @@ func loadConfigWithSettings(settings *Settings, flagSet map[string]bool) (*confi
return conf, conf.CheckConfig()
}
// FlagSet defines set flags from command line args for comparison methods
type FlagSet map[string]bool
// WithBool checks the supplied flag. If set it will overide the config boolean
// value as a command line takes precedence. If not set will fall back to config
// options.
func (f FlagSet) WithBool(key string, flagValue *bool, configValue bool) {
isSet := f[key]
*flagValue = !isSet && configValue || isSet && *flagValue
}
// validateSettings validates and sets all bot settings
func validateSettings(b *Engine, s *Settings, flagSet map[string]bool) {
func validateSettings(b *Engine, s *Settings, flagSet FlagSet) {
b.Settings = *s
b.Settings.EnableDataHistoryManager = (flagSet["datahistorymanager"] && b.Settings.EnableDatabaseManager) || b.Config.DataHistoryManager.Enabled
flagSet.WithBool("coinmarketcap", &b.Settings.EnableCoinmarketcapAnalysis, b.Config.Currency.CryptocurrencyProvider.Enabled)
b.Settings.EnableCurrencyStateManager = (flagSet["currencystatemanager"] &&
b.Settings.EnableCurrencyStateManager) ||
b.Config.CurrencyStateManager.Enabled != nil &&
*b.Config.CurrencyStateManager.Enabled
flagSet.WithBool("currencyconverter", &b.Settings.EnableCurrencyConverter, b.Config.Currency.ForexProviders.IsEnabled("currencyconverter"))
b.Settings.EnableGCTScriptManager = b.Settings.EnableGCTScriptManager &&
(flagSet["gctscriptmanager"] || b.Config.GCTScript.Enabled)
flagSet.WithBool("currencylayer", &b.Settings.EnableCurrencyLayer, b.Config.Currency.ForexProviders.IsEnabled("currencylayer"))
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)
flagSet.WithBool("gctscriptmanager", &b.Settings.EnableGCTScriptManager, b.Config.GCTScript.Enabled)
if b.Settings.EnablePortfolioManager &&
b.Settings.PortfolioManagerDelay <= 0 {
b.Settings.PortfolioManagerDelay = PortfolioSleepDelay
}
if !flagSet["grpc"] {
b.Settings.EnableGRPC = b.Config.RemoteControl.GRPC.Enabled
}
if !flagSet["grpcproxy"] {
b.Settings.EnableGRPCProxy = b.Config.RemoteControl.GRPC.GRPCProxyEnabled
}
if !flagSet["websocketrpc"] {
b.Settings.EnableWebsocketRPC = b.Config.RemoteControl.WebsocketRPC.Enabled
}
if !flagSet["deprecatedrpc"] {
b.Settings.EnableDeprecatedRPC = b.Config.RemoteControl.DeprecatedRPC.Enabled
}
flagSet.WithBool("grpc", &b.Settings.EnableGRPC, b.Config.RemoteControl.GRPC.Enabled)
flagSet.WithBool("grpcproxy", &b.Settings.EnableGRPCProxy, b.Config.RemoteControl.GRPC.GRPCProxyEnabled)
flagSet.WithBool("websocketrpc", &b.Settings.EnableWebsocketRPC, b.Config.RemoteControl.WebsocketRPC.Enabled)
flagSet.WithBool("deprecatedrpc", &b.Settings.EnableDeprecatedRPC, b.Config.RemoteControl.DeprecatedRPC.Enabled)
if flagSet["maxvirtualmachines"] {
maxMachines := uint8(b.Settings.MaxVirtualMachines)
@@ -250,7 +253,7 @@ func PrintSettings(s *Settings) {
gctlog.Debugf(gctlog.Global, "\t Enable dry run mode: %v", s.EnableDryRun)
gctlog.Debugf(gctlog.Global, "\t Enable all exchanges: %v", s.EnableAllExchanges)
gctlog.Debugf(gctlog.Global, "\t Enable all pairs: %v", s.EnableAllPairs)
gctlog.Debugf(gctlog.Global, "\t Enable coinmarketcap analysis: %v", s.EnableCoinmarketcapAnalysis)
gctlog.Debugf(gctlog.Global, "\t Enable CoinMarketCap analysis: %v", s.EnableCoinmarketcapAnalysis)
gctlog.Debugf(gctlog.Global, "\t Enable portfolio manager: %v", s.EnablePortfolioManager)
gctlog.Debugf(gctlog.Global, "\t Enable data history manager: %v", s.EnableDataHistoryManager)
gctlog.Debugf(gctlog.Global, "\t Enable currency state manager: %v", s.EnableCurrencyStateManager)
@@ -273,16 +276,17 @@ func PrintSettings(s *Settings) {
gctlog.Debugf(gctlog.Global, "\t Dispatch package jobs limit: %d", s.DispatchJobsLimit)
gctlog.Debugf(gctlog.Global, "- EXCHANGE SYNCER SETTINGS:\n")
gctlog.Debugf(gctlog.Global, "\t Exchange sync continuously: %v\n", s.SyncContinuously)
gctlog.Debugf(gctlog.Global, "\t Exchange sync workers: %v\n", s.SyncWorkers)
gctlog.Debugf(gctlog.Global, "\t Exchange sync workers count: %v\n", s.SyncWorkersCount)
gctlog.Debugf(gctlog.Global, "\t Enable ticker syncing: %v\n", s.EnableTickerSyncing)
gctlog.Debugf(gctlog.Global, "\t Enable orderbook syncing: %v\n", s.EnableOrderbookSyncing)
gctlog.Debugf(gctlog.Global, "\t Enable trade syncing: %v\n", s.EnableTradeSyncing)
gctlog.Debugf(gctlog.Global, "\t Exchange REST sync timeout: %v\n", s.SyncTimeoutREST)
gctlog.Debugf(gctlog.Global, "\t Exchange Websocket sync timeout: %v\n", s.SyncTimeoutWebsocket)
gctlog.Debugf(gctlog.Global, "- FOREX SETTINGS:")
gctlog.Debugf(gctlog.Global, "\t Enable currency converter: %v", s.EnableCurrencyConverter)
gctlog.Debugf(gctlog.Global, "\t Enable currency layer: %v", s.EnableCurrencyLayer)
gctlog.Debugf(gctlog.Global, "\t Enable fixer: %v", s.EnableFixer)
gctlog.Debugf(gctlog.Global, "\t Enable Currency Converter: %v", s.EnableCurrencyConverter)
gctlog.Debugf(gctlog.Global, "\t Enable Currency Layer: %v", s.EnableCurrencyLayer)
gctlog.Debugf(gctlog.Global, "\t Enable ExchangeRatesAPI.io: %v", s.EnableExchangeRates)
gctlog.Debugf(gctlog.Global, "\t Enable Fixer: %v", s.EnableFixer)
gctlog.Debugf(gctlog.Global, "\t Enable OpenExchangeRates: %v", s.EnableOpenExchangeRates)
gctlog.Debugf(gctlog.Global, "\t Enable ExchangeRateHost: %v", s.EnableExchangeRateHost)
gctlog.Debugf(gctlog.Global, "- EXCHANGE SETTINGS:")
@@ -407,32 +411,20 @@ func (bot *Engine) Start() error {
}
}
}
if bot.Settings.EnableCoinmarketcapAnalysis ||
bot.Settings.EnableCurrencyConverter ||
bot.Settings.EnableCurrencyLayer ||
bot.Settings.EnableFixer ||
bot.Settings.EnableOpenExchangeRates ||
bot.Settings.EnableExchangeRateHost {
err = currency.RunStorageUpdater(currency.BotOverrides{
Coinmarketcap: bot.Settings.EnableCoinmarketcapAnalysis,
FxCurrencyConverter: bot.Settings.EnableCurrencyConverter,
FxCurrencyLayer: bot.Settings.EnableCurrencyLayer,
FxFixer: bot.Settings.EnableFixer,
FxOpenExchangeRates: bot.Settings.EnableOpenExchangeRates,
FxExchangeRateHost: bot.Settings.EnableExchangeRateHost,
},
&currency.MainConfiguration{
ForexProviders: bot.Config.GetForexProviders(),
CryptocurrencyProvider: coinmarketcap.Settings(bot.Config.Currency.CryptocurrencyProvider),
Cryptocurrencies: bot.Config.Currency.Cryptocurrencies,
FiatDisplayCurrency: bot.Config.Currency.FiatDisplayCurrency,
CurrencyDelay: bot.Config.Currency.CurrencyFileUpdateDuration,
FxRateDelay: bot.Config.Currency.ForeignExchangeUpdateDuration,
},
bot.Settings.DataDir)
if err != nil {
gctlog.Errorf(gctlog.Global, "ExchangeSettings updater system failed to start %s", err)
}
err = currency.RunStorageUpdater(currency.BotOverrides{
Coinmarketcap: bot.Settings.EnableCoinmarketcapAnalysis,
CurrencyConverter: bot.Settings.EnableCurrencyConverter,
CurrencyLayer: bot.Settings.EnableCurrencyLayer,
ExchangeRates: bot.Settings.EnableExchangeRates,
Fixer: bot.Settings.EnableFixer,
OpenExchangeRates: bot.Settings.EnableOpenExchangeRates,
ExchangeRateHost: bot.Settings.EnableExchangeRateHost,
},
&bot.Config.Currency,
bot.Settings.DataDir)
if err != nil {
gctlog.Errorf(gctlog.Global, "ExchangeSettings updater system failed to start %s", err)
}
if bot.Settings.EnableGRPC {
@@ -524,15 +516,17 @@ func (bot *Engine) Start() error {
}
if bot.Settings.EnableExchangeSyncManager {
exchangeSyncCfg := &Config{
SyncTicker: bot.Settings.EnableTickerSyncing,
SyncOrderbook: bot.Settings.EnableOrderbookSyncing,
SyncTrades: bot.Settings.EnableTradeSyncing,
SyncContinuously: bot.Settings.SyncContinuously,
NumWorkers: bot.Settings.SyncWorkers,
Verbose: bot.Settings.Verbose,
SyncTimeoutREST: bot.Settings.SyncTimeoutREST,
SyncTimeoutWebsocket: bot.Settings.SyncTimeoutWebsocket,
exchangeSyncCfg := &SyncManagerConfig{
SynchronizeTicker: bot.Settings.EnableTickerSyncing,
SynchronizeOrderbook: bot.Settings.EnableOrderbookSyncing,
SynchronizeTrades: bot.Settings.EnableTradeSyncing,
SynchronizeContinuously: bot.Settings.SyncContinuously,
TimeoutREST: bot.Settings.SyncTimeoutREST,
TimeoutWebsocket: bot.Settings.SyncTimeoutWebsocket,
NumWorkers: bot.Settings.SyncWorkersCount,
Verbose: bot.Settings.Verbose,
FiatDisplayCurrency: bot.Config.Currency.FiatDisplayCurrency,
PairFormatDisplay: bot.Config.Currency.CurrencyPairFormat,
}
bot.currencyPairSyncer, err = setupSyncManager(
@@ -692,15 +686,8 @@ func (bot *Engine) Stop() {
}
}
if bot.Settings.EnableCoinmarketcapAnalysis ||
bot.Settings.EnableCurrencyConverter ||
bot.Settings.EnableCurrencyLayer ||
bot.Settings.EnableFixer ||
bot.Settings.EnableOpenExchangeRates ||
bot.Settings.EnableExchangeRateHost {
if err := currency.ShutdownStorageUpdater(); err != nil {
gctlog.Errorf(gctlog.Global, "ExchangeSettings storage system. Error: %v", err)
}
if err := currency.ShutdownStorageUpdater(); err != nil {
gctlog.Errorf(gctlog.Global, "ExchangeSettings storage system. Error: %v", err)
}
if !bot.Settings.EnableDryRun {

View File

@@ -93,7 +93,7 @@ func TestStartStopDoesNotCausePanic(t *testing.T) {
DataDir: tempDir,
}, nil)
if err != nil {
t.Error(err)
t.Fatal(err)
}
botOne.Settings.EnableGRPCProxy = false
for i := range botOne.Config.Exchanges {
@@ -281,3 +281,41 @@ func TestDryRunParamInteraction(t *testing.T) {
t.Error("dryrun should be true and verbose should be true")
}
}
func TestFlagSetWith(t *testing.T) {
var isRunning bool
flags := make(FlagSet)
// Flag not set default to config
flags.WithBool("NOT SET", &isRunning, true)
if !isRunning {
t.Fatalf("received: '%v' but expected: '%v'", isRunning, true)
}
flags.WithBool("NOT SET", &isRunning, false)
if isRunning {
t.Fatalf("received: '%v' but expected: '%v'", isRunning, false)
}
flags["IS SET"] = true
isRunning = true
// Flag set true which will overide config
flags.WithBool("IS SET", &isRunning, true)
if !isRunning {
t.Fatalf("received: '%v' but expected: '%v'", isRunning, true)
}
flags.WithBool("IS SET", &isRunning, false)
if !isRunning {
t.Fatalf("received: '%v' but expected: '%v'", isRunning, true)
}
flags["IS SET"] = true
isRunning = false
// Flag set false which will overide config
flags.WithBool("IS SET", &isRunning, true)
if isRunning {
t.Fatalf("received: '%v' but expected: '%v'", isRunning, false)
}
flags.WithBool("IS SET", &isRunning, false)
if isRunning {
t.Fatalf("received: '%v' but expected: '%v'", isRunning, false)
}
}

View File

@@ -44,7 +44,7 @@ type Settings struct {
EnableTickerSyncing bool
EnableOrderbookSyncing bool
EnableTradeSyncing bool
SyncWorkers int
SyncWorkersCount int
SyncContinuously bool
SyncTimeoutREST time.Duration
SyncTimeoutWebsocket time.Duration
@@ -52,6 +52,7 @@ type Settings struct {
// Forex settings
EnableCurrencyConverter bool
EnableCurrencyLayer bool
EnableExchangeRates bool
EnableFixer bool
EnableOpenExchangeRates bool
EnableExchangeRateHost bool

View File

@@ -184,15 +184,16 @@ func (bot *Engine) SetSubsystem(subSystemName string, enable bool) error {
case SyncManagerName:
if enable {
if bot.currencyPairSyncer == nil {
exchangeSyncCfg := &Config{
SyncTicker: bot.Settings.EnableTickerSyncing,
SyncOrderbook: bot.Settings.EnableOrderbookSyncing,
SyncTrades: bot.Settings.EnableTradeSyncing,
SyncContinuously: bot.Settings.SyncContinuously,
NumWorkers: bot.Settings.SyncWorkers,
Verbose: bot.Settings.Verbose,
SyncTimeoutREST: bot.Settings.SyncTimeoutREST,
SyncTimeoutWebsocket: bot.Settings.SyncTimeoutWebsocket,
exchangeSyncCfg := &SyncManagerConfig{
SynchronizeTicker: bot.Settings.EnableTickerSyncing,
SynchronizeOrderbook: bot.Settings.EnableOrderbookSyncing,
SynchronizeTrades: bot.Settings.EnableTradeSyncing,
SynchronizeContinuously: bot.Settings.SyncContinuously,
TimeoutREST: bot.Settings.SyncTimeoutREST,
TimeoutWebsocket: bot.Settings.SyncTimeoutWebsocket,
NumWorkers: bot.Settings.SyncWorkersCount,
FiatDisplayCurrency: bot.Config.Currency.FiatDisplayCurrency,
Verbose: bot.Settings.Verbose,
}
bot.currencyPairSyncer, err = setupSyncManager(
exchangeSyncCfg,
@@ -376,9 +377,9 @@ func (bot *Engine) GetSpecificAvailablePairs(enabledExchangesOnly, fiatPairs, in
for x := range supportedPairs {
if fiatPairs {
if supportedPairs[x].IsCryptoFiatPair() &&
!supportedPairs[x].ContainsCurrency(currency.USDT) ||
!supportedPairs[x].Contains(currency.USDT) ||
(includeUSDT &&
supportedPairs[x].ContainsCurrency(currency.USDT) &&
supportedPairs[x].Contains(currency.USDT) &&
supportedPairs[x].IsCryptoPair()) {
if pairList.Contains(supportedPairs[x], false) {
continue
@@ -428,18 +429,12 @@ func (bot *Engine) MapCurrenciesByExchange(p currency.Pairs, enabledExchangesOnl
continue
}
result, ok := currencyExchange[exchName]
if !ok {
var pairs []currency.Pair
pairs = append(pairs, p[x])
currencyExchange[exchName] = pairs
} else {
if result.Contains(p[x], false) {
continue
}
result = append(result, p[x])
currencyExchange[exchName] = result
result := currencyExchange[exchName]
if result.Contains(p[x], false) {
continue
}
result = append(result, p[x])
currencyExchange[exchName] = result
}
}
return currencyExchange
@@ -468,18 +463,14 @@ func (bot *Engine) GetExchangeNamesByCurrency(p currency.Pair, enabled bool, ass
func GetRelatableCryptocurrencies(p currency.Pair) currency.Pairs {
var pairs currency.Pairs
cryptocurrencies := currency.GetCryptocurrencies()
for x := range cryptocurrencies {
newPair := currency.NewPair(p.Base, cryptocurrencies[x])
if newPair.IsInvalid() {
continue
}
if newPair.Base.Upper() == p.Base.Upper() &&
newPair.Quote.Upper() == p.Quote.Upper() {
if newPair.Equal(p) {
continue
}
if pairs.Contains(newPair, false) {
continue
}
@@ -496,12 +487,11 @@ func GetRelatableFiatCurrencies(p currency.Pair) currency.Pairs {
for x := range fiatCurrencies {
newPair := currency.NewPair(p.Base, fiatCurrencies[x])
if newPair.Base.Upper() == newPair.Quote.Upper() {
if newPair.Base.Equal(newPair.Quote) {
continue
}
if newPair.Base.Upper() == p.Base.Upper() &&
newPair.Quote.Upper() == p.Quote.Upper() {
if newPair.Equal(p) {
continue
}
@@ -532,17 +522,17 @@ func GetRelatableCurrencies(p currency.Pair, incOrig, incUSDT bool) currency.Pai
}
first := currency.GetTranslation(p.Base)
if first != p.Base {
if !first.Equal(p.Base) {
addPair(currency.NewPair(first, p.Quote))
second := currency.GetTranslation(p.Quote)
if second != p.Quote {
if !second.Equal(p.Quote) {
addPair(currency.NewPair(first, second))
}
}
second := currency.GetTranslation(p.Quote)
if second != p.Quote {
if !second.Equal(p.Quote) {
addPair(currency.NewPair(p.Base, second))
}
}
@@ -641,30 +631,17 @@ func (bot *Engine) GetCryptocurrenciesByExchange(exchangeName string, enabledExc
}
var err error
var pairs []currency.Pair
var pairs currency.Pairs
if enabledPairs {
pairs, err = bot.Config.GetEnabledPairs(exchangeName, assetType)
if err != nil {
return nil, err
}
} else {
pairs, err = bot.Config.GetAvailablePairs(exchangeName, assetType)
if err != nil {
return nil, err
}
}
for y := range pairs {
if pairs[y].Base.IsCryptocurrency() &&
!common.StringDataCompareInsensitive(cryptocurrencies, pairs[y].Base.String()) {
cryptocurrencies = append(cryptocurrencies, pairs[y].Base.String())
}
if pairs[y].Quote.IsCryptocurrency() &&
!common.StringDataCompareInsensitive(cryptocurrencies, pairs[y].Quote.String()) {
cryptocurrencies = append(cryptocurrencies, pairs[y].Quote.String())
}
if err != nil {
return nil, err
}
cryptocurrencies = pairs.GetCrypto().Strings()
break
}
return cryptocurrencies, nil
}
@@ -681,7 +658,7 @@ func (bot *Engine) GetCryptocurrencyDepositAddressesByExchange(exchName string)
result := bot.GetAllExchangeCryptocurrencyDepositAddresses()
r, ok := result[exchName]
if !ok {
return nil, ErrExchangeNotFound
return nil, fmt.Errorf("%s %w", exchName, ErrExchangeNotFound)
}
return r, nil
}
@@ -689,7 +666,9 @@ func (bot *Engine) GetCryptocurrencyDepositAddressesByExchange(exchName string)
// GetExchangeCryptocurrencyDepositAddress returns the cryptocurrency deposit address for a particular
// exchange
func (bot *Engine) GetExchangeCryptocurrencyDepositAddress(ctx context.Context, exchName, accountID, chain string, item currency.Code, bypassCache bool) (*deposit.Address, error) {
if bot.DepositAddressManager != nil && bot.DepositAddressManager.IsSynced() && !bypassCache {
if bot.DepositAddressManager != nil &&
bot.DepositAddressManager.IsSynced() &&
!bypassCache {
resp, err := bot.DepositAddressManager.GetDepositAddressByExchangeAndCurrency(exchName, chain, item)
return &resp, err
}

View File

@@ -99,7 +99,7 @@ func CreateTestBot(t *testing.T) *Engine {
func TestGetSubsystemsStatus(t *testing.T) {
m := (&Engine{}).GetSubsystemsStatus()
if len(m) != 15 {
t.Fatalf("subsystem count is wrong expecting: %d but received: %d", 14, len(m))
t.Fatalf("subsystem count is wrong expecting: %d but received: %d", 15, len(m))
}
}
@@ -1029,10 +1029,10 @@ func (f fakeDepositExchange) GetAvailableTransferChains(_ context.Context, c cur
if f.ThrowTransferChainError {
return nil, errors.New("unable to get available transfer chains")
}
if c.Match(currency.XRP) {
if c.Equal(currency.XRP) {
return nil, nil
}
if c.Match(currency.USDT) {
if c.Equal(currency.USDT) {
return []string{"sol", "btc", "usdt"}, nil
}
return []string{"BITCOIN"}, nil

View File

@@ -653,8 +653,9 @@ func (m *OrderManager) processOrders() {
upsertResponse, err := m.UpsertOrder(&result[z])
if err != nil {
log.Error(log.OrderMgr, err)
} else {
requiresProcessing[upsertResponse.OrderDetails.InternalOrderID] = false
}
requiresProcessing[upsertResponse.OrderDetails.InternalOrderID] = false
}
if !exchanges[i].GetBase().GetSupportedFeatures().RESTCapabilities.GetOrder {
continue

View File

@@ -519,14 +519,14 @@ func TestCancelOrder(t *testing.T) {
func TestGetOrderInfo(t *testing.T) {
m := OrdersSetup(t)
_, err := m.GetOrderInfo(context.Background(), "", "", currency.Pair{}, "")
_, err := m.GetOrderInfo(context.Background(), "", "", currency.EMPTYPAIR, "")
if err == nil {
t.Error("Expected error due to empty order")
}
var result order.Detail
result, err = m.GetOrderInfo(context.Background(),
testExchange, "1337", currency.Pair{}, "")
testExchange, "1337", currency.EMPTYPAIR, "")
if err != nil {
t.Error(err)
}
@@ -535,7 +535,7 @@ func TestGetOrderInfo(t *testing.T) {
}
result, err = m.GetOrderInfo(context.Background(),
testExchange, "1337", currency.Pair{}, "")
testExchange, "1337", currency.EMPTYPAIR, "")
if err != nil {
t.Error(err)
}

View File

@@ -164,7 +164,7 @@ func (m *portfolioManager) seedExchangeAccountInfo(accounts []account.Holdings)
for z := range accounts[x].Accounts[y].Currencies {
var update bool
for i := range currencies {
if accounts[x].Accounts[y].Currencies[z].CurrencyName == currencies[i].CurrencyName {
if accounts[x].Accounts[y].Currencies[z].CurrencyName.Equal(currencies[i].CurrencyName) {
currencies[i].Hold += accounts[x].Accounts[y].Currencies[z].Hold
currencies[i].TotalValue += accounts[x].Accounts[y].Currencies[z].TotalValue
update = true

View File

@@ -559,7 +559,7 @@ func (s *RPCServer) GetAccountInfo(ctx context.Context, r *gctrpc.GetAccountInfo
return nil, err
}
err = checkParams(r.Exchange, exch, assetType, currency.Pair{})
err = checkParams(r.Exchange, exch, assetType, currency.EMPTYPAIR)
if err != nil {
return nil, err
}
@@ -584,7 +584,7 @@ func (s *RPCServer) UpdateAccountInfo(ctx context.Context, r *gctrpc.GetAccountI
return nil, err
}
err = checkParams(r.Exchange, exch, assetType, currency.Pair{})
err = checkParams(r.Exchange, exch, assetType, currency.EMPTYPAIR)
if err != nil {
return nil, err
}
@@ -627,7 +627,7 @@ func (s *RPCServer) GetAccountInfoStream(r *gctrpc.GetAccountInfoRequest, stream
return err
}
err = checkParams(r.Exchange, exch, assetType, currency.Pair{})
err = checkParams(r.Exchange, exch, assetType, currency.EMPTYPAIR)
if err != nil {
return err
}
@@ -832,7 +832,7 @@ func (s *RPCServer) GetForexProviders(_ context.Context, _ *gctrpc.GetForexProvi
Name: providers[x].Name,
Enabled: providers[x].Enabled,
Verbose: providers[x].Verbose,
RestPollingDelay: providers[x].RESTPollingDelay.String(),
RestPollingDelay: s.Config.Currency.ForeignExchangeUpdateDuration.String(),
ApiKey: providers[x].APIKey,
ApiKeyLevel: int64(providers[x].APIKeyLvl),
PrimaryProvider: providers[x].PrimaryProvider,
@@ -1954,7 +1954,7 @@ func (s *RPCServer) SetExchangePair(_ context.Context, r *gctrpc.SetExchangePair
return nil, err
}
err = checkParams(r.Exchange, exch, a, currency.Pair{})
err = checkParams(r.Exchange, exch, a, currency.EMPTYPAIR)
if err != nil {
return nil, err
}

View File

@@ -42,8 +42,12 @@ var (
)
// setupSyncManager starts a new CurrencyPairSyncer
func setupSyncManager(c *Config, exchangeManager iExchangeManager, remoteConfig *config.RemoteControlConfig, websocketRoutineManagerEnabled bool) (*syncManager, error) {
if !c.SyncOrderbook && !c.SyncTicker && !c.SyncTrades {
func setupSyncManager(c *SyncManagerConfig, exchangeManager iExchangeManager, remoteConfig *config.RemoteControlConfig, websocketRoutineManagerEnabled bool) (*syncManager, error) {
if c == nil {
return nil, fmt.Errorf("%T %w", c, common.ErrNilPointer)
}
if !c.SynchronizeOrderbook && !c.SynchronizeTicker && !c.SynchronizeTrades {
return nil, errNoSyncItemsEnabled
}
if exchangeManager == nil {
@@ -57,12 +61,24 @@ func setupSyncManager(c *Config, exchangeManager iExchangeManager, remoteConfig
c.NumWorkers = DefaultSyncerWorkers
}
if c.SyncTimeoutREST <= time.Duration(0) {
c.SyncTimeoutREST = DefaultSyncerTimeoutREST
if c.TimeoutREST <= time.Duration(0) {
c.TimeoutREST = DefaultSyncerTimeoutREST
}
if c.SyncTimeoutWebsocket <= time.Duration(0) {
c.SyncTimeoutWebsocket = DefaultSyncerTimeoutWebsocket
if c.TimeoutWebsocket <= time.Duration(0) {
c.TimeoutWebsocket = DefaultSyncerTimeoutWebsocket
}
if c.FiatDisplayCurrency.IsEmpty() {
return nil, fmt.Errorf("FiatDisplayCurrency %w", currency.ErrCurrencyCodeEmpty)
}
if !c.FiatDisplayCurrency.IsFiatCurrency() {
return nil, fmt.Errorf("%s %w", c.FiatDisplayCurrency, currency.ErrFiatDisplayCurrencyIsNotFiat)
}
if c.PairFormatDisplay == nil {
return nil, fmt.Errorf("%T %w", c.PairFormatDisplay, common.ErrNilPointer)
}
s := &syncManager{
@@ -70,27 +86,26 @@ func setupSyncManager(c *Config, exchangeManager iExchangeManager, remoteConfig
remoteConfig: remoteConfig,
exchangeManager: exchangeManager,
websocketRoutineManagerEnabled: websocketRoutineManagerEnabled,
fiatDisplayCurrency: c.FiatDisplayCurrency,
delimiter: c.PairFormatDisplay.Delimiter,
uppercase: c.PairFormatDisplay.Uppercase,
tickerBatchLastRequested: make(map[string]time.Time),
}
s.tickerBatchLastRequested = make(map[string]time.Time)
log.Debugf(log.SyncMgr,
"Exchange currency pair syncer config: continuous: %v ticker: %v"+
" orderbook: %v trades: %v workers: %v verbose: %v timeout REST: %v"+
" timeout Websocket: %v",
s.config.SyncContinuously, s.config.SyncTicker, s.config.SyncOrderbook,
s.config.SyncTrades, s.config.NumWorkers, s.config.Verbose, s.config.SyncTimeoutREST,
s.config.SyncTimeoutWebsocket)
s.config.SynchronizeContinuously, s.config.SynchronizeTicker, s.config.SynchronizeOrderbook,
s.config.SynchronizeTrades, s.config.NumWorkers, s.config.Verbose, s.config.TimeoutREST,
s.config.TimeoutWebsocket)
s.inService.Add(1)
return s, nil
}
// IsRunning safely checks whether the subsystem is running
func (m *syncManager) IsRunning() bool {
if m == nil {
return false
}
return atomic.LoadInt32(&m.started) == 1
return m != nil && atomic.LoadInt32(&m.started) == 1
}
// Start runs the subsystem
@@ -169,13 +184,13 @@ func (m *syncManager) Start() error {
IsUsingREST: usingREST || !wsAssetSupported,
IsUsingWebsocket: usingWebsocket && wsAssetSupported,
}
if m.config.SyncTicker {
if m.config.SynchronizeTicker {
c.Ticker = sBase
}
if m.config.SyncOrderbook {
if m.config.SynchronizeOrderbook {
c.Orderbook = sBase
}
if m.config.SyncTrades {
if m.config.SynchronizeTrades {
c.Trade = sBase
}
@@ -199,7 +214,7 @@ func (m *syncManager) Start() error {
log.Debugf(log.SyncMgr, "Exchange CurrencyPairSyncer initial sync took %v [%v sync items].",
completedTime.Sub(m.initSyncStartTime), createdCounter)
if !m.config.SyncContinuously {
if !m.config.SynchronizeContinuously {
log.Debugln(log.SyncMgr, "Exchange CurrencyPairSyncer stopping.")
err := m.Stop()
if err != nil {
@@ -210,7 +225,7 @@ func (m *syncManager) Start() error {
}
}()
if atomic.LoadInt32(&m.initSyncCompleted) == 1 && !m.config.SyncContinuously {
if atomic.LoadInt32(&m.initSyncCompleted) == 1 && !m.config.SynchronizeContinuously {
return nil
}
@@ -267,7 +282,7 @@ func (m *syncManager) add(c *currencyPairSyncAgent) {
m.mux.Lock()
defer m.mux.Unlock()
if m.config.SyncTicker {
if m.config.SynchronizeTicker {
if m.config.Verbose {
log.Debugf(log.SyncMgr,
"%s: Added ticker sync item %v: using websocket: %v using REST: %v",
@@ -280,7 +295,7 @@ func (m *syncManager) add(c *currencyPairSyncAgent) {
}
}
if m.config.SyncOrderbook {
if m.config.SynchronizeOrderbook {
if m.config.Verbose {
log.Debugf(log.SyncMgr,
"%s: Added orderbook sync item %v: using websocket: %v using REST: %v",
@@ -293,7 +308,7 @@ func (m *syncManager) add(c *currencyPairSyncAgent) {
}
}
if m.config.SyncTrades {
if m.config.SynchronizeTrades {
if m.config.Verbose {
log.Debugf(log.SyncMgr,
"%s: Added trade sync item %v: using websocket: %v using REST: %v",
@@ -367,15 +382,15 @@ func (m *syncManager) Update(exchangeName string, p currency.Pair, a asset.Item,
switch syncType {
case SyncItemOrderbook:
if !m.config.SyncOrderbook {
if !m.config.SynchronizeOrderbook {
return nil
}
case SyncItemTicker:
if !m.config.SyncTicker {
if !m.config.SynchronizeTicker {
return nil
}
case SyncItemTrade:
if !m.config.SyncTrades {
if !m.config.SynchronizeTrades {
return nil
}
default:
@@ -517,15 +532,15 @@ func (m *syncManager) worker() {
IsUsingWebsocket: usingWebsocket && wsAssetSupported,
}
if m.config.SyncTicker {
if m.config.SynchronizeTicker {
c.Ticker = sBase
}
if m.config.SyncOrderbook {
if m.config.SynchronizeOrderbook {
c.Orderbook = sBase
}
if m.config.SyncTrades {
if m.config.SynchronizeTrades {
c.Trade = sBase
}
@@ -542,13 +557,13 @@ func (m *syncManager) worker() {
switchedToRest = false
}
if m.config.SyncOrderbook {
if m.config.SynchronizeOrderbook {
if !m.isProcessing(exchangeName, c.Pair, c.AssetType, SyncItemOrderbook) {
if c.Orderbook.LastUpdated.IsZero() ||
(time.Since(c.Orderbook.LastUpdated) > m.config.SyncTimeoutREST && c.Orderbook.IsUsingREST) ||
(time.Since(c.Orderbook.LastUpdated) > m.config.SyncTimeoutWebsocket && c.Orderbook.IsUsingWebsocket) {
(time.Since(c.Orderbook.LastUpdated) > m.config.TimeoutREST && c.Orderbook.IsUsingREST) ||
(time.Since(c.Orderbook.LastUpdated) > m.config.TimeoutWebsocket && c.Orderbook.IsUsingWebsocket) {
if c.Orderbook.IsUsingWebsocket {
if time.Since(c.Created) < m.config.SyncTimeoutWebsocket {
if time.Since(c.Created) < m.config.TimeoutWebsocket {
continue
}
if supportsREST {
@@ -560,7 +575,7 @@ func (m *syncManager) worker() {
c.Exchange,
m.FormatCurrency(c.Pair).String(),
strings.ToUpper(c.AssetType.String()),
m.config.SyncTimeoutWebsocket,
m.config.TimeoutWebsocket,
)
switchedToRest = true
m.setProcessing(c.Exchange, c.Pair, c.AssetType, SyncItemOrderbook, false)
@@ -586,13 +601,13 @@ func (m *syncManager) worker() {
}
}
if m.config.SyncTicker {
if m.config.SynchronizeTicker {
if !m.isProcessing(exchangeName, c.Pair, c.AssetType, SyncItemTicker) {
if c.Ticker.LastUpdated.IsZero() ||
(time.Since(c.Ticker.LastUpdated) > m.config.SyncTimeoutREST && c.Ticker.IsUsingREST) ||
(time.Since(c.Ticker.LastUpdated) > m.config.SyncTimeoutWebsocket && c.Ticker.IsUsingWebsocket) {
(time.Since(c.Ticker.LastUpdated) > m.config.TimeoutREST && c.Ticker.IsUsingREST) ||
(time.Since(c.Ticker.LastUpdated) > m.config.TimeoutWebsocket && c.Ticker.IsUsingWebsocket) {
if c.Ticker.IsUsingWebsocket {
if time.Since(c.Created) < m.config.SyncTimeoutWebsocket {
if time.Since(c.Created) < m.config.TimeoutWebsocket {
continue
}
@@ -605,7 +620,7 @@ func (m *syncManager) worker() {
c.Exchange,
m.FormatCurrency(enabledPairs[i]).String(),
strings.ToUpper(c.AssetType.String()),
m.config.SyncTimeoutWebsocket,
m.config.TimeoutWebsocket,
)
switchedToRest = true
m.setProcessing(c.Exchange, c.Pair, c.AssetType, SyncItemTicker, false)
@@ -625,7 +640,7 @@ func (m *syncManager) worker() {
}
m.mux.Unlock()
if batchLastDone.IsZero() || time.Since(batchLastDone) > m.config.SyncTimeoutREST {
if batchLastDone.IsZero() || time.Since(batchLastDone) > m.config.TimeoutREST {
m.mux.Lock()
if m.config.Verbose {
log.Debugf(log.SyncMgr, "Initialising %s REST ticker batching", exchangeName)
@@ -666,9 +681,9 @@ func (m *syncManager) worker() {
}
}
if m.config.SyncTrades {
if m.config.SynchronizeTrades {
if !m.isProcessing(exchangeName, c.Pair, c.AssetType, SyncItemTrade) {
if c.Trade.LastUpdated.IsZero() || time.Since(c.Trade.LastUpdated) > m.config.SyncTimeoutREST {
if c.Trade.LastUpdated.IsZero() || time.Since(c.Trade.LastUpdated) > m.config.TimeoutREST {
m.setProcessing(c.Exchange, c.Pair, c.AssetType, SyncItemTrade, true)
err := m.Update(c.Exchange, c.Pair, c.AssetType, SyncItemTrade, nil)
if err != nil {
@@ -693,12 +708,14 @@ func printCurrencyFormat(price float64, displayCurrency currency.Code) string {
return fmt.Sprintf("%s%.8f", displaySymbol, price)
}
func printConvertCurrencyFormat(origCurrency currency.Code, origPrice float64, displayCurrency currency.Code) string {
conv, err := currency.ConvertCurrency(origPrice,
origCurrency,
displayCurrency)
if err != nil {
log.Errorf(log.SyncMgr, "Failed to convert currency: %s", err)
func printConvertCurrencyFormat(origPrice float64, origCurrency, displayCurrency currency.Code) string {
var conv float64
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)
}
}
displaySymbol, err := currency.GetSymbolByCurrencyName(displayCurrency)
@@ -745,7 +762,7 @@ func (m *syncManager) PrintTickerSummary(result *ticker.Price, protocol string,
_ = stats.Add(result.ExchangeName, result.Pair, result.AssetType, result.Last, result.Volume)
if result.Pair.Quote.IsFiatCurrency() &&
result.Pair.Quote != m.fiatDisplayCurrency &&
!result.Pair.Quote.Equal(m.fiatDisplayCurrency) &&
!m.fiatDisplayCurrency.IsEmpty() {
origCurrency := result.Pair.Quote.Upper()
log.Infof(log.Ticker, "%s %s %s %s: TICKER: Last %s Ask %s Bid %s High %s Low %s Volume %.8f",
@@ -753,15 +770,15 @@ func (m *syncManager) PrintTickerSummary(result *ticker.Price, protocol string,
protocol,
m.FormatCurrency(result.Pair),
strings.ToUpper(result.AssetType.String()),
printConvertCurrencyFormat(origCurrency, result.Last, m.fiatDisplayCurrency),
printConvertCurrencyFormat(origCurrency, result.Ask, m.fiatDisplayCurrency),
printConvertCurrencyFormat(origCurrency, result.Bid, m.fiatDisplayCurrency),
printConvertCurrencyFormat(origCurrency, result.High, m.fiatDisplayCurrency),
printConvertCurrencyFormat(origCurrency, result.Low, m.fiatDisplayCurrency),
printConvertCurrencyFormat(result.Last, origCurrency, m.fiatDisplayCurrency),
printConvertCurrencyFormat(result.Ask, origCurrency, m.fiatDisplayCurrency),
printConvertCurrencyFormat(result.Bid, origCurrency, m.fiatDisplayCurrency),
printConvertCurrencyFormat(result.High, origCurrency, m.fiatDisplayCurrency),
printConvertCurrencyFormat(result.Low, origCurrency, m.fiatDisplayCurrency),
result.Volume)
} else {
if result.Pair.Quote.IsFiatCurrency() &&
result.Pair.Quote == m.fiatDisplayCurrency &&
result.Pair.Quote.Equal(m.fiatDisplayCurrency) &&
!m.fiatDisplayCurrency.IsEmpty() {
log.Infof(log.Ticker, "%s %s %s %s: TICKER: Last %s Ask %s Bid %s High %s Low %s Volume %.8f",
result.ExchangeName,
@@ -838,11 +855,15 @@ func (m *syncManager) PrintOrderbookSummary(result *orderbook.Base, protocol str
var bidValueResult, askValueResult string
switch {
case result.Pair.Quote.IsFiatCurrency() && result.Pair.Quote != m.fiatDisplayCurrency && !m.fiatDisplayCurrency.IsEmpty():
case result.Pair.Quote.IsFiatCurrency() && !result.Pair.Quote.Equal(m.fiatDisplayCurrency) && !m.fiatDisplayCurrency.IsEmpty():
origCurrency := result.Pair.Quote.Upper()
bidValueResult = printConvertCurrencyFormat(origCurrency, bidsValue, m.fiatDisplayCurrency)
askValueResult = printConvertCurrencyFormat(origCurrency, asksValue, m.fiatDisplayCurrency)
case result.Pair.Quote.IsFiatCurrency() && result.Pair.Quote == m.fiatDisplayCurrency && !m.fiatDisplayCurrency.IsEmpty():
if bidsValue > 0 {
bidValueResult = printConvertCurrencyFormat(bidsValue, origCurrency, m.fiatDisplayCurrency)
}
if asksValue > 0 {
askValueResult = printConvertCurrencyFormat(asksValue, origCurrency, m.fiatDisplayCurrency)
}
case result.Pair.Quote.IsFiatCurrency() && result.Pair.Quote.Equal(m.fiatDisplayCurrency) && !m.fiatDisplayCurrency.IsEmpty():
bidValueResult = printCurrencyFormat(bidsValue, m.fiatDisplayCurrency)
askValueResult = printCurrencyFormat(asksValue, m.fiatDisplayCurrency)
default:

View File

@@ -14,22 +14,42 @@ import (
func TestSetupSyncManager(t *testing.T) {
t.Parallel()
_, err := setupSyncManager(&Config{}, nil, nil, false)
_, err := setupSyncManager(nil, nil, nil, false)
if !errors.Is(err, common.ErrNilPointer) {
t.Errorf("error '%v', expected '%v'", err, common.ErrNilPointer)
}
_, err = setupSyncManager(&SyncManagerConfig{}, nil, nil, false)
if !errors.Is(err, errNoSyncItemsEnabled) {
t.Errorf("error '%v', expected '%v'", err, errNoSyncItemsEnabled)
}
_, err = setupSyncManager(&Config{SyncTrades: true}, nil, nil, false)
_, err = setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true}, nil, nil, false)
if !errors.Is(err, errNilExchangeManager) {
t.Errorf("error '%v', expected '%v'", err, errNilExchangeManager)
}
_, err = setupSyncManager(&Config{SyncTrades: true}, &ExchangeManager{}, nil, false)
_, err = setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true}, &ExchangeManager{}, nil, false)
if !errors.Is(err, errNilConfig) {
t.Errorf("error '%v', expected '%v'", err, errNilConfig)
}
m, err := setupSyncManager(&Config{SyncTrades: true}, &ExchangeManager{}, &config.RemoteControlConfig{}, true)
_, err = setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true}, &ExchangeManager{}, &config.RemoteControlConfig{}, true)
if !errors.Is(err, currency.ErrCurrencyCodeEmpty) {
t.Errorf("error '%v', expected '%v'", err, currency.ErrCurrencyCodeEmpty)
}
_, err = setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true, FiatDisplayCurrency: currency.BTC}, &ExchangeManager{}, &config.RemoteControlConfig{}, true)
if !errors.Is(err, currency.ErrFiatDisplayCurrencyIsNotFiat) {
t.Errorf("error '%v', expected '%v'", err, currency.ErrFiatDisplayCurrencyIsNotFiat)
}
_, err = setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true, FiatDisplayCurrency: currency.USD}, &ExchangeManager{}, &config.RemoteControlConfig{}, true)
if !errors.Is(err, common.ErrNilPointer) {
t.Errorf("error '%v', expected '%v'", err, common.ErrNilPointer)
}
m, err := setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true, FiatDisplayCurrency: currency.USD, PairFormatDisplay: &currency.PairFormat{}}, &ExchangeManager{}, &config.RemoteControlConfig{}, true)
if !errors.Is(err, nil) {
t.Errorf("error '%v', expected '%v'", err, nil)
}
@@ -40,7 +60,7 @@ func TestSetupSyncManager(t *testing.T) {
func TestSyncManagerStart(t *testing.T) {
t.Parallel()
m, err := setupSyncManager(&Config{SyncTrades: true}, &ExchangeManager{}, &config.RemoteControlConfig{}, true)
m, err := setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true, FiatDisplayCurrency: currency.USD, PairFormatDisplay: &currency.PairFormat{}}, &ExchangeManager{}, &config.RemoteControlConfig{}, true)
if !errors.Is(err, nil) {
t.Errorf("error '%v', expected '%v'", err, nil)
}
@@ -52,7 +72,7 @@ func TestSyncManagerStart(t *testing.T) {
exch.SetDefaults()
em.Add(exch)
m.exchangeManager = em
m.config.SyncContinuously = true
m.config.SynchronizeContinuously = true
err = m.Start()
if !errors.Is(err, nil) {
t.Errorf("error '%v', expected '%v'", err, nil)
@@ -85,7 +105,7 @@ func TestSyncManagerStop(t *testing.T) {
}
exch.SetDefaults()
em.Add(exch)
m, err = setupSyncManager(&Config{SyncTrades: true, SyncContinuously: true}, em, &config.RemoteControlConfig{}, false)
m, err = setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true, SynchronizeContinuously: true, FiatDisplayCurrency: currency.USD, PairFormatDisplay: &currency.PairFormat{}}, em, &config.RemoteControlConfig{}, false)
if !errors.Is(err, nil) {
t.Errorf("error '%v', expected '%v'", err, nil)
}
@@ -115,7 +135,7 @@ func TestPrintCurrencyFormat(t *testing.T) {
func TestPrintConvertCurrencyFormat(t *testing.T) {
t.Parallel()
c := printConvertCurrencyFormat(currency.BTC, 1337, currency.USD)
c := printConvertCurrencyFormat(1337, currency.BTC, currency.USD)
if c == "" {
t.Error("expected formatted currency")
}
@@ -133,7 +153,7 @@ func TestPrintTickerSummary(t *testing.T) {
}
exch.SetDefaults()
em.Add(exch)
m, err = setupSyncManager(&Config{SyncTrades: true, SyncContinuously: true}, em, &config.RemoteControlConfig{}, false)
m, err = setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true, SynchronizeContinuously: true, FiatDisplayCurrency: currency.USD, PairFormatDisplay: &currency.PairFormat{}}, em, &config.RemoteControlConfig{}, false)
if !errors.Is(err, nil) {
t.Errorf("error '%v', expected '%v'", err, nil)
}
@@ -172,7 +192,7 @@ func TestPrintOrderbookSummary(t *testing.T) {
}
exch.SetDefaults()
em.Add(exch)
m, err = setupSyncManager(&Config{SyncTrades: true, SyncContinuously: true}, em, &config.RemoteControlConfig{}, false)
m, err = setupSyncManager(&SyncManagerConfig{SynchronizeTrades: true, SynchronizeContinuously: true, FiatDisplayCurrency: currency.USD, PairFormatDisplay: &currency.PairFormat{}}, em, &config.RemoteControlConfig{}, false)
if !errors.Is(err, nil) {
t.Errorf("error '%v', expected '%v'", err, nil)
}

View File

@@ -30,16 +30,18 @@ type currencyPairSyncAgent struct {
Trade syncBase
}
// Config stores the currency pair config
type Config struct {
SyncTicker bool
SyncOrderbook bool
SyncTrades bool
SyncContinuously bool
SyncTimeoutREST time.Duration
SyncTimeoutWebsocket time.Duration
NumWorkers int
Verbose bool
// SyncManagerConfig stores the currency pair synchronization manager config
type SyncManagerConfig struct {
SynchronizeTicker bool
SynchronizeOrderbook bool
SynchronizeTrades bool
SynchronizeContinuously bool
TimeoutREST time.Duration
TimeoutWebsocket time.Duration
NumWorkers int
FiatDisplayCurrency currency.Code
PairFormatDisplay *currency.PairFormat
Verbose bool
}
// syncManager stores the exchange currency pair syncer object
@@ -60,6 +62,6 @@ type syncManager struct {
tickerBatchLastRequested map[string]time.Time
remoteConfig *config.RemoteControlConfig
config Config
config SyncManagerConfig
exchangeManager iExchangeManager
}

View File

@@ -5,7 +5,6 @@ import (
"sync/atomic"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
"github.com/thrasher-corp/gocryptotrader/exchanges/fill"
@@ -18,7 +17,7 @@ import (
)
// setupWebsocketRoutineManager creates a new websocket routine manager
func setupWebsocketRoutineManager(exchangeManager iExchangeManager, orderManager iOrderManager, syncer iCurrencyPairSyncer, cfg *config.CurrencyConfig, verbose bool) (*websocketRoutineManager, error) {
func setupWebsocketRoutineManager(exchangeManager iExchangeManager, orderManager iOrderManager, syncer iCurrencyPairSyncer, cfg *currency.Config, verbose bool) (*websocketRoutineManager, error) {
if exchangeManager == nil {
return nil, errNilExchangeManager
}

View File

@@ -5,7 +5,6 @@ import (
"sync"
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
@@ -34,12 +33,12 @@ func TestWebsocketRoutineManagerSetup(t *testing.T) {
t.Errorf("error '%v', expected '%v'", err, errNilCurrencyConfig)
}
_, err = setupWebsocketRoutineManager(SetupExchangeManager(), &OrderManager{}, &syncManager{}, &config.CurrencyConfig{}, true)
_, err = setupWebsocketRoutineManager(SetupExchangeManager(), &OrderManager{}, &syncManager{}, &currency.Config{}, true)
if !errors.Is(err, errNilCurrencyPairFormat) {
t.Errorf("error '%v', expected '%v'", err, errNilCurrencyPairFormat)
}
m, err := setupWebsocketRoutineManager(SetupExchangeManager(), &OrderManager{}, &syncManager{}, &config.CurrencyConfig{}, false)
m, err := setupWebsocketRoutineManager(SetupExchangeManager(), &OrderManager{}, &syncManager{}, &currency.Config{}, false)
if !errors.Is(err, nil) {
t.Errorf("error '%v', expected '%v'", err, nil)
}
@@ -54,7 +53,7 @@ func TestWebsocketRoutineManagerStart(t *testing.T) {
if !errors.Is(err, ErrNilSubsystem) {
t.Errorf("error '%v', expected '%v'", err, ErrNilSubsystem)
}
cfg := &config.CurrencyConfig{CurrencyPairFormat: &config.CurrencyPairFormatConfig{
cfg := &currency.Config{CurrencyPairFormat: &currency.PairFormat{
Uppercase: false,
Delimiter: "-",
}}
@@ -78,7 +77,7 @@ func TestWebsocketRoutineManagerIsRunning(t *testing.T) {
t.Error("expected false")
}
m, err := setupWebsocketRoutineManager(SetupExchangeManager(), &OrderManager{}, &syncManager{}, &config.CurrencyConfig{}, false)
m, err := setupWebsocketRoutineManager(SetupExchangeManager(), &OrderManager{}, &syncManager{}, &currency.Config{}, false)
if !errors.Is(err, nil) {
t.Errorf("error '%v', expected '%v'", err, nil)
}
@@ -102,7 +101,7 @@ func TestWebsocketRoutineManagerStop(t *testing.T) {
t.Errorf("error '%v', expected '%v'", err, ErrNilSubsystem)
}
m, err = setupWebsocketRoutineManager(SetupExchangeManager(), &OrderManager{}, &syncManager{}, &config.CurrencyConfig{}, false)
m, err = setupWebsocketRoutineManager(SetupExchangeManager(), &OrderManager{}, &syncManager{}, &currency.Config{}, false)
if !errors.Is(err, nil) {
t.Errorf("error '%v', expected '%v'", err, nil)
}
@@ -140,7 +139,7 @@ func TestWebsocketRoutineManagerHandleData(t *testing.T) {
if !errors.Is(err, nil) {
t.Errorf("error '%v', expected '%v'", err, nil)
}
cfg := &config.CurrencyConfig{CurrencyPairFormat: &config.CurrencyPairFormatConfig{
cfg := &currency.Config{CurrencyPairFormat: &currency.PairFormat{
Uppercase: false,
Delimiter: "-",
}}

View File

@@ -4,7 +4,7 @@ import (
"errors"
"sync"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
)
// websocketRoutineManager is used to process websocket updates from a unified location
@@ -14,7 +14,7 @@ type websocketRoutineManager struct {
exchangeManager iExchangeManager
orderManager iOrderManager
syncer iCurrencyPairSyncer
currencyConfig *config.CurrencyConfig
currencyConfig *currency.Config
shutdown chan struct{}
wg sync.WaitGroup
}

View File

@@ -230,7 +230,7 @@ func TestUGetMarkPrice(t *testing.T) {
if err != nil {
t.Error(err)
}
_, err = b.UGetMarkPrice(context.Background(), currency.Pair{})
_, err = b.UGetMarkPrice(context.Background(), currency.EMPTYPAIR)
if err != nil {
t.Error(err)
}
@@ -254,7 +254,7 @@ func TestU24HTickerPriceChangeStats(t *testing.T) {
if err != nil {
t.Error(err)
}
_, err = b.U24HTickerPriceChangeStats(context.Background(), currency.Pair{})
_, err = b.U24HTickerPriceChangeStats(context.Background(), currency.EMPTYPAIR)
if err != nil {
t.Error(err)
}
@@ -266,7 +266,7 @@ func TestUSymbolPriceTicker(t *testing.T) {
if err != nil {
t.Error(err)
}
_, err = b.USymbolPriceTicker(context.Background(), currency.Pair{})
_, err = b.USymbolPriceTicker(context.Background(), currency.EMPTYPAIR)
if err != nil {
t.Error(err)
}
@@ -278,7 +278,7 @@ func TestUSymbolOrderbookTicker(t *testing.T) {
if err != nil {
t.Error(err)
}
_, err = b.USymbolOrderbookTicker(context.Background(), currency.Pair{})
_, err = b.USymbolOrderbookTicker(context.Background(), currency.EMPTYPAIR)
if err != nil {
t.Error(err)
}
@@ -370,7 +370,7 @@ func TestUCompositeIndexInfo(t *testing.T) {
if err != nil {
t.Error(err)
}
_, err = b.UCompositeIndexInfo(context.Background(), currency.Pair{})
_, err = b.UCompositeIndexInfo(context.Background(), currency.EMPTYPAIR)
if err != nil {
t.Error(err)
}
@@ -489,7 +489,7 @@ func TestUAllAccountOrders(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
_, err := b.UAllAccountOrders(context.Background(), currency.Pair{}, 0, 0, time.Time{}, time.Time{})
_, err := b.UAllAccountOrders(context.Background(), currency.EMPTYPAIR, 0, 0, time.Time{}, time.Time{})
if err != nil {
t.Error(err)
}
@@ -592,7 +592,7 @@ func TestUAccountIncomeHistory(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
_, err := b.UAccountIncomeHistory(context.Background(), currency.Pair{}, "", 5, time.Now().Add(-time.Hour*48), time.Now())
_, err := b.UAccountIncomeHistory(context.Background(), currency.EMPTYPAIR, "", 5, time.Now().Add(-time.Hour*48), time.Now())
if err != nil {
t.Error(err)
}
@@ -764,7 +764,7 @@ func TestGetFuturesSwapTickerChangeStats(t *testing.T) {
if err != nil {
t.Error(err)
}
_, err = b.GetFuturesSwapTickerChangeStats(context.Background(), currency.Pair{}, "")
_, err = b.GetFuturesSwapTickerChangeStats(context.Background(), currency.EMPTYPAIR, "")
if err != nil {
t.Error(err)
}
@@ -810,7 +810,7 @@ func TestGetFuturesSymbolPriceTicker(t *testing.T) {
func TestGetFuturesOrderbookTicker(t *testing.T) {
t.Parallel()
_, err := b.GetFuturesOrderbookTicker(context.Background(), currency.Pair{}, "")
_, err := b.GetFuturesOrderbookTicker(context.Background(), currency.EMPTYPAIR, "")
if err != nil {
t.Error(err)
}
@@ -822,7 +822,7 @@ func TestGetFuturesOrderbookTicker(t *testing.T) {
func TestGetFuturesLiquidationOrders(t *testing.T) {
t.Parallel()
_, err := b.GetFuturesLiquidationOrders(context.Background(), currency.Pair{}, "", 0, time.Time{}, time.Time{})
_, err := b.GetFuturesLiquidationOrders(context.Background(), currency.EMPTYPAIR, "", 0, time.Time{}, time.Time{})
if err != nil {
t.Error(err)
}
@@ -1124,7 +1124,7 @@ func TestFuturesIncomeHistory(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
_, err := b.FuturesIncomeHistory(context.Background(), currency.Pair{}, "TRANSFER", time.Time{}, time.Time{}, 5)
_, err := b.FuturesIncomeHistory(context.Background(), currency.EMPTYPAIR, "TRANSFER", time.Time{}, time.Time{}, 5)
if err != nil {
t.Error(err)
}
@@ -1135,7 +1135,7 @@ func TestFuturesForceOrders(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
_, err := b.FuturesForceOrders(context.Background(), currency.Pair{}, "ADL", time.Time{}, time.Time{})
_, err := b.FuturesForceOrders(context.Background(), currency.EMPTYPAIR, "ADL", time.Time{}, time.Time{})
if err != nil {
t.Error(err)
}
@@ -1161,7 +1161,7 @@ func TestFuturesPositionsADLEstimate(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
_, err := b.FuturesPositionsADLEstimate(context.Background(), currency.Pair{})
_, err := b.FuturesPositionsADLEstimate(context.Background(), currency.EMPTYPAIR)
if err != nil {
t.Error(err)
}
@@ -1341,7 +1341,7 @@ func TestOpenOrders(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip()
}
_, err := b.OpenOrders(context.Background(), currency.Pair{})
_, err := b.OpenOrders(context.Background(), currency.EMPTYPAIR)
if err != nil {
t.Error(err)
}

View File

@@ -489,7 +489,7 @@ func (b *Binance) UpdateTickers(ctx context.Context, a asset.Item) error {
}
}
case asset.USDTMarginedFutures:
tick, err := b.U24HTickerPriceChangeStats(ctx, currency.Pair{})
tick, err := b.U24HTickerPriceChangeStats(ctx, currency.EMPTYPAIR)
if err != nil {
return err
}
@@ -516,7 +516,7 @@ func (b *Binance) UpdateTickers(ctx context.Context, a asset.Item) error {
}
}
case asset.CoinMarginedFutures:
tick, err := b.GetFuturesSwapTickerChangeStats(ctx, currency.Pair{}, "")
tick, err := b.GetFuturesSwapTickerChangeStats(ctx, currency.EMPTYPAIR, "")
if err != nil {
return err
}
@@ -1323,7 +1323,7 @@ func (b *Binance) GetActiveOrders(ctx context.Context, req *order.GetOrdersReque
}
if len(req.Pairs) == 0 || len(req.Pairs) >= 40 {
// sending an empty currency pair retrieves data for all currencies
req.Pairs = append(req.Pairs, currency.Pair{})
req.Pairs = append(req.Pairs, currency.EMPTYPAIR)
}
var orders []order.Detail
for i := range req.Pairs {
@@ -1832,7 +1832,7 @@ func (b *Binance) GetAvailableTransferChains(ctx context.Context, cryptocurrency
func (b *Binance) FormatExchangeCurrency(p currency.Pair, a asset.Item) (currency.Pair, error) {
pairFmt, err := b.GetPairFormat(a, true)
if err != nil {
return currency.Pair{}, err
return currency.EMPTYPAIR, err
}
if a == asset.USDTMarginedFutures {
return b.formatUSDTMarginedFuturesPair(p, pairFmt), nil

View File

@@ -1148,7 +1148,8 @@ func TestUpdateTicker(t *testing.T) {
}
func TestUpdateTickers(t *testing.T) {
err := b.UpdateTickers(context.Background(), asset.Spot)
t.Parallel()
err := b.UpdateTickers(context.Background(), asset.PerpetualContract)
if err != nil {
t.Fatal(err)
}

View File

@@ -43,12 +43,12 @@ func TestGetSnapshot(t *testing.T) {
func TestCanTradePair(t *testing.T) {
t.Parallel()
err := (*States)(nil).CanTradePair(currency.Pair{}, "")
err := (*States)(nil).CanTradePair(currency.EMPTYPAIR, "")
if !errors.Is(err, errNilStates) {
t.Fatalf("received: %v, but expected: %v", err, errNilStates)
}
err = (&States{}).CanTradePair(currency.Pair{}, "")
err = (&States{}).CanTradePair(currency.EMPTYPAIR, "")
if !errors.Is(err, errEmptyCurrency) {
t.Fatalf("received: %v, but expected: %v", err, errEmptyCurrency)
}
@@ -115,11 +115,11 @@ func TestCanTradePair(t *testing.T) {
func TestStatesCanTrade(t *testing.T) {
t.Parallel()
err := (*States)(nil).CanTrade(currency.Code{}, "")
err := (*States)(nil).CanTrade(currency.EMPTYCODE, "")
if !errors.Is(err, errNilStates) {
t.Fatalf("received: %v, but expected: %v", err, errNilStates)
}
err = (&States{}).CanTrade(currency.Code{}, "")
err = (&States{}).CanTrade(currency.EMPTYCODE, "")
if !errors.Is(err, errEmptyCurrency) {
t.Fatalf("received: %v, but expected: %v", err, errEmptyCurrency)
}
@@ -127,11 +127,11 @@ func TestStatesCanTrade(t *testing.T) {
func TestStatesCanWithdraw(t *testing.T) {
t.Parallel()
err := (*States)(nil).CanWithdraw(currency.Code{}, "")
err := (*States)(nil).CanWithdraw(currency.EMPTYCODE, "")
if !errors.Is(err, errNilStates) {
t.Fatalf("received: %v, but expected: %v", err, errNilStates)
}
err = (&States{}).CanWithdraw(currency.Code{}, "")
err = (&States{}).CanWithdraw(currency.EMPTYCODE, "")
if !errors.Is(err, errEmptyCurrency) {
t.Fatalf("received: %v, but expected: %v", err, errEmptyCurrency)
}
@@ -161,11 +161,11 @@ func TestStatesCanWithdraw(t *testing.T) {
func TestStatesCanDeposit(t *testing.T) {
t.Parallel()
err := (*States)(nil).CanDeposit(currency.Code{}, "")
err := (*States)(nil).CanDeposit(currency.EMPTYCODE, "")
if !errors.Is(err, errNilStates) {
t.Fatalf("received: %v, but expected: %v", err, errNilStates)
}
err = (&States{}).CanDeposit(currency.Code{}, "")
err = (&States{}).CanDeposit(currency.EMPTYCODE, "")
if !errors.Is(err, errEmptyCurrency) {
t.Fatalf("received: %v, but expected: %v", err, errEmptyCurrency)
}
@@ -246,12 +246,12 @@ func TestStatesUpdateAll(t *testing.T) {
func TestStatesUpdate(t *testing.T) {
t.Parallel()
err := (*States)(nil).Update(currency.Code{}, "", Options{})
err := (*States)(nil).Update(currency.EMPTYCODE, "", Options{})
if !errors.Is(err, errNilStates) {
t.Fatalf("received: %v, but expected: %v", err, errNilStates)
}
err = (&States{}).Update(currency.Code{}, "", Options{})
err = (&States{}).Update(currency.EMPTYCODE, "", Options{})
if !errors.Is(err, errEmptyCurrency) {
t.Fatalf("received: %v, but expected: %v", err, errEmptyCurrency)
}
@@ -273,12 +273,12 @@ func TestStatesUpdate(t *testing.T) {
func TestStatesGet(t *testing.T) {
t.Parallel()
_, err := (*States)(nil).Get(currency.Code{}, "")
_, err := (*States)(nil).Get(currency.EMPTYCODE, "")
if !errors.Is(err, errNilStates) {
t.Fatalf("received: %v, but expected: %v", err, errNilStates)
}
_, err = (&States{}).Get(currency.Code{}, "")
_, err = (&States{}).Get(currency.EMPTYCODE, "")
if !errors.Is(err, errEmptyCurrency) {
t.Fatalf("received: %v, but expected: %v", err, errEmptyCurrency)
}

View File

@@ -535,7 +535,7 @@ func (b *Base) FormatExchangeCurrencies(pairs []currency.Pair, assetType asset.I
func (b *Base) FormatExchangeCurrency(p currency.Pair, assetType asset.Item) (currency.Pair, error) {
pairFmt, err := b.GetPairFormat(assetType, true)
if err != nil {
return currency.Pair{}, err
return currency.EMPTYPAIR, err
}
return p.Format(pairFmt.Delimiter, pairFmt.Uppercase), nil
}

View File

@@ -694,7 +694,7 @@ func TestLoadConfigPairs(t *testing.T) {
}
p = pairs[2].Format(pFmt.Delimiter, pFmt.Uppercase).String()
if p != "xrp/usd" {
t.Error("incorrect value, expected xrp/usd")
t.Error("incorrect value, expected xrp/usd", p)
}
avail, err = b.GetAvailablePairs(asset.Spot)
@@ -708,7 +708,7 @@ func TestLoadConfigPairs(t *testing.T) {
}
p = format.String()
if p != "xrp~usd" {
t.Error("incorrect value, expected xrp~usd")
t.Error("incorrect value, expected xrp~usd", p)
}
ps, err := b.Config.CurrencyPairs.Get(asset.Spot)
if err != nil {
@@ -1609,7 +1609,7 @@ func TestUpdatePairs(t *testing.T) {
t.Fatal(err)
}
pairs := currency.Pairs{
currency.Pair{},
currency.EMPTYPAIR,
p,
}
err = UAC.UpdatePairs(pairs, asset.Spot, true, true)

View File

@@ -506,7 +506,7 @@ func TestGetOrderInfo(t *testing.T) {
}
_, err := g.GetOrderInfo(context.Background(),
"917591554", currency.Pair{}, asset.Spot)
"917591554", currency.EMPTYPAIR, asset.Spot)
if err != nil {
if err.Error() != "no order found with id 917591554" && err.Error() != "failed to get open orders" {
t.Fatalf("GetOrderInfo() returned an error skipping test: %v", err)

View File

@@ -101,7 +101,7 @@ func TestStart(t *testing.T) {
func TestGetCurrenciesIncludingChains(t *testing.T) {
t.Parallel()
r, err := h.GetCurrenciesIncludingChains(context.Background(), currency.Code{})
r, err := h.GetCurrenciesIncludingChains(context.Background(), currency.EMPTYCODE)
if err != nil {
t.Error(err)
}
@@ -119,7 +119,7 @@ func TestGetCurrenciesIncludingChains(t *testing.T) {
func TestFGetContractInfo(t *testing.T) {
t.Parallel()
_, err := h.FGetContractInfo(context.Background(), "", "", currency.Pair{})
_, err := h.FGetContractInfo(context.Background(), "", "", currency.EMPTYPAIR)
if err != nil {
t.Error(err)
}
@@ -136,7 +136,7 @@ func TestFIndexPriceInfo(t *testing.T) {
func TestFContractPriceLimitations(t *testing.T) {
t.Parallel()
_, err := h.FContractPriceLimitations(context.Background(),
"BTC", "next_quarter", currency.Pair{})
"BTC", "next_quarter", currency.EMPTYPAIR)
if err != nil {
t.Error(err)
}
@@ -145,7 +145,7 @@ func TestFContractPriceLimitations(t *testing.T) {
func TestFContractOpenInterest(t *testing.T) {
t.Parallel()
_, err := h.FContractOpenInterest(context.Background(),
"BTC", "next_quarter", currency.Pair{})
"BTC", "next_quarter", currency.EMPTYPAIR)
if err != nil {
t.Error(err)
}
@@ -289,7 +289,7 @@ func TestFGetAccountInfo(t *testing.T) {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := h.FGetAccountInfo(context.Background(), currency.Code{})
_, err := h.FGetAccountInfo(context.Background(), currency.EMPTYCODE)
if err != nil {
t.Error(err)
}
@@ -300,7 +300,7 @@ func TestFGetPositionsInfo(t *testing.T) {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := h.FGetPositionsInfo(context.Background(), currency.Code{})
_, err := h.FGetPositionsInfo(context.Background(), currency.EMPTYCODE)
if err != nil {
t.Error(err)
}
@@ -311,7 +311,7 @@ func TestFGetAllSubAccountAssets(t *testing.T) {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := h.FGetAllSubAccountAssets(context.Background(), currency.Code{})
_, err := h.FGetAllSubAccountAssets(context.Background(), currency.EMPTYCODE)
if err != nil {
t.Error(err)
}
@@ -368,7 +368,7 @@ func TestFContractTradingFee(t *testing.T) {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := h.FContractTradingFee(context.Background(), currency.Code{})
_, err := h.FContractTradingFee(context.Background(), currency.EMPTYCODE)
if err != nil {
t.Error(err)
}
@@ -379,7 +379,7 @@ func TestFGetTransferLimits(t *testing.T) {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := h.FGetTransferLimits(context.Background(), currency.Code{})
_, err := h.FGetTransferLimits(context.Background(), currency.EMPTYCODE)
if err != nil {
t.Error(err)
}
@@ -390,7 +390,7 @@ func TestFGetPositionLimits(t *testing.T) {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := h.FGetPositionLimits(context.Background(), currency.Code{})
_, err := h.FGetPositionLimits(context.Background(), currency.EMPTYCODE)
if err != nil {
t.Error(err)
}
@@ -460,7 +460,7 @@ func TestFOrder(t *testing.T) {
t.Error(err)
}
_, err = h.FOrder(context.Background(),
currency.Pair{}, cp.Base.Upper().String(),
currency.EMPTYPAIR, cp.Base.Upper().String(),
"quarter", "123", "BUY", "open", "limit", 1, 1, 1)
if err != nil {
t.Error(err)
@@ -542,7 +542,7 @@ func TestFFlashCloseOrder(t *testing.T) {
}
t.Parallel()
_, err := h.FFlashCloseOrder(context.Background(),
currency.Pair{}, "BTC", "quarter", "BUY", "lightning", "", 1)
currency.EMPTYPAIR, "BTC", "quarter", "BUY", "lightning", "", 1)
if err != nil {
t.Error(err)
}
@@ -600,7 +600,7 @@ func TestFGetOrderHistory(t *testing.T) {
t.Error(err)
}
_, err = h.FGetOrderHistory(context.Background(),
currency.Pair{}, cp.Base.Upper().String(),
currency.EMPTYPAIR, cp.Base.Upper().String(),
"all", "all", "limit",
[]order.Status{},
5, 0, 0)
@@ -615,7 +615,7 @@ func TestFTradeHistory(t *testing.T) {
}
t.Parallel()
_, err := h.FTradeHistory(context.Background(),
currency.Pair{}, "BTC", "all", 10, 0, 0)
currency.EMPTYPAIR, "BTC", "all", 10, 0, 0)
if err != nil {
t.Error(err)
}
@@ -627,7 +627,7 @@ func TestFPlaceTriggerOrder(t *testing.T) {
}
t.Parallel()
_, err := h.FPlaceTriggerOrder(context.Background(),
currency.Pair{}, "EOS", "quarter", "greaterOrEqual",
currency.EMPTYPAIR, "EOS", "quarter", "greaterOrEqual",
"limit", "buy", "close", 1.1, 1.05, 5, 2)
if err != nil {
t.Error(err)
@@ -651,7 +651,7 @@ func TestFCancelAllTriggerOrders(t *testing.T) {
}
t.Parallel()
_, err := h.FCancelAllTriggerOrders(context.Background(),
currency.Pair{}, "BTC", "this_week")
currency.EMPTYPAIR, "BTC", "this_week")
if err != nil {
t.Error(err)
}
@@ -663,7 +663,7 @@ func TestFQueryTriggerOpenOrders(t *testing.T) {
}
t.Parallel()
_, err := h.FQueryTriggerOpenOrders(context.Background(),
currency.Pair{}, "BTC", 0, 0)
currency.EMPTYPAIR, "BTC", 0, 0)
if err != nil {
t.Error(err)
}
@@ -675,7 +675,7 @@ func TestFQueryTriggerOrderHistory(t *testing.T) {
}
t.Parallel()
_, err := h.FQueryTriggerOrderHistory(context.Background(),
currency.Pair{}, "EOS", "all", "all", 10, 0, 0)
currency.EMPTYPAIR, "EOS", "all", "all", 10, 0, 0)
if err != nil {
t.Error(err)
}
@@ -1571,7 +1571,7 @@ func TestGetSwapTriggerOrderHistory(t *testing.T) {
func TestGetSwapMarkets(t *testing.T) {
t.Parallel()
_, err := h.GetSwapMarkets(context.Background(), currency.Pair{})
_, err := h.GetSwapMarkets(context.Background(), currency.EMPTYPAIR)
if err != nil {
t.Error(err)
}

View File

@@ -360,7 +360,7 @@ func (h *HUOBI) FetchTradablePairs(ctx context.Context, a asset.Item) ([]string,
}
case asset.CoinMarginedFutures:
symbols, err := h.GetSwapMarkets(ctx, currency.Pair{})
symbols, err := h.GetSwapMarkets(ctx, currency.EMPTYPAIR)
if err != nil {
return nil, err
}
@@ -375,7 +375,7 @@ func (h *HUOBI) FetchTradablePairs(ctx context.Context, a asset.Item) ([]string,
}
}
case asset.Futures:
symbols, err := h.FGetContractInfo(ctx, "", "", currency.Pair{})
symbols, err := h.FGetContractInfo(ctx, "", "", currency.EMPTYPAIR)
if err != nil {
return nil, err
}
@@ -707,7 +707,7 @@ func (h *HUOBI) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (ac
case asset.CoinMarginedFutures:
// fetch swap account info
acctInfo, err := h.GetSwapAccountInfo(ctx, currency.Pair{})
acctInfo, err := h.GetSwapAccountInfo(ctx, currency.EMPTYPAIR)
if err != nil {
return info, err
}
@@ -727,14 +727,14 @@ func (h *HUOBI) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (ac
})
// fetch subaccounts data
subAccsData, err := h.GetSwapAllSubAccAssets(ctx, currency.Pair{})
subAccsData, err := h.GetSwapAllSubAccAssets(ctx, currency.EMPTYPAIR)
if err != nil {
return info, err
}
var currencyDetails []account.Balance
for x := range subAccsData.Data {
a, err := h.SwapSingleSubAccAssets(ctx,
currency.Pair{},
currency.EMPTYPAIR,
subAccsData.Data[x].SubUID)
if err != nil {
return info, err
@@ -750,7 +750,7 @@ func (h *HUOBI) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (ac
acc.Currencies = currencyDetails
case asset.Futures:
// fetch main account data
mainAcctData, err := h.FGetAccountInfo(ctx, currency.Code{})
mainAcctData, err := h.FGetAccountInfo(ctx, currency.EMPTYCODE)
if err != nil {
return info, err
}
@@ -770,7 +770,7 @@ func (h *HUOBI) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (ac
})
// fetch subaccounts data
subAccsData, err := h.FGetAllSubAccountAssets(ctx, currency.Code{})
subAccsData, err := h.FGetAllSubAccountAssets(ctx, currency.EMPTYCODE)
if err != nil {
return info, err
}

View File

@@ -163,7 +163,7 @@ func TestWrapperGetOrderInfo(t *testing.T) {
t.Skip("skipping test: api keys not set")
}
_, err := k.GetOrderInfo(context.Background(),
"123", currency.Pair{}, asset.Futures)
"123", currency.EMPTYPAIR, asset.Futures)
if err != nil {
t.Error(err)
}
@@ -882,7 +882,7 @@ func TestGetOrderInfo(t *testing.T) {
}
_, err := k.GetOrderInfo(context.Background(),
"OZPTPJ-HVYHF-EDIGXS", currency.Pair{}, asset.Spot)
"OZPTPJ-HVYHF-EDIGXS", currency.EMPTYPAIR, asset.Spot)
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting error")
}

View File

@@ -381,7 +381,7 @@ func TestGetOrderInfo(t *testing.T) {
t.Skip("API keys required but not set, skipping test")
}
_, err := l.GetOrderInfo(context.Background(),
"9ead39f5-701a-400b-b635-d7349eb0f6b", currency.Pair{}, asset.Spot)
"9ead39f5-701a-400b-b635-d7349eb0f6b", currency.EMPTYPAIR, asset.Spot)
if err != nil {
t.Error(err)
}

View File

@@ -847,7 +847,7 @@ func (o *OKGroup) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription,
subscriptions = append(subscriptions,
stream.ChannelSubscription{
Channel: channels[y],
Currency: currency.NewPair(newP.Base, currency.Code{}),
Currency: currency.NewPair(newP.Base, currency.EMPTYCODE),
Asset: asset.Futures,
})
futuresAccountCodes = append(futuresAccountCodes, newP.Base)

View File

@@ -374,7 +374,7 @@ func TestFilterOrdersByCurrencies(t *testing.T) {
if len(orders) != 1 {
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders))
}
currencies = append(currencies, currency.Pair{})
currencies = append(currencies, currency.EMPTYPAIR)
FilterOrdersByCurrencies(&orders, currencies)
if len(orders) != 1 {
t.Errorf("Orders failed to be filtered. Expected %v, received %v", 1, len(orders))
@@ -718,7 +718,7 @@ func TestUpdateOrderFromModify(t *testing.T) {
AssetType: "",
Date: time.Time{},
LastUpdated: time.Time{},
Pair: currency.Pair{},
Pair: currency.EMPTYPAIR,
Trades: nil,
}
updated := time.Now()
@@ -910,7 +910,7 @@ func TestUpdateOrderFromDetail(t *testing.T) {
AssetType: "",
Date: time.Time{},
LastUpdated: time.Time{},
Pair: currency.Pair{},
Pair: currency.EMPTYPAIR,
Trades: nil,
}
updated := time.Now()

View File

@@ -112,7 +112,9 @@ func (d *Detail) UpdateOrderFromDetail(m *Detail) {
d.PostOnly = m.PostOnly
updated = true
}
if !m.Pair.IsEmpty() && m.Pair != d.Pair {
if !m.Pair.IsEmpty() && !m.Pair.Equal(d.Pair) {
// TODO: Add a check to see if the original pair is empty as well, but
// error if it is changing from BTC-USD -> LTC-USD.
d.Pair = m.Pair
updated = true
}
@@ -274,7 +276,9 @@ func (d *Detail) UpdateOrderFromModify(m *Modify) {
d.PostOnly = m.PostOnly
updated = true
}
if !m.Pair.IsEmpty() && m.Pair != d.Pair {
if !m.Pair.IsEmpty() && !m.Pair.Equal(d.Pair) {
// TODO: Add a check to see if the original pair is empty as well, but
// error if it is changing from BTC-USD -> LTC-USD.
d.Pair = m.Pair
updated = true
}

View File

@@ -303,7 +303,7 @@ func TestDeployDepth(t *testing.T) {
if !errors.Is(err, errExchangeNameUnset) {
t.Fatalf("expecting %s error but received %v", errExchangeNameUnset, err)
}
_, err = DeployDepth("test", currency.Pair{}, asset.Spot)
_, err = DeployDepth("test", currency.EMPTYPAIR, asset.Spot)
if !errors.Is(err, errPairNotSet) {
t.Fatalf("expecting %s error but received %v", errPairNotSet, err)
}
@@ -370,7 +370,7 @@ func TestProcessOrderbook(t *testing.T) {
}
// test for empty pair
base.Pair = currency.Pair{}
base.Pair = currency.EMPTYPAIR
err = base.Process()
if err == nil {
t.Error("empty pair should throw an err")

View File

@@ -101,7 +101,7 @@ func (w *CurrencyDetails) GetPair(id float64) (currency.Pair, error) {
w.m.RLock()
defer w.m.RUnlock()
if w.pairs == nil {
return currency.Pair{}, errPairMapIsNil
return currency.EMPTYPAIR, errPairMapIsNil
}
p, ok := w.pairs[id]
@@ -124,13 +124,13 @@ func (w *CurrencyDetails) GetCode(id float64) (currency.Code, error) {
w.m.RLock()
defer w.m.RUnlock()
if w.codes == nil {
return currency.Code{}, errCodeMapIsNil
return currency.EMPTYCODE, errCodeMapIsNil
}
c, ok := w.codes[id]
if ok {
return c.Currency, nil
}
return currency.Code{}, errIDNotFoundInCodeMap
return currency.EMPTYCODE, errIDNotFoundInCodeMap
}
// GetWithdrawalTXFee returns withdrawal transaction fee for the currency

View File

@@ -35,32 +35,32 @@ func TestWsCurrencyMap(t *testing.T) {
t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err)
}
_, err = m.GetWithdrawalTXFee(currency.Code{})
_, err = m.GetWithdrawalTXFee(currency.EMPTYCODE)
if !errors.Is(err, errCodeMapIsNil) {
t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err)
}
_, err = m.GetDepositAddress(currency.Code{})
_, err = m.GetDepositAddress(currency.EMPTYCODE)
if !errors.Is(err, errCodeMapIsNil) {
t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err)
}
_, err = m.IsWithdrawAndDepositsEnabled(currency.Code{})
_, err = m.IsWithdrawAndDepositsEnabled(currency.EMPTYCODE)
if !errors.Is(err, errCodeMapIsNil) {
t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err)
}
_, err = m.IsTradingEnabledForCurrency(currency.Code{})
_, err = m.IsTradingEnabledForCurrency(currency.EMPTYCODE)
if !errors.Is(err, errCodeMapIsNil) {
t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err)
}
_, err = m.IsTradingEnabledForPair(currency.Pair{})
_, err = m.IsTradingEnabledForPair(currency.EMPTYPAIR)
if !errors.Is(err, errCodeMapIsNil) {
t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err)
}
_, err = m.IsPostOnlyForPair(currency.Pair{})
_, err = m.IsPostOnlyForPair(currency.EMPTYPAIR)
if !errors.Is(err, errCodeMapIsNil) {
t.Fatalf("expected: %v but received: %v", errCodeMapIsNil, err)
}
@@ -178,32 +178,32 @@ func TestWsCurrencyMap(t *testing.T) {
t.Fatal("unexpected results")
}
_, err = m.GetWithdrawalTXFee(currency.Code{})
_, err = m.GetWithdrawalTXFee(currency.EMPTYCODE)
if !errors.Is(err, errCurrencyNotFoundInMap) {
t.Fatalf("expected: %v but received: %v", errCurrencyNotFoundInMap, err)
}
_, err = m.GetDepositAddress(currency.Code{})
_, err = m.GetDepositAddress(currency.EMPTYCODE)
if !errors.Is(err, errCurrencyNotFoundInMap) {
t.Fatalf("expected: %v but received: %v", errCurrencyNotFoundInMap, err)
}
_, err = m.IsWithdrawAndDepositsEnabled(currency.Code{})
_, err = m.IsWithdrawAndDepositsEnabled(currency.EMPTYCODE)
if !errors.Is(err, errCurrencyNotFoundInMap) {
t.Fatalf("expected: %v but received: %v", errCurrencyNotFoundInMap, err)
}
_, err = m.IsTradingEnabledForCurrency(currency.Code{})
_, err = m.IsTradingEnabledForCurrency(currency.EMPTYCODE)
if !errors.Is(err, errCurrencyNotFoundInMap) {
t.Fatalf("expected: %v but received: %v", errCurrencyNotFoundInMap, err)
}
_, err = m.IsTradingEnabledForPair(currency.Pair{})
_, err = m.IsTradingEnabledForPair(currency.EMPTYPAIR)
if !errors.Is(err, errCurrencyNotFoundInMap) {
t.Fatalf("expected: %v but received: %v", errCurrencyNotFoundInMap, err)
}
_, err = m.IsPostOnlyForPair(currency.Pair{})
_, err = m.IsPostOnlyForPair(currency.EMPTYPAIR)
if !errors.Is(err, errCurrencyNotFoundInMap) {
t.Fatalf("expected: %v but received: %v", errCurrencyNotFoundInMap, err)
}

View File

@@ -61,7 +61,7 @@ func Add(exchange string, p currency.Pair, a asset.Item, price, volume float64)
return errors.New("cannot add or update, invalid params")
}
if p.Base == currency.XBT {
if p.Base.Equal(currency.XBT) {
newPair, err := currency.NewPairFromStrings(currency.BTC.String(),
p.Quote.String())
if err != nil {
@@ -70,7 +70,7 @@ func Add(exchange string, p currency.Pair, a asset.Item, price, volume float64)
Append(exchange, newPair, a, price, volume)
}
if p.Quote == currency.USDT {
if p.Quote.Equal(currency.USDT) {
newPair, err := currency.NewPairFromStrings(p.Base.String(), currency.USD.String())
if err != nil {
return err

View File

@@ -29,7 +29,7 @@ func TestMain(m *testing.M) {
var cpyMux *dispatch.Mux
func TestSubscribeTicker(t *testing.T) {
_, err := SubscribeTicker("", currency.Pair{}, asset.Item(""))
_, err := SubscribeTicker("", currency.EMPTYPAIR, asset.Item(""))
if err == nil {
t.Error("error cannot be nil")
}

View File

@@ -125,7 +125,7 @@ func TestGetOpenOrders(t *testing.T) {
func TestGetOrderInfo(t *testing.T) {
t.Parallel()
_, err := y.GetOrderInfo(context.Background(),
"6196974", currency.Pair{}, asset.Spot)
"6196974", currency.EMPTYPAIR, asset.Spot)
if err == nil {
t.Error("GetOrderInfo() Expected error")
}

View File

@@ -971,15 +971,15 @@ func Test_FormatExchangeKlineInterval(t *testing.T) {
}
func TestValidateCandlesRequest(t *testing.T) {
_, err := z.validateCandlesRequest(currency.Pair{}, "", time.Time{}, time.Time{}, kline.Interval(-1))
_, err := z.validateCandlesRequest(currency.EMPTYPAIR, "", time.Time{}, time.Time{}, kline.Interval(-1))
if !errors.Is(err, common.ErrDateUnset) {
t.Error(err)
}
_, err = z.validateCandlesRequest(currency.Pair{}, "", time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC), time.Time{}, kline.Interval(-1))
_, err = z.validateCandlesRequest(currency.EMPTYPAIR, "", time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC), time.Time{}, kline.Interval(-1))
if !errors.Is(err, common.ErrDateUnset) {
t.Error(err)
}
_, err = z.validateCandlesRequest(currency.Pair{}, asset.Spot, time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC), time.Date(2020, 1, 1, 1, 1, 1, 3, time.UTC), kline.OneHour)
_, err = z.validateCandlesRequest(currency.EMPTYPAIR, asset.Spot, time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC), time.Date(2020, 1, 1, 1, 1, 1, 3, time.UTC), kline.OneHour)
if err != nil && err.Error() != "pair not enabled" {
t.Error(err)
}

View File

@@ -140,7 +140,7 @@ func TestExchange_QueryOrder(t *testing.T) {
}
t.Parallel()
_, err := exchangeTest.QueryOrder(context.Background(),
exchName, orderID, currency.Pair{}, assetType)
exchName, orderID, currency.EMPTYPAIR, assetType)
if err != nil {
t.Fatal(err)
}

View File

@@ -99,7 +99,7 @@ func TestWrapper_CancelOrder(t *testing.T) {
}
_, err = testWrapper.CancelOrder(context.Background(),
exchName, orderID, currency.Pair{}, assetType)
exchName, orderID, currency.EMPTYPAIR, assetType)
if err != nil {
t.Error(err)
}
@@ -163,13 +163,13 @@ func TestWrapper_QueryOrder(t *testing.T) {
t.Parallel()
_, err := testWrapper.QueryOrder(context.Background(),
exchName, orderID, currency.Pair{}, assetType)
exchName, orderID, currency.EMPTYPAIR, assetType)
if err != nil {
t.Fatal(err)
}
_, err = testWrapper.QueryOrder(context.Background(),
exchError.String(), "", currency.Pair{}, assetType)
exchError.String(), "", currency.EMPTYPAIR, assetType)
if err == nil {
t.Fatal("expected QueryOrder to return error on invalid name")
}

View File

@@ -181,4 +181,5 @@ func init() {
OrderBook = registerNewSubLogger("ORDERBOOK")
Trade = registerNewSubLogger("TRADE")
Fill = registerNewSubLogger("FILL")
Currency = registerNewSubLogger("CURRENCY")
}

View File

@@ -35,6 +35,7 @@ var (
OrderBook *SubLogger
Trade *SubLogger
Fill *SubLogger
Currency *SubLogger
)
// SubLogger defines a sub logger can be used externally for packages wanted to

View File

@@ -63,7 +63,7 @@ func main() {
flag.BoolVar(&settings.EnableTickerSyncing, "tickersync", true, "enables ticker syncing for all enabled exchanges")
flag.BoolVar(&settings.EnableOrderbookSyncing, "orderbooksync", true, "enables orderbook syncing for all enabled exchanges")
flag.BoolVar(&settings.EnableTradeSyncing, "tradesync", false, "enables trade syncing for all enabled exchanges")
flag.IntVar(&settings.SyncWorkers, "syncworkers", engine.DefaultSyncerWorkers, "the amount of workers (goroutines) to use for syncing exchange data")
flag.IntVar(&settings.SyncWorkersCount, "syncworkers", engine.DefaultSyncerWorkers, "the amount of workers (goroutines) to use for syncing exchange data")
flag.BoolVar(&settings.SyncContinuously, "synccontinuously", true, "whether to sync exchange data continuously (ticker, orderbook and trade history info")
flag.DurationVar(&settings.SyncTimeoutREST, "synctimeoutrest", engine.DefaultSyncerTimeoutREST,
"the amount of time before the syncer will switch from rest protocol to the streaming protocol (e.g. from REST to websocket)")
@@ -73,6 +73,7 @@ func main() {
// Forex provider settings
flag.BoolVar(&settings.EnableCurrencyConverter, "currencyconverter", false, "overrides config and sets up foreign exchange Currency Converter")
flag.BoolVar(&settings.EnableCurrencyLayer, "currencylayer", false, "overrides config and sets up foreign exchange Currency Layer")
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")

View File

@@ -102,7 +102,7 @@ func (b *Account) ValidateForWithdrawal(exchange string, cur currency.Code) (err
err = append(err, ErrCurrencyNotSupportedByAccount)
}
if cur.Upper() == currency.AUD {
if cur.Equal(currency.AUD) {
if b.BSBNumber == "" {
err = append(err, ErrBSBRequiredForAUD)
}

View File

@@ -113,7 +113,7 @@ func (b *Base) GetAddressBalance(address, description string, coinType currency.
for x := range b.Addresses {
if b.Addresses[x].Address == address &&
b.Addresses[x].Description == description &&
b.Addresses[x].CoinType == coinType {
b.Addresses[x].CoinType.Equal(coinType) {
return b.Addresses[x].Balance, true
}
}
@@ -145,7 +145,7 @@ func (b *Base) AddressExists(address string) bool {
// associated with the portfolio base
func (b *Base) ExchangeAddressExists(exchangeName string, coinType currency.Code) bool {
for x := range b.Addresses {
if b.Addresses[x].Address == exchangeName && b.Addresses[x].CoinType == coinType {
if b.Addresses[x].Address == exchangeName && b.Addresses[x].CoinType.Equal(coinType) {
return true
}
}
@@ -176,7 +176,7 @@ func (b *Base) UpdateAddressBalance(address string, amount float64) {
// RemoveExchangeAddress removes an exchange address from the portfolio.
func (b *Base) RemoveExchangeAddress(exchangeName string, coinType currency.Code) {
for x := range b.Addresses {
if b.Addresses[x].Address == exchangeName && b.Addresses[x].CoinType == coinType {
if b.Addresses[x].Address == exchangeName && b.Addresses[x].CoinType.Equal(coinType) {
b.Addresses = append(b.Addresses[:x], b.Addresses[x+1:]...)
return
}
@@ -187,7 +187,7 @@ func (b *Base) RemoveExchangeAddress(exchangeName string, coinType currency.Code
// against correct exchangeName and coinType.
func (b *Base) UpdateExchangeAddressBalance(exchangeName string, coinType currency.Code, balance float64) {
for x := range b.Addresses {
if b.Addresses[x].Address == exchangeName && b.Addresses[x].CoinType == coinType {
if b.Addresses[x].Address == exchangeName && b.Addresses[x].CoinType.Equal(coinType) {
b.Addresses[x].Balance = balance
}
}
@@ -237,7 +237,7 @@ func (b *Base) RemoveAddress(address, description string, coinType currency.Code
for x := range b.Addresses {
if b.Addresses[x].Address == address &&
b.Addresses[x].CoinType == coinType &&
b.Addresses[x].CoinType.Equal(coinType) &&
b.Addresses[x].Description == description {
b.Addresses = append(b.Addresses[:x], b.Addresses[x+1:]...)
return nil

View File

@@ -174,7 +174,7 @@ func TestUpdateAddressBalance(t *testing.T) {
newBase.UpdateAddressBalance("someaddress", 0.03)
value := newBase.GetPortfolioSummary()
if value.Totals[0].Coin != currency.LTC &&
if !value.Totals[0].Coin.Equal(currency.LTC) &&
value.Totals[0].Balance != 0.03 {
t.Error("UpdateUpdateAddressBalance error")
}
@@ -245,7 +245,7 @@ func TestUpdateExchangeAddressBalance(t *testing.T) {
b.UpdateExchangeAddressBalance("someaddress", currency.LTC, 0.04)
value := b.GetPortfolioSummary()
if value.Totals[0].Coin != currency.LTC && value.Totals[0].Balance != 0.04 {
if !value.Totals[0].Coin.Equal(currency.LTC) && value.Totals[0].Balance != 0.04 {
t.Error("incorrect portfolio balance")
}
}
@@ -487,18 +487,18 @@ func TestGetPortfolioSummary(t *testing.T) {
getTotalsVal := func(c currency.Code) Coin {
for x := range value.Totals {
if value.Totals[x].Coin == c {
if value.Totals[x].Coin.Equal(c) {
return value.Totals[x]
}
}
return Coin{}
}
if getTotalsVal(currency.LTC).Coin != currency.LTC {
if !getTotalsVal(currency.LTC).Coin.Equal(currency.LTC) {
t.Error("mismatched currency")
}
if getTotalsVal(currency.ETH).Coin == currency.LTC {
if getTotalsVal(currency.ETH).Coin.Equal(currency.LTC) {
t.Error("mismatched currency")
}

Some files were not shown because too many files have changed in this diff Show More