mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
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:
10
README.md
10
README.md
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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{},
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -82,8 +82,8 @@ func TestFindMatchingUSDPairs(t *testing.T) {
|
||||
description: "already has USD",
|
||||
initialPair: currency.NewPair(currency.BTC, currency.USDT),
|
||||
availablePairs: ¤cy.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: ¤cy.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: ¤cy.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: ¤cy.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: ¤cy.PairStore{Available: currency.Pairs{currency.NewPair(currency.BTC, currency.DOGE)}},
|
||||
basePair: currency.Pair{},
|
||||
quotePair: currency.Pair{},
|
||||
basePair: currency.EMPTYPAIR,
|
||||
quotePair: currency.EMPTYPAIR,
|
||||
expectedErr: errCurrencyNotFoundInPairs,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
+ Currency Converter API support
|
||||
+ Currency Layer support
|
||||
+ Exchange Rates support
|
||||
+ Fixer.io support
|
||||
+ Open Exchange Rates support
|
||||
+ ExchangeRate.host support
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMain(t *testing.T) {
|
||||
|
||||
}
|
||||
259
config/config.go
259
config/config.go
@@ -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 = ¤cy.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)
|
||||
}
|
||||
|
||||
@@ -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: ¤cy.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: ¤cy.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: ¤cy.PairFormat{},
|
||||
RequestFormat: ¤cy.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 = ¤cy.PairFormat{
|
||||
Uppercase: true,
|
||||
}
|
||||
cfg.Currency.FiatDisplayCurrency = currency.Code{}
|
||||
cfg.Currency.FiatDisplayCurrency = currency.EMPTYCODE
|
||||
cfg.FiatDisplayCurrency = ¤cy.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 = ¤cy.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 = ¤cy.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")
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"maxsize": 250
|
||||
},
|
||||
"advancedSettings": {
|
||||
"showLogSystemName": false,
|
||||
"showLogSystemName": true,
|
||||
"spacer": " | ",
|
||||
"timeStampFormat": "02/01/2006 15:04:05",
|
||||
"headers": {
|
||||
|
||||
186
currency/code.go
186
currency/code.go
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -20,9 +20,6 @@ const (
|
||||
APIEndpointConversion = "convert"
|
||||
APIEndpointTimeframe = "timeframe"
|
||||
APIEndpointChange = "change"
|
||||
|
||||
authRate = 0
|
||||
unAuthRate = 0
|
||||
)
|
||||
|
||||
// CurrencyLayer is a foreign exchange rate provider at
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
133
engine/engine.go
133
engine/engine.go
@@ -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,
|
||||
},
|
||||
¤cy.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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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: ¤cy.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: ¤cy.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: ¤cy.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: ¤cy.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: ¤cy.PairFormat{}}, em, &config.RemoteControlConfig{}, false)
|
||||
if !errors.Is(err, nil) {
|
||||
t.Errorf("error '%v', expected '%v'", err, nil)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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{}, ¤cy.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{}, ¤cy.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 := ¤cy.Config{CurrencyPairFormat: ¤cy.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{}, ¤cy.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{}, ¤cy.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 := ¤cy.Config{CurrencyPairFormat: ¤cy.PairFormat{
|
||||
Uppercase: false,
|
||||
Delimiter: "-",
|
||||
}}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -181,4 +181,5 @@ func init() {
|
||||
OrderBook = registerNewSubLogger("ORDERBOOK")
|
||||
Trade = registerNewSubLogger("TRADE")
|
||||
Fill = registerNewSubLogger("FILL")
|
||||
Currency = registerNewSubLogger("CURRENCY")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
3
main.go
3
main.go
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user