mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-18 15:10:03 +00:00
* Convert: Fix TimeFromUnixTimestampDecimal using local All parsed times should be in UTC * Subscriptions: Add IgnoringAssetsKey * Tests: Pass tb to curried WS handlers * Websocket: Make ErrNoMessageListener a public error * Kraken: Fix URLMap ignored for websocket URLs * Kraken: Move SeedAssets from Setup to Bootstrap Having SeedAssets in Setup is cruel and unusual because it calls the API. Most other interactive data seeding happens in Bootstrap. This made it so that fixing and creating unit tests for Kraken was painfully slow, particularly on flaky internet. * Kraken: Remove convert test Duplicate of convert_test.go TestTimeFromUnixTimestampDecimal * Kraken: Test config upgrades * Kraken: Sub Channel improvements * Use Websocket subscriptionChannels instead of local slice * Remove ChannelID - Deprecated in docs * Simplify ping handlers and hardcodes message * Add Depth as configurable orderbook channel param * Simplify auth/non-auth channel updates * Add configurable Book depth * Add configurable Candle timeframes Kraken: Simplify all WS handlers with reqId * Kraken: Subscription templating * Generate N+ subs for pairs If we generate one sub for all pairs, but then fan it out in the responses, we end up with a mis-match between the sub store and GenerateSubs, and when we do FlushChannels it will try to resub everything again. * Kraken: Rename channelName var throughout Avoid shadowing func of same name * Kraken: Add TestEnforceStandardChannelNames * Websocket: Fix Resubscribe erroring Duplicate
161 lines
4.4 KiB
Go
161 lines
4.4 KiB
Go
package currency
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"unicode"
|
|
)
|
|
|
|
var errCannotCreatePair = errors.New("cannot create currency pair")
|
|
|
|
// NewBTCUSDT is a shortcut for NewPair(BTC, USDT)
|
|
func NewBTCUSDT() Pair {
|
|
return NewPair(BTC, USDT)
|
|
}
|
|
|
|
// NewBTCUSD is a shortcut for NewPair(BTC, USD)
|
|
func NewBTCUSD() Pair {
|
|
return NewPair(BTC, USD)
|
|
}
|
|
|
|
// NewPairDelimiter splits the desired currency string at delimiter, then returns a Pair struct
|
|
func NewPairDelimiter(currencyPair, delimiter string) (Pair, error) {
|
|
if !strings.Contains(currencyPair, delimiter) {
|
|
return EMPTYPAIR,
|
|
fmt.Errorf("delimiter: [%s] not found in currencypair string", delimiter)
|
|
}
|
|
result := strings.Split(currencyPair, delimiter)
|
|
if len(result) < 2 {
|
|
return EMPTYPAIR,
|
|
fmt.Errorf("supplied pair: [%s] cannot be split with %s",
|
|
currencyPair,
|
|
delimiter)
|
|
}
|
|
if len(result) > 2 {
|
|
result[1] = strings.Join(result[1:], delimiter)
|
|
}
|
|
return Pair{
|
|
Delimiter: delimiter,
|
|
Base: NewCode(result[0]),
|
|
Quote: NewCode(result[1]),
|
|
}, nil
|
|
}
|
|
|
|
// NewPairFromStrings returns a CurrencyPair without a delimiter
|
|
func NewPairFromStrings(base, quote string) (Pair, error) {
|
|
if strings.Contains(base, " ") {
|
|
return EMPTYPAIR,
|
|
fmt.Errorf("cannot create pair, invalid base currency string [%s]",
|
|
base)
|
|
}
|
|
|
|
if strings.Contains(quote, " ") {
|
|
return EMPTYPAIR,
|
|
fmt.Errorf("cannot create pair, invalid quote currency string [%s]",
|
|
quote)
|
|
}
|
|
|
|
return Pair{Base: NewCode(base), Quote: NewCode(quote)}, nil
|
|
}
|
|
|
|
// NewPair returns a currency pair from currency codes
|
|
func NewPair(baseCurrency, quoteCurrency Code) Pair {
|
|
return Pair{
|
|
Base: baseCurrency,
|
|
Quote: quoteCurrency,
|
|
}
|
|
}
|
|
|
|
// NewPairWithDelimiter returns a CurrencyPair with a delimiter
|
|
func NewPairWithDelimiter(base, quote, delimiter string) Pair {
|
|
return Pair{
|
|
Base: NewCode(base),
|
|
Quote: NewCode(quote),
|
|
Delimiter: delimiter,
|
|
}
|
|
}
|
|
|
|
// NewPairFromString converts currency string into a new CurrencyPair
|
|
// with or without delimiter
|
|
func NewPairFromString(currencyPair string) (Pair, error) {
|
|
if len(currencyPair) < 3 {
|
|
return EMPTYPAIR,
|
|
fmt.Errorf("%w from %s string too short to be a currency pair",
|
|
errCannotCreatePair,
|
|
currencyPair)
|
|
}
|
|
|
|
for x := range currencyPair {
|
|
if unicode.IsPunct(rune(currencyPair[x])) {
|
|
return Pair{
|
|
Base: NewCode(currencyPair[:x]),
|
|
Delimiter: string(currencyPair[x]),
|
|
Quote: NewCode(currencyPair[x+1:]),
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
return NewPairFromStrings(currencyPair[0:3], currencyPair[3:])
|
|
}
|
|
|
|
// NewPairFromFormattedPairs matches a supplied currency pair to a list of pairs
|
|
// with a specific format. This is helpful for exchanges which
|
|
// provide currency pairs with no delimiter so we can match it with a list and
|
|
// apply the same format
|
|
func NewPairFromFormattedPairs(currencyPair string, pairs Pairs, pairFmt PairFormat) (Pair, error) {
|
|
for x := range pairs {
|
|
if strings.EqualFold(pairFmt.Format(pairs[x]), currencyPair) {
|
|
return pairs[x], nil
|
|
}
|
|
}
|
|
return NewPairFromString(currencyPair)
|
|
}
|
|
|
|
// Format formats the given pair as a string
|
|
func (f PairFormat) Format(pair Pair) string {
|
|
return pair.Format(f).String()
|
|
}
|
|
|
|
// clone returns a clone of the PairFormat
|
|
func (f *PairFormat) clone() *PairFormat {
|
|
if f == nil {
|
|
return nil
|
|
}
|
|
c := *f
|
|
return &c
|
|
}
|
|
|
|
// MatchPairsWithNoDelimiter will move along a predictable index on the provided currencyPair
|
|
// it will then split on that index and verify whether that currencypair exists in the
|
|
// supplied pairs
|
|
// this allows for us to match strange currencies with no delimiter where it is difficult to
|
|
// infer where the delimiter is located eg BETHERETH is BETHER ETH
|
|
func MatchPairsWithNoDelimiter(currencyPair string, pairs Pairs, pairFmt PairFormat) (Pair, error) {
|
|
for i := range pairs {
|
|
fPair := pairs[i].Format(pairFmt)
|
|
maxLen := 6
|
|
if len(currencyPair) < maxLen {
|
|
maxLen = len(currencyPair)
|
|
}
|
|
for j := 1; j <= maxLen; j++ {
|
|
if fPair.Base.String() == currencyPair[0:j] &&
|
|
fPair.Quote.String() == currencyPair[j:] {
|
|
return fPair, nil
|
|
}
|
|
}
|
|
}
|
|
return EMPTYPAIR, fmt.Errorf("currency %v not found in supplied pairs", currencyPair)
|
|
}
|
|
|
|
// GetFormatting returns the formatting style of a pair
|
|
func (p Pair) GetFormatting() (PairFormat, error) {
|
|
if p.Base.UpperCase != p.Quote.UpperCase {
|
|
return PairFormat{}, fmt.Errorf("%w casing mismatch", errPairFormattingInconsistent)
|
|
}
|
|
return PairFormat{
|
|
Uppercase: p.Base.UpperCase,
|
|
Delimiter: p.Delimiter,
|
|
}, nil
|
|
}
|