mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-14 07:26:47 +00:00
343 lines
11 KiB
Go
343 lines
11 KiB
Go
package exchange
|
|
|
|
import (
|
|
"log"
|
|
"time"
|
|
|
|
"github.com/thrasher-/gocryptotrader/common"
|
|
"github.com/thrasher-/gocryptotrader/config"
|
|
"github.com/thrasher-/gocryptotrader/currency/pair"
|
|
"github.com/thrasher-/gocryptotrader/exchanges/nonce"
|
|
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
|
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
|
)
|
|
|
|
const (
|
|
warningBase64DecryptSecretKeyFailed = "WARNING -- Exchange %s unable to base64 decode secret key.. Disabling Authenticated API support."
|
|
|
|
// WarningAuthenticatedRequestWithoutCredentialsSet error message for authenticated request without credentails set
|
|
WarningAuthenticatedRequestWithoutCredentialsSet = "WARNING -- Exchange %s authenticated HTTP request called but not supported due to unset/default API keys."
|
|
// ErrExchangeNotFound is a constant for an error message
|
|
ErrExchangeNotFound = "Exchange not found in dataset."
|
|
)
|
|
|
|
// AccountInfo is a Generic type to hold each exchange's holdings in
|
|
// all enabled currencies
|
|
type AccountInfo struct {
|
|
ExchangeName string
|
|
Currencies []AccountCurrencyInfo
|
|
}
|
|
|
|
// AccountCurrencyInfo is a sub type to store currency name and value
|
|
type AccountCurrencyInfo struct {
|
|
CurrencyName string
|
|
TotalValue float64
|
|
Hold float64
|
|
}
|
|
|
|
// Base stores the individual exchange information
|
|
type Base struct {
|
|
Name string
|
|
Enabled bool
|
|
Verbose bool
|
|
Websocket bool
|
|
RESTPollingDelay time.Duration
|
|
AuthenticatedAPISupport bool
|
|
APISecret, APIKey, ClientID string
|
|
Nonce nonce.Nonce
|
|
TakerFee, MakerFee, Fee float64
|
|
BaseCurrencies []string
|
|
AvailablePairs []string
|
|
EnabledPairs []string
|
|
WebsocketURL string
|
|
APIUrl string
|
|
RequestCurrencyPairFormat config.CurrencyPairFormatConfig
|
|
ConfigCurrencyPairFormat config.CurrencyPairFormatConfig
|
|
}
|
|
|
|
// IBotExchange enforces standard functions for all exchanges supported in
|
|
// GoCryptoTrader
|
|
type IBotExchange interface {
|
|
Setup(exch config.ExchangeConfig)
|
|
Start()
|
|
SetDefaults()
|
|
GetName() string
|
|
IsEnabled() bool
|
|
GetTickerPrice(currency pair.CurrencyPair) (ticker.TickerPrice, error)
|
|
GetOrderbookEx(currency pair.CurrencyPair) (orderbook.OrderbookBase, error)
|
|
GetEnabledCurrencies() []pair.CurrencyPair
|
|
GetExchangeAccountInfo() (AccountInfo, error)
|
|
GetAuthenticatedAPISupport() bool
|
|
}
|
|
|
|
// SetCurrencyPairFormat checks the exchange request and config currency pair
|
|
// formats and sets it to a default setting if it doesn't exist
|
|
func (e *Base) SetCurrencyPairFormat() error {
|
|
cfg := config.GetConfig()
|
|
exch, err := cfg.GetExchangeConfig(e.Name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
update := false
|
|
if exch.RequestCurrencyPairFormat == nil {
|
|
exch.RequestCurrencyPairFormat = &config.CurrencyPairFormatConfig{
|
|
Delimiter: e.RequestCurrencyPairFormat.Delimiter,
|
|
Uppercase: e.RequestCurrencyPairFormat.Uppercase,
|
|
Separator: e.RequestCurrencyPairFormat.Separator,
|
|
Index: e.RequestCurrencyPairFormat.Index,
|
|
}
|
|
update = true
|
|
} else {
|
|
e.RequestCurrencyPairFormat = *exch.RequestCurrencyPairFormat
|
|
}
|
|
|
|
if exch.ConfigCurrencyPairFormat == nil {
|
|
exch.ConfigCurrencyPairFormat = &config.CurrencyPairFormatConfig{
|
|
Delimiter: e.ConfigCurrencyPairFormat.Delimiter,
|
|
Uppercase: e.ConfigCurrencyPairFormat.Uppercase,
|
|
Separator: e.ConfigCurrencyPairFormat.Separator,
|
|
Index: e.ConfigCurrencyPairFormat.Index,
|
|
}
|
|
update = true
|
|
} else {
|
|
e.ConfigCurrencyPairFormat = *exch.ConfigCurrencyPairFormat
|
|
}
|
|
|
|
if update {
|
|
return cfg.UpdateExchangeConfig(exch)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetAuthenticatedAPISupport returns whether the exchange supports
|
|
// authenticated API requests
|
|
func (e *Base) GetAuthenticatedAPISupport() bool {
|
|
return e.AuthenticatedAPISupport
|
|
}
|
|
|
|
// GetName is a method that returns the name of the exchange base
|
|
func (e *Base) GetName() string {
|
|
return e.Name
|
|
}
|
|
|
|
// GetEnabledCurrencies is a method that returns the enabled currency pairs of
|
|
// the exchange base
|
|
func (e *Base) GetEnabledCurrencies() []pair.CurrencyPair {
|
|
var pairs []pair.CurrencyPair
|
|
for x := range e.EnabledPairs {
|
|
var currencyPair pair.CurrencyPair
|
|
if e.RequestCurrencyPairFormat.Delimiter != "" {
|
|
if e.ConfigCurrencyPairFormat.Delimiter != "" {
|
|
if e.ConfigCurrencyPairFormat.Delimiter == e.RequestCurrencyPairFormat.Delimiter {
|
|
currencyPair = pair.NewCurrencyPairDelimiter(e.EnabledPairs[x],
|
|
e.RequestCurrencyPairFormat.Delimiter)
|
|
} else {
|
|
currencyPair = pair.NewCurrencyPairDelimiter(e.EnabledPairs[x],
|
|
e.ConfigCurrencyPairFormat.Delimiter)
|
|
currencyPair.Delimiter = "-"
|
|
}
|
|
} else {
|
|
if e.ConfigCurrencyPairFormat.Index != "" {
|
|
currencyPair = pair.NewCurrencyPairFromIndex(e.EnabledPairs[x],
|
|
e.ConfigCurrencyPairFormat.Index)
|
|
} else {
|
|
currencyPair = pair.NewCurrencyPair(e.EnabledPairs[x][0:3],
|
|
e.EnabledPairs[x][3:])
|
|
}
|
|
}
|
|
} else {
|
|
if e.ConfigCurrencyPairFormat.Delimiter != "" {
|
|
currencyPair = pair.NewCurrencyPairDelimiter(e.EnabledPairs[x],
|
|
e.ConfigCurrencyPairFormat.Delimiter)
|
|
} else {
|
|
if e.ConfigCurrencyPairFormat.Index != "" {
|
|
currencyPair = pair.NewCurrencyPairFromIndex(e.EnabledPairs[x],
|
|
e.ConfigCurrencyPairFormat.Index)
|
|
} else {
|
|
currencyPair = pair.NewCurrencyPair(e.EnabledPairs[x][0:3],
|
|
e.EnabledPairs[x][3:])
|
|
}
|
|
}
|
|
}
|
|
pairs = append(pairs, currencyPair)
|
|
}
|
|
return pairs
|
|
}
|
|
|
|
// GetAvailableCurrencies is a method that returns the available currency pairs
|
|
// of the exchange base
|
|
func (e *Base) GetAvailableCurrencies() []pair.CurrencyPair {
|
|
var pairs []pair.CurrencyPair
|
|
for x := range e.AvailablePairs {
|
|
var currencyPair pair.CurrencyPair
|
|
if e.RequestCurrencyPairFormat.Delimiter != "" {
|
|
if e.ConfigCurrencyPairFormat.Delimiter != "" {
|
|
if e.ConfigCurrencyPairFormat.Delimiter == e.RequestCurrencyPairFormat.Delimiter {
|
|
currencyPair = pair.NewCurrencyPairDelimiter(e.AvailablePairs[x],
|
|
e.RequestCurrencyPairFormat.Delimiter)
|
|
} else {
|
|
currencyPair = pair.NewCurrencyPairDelimiter(e.AvailablePairs[x],
|
|
e.ConfigCurrencyPairFormat.Delimiter)
|
|
currencyPair.Delimiter = "-"
|
|
}
|
|
} else {
|
|
if e.ConfigCurrencyPairFormat.Index != "" {
|
|
currencyPair = pair.NewCurrencyPairFromIndex(e.AvailablePairs[x],
|
|
e.ConfigCurrencyPairFormat.Index)
|
|
} else {
|
|
currencyPair = pair.NewCurrencyPair(e.AvailablePairs[x][0:3],
|
|
e.AvailablePairs[x][3:])
|
|
}
|
|
}
|
|
} else {
|
|
if e.ConfigCurrencyPairFormat.Delimiter != "" {
|
|
currencyPair = pair.NewCurrencyPairDelimiter(e.AvailablePairs[x],
|
|
e.ConfigCurrencyPairFormat.Delimiter)
|
|
} else {
|
|
if e.ConfigCurrencyPairFormat.Index != "" {
|
|
currencyPair = pair.NewCurrencyPairFromIndex(e.AvailablePairs[x],
|
|
e.ConfigCurrencyPairFormat.Index)
|
|
} else {
|
|
currencyPair = pair.NewCurrencyPair(e.AvailablePairs[x][0:3],
|
|
e.AvailablePairs[x][3:])
|
|
}
|
|
}
|
|
}
|
|
pairs = append(pairs, currencyPair)
|
|
}
|
|
return pairs
|
|
}
|
|
|
|
// GetExchangeFormatCurrencySeperator returns whether or not a specific
|
|
// exchange contains a separator used for API requests
|
|
func GetExchangeFormatCurrencySeperator(exchName string) bool {
|
|
cfg := config.GetConfig()
|
|
exch, err := cfg.GetExchangeConfig(exchName)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
if exch.RequestCurrencyPairFormat.Separator != "" {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// GetAndFormatExchangeCurrencies returns a pair.CurrencyItem string containing
|
|
// the exchanges formatted currency pairs
|
|
func GetAndFormatExchangeCurrencies(exchName string, pairs []pair.CurrencyPair) (pair.CurrencyItem, error) {
|
|
var currencyItems pair.CurrencyItem
|
|
cfg := config.GetConfig()
|
|
exch, err := cfg.GetExchangeConfig(exchName)
|
|
if err != nil {
|
|
return currencyItems, err
|
|
}
|
|
|
|
for x := range pairs {
|
|
currencyItems += FormatExchangeCurrency(exchName, pairs[x])
|
|
if x == len(pairs)-1 {
|
|
continue
|
|
}
|
|
currencyItems += pair.CurrencyItem(exch.RequestCurrencyPairFormat.Separator)
|
|
}
|
|
return currencyItems, nil
|
|
}
|
|
|
|
// FormatExchangeCurrency is a method that formats and returns a currency pair
|
|
// based on the user currency display preferences
|
|
func FormatExchangeCurrency(exchName string, p pair.CurrencyPair) pair.CurrencyItem {
|
|
cfg := config.GetConfig()
|
|
exch, _ := cfg.GetExchangeConfig(exchName)
|
|
|
|
return p.Display(exch.RequestCurrencyPairFormat.Delimiter,
|
|
exch.RequestCurrencyPairFormat.Uppercase)
|
|
}
|
|
|
|
// FormatCurrency is a method that formats and returns a currency pair
|
|
// based on the user currency display preferences
|
|
func FormatCurrency(p pair.CurrencyPair) pair.CurrencyItem {
|
|
cfg := config.GetConfig()
|
|
return p.Display(cfg.CurrencyPairFormat.Delimiter,
|
|
cfg.CurrencyPairFormat.Uppercase)
|
|
}
|
|
|
|
// SetEnabled is a method that sets if the exchange is enabled
|
|
func (e *Base) SetEnabled(enabled bool) {
|
|
e.Enabled = enabled
|
|
}
|
|
|
|
// IsEnabled is a method that returns if the current exchange is enabled
|
|
func (e *Base) IsEnabled() bool {
|
|
return e.Enabled
|
|
}
|
|
|
|
// SetAPIKeys is a method that sets the current API keys for the exchange
|
|
func (e *Base) SetAPIKeys(APIKey, APISecret, ClientID string, b64Decode bool) {
|
|
if !e.AuthenticatedAPISupport {
|
|
return
|
|
}
|
|
|
|
e.APIKey = APIKey
|
|
e.ClientID = ClientID
|
|
|
|
if b64Decode {
|
|
result, err := common.Base64Decode(APISecret)
|
|
if err != nil {
|
|
e.AuthenticatedAPISupport = false
|
|
log.Printf(warningBase64DecryptSecretKeyFailed, e.Name)
|
|
}
|
|
e.APISecret = string(result)
|
|
} else {
|
|
e.APISecret = APISecret
|
|
}
|
|
}
|
|
|
|
// UpdateEnabledCurrencies is a method that sets new pairs to the current
|
|
// exchange. Setting force to true upgrades the enabled currencies
|
|
func (e *Base) UpdateEnabledCurrencies(exchangeProducts []string, force bool) error {
|
|
exchangeProducts = common.SplitStrings(common.StringToUpper(common.JoinStrings(exchangeProducts, ",")), ",")
|
|
diff := common.StringSliceDifference(e.EnabledPairs, exchangeProducts)
|
|
if force || len(diff) > 0 {
|
|
cfg := config.GetConfig()
|
|
exch, err := cfg.GetExchangeConfig(e.Name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if force {
|
|
log.Printf("%s forced update of enabled pairs.", e.Name)
|
|
} else {
|
|
log.Printf("%s Updating available pairs. Difference: %s.\n", e.Name, diff)
|
|
}
|
|
exch.EnabledPairs = common.JoinStrings(exchangeProducts, ",")
|
|
e.EnabledPairs = exchangeProducts
|
|
return cfg.UpdateExchangeConfig(exch)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// UpdateAvailableCurrencies is a method that sets new pairs to the current
|
|
// exchange. Setting force to true upgrades the available currencies
|
|
func (e *Base) UpdateAvailableCurrencies(exchangeProducts []string, force bool) error {
|
|
exchangeProducts = common.SplitStrings(common.StringToUpper(common.JoinStrings(exchangeProducts, ",")), ",")
|
|
diff := common.StringSliceDifference(e.AvailablePairs, exchangeProducts)
|
|
if force || len(diff) > 0 {
|
|
cfg := config.GetConfig()
|
|
exch, err := cfg.GetExchangeConfig(e.Name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if force {
|
|
log.Printf("%s forced update of available pairs.", e.Name)
|
|
} else {
|
|
log.Printf("%s Updating available pairs. Difference: %s.\n", e.Name, diff)
|
|
}
|
|
exch.AvailablePairs = common.JoinStrings(exchangeProducts, ",")
|
|
e.AvailablePairs = exchangeProducts
|
|
return cfg.UpdateExchangeConfig(exch)
|
|
}
|
|
return nil
|
|
}
|