mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-05 07:26:47 +00:00
currency: Adds matching lookup table built from available pairs (#1312)
* currency: Add pair matching update (cherry-pick) * exchange/currency: Add tests and update func * linter fix, also if using json unmarshal functionality stop usage of string conversion without delimiter * gemini: fix test * currency/manager: potential optimisation * exchanges: purge derive from wrapper cases and add warning comment * glorious: nits * glorious: nits * linter: fix * glorious: nits * whoops * whoops * glorious: nits continued * glorious: diff THANKS! * hitbtc: fix update tradable pairs strings splitting. continue if not enabled tickers update pair. * glorious: nits * linter: fix * Update exchanges/exmo/exmo_wrapper.go Co-authored-by: Scott <gloriousCode@users.noreply.github.com> * bitstamp: fix test when 32 biterinos architecturinos * capture more strings for speed * swapsies because whos running 32bit \0/? --------- Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io> Co-authored-by: Scott <gloriousCode@users.noreply.github.com>
This commit is contained in:
@@ -300,6 +300,8 @@ func generateMethodArg(ctx context.Context, t *testing.T, argGenerator *MethodAr
|
|||||||
// Crypto Chain
|
// Crypto Chain
|
||||||
input = reflect.ValueOf(cryptoChainPerExchange[exchName])
|
input = reflect.ValueOf(cryptoChainPerExchange[exchName])
|
||||||
}
|
}
|
||||||
|
case "MatchSymbolWithAvailablePairs", "MatchSymbolCheckEnabled":
|
||||||
|
input = reflect.ValueOf(argGenerator.AssetParams.Pair.Base.Lower().String() + argGenerator.AssetParams.Pair.Quote.Lower().String())
|
||||||
default:
|
default:
|
||||||
// OrderID
|
// OrderID
|
||||||
input = reflect.ValueOf("1337")
|
input = reflect.ValueOf("1337")
|
||||||
@@ -602,6 +604,7 @@ var acceptableErrors = []error{
|
|||||||
order.ErrPairIsEmpty, // Is thrown when the empty pair and asset scenario for an order submission is sent in the Validate() function
|
order.ErrPairIsEmpty, // Is thrown when the empty pair and asset scenario for an order submission is sent in the Validate() function
|
||||||
deposit.ErrAddressNotFound, // Is thrown when an address is not found due to the exchange requiring valid API keys
|
deposit.ErrAddressNotFound, // Is thrown when an address is not found due to the exchange requiring valid API keys
|
||||||
futures.ErrNotFuturesAsset, // Is thrown when a futures function receives a non-futures asset
|
futures.ErrNotFuturesAsset, // Is thrown when a futures function receives a non-futures asset
|
||||||
|
currency.ErrSymbolStringEmpty, // Is thrown when a symbol string is empty for blank MatchSymbol func checks
|
||||||
}
|
}
|
||||||
|
|
||||||
// warningErrors will t.Log(err) when thrown to diagnose things, but not necessarily suggest
|
// warningErrors will t.Log(err) when thrown to diagnose things, but not necessarily suggest
|
||||||
|
|||||||
@@ -81,11 +81,3 @@ const (
|
|||||||
ForwardSlashDelimiter = "/"
|
ForwardSlashDelimiter = "/"
|
||||||
ColonDelimiter = ":"
|
ColonDelimiter = ":"
|
||||||
)
|
)
|
||||||
|
|
||||||
// delimiters is a delimiter list
|
|
||||||
var delimiters = []string{
|
|
||||||
DashDelimiter,
|
|
||||||
UnderscoreDelimiter,
|
|
||||||
ForwardSlashDelimiter,
|
|
||||||
ColonDelimiter,
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/common"
|
"github.com/thrasher-corp/gocryptotrader/common"
|
||||||
"github.com/thrasher-corp/gocryptotrader/common/convert"
|
"github.com/thrasher-corp/gocryptotrader/common/convert"
|
||||||
@@ -29,9 +30,12 @@ var (
|
|||||||
ErrPairManagerNotInitialised = errors.New("pair manager not initialised")
|
ErrPairManagerNotInitialised = errors.New("pair manager not initialised")
|
||||||
// ErrAssetNotFound is returned when an asset does not exist in the pairstore
|
// ErrAssetNotFound is returned when an asset does not exist in the pairstore
|
||||||
ErrAssetNotFound = errors.New("asset type not found in pair store")
|
ErrAssetNotFound = errors.New("asset type not found in pair store")
|
||||||
|
// ErrSymbolStringEmpty is an error when a symbol string is empty
|
||||||
|
ErrSymbolStringEmpty = errors.New("symbol string is empty")
|
||||||
|
|
||||||
errPairStoreIsNil = errors.New("pair store is nil")
|
errPairStoreIsNil = errors.New("pair store is nil")
|
||||||
errPairFormatIsNil = errors.New("pair format is nil")
|
errPairFormatIsNil = errors.New("pair format is nil")
|
||||||
|
errPairMatcherIsNil = errors.New("pair matcher is nil")
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetAssetTypes returns a list of stored asset types
|
// GetAssetTypes returns a list of stored asset types
|
||||||
@@ -64,6 +68,24 @@ func (p *PairsManager) Get(a asset.Item) (*PairStore, error) {
|
|||||||
return c.copy()
|
return c.copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Match returns a currency pair based on the supplied symbol and asset type
|
||||||
|
func (p *PairsManager) Match(symbol string, a asset.Item) (Pair, error) {
|
||||||
|
if symbol == "" {
|
||||||
|
return EMPTYPAIR, ErrSymbolStringEmpty
|
||||||
|
}
|
||||||
|
symbol = strings.ToLower(symbol)
|
||||||
|
p.mutex.RLock()
|
||||||
|
defer p.mutex.RUnlock()
|
||||||
|
if p.matcher == nil {
|
||||||
|
return EMPTYPAIR, errPairMatcherIsNil
|
||||||
|
}
|
||||||
|
pair, ok := p.matcher[key{symbol, a}]
|
||||||
|
if !ok {
|
||||||
|
return EMPTYPAIR, fmt.Errorf("%w for %v %v", ErrPairNotFound, symbol, a)
|
||||||
|
}
|
||||||
|
return *pair, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Store stores a new currency pair config based on its asset type
|
// Store stores a new currency pair config based on its asset type
|
||||||
func (p *PairsManager) Store(a asset.Item, ps *PairStore) error {
|
func (p *PairsManager) Store(a asset.Item, ps *PairStore) error {
|
||||||
if !a.IsValid() {
|
if !a.IsValid() {
|
||||||
@@ -78,6 +100,14 @@ func (p *PairsManager) Store(a asset.Item, ps *PairStore) error {
|
|||||||
p.Pairs = make(map[asset.Item]*PairStore)
|
p.Pairs = make(map[asset.Item]*PairStore)
|
||||||
}
|
}
|
||||||
p.Pairs[a] = cpy
|
p.Pairs[a] = cpy
|
||||||
|
if p.matcher == nil {
|
||||||
|
p.matcher = make(map[key]*Pair)
|
||||||
|
}
|
||||||
|
for x := range cpy.Available {
|
||||||
|
p.matcher[key{
|
||||||
|
Symbol: cpy.Available[x].Base.Lower().String() + cpy.Available[x].Quote.Lower().String(),
|
||||||
|
Asset: a}] = &cpy.Available[x]
|
||||||
|
}
|
||||||
p.mutex.Unlock()
|
p.mutex.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -85,6 +115,14 @@ func (p *PairsManager) Store(a asset.Item, ps *PairStore) error {
|
|||||||
// Delete deletes a map entry based on the supplied asset type
|
// Delete deletes a map entry based on the supplied asset type
|
||||||
func (p *PairsManager) Delete(a asset.Item) {
|
func (p *PairsManager) Delete(a asset.Item) {
|
||||||
p.mutex.Lock()
|
p.mutex.Lock()
|
||||||
|
vals, ok := p.Pairs[a]
|
||||||
|
if !ok {
|
||||||
|
p.mutex.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for x := range vals.Available {
|
||||||
|
delete(p.matcher, key{Symbol: vals.Available[x].Base.Lower().String() + vals.Available[x].Quote.Lower().String(), Asset: a})
|
||||||
|
}
|
||||||
delete(p.Pairs, a)
|
delete(p.Pairs, a)
|
||||||
p.mutex.Unlock()
|
p.mutex.Unlock()
|
||||||
}
|
}
|
||||||
@@ -187,6 +225,15 @@ func (p *PairsManager) StorePairs(a asset.Item, pairs Pairs, enabled bool) error
|
|||||||
pairStore.Enabled = cpy
|
pairStore.Enabled = cpy
|
||||||
} else {
|
} else {
|
||||||
pairStore.Available = cpy
|
pairStore.Available = cpy
|
||||||
|
|
||||||
|
if p.matcher == nil {
|
||||||
|
p.matcher = make(map[key]*Pair)
|
||||||
|
}
|
||||||
|
for x := range pairStore.Available {
|
||||||
|
p.matcher[key{
|
||||||
|
Symbol: pairStore.Available[x].Base.Lower().String() + pairStore.Available[x].Quote.Lower().String(),
|
||||||
|
Asset: a}] = &pairStore.Available[x]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -285,14 +332,14 @@ func (p *PairsManager) EnablePair(a asset.Item, pair Pair) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsAssetPairEnabled checks if a pair is enabled for an enabled asset type
|
// IsPairEnabled checks if a pair is enabled for an enabled asset type
|
||||||
func (p *PairsManager) IsAssetPairEnabled(a asset.Item, pair Pair) error {
|
func (p *PairsManager) IsPairEnabled(pair Pair, a asset.Item) (bool, error) {
|
||||||
if !a.IsValid() {
|
if !a.IsValid() {
|
||||||
return fmt.Errorf("%s %w", a, asset.ErrNotSupported)
|
return false, fmt.Errorf("%s %w", a, asset.ErrNotSupported)
|
||||||
}
|
}
|
||||||
|
|
||||||
if pair.IsEmpty() {
|
if pair.IsEmpty() {
|
||||||
return ErrCurrencyPairEmpty
|
return false, ErrCurrencyPairEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
p.mutex.RLock()
|
p.mutex.RLock()
|
||||||
@@ -300,20 +347,12 @@ func (p *PairsManager) IsAssetPairEnabled(a asset.Item, pair Pair) error {
|
|||||||
|
|
||||||
pairStore, err := p.getPairStoreRequiresLock(a)
|
pairStore, err := p.getPairStoreRequiresLock(a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if pairStore.AssetEnabled == nil {
|
if pairStore.AssetEnabled == nil {
|
||||||
return fmt.Errorf("%s %w", a, ErrAssetIsNil)
|
return false, fmt.Errorf("%s %w", a, ErrAssetIsNil)
|
||||||
}
|
}
|
||||||
if !*pairStore.AssetEnabled {
|
return *pairStore.AssetEnabled && pairStore.Enabled.Contains(pair, true), nil
|
||||||
return fmt.Errorf("%s %w", a, asset.ErrNotEnabled)
|
|
||||||
}
|
|
||||||
if !pairStore.Enabled.Contains(pair, true) {
|
|
||||||
return fmt.Errorf("%s %w", pair, ErrPairNotFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsAssetEnabled checks to see if an asset is enabled
|
// IsAssetEnabled checks to see if an asset is enabled
|
||||||
|
|||||||
@@ -92,6 +92,48 @@ func TestGet(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPairsManagerMatch(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
p := &PairsManager{}
|
||||||
|
|
||||||
|
_, err := p.Match("", 1337)
|
||||||
|
if !errors.Is(err, ErrSymbolStringEmpty) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", err, ErrSymbolStringEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = p.Match("sillyBilly", 1337)
|
||||||
|
if !errors.Is(err, errPairMatcherIsNil) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", err, errPairMatcherIsNil)
|
||||||
|
}
|
||||||
|
|
||||||
|
p = initTest(t)
|
||||||
|
|
||||||
|
_, err = p.Match("sillyBilly", 1337)
|
||||||
|
if !errors.Is(err, ErrPairNotFound) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", err, ErrPairNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = p.Match("sillyBilly", asset.Spot)
|
||||||
|
if !errors.Is(err, ErrPairNotFound) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", err, ErrPairNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
whatIgot, err := p.Match("bTCuSD", asset.Spot)
|
||||||
|
if !errors.Is(err, nil) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
whatIwant, err := NewPairFromString("btc-usd")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !whatIgot.Equal(whatIwant) {
|
||||||
|
t.Fatal("expected btc-usd")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStore(t *testing.T) {
|
func TestStore(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
availPairs, err := NewPairsFromStrings([]string{"BTC-USD", "LTC-USD"})
|
availPairs, err := NewPairsFromStrings([]string{"BTC-USD", "LTC-USD"})
|
||||||
@@ -531,43 +573,64 @@ func TestUnmarshalMarshal(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsAssetPairEnabled(t *testing.T) {
|
func TestIsPairEnabled(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
pm := initTest(t)
|
pm := initTest(t)
|
||||||
cp := NewPairWithDelimiter("BTC", "USD", "-")
|
cp := NewPairWithDelimiter("BTC", "USD", "-")
|
||||||
err := pm.IsAssetPairEnabled(asset.Spot, cp)
|
enabled, err := pm.IsPairEnabled(cp, asset.Spot)
|
||||||
if !errors.Is(err, nil) {
|
if !errors.Is(err, nil) {
|
||||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = pm.IsAssetPairEnabled(asset.Futures, cp)
|
if !enabled {
|
||||||
if !errors.Is(err, asset.ErrNotEnabled) {
|
t.Fatal("expected pair to be enabled")
|
||||||
t.Fatalf("received: '%v' but expected: '%v'", err, asset.ErrNotEnabled)
|
}
|
||||||
|
|
||||||
|
enabled, err = pm.IsPairEnabled(NewPair(SAFE, MOONRISE), asset.Spot)
|
||||||
|
if !errors.Is(err, nil) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if enabled {
|
||||||
|
t.Fatal("expected pair to be disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
enabled, err = pm.IsPairEnabled(cp, asset.Futures)
|
||||||
|
if !errors.Is(err, nil) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if enabled {
|
||||||
|
t.Fatal("expected pair to be disabled because asset type is not enabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
cp = NewPairWithDelimiter("XRP", "DOGE", "-")
|
cp = NewPairWithDelimiter("XRP", "DOGE", "-")
|
||||||
err = pm.IsAssetPairEnabled(asset.Spot, cp)
|
enabled, err = pm.IsPairEnabled(cp, asset.Spot)
|
||||||
if !errors.Is(err, ErrPairNotFound) {
|
if !errors.Is(err, nil) {
|
||||||
t.Fatalf("received: '%v' but expected: '%v'", err, ErrPairNotFound)
|
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = pm.IsAssetPairEnabled(asset.PerpetualSwap, cp)
|
if enabled {
|
||||||
|
t.Fatal("expected pair to be disabled because pair not found in enabled list")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = pm.IsPairEnabled(cp, asset.PerpetualSwap)
|
||||||
if !errors.Is(err, ErrAssetNotFound) {
|
if !errors.Is(err, ErrAssetNotFound) {
|
||||||
t.Fatalf("received: '%v' but expected: '%v'", err, ErrAssetNotFound)
|
t.Fatalf("received: '%v' but expected: '%v'", err, ErrAssetNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = pm.IsAssetPairEnabled(asset.Item(1337), cp)
|
_, err = pm.IsPairEnabled(cp, asset.Item(1337))
|
||||||
if !errors.Is(err, asset.ErrNotSupported) {
|
if !errors.Is(err, asset.ErrNotSupported) {
|
||||||
t.Fatalf("received: '%v' but expected: '%v'", err, asset.ErrNotSupported)
|
t.Fatalf("received: '%v' but expected: '%v'", err, asset.ErrNotSupported)
|
||||||
}
|
}
|
||||||
|
|
||||||
pm.Pairs[asset.PerpetualSwap] = &PairStore{}
|
pm.Pairs[asset.PerpetualSwap] = &PairStore{}
|
||||||
err = pm.IsAssetPairEnabled(asset.PerpetualSwap, cp)
|
_, err = pm.IsPairEnabled(cp, asset.PerpetualSwap)
|
||||||
if !errors.Is(err, ErrAssetIsNil) {
|
if !errors.Is(err, ErrAssetIsNil) {
|
||||||
t.Fatalf("received: '%v' but expected: '%v'", err, ErrAssetIsNil)
|
t.Fatalf("received: '%v' but expected: '%v'", err, ErrAssetIsNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = pm.IsAssetPairEnabled(asset.PerpetualSwap, EMPTYPAIR)
|
_, err = pm.IsPairEnabled(EMPTYPAIR, asset.PerpetualSwap)
|
||||||
if !errors.Is(err, ErrCurrencyPairEmpty) {
|
if !errors.Is(err, ErrCurrencyPairEmpty) {
|
||||||
t.Fatalf("received: '%v' but expected: '%v'", err, ErrCurrencyPairEmpty)
|
t.Fatalf("received: '%v' but expected: '%v'", err, ErrCurrencyPairEmpty)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,13 +8,14 @@ import (
|
|||||||
|
|
||||||
// PairsManager manages asset pairs
|
// PairsManager manages asset pairs
|
||||||
type PairsManager struct {
|
type PairsManager struct {
|
||||||
BypassConfigFormatUpgrades bool `json:"bypassConfigFormatUpgrades"`
|
BypassConfigFormatUpgrades bool `json:"bypassConfigFormatUpgrades"`
|
||||||
RequestFormat *PairFormat `json:"requestFormat,omitempty"`
|
RequestFormat *PairFormat `json:"requestFormat,omitempty"`
|
||||||
ConfigFormat *PairFormat `json:"configFormat,omitempty"`
|
ConfigFormat *PairFormat `json:"configFormat,omitempty"`
|
||||||
UseGlobalFormat bool `json:"useGlobalFormat,omitempty"`
|
UseGlobalFormat bool `json:"useGlobalFormat,omitempty"`
|
||||||
LastUpdated int64 `json:"lastUpdated,omitempty"`
|
LastUpdated int64 `json:"lastUpdated,omitempty"`
|
||||||
Pairs FullStore `json:"pairs"`
|
Pairs FullStore `json:"pairs"`
|
||||||
mutex sync.RWMutex `json:"-"`
|
matcher map[key]*Pair
|
||||||
|
mutex sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// FullStore holds all supported asset types with the enabled and available
|
// FullStore holds all supported asset types with the enabled and available
|
||||||
@@ -37,3 +38,9 @@ type PairFormat struct {
|
|||||||
Separator string `json:"separator,omitempty"`
|
Separator string `json:"separator,omitempty"`
|
||||||
Index string `json:"index,omitempty"`
|
Index string `json:"index,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// key is used to store the asset type and symbol in a map
|
||||||
|
type key struct {
|
||||||
|
Symbol string
|
||||||
|
Asset asset.Item
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errCannotCreatePair = errors.New("cannot create currency pair")
|
var errCannotCreatePair = errors.New("cannot create currency pair")
|
||||||
@@ -100,23 +101,17 @@ func NewPairFromString(currencyPair string) (Pair, error) {
|
|||||||
errCannotCreatePair,
|
errCannotCreatePair,
|
||||||
currencyPair)
|
currencyPair)
|
||||||
}
|
}
|
||||||
var delimiter string
|
|
||||||
pairStrings := []string{currencyPair}
|
for x := range currencyPair {
|
||||||
for x := range delimiters {
|
if unicode.IsPunct(rune(currencyPair[x])) {
|
||||||
if strings.Contains(pairStrings[0], delimiters[x]) {
|
return Pair{
|
||||||
values := strings.SplitN(pairStrings[0], delimiters[x], 2)
|
Base: NewCode(currencyPair[:x]),
|
||||||
if delimiter != "" {
|
Delimiter: string(currencyPair[x]),
|
||||||
values[1] += delimiter + pairStrings[1]
|
Quote: NewCode(currencyPair[x+1:]),
|
||||||
pairStrings = values
|
}, nil
|
||||||
} else {
|
|
||||||
pairStrings = values
|
|
||||||
}
|
|
||||||
delimiter = delimiters[x]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if delimiter != "" {
|
|
||||||
return Pair{Base: NewCode(pairStrings[0]), Delimiter: delimiter, Quote: NewCode(pairStrings[1])}, nil
|
|
||||||
}
|
|
||||||
return NewPairFromStrings(currencyPair[0:3], currencyPair[3:])
|
return NewPairFromStrings(currencyPair[0:3], currencyPair[3:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EMPTYFORMAT defines an empty pair format
|
// EMPTYFORMAT defines an empty pair format
|
||||||
@@ -45,8 +46,21 @@ func (p *Pair) UnmarshalJSON(d []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
*p, err = NewPairFromString(pair)
|
// Check if pair is in the format of BTC-USD
|
||||||
return err
|
for x := range pair {
|
||||||
|
if unicode.IsPunct(rune(pair[x])) {
|
||||||
|
p.Base = NewCode(pair[:x])
|
||||||
|
p.Delimiter = string(pair[x])
|
||||||
|
p.Quote = NewCode(pair[x+1:])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Pair string could be in format DUSKUSDT (Kucoin) which will be
|
||||||
|
// incorrectly converted to DUS-KUSDT, ELKRW (Bithumb) which will convert
|
||||||
|
// converted to ELK-RW and HTUSDT (Lbank) which will be incorrectly
|
||||||
|
// converted to HTU-SDT.
|
||||||
|
return fmt.Errorf("%w from %s cannot ensure pair is in correct format, please use exchange method MatchSymbolWithAvailablePairs", errCannotCreatePair, pair)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON conforms type to the marshaler interface
|
// MarshalJSON conforms type to the marshaler interface
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package currency
|
package currency
|
||||||
|
|
||||||
// Pair holds currency pair information
|
// Pair holds currency pair information
|
||||||
|
// NOTE: UnmarshalJSON allows string conversion to Pair type but only if there
|
||||||
|
// is a delimiter present in the string, otherwise it will return an error.
|
||||||
type Pair struct {
|
type Pair struct {
|
||||||
Delimiter string `json:"delimiter,omitempty"`
|
Delimiter string `json:"delimiter,omitempty"`
|
||||||
Base Code `json:"base,omitempty"`
|
Base Code `json:"base,omitempty"`
|
||||||
|
|||||||
@@ -303,7 +303,8 @@ func (p Pairs) GetRandomPair() (Pair, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeriveFrom matches symbol string to the available pairs list when no
|
// DeriveFrom matches symbol string to the available pairs list when no
|
||||||
// delimiter is supplied.
|
// delimiter is supplied. WARNING: This is not optimised and should only be used
|
||||||
|
// for one off processes.
|
||||||
func (p Pairs) DeriveFrom(symbol string) (Pair, error) {
|
func (p Pairs) DeriveFrom(symbol string) (Pair, error) {
|
||||||
if len(p) == 0 {
|
if len(p) == 0 {
|
||||||
return EMPTYPAIR, ErrCurrencyPairsEmpty
|
return EMPTYPAIR, ErrCurrencyPairsEmpty
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ var (
|
|||||||
errGRPCShutdownSignalIsNil = errors.New("cannot shutdown, gRPC shutdown channel is nil")
|
errGRPCShutdownSignalIsNil = errors.New("cannot shutdown, gRPC shutdown channel is nil")
|
||||||
errInvalidStrategy = errors.New("invalid strategy")
|
errInvalidStrategy = errors.New("invalid strategy")
|
||||||
errSpecificPairNotEnabled = errors.New("specified pair is not enabled")
|
errSpecificPairNotEnabled = errors.New("specified pair is not enabled")
|
||||||
|
errPairNotEnabled = errors.New("pair is not enabled")
|
||||||
)
|
)
|
||||||
|
|
||||||
// RPCServer struct
|
// RPCServer struct
|
||||||
@@ -4686,13 +4687,18 @@ func (s *RPCServer) GetFundingRates(ctx context.Context, r *gctrpc.GetFundingRat
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cp, err := exch.MatchSymbolWithAvailablePairs(r.Pair.Base+r.Pair.Quote, a, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
pairs, err := exch.GetEnabledPairs(a)
|
pairs, err := exch.GetEnabledPairs(a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
cp, err := pairs.DeriveFrom(r.Pair.Base + r.Pair.Quote)
|
|
||||||
if err != nil {
|
if !pairs.Contains(cp, true) {
|
||||||
return nil, err
|
return nil, fmt.Errorf("%w %v", errPairNotEnabled, cp)
|
||||||
}
|
}
|
||||||
|
|
||||||
funding, err := exch.GetFundingRates(ctx, &fundingrate.RatesRequest{
|
funding, err := exch.GetFundingRates(ctx, &fundingrate.RatesRequest{
|
||||||
@@ -4779,14 +4785,20 @@ func (s *RPCServer) GetLatestFundingRate(ctx context.Context, r *gctrpc.GetLates
|
|||||||
return nil, fmt.Errorf("%s %w", a, futures.ErrNotFuturesAsset)
|
return nil, fmt.Errorf("%s %w", a, futures.ErrNotFuturesAsset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cp, err := exch.MatchSymbolWithAvailablePairs(r.Pair.Base+r.Pair.Quote, a, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
pairs, err := exch.GetEnabledPairs(a)
|
pairs, err := exch.GetEnabledPairs(a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
cp, err := pairs.DeriveFrom(r.Pair.Base + r.Pair.Quote)
|
|
||||||
if err != nil {
|
if !pairs.Contains(cp, true) {
|
||||||
return nil, err
|
return nil, fmt.Errorf("%w %v", errPairNotEnabled, cp)
|
||||||
}
|
}
|
||||||
|
|
||||||
funding, err := exch.GetLatestFundingRate(ctx, &fundingrate.LatestRateRequest{
|
funding, err := exch.GetLatestFundingRate(ctx, &fundingrate.LatestRateRequest{
|
||||||
Asset: a,
|
Asset: a,
|
||||||
Pair: cp,
|
Pair: cp,
|
||||||
|
|||||||
@@ -2927,19 +2927,26 @@ func TestGetFundingRates(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
b.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore)
|
b.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore)
|
||||||
b.CurrencyPairs.Pairs[asset.Futures] = ¤cy.PairStore{
|
err = b.CurrencyPairs.Store(asset.Futures, ¤cy.PairStore{
|
||||||
AssetEnabled: convert.BoolPtr(true),
|
AssetEnabled: convert.BoolPtr(true),
|
||||||
RequestFormat: ¤cy.PairFormat{Delimiter: "-"},
|
RequestFormat: ¤cy.PairFormat{Delimiter: "-"},
|
||||||
ConfigFormat: ¤cy.PairFormat{Delimiter: "-"},
|
ConfigFormat: ¤cy.PairFormat{Delimiter: "-"},
|
||||||
Available: currency.Pairs{cp},
|
Available: currency.Pairs{cp},
|
||||||
Enabled: currency.Pairs{cp},
|
Enabled: currency.Pairs{cp},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
b.CurrencyPairs.Pairs[asset.Spot] = ¤cy.PairStore{
|
|
||||||
|
err = b.CurrencyPairs.Store(asset.Spot, ¤cy.PairStore{
|
||||||
AssetEnabled: convert.BoolPtr(true),
|
AssetEnabled: convert.BoolPtr(true),
|
||||||
ConfigFormat: ¤cy.PairFormat{Delimiter: "/"},
|
ConfigFormat: ¤cy.PairFormat{Delimiter: "/"},
|
||||||
RequestFormat: ¤cy.PairFormat{Delimiter: "/"},
|
RequestFormat: ¤cy.PairFormat{Delimiter: "/"},
|
||||||
Available: currency.Pairs{cp},
|
Available: currency.Pairs{cp},
|
||||||
Enabled: currency.Pairs{cp},
|
Enabled: currency.Pairs{cp},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
b.Features.Supports.FuturesCapabilities.FundingRates = true
|
b.Features.Supports.FuturesCapabilities.FundingRates = true
|
||||||
fakeExchange := fExchange{
|
fakeExchange := fExchange{
|
||||||
@@ -3028,19 +3035,25 @@ func TestGetLatestFundingRate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
b.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore)
|
b.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore)
|
||||||
b.CurrencyPairs.Pairs[asset.Futures] = ¤cy.PairStore{
|
err = b.CurrencyPairs.Store(asset.Futures, ¤cy.PairStore{
|
||||||
AssetEnabled: convert.BoolPtr(true),
|
AssetEnabled: convert.BoolPtr(true),
|
||||||
RequestFormat: ¤cy.PairFormat{Delimiter: "-"},
|
RequestFormat: ¤cy.PairFormat{Delimiter: "-"},
|
||||||
ConfigFormat: ¤cy.PairFormat{Delimiter: "-"},
|
ConfigFormat: ¤cy.PairFormat{Delimiter: "-"},
|
||||||
Available: currency.Pairs{cp},
|
Available: currency.Pairs{cp},
|
||||||
Enabled: currency.Pairs{cp},
|
Enabled: currency.Pairs{cp},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
b.CurrencyPairs.Pairs[asset.Spot] = ¤cy.PairStore{
|
err = b.CurrencyPairs.Store(asset.Spot, ¤cy.PairStore{
|
||||||
AssetEnabled: convert.BoolPtr(true),
|
AssetEnabled: convert.BoolPtr(true),
|
||||||
ConfigFormat: ¤cy.PairFormat{Delimiter: "/"},
|
ConfigFormat: ¤cy.PairFormat{Delimiter: "/"},
|
||||||
RequestFormat: ¤cy.PairFormat{Delimiter: "/"},
|
RequestFormat: ¤cy.PairFormat{Delimiter: "/"},
|
||||||
Available: currency.Pairs{cp},
|
Available: currency.Pairs{cp},
|
||||||
Enabled: currency.Pairs{cp},
|
Enabled: currency.Pairs{cp},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
fakeExchange := fExchange{
|
fakeExchange := fExchange{
|
||||||
IBotExchange: exch,
|
IBotExchange: exch,
|
||||||
|
|||||||
@@ -356,20 +356,20 @@ func (b *Bitfinex) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item)
|
|||||||
|
|
||||||
// UpdateTickers updates the ticker for all currency pairs of a given asset type
|
// UpdateTickers updates the ticker for all currency pairs of a given asset type
|
||||||
func (b *Bitfinex) UpdateTickers(ctx context.Context, a asset.Item) error {
|
func (b *Bitfinex) UpdateTickers(ctx context.Context, a asset.Item) error {
|
||||||
enabled, err := b.GetEnabledPairs(a)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tickerNew, err := b.GetTickerBatch(ctx)
|
tickerNew, err := b.GetTickerBatch(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, val := range tickerNew {
|
for key, val := range tickerNew {
|
||||||
pair, err := enabled.DeriveFrom(strings.Replace(key, ":", "", 1)[1:])
|
pair, enabled, err := b.MatchSymbolCheckEnabled(key[1:], a, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// GetTickerBatch returns all pairs in call across all asset types.
|
if !errors.Is(err, currency.ErrPairNotFound) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !enabled {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1098,6 +1098,7 @@ func TestGetHistoricTrades(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateTicker(t *testing.T) {
|
func TestUpdateTicker(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
cp := currency.NewPair(currency.ETH, currency.USD)
|
cp := currency.NewPair(currency.ETH, currency.USD)
|
||||||
_, err := b.UpdateTicker(context.Background(), cp, asset.PerpetualContract)
|
_, err := b.UpdateTicker(context.Background(), cp, asset.PerpetualContract)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -364,11 +364,7 @@ func (b *Bitmex) UpdateTickers(ctx context.Context, a asset.Item) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
enabled, err := b.GetEnabledPairs(a)
|
var enabled bool
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
instruments:
|
instruments:
|
||||||
for j := range tick {
|
for j := range tick {
|
||||||
var pair currency.Pair
|
var pair currency.Pair
|
||||||
@@ -377,7 +373,7 @@ instruments:
|
|||||||
if tick[j].Typ != futuresID {
|
if tick[j].Typ != futuresID {
|
||||||
continue instruments
|
continue instruments
|
||||||
}
|
}
|
||||||
pair, err = enabled.DeriveFrom(tick[j].Symbol)
|
pair, enabled, err = b.MatchSymbolCheckEnabled(tick[j].Symbol, a, false)
|
||||||
case asset.Index:
|
case asset.Index:
|
||||||
switch tick[j].Typ {
|
switch tick[j].Typ {
|
||||||
case bitMEXBasketIndexID,
|
case bitMEXBasketIndexID,
|
||||||
@@ -392,23 +388,27 @@ instruments:
|
|||||||
// contain an underscore. Calling DeriveFrom will then error and
|
// contain an underscore. Calling DeriveFrom will then error and
|
||||||
// the instruments will be missed.
|
// the instruments will be missed.
|
||||||
tick[j].Symbol = strings.Replace(tick[j].Symbol, currency.UnderscoreDelimiter, "", 1)
|
tick[j].Symbol = strings.Replace(tick[j].Symbol, currency.UnderscoreDelimiter, "", 1)
|
||||||
pair, err = enabled.DeriveFrom(tick[j].Symbol)
|
pair, enabled, err = b.MatchSymbolCheckEnabled(tick[j].Symbol, a, false)
|
||||||
case asset.PerpetualContract:
|
case asset.PerpetualContract:
|
||||||
if tick[j].Typ != perpetualContractID {
|
if tick[j].Typ != perpetualContractID {
|
||||||
continue instruments
|
continue instruments
|
||||||
}
|
}
|
||||||
pair, err = enabled.DeriveFrom(tick[j].Symbol)
|
pair, enabled, err = b.MatchSymbolCheckEnabled(tick[j].Symbol, a, false)
|
||||||
case asset.Spot:
|
case asset.Spot:
|
||||||
if tick[j].Typ != spotID {
|
if tick[j].Typ != spotID {
|
||||||
continue instruments
|
continue instruments
|
||||||
}
|
}
|
||||||
tick[j].Symbol = strings.Replace(tick[j].Symbol, currency.UnderscoreDelimiter, "", 1)
|
tick[j].Symbol = strings.Replace(tick[j].Symbol, currency.UnderscoreDelimiter, "", 1)
|
||||||
pair, err = enabled.DeriveFrom(tick[j].Symbol)
|
pair, enabled, err = b.MatchSymbolCheckEnabled(tick[j].Symbol, a, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, currency.ErrPairNotFound) {
|
if !errors.Is(err, currency.ErrPairNotFound) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !enabled {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,13 +44,22 @@ func (t *microTimestamp) UnmarshalJSON(data []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strconv.IntSize == 32 && len(s) >= 10 {
|
||||||
|
i, err := strconv.ParseInt(s, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*t = microTimestamp(time.UnixMicro(i))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has Fast path optimisation when int == 64
|
||||||
i, err := strconv.Atoi(s)
|
i, err := strconv.Atoi(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
*t = microTimestamp(time.UnixMicro(int64(i)))
|
*t = microTimestamp(time.UnixMicro(int64(i)))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2042,7 +2042,7 @@ func TestUpdateTicker(t *testing.T) {
|
|||||||
var pairs currency.Pairs
|
var pairs currency.Pairs
|
||||||
if mockTests {
|
if mockTests {
|
||||||
var pair2 currency.Pair
|
var pair2 currency.Pair
|
||||||
pair2, err = currency.NewPairFromString("BTCUSD-U23")
|
pair2, err = currency.NewPairFromString("BTCUSD-Z23")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -2230,7 +2230,7 @@ func TestGetHistoricCandles(t *testing.T) {
|
|||||||
}
|
}
|
||||||
var pair2 currency.Pair
|
var pair2 currency.Pair
|
||||||
if mockTests {
|
if mockTests {
|
||||||
pair2, err = currency.NewPairFromString("BTCUSD-U23")
|
pair2, err = currency.NewPairFromString("BTCUSD-Z23")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -2292,7 +2292,7 @@ func TestGetHistoricCandlesExtended(t *testing.T) {
|
|||||||
}
|
}
|
||||||
var pair2 currency.Pair
|
var pair2 currency.Pair
|
||||||
if mockTests {
|
if mockTests {
|
||||||
pair2, err = currency.NewPairFromString("BTCUSD-U23")
|
pair2, err = currency.NewPairFromString("BTCUSD-Z23")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -266,11 +266,15 @@ func (by *Bybit) wsHandleData(respRaw []byte) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p, err := by.extractCurrencyPair(data.OBData.Symbol, asset.Spot)
|
p, enabled, err := by.MatchSymbolCheckEnabled(data.OBData.Symbol, asset.Spot, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
err = by.wsUpdateOrderbook(&data.OBData, p, asset.Spot)
|
err = by.wsUpdateOrderbook(&data.OBData, p, asset.Spot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -286,11 +290,15 @@ func (by *Bybit) wsHandleData(respRaw []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := by.extractCurrencyPair(data.Parameters.Symbol, asset.Spot)
|
p, enabled, err := by.MatchSymbolCheckEnabled(data.Parameters.Symbol, asset.Spot, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
side := order.Sell
|
side := order.Sell
|
||||||
if data.TradeData.Side {
|
if data.TradeData.Side {
|
||||||
side = order.Buy
|
side = order.Buy
|
||||||
@@ -313,11 +321,15 @@ func (by *Bybit) wsHandleData(respRaw []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := by.extractCurrencyPair(data.Ticker.Symbol, asset.Spot)
|
p, enabled, err := by.MatchSymbolCheckEnabled(data.Ticker.Symbol, asset.Spot, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
by.Websocket.DataHandler <- &ticker.Price{
|
by.Websocket.DataHandler <- &ticker.Price{
|
||||||
ExchangeName: by.Name,
|
ExchangeName: by.Name,
|
||||||
Bid: data.Ticker.Bid.Float64(),
|
Bid: data.Ticker.Bid.Float64(),
|
||||||
@@ -334,11 +346,15 @@ func (by *Bybit) wsHandleData(respRaw []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := by.extractCurrencyPair(data.Kline.Symbol, asset.Spot)
|
p, enabled, err := by.MatchSymbolCheckEnabled(data.Kline.Symbol, asset.Spot, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
by.Websocket.DataHandler <- stream.KlineData{
|
by.Websocket.DataHandler <- stream.KlineData{
|
||||||
Pair: p,
|
Pair: p,
|
||||||
AssetType: asset.Spot,
|
AssetType: asset.Spot,
|
||||||
@@ -425,11 +441,15 @@ func (by *Bybit) wsHandleData(respRaw []byte) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := by.extractCurrencyPair(data[j].Symbol, asset.Spot)
|
p, enabled, err := by.MatchSymbolCheckEnabled(data[j].Symbol, asset.Spot, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !enabled {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
by.Websocket.DataHandler <- order.Detail{
|
by.Websocket.DataHandler <- order.Detail{
|
||||||
Price: data[j].Price.Float64(),
|
Price: data[j].Price.Float64(),
|
||||||
Amount: data[j].Quantity.Float64(),
|
Amount: data[j].Quantity.Float64(),
|
||||||
@@ -475,11 +495,15 @@ func (by *Bybit) wsHandleData(respRaw []byte) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := by.extractCurrencyPair(data[j].Symbol, asset.Spot)
|
p, enabled, err := by.MatchSymbolCheckEnabled(data[j].Symbol, asset.Spot, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !enabled {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
by.Websocket.DataHandler <- &order.Detail{
|
by.Websocket.DataHandler <- &order.Detail{
|
||||||
Exchange: by.Name,
|
Exchange: by.Name,
|
||||||
OrderID: data[j].OrderID,
|
OrderID: data[j].OrderID,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package bybit
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -416,16 +417,6 @@ func (by *Bybit) UpdateTradablePairs(ctx context.Context, forceUpdate bool) erro
|
|||||||
|
|
||||||
// UpdateTickers updates the ticker for all currency pairs of a given asset type
|
// UpdateTickers updates the ticker for all currency pairs of a given asset type
|
||||||
func (by *Bybit) UpdateTickers(ctx context.Context, assetType asset.Item) error {
|
func (by *Bybit) UpdateTickers(ctx context.Context, assetType asset.Item) error {
|
||||||
avail, err := by.GetAvailablePairs(assetType)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
enabled, err := by.GetEnabledPairs(assetType)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch assetType {
|
switch assetType {
|
||||||
case asset.Spot:
|
case asset.Spot:
|
||||||
ticks, err := by.GetTickersV5(ctx, "spot", "", "")
|
ticks, err := by.GetTickersV5(ctx, "spot", "", "")
|
||||||
@@ -434,17 +425,19 @@ func (by *Bybit) UpdateTickers(ctx context.Context, assetType asset.Item) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
for x := range ticks.List {
|
for x := range ticks.List {
|
||||||
pair, err := avail.DeriveFrom(ticks.List[x].Symbol)
|
pair, enabled, err := by.MatchSymbolCheckEnabled(ticks.List[x].Symbol, assetType, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// These symbols below do not have a spot market but are in fact
|
// These symbols below do not have a spot market but are in fact
|
||||||
// perpetuals.
|
// perpetuals.
|
||||||
if ticks.List[x].Symbol == "ZECUSDT" || ticks.List[x].Symbol == "DASHUSDT" {
|
if ticks.List[x].Symbol == "ZECUSDT" || ticks.List[x].Symbol == "DASHUSDT" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return err
|
if !errors.Is(err, currency.ErrPairNotFound) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !enabled.Contains(pair, true) {
|
if !enabled {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,6 +458,11 @@ func (by *Bybit) UpdateTickers(ctx context.Context, assetType asset.Item) error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case asset.CoinMarginedFutures, asset.USDTMarginedFutures, asset.Futures:
|
case asset.CoinMarginedFutures, asset.USDTMarginedFutures, asset.Futures:
|
||||||
|
enabled, err := by.GetEnabledPairs(assetType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
tick, err := by.GetFuturesSymbolPriceTicker(ctx, currency.EMPTYPAIR)
|
tick, err := by.GetFuturesSymbolPriceTicker(ctx, currency.EMPTYPAIR)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -480,7 +478,8 @@ func (by *Bybit) UpdateTickers(ctx context.Context, assetType asset.Item) error
|
|||||||
if tick[y].Symbol != formattedPair.String() {
|
if tick[y].Symbol != formattedPair.String() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
cp, err := by.extractCurrencyPair(tick[y].Symbol, assetType)
|
// Don't need to check if this pair is enabled due to call above.
|
||||||
|
cp, err := by.MatchSymbolWithAvailablePairs(tick[y].Symbol, assetType, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -501,6 +500,11 @@ func (by *Bybit) UpdateTickers(ctx context.Context, assetType asset.Item) error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case asset.USDCMarginedFutures:
|
case asset.USDCMarginedFutures:
|
||||||
|
enabled, err := by.GetEnabledPairs(assetType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
for x := range enabled {
|
for x := range enabled {
|
||||||
formattedPair, err := by.FormatExchangeCurrency(enabled[x], assetType)
|
formattedPair, err := by.FormatExchangeCurrency(enabled[x], assetType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -512,7 +516,8 @@ func (by *Bybit) UpdateTickers(ctx context.Context, assetType asset.Item) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cp, err := by.extractCurrencyPair(tick.Symbol, assetType)
|
// Don't need to check if this pair is enabled due to call above.
|
||||||
|
cp, err := by.MatchSymbolWithAvailablePairs(tick.Symbol, assetType, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -551,10 +556,13 @@ func (by *Bybit) UpdateTicker(ctx context.Context, p currency.Pair, assetType as
|
|||||||
}
|
}
|
||||||
|
|
||||||
for y := range tick {
|
for y := range tick {
|
||||||
cp, err := by.extractCurrencyPair(tick[y].Symbol, assetType)
|
cp, enabled, err := by.MatchSymbolCheckEnabled(tick[y].Symbol, assetType, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if !enabled {
|
||||||
|
continue
|
||||||
|
}
|
||||||
err = ticker.ProcessTicker(&ticker.Price{
|
err = ticker.ProcessTicker(&ticker.Price{
|
||||||
Last: tick[y].LastPrice.Float64(),
|
Last: tick[y].LastPrice.Float64(),
|
||||||
High: tick[y].HighPrice.Float64(),
|
High: tick[y].HighPrice.Float64(),
|
||||||
@@ -572,7 +580,6 @@ func (by *Bybit) UpdateTicker(ctx context.Context, p currency.Pair, assetType as
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case asset.CoinMarginedFutures, asset.USDTMarginedFutures, asset.Futures:
|
case asset.CoinMarginedFutures, asset.USDTMarginedFutures, asset.Futures:
|
||||||
tick, err := by.GetFuturesSymbolPriceTicker(ctx, formattedPair)
|
tick, err := by.GetFuturesSymbolPriceTicker(ctx, formattedPair)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -580,10 +587,13 @@ func (by *Bybit) UpdateTicker(ctx context.Context, p currency.Pair, assetType as
|
|||||||
}
|
}
|
||||||
|
|
||||||
for y := range tick {
|
for y := range tick {
|
||||||
cp, err := by.extractCurrencyPair(tick[y].Symbol, assetType)
|
cp, enabled, err := by.MatchSymbolCheckEnabled(tick[y].Symbol, assetType, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if !enabled {
|
||||||
|
continue
|
||||||
|
}
|
||||||
err = ticker.ProcessTicker(&ticker.Price{
|
err = ticker.ProcessTicker(&ticker.Price{
|
||||||
Last: tick[y].LastPrice.Float64(),
|
Last: tick[y].LastPrice.Float64(),
|
||||||
High: tick[y].HighPrice24h.Float64(),
|
High: tick[y].HighPrice24h.Float64(),
|
||||||
@@ -606,10 +616,13 @@ func (by *Bybit) UpdateTicker(ctx context.Context, p currency.Pair, assetType as
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cp, err := by.extractCurrencyPair(tick.Symbol, assetType)
|
cp, enabled, err := by.MatchSymbolCheckEnabled(tick.Symbol, assetType, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if !enabled {
|
||||||
|
return nil, fmt.Errorf("%v %v not enabled", formattedPair, assetType)
|
||||||
|
}
|
||||||
err = ticker.ProcessTicker(&ticker.Price{
|
err = ticker.ProcessTicker(&ticker.Price{
|
||||||
Last: tick.LastPrice.Float64(),
|
Last: tick.LastPrice.Float64(),
|
||||||
High: tick.High24h.Float64(),
|
High: tick.High24h.Float64(),
|
||||||
@@ -2097,30 +2110,12 @@ func (by *Bybit) GetServerTime(ctx context.Context, a asset.Item) (time.Time, er
|
|||||||
return time.Time{}, fmt.Errorf("%s %w", a, asset.ErrNotSupported)
|
return time.Time{}, fmt.Errorf("%s %w", a, asset.ErrNotSupported)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (by *Bybit) extractCurrencyPair(symbol string, item asset.Item) (currency.Pair, error) {
|
|
||||||
pairs, err := by.CurrencyPairs.GetPairs(item, true)
|
|
||||||
if err != nil {
|
|
||||||
return currency.EMPTYPAIR, err
|
|
||||||
}
|
|
||||||
pair, err := pairs.DeriveFrom(symbol)
|
|
||||||
if err != nil {
|
|
||||||
return currency.EMPTYPAIR, err
|
|
||||||
}
|
|
||||||
return pair, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateOrderExecutionLimits sets exchange executions for a required asset type
|
// UpdateOrderExecutionLimits sets exchange executions for a required asset type
|
||||||
func (by *Bybit) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) error {
|
func (by *Bybit) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) error {
|
||||||
avail, err := by.GetAvailablePairs(a)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var limits []order.MinMaxLevel
|
var limits []order.MinMaxLevel
|
||||||
switch a {
|
switch a {
|
||||||
case asset.Spot:
|
case asset.Spot:
|
||||||
var pairsData []PairData
|
pairsData, err := by.GetAllSpotPairs(ctx)
|
||||||
pairsData, err = by.GetAllSpotPairs(ctx)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -2128,11 +2123,15 @@ func (by *Bybit) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) e
|
|||||||
limits = make([]order.MinMaxLevel, 0, len(pairsData))
|
limits = make([]order.MinMaxLevel, 0, len(pairsData))
|
||||||
for x := range pairsData {
|
for x := range pairsData {
|
||||||
var pair currency.Pair
|
var pair currency.Pair
|
||||||
pair, err = avail.DeriveFrom(pairsData[x].Name)
|
var enabled bool
|
||||||
|
pair, enabled, err = by.MatchSymbolCheckEnabled(pairsData[x].Name, a, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(log.ExchangeSys, "%s unable to load limits for %v, pair data missing", by.Name, pairsData[x].Name)
|
log.Warnf(log.ExchangeSys, "%s unable to load limits for %v, pair data missing", by.Name, pairsData[x].Name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if !enabled {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
limits = append(limits, order.MinMaxLevel{
|
limits = append(limits, order.MinMaxLevel{
|
||||||
Asset: a,
|
Asset: a,
|
||||||
|
|||||||
@@ -215,6 +215,7 @@ func (by *Bybit) wsCoinHandleData(respRaw []byte) error {
|
|||||||
if wsType, ok := multiStreamData["type"].(string); ok {
|
if wsType, ok := multiStreamData["type"].(string); ok {
|
||||||
switch topics[0] {
|
switch topics[0] {
|
||||||
case wsOrder25, wsOrder200:
|
case wsOrder25, wsOrder200:
|
||||||
|
var enabled bool
|
||||||
switch wsType {
|
switch wsType {
|
||||||
case wsOperationSnapshot:
|
case wsOperationSnapshot:
|
||||||
var response WsFuturesOrderbook
|
var response WsFuturesOrderbook
|
||||||
@@ -224,11 +225,15 @@ func (by *Bybit) wsCoinHandleData(respRaw []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.OBData[0].Symbol, asset.CoinMarginedFutures)
|
p, enabled, err = by.MatchSymbolCheckEnabled(response.OBData[0].Symbol, asset.CoinMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
err = by.processOrderbook(response.OBData,
|
err = by.processOrderbook(response.OBData,
|
||||||
response.Type,
|
response.Type,
|
||||||
p,
|
p,
|
||||||
@@ -236,7 +241,6 @@ func (by *Bybit) wsCoinHandleData(respRaw []byte) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case wsOperationDelta:
|
case wsOperationDelta:
|
||||||
var response WsCoinDeltaOrderbook
|
var response WsCoinDeltaOrderbook
|
||||||
err = json.Unmarshal(respRaw, &response)
|
err = json.Unmarshal(respRaw, &response)
|
||||||
@@ -246,11 +250,15 @@ func (by *Bybit) wsCoinHandleData(respRaw []byte) error {
|
|||||||
|
|
||||||
if len(response.OBData.Delete) > 0 {
|
if len(response.OBData.Delete) > 0 {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.OBData.Delete[0].Symbol, asset.CoinMarginedFutures)
|
p, enabled, err = by.MatchSymbolCheckEnabled(response.OBData.Delete[0].Symbol, asset.CoinMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
err = by.processOrderbook(response.OBData.Delete,
|
err = by.processOrderbook(response.OBData.Delete,
|
||||||
wsOrderbookActionDelete,
|
wsOrderbookActionDelete,
|
||||||
p,
|
p,
|
||||||
@@ -262,11 +270,15 @@ func (by *Bybit) wsCoinHandleData(respRaw []byte) error {
|
|||||||
|
|
||||||
if len(response.OBData.Update) > 0 {
|
if len(response.OBData.Update) > 0 {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.OBData.Update[0].Symbol, asset.CoinMarginedFutures)
|
p, enabled, err = by.MatchSymbolCheckEnabled(response.OBData.Update[0].Symbol, asset.CoinMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
err = by.processOrderbook(response.OBData.Update,
|
err = by.processOrderbook(response.OBData.Update,
|
||||||
wsOrderbookActionUpdate,
|
wsOrderbookActionUpdate,
|
||||||
p,
|
p,
|
||||||
@@ -278,11 +290,15 @@ func (by *Bybit) wsCoinHandleData(respRaw []byte) error {
|
|||||||
|
|
||||||
if len(response.OBData.Insert) > 0 {
|
if len(response.OBData.Insert) > 0 {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.OBData.Insert[0].Symbol, asset.CoinMarginedFutures)
|
p, enabled, err = by.MatchSymbolCheckEnabled(response.OBData.Insert[0].Symbol, asset.CoinMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
err = by.processOrderbook(response.OBData.Insert,
|
err = by.processOrderbook(response.OBData.Insert,
|
||||||
wsOrderbookActionInsert,
|
wsOrderbookActionInsert,
|
||||||
p,
|
p,
|
||||||
@@ -308,7 +324,7 @@ func (by *Bybit) wsCoinHandleData(respRaw []byte) error {
|
|||||||
trades := make([]trade.Data, len(response.TradeData))
|
trades := make([]trade.Data, len(response.TradeData))
|
||||||
for i := range response.TradeData {
|
for i := range response.TradeData {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.TradeData[0].Symbol, asset.CoinMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.TradeData[0].Symbol, asset.CoinMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -344,7 +360,7 @@ func (by *Bybit) wsCoinHandleData(respRaw []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(topics[len(topics)-1], asset.CoinMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(topics[len(topics)-1], asset.CoinMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -382,7 +398,7 @@ func (by *Bybit) wsCoinHandleData(respRaw []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Ticker.Symbol, asset.CoinMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Ticker.Symbol, asset.CoinMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -411,7 +427,7 @@ func (by *Bybit) wsCoinHandleData(respRaw []byte) error {
|
|||||||
if len(response.Data.Delete) > 0 {
|
if len(response.Data.Delete) > 0 {
|
||||||
for x := range response.Data.Delete {
|
for x := range response.Data.Delete {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Data.Delete[x].Symbol, asset.CoinMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Data.Delete[x].Symbol, asset.CoinMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -435,7 +451,7 @@ func (by *Bybit) wsCoinHandleData(respRaw []byte) error {
|
|||||||
if len(response.Data.Update) > 0 {
|
if len(response.Data.Update) > 0 {
|
||||||
for x := range response.Data.Update {
|
for x := range response.Data.Update {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Data.Update[x].Symbol, asset.CoinMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Data.Update[x].Symbol, asset.CoinMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -459,7 +475,7 @@ func (by *Bybit) wsCoinHandleData(respRaw []byte) error {
|
|||||||
if len(response.Data.Insert) > 0 {
|
if len(response.Data.Insert) > 0 {
|
||||||
for x := range response.Data.Insert {
|
for x := range response.Data.Insert {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Data.Insert[x].Symbol, asset.CoinMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Data.Insert[x].Symbol, asset.CoinMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -510,7 +526,7 @@ func (by *Bybit) wsCoinHandleData(respRaw []byte) error {
|
|||||||
|
|
||||||
for i := range response.Data {
|
for i := range response.Data {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Data[i].Symbol, asset.CoinMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Data[i].Symbol, asset.CoinMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -566,7 +582,7 @@ func (by *Bybit) wsCoinHandleData(respRaw []byte) error {
|
|||||||
}
|
}
|
||||||
for x := range response.Data {
|
for x := range response.Data {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Data[x].Symbol, asset.CoinMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Data[x].Symbol, asset.CoinMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -628,7 +644,7 @@ func (by *Bybit) wsCoinHandleData(respRaw []byte) error {
|
|||||||
}
|
}
|
||||||
for x := range response.Data {
|
for x := range response.Data {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Data[x].Symbol, asset.CoinMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Data[x].Symbol, asset.CoinMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ func (by *Bybit) wsFuturesHandleData(respRaw []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.OBData[0].Symbol, asset.Futures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.OBData[0].Symbol, asset.Futures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -198,7 +198,7 @@ func (by *Bybit) wsFuturesHandleData(respRaw []byte) error {
|
|||||||
|
|
||||||
if len(response.OBData.Delete) > 0 {
|
if len(response.OBData.Delete) > 0 {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.OBData.Delete[0].Symbol, asset.Futures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.OBData.Delete[0].Symbol, asset.Futures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -213,7 +213,7 @@ func (by *Bybit) wsFuturesHandleData(respRaw []byte) error {
|
|||||||
|
|
||||||
if len(response.OBData.Update) > 0 {
|
if len(response.OBData.Update) > 0 {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.OBData.Update[0].Symbol, asset.Futures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.OBData.Update[0].Symbol, asset.Futures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -229,7 +229,7 @@ func (by *Bybit) wsFuturesHandleData(respRaw []byte) error {
|
|||||||
|
|
||||||
if len(response.OBData.Insert) > 0 {
|
if len(response.OBData.Insert) > 0 {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.OBData.Insert[0].Symbol, asset.Futures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.OBData.Insert[0].Symbol, asset.Futures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -261,7 +261,7 @@ func (by *Bybit) wsFuturesHandleData(respRaw []byte) error {
|
|||||||
trades := make([]trade.Data, len(response.TradeData))
|
trades := make([]trade.Data, len(response.TradeData))
|
||||||
for i := range response.TradeData {
|
for i := range response.TradeData {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.TradeData[0].Symbol, asset.Futures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.TradeData[0].Symbol, asset.Futures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -297,7 +297,7 @@ func (by *Bybit) wsFuturesHandleData(respRaw []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(topics[len(topics)-1], asset.Futures)
|
p, err = by.MatchSymbolWithAvailablePairs(topics[len(topics)-1], asset.Futures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -327,7 +327,7 @@ func (by *Bybit) wsFuturesHandleData(respRaw []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Ticker.Symbol, asset.Futures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Ticker.Symbol, asset.Futures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -356,7 +356,7 @@ func (by *Bybit) wsFuturesHandleData(respRaw []byte) error {
|
|||||||
if len(response.Data.Delete) > 0 {
|
if len(response.Data.Delete) > 0 {
|
||||||
for x := range response.Data.Delete {
|
for x := range response.Data.Delete {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Data.Delete[x].Symbol, asset.Futures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Data.Delete[x].Symbol, asset.Futures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -380,7 +380,7 @@ func (by *Bybit) wsFuturesHandleData(respRaw []byte) error {
|
|||||||
if len(response.Data.Update) > 0 {
|
if len(response.Data.Update) > 0 {
|
||||||
for x := range response.Data.Update {
|
for x := range response.Data.Update {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Data.Update[x].Symbol, asset.Futures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Data.Update[x].Symbol, asset.Futures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -404,7 +404,7 @@ func (by *Bybit) wsFuturesHandleData(respRaw []byte) error {
|
|||||||
if len(response.Data.Insert) > 0 {
|
if len(response.Data.Insert) > 0 {
|
||||||
for x := range response.Data.Insert {
|
for x := range response.Data.Insert {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Data.Insert[x].Symbol, asset.Futures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Data.Insert[x].Symbol, asset.Futures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -455,7 +455,7 @@ func (by *Bybit) wsFuturesHandleData(respRaw []byte) error {
|
|||||||
|
|
||||||
for i := range response.Data {
|
for i := range response.Data {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Data[i].Symbol, asset.Futures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Data[i].Symbol, asset.Futures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -509,7 +509,7 @@ func (by *Bybit) wsFuturesHandleData(respRaw []byte) error {
|
|||||||
}
|
}
|
||||||
for x := range response.Data {
|
for x := range response.Data {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Data[x].Symbol, asset.Futures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Data[x].Symbol, asset.Futures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -571,7 +571,7 @@ func (by *Bybit) wsFuturesHandleData(respRaw []byte) error {
|
|||||||
}
|
}
|
||||||
for x := range response.Data {
|
for x := range response.Data {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Data[x].Symbol, asset.Futures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Data[x].Symbol, asset.Futures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ func (by *Bybit) wsUSDTHandleData(respRaw []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Data.OBData[0].Symbol, asset.USDTMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Data.OBData[0].Symbol, asset.USDTMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -206,7 +206,7 @@ func (by *Bybit) wsUSDTHandleData(respRaw []byte) error {
|
|||||||
|
|
||||||
if len(response.OBData.Delete) > 0 {
|
if len(response.OBData.Delete) > 0 {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.OBData.Delete[0].Symbol, asset.USDTMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.OBData.Delete[0].Symbol, asset.USDTMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -222,7 +222,7 @@ func (by *Bybit) wsUSDTHandleData(respRaw []byte) error {
|
|||||||
|
|
||||||
if len(response.OBData.Update) > 0 {
|
if len(response.OBData.Update) > 0 {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.OBData.Update[0].Symbol, asset.USDTMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.OBData.Update[0].Symbol, asset.USDTMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -238,7 +238,7 @@ func (by *Bybit) wsUSDTHandleData(respRaw []byte) error {
|
|||||||
|
|
||||||
if len(response.OBData.Insert) > 0 {
|
if len(response.OBData.Insert) > 0 {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.OBData.Insert[0].Symbol, asset.USDTMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.OBData.Insert[0].Symbol, asset.USDTMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -268,7 +268,7 @@ func (by *Bybit) wsUSDTHandleData(respRaw []byte) error {
|
|||||||
trades := make([]trade.Data, len(response.TradeData))
|
trades := make([]trade.Data, len(response.TradeData))
|
||||||
for i := range response.TradeData {
|
for i := range response.TradeData {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.TradeData[0].Symbol, asset.USDTMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.TradeData[0].Symbol, asset.USDTMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -303,7 +303,7 @@ func (by *Bybit) wsUSDTHandleData(respRaw []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(topics[len(topics)-1], asset.USDTMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(topics[len(topics)-1], asset.USDTMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -333,7 +333,7 @@ func (by *Bybit) wsUSDTHandleData(respRaw []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Ticker.Symbol, asset.USDTMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Ticker.Symbol, asset.USDTMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -362,7 +362,7 @@ func (by *Bybit) wsUSDTHandleData(respRaw []byte) error {
|
|||||||
if len(response.Data.Delete) > 0 {
|
if len(response.Data.Delete) > 0 {
|
||||||
for x := range response.Data.Delete {
|
for x := range response.Data.Delete {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Data.Delete[x].Symbol, asset.USDTMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Data.Delete[x].Symbol, asset.USDTMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -386,7 +386,7 @@ func (by *Bybit) wsUSDTHandleData(respRaw []byte) error {
|
|||||||
if len(response.Data.Update) > 0 {
|
if len(response.Data.Update) > 0 {
|
||||||
for x := range response.Data.Update {
|
for x := range response.Data.Update {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Data.Update[x].Symbol, asset.USDTMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Data.Update[x].Symbol, asset.USDTMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -410,7 +410,7 @@ func (by *Bybit) wsUSDTHandleData(respRaw []byte) error {
|
|||||||
if len(response.Data.Insert) > 0 {
|
if len(response.Data.Insert) > 0 {
|
||||||
for x := range response.Data.Insert {
|
for x := range response.Data.Insert {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Data.Insert[x].Symbol, asset.USDTMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Data.Insert[x].Symbol, asset.USDTMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -461,7 +461,7 @@ func (by *Bybit) wsUSDTHandleData(respRaw []byte) error {
|
|||||||
|
|
||||||
for i := range response.Data {
|
for i := range response.Data {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Data[i].Symbol, asset.USDTMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Data[i].Symbol, asset.USDTMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -515,7 +515,7 @@ func (by *Bybit) wsUSDTHandleData(respRaw []byte) error {
|
|||||||
}
|
}
|
||||||
for x := range response.Data {
|
for x := range response.Data {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Data[x].Symbol, asset.USDTMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Data[x].Symbol, asset.USDTMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -577,7 +577,7 @@ func (by *Bybit) wsUSDTHandleData(respRaw []byte) error {
|
|||||||
}
|
}
|
||||||
for x := range response.Data {
|
for x := range response.Data {
|
||||||
var p currency.Pair
|
var p currency.Pair
|
||||||
p, err = by.extractCurrencyPair(response.Data[x].Symbol, asset.USDTMarginedFutures)
|
p, err = by.MatchSymbolWithAvailablePairs(response.Data[x].Symbol, asset.USDTMarginedFutures, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,8 +125,8 @@ type OrderResponse struct {
|
|||||||
|
|
||||||
// Commission holds trade commission structure
|
// Commission holds trade commission structure
|
||||||
type Commission struct {
|
type Commission struct {
|
||||||
Currency currency.Pair `json:"currency"`
|
Currency string `json:"currency"`
|
||||||
Amount float64 `json:"amount,string"`
|
Amount float64 `json:"amount,string"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// OrderFilledResponse contains order filled response
|
// OrderFilledResponse contains order filled response
|
||||||
@@ -545,8 +545,8 @@ type WsOrderData struct {
|
|||||||
|
|
||||||
// WsOrderFilledCommissionData ws response data
|
// WsOrderFilledCommissionData ws response data
|
||||||
type WsOrderFilledCommissionData struct {
|
type WsOrderFilledCommissionData struct {
|
||||||
Amount float64 `json:"amount,string"`
|
Amount float64 `json:"amount,string"`
|
||||||
Currency currency.Pair `json:"currency"`
|
Currency string `json:"currency"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// WsOrderRejectedResponse ws response
|
// WsOrderRejectedResponse ws response
|
||||||
@@ -594,8 +594,8 @@ type WsTradeHistoryResponse struct {
|
|||||||
|
|
||||||
// WsTradeHistoryCommissionData ws response data
|
// WsTradeHistoryCommissionData ws response data
|
||||||
type WsTradeHistoryCommissionData struct {
|
type WsTradeHistoryCommissionData struct {
|
||||||
Amount float64 `json:"amount,string"`
|
Amount float64 `json:"amount,string"`
|
||||||
Currency currency.Pair `json:"currency"`
|
Currency string `json:"currency"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// WsTradeHistoryTradeData ws response data
|
// WsTradeHistoryTradeData ws response data
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"github.com/thrasher-corp/gocryptotrader/common"
|
"github.com/thrasher-corp/gocryptotrader/common"
|
||||||
"github.com/thrasher-corp/gocryptotrader/common/convert"
|
"github.com/thrasher-corp/gocryptotrader/common/convert"
|
||||||
@@ -1731,3 +1732,38 @@ func (b *Base) SetLeverage(_ context.Context, _ asset.Item, _ currency.Pair, _ m
|
|||||||
func (b *Base) GetLeverage(_ context.Context, _ asset.Item, _ currency.Pair, _ margin.Type, _ order.Side) (float64, error) {
|
func (b *Base) GetLeverage(_ context.Context, _ asset.Item, _ currency.Pair, _ margin.Type, _ order.Side) (float64, error) {
|
||||||
return -1, common.ErrNotYetImplemented
|
return -1, common.ErrNotYetImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MatchSymbolWithAvailablePairs returns a currency pair based on the supplied
|
||||||
|
// symbol and asset type. If the string is expected to have a delimiter this
|
||||||
|
// will attempt to screen it out.
|
||||||
|
func (b *Base) MatchSymbolWithAvailablePairs(symbol string, a asset.Item, hasDelimiter bool) (currency.Pair, error) {
|
||||||
|
if hasDelimiter {
|
||||||
|
for x := range symbol {
|
||||||
|
if unicode.IsPunct(rune(symbol[x])) {
|
||||||
|
symbol = symbol[:x] + symbol[x+1:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b.CurrencyPairs.Match(symbol, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchSymbolCheckEnabled returns a currency pair based on the supplied symbol
|
||||||
|
// and asset type against the available pairs list. If the string is expected to
|
||||||
|
// have a delimiter this will attempt to screen it out. It will also check if
|
||||||
|
// the pair is enabled.
|
||||||
|
func (b *Base) MatchSymbolCheckEnabled(symbol string, a asset.Item, hasDelimiter bool) (pair currency.Pair, enabled bool, err error) {
|
||||||
|
pair, err = b.MatchSymbolWithAvailablePairs(symbol, a, hasDelimiter)
|
||||||
|
if err != nil {
|
||||||
|
return pair, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
enabled, err = b.IsPairEnabled(pair, a)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPairEnabled checks if a pair is enabled for an enabled asset type.
|
||||||
|
// TODO: Optimisation map for enabled pair matching, instead of linear traversal.
|
||||||
|
func (b *Base) IsPairEnabled(pair currency.Pair, a asset.Item) (bool, error) {
|
||||||
|
return b.CurrencyPairs.IsPairEnabled(pair, a)
|
||||||
|
}
|
||||||
|
|||||||
@@ -3089,3 +3089,139 @@ func TestGetStandardConfig(t *testing.T) {
|
|||||||
t.Fatalf("received: '%v' but expected: '%v'", cfg.WebsocketTrafficTimeout, config.DefaultWebsocketTrafficTimeout)
|
t.Fatalf("received: '%v' but expected: '%v'", cfg.WebsocketTrafficTimeout, config.DefaultWebsocketTrafficTimeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMatchSymbolWithAvailablePairs(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
b := Base{Name: "test"}
|
||||||
|
whatIWant := currency.NewPair(currency.BTC, currency.USDT)
|
||||||
|
err := b.CurrencyPairs.Store(asset.Spot, ¤cy.PairStore{
|
||||||
|
AssetEnabled: convert.BoolPtr(true),
|
||||||
|
Available: []currency.Pair{whatIWant}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = b.MatchSymbolWithAvailablePairs("sillBillies", asset.Futures, false)
|
||||||
|
if !errors.Is(err, currency.ErrPairNotFound) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", err, currency.ErrPairNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
whatIGot, err := b.MatchSymbolWithAvailablePairs("btcusdT", asset.Spot, false)
|
||||||
|
if !errors.Is(err, nil) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !whatIGot.Equal(whatIWant) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", whatIGot, whatIWant)
|
||||||
|
}
|
||||||
|
|
||||||
|
whatIGot, err = b.MatchSymbolWithAvailablePairs("btc-usdT", asset.Spot, true)
|
||||||
|
if !errors.Is(err, nil) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !whatIGot.Equal(whatIWant) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", whatIGot, whatIWant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMatchSymbolCheckEnabled(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
b := Base{Name: "test"}
|
||||||
|
whatIWant := currency.NewPair(currency.BTC, currency.USDT)
|
||||||
|
availButNoEnabled := currency.NewPair(currency.BTC, currency.AUD)
|
||||||
|
err := b.CurrencyPairs.Store(asset.Spot, ¤cy.PairStore{
|
||||||
|
AssetEnabled: convert.BoolPtr(true),
|
||||||
|
Available: []currency.Pair{whatIWant, availButNoEnabled},
|
||||||
|
Enabled: []currency.Pair{whatIWant},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err = b.MatchSymbolCheckEnabled("sillBillies", asset.Futures, false)
|
||||||
|
if !errors.Is(err, currency.ErrPairNotFound) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", err, currency.ErrPairNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
whatIGot, enabled, err := b.MatchSymbolCheckEnabled("btcusdT", asset.Spot, false)
|
||||||
|
if !errors.Is(err, nil) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !enabled {
|
||||||
|
t.Fatal("expected true")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !whatIGot.Equal(whatIWant) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", whatIGot, whatIWant)
|
||||||
|
}
|
||||||
|
|
||||||
|
whatIGot, enabled, err = b.MatchSymbolCheckEnabled("btc-usdT", asset.Spot, true)
|
||||||
|
if !errors.Is(err, nil) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !whatIGot.Equal(whatIWant) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", whatIGot, whatIWant)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !enabled {
|
||||||
|
t.Fatal("expected true")
|
||||||
|
}
|
||||||
|
|
||||||
|
whatIGot, enabled, err = b.MatchSymbolCheckEnabled("btc-AUD", asset.Spot, true)
|
||||||
|
if !errors.Is(err, nil) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !whatIGot.Equal(availButNoEnabled) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", whatIGot, whatIWant)
|
||||||
|
}
|
||||||
|
|
||||||
|
if enabled {
|
||||||
|
t.Fatal("expected false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsPairEnabled(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
b := Base{Name: "test"}
|
||||||
|
whatIWant := currency.NewPair(currency.BTC, currency.USDT)
|
||||||
|
availButNoEnabled := currency.NewPair(currency.BTC, currency.AUD)
|
||||||
|
err := b.CurrencyPairs.Store(asset.Spot, ¤cy.PairStore{
|
||||||
|
AssetEnabled: convert.BoolPtr(true),
|
||||||
|
Available: []currency.Pair{whatIWant, availButNoEnabled},
|
||||||
|
Enabled: []currency.Pair{whatIWant},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
enabled, err := b.IsPairEnabled(currency.NewPair(currency.AAA, currency.CYC), asset.Spot)
|
||||||
|
if !errors.Is(err, nil) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if enabled {
|
||||||
|
t.Fatal("expected false")
|
||||||
|
}
|
||||||
|
|
||||||
|
enabled, err = b.IsPairEnabled(availButNoEnabled, asset.Spot)
|
||||||
|
if !errors.Is(err, nil) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if enabled {
|
||||||
|
t.Fatal("expected false")
|
||||||
|
}
|
||||||
|
|
||||||
|
enabled, err = b.IsPairEnabled(whatIWant, asset.Spot)
|
||||||
|
if !errors.Is(err, nil) {
|
||||||
|
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !enabled {
|
||||||
|
t.Fatal("expected true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -48,22 +48,6 @@ func TestMain(m *testing.M) {
|
|||||||
|
|
||||||
e.API.AuthenticatedSupport = true
|
e.API.AuthenticatedSupport = true
|
||||||
e.SetCredentials(APIKey, APISecret, "", "", "", "")
|
e.SetCredentials(APIKey, APISecret, "", "", "", "")
|
||||||
|
|
||||||
err = e.UpdateTradablePairs(context.Background(), false)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Exmo UpdateTradablePairs error", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
avail, err := e.GetAvailablePairs(asset.Spot)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Exmo GetAvailablePairs error", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = e.CurrencyPairs.StorePairs(asset.Spot, avail, true)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Exmo StorePairs error", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Exit(m.Run())
|
os.Exit(m.Run())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,18 +497,19 @@ func TestUpdateTicker(t *testing.T) {
|
|||||||
|
|
||||||
func TestUpdateTickers(t *testing.T) {
|
func TestUpdateTickers(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
err := e.UpdateTickers(context.Background(), asset.Spot)
|
err := e.UpdateTickers(context.Background(), asset.Spot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
avail, err := e.GetAvailablePairs(asset.Spot)
|
enabled, err := e.GetEnabledPairs(asset.Spot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for x := range avail {
|
for x := range enabled {
|
||||||
_, err := ticker.GetTicker(e.Name, avail[x], asset.Spot)
|
_, err := ticker.GetTicker(e.Name, enabled[x], asset.Spot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,28 +206,21 @@ func (e *EXMO) UpdateTradablePairs(ctx context.Context, forceUpdate bool) error
|
|||||||
|
|
||||||
// UpdateTickers updates the ticker for all currency pairs of a given asset type
|
// UpdateTickers updates the ticker for all currency pairs of a given asset type
|
||||||
func (e *EXMO) UpdateTickers(ctx context.Context, a asset.Item) error {
|
func (e *EXMO) UpdateTickers(ctx context.Context, a asset.Item) error {
|
||||||
avail, err := e.GetAvailablePairs(a)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
enabled, err := e.GetEnabledPairs(a)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := e.GetTicker(ctx)
|
result, err := e.GetTicker(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var enabled bool
|
||||||
for symbol, tick := range result {
|
for symbol, tick := range result {
|
||||||
var pair currency.Pair
|
var pair currency.Pair
|
||||||
pair, err = avail.DeriveFrom(strings.Replace(symbol, "_", "", 1))
|
pair, enabled, err = e.MatchSymbolCheckEnabled(symbol, asset.Spot, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
if !errors.Is(err, currency.ErrPairNotFound) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !enabled.Contains(pair, true) {
|
if !enabled {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
err = ticker.ProcessTicker(&ticker.Price{
|
err = ticker.ProcessTicker(&ticker.Price{
|
||||||
|
|||||||
@@ -2112,11 +2112,6 @@ func (g *Gateio) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) e
|
|||||||
return fmt.Errorf("%s %w", a, asset.ErrNotSupported)
|
return fmt.Errorf("%s %w", a, asset.ErrNotSupported)
|
||||||
}
|
}
|
||||||
|
|
||||||
avail, err := g.GetAvailablePairs(a)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var limits []order.MinMaxLevel
|
var limits []order.MinMaxLevel
|
||||||
switch a {
|
switch a {
|
||||||
case asset.Spot:
|
case asset.Spot:
|
||||||
@@ -2132,7 +2127,7 @@ func (g *Gateio) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) e
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var pair currency.Pair
|
var pair currency.Pair
|
||||||
pair, err = avail.DeriveFrom(strings.ReplaceAll(pairsData[x].ID, "_", ""))
|
pair, err = g.MatchSymbolWithAvailablePairs(pairsData[x].ID, a, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,17 +31,17 @@ type Ticker struct {
|
|||||||
|
|
||||||
// TickerV2 holds returned ticker data from the exchange
|
// TickerV2 holds returned ticker data from the exchange
|
||||||
type TickerV2 struct {
|
type TickerV2 struct {
|
||||||
Ask float64 `json:"ask,string"`
|
Ask float64 `json:"ask,string"`
|
||||||
Bid float64 `json:"bid,string"`
|
Bid float64 `json:"bid,string"`
|
||||||
Changes []string `json:"changes"`
|
Changes []string `json:"changes"`
|
||||||
Close float64 `json:"close,string"`
|
Close float64 `json:"close,string"`
|
||||||
High float64 `json:"high,string"`
|
High float64 `json:"high,string"`
|
||||||
Low float64 `json:"low,string"`
|
Low float64 `json:"low,string"`
|
||||||
Open float64 `json:"open,string"`
|
Open float64 `json:"open,string"`
|
||||||
Message string `json:"message,omitempty"`
|
Message string `json:"message,omitempty"`
|
||||||
Reason string `json:"reason,omitempty"`
|
Reason string `json:"reason,omitempty"`
|
||||||
Result string `json:"result,omitempty"`
|
Result string `json:"result,omitempty"`
|
||||||
Symbol currency.Pair `json:"symbol"`
|
Symbol string `json:"symbol"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Orderbook contains orderbook information for both bid and ask side
|
// Orderbook contains orderbook information for both bid and ask side
|
||||||
|
|||||||
@@ -186,23 +186,18 @@ func TestUpdateTicker(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateTickers(t *testing.T) {
|
func TestUpdateTickers(t *testing.T) {
|
||||||
avail, err := h.GetAvailablePairs(asset.Spot)
|
err := h.UpdateTickers(context.Background(), asset.Spot)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = h.CurrencyPairs.StorePairs(asset.Spot, avail, true)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = h.UpdateTickers(context.Background(), asset.Spot)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for j := range avail {
|
enabled, err := h.GetEnabledPairs(asset.Spot)
|
||||||
_, err = h.FetchTicker(context.Background(), avail[j], asset.Spot)
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := range enabled {
|
||||||
|
_, err = h.FetchTicker(context.Background(), enabled[j], asset.Spot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -291,9 +291,9 @@ func (h *HitBTC) FetchTradablePairs(ctx context.Context, _ asset.Item) (currency
|
|||||||
|
|
||||||
pairs := make([]currency.Pair, len(symbols))
|
pairs := make([]currency.Pair, len(symbols))
|
||||||
for x := range symbols {
|
for x := range symbols {
|
||||||
quote := strings.Replace(symbols[x].ID, symbols[x].BaseCurrency, "", 1)
|
index := strings.Index(symbols[x].ID, symbols[x].QuoteCurrency)
|
||||||
var pair currency.Pair
|
var pair currency.Pair
|
||||||
pair, err = currency.NewPairFromStrings(symbols[x].BaseCurrency, quote)
|
pair, err = currency.NewPairFromStrings(symbols[x].ID[:index], symbols[x].ID[index:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -322,23 +322,18 @@ func (h *HitBTC) UpdateTickers(ctx context.Context, a asset.Item) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
avail, err := h.GetAvailablePairs(a)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
enabled, err := h.GetEnabledPairs(a)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for x := range tick {
|
for x := range tick {
|
||||||
pair, err := avail.DeriveFrom(tick[x].Symbol)
|
var pair currency.Pair
|
||||||
|
var enabled bool
|
||||||
|
pair, enabled, err = h.MatchSymbolCheckEnabled(tick[x].Symbol, a, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
if !errors.Is(err, currency.ErrPairNotFound) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !enabled.Contains(pair, true) {
|
if !enabled {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,6 +97,18 @@ type IBotExchange interface {
|
|||||||
CurrencyStateManagement
|
CurrencyStateManagement
|
||||||
FuturesManagement
|
FuturesManagement
|
||||||
MarginManagement
|
MarginManagement
|
||||||
|
|
||||||
|
// MatchSymbolWithAvailablePairs returns a currency pair based on the supplied
|
||||||
|
// symbol and asset type. If the string is expected to have a delimiter this
|
||||||
|
// will attempt to screen it out.
|
||||||
|
MatchSymbolWithAvailablePairs(symbol string, a asset.Item, hasDelimiter bool) (currency.Pair, error)
|
||||||
|
// MatchSymbolCheckEnabled returns a currency pair based on the supplied symbol
|
||||||
|
// and asset type against the available pairs list. If the string is expected to
|
||||||
|
// have a delimiter this will attempt to screen it out. It will also check if
|
||||||
|
// the pair is enabled.
|
||||||
|
MatchSymbolCheckEnabled(symbol string, a asset.Item, hasDelimiter bool) (pair currency.Pair, enabled bool, err error)
|
||||||
|
// IsPairEnabled checks if a pair is enabled for an enabled asset type
|
||||||
|
IsPairEnabled(pair currency.Pair, a asset.Item) (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OrderManagement defines functionality for order management
|
// OrderManagement defines functionality for order management
|
||||||
|
|||||||
@@ -4328,10 +4328,18 @@ func (ok *Okx) GetAssetsFromInstrumentTypeOrID(instType, instrumentID string) ([
|
|||||||
switch {
|
switch {
|
||||||
case len(splitSymbol) == 2:
|
case len(splitSymbol) == 2:
|
||||||
resp := make([]asset.Item, 0, 2)
|
resp := make([]asset.Item, 0, 2)
|
||||||
if err := ok.CurrencyPairs.IsAssetPairEnabled(asset.Spot, pair); err == nil {
|
enabled, err := ok.IsPairEnabled(pair, asset.Spot)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if enabled {
|
||||||
resp = append(resp, asset.Spot)
|
resp = append(resp, asset.Spot)
|
||||||
}
|
}
|
||||||
if err := ok.CurrencyPairs.IsAssetPairEnabled(asset.Margin, pair); err == nil {
|
enabled, err = ok.IsPairEnabled(pair, asset.Margin)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if enabled {
|
||||||
resp = append(resp, asset.Margin)
|
resp = append(resp, asset.Margin)
|
||||||
}
|
}
|
||||||
if len(resp) > 0 {
|
if len(resp) > 0 {
|
||||||
@@ -4340,15 +4348,27 @@ func (ok *Okx) GetAssetsFromInstrumentTypeOrID(instType, instrumentID string) ([
|
|||||||
case len(splitSymbol) > 2:
|
case len(splitSymbol) > 2:
|
||||||
switch splitSymbol[len(splitSymbol)-1] {
|
switch splitSymbol[len(splitSymbol)-1] {
|
||||||
case "SWAP", "swap":
|
case "SWAP", "swap":
|
||||||
if err := ok.CurrencyPairs.IsAssetPairEnabled(asset.PerpetualSwap, pair); err == nil {
|
enabled, err := ok.IsPairEnabled(pair, asset.PerpetualSwap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if enabled {
|
||||||
return []asset.Item{asset.PerpetualSwap}, nil
|
return []asset.Item{asset.PerpetualSwap}, nil
|
||||||
}
|
}
|
||||||
case "C", "P", "c", "p":
|
case "C", "P", "c", "p":
|
||||||
if err := ok.CurrencyPairs.IsAssetPairEnabled(asset.Options, pair); err == nil {
|
enabled, err := ok.IsPairEnabled(pair, asset.Options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if enabled {
|
||||||
return []asset.Item{asset.Options}, nil
|
return []asset.Item{asset.Options}, nil
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if err := ok.CurrencyPairs.IsAssetPairEnabled(asset.Futures, pair); err == nil {
|
enabled, err := ok.IsPairEnabled(pair, asset.Futures)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if enabled {
|
||||||
return []asset.Item{asset.Futures}, nil
|
return []asset.Item{asset.Futures}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user