Files
gocryptotrader/currency/forexprovider/base/base_interface.go
Ryan O'Hara-Reid a2381310da GCT: general updates across codebase (#699)
* orderbook: export orderbook nodes for external strategy inspection

* orderbook: Add in methods for locking and unlocking multiple books at the same time e.g. book1.LockWith(book2); defer book1.UnlockWith(book2)

* include waiting functionality for depth change alert

* backtester: add word.

* log: include logger changes to impl with downstream integration

* engine: reduce params for loading exchange

* assort: rm verbose in tests, change wording in ob, expose sync.waitgroup for ext. sync options

* ticker: reduce map look ups and contention when using RW mutex when there are over 80% writes adds find last function to get the latest rate

* engine/syncmanager: add in waitgroup for step over for external package calls

* cleaup

* engine: linter fix

* currency/fx: include all references to fiat currencies to default

* orderbook: Add in fields to Unsafe type for strategies to detect potential out of sync book operations

* syncmanager: changed config variable to display correct time

* ordermanager: Add time when none provided

* currency/manager: update getasset param to get enabled assets for minor optimizations

* ftx: use get all wallet balances for a better accounts breakdown

* orderbook: unlock in reverse order

* bithumb: fixes bug on market buy and sell orders

* bithumb: fix bug for nonce is also time window sensitive

* bithumb: get orders add required parameter

* bithumb: Add asset type to account struct

* currency: improve log output when checking currency and it fails

* bithumb: Add error return on incomplete pair

* ticker:unexport all service related methods

* ticker/currency: fixes

* orderbook: fix comment

* engine: revert variable name in LoadExchange method

* sync_manager: fix panic when enabling disabling manager

* engine: fix naming convention of exported function and comments

* engine: update comment

* orderbook: fix comment for unsafe type
2021-07-29 14:42:28 +10:00

158 lines
3.9 KiB
Go

package base
import (
"errors"
"fmt"
"strings"
"sync"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/log"
)
// IFXProvider enforces standard functions for all foreign exchange providers
// supported in GoCryptoTrader
type IFXProvider interface {
Setup(config Settings) error
GetRates(baseCurrency, symbols string) (map[string]float64, error)
GetName() string
IsEnabled() bool
IsPrimaryProvider() bool
GetSupportedCurrencies() ([]string, error)
}
// FXHandler defines a full suite of FX data providers with failure backup with
// unsupported currency shunt procedure
type FXHandler struct {
Primary Provider
Support []Provider
mtx sync.Mutex
}
// Provider defines a singular foreign exchange provider with its supported
// currencies to cross reference request currencies and if not supported shunt
// request traffic to and from other providers so that we can maintain full
// currency list integration
type Provider struct {
Provider IFXProvider
SupportedCurrencies []string
}
// GetNewRate access rates by predetermined logic based on how a provider
// handles requests
func (p *Provider) GetNewRate(base string, currencies []string) (map[string]float64, error) {
if !p.Provider.IsEnabled() {
return nil, fmt.Errorf("provider %s is not enabled",
p.Provider.GetName())
}
switch p.Provider.GetName() {
case "ExchangeRates":
return p.Provider.GetRates(base, "") // Zero value to get all rates
default:
return p.Provider.GetRates(base, strings.Join(currencies, ","))
}
}
// CheckCurrencies cross references supplied currencies with exchange supported
// currencies, if there are any currencies not supported it returns a list
// to pass on to the next provider
func (p Provider) CheckCurrencies(currencies []string) []string {
var spillOver []string
for _, c := range currencies {
if !common.StringDataCompareInsensitive(p.SupportedCurrencies, c) {
spillOver = append(spillOver, c)
}
}
return spillOver
}
// GetCurrencyData returns currency data from enabled FX providers
func (f *FXHandler) GetCurrencyData(baseCurrency string, currencies []string) (map[string]float64, error) {
var fullRange = currencies
if !common.StringDataCompareInsensitive(currencies, baseCurrency) {
fullRange = append(fullRange, baseCurrency)
}
f.mtx.Lock()
defer f.mtx.Unlock()
if f.Primary.Provider == nil {
return nil, errors.New("primary foreign exchange provider details not set")
}
shunt := f.Primary.CheckCurrencies(fullRange)
rates, err := f.Primary.GetNewRate(baseCurrency, currencies)
if err != nil {
return f.backupGetRate(baseCurrency, currencies)
}
if len(shunt) != 0 {
return rates, nil
}
rateNew, err := f.backupGetRate(baseCurrency, shunt)
if err != nil {
log.Warnf(log.Global, "%s and subsequent providers, failed to update rate map for currencies %v %v",
f.Primary.Provider.GetName(),
shunt,
err)
}
for key, val := range rateNew {
rates[key] = val
}
return rates, nil
}
// backupGetRate uses the currencies that are supported and falls through, and
// errors when unsupported currency found
func (f *FXHandler) backupGetRate(base string, currencies []string) (map[string]float64, error) {
if f.Support == nil {
return nil, errors.New("no supporting foreign exchange providers set")
}
var shunt []string
rate := make(map[string]float64)
for i := range f.Support {
if len(shunt) != 0 {
shunt = f.Support[i].CheckCurrencies(shunt)
newRate, err := f.Support[i].GetNewRate(base, shunt)
if err != nil {
continue
}
for k, v := range newRate {
rate[k] = v
}
if len(shunt) != 0 {
continue
}
return rate, nil
}
shunt = f.Support[i].CheckCurrencies(currencies)
newRate, err := f.Support[i].GetNewRate(base, currencies)
if err != nil {
continue
}
for k, v := range newRate {
rate[k] = v
}
if len(shunt) != 0 {
continue
}
return rate, nil
}
return nil, fmt.Errorf("currencies %s not supported", shunt)
}