exchange: upgrade UpdatePair method (#991)

* exchange: upgrade update pair

* exchanges: Add enabled string matching and format handling if discrepency is found.

* linter: fixes

* bithumb: fix tests

* BTSE: api change fix ordering

* huobi: fix tests

* gloriousnits: stage 1

* gloriousnits: stage 2

* currency: more nits

* bitmex: add spot and process pairs before currency package call.

* bitmex: finished correct orderbook matching and other implementations

* linter: fix issue

* currency: Fix linter

* currency: segregate and protect pair store, update tests

* currency/manager: clean code, rm log output

* currency: Add store method and make sure formatting stays nil if not stored.

* gct: check errors

* engine/websocketroutineman: fix tests

* bybit: fix duplication bug

* huobi: fix test

* btse: fix tests?

* ob/buffer: fix tests

* Update currency/manager.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* glorious: nits

* glorious: nits strikes again.

* exchange: add bypassConfigFormatUpgrades to stop formatting

* GLORIOUS LINTER

* Update exchanges/bithumb/bithumb_wrapper.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* glorious: nits

* exchange: fix pair upgrade issue when duplications are in both avail and enabled pairs

* linter: fix shadow dec

* config: fix test

* Update currency/pair_test.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

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:
Ryan O'Hara-Reid
2022-09-16 08:59:27 +10:00
committed by GitHub
parent ecc3b10402
commit f843b7d277
55 changed files with 1832 additions and 685 deletions

View File

@@ -1893,7 +1893,7 @@ func (b *Binance) FormatExchangeCurrency(p currency.Pair, a asset.Item) (currenc
if a == asset.USDTMarginedFutures {
return b.formatUSDTMarginedFuturesPair(p, pairFmt), nil
}
return p.Format(pairFmt.Delimiter, pairFmt.Uppercase), nil
return p.Format(pairFmt), nil
}
// FormatSymbol formats the given pair to a string suitable for exchange API requests
@@ -1917,10 +1917,11 @@ func (b *Binance) formatUSDTMarginedFuturesPair(p currency.Pair, pairFmt currenc
for _, c := range quote {
if c < '0' || c > '9' {
// character rune is alphabetic, cannot be expiring contract
return p.Format(pairFmt.Delimiter, pairFmt.Uppercase)
return p.Format(pairFmt)
}
}
return p.Format(currency.UnderscoreDelimiter, pairFmt.Uppercase)
pairFmt.Delimiter = currency.UnderscoreDelimiter
return p.Format(pairFmt)
}
// GetServerTime returns the current exchange server time.

View File

@@ -65,7 +65,7 @@ func (b *Bitfinex) SetDefaults() {
fmt1 := currency.PairStore{
RequestFormat: &currency.PairFormat{Uppercase: true},
ConfigFormat: &currency.PairFormat{Uppercase: true},
ConfigFormat: &currency.PairFormat{Uppercase: true, Delimiter: currency.DashDelimiter},
}
fmt2 := currency.PairStore{
@@ -323,9 +323,20 @@ func (b *Bitfinex) UpdateTradablePairs(ctx context.Context, forceUpdate bool) er
return err
}
p, err := currency.NewPairsFromStrings(pairs)
if err != nil {
return err
var p currency.Pairs
if assets[i] == asset.MarginFunding {
p = make(currency.Pairs, len(pairs))
for x := range pairs {
p[x], err = currency.NewPairFromStrings(pairs[x], "")
if err != nil {
return err
}
}
} else {
p, err = currency.NewPairsFromStrings(pairs)
if err != nil {
return err
}
}
err = b.UpdatePairs(p, assets[i], false, forceUpdate)

View File

@@ -51,6 +51,11 @@ func TestMain(m *testing.M) {
log.Fatal("Bithumb setup error", err)
}
err = b.UpdateTradablePairs(context.Background(), false)
if err != nil {
log.Fatal("Bithumb Setup() init error")
}
os.Exit(m.Run())
}

View File

@@ -175,11 +175,16 @@ func (b *Bithumb) GenerateSubscriptions() ([]stream.ChannelSubscription, error)
return nil, err
}
pFmt, err := b.GetPairFormat(asset.Spot, true)
if err != nil {
return nil, err
}
for x := range pairs {
for y := range channels {
subscriptions = append(subscriptions, stream.ChannelSubscription{
Channel: channels[y],
Currency: pairs[x].Format("_", true),
Currency: pairs[x].Format(pFmt),
Asset: asset.Spot,
})
}

View File

@@ -40,6 +40,7 @@ func TestWsHandleData(t *testing.T) {
Enabled: pairs,
ConfigFormat: &currency.PairFormat{
Uppercase: true,
Delimiter: currency.DashDelimiter,
},
},
},

View File

@@ -67,7 +67,7 @@ func (b *Bithumb) SetDefaults() {
b.API.CredentialsValidator.RequiresSecret = true
requestFmt := &currency.PairFormat{Uppercase: true, Delimiter: currency.UnderscoreDelimiter}
configFmt := &currency.PairFormat{Uppercase: true, Index: "KRW"}
configFmt := &currency.PairFormat{Uppercase: true, Delimiter: currency.DashDelimiter}
err := b.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot)
if err != nil {
log.Errorln(log.ExchangeSys, err)
@@ -237,7 +237,7 @@ func (b *Bithumb) FetchTradablePairs(ctx context.Context, asset asset.Item) ([]s
}
for x := range currencies {
currencies[x] += "KRW"
currencies[x] += currency.DashDelimiter + "KRW"
}
return currencies, nil

View File

@@ -103,6 +103,14 @@ const (
ContractDownsideProfit
// ContractUpsideProfit upside profit contract type
ContractUpsideProfit
perpetualContractID = "FFWCSX"
spotID = "IFXXXP"
futuresID = "FFCCSX"
bitMEXBasketIndexID = "MRBXXX"
bitMEXPriceIndexID = "MRCXXX"
bitMEXLendingPremiumIndexID = "MRRXXX"
bitMEXVolatilityIndexID = "MRIXXX"
)
// GetAnnouncement returns the general announcements from Bitmex

View File

@@ -3,7 +3,6 @@ package bitmex
import (
"time"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
)
@@ -122,107 +121,107 @@ type Funding struct {
// Instrument Tradeable Contracts, Indices, and History
type Instrument struct {
AskPrice float64 `json:"askPrice"`
BankruptLimitDownPrice float64 `json:"bankruptLimitDownPrice"`
BankruptLimitUpPrice float64 `json:"bankruptLimitUpPrice"`
BidPrice float64 `json:"bidPrice"`
BuyLeg string `json:"buyLeg"`
CalcInterval string `json:"calcInterval"`
Capped bool `json:"capped"`
ClosingTimestamp time.Time `json:"closingTimestamp"`
Deleverage bool `json:"deleverage"`
Expiry string `json:"expiry"`
FairBasis float64 `json:"fairBasis"`
FairBasisRate float64 `json:"fairBasisRate"`
FairMethod string `json:"fairMethod"`
FairPrice float64 `json:"fairPrice"`
Front string `json:"front"`
FundingBaseSymbol string `json:"fundingBaseSymbol"`
FundingInterval string `json:"fundingInterval"`
FundingPremiumSymbol string `json:"fundingPremiumSymbol"`
FundingQuoteSymbol string `json:"fundingQuoteSymbol"`
FundingRate float64 `json:"fundingRate"`
FundingTimestamp time.Time `json:"fundingTimestamp"`
HasLiquidity bool `json:"hasLiquidity"`
HighPrice float64 `json:"highPrice"`
ImpactAskPrice float64 `json:"impactAskPrice"`
ImpactBidPrice float64 `json:"impactBidPrice"`
ImpactMidPrice float64 `json:"impactMidPrice"`
IndicativeFundingRate float64 `json:"indicativeFundingRate"`
IndicativeSettlePrice float64 `json:"indicativeSettlePrice"`
IndicativeTaxRate float64 `json:"indicativeTaxRate"`
InitMargin float64 `json:"initMargin"`
InsuranceFee float64 `json:"insuranceFee"`
InverseLeg string `json:"inverseLeg"`
IsInverse bool `json:"isInverse"`
IsQuanto bool `json:"isQuanto"`
LastChangePcnt float64 `json:"lastChangePcnt"`
LastPrice float64 `json:"lastPrice"`
LastPriceProtected float64 `json:"lastPriceProtected"`
LastTickDirection string `json:"lastTickDirection"`
Limit float64 `json:"limit"`
LimitDownPrice float64 `json:"limitDownPrice"`
LimitUpPrice float64 `json:"limitUpPrice"`
Listing string `json:"listing"`
LotSize int64 `json:"lotSize"`
LowPrice float64 `json:"lowPrice"`
MaintMargin float64 `json:"maintMargin"`
MakerFee float64 `json:"makerFee"`
MarkMethod string `json:"markMethod"`
MarkPrice float64 `json:"markPrice"`
MaxOrderQty int64 `json:"maxOrderQty"`
MaxPrice float64 `json:"maxPrice"`
MidPrice float64 `json:"midPrice"`
Multiplier int64 `json:"multiplier"`
OpenInterest int64 `json:"openInterest"`
OpenValue int64 `json:"openValue"`
OpeningTimestamp time.Time `json:"openingTimestamp"`
OptionMultiplier float64 `json:"optionMultiplier"`
OptionStrikePcnt float64 `json:"optionStrikePcnt"`
OptionStrikePrice float64 `json:"optionStrikePrice"`
OptionStrikeRound float64 `json:"optionStrikeRound"`
OptionUnderlyingPrice float64 `json:"optionUnderlyingPrice"`
PositionCurrency string `json:"positionCurrency"`
PrevClosePrice float64 `json:"prevClosePrice"`
PrevPrice24h float64 `json:"prevPrice24h"`
PrevTotalTurnover int64 `json:"prevTotalTurnover"`
PrevTotalVolume int64 `json:"prevTotalVolume"`
PublishInterval string `json:"publishInterval"`
PublishTime string `json:"publishTime"`
QuoteCurrency string `json:"quoteCurrency"`
QuoteToSettleMultiplier int64 `json:"quoteToSettleMultiplier"`
RebalanceInterval string `json:"rebalanceInterval"`
RebalanceTimestamp time.Time `json:"rebalanceTimestamp"`
Reference string `json:"reference"`
ReferenceSymbol string `json:"referenceSymbol"`
RelistInterval string `json:"relistInterval"`
RiskLimit int64 `json:"riskLimit"`
RiskStep int64 `json:"riskStep"`
RootSymbol string `json:"rootSymbol"`
SellLeg string `json:"sellLeg"`
SessionInterval string `json:"sessionInterval"`
SettlCurrency string `json:"settlCurrency"`
Settle string `json:"settle"`
SettledPrice float64 `json:"settledPrice"`
SettlementFee float64 `json:"settlementFee"`
State string `json:"state"`
Symbol currency.Pair `json:"symbol"`
TakerFee float64 `json:"takerFee"`
Taxed bool `json:"taxed"`
TickSize float64 `json:"tickSize"`
Timestamp time.Time `json:"timestamp"`
TotalTurnover int64 `json:"totalTurnover"`
TotalVolume int64 `json:"totalVolume"`
Turnover int64 `json:"turnover"`
Turnover24h int64 `json:"turnover24h"`
Typ string `json:"typ"`
Underlying string `json:"underlying"`
UnderlyingSymbol string `json:"underlyingSymbol"`
UnderlyingToPositionMultiplier int64 `json:"underlyingToPositionMultiplier"`
UnderlyingToSettleMultiplier int64 `json:"underlyingToSettleMultiplier"`
Volume float64 `json:"volume"`
Volume24h float64 `json:"volume24h"`
Vwap float64 `json:"vwap"`
AskPrice float64 `json:"askPrice"`
BankruptLimitDownPrice float64 `json:"bankruptLimitDownPrice"`
BankruptLimitUpPrice float64 `json:"bankruptLimitUpPrice"`
BidPrice float64 `json:"bidPrice"`
BuyLeg string `json:"buyLeg"`
CalcInterval string `json:"calcInterval"`
Capped bool `json:"capped"`
ClosingTimestamp time.Time `json:"closingTimestamp"`
Deleverage bool `json:"deleverage"`
Expiry string `json:"expiry"`
FairBasis float64 `json:"fairBasis"`
FairBasisRate float64 `json:"fairBasisRate"`
FairMethod string `json:"fairMethod"`
FairPrice float64 `json:"fairPrice"`
Front string `json:"front"`
FundingBaseSymbol string `json:"fundingBaseSymbol"`
FundingInterval string `json:"fundingInterval"`
FundingPremiumSymbol string `json:"fundingPremiumSymbol"`
FundingQuoteSymbol string `json:"fundingQuoteSymbol"`
FundingRate float64 `json:"fundingRate"`
FundingTimestamp time.Time `json:"fundingTimestamp"`
HasLiquidity bool `json:"hasLiquidity"`
HighPrice float64 `json:"highPrice"`
ImpactAskPrice float64 `json:"impactAskPrice"`
ImpactBidPrice float64 `json:"impactBidPrice"`
ImpactMidPrice float64 `json:"impactMidPrice"`
IndicativeFundingRate float64 `json:"indicativeFundingRate"`
IndicativeSettlePrice float64 `json:"indicativeSettlePrice"`
IndicativeTaxRate float64 `json:"indicativeTaxRate"`
InitMargin float64 `json:"initMargin"`
InsuranceFee float64 `json:"insuranceFee"`
InverseLeg string `json:"inverseLeg"`
IsInverse bool `json:"isInverse"`
IsQuanto bool `json:"isQuanto"`
LastChangePcnt float64 `json:"lastChangePcnt"`
LastPrice float64 `json:"lastPrice"`
LastPriceProtected float64 `json:"lastPriceProtected"`
LastTickDirection string `json:"lastTickDirection"`
Limit float64 `json:"limit"`
LimitDownPrice float64 `json:"limitDownPrice"`
LimitUpPrice float64 `json:"limitUpPrice"`
Listing string `json:"listing"`
LotSize int64 `json:"lotSize"`
LowPrice float64 `json:"lowPrice"`
MaintMargin float64 `json:"maintMargin"`
MakerFee float64 `json:"makerFee"`
MarkMethod string `json:"markMethod"`
MarkPrice float64 `json:"markPrice"`
MaxOrderQty int64 `json:"maxOrderQty"`
MaxPrice float64 `json:"maxPrice"`
MidPrice float64 `json:"midPrice"`
Multiplier int64 `json:"multiplier"`
OpenInterest int64 `json:"openInterest"`
OpenValue int64 `json:"openValue"`
OpeningTimestamp time.Time `json:"openingTimestamp"`
OptionMultiplier float64 `json:"optionMultiplier"`
OptionStrikePcnt float64 `json:"optionStrikePcnt"`
OptionStrikePrice float64 `json:"optionStrikePrice"`
OptionStrikeRound float64 `json:"optionStrikeRound"`
OptionUnderlyingPrice float64 `json:"optionUnderlyingPrice"`
PositionCurrency string `json:"positionCurrency"`
PrevClosePrice float64 `json:"prevClosePrice"`
PrevPrice24h float64 `json:"prevPrice24h"`
PrevTotalTurnover int64 `json:"prevTotalTurnover"`
PrevTotalVolume int64 `json:"prevTotalVolume"`
PublishInterval string `json:"publishInterval"`
PublishTime string `json:"publishTime"`
QuoteCurrency string `json:"quoteCurrency"`
QuoteToSettleMultiplier int64 `json:"quoteToSettleMultiplier"`
RebalanceInterval string `json:"rebalanceInterval"`
RebalanceTimestamp time.Time `json:"rebalanceTimestamp"`
Reference string `json:"reference"`
ReferenceSymbol string `json:"referenceSymbol"`
RelistInterval string `json:"relistInterval"`
RiskLimit int64 `json:"riskLimit"`
RiskStep int64 `json:"riskStep"`
RootSymbol string `json:"rootSymbol"`
SellLeg string `json:"sellLeg"`
SessionInterval string `json:"sessionInterval"`
SettlCurrency string `json:"settlCurrency"`
Settle string `json:"settle"`
SettledPrice float64 `json:"settledPrice"`
SettlementFee float64 `json:"settlementFee"`
State string `json:"state"`
Symbol string `json:"symbol"`
TakerFee float64 `json:"takerFee"`
Taxed bool `json:"taxed"`
TickSize float64 `json:"tickSize"`
Timestamp time.Time `json:"timestamp"`
TotalTurnover int64 `json:"totalTurnover"`
TotalVolume int64 `json:"totalVolume"`
Turnover int64 `json:"turnover"`
Turnover24h int64 `json:"turnover24h"`
Typ string `json:"typ"`
Underlying string `json:"underlying"`
UnderlyingSymbol string `json:"underlyingSymbol"`
UnderlyingToPositionMultiplier int64 `json:"underlyingToPositionMultiplier"`
UnderlyingToSettleMultiplier int64 `json:"underlyingToSettleMultiplier"`
Volume float64 `json:"volume"`
Volume24h float64 `json:"volume24h"`
Vwap float64 `json:"vwap"`
}
// InstrumentInterval instrument interval

View File

@@ -186,26 +186,18 @@ func (b *Bitmex) wsHandleData(respRaw []byte) error {
if len(orderbooks.Data) == 0 {
return fmt.Errorf("%s - Empty orderbook data received: %s", b.Name, respRaw)
}
var p currency.Pair
p, err = currency.NewPairFromString(orderbooks.Data[0].Symbol)
if err != nil {
return err
}
var pair currency.Pair
var a asset.Item
a, err = b.GetPairAssetType(p)
pair, a, err = b.GetPairAndAssetTypeRequestFormatted(orderbooks.Data[0].Symbol)
if err != nil {
return err
}
err = b.processOrderbook(orderbooks.Data,
orderbooks.Action,
p,
a)
err = b.processOrderbook(orderbooks.Data, orderbooks.Action, pair, a)
if err != nil {
return err
}
case bitmexWSTrade:
if !b.IsSaveTradeDataEnabled() {
return nil
@@ -223,13 +215,8 @@ func (b *Bitmex) wsHandleData(respRaw []byte) error {
continue
}
var p currency.Pair
p, err = currency.NewPairFromString(tradeHolder.Data[i].Symbol)
if err != nil {
return err
}
var a asset.Item
a, err = b.GetPairAssetType(p)
p, a, err = b.GetPairAndAssetTypeRequestFormatted(tradeHolder.Data[i].Symbol)
if err != nil {
return err
}
@@ -285,13 +272,8 @@ func (b *Bitmex) wsHandleData(respRaw []byte) error {
for i := range response.Data {
var p currency.Pair
p, err = currency.NewPairFromString(response.Data[i].Symbol)
if err != nil {
return err
}
var a asset.Item
a, err = b.GetPairAssetType(p)
p, a, err = b.GetPairAndAssetTypeRequestFormatted(response.Data[i].Symbol)
if err != nil {
return err
}
@@ -570,6 +552,10 @@ func (b *Bitmex) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, e
assets := b.GetAssetTypes(true)
for x := range assets {
pFmt, err := b.GetPairFormat(assets[x], true)
if err != nil {
return nil, err
}
contracts, err := b.GetEnabledPairs(assets[x])
if err != nil {
return nil, err
@@ -581,7 +567,7 @@ func (b *Bitmex) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, e
continue
}
subscriptions = append(subscriptions, stream.ChannelSubscription{
Channel: channels[z] + ":" + contracts[y].String(),
Channel: channels[z] + ":" + pFmt.Format(contracts[y]),
Currency: contracts[y],
Asset: assets[x],
})
@@ -596,6 +582,11 @@ func (b *Bitmex) GenerateAuthenticatedSubscriptions() ([]stream.ChannelSubscript
if !b.Websocket.CanUseAuthenticatedEndpoints() {
return nil, nil
}
pFmt, err := b.GetPairFormat(asset.PerpetualContract, true)
if err != nil {
return nil, err
}
contracts, err := b.GetEnabledPairs(asset.PerpetualContract)
if err != nil {
return nil, err
@@ -626,7 +617,7 @@ func (b *Bitmex) GenerateAuthenticatedSubscriptions() ([]stream.ChannelSubscript
for i := range channels {
for j := range contracts {
subscriptions = append(subscriptions, stream.ChannelSubscription{
Channel: channels[i] + ":" + contracts[j].String(),
Channel: channels[i] + ":" + pFmt.Format(contracts[j]),
Currency: contracts[j],
Asset: asset.PerpetualContract,
})
@@ -639,7 +630,6 @@ func (b *Bitmex) GenerateAuthenticatedSubscriptions() ([]stream.ChannelSubscript
func (b *Bitmex) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error {
var subscriber WebsocketRequest
subscriber.Command = "subscribe"
for i := range channelsToSubscribe {
subscriber.Arguments = append(subscriber.Arguments,
channelsToSubscribe[i].Channel)

View File

@@ -62,13 +62,30 @@ func (b *Bitmex) SetDefaults() {
b.API.CredentialsValidator.RequiresKey = true
b.API.CredentialsValidator.RequiresSecret = true
requestFmt := &currency.PairFormat{Uppercase: true}
configFmt := &currency.PairFormat{Uppercase: true}
err := b.SetGlobalPairsManager(requestFmt,
configFmt,
asset.PerpetualContract,
asset.Futures,
asset.Index)
configFmt := &currency.PairFormat{Uppercase: true, Delimiter: currency.DashDelimiter}
standardRequestFmt := &currency.PairFormat{Uppercase: true}
spotRequestFormat := &currency.PairFormat{Uppercase: true, Delimiter: currency.UnderscoreDelimiter}
spot := currency.PairStore{RequestFormat: spotRequestFormat, ConfigFormat: configFmt}
err := b.StoreAssetPairFormat(asset.Spot, spot)
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
perp := currency.PairStore{RequestFormat: standardRequestFmt, ConfigFormat: configFmt}
err = b.StoreAssetPairFormat(asset.PerpetualContract, perp)
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
futures := currency.PairStore{RequestFormat: standardRequestFmt, ConfigFormat: configFmt}
err = b.StoreAssetPairFormat(asset.Futures, futures)
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
index := currency.PairStore{RequestFormat: standardRequestFmt, ConfigFormat: configFmt}
err = b.StoreAssetPairFormat(asset.Index, index)
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
@@ -227,66 +244,99 @@ func (b *Bitmex) Run() {
}
// FetchTradablePairs returns a list of the exchanges tradable pairs
func (b *Bitmex) FetchTradablePairs(ctx context.Context, asset asset.Item) ([]string, error) {
func (b *Bitmex) FetchTradablePairs(ctx context.Context, a asset.Item) ([]string, error) {
marketInfo, err := b.GetActiveAndIndexInstruments(ctx)
if err != nil {
return nil, err
}
products := make([]string, len(marketInfo))
products := make([]string, 0, len(marketInfo))
for x := range marketInfo {
products[x] = marketInfo[x].Symbol.String()
}
if marketInfo[x].State != "Open" && a != asset.Index {
continue
}
switch a {
case asset.Spot:
if marketInfo[x].Typ == spotID {
products = append(products, marketInfo[x].Symbol)
}
case asset.PerpetualContract:
if marketInfo[x].Typ == perpetualContractID {
var settleTrail string
if strings.Contains(marketInfo[x].Symbol, currency.UnderscoreDelimiter) {
// Example: ETHUSD_ETH quoted in USD, paid out in ETH.
settlement := strings.Split(marketInfo[x].Symbol, currency.UnderscoreDelimiter)
if len(settlement) != 2 {
log.Warnf(log.ExchangeSys, "%s currency %s %s cannot be added to tradable pairs",
b.Name,
marketInfo[x].Symbol,
a)
break
}
settleTrail = currency.UnderscoreDelimiter + settlement[1]
}
products = append(products, marketInfo[x].Underlying+
currency.DashDelimiter+
marketInfo[x].QuoteCurrency+settleTrail)
}
case asset.Futures:
if marketInfo[x].Typ == futuresID {
isolate := strings.Split(marketInfo[x].Symbol, currency.UnderscoreDelimiter)
if len(isolate[0]) < 3 {
log.Warnf(log.ExchangeSys, "%s currency %s %s be cannot added to tradable pairs",
b.Name,
marketInfo[x].Symbol,
a)
break
}
var settleTrail string
if len(isolate) == 2 {
// Example: ETHUSDU22_ETH quoted in USD, paid out in ETH.
settleTrail = currency.UnderscoreDelimiter + isolate[1]
}
root := isolate[0][:len(isolate[0])-3]
contract := isolate[0][len(isolate[0])-3:]
products = append(products, root+currency.DashDelimiter+contract+settleTrail)
}
case asset.Index:
// TODO: This can be expanded into individual assets later.
if marketInfo[x].Typ == bitMEXBasketIndexID ||
marketInfo[x].Typ == bitMEXPriceIndexID ||
marketInfo[x].Typ == bitMEXLendingPremiumIndexID ||
marketInfo[x].Typ == bitMEXVolatilityIndexID {
products = append(products, marketInfo[x].Symbol)
}
default:
return nil, errors.New("unhandled asset type")
}
}
return products, nil
}
// UpdateTradablePairs updates the exchanges available pairs and stores
// them in the exchanges config
func (b *Bitmex) UpdateTradablePairs(ctx context.Context, forceUpdate bool) error {
pairs, err := b.FetchTradablePairs(ctx, asset.Spot)
if err != nil {
return err
}
assets := b.GetAssetTypes(false)
// Zerovalue current list which will remove old asset pairs when contract
// types expire or become obsolete
var assetPairs = map[asset.Item][]string{
asset.Index: {},
asset.PerpetualContract: {},
asset.Futures: {},
}
for x := range pairs {
if strings.Contains(pairs[x], ".") {
assetPairs[asset.Index] = append(assetPairs[asset.Index], pairs[x])
continue
}
if strings.Contains(pairs[x], "USD") {
assetPairs[asset.PerpetualContract] = append(assetPairs[asset.PerpetualContract],
pairs[x])
continue
}
assetPairs[asset.Futures] = append(assetPairs[asset.Futures], pairs[x])
}
for a, values := range assetPairs {
p, err := currency.NewPairsFromStrings(values)
for x := range assets {
pairsStr, err := b.FetchTradablePairs(ctx, assets[x])
if err != nil {
return err
}
err = b.UpdatePairs(p, a, false, false)
pairs, err := currency.NewPairsFromStrings(pairsStr)
if err != nil {
log.Warnf(log.ExchangeSys,
"%s failed to update available pairs. Err: %v",
b.Name,
err)
return err
}
err = b.UpdatePairs(pairs, assets[x], false, false)
if err != nil {
return err
}
}
return nil
}
@@ -303,7 +353,13 @@ func (b *Bitmex) UpdateTickers(ctx context.Context, a asset.Item) error {
}
for j := range tick {
if !pairs.Contains(tick[j].Symbol, true) {
var pair currency.Pair
pair, err = currency.NewPairFromString(tick[j].Symbol)
if err != nil {
return err
}
if !pairs.Contains(pair, true) {
continue
}
@@ -315,7 +371,7 @@ func (b *Bitmex) UpdateTickers(ctx context.Context, a asset.Item) error {
Ask: tick[j].AskPrice,
Volume: tick[j].Volume24h,
Close: tick[j].PrevClosePrice,
Pair: tick[j].Symbol,
Pair: pair,
LastUpdated: tick[j].Timestamp,
ExchangeName: b.Name,
AssetType: a})

View File

@@ -58,7 +58,7 @@ func (b *Bitstamp) SetDefaults() {
b.API.CredentialsValidator.RequiresKey = true
b.API.CredentialsValidator.RequiresSecret = true
b.API.CredentialsValidator.RequiresClientID = true
requestFmt := &currency.PairFormat{}
requestFmt := &currency.EMPTYFORMAT
configFmt := &currency.PairFormat{
Uppercase: true,
Delimiter: currency.ForwardSlashDelimiter,

View File

@@ -82,6 +82,7 @@ func (b *BTSE) SetDefaults() {
},
ConfigFormat: &currency.PairFormat{
Uppercase: true,
Delimiter: currency.DashDelimiter,
},
}
err = b.StoreAssetPairFormat(asset.Futures, fmt2)
@@ -379,7 +380,7 @@ func (b *BTSE) UpdateOrderbook(ctx context.Context, p currency.Pair, assetType a
Amount: a.SellQuote[x].Size,
})
}
book.Asks.SortAsks() // Sort asks for correct alignment
book.Asks.SortAsks()
book.Pair = p
book.Exchange = b.Name
book.Asset = assetType

View File

@@ -303,7 +303,17 @@ func (by *Bybit) FetchTradablePairs(ctx context.Context, a asset.Item) ([]string
if allPairs[x].Status != "Trading" || allPairs[x].QuoteCurrency != "USD" {
continue
}
symbol := allPairs[x].BaseCurrency + currency.DashDelimiter + allPairs[x].QuoteCurrency
contractSplit := strings.Split(allPairs[x].Name, allPairs[x].BaseCurrency)
if len(contractSplit) != 2 {
log.Warnf(log.ExchangeSys, "%s base currency %s cannot split contract name %s cannot add to tradable pairs",
by.Name,
allPairs[x].BaseCurrency,
allPairs[x].Name)
continue
}
symbol := allPairs[x].BaseCurrency + currency.DashDelimiter + contractSplit[1]
pairs = append(pairs, symbol)
}
return pairs, nil

View File

@@ -21,10 +21,6 @@ import (
"github.com/thrasher-corp/gocryptotrader/log"
)
const (
wsFuturesPath = "realtime"
)
// WsFuturesConnect connects to a Futures websocket feed
func (by *Bybit) WsFuturesConnect() error {
if !by.Websocket.IsEnabled() || !by.IsEnabled() {

View File

@@ -21,12 +21,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/log"
)
const (
wsUSDTMarginedPathPublic = "realtime_public"
wsUSDTMarginedPathPrivate = "realtime_private"
wsUSDTKline = "candle"
)
const wsUSDTKline = "candle"
// WsUSDTConnect connects to a USDT websocket feed
func (by *Bybit) WsUSDTConnect() error {

View File

@@ -41,7 +41,11 @@ const (
ResetConfigPairsWarningMessage = "%s Enabled and available pairs for %s reset due to config upgrade, please enable the ones you would like to use again. Defaulting to %v"
)
var errEndpointStringNotFound = errors.New("endpoint string not found")
var (
errEndpointStringNotFound = errors.New("endpoint string not found")
errConfigPairFormatRequiresDelimiter = errors.New("config pair format requires delimiter")
errSymbolCannotBeMatched = errors.New("symbol cannot be matched")
)
// SetClientProxyAddress sets a proxy address for REST and websocket requests
func (b *Base) SetClientProxyAddress(addr string) error {
@@ -140,11 +144,8 @@ func (b *Base) SupportsRESTTickerBatchUpdates() bool {
// SupportsAutoPairUpdates returns whether or not the exchange supports
// auto currency pair updating
func (b *Base) SupportsAutoPairUpdates() bool {
if b.Features.Supports.RESTCapabilities.AutoPairUpdates ||
b.Features.Supports.WebsocketCapabilities.AutoPairUpdates {
return true
}
return false
return b.Features.Supports.RESTCapabilities.AutoPairUpdates ||
b.Features.Supports.WebsocketCapabilities.AutoPairUpdates
}
// GetLastPairsUpdateTime returns the unix timestamp of when the exchanges
@@ -176,6 +177,33 @@ func (b *Base) GetPairAssetType(c currency.Pair) (asset.Item, error) {
return asset.Empty, errors.New("asset type not associated with currency pair")
}
// GetPairAndAssetTypeRequestFormatted returns the pair and the asset type
// when there is distinct differentiation between exchange request symbols asset
// types. e.g. "BTC-USD" Spot and "BTC_USD" PERP request formatted.
func (b *Base) GetPairAndAssetTypeRequestFormatted(symbol string) (currency.Pair, asset.Item, error) {
if symbol == "" {
return currency.Pair{}, asset.Empty, currency.ErrCurrencyPairEmpty
}
assetTypes := b.GetAssetTypes(true)
for i := range assetTypes {
pFmt, err := b.GetPairFormat(assetTypes[i], true)
if err != nil {
return currency.Pair{}, asset.Empty, err
}
enabled, err := b.GetEnabledPairs(assetTypes[i])
if err != nil {
return currency.Pair{}, asset.Empty, err
}
for j := range enabled {
if pFmt.Format(enabled[j]) == symbol {
return enabled[j], assetTypes[i], nil
}
}
}
return currency.Pair{}, asset.Empty, errSymbolCannotBeMatched
}
// GetClientBankAccounts returns banking details associated with
// a client for withdrawal purposes
func (b *Base) GetClientBankAccounts(exchangeName, withdrawalCurrency string) (*banking.Account, error) {
@@ -192,7 +220,7 @@ func (b *Base) GetExchangeBankAccounts(id, depositCurrency string) (*banking.Acc
// SetCurrencyPairFormat checks the exchange request and config currency pair
// formats and syncs it with the exchanges SetDefault settings
func (b *Base) SetCurrencyPairFormat() {
func (b *Base) SetCurrencyPairFormat() error {
if b.Config.CurrencyPairs == nil {
b.Config.CurrencyPairs = new(currency.PairsManager)
}
@@ -201,7 +229,7 @@ func (b *Base) SetCurrencyPairFormat() {
if b.Config.CurrencyPairs.UseGlobalFormat {
b.Config.CurrencyPairs.RequestFormat = b.CurrencyPairs.RequestFormat
b.Config.CurrencyPairs.ConfigFormat = b.CurrencyPairs.ConfigFormat
return
return nil
}
if b.Config.CurrencyPairs.ConfigFormat != nil {
@@ -216,11 +244,15 @@ func (b *Base) SetCurrencyPairFormat() {
if _, err := b.Config.CurrencyPairs.Get(assetTypes[x]); err != nil {
ps, err := b.CurrencyPairs.Get(assetTypes[x])
if err != nil {
continue
return err
}
err = b.Config.CurrencyPairs.Store(assetTypes[x], ps)
if err != nil {
return err
}
b.Config.CurrencyPairs.Store(assetTypes[x], *ps)
}
}
return nil
}
// SetConfigPairs sets the exchanges currency pairs to the pairs set in the config
@@ -235,37 +267,62 @@ func (b *Base) SetConfigPairs() error {
assetTypes[x])
continue // If there are unsupported assets contained in config, skip.
}
cfgPS, err := b.Config.CurrencyPairs.Get(assetTypes[x])
if err != nil {
return err
}
var enabledAsset bool
if b.Config.CurrencyPairs.IsAssetEnabled(assetTypes[x]) == nil {
enabledAsset = true
}
err = b.CurrencyPairs.SetAssetEnabled(assetTypes[x], enabledAsset)
err := b.CurrencyPairs.SetAssetEnabled(assetTypes[x], enabledAsset)
// Suppress error when assets are enabled by default and they are being
// enabled by config. A check for the inverse
// e.g. currency.ErrAssetAlreadyDisabled is not needed.
if err != nil && err != currency.ErrAssetAlreadyEnabled {
if err != nil && !errors.Is(err, currency.ErrAssetAlreadyEnabled) {
return err
}
cfgPS, err := b.Config.CurrencyPairs.Get(assetTypes[x])
if err != nil {
return err
}
if b.Config.CurrencyPairs.UseGlobalFormat {
b.CurrencyPairs.StorePairs(assetTypes[x], cfgPS.Available, false)
b.CurrencyPairs.StorePairs(assetTypes[x], cfgPS.Enabled, true)
err = b.CurrencyPairs.StorePairs(assetTypes[x], cfgPS.Available, false)
if err != nil {
return err
}
err = b.CurrencyPairs.StorePairs(assetTypes[x], cfgPS.Enabled, true)
if err != nil {
return err
}
continue
}
exchPS, err := b.CurrencyPairs.Get(assetTypes[x])
if err != nil {
return err
}
cfgPS.ConfigFormat = exchPS.ConfigFormat
cfgPS.RequestFormat = exchPS.RequestFormat
b.CurrencyPairs.StorePairs(assetTypes[x], cfgPS.Available, false)
b.CurrencyPairs.StorePairs(assetTypes[x], cfgPS.Enabled, true)
if exchPS.ConfigFormat != nil {
err = b.Config.CurrencyPairs.StoreFormat(assetTypes[x], exchPS.ConfigFormat, true)
if err != nil {
return err
}
}
if exchPS.RequestFormat != nil {
err = b.Config.CurrencyPairs.StoreFormat(assetTypes[x], exchPS.RequestFormat, false)
if err != nil {
return err
}
}
err = b.CurrencyPairs.StorePairs(assetTypes[x], cfgPS.Available, false)
if err != nil {
return err
}
err = b.CurrencyPairs.StorePairs(assetTypes[x], cfgPS.Enabled, true)
if err != nil {
return err
}
}
return nil
}
@@ -291,14 +348,14 @@ func (b *Base) GetPairFormat(assetType asset.Item, requestFormat bool) (currency
if b.CurrencyPairs.UseGlobalFormat {
if requestFormat {
if b.CurrencyPairs.RequestFormat == nil {
return currency.PairFormat{},
return currency.EMPTYFORMAT,
errors.New("global request format is nil")
}
return *b.CurrencyPairs.RequestFormat, nil
}
if b.CurrencyPairs.ConfigFormat == nil {
return currency.PairFormat{},
return currency.EMPTYFORMAT,
errors.New("global config format is nil")
}
return *b.CurrencyPairs.ConfigFormat, nil
@@ -306,19 +363,19 @@ func (b *Base) GetPairFormat(assetType asset.Item, requestFormat bool) (currency
ps, err := b.CurrencyPairs.Get(assetType)
if err != nil {
return currency.PairFormat{}, err
return currency.EMPTYFORMAT, err
}
if requestFormat {
if ps.RequestFormat == nil {
return currency.PairFormat{},
return currency.EMPTYFORMAT,
errors.New("asset type request format is nil")
}
return *ps.RequestFormat, nil
}
if ps.ConfigFormat == nil {
return currency.PairFormat{},
return currency.EMPTYFORMAT,
errors.New("asset type config format is nil")
}
return *ps.ConfigFormat, nil
@@ -336,14 +393,11 @@ func (b *Base) GetEnabledPairs(a asset.Item) (currency.Pairs, error) {
if err != nil {
return nil, err
}
enabledpairs, err := b.CurrencyPairs.GetPairs(a, true)
enabledPairs, err := b.CurrencyPairs.GetPairs(a, true)
if err != nil {
return nil, err
}
return enabledpairs.Format(format.Delimiter,
format.Index,
format.Uppercase),
nil
return enabledPairs.Format(format), nil
}
// GetRequestFormattedPairAndAssetType is a method that returns the enabled currency pair of
@@ -362,7 +416,7 @@ func (b *Base) GetRequestFormattedPairAndAssetType(p string) (currency.Pair, ass
}
for j := range pairs {
formattedPair := pairs[j].Format(format.Delimiter, format.Uppercase)
formattedPair := pairs[j].Format(format)
if strings.EqualFold(formattedPair.String(), p) {
return formattedPair, assetTypes[i], nil
}
@@ -382,28 +436,23 @@ func (b *Base) GetAvailablePairs(assetType asset.Item) (currency.Pairs, error) {
if err != nil {
return nil, err
}
return pairs.Format(format.Delimiter, format.Index, format.Uppercase), nil
return pairs.Format(format), nil
}
// SupportsPair returns true or not whether a currency pair exists in the
// exchange available currencies or not
func (b *Base) SupportsPair(p currency.Pair, enabledPairs bool, assetType asset.Item) error {
var pairs currency.Pairs
var err error
if enabledPairs {
pairs, err := b.GetEnabledPairs(assetType)
if err != nil {
return err
}
if pairs.Contains(p, false) {
return nil
}
return errors.New("pair not supported")
pairs, err = b.GetEnabledPairs(assetType)
} else {
pairs, err = b.GetAvailablePairs(assetType)
}
avail, err := b.GetAvailablePairs(assetType)
if err != nil {
return err
}
if avail.Contains(p, false) {
if pairs.Contains(p, false) {
return nil
}
return errors.New("pair not supported")
@@ -443,7 +492,7 @@ func (b *Base) FormatExchangeCurrency(p currency.Pair, assetType asset.Item) (cu
if err != nil {
return currency.EMPTYPAIR, err
}
return p.Format(pairFmt.Delimiter, pairFmt.Uppercase), nil
return p.Format(pairFmt), nil
}
// SetEnabled is a method that sets if the exchange is enabled
@@ -503,7 +552,11 @@ func (b *Base) SetupDefaults(exch *config.Exchange) error {
if err != nil {
return err
}
b.SetCurrencyPairFormat()
err = b.SetCurrencyPairFormat()
if err != nil {
return err
}
err = b.SetConfigPairs()
if err != nil {
@@ -551,95 +604,166 @@ func (b *Base) SetPairs(pairs currency.Pairs, assetType asset.Item, enabled bool
return err
}
var newPairs currency.Pairs
for x := range pairs {
newPairs = append(newPairs, pairs[x].Format(pairFmt.Delimiter,
pairFmt.Uppercase))
pairs[x] = pairs[x].Format(pairFmt)
}
b.CurrencyPairs.StorePairs(assetType, newPairs, enabled)
b.Config.CurrencyPairs.StorePairs(assetType, newPairs, enabled)
return nil
err = b.CurrencyPairs.StorePairs(assetType, pairs, enabled)
if err != nil {
return err
}
return b.Config.CurrencyPairs.StorePairs(assetType, pairs, enabled)
}
// UpdatePairs updates the exchange currency pairs for either enabledPairs or
// availablePairs
func (b *Base) UpdatePairs(exchangeProducts currency.Pairs, assetType asset.Item, enabled, force bool) error {
exchangeProducts = exchangeProducts.Upper()
var products currency.Pairs
for x := range exchangeProducts {
if exchangeProducts[x].String() == "" {
continue
}
products = append(products, exchangeProducts[x])
}
var updateType string
targetPairs, err := b.CurrencyPairs.GetPairs(assetType, enabled)
func (b *Base) UpdatePairs(incoming currency.Pairs, a asset.Item, enabled, force bool) error {
pFmt, err := b.GetPairFormat(a, false)
if err != nil {
return err
}
if enabled {
updateType = "enabled"
} else {
updateType = "available"
incoming, err = incoming.ValidateAndConform(pFmt, b.BypassConfigFormatUpgrades)
if err != nil {
return err
}
newPairs, removedPairs := targetPairs.FindDifferences(products)
if force || len(newPairs) > 0 || len(removedPairs) > 0 {
oldPairs, err := b.CurrencyPairs.GetPairs(a, enabled)
if err != nil {
return err
}
diff, err := oldPairs.FindDifferences(incoming, pFmt)
if err != nil {
return err
}
if force || len(diff.New) != 0 || len(diff.Remove) != 0 || diff.FormatDifference {
var updateType string
if enabled {
updateType = "enabled"
} else {
updateType = "available"
}
if force {
log.Debugf(log.ExchangeSys,
"%s forced update of %s [%v] pairs.",
b.Name,
updateType,
strings.ToUpper(assetType.String()))
strings.ToUpper(a.String()))
} else {
if len(newPairs) > 0 {
if len(diff.New) > 0 {
log.Debugf(log.ExchangeSys,
"%s Updating %s pairs [%v] - Added: %s.\n",
b.Name,
updateType,
strings.ToUpper(assetType.String()),
newPairs)
strings.ToUpper(a.String()),
diff.New)
}
if len(removedPairs) > 0 {
if len(diff.Remove) > 0 {
log.Debugf(log.ExchangeSys,
"%s Updating %s pairs [%v] - Removed: %s.\n",
b.Name,
updateType,
strings.ToUpper(assetType.String()),
removedPairs)
strings.ToUpper(a.String()),
diff.Remove)
}
}
b.Config.CurrencyPairs.StorePairs(assetType, products, enabled)
b.CurrencyPairs.StorePairs(assetType, products, enabled)
if !enabled {
// If available pairs are changed we will remove currency pair items
// that are still included in the enabled pairs list.
enabledPairs, err := b.CurrencyPairs.GetPairs(assetType, true)
if err == nil {
return nil
}
_, remove := enabledPairs.FindDifferences(products)
for i := range remove {
enabledPairs = enabledPairs.Remove(remove[i])
}
if len(remove) > 0 {
log.Debugf(log.ExchangeSys,
"%s Checked and updated enabled pairs [%v] - Removed: %s.\n",
b.Name,
strings.ToUpper(assetType.String()),
remove)
b.Config.CurrencyPairs.StorePairs(assetType, enabledPairs, true)
b.CurrencyPairs.StorePairs(assetType, enabledPairs, true)
}
err = b.Config.CurrencyPairs.StorePairs(a, incoming, enabled)
if err != nil {
return err
}
err = b.CurrencyPairs.StorePairs(a, incoming, enabled)
if err != nil {
return err
}
}
return nil
if enabled {
return nil
}
// This section checks for differences after an available pairs adjustment
// which will remove currency pairs from enabled pairs that have been
// disabled by an exchange, adjust the entire list of enabled pairs if there
// is a required formatting change and it will also capture unintentional
// client inputs e.g. a client can enter `linkusd` via config and loaded
// into memory that might be unintentionally formatted too `lin-kusd` it
// will match that against the correct available pair in memory and apply
// correct formatting (LINK-USD) instead of being removed altogether which
// will require a shutdown and update of the config file to enable that
// asset.
enabledPairs, err := b.CurrencyPairs.GetPairs(a, true)
if err != nil &&
!errors.Is(err, currency.ErrPairNotContainedInAvailablePairs) &&
!errors.Is(err, currency.ErrPairDuplication) {
return err
}
if err == nil && !enabledPairs.HasFormatDifference(pFmt) {
return nil
}
diff, err = enabledPairs.FindDifferences(incoming, pFmt)
if err != nil {
return err
}
check := make(map[string]bool)
var target int
for x := range enabledPairs {
pairNoFmt := currency.EMPTYFORMAT.Format(enabledPairs[x])
if check[pairNoFmt] {
diff.Remove = diff.Remove.Add(enabledPairs[x])
continue
}
check[pairNoFmt] = true
if !diff.Remove.Contains(enabledPairs[x], true) {
enabledPairs[target] = enabledPairs[x].Format(pFmt)
} else {
var match currency.Pair
match, err = incoming.DeriveFrom(pairNoFmt)
if err != nil {
continue
}
diff.Remove, err = diff.Remove.Remove(enabledPairs[x])
if err != nil {
return err
}
enabledPairs[target] = match.Format(pFmt)
}
target++
}
enabledPairs = enabledPairs[:target]
if len(enabledPairs) == 0 {
// NOTE: If enabled pairs are not populated for any reason.
var randomPair currency.Pair
randomPair, err = incoming.GetRandomPair()
if err != nil {
return err
}
log.Debugf(log.ExchangeSys, "%s Enabled pairs missing for %s. Added %s.\n",
b.Name,
strings.ToUpper(a.String()),
randomPair)
enabledPairs = currency.Pairs{randomPair}
}
if len(diff.Remove) > 0 {
log.Debugf(log.ExchangeSys, "%s Checked and updated enabled pairs [%v] - Removed: %s.\n",
b.Name,
strings.ToUpper(a.String()),
diff.Remove)
}
err = b.Config.CurrencyPairs.StorePairs(a, enabledPairs, true)
if err != nil {
return err
}
return b.CurrencyPairs.StorePairs(a, enabledPairs, true)
}
// SetAPIURL sets configuration API URL for an exchange
@@ -865,6 +989,11 @@ func (b *Base) StoreAssetPairFormat(a asset.Item, f currency.PairStore) error {
b.Name)
}
if f.ConfigFormat.Delimiter == "" {
return fmt.Errorf("exchange %s cannot set asset %s pair format %w",
b.Name, a, errConfigPairFormatRequiresDelimiter)
}
if b.CurrencyPairs.Pairs == nil {
b.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore)
}
@@ -891,6 +1020,11 @@ func (b *Base) SetGlobalPairsManager(request, config *currency.PairFormat, asset
b.Name)
}
if config.Delimiter == "" {
return fmt.Errorf("exchange %s cannot set global pairs manager %w for assets %s",
b.Name, errConfigPairFormatRequiresDelimiter, assets)
}
b.CurrencyPairs.UseGlobalFormat = true
b.CurrencyPairs.RequestFormat = request
b.CurrencyPairs.ConfigFormat = config

View File

@@ -418,7 +418,10 @@ func TestSetCurrencyPairFormat(t *testing.T) {
b := Base{
Config: &config.Exchange{},
}
b.SetCurrencyPairFormat()
err := b.SetCurrencyPairFormat()
if err != nil {
t.Fatal(err)
}
if b.Config.CurrencyPairs == nil {
t.Error("currencyPairs shouldn't be nil")
}
@@ -431,7 +434,10 @@ func TestSetCurrencyPairFormat(t *testing.T) {
}
b.CurrencyPairs.RequestFormat = pFmt
b.CurrencyPairs.ConfigFormat = pFmt
b.SetCurrencyPairFormat()
err = b.SetCurrencyPairFormat()
if err != nil {
t.Fatal(err)
}
spot, err := b.GetPairFormat(asset.Spot, true)
if err != nil {
t.Fatal(err)
@@ -444,17 +450,22 @@ func TestSetCurrencyPairFormat(t *testing.T) {
// Test individual asset type formatting logic
b.CurrencyPairs.UseGlobalFormat = false
// Store non-nil pair stores
b.CurrencyPairs.Store(asset.Spot, currency.PairStore{
ConfigFormat: &currency.PairFormat{
Delimiter: "~",
},
err = b.CurrencyPairs.Store(asset.Spot, &currency.PairStore{
ConfigFormat: &currency.PairFormat{Delimiter: "~"},
})
b.CurrencyPairs.Store(asset.Futures, currency.PairStore{
ConfigFormat: &currency.PairFormat{
Delimiter: ":)",
},
if err != nil {
t.Fatal(err)
}
err = b.CurrencyPairs.Store(asset.Futures, &currency.PairStore{
ConfigFormat: &currency.PairFormat{Delimiter: ":)"},
})
b.SetCurrencyPairFormat()
if err != nil {
t.Fatal(err)
}
err = b.SetCurrencyPairFormat()
if err != nil {
t.Fatal(err)
}
spot, err = b.GetPairFormat(asset.Spot, false)
if err != nil {
t.Fatal(err)
@@ -492,8 +503,8 @@ func TestLoadConfigPairs(t *testing.T) {
},
Pairs: map[asset.Item]*currency.PairStore{
asset.Spot: {
RequestFormat: &currency.PairFormat{},
ConfigFormat: &currency.PairFormat{},
RequestFormat: &currency.EMPTYFORMAT,
ConfigFormat: &currency.EMPTYFORMAT,
},
},
},
@@ -529,7 +540,11 @@ func TestLoadConfigPairs(t *testing.T) {
}
// Test UseGlobalFormat setting of pairs
b.SetCurrencyPairFormat()
err = b.SetCurrencyPairFormat()
if err != nil {
t.Fatal(err)
}
err = b.SetConfigPairs()
if err != nil {
t.Fatal(err)
@@ -548,7 +563,7 @@ func TestLoadConfigPairs(t *testing.T) {
t.Fatal(err)
}
p := pairs[0].Format(pFmt.Delimiter, pFmt.Uppercase).String()
p := pairs[0].Format(pFmt).String()
if p != "BTC^USD" {
t.Errorf("incorrect value, expected BTC^USD")
}
@@ -575,17 +590,23 @@ func TestLoadConfigPairs(t *testing.T) {
}
// Test !UseGlobalFormat setting of pairs
exchPS, err := b.CurrencyPairs.Get(asset.Spot)
err = b.CurrencyPairs.StoreFormat(asset.Spot, &currency.PairFormat{Delimiter: "~"}, false)
if err != nil {
t.Fatal(err)
}
err = b.CurrencyPairs.StoreFormat(asset.Spot, &currency.PairFormat{Delimiter: "/"}, true)
if err != nil {
t.Fatal(err)
}
exchPS.RequestFormat.Delimiter = "~"
exchPS.RequestFormat.Uppercase = false
exchPS.ConfigFormat.Delimiter = "/"
exchPS.ConfigFormat.Uppercase = false
pairs = append(pairs, currency.Pair{Base: currency.XRP, Quote: currency.USD})
b.Config.CurrencyPairs.StorePairs(asset.Spot, pairs, false)
b.Config.CurrencyPairs.StorePairs(asset.Spot, pairs, true)
err = b.Config.CurrencyPairs.StorePairs(asset.Spot, pairs, false)
if err != nil {
t.Fatal(err)
}
err = b.Config.CurrencyPairs.StorePairs(asset.Spot, pairs, true)
if err != nil {
t.Fatal(err)
}
b.Config.CurrencyPairs.UseGlobalFormat = false
b.CurrencyPairs.UseGlobalFormat = false
@@ -598,7 +619,7 @@ func TestLoadConfigPairs(t *testing.T) {
// 2) pair format is set for RequestFormat
// 3) pair format is set for ConfigFormat
// 4) Config pair store formats are the same as the exchanges
pFmt, err = b.GetPairFormat(asset.Spot, false)
configFmt, err := b.GetPairFormat(asset.Spot, false)
if err != nil {
t.Fatal(err)
}
@@ -606,7 +627,7 @@ func TestLoadConfigPairs(t *testing.T) {
if err != nil {
t.Fatal(err)
}
p = pairs[2].Format(pFmt.Delimiter, pFmt.Uppercase).String()
p = pairs[2].Format(configFmt).String()
if p != "xrp/usd" {
t.Error("incorrect value, expected xrp/usd", p)
}
@@ -700,13 +721,13 @@ func TestGetPairFormat(t *testing.T) {
// Test individual asset pair store formatting
b.CurrencyPairs.UseGlobalFormat = false
b.CurrencyPairs.Store(asset.Spot, currency.PairStore{
ConfigFormat: &pFmt,
RequestFormat: &currency.PairFormat{
Delimiter: "/",
Uppercase: true,
},
err = b.CurrencyPairs.Store(asset.Spot, &currency.PairStore{
ConfigFormat: &pFmt,
RequestFormat: &currency.PairFormat{Delimiter: "/", Uppercase: true},
})
if err != nil {
t.Fatal(err)
}
pFmt, err = b.GetPairFormat(asset.Spot, false)
if err != nil {
t.Fatal(err)
@@ -735,8 +756,14 @@ func TestGetEnabledPairs(t *testing.T) {
t.Fatal(err)
}
b.CurrencyPairs.StorePairs(asset.Spot, defaultPairs, true)
b.CurrencyPairs.StorePairs(asset.Spot, defaultPairs, false)
err = b.CurrencyPairs.StorePairs(asset.Spot, defaultPairs, true)
if err != nil {
t.Fatal(err)
}
err = b.CurrencyPairs.StorePairs(asset.Spot, defaultPairs, false)
if err != nil {
t.Fatal(err)
}
format := currency.PairFormat{
Delimiter: "-",
Index: "",
@@ -786,8 +813,14 @@ func TestGetEnabledPairs(t *testing.T) {
t.Fatal(err)
}
b.CurrencyPairs.StorePairs(asset.Spot, btcdoge, true)
b.CurrencyPairs.StorePairs(asset.Spot, btcdoge, false)
err = b.CurrencyPairs.StorePairs(asset.Spot, btcdoge, true)
if err != nil {
t.Fatal(err)
}
err = b.CurrencyPairs.StorePairs(asset.Spot, btcdoge, false)
if err != nil {
t.Fatal(err)
}
format.Index = currency.BTC.String()
b.CurrencyPairs.ConfigFormat = &format
c, err = b.GetEnabledPairs(asset.Spot)
@@ -803,8 +836,14 @@ func TestGetEnabledPairs(t *testing.T) {
t.Fatal(err)
}
b.CurrencyPairs.StorePairs(asset.Spot, btcusdUnderscore, true)
b.CurrencyPairs.StorePairs(asset.Spot, btcusdUnderscore, false)
err = b.CurrencyPairs.StorePairs(asset.Spot, btcusdUnderscore, true)
if err != nil {
t.Fatal(err)
}
err = b.CurrencyPairs.StorePairs(asset.Spot, btcusdUnderscore, false)
if err != nil {
t.Fatal(err)
}
b.CurrencyPairs.RequestFormat.Delimiter = ""
b.CurrencyPairs.ConfigFormat.Delimiter = "_"
c, err = b.GetEnabledPairs(asset.Spot)
@@ -815,8 +854,14 @@ func TestGetEnabledPairs(t *testing.T) {
t.Error("Exchange GetAvailablePairs() incorrect string")
}
b.CurrencyPairs.StorePairs(asset.Spot, btcdoge, true)
b.CurrencyPairs.StorePairs(asset.Spot, btcdoge, false)
err = b.CurrencyPairs.StorePairs(asset.Spot, btcdoge, true)
if err != nil {
t.Fatal(err)
}
err = b.CurrencyPairs.StorePairs(asset.Spot, btcdoge, false)
if err != nil {
t.Fatal(err)
}
b.CurrencyPairs.RequestFormat.Delimiter = ""
b.CurrencyPairs.ConfigFormat.Delimiter = ""
b.CurrencyPairs.ConfigFormat.Index = currency.BTC.String()
@@ -833,8 +878,14 @@ func TestGetEnabledPairs(t *testing.T) {
t.Fatal(err)
}
b.CurrencyPairs.StorePairs(asset.Spot, btcusd, true)
b.CurrencyPairs.StorePairs(asset.Spot, btcusd, false)
err = b.CurrencyPairs.StorePairs(asset.Spot, btcusd, true)
if err != nil {
t.Fatal(err)
}
err = b.CurrencyPairs.StorePairs(asset.Spot, btcusd, false)
if err != nil {
t.Fatal(err)
}
b.CurrencyPairs.ConfigFormat.Index = ""
c, err = b.GetEnabledPairs(asset.Spot)
if err != nil {
@@ -857,7 +908,10 @@ func TestGetAvailablePairs(t *testing.T) {
t.Fatal(err)
}
b.CurrencyPairs.StorePairs(asset.Spot, defaultPairs, false)
err = b.CurrencyPairs.StorePairs(asset.Spot, defaultPairs, false)
if err != nil {
t.Fatal(err)
}
format := currency.PairFormat{
Delimiter: "-",
Index: "",
@@ -905,7 +959,11 @@ func TestGetAvailablePairs(t *testing.T) {
t.Fatal(err)
}
b.CurrencyPairs.StorePairs(asset.Spot, dogePairs, false)
err = b.CurrencyPairs.StorePairs(asset.Spot, dogePairs, false)
if err != nil {
t.Fatal(err)
}
format.Index = currency.BTC.String()
b.CurrencyPairs.ConfigFormat = &format
c, err = b.GetAvailablePairs(assetType)
@@ -922,7 +980,11 @@ func TestGetAvailablePairs(t *testing.T) {
t.Fatal(err)
}
b.CurrencyPairs.StorePairs(asset.Spot, btcusdUnderscore, false)
err = b.CurrencyPairs.StorePairs(asset.Spot, btcusdUnderscore, false)
if err != nil {
t.Fatal(err)
}
b.CurrencyPairs.RequestFormat.Delimiter = ""
b.CurrencyPairs.ConfigFormat.Delimiter = "_"
c, err = b.GetAvailablePairs(assetType)
@@ -934,7 +996,11 @@ func TestGetAvailablePairs(t *testing.T) {
t.Error("Exchange GetAvailablePairs() incorrect string")
}
b.CurrencyPairs.StorePairs(asset.Spot, dogePairs, false)
err = b.CurrencyPairs.StorePairs(asset.Spot, dogePairs, false)
if err != nil {
t.Fatal(err)
}
b.CurrencyPairs.RequestFormat.Delimiter = ""
b.CurrencyPairs.ConfigFormat.Delimiter = "_"
b.CurrencyPairs.ConfigFormat.Index = currency.BTC.String()
@@ -952,7 +1018,11 @@ func TestGetAvailablePairs(t *testing.T) {
t.Fatal(err)
}
b.CurrencyPairs.StorePairs(asset.Spot, btcusd, false)
err = b.CurrencyPairs.StorePairs(asset.Spot, btcusd, false)
if err != nil {
t.Fatal(err)
}
b.CurrencyPairs.ConfigFormat.Index = ""
c, err = b.GetAvailablePairs(assetType)
if err != nil {
@@ -984,14 +1054,20 @@ func TestSupportsPair(t *testing.T) {
t.Fatal(err)
}
b.CurrencyPairs.StorePairs(asset.Spot, pairs, false)
err = b.CurrencyPairs.StorePairs(asset.Spot, pairs, false)
if err != nil {
t.Fatal(err)
}
defaultpairs, err := currency.NewPairsFromStrings([]string{defaultTestCurrencyPair})
if err != nil {
t.Fatal(err)
}
b.CurrencyPairs.StorePairs(asset.Spot, defaultpairs, true)
err = b.CurrencyPairs.StorePairs(asset.Spot, defaultpairs, true)
if err != nil {
t.Fatal(err)
}
format := &currency.PairFormat{
Delimiter: "-",
@@ -1161,13 +1237,12 @@ func TestSetupDefaults(t *testing.T) {
if err != nil {
t.Fatal(err)
}
b.CurrencyPairs.Store(asset.Spot,
currency.PairStore{
Enabled: currency.Pairs{
p,
},
},
)
err = b.CurrencyPairs.Store(asset.Spot, &currency.PairStore{
Enabled: currency.Pairs{p},
})
if err != nil {
t.Fatal(err)
}
err = b.SetupDefaults(&cfg)
if err != nil {
t.Fatal(err)
@@ -1289,6 +1364,8 @@ func TestUpdatePairs(t *testing.T) {
AssetEnabled: convert.BoolPtr(true),
},
},
ConfigFormat: &currency.PairFormat{Uppercase: true, Delimiter: currency.DashDelimiter},
UseGlobalFormat: true,
},
}
UAC.Config = exchCfg
@@ -1363,7 +1440,7 @@ func TestUpdatePairs(t *testing.T) {
}
err = UAC.UpdatePairs(exchangeProducts, asset.Spot, false, false)
if err != nil {
t.Errorf("Forced Exchange UpdatePairs() error: %s", err)
t.Errorf("Exchange UpdatePairs() error: %s", err)
}
// Test empty pair
@@ -1371,18 +1448,29 @@ func TestUpdatePairs(t *testing.T) {
if err != nil {
t.Fatal(err)
}
pairs := currency.Pairs{
currency.EMPTYPAIR,
p,
}
pairs := currency.Pairs{currency.EMPTYPAIR, p}
err = UAC.UpdatePairs(pairs, asset.Spot, true, true)
if err != nil {
t.Errorf("Forced Exchange UpdatePairs() error: %s", err)
if !errors.Is(err, currency.ErrCurrencyPairEmpty) {
t.Fatalf("received: '%v' but expected: '%v'", err, currency.ErrCurrencyPairEmpty)
}
pairs = currency.Pairs{p, p}
err = UAC.UpdatePairs(pairs, asset.Spot, false, true)
if err != nil {
t.Errorf("Forced Exchange UpdatePairs() error: %s", err)
if !errors.Is(err, currency.ErrPairDuplication) {
t.Fatalf("received: '%v' but expected: '%v'", err, currency.ErrPairDuplication)
}
pairs = currency.Pairs{p}
err = UAC.UpdatePairs(pairs, asset.Spot, false, true)
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
err = UAC.UpdatePairs(pairs, asset.Spot, true, true)
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
UAC.CurrencyPairs.UseGlobalFormat = true
UAC.CurrencyPairs.ConfigFormat = &currency.PairFormat{
Delimiter: "-",
@@ -1395,6 +1483,86 @@ func TestUpdatePairs(t *testing.T) {
if !uacPairs.Contains(p, true) {
t.Fatal("expected currency pair not found")
}
pairs = currency.Pairs{
currency.NewPair(currency.XRP, currency.USD),
currency.NewPair(currency.BTC, currency.USD),
currency.NewPair(currency.LTC, currency.USD),
currency.NewPair(currency.LTC, currency.USDT),
}
err = UAC.UpdatePairs(pairs, asset.Spot, true, true)
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
pairs = currency.Pairs{
currency.NewPair(currency.WABI, currency.USD),
currency.NewPair(currency.EASY, currency.USD),
currency.NewPair(currency.LARIX, currency.USD),
currency.NewPair(currency.LTC, currency.USDT),
}
err = UAC.UpdatePairs(pairs, asset.Spot, false, true)
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
uacEnabledPairs, err := UAC.GetEnabledPairs(asset.Spot)
if err != nil {
t.Fatal(err)
}
if uacEnabledPairs.Contains(currency.NewPair(currency.XRP, currency.USD), true) {
t.Fatal("expected currency pair not found")
}
if uacEnabledPairs.Contains(currency.NewPair(currency.BTC, currency.USD), true) {
t.Fatal("expected currency pair not found")
}
if uacEnabledPairs.Contains(currency.NewPair(currency.LTC, currency.USD), true) {
t.Fatal("expected currency pair not found")
}
if !uacEnabledPairs.Contains(currency.NewPair(currency.LTC, currency.USDT), true) {
t.Fatal("expected currency pair not found")
}
// This should be matched and formatted to `link-usd`
unintentionalInput, err := currency.NewPairFromString("linkusd")
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
pairs = currency.Pairs{
currency.NewPair(currency.WABI, currency.USD),
currency.NewPair(currency.EASY, currency.USD),
currency.NewPair(currency.LARIX, currency.USD),
currency.NewPair(currency.LTC, currency.USDT),
unintentionalInput,
}
err = UAC.UpdatePairs(pairs, asset.Spot, true, true)
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
pairs = currency.Pairs{
currency.NewPair(currency.WABI, currency.USD),
currency.NewPair(currency.EASY, currency.USD),
currency.NewPair(currency.LARIX, currency.USD),
currency.NewPair(currency.LTC, currency.USDT),
currency.NewPair(currency.LINK, currency.USD),
}
err = UAC.UpdatePairs(pairs, asset.Spot, false, true)
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
uacEnabledPairs, err = UAC.GetEnabledPairs(asset.Spot)
if err != nil {
t.Fatal(err)
}
if !uacEnabledPairs.Contains(currency.NewPair(currency.LINK, currency.USD), true) {
t.Fatalf("received: '%v' but expected: '%v'", false, true)
}
}
func TestSupportsWebsocket(t *testing.T) {
@@ -1604,7 +1772,10 @@ func TestGetFormattedPairAndAssetType(t *testing.T) {
b := Base{
Config: &config.Exchange{},
}
b.SetCurrencyPairFormat()
err := b.SetCurrencyPairFormat()
if err != nil {
t.Fatal(err)
}
b.Config.CurrencyPairs.UseGlobalFormat = true
b.CurrencyPairs.UseGlobalFormat = true
pFmt := &currency.PairFormat{
@@ -1662,13 +1833,20 @@ func TestStoreAssetPairFormat(t *testing.T) {
err = b.StoreAssetPairFormat(asset.Spot, currency.PairStore{
RequestFormat: &currency.PairFormat{Uppercase: true},
ConfigFormat: &currency.PairFormat{Uppercase: true}})
if !errors.Is(err, errConfigPairFormatRequiresDelimiter) {
t.Fatalf("received: '%v' but expected: '%v'", err, errConfigPairFormatRequiresDelimiter)
}
err = b.StoreAssetPairFormat(asset.Futures, currency.PairStore{
RequestFormat: &currency.PairFormat{Uppercase: true},
ConfigFormat: &currency.PairFormat{Uppercase: true, Delimiter: currency.DashDelimiter}})
if err != nil {
t.Error(err)
}
err = b.StoreAssetPairFormat(asset.Futures, currency.PairStore{
RequestFormat: &currency.PairFormat{Uppercase: true},
ConfigFormat: &currency.PairFormat{Uppercase: true}})
ConfigFormat: &currency.PairFormat{Uppercase: true, Delimiter: currency.DashDelimiter}})
if err != nil {
t.Error(err)
}
@@ -1702,7 +1880,17 @@ func TestSetGlobalPairsManager(t *testing.T) {
}
err = b.SetGlobalPairsManager(&currency.PairFormat{Uppercase: true},
&currency.PairFormat{Uppercase: true}, asset.Spot, asset.Binary)
&currency.PairFormat{Uppercase: true},
asset.Spot,
asset.Binary)
if !errors.Is(err, errConfigPairFormatRequiresDelimiter) {
t.Fatalf("received: '%v' but expected: '%v'", err, errConfigPairFormatRequiresDelimiter)
}
err = b.SetGlobalPairsManager(&currency.PairFormat{Uppercase: true},
&currency.PairFormat{Uppercase: true, Delimiter: currency.DashDelimiter},
asset.Spot,
asset.Binary)
if err != nil {
t.Error(err)
}
@@ -2164,10 +2352,11 @@ func TestAssetWebsocketFunctionality(t *testing.T) {
},
ConfigFormat: &currency.PairFormat{
Uppercase: true,
Delimiter: currency.DashDelimiter,
},
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
err = b.DisableAssetWebsocketSupport(asset.Spot)
@@ -2372,3 +2561,72 @@ func TestIsPerpetualFutureCurrency(t *testing.T) {
t.Errorf("received: %v, expected: %v", err, common.ErrNotYetImplemented)
}
}
func TestGetPairAndAssetTypeRequestFormatted(t *testing.T) {
t.Parallel()
expected := currency.Pair{Base: currency.BTC, Quote: currency.USDT}
enabledPairs := currency.Pairs{expected}
availablePairs := currency.Pairs{
currency.Pair{Base: currency.BTC, Quote: currency.USDT},
currency.Pair{Base: currency.BTC, Quote: currency.AUD},
}
b := Base{
CurrencyPairs: currency.PairsManager{
Pairs: map[asset.Item]*currency.PairStore{
asset.Spot: {
AssetEnabled: convert.BoolPtr(true),
Enabled: enabledPairs,
Available: availablePairs,
RequestFormat: &currency.PairFormat{Delimiter: "-", Uppercase: true},
ConfigFormat: &currency.EMPTYFORMAT,
},
asset.PerpetualContract: {
AssetEnabled: convert.BoolPtr(true),
Enabled: enabledPairs,
Available: availablePairs,
RequestFormat: &currency.PairFormat{Delimiter: "_", Uppercase: true},
ConfigFormat: &currency.EMPTYFORMAT,
},
},
},
}
_, _, err := b.GetPairAndAssetTypeRequestFormatted("")
if !errors.Is(err, currency.ErrCurrencyPairEmpty) {
t.Fatalf("received: '%v' but expected: '%v'", err, currency.ErrCurrencyPairEmpty)
}
_, _, err = b.GetPairAndAssetTypeRequestFormatted("BTCAUD")
if !errors.Is(err, errSymbolCannotBeMatched) {
t.Fatalf("received: '%v' but expected: '%v'", err, errSymbolCannotBeMatched)
}
_, _, err = b.GetPairAndAssetTypeRequestFormatted("BTCUSDT")
if !errors.Is(err, errSymbolCannotBeMatched) {
t.Fatalf("received: '%v' but expected: '%v'", err, errSymbolCannotBeMatched)
}
p, a, err := b.GetPairAndAssetTypeRequestFormatted("BTC-USDT")
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
if a != asset.Spot {
t.Fatal("unexpected value", a)
}
if !p.Equal(expected) {
t.Fatalf("received: '%v' but expected: '%v'", p, expected)
}
p, a, err = b.GetPairAndAssetTypeRequestFormatted("BTC_USDT")
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
if a != asset.PerpetualContract {
t.Fatal("unexpected value", a)
}
if !p.Equal(expected) {
t.Fatalf("received: '%v' but expected: '%v'", p, expected)
}
}

View File

@@ -1348,8 +1348,12 @@ func TestGetHistoricTrades(t *testing.T) {
if err != nil {
t.Fatal(err)
}
rPair, err := enabledPairs.GetRandomPair()
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
_, err = f.GetHistoricTrades(context.Background(),
enabledPairs.GetRandomPair(),
rPair,
assets[i],
time.Now().Add(-time.Minute*15),
time.Now())
@@ -1367,8 +1371,12 @@ func TestGetRecentTrades(t *testing.T) {
if err != nil {
t.Fatal(err)
}
rPair, err := enabledPairs.GetRandomPair()
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
_, err = f.GetRecentTrades(context.Background(),
enabledPairs.GetRandomPair(), assets[i])
rPair, assets[i])
if err != nil {
t.Error(err)
}

View File

@@ -2067,7 +2067,7 @@ func (f *FTX) GetFundingRates(ctx context.Context, request *order.FundingRatesRe
if err != nil {
return nil, err
}
request.Pairs = request.Pairs.Format(pairFmt.Delimiter, pairFmt.Index, pairFmt.Uppercase)
request.Pairs = request.Pairs.Format(pairFmt)
response := make([]order.FundingRates, 0, len(request.Pairs))
for x := range request.Pairs {
var isPerp bool

View File

@@ -174,7 +174,10 @@ func TestUpdateTicker(t *testing.T) {
if err != nil {
t.Fatal(err)
}
h.CurrencyPairs.StorePairs(asset.Spot, pairs, true)
err = h.CurrencyPairs.StorePairs(asset.Spot, pairs, true)
if err != nil {
t.Fatal(err)
}
_, err = h.UpdateTicker(context.Background(),
currency.NewPair(currency.BTC, currency.USD),
asset.Spot)

View File

@@ -1219,7 +1219,7 @@ func (h *HUOBI) formatFuturesCode(p currency.Code) (string, error) {
// formatFuturesPair handles pairs in the format as "BTC-NW" and "BTC210827"
func (h *HUOBI) formatFuturesPair(p currency.Pair) (string, error) {
if common.StringDataCompareInsensitive(validContractShortTypes, p.Quote.String()) {
return p.Format("_", true).String(), nil
return p.Format(currency.PairFormat{Delimiter: "_", Uppercase: true}).String(), nil
}
return h.FormatSymbol(p, asset.Futures)
}

View File

@@ -2752,7 +2752,10 @@ func TestFormatFuturesPair(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if r != availInstruments[0] {
t.Errorf("expected %s, got %s", availInstruments[0], r)
// Test for upper case 'BTC' not lower case 'btc', disregarded numerals
// as they not deterministic from this endpoint.
if !strings.Contains(r, "BTC") {
t.Errorf("expected %s, got %s", "BTC220708", r)
}
}

View File

@@ -74,6 +74,7 @@ func (h *HUOBI) SetDefaults() {
},
ConfigFormat: &currency.PairFormat{
Uppercase: true,
Delimiter: currency.DashDelimiter,
},
}
futures := currency.PairStore{
@@ -82,6 +83,7 @@ func (h *HUOBI) SetDefaults() {
},
ConfigFormat: &currency.PairFormat{
Uppercase: true,
Delimiter: currency.DashDelimiter,
},
}
err := h.StoreAssetPairFormat(asset.Spot, fmt1)

View File

@@ -59,7 +59,7 @@ func (i *ItBit) SetDefaults() {
i.API.CredentialsValidator.RequiresSecret = true
requestFmt := &currency.PairFormat{Uppercase: true}
configFmt := &currency.PairFormat{Uppercase: true}
configFmt := &currency.PairFormat{Uppercase: true, Delimiter: currency.DashDelimiter}
err := i.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot)
if err != nil {
log.Errorln(log.ExchangeSys, err)
@@ -67,8 +67,7 @@ func (i *ItBit) SetDefaults() {
i.Features = exchange.Features{
Supports: exchange.FeaturesSupported{
REST: true,
Websocket: false,
REST: true,
RESTCapabilities: protocol.Features{
TickerFetching: true,
TradeFetching: true,
@@ -88,9 +87,6 @@ func (i *ItBit) SetDefaults() {
WithdrawPermissions: exchange.WithdrawCryptoViaWebsiteOnly |
exchange.WithdrawFiatViaWebsiteOnly,
},
Enabled: exchange.FeaturesEnabled{
AutoPairUpdates: false,
},
}
i.Requester, err = request.New(i.Name,

View File

@@ -61,7 +61,9 @@ func (l *LocalBitcoins) SetDefaults() {
l.API.CredentialsValidator.RequiresSecret = true
requestFmt := &currency.PairFormat{Uppercase: true}
configFmt := &currency.PairFormat{Uppercase: true}
configFmt := &currency.PairFormat{
Uppercase: true,
Delimiter: currency.DashDelimiter}
err := l.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot)
if err != nil {
log.Errorln(log.ExchangeSys, err)

View File

@@ -370,7 +370,7 @@ func TestPlaceMultipleSpotOrdersOverPairLimits(t *testing.T) {
}
for x := range pairs {
ord.InstrumentID = pairs[x].Format("-", false).String()
ord.InstrumentID = pairs[x].Format(currency.PairFormat{Delimiter: "-"}).String()
request = append(request, ord)
}
@@ -674,7 +674,7 @@ func TestPlaceMultipleMarginOrdersOverPairLimits(t *testing.T) {
}
for x := range pairs {
ord.InstrumentID = pairs[x].Format("-", false).String()
ord.InstrumentID = pairs[x].Format(currency.PairFormat{Delimiter: "-"}).String()
request = append(request, ord)
}

View File

@@ -500,7 +500,7 @@ func TestPlaceMultipleSpotOrdersOverPairLimits(t *testing.T) {
}
for x := range pairs {
ord.InstrumentID = pairs[x].Format("-", false).String()
ord.InstrumentID = pairs[x].Format(currency.PairFormat{Delimiter: "-"}).String()
request = append(request, ord)
}
@@ -884,7 +884,7 @@ func TestPlaceMultipleMarginOrdersOverPairLimits(t *testing.T) {
}
for x := range pairs {
ord.InstrumentID = pairs[x].Format("-", false).String()
ord.InstrumentID = pairs[x].Format(currency.PairFormat{Delimiter: "-"}).String()
request = append(request, ord)
}

View File

@@ -98,6 +98,7 @@ func (o *OKEX) SetDefaults() {
},
ConfigFormat: &currency.PairFormat{
Uppercase: true,
Delimiter: currency.DashDelimiter,
},
}

View File

@@ -1054,7 +1054,7 @@ func TestUpdateByIDAndAction(t *testing.T) {
t.Fatal("did not adjust ask item placement and details")
}
book.LoadSnapshot(append(bids[:0:0], bids...), append(bids[:0:0], bids...), 0, time.Time{}, true) //nolint:gocritic
book.LoadSnapshot(append(bids[:0:0], bids...), append(asks[:0:0], asks...), 0, time.Time{}, true) //nolint:gocritic
// Delete - not found
err = holder.updateByIDAndAction(&orderbook.Update{
Action: orderbook.Delete,
@@ -1070,7 +1070,7 @@ func TestUpdateByIDAndAction(t *testing.T) {
t.Fatalf("received: '%v' but expected: '%v'", err, errDeleteFailure)
}
book.LoadSnapshot(append(bids[:0:0], bids...), append(bids[:0:0], bids...), 0, time.Time{}, true) //nolint:gocritic
book.LoadSnapshot(append(bids[:0:0], bids...), append(asks[:0:0], asks...), 0, time.Time{}, true) //nolint:gocritic
// Delete - found
err = holder.updateByIDAndAction(&orderbook.Update{
Action: orderbook.Delete,

View File

@@ -263,7 +263,7 @@ func (z *ZB) UpdateTickers(ctx context.Context, a asset.Item) error {
for x := range enabledPairs {
// We can't use either pair format here, so format it to lower-
// case and without any delimiter
curr := enabledPairs[x].Format("", false).String()
curr := enabledPairs[x].Format(currency.EMPTYFORMAT).String()
if _, ok := result[curr]; !ok {
continue
}