From 7b75c7e9de349aecd69ded2883d72a2b0cfc155b Mon Sep 17 00:00:00 2001 From: Ryan O'Hara-Reid Date: Tue, 28 May 2024 11:42:17 +1000 Subject: [PATCH 01/11] currency: translation and matching pairs --- currency/code_types.go | 1 + currency/translation.go | 80 +++++++++++++++++++++++++ currency/translation_test.go | 96 ++++++++++++++++++++++++++++++ exchanges/exchange_types.go | 1 + exchanges/kucoin/kucoin_wrapper.go | 6 ++ 5 files changed, 184 insertions(+) diff --git a/currency/code_types.go b/currency/code_types.go index a52f5b53adc..f64d7b6c7c5 100644 --- a/currency/code_types.go +++ b/currency/code_types.go @@ -3070,6 +3070,7 @@ var ( WIF = NewCode("WIF") AIDOGE = NewCode("AIDOGE") PEPE = NewCode("PEPE") + USDCM = NewCode("USDCM") stables = Currencies{ USDT, diff --git a/currency/translation.go b/currency/translation.go index 7948ef085ee..9e281a1a2ea 100644 --- a/currency/translation.go +++ b/currency/translation.go @@ -20,3 +20,83 @@ var translations = map[*Item]Code{ XDG.Item: DOGE, USDT.Item: USD, } + +// NewTranslations returns a new translation map, the key indicates the exchange +// representation and the value indicates the internal representation/common/standard +// representation. e.g. XBT as key and BTC as value, this is useful for exchanges +// that use different naming conventions. +func NewTranslations(t map[Code]Code) Translations { + lookup := make(map[*Item]Code) + for k, v := range t { + lookup[k.Item] = v + } + return lookup +} + +// Translations is a map of translations for a specific exchange implementation +type Translations map[*Item]Code + +// Translate returns the translated currency code, usually used to convert +// exchange specific currency codes to common currency codes. If no translation +// is found it will return the original currency code. +func (t Translations) Translate(incoming Code) Code { + if len(t) == 0 { + return incoming + } + val, ok := (t)[incoming.Item] + if !ok { + return incoming + } + return val +} + +// Translator is an interface for translating currency codes +type Translator interface { + // TODO: Add a asset.Item param so that we can translate for asset + // permutations. Also return error. + Translate(Code) Code +} + +// PairsWithTranslation is a pair list with a translator for a specific exchange. +type PairsWithTranslation struct { + Pairs Pairs + Translator Translator +} + +// keyPair defines an immutable pair for lookup purposes +type keyPair struct { + Base *Item + Quote *Item +} + +// FindMatchingPairsBetween returns all pairs that match the incoming pairs. +// Translator is used to convert exchange specific currency codes to common +// currency codes used in lookup process. The pairs are not modified. So that +// the original pairs are returned for deployment to the specific exchange. +// NOTE: Translator is optional and can be nil. Translator can be obtained from +// the exchange implementation by calling Base() method and accessing Features +// and Translation fields. +func FindMatchingPairsBetween(this, that PairsWithTranslation) map[Pair]Pair { + lookup := make(map[keyPair]*Pair) + var k keyPair + for i := range this.Pairs { + if this.Translator != nil { + k = keyPair{Base: this.Translator.Translate(this.Pairs[i].Base).Item, Quote: this.Translator.Translate(this.Pairs[i].Quote).Item} + lookup[k] = &this.Pairs[i] + continue + } + lookup[keyPair{Base: this.Pairs[i].Base.Item, Quote: this.Pairs[i].Quote.Item}] = &this.Pairs[i] + } + outgoing := make(map[Pair]Pair) + for i := range that.Pairs { + if that.Translator != nil { + k = keyPair{Base: that.Translator.Translate(that.Pairs[i].Base).Item, Quote: that.Translator.Translate(that.Pairs[i].Quote).Item} + } else { + k = keyPair{Base: that.Pairs[i].Base.Item, Quote: that.Pairs[i].Quote.Item} + } + if p, ok := lookup[k]; ok { + outgoing[*p] = that.Pairs[i] + } + } + return outgoing +} diff --git a/currency/translation_test.go b/currency/translation_test.go index d1254bfdb3f..29500d6ab72 100644 --- a/currency/translation_test.go +++ b/currency/translation_test.go @@ -2,6 +2,8 @@ package currency import ( "testing" + + "github.com/stretchr/testify/require" ) func TestGetTranslation(t *testing.T) { @@ -34,3 +36,97 @@ func TestGetTranslation(t *testing.T) { t.Errorf("received: '%v', but expected: '%v'", actual, XBT) } } + +func TestNewTranslations(t *testing.T) { + t.Parallel() + translations := NewTranslations(map[Code]Code{ + XBT: BTC, + XETH: ETH, + XDG: DOGE, + USDM: USD, + }) + require.NotNil(t, translations) + + if !translations.Translate(XBT).Equal(BTC) { + t.Error("NewTranslations: translation failed") + } + + if !translations.Translate(LTC).Equal(LTC) { + t.Error("NewTranslations: translation failed") + } +} + +func TestFindMatchingPairsBetween(t *testing.T) { + t.Parallel() + ltcusd := NewPair(LTC, USD) + + spotPairs := Pairs{ + NewPair(BTC, USD), + NewPair(ETH, USD), + NewPair(ETH, BTC), + ltcusd, + } + + futuresPairs := Pairs{ + NewPair(XBT, USDM), + NewPair(XETH, USDM), + NewPair(XETH, BTCM), + ltcusd, // exact match + NewPair(XRP, USDM), // no match + } + + matchingPairs := FindMatchingPairsBetween(PairsWithTranslation{spotPairs, nil}, PairsWithTranslation{futuresPairs, nil}) + require.Len(t, matchingPairs, 1) + + if !matchingPairs[ltcusd].Equal(ltcusd) { + t.Error("FindMatchingPairsBetween: matching pair not found") + } + + translations := NewTranslations(map[Code]Code{ + XBT: BTC, + XETH: ETH, + XDG: DOGE, + USDM: USD, + BTCM: BTC, + }) + + matchingPairs = FindMatchingPairsBetween(PairsWithTranslation{spotPairs, nil}, PairsWithTranslation{futuresPairs, translations}) + require.Len(t, matchingPairs, 4) + + matchingPairs = FindMatchingPairsBetween(PairsWithTranslation{spotPairs, translations}, PairsWithTranslation{futuresPairs, translations}) + require.Len(t, matchingPairs, 4) + + matchingPairs = FindMatchingPairsBetween(PairsWithTranslation{spotPairs, translations}, PairsWithTranslation{futuresPairs, nil}) + require.Len(t, matchingPairs, 1) +} + +func BenchmarkFindMatchingPairsBetween(b *testing.B) { + ltcusd := NewPair(LTC, USD) + + spotPairs := Pairs{ + NewPair(BTC, USD), + NewPair(ETH, USD), + NewPair(ETH, BTC), + ltcusd, + } + + futuresPairs := Pairs{ + NewPair(XBT, USDM), + NewPair(XETH, USDM), + NewPair(XETH, BTCM), + ltcusd, // exact match + NewPair(XRP, USDM), // no match + } + + translations := NewTranslations(map[Code]Code{ + XBT: BTC, + XETH: ETH, + XDG: DOGE, + USDM: USD, + BTCM: BTC, + }) + + for i := 0; i < b.N; i++ { + _ = FindMatchingPairsBetween(PairsWithTranslation{spotPairs, translations}, PairsWithTranslation{futuresPairs, translations}) + } +} diff --git a/exchanges/exchange_types.go b/exchanges/exchange_types.go index be50855c91e..42a5c301dfd 100644 --- a/exchanges/exchange_types.go +++ b/exchanges/exchange_types.go @@ -153,6 +153,7 @@ type Features struct { Supports FeaturesSupported Enabled FeaturesEnabled Subscriptions []*subscription.Subscription + Translation currency.Translations } // FeaturesEnabled stores the exchange enabled features diff --git a/exchanges/kucoin/kucoin_wrapper.go b/exchanges/kucoin/kucoin_wrapper.go index 49773b17d76..fc6e52ff1ac 100644 --- a/exchanges/kucoin/kucoin_wrapper.go +++ b/exchanges/kucoin/kucoin_wrapper.go @@ -66,6 +66,12 @@ func (ku *Kucoin) SetDefaults() { log.Errorln(log.ExchangeSys, err) } ku.Features = exchange.Features{ + Translation: currency.NewTranslations(map[currency.Code]currency.Code{ + currency.XBT: currency.BTC, + currency.USDTM: currency.USDT, + currency.USDM: currency.USD, + currency.USDCM: currency.USDC, + }), Supports: exchange.FeaturesSupported{ REST: true, Websocket: true, From 5254d834aab3b16dad2edc139d6e136cd8513566 Mon Sep 17 00:00:00 2001 From: Ryan O'Hara-Reid Date: Fri, 14 Jun 2024 11:41:00 +1000 Subject: [PATCH 02/11] Update exchanges/exchange_types.go Co-authored-by: Scott --- exchanges/exchange_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchanges/exchange_types.go b/exchanges/exchange_types.go index 42a5c301dfd..e4c4be64fd6 100644 --- a/exchanges/exchange_types.go +++ b/exchanges/exchange_types.go @@ -153,7 +153,7 @@ type Features struct { Supports FeaturesSupported Enabled FeaturesEnabled Subscriptions []*subscription.Subscription - Translation currency.Translations + CurrencyTranslations currency.Translations } // FeaturesEnabled stores the exchange enabled features From 7cf73c98348d9098013db53199594cf30043c140 Mon Sep 17 00:00:00 2001 From: Ryan O'Hara-Reid Date: Fri, 14 Jun 2024 12:02:37 +1000 Subject: [PATCH 03/11] glorious: nits --- currency/translation.go | 6 ++-- currency/translation_test.go | 55 ++++++++++++++++++++++++------ exchanges/kucoin/kucoin_wrapper.go | 2 +- 3 files changed, 48 insertions(+), 15 deletions(-) diff --git a/currency/translation.go b/currency/translation.go index 9e281a1a2ea..eb0ec4c55b6 100644 --- a/currency/translation.go +++ b/currency/translation.go @@ -21,6 +21,9 @@ var translations = map[*Item]Code{ USDT.Item: USD, } +// Translations is a map of translations for a specific exchange implementation +type Translations map[*Item]Code + // NewTranslations returns a new translation map, the key indicates the exchange // representation and the value indicates the internal representation/common/standard // representation. e.g. XBT as key and BTC as value, this is useful for exchanges @@ -33,9 +36,6 @@ func NewTranslations(t map[Code]Code) Translations { return lookup } -// Translations is a map of translations for a specific exchange implementation -type Translations map[*Item]Code - // Translate returns the translated currency code, usually used to convert // exchange specific currency codes to common currency codes. If no translation // is found it will return the original currency code. diff --git a/currency/translation_test.go b/currency/translation_test.go index 29500d6ab72..2afc47d3ad9 100644 --- a/currency/translation_test.go +++ b/currency/translation_test.go @@ -39,7 +39,7 @@ func TestGetTranslation(t *testing.T) { func TestNewTranslations(t *testing.T) { t.Parallel() - translations := NewTranslations(map[Code]Code{ + translationsTest := NewTranslations(map[Code]Code{ XBT: BTC, XETH: ETH, XDG: DOGE, @@ -47,11 +47,11 @@ func TestNewTranslations(t *testing.T) { }) require.NotNil(t, translations) - if !translations.Translate(XBT).Equal(BTC) { + if !translationsTest.Translate(XBT).Equal(BTC) { t.Error("NewTranslations: translation failed") } - if !translations.Translate(LTC).Equal(LTC) { + if !translationsTest.Translate(LTC).Equal(LTC) { t.Error("NewTranslations: translation failed") } } @@ -61,17 +61,17 @@ func TestFindMatchingPairsBetween(t *testing.T) { ltcusd := NewPair(LTC, USD) spotPairs := Pairs{ - NewPair(BTC, USD), + NewPair(BTC, USD).Format(PairFormat{Delimiter: "DELIMITER"}), NewPair(ETH, USD), - NewPair(ETH, BTC), + NewPair(ETH, BTC).Format(PairFormat{Delimiter: "DELIMITER"}), ltcusd, } futuresPairs := Pairs{ NewPair(XBT, USDM), - NewPair(XETH, USDM), + NewPair(XETH, USDM).Format(PairFormat{Delimiter: "DELIMITER"}), NewPair(XETH, BTCM), - ltcusd, // exact match + ltcusd.Format(PairFormat{Delimiter: "DELIMITER"}), // exact match NewPair(XRP, USDM), // no match } @@ -82,7 +82,7 @@ func TestFindMatchingPairsBetween(t *testing.T) { t.Error("FindMatchingPairsBetween: matching pair not found") } - translations := NewTranslations(map[Code]Code{ + translationsTest := NewTranslations(map[Code]Code{ XBT: BTC, XETH: ETH, XDG: DOGE, @@ -90,14 +90,47 @@ func TestFindMatchingPairsBetween(t *testing.T) { BTCM: BTC, }) - matchingPairs = FindMatchingPairsBetween(PairsWithTranslation{spotPairs, nil}, PairsWithTranslation{futuresPairs, translations}) + expected := map[keyPair]Pair{ + NewPair(BTC, USD).KeyPair(): NewPair(XBT, USDM), + NewPair(ETH, USD).KeyPair(): NewPair(XETH, USDM), + NewPair(ETH, BTC).KeyPair(): NewPair(XETH, BTCM), + ltcusd.KeyPair(): ltcusd, + } + + matchingPairs = FindMatchingPairsBetween(PairsWithTranslation{spotPairs, nil}, PairsWithTranslation{futuresPairs, translationsTest}) require.Len(t, matchingPairs, 4) - matchingPairs = FindMatchingPairsBetween(PairsWithTranslation{spotPairs, translations}, PairsWithTranslation{futuresPairs, translations}) + for k, v := range matchingPairs { + if !expected[k.KeyPair()].Equal(v) { + t.Error("FindMatchingPairsBetween: translation failed") + } + } + + matchingPairs = FindMatchingPairsBetween(PairsWithTranslation{spotPairs, translationsTest}, PairsWithTranslation{futuresPairs, translationsTest}) require.Len(t, matchingPairs, 4) - matchingPairs = FindMatchingPairsBetween(PairsWithTranslation{spotPairs, translations}, PairsWithTranslation{futuresPairs, nil}) + for k, v := range matchingPairs { + if !expected[k.KeyPair()].Equal(v) { + t.Error("FindMatchingPairsBetween: translation failed") + } + } + + expected = map[keyPair]Pair{ + ltcusd.KeyPair(): ltcusd, + } + + matchingPairs = FindMatchingPairsBetween(PairsWithTranslation{spotPairs, translationsTest}, PairsWithTranslation{futuresPairs, nil}) require.Len(t, matchingPairs, 1) + + for k, v := range matchingPairs { + if !expected[k.KeyPair()].Equal(v) { + t.Error("FindMatchingPairsBetween: translation failed") + } + } +} + +func (p Pair) KeyPair() keyPair { + return keyPair{Base: p.Base.Item, Quote: p.Quote.Item} } func BenchmarkFindMatchingPairsBetween(b *testing.B) { diff --git a/exchanges/kucoin/kucoin_wrapper.go b/exchanges/kucoin/kucoin_wrapper.go index fc6e52ff1ac..a79313a6a1a 100644 --- a/exchanges/kucoin/kucoin_wrapper.go +++ b/exchanges/kucoin/kucoin_wrapper.go @@ -66,7 +66,7 @@ func (ku *Kucoin) SetDefaults() { log.Errorln(log.ExchangeSys, err) } ku.Features = exchange.Features{ - Translation: currency.NewTranslations(map[currency.Code]currency.Code{ + CurrencyTranslations: currency.NewTranslations(map[currency.Code]currency.Code{ currency.XBT: currency.BTC, currency.USDTM: currency.USDT, currency.USDM: currency.USD, From 4828ea9c520c36df2e6e40f80a17e142e06b72bd Mon Sep 17 00:00:00 2001 From: Ryan O'Hara-Reid Date: Mon, 17 Jun 2024 10:21:10 +1000 Subject: [PATCH 04/11] linter: fix? --- currency/translation_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/currency/translation_test.go b/currency/translation_test.go index 2afc47d3ad9..cded0ae496a 100644 --- a/currency/translation_test.go +++ b/currency/translation_test.go @@ -91,17 +91,17 @@ func TestFindMatchingPairsBetween(t *testing.T) { }) expected := map[keyPair]Pair{ - NewPair(BTC, USD).KeyPair(): NewPair(XBT, USDM), - NewPair(ETH, USD).KeyPair(): NewPair(XETH, USDM), - NewPair(ETH, BTC).KeyPair(): NewPair(XETH, BTCM), - ltcusd.KeyPair(): ltcusd, + NewPair(BTC, USD).keyPair(): NewPair(XBT, USDM), + NewPair(ETH, USD).keyPair(): NewPair(XETH, USDM), + NewPair(ETH, BTC).keyPair(): NewPair(XETH, BTCM), + ltcusd.keyPair(): ltcusd, } matchingPairs = FindMatchingPairsBetween(PairsWithTranslation{spotPairs, nil}, PairsWithTranslation{futuresPairs, translationsTest}) require.Len(t, matchingPairs, 4) for k, v := range matchingPairs { - if !expected[k.KeyPair()].Equal(v) { + if !expected[k.keyPair()].Equal(v) { t.Error("FindMatchingPairsBetween: translation failed") } } @@ -110,26 +110,26 @@ func TestFindMatchingPairsBetween(t *testing.T) { require.Len(t, matchingPairs, 4) for k, v := range matchingPairs { - if !expected[k.KeyPair()].Equal(v) { + if !expected[k.keyPair()].Equal(v) { t.Error("FindMatchingPairsBetween: translation failed") } } expected = map[keyPair]Pair{ - ltcusd.KeyPair(): ltcusd, + ltcusd.keyPair(): ltcusd, } matchingPairs = FindMatchingPairsBetween(PairsWithTranslation{spotPairs, translationsTest}, PairsWithTranslation{futuresPairs, nil}) require.Len(t, matchingPairs, 1) for k, v := range matchingPairs { - if !expected[k.KeyPair()].Equal(v) { + if !expected[k.keyPair()].Equal(v) { t.Error("FindMatchingPairsBetween: translation failed") } } } -func (p Pair) KeyPair() keyPair { +func (p Pair) keyPair() keyPair { return keyPair{Base: p.Base.Item, Quote: p.Quote.Item} } From 89d053de353e8c8c91ab1b0977f46cba88d894b0 Mon Sep 17 00:00:00 2001 From: Ryan O'Hara-Reid Date: Mon, 24 Jun 2024 10:55:56 +1000 Subject: [PATCH 05/11] translation --- exchanges/bybit/bybit_wrapper.go | 36 +++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/exchanges/bybit/bybit_wrapper.go b/exchanges/bybit/bybit_wrapper.go index b9138e4a93e..32ae04a15d5 100644 --- a/exchanges/bybit/bybit_wrapper.go +++ b/exchanges/bybit/bybit_wrapper.go @@ -91,6 +91,32 @@ func (by *Bybit) SetDefaults() { log.Errorln(log.ExchangeSys, err) } by.Features = exchange.Features{ + Translation: currency.NewTranslations( + map[currency.Code]currency.Code{ + currency.NewCode("10000000AIDOGE"): currency.NewCode("AIDOGE"), + currency.NewCode("1000000BABYDOGE"): currency.NewCode("BABYDOGE"), + currency.NewCode("1000000MOG"): currency.NewCode("MOG"), + currency.NewCode("10000COQ"): currency.NewCode("COQ"), + currency.NewCode("10000LADYS"): currency.NewCode("LADYS"), + currency.NewCode("10000NFT"): currency.NewCode("NFT"), + currency.NewCode("10000SATS"): currency.NewCode("SATS"), + currency.NewCode("10000STARL"): currency.NewCode("STARL"), + currency.NewCode("10000WEN"): currency.NewCode("WEN"), + currency.NewCode("1000APU"): currency.NewCode("APU"), + currency.NewCode("1000BEER"): currency.NewCode("BEER"), + currency.NewCode("1000BONK"): currency.NewCode("BONK"), + currency.NewCode("1000BTT"): currency.NewCode("BTT"), + currency.NewCode("1000FLOKI"): currency.NewCode("FLOKI"), + currency.NewCode("1000IQ50"): currency.NewCode("IQ50"), + currency.NewCode("1000LUNC"): currency.NewCode("LUNC"), + currency.NewCode("1000PEPE"): currency.NewCode("PEPE"), + currency.NewCode("1000RATS"): currency.NewCode("RATS"), + currency.NewCode("1000TURBO"): currency.NewCode("TURBO"), + currency.NewCode("1000XEC"): currency.NewCode("XEC"), + currency.NewCode("LUNA2"): currency.NewCode("LUNA"), + currency.NewCode("SHIB1000"): currency.NewCode("SHIB"), + }, + ), Supports: exchange.FeaturesSupported{ REST: true, Websocket: true, @@ -1887,9 +1913,9 @@ func (by *Bybit) GetLatestFundingRates(ctx context.Context, r *fundingrate.Lates if r == nil { return nil, fmt.Errorf("%w LatestRateRequest", common.ErrNilPointer) } - if r.IncludePredictedRate { - return nil, fmt.Errorf("%w IncludePredictedRate", common.ErrFunctionNotSupported) - } + // if r.IncludePredictedRate { + // return nil, fmt.Errorf("%w IncludePredictedRate", common.ErrFunctionNotSupported) + // } switch r.Asset { case asset.USDCMarginedFutures, asset.USDTMarginedFutures, @@ -1944,6 +1970,10 @@ func (by *Bybit) GetLatestFundingRates(ctx context.Context, r *fundingrate.Lates Time: lrt, Rate: decimal.NewFromFloat(ticks.List[i].FundingRate.Float64()), }, + PredictedUpcomingRate: fundingrate.Rate{ + Time: lrt.Add(fundingInterval), + Rate: decimal.NewFromFloat(ticks.List[i].FundingRate.Float64()), + }, TimeOfNextRate: ticks.List[i].NextFundingTime.Time(), }) } From cf512c98860a38628b15046de15f02f60ee804d7 Mon Sep 17 00:00:00 2001 From: Ryan O'Hara-Reid Date: Mon, 24 Jun 2024 14:02:13 +1000 Subject: [PATCH 06/11] fix cherry pick --- exchanges/bybit/bybit_wrapper.go | 36 ++++++++++++++------------------ 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/exchanges/bybit/bybit_wrapper.go b/exchanges/bybit/bybit_wrapper.go index 32ae04a15d5..f1a963de22d 100644 --- a/exchanges/bybit/bybit_wrapper.go +++ b/exchanges/bybit/bybit_wrapper.go @@ -91,30 +91,30 @@ func (by *Bybit) SetDefaults() { log.Errorln(log.ExchangeSys, err) } by.Features = exchange.Features{ - Translation: currency.NewTranslations( + CurrencyTranslations: currency.NewTranslations( map[currency.Code]currency.Code{ - currency.NewCode("10000000AIDOGE"): currency.NewCode("AIDOGE"), - currency.NewCode("1000000BABYDOGE"): currency.NewCode("BABYDOGE"), + currency.NewCode("10000000AIDOGE"): currency.AIDOGE, + currency.NewCode("1000000BABYDOGE"): currency.BABYDOGE, currency.NewCode("1000000MOG"): currency.NewCode("MOG"), currency.NewCode("10000COQ"): currency.NewCode("COQ"), currency.NewCode("10000LADYS"): currency.NewCode("LADYS"), - currency.NewCode("10000NFT"): currency.NewCode("NFT"), + currency.NewCode("10000NFT"): currency.NFT, currency.NewCode("10000SATS"): currency.NewCode("SATS"), - currency.NewCode("10000STARL"): currency.NewCode("STARL"), + currency.NewCode("10000STARL"): currency.STARL, currency.NewCode("10000WEN"): currency.NewCode("WEN"), currency.NewCode("1000APU"): currency.NewCode("APU"), currency.NewCode("1000BEER"): currency.NewCode("BEER"), - currency.NewCode("1000BONK"): currency.NewCode("BONK"), - currency.NewCode("1000BTT"): currency.NewCode("BTT"), - currency.NewCode("1000FLOKI"): currency.NewCode("FLOKI"), + currency.NewCode("1000BONK"): currency.BONK, + currency.NewCode("1000BTT"): currency.BTT, + currency.NewCode("1000FLOKI"): currency.FLOKI, currency.NewCode("1000IQ50"): currency.NewCode("IQ50"), - currency.NewCode("1000LUNC"): currency.NewCode("LUNC"), - currency.NewCode("1000PEPE"): currency.NewCode("PEPE"), + currency.NewCode("1000LUNC"): currency.LUNC, + currency.NewCode("1000PEPE"): currency.PEPE, currency.NewCode("1000RATS"): currency.NewCode("RATS"), currency.NewCode("1000TURBO"): currency.NewCode("TURBO"), - currency.NewCode("1000XEC"): currency.NewCode("XEC"), - currency.NewCode("LUNA2"): currency.NewCode("LUNA"), - currency.NewCode("SHIB1000"): currency.NewCode("SHIB"), + currency.NewCode("1000XEC"): currency.XEC, + currency.NewCode("LUNA2"): currency.LUNA, + currency.NewCode("SHIB1000"): currency.SHIB, }, ), Supports: exchange.FeaturesSupported{ @@ -1913,9 +1913,9 @@ func (by *Bybit) GetLatestFundingRates(ctx context.Context, r *fundingrate.Lates if r == nil { return nil, fmt.Errorf("%w LatestRateRequest", common.ErrNilPointer) } - // if r.IncludePredictedRate { - // return nil, fmt.Errorf("%w IncludePredictedRate", common.ErrFunctionNotSupported) - // } + if r.IncludePredictedRate { + return nil, fmt.Errorf("%w IncludePredictedRate", common.ErrFunctionNotSupported) + } switch r.Asset { case asset.USDCMarginedFutures, asset.USDTMarginedFutures, @@ -1970,10 +1970,6 @@ func (by *Bybit) GetLatestFundingRates(ctx context.Context, r *fundingrate.Lates Time: lrt, Rate: decimal.NewFromFloat(ticks.List[i].FundingRate.Float64()), }, - PredictedUpcomingRate: fundingrate.Rate{ - Time: lrt.Add(fundingInterval), - Rate: decimal.NewFromFloat(ticks.List[i].FundingRate.Float64()), - }, TimeOfNextRate: ticks.List[i].NextFundingTime.Time(), }) } From 2a32703f42d58386f86d0d799d638568cfa71da7 Mon Sep 17 00:00:00 2001 From: Ryan O'Hara-Reid Date: Fri, 28 Jun 2024 08:32:34 +1000 Subject: [PATCH 07/11] gateio: translation for mbabydoge with 1e6 divisor --- exchanges/gateio/gateio_wrapper.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/exchanges/gateio/gateio_wrapper.go b/exchanges/gateio/gateio_wrapper.go index 0972091d8f0..184f2337bd1 100644 --- a/exchanges/gateio/gateio_wrapper.go +++ b/exchanges/gateio/gateio_wrapper.go @@ -55,6 +55,9 @@ func (g *Gateio) SetDefaults() { } g.Features = exchange.Features{ + CurrencyTranslations: currency.NewTranslations(map[currency.Code]currency.Code{ + currency.NewCode("MBABYDOGE"): currency.BABYDOGE, + }), Supports: exchange.FeaturesSupported{ REST: true, Websocket: true, From c19047402c3db67bb4e0cddcf3348109b785739f Mon Sep 17 00:00:00 2001 From: Ryan O'Hara-Reid Date: Fri, 5 Jul 2024 16:26:11 +1000 Subject: [PATCH 08/11] okx: add translation --- exchanges/okx/okx.go | 2 +- exchanges/okx/okx_types.go | 4 ++-- exchanges/okx/okx_wrapper.go | 11 ++++++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/exchanges/okx/okx.go b/exchanges/okx/okx.go index af782f4d2f4..a3bbe1a552c 100644 --- a/exchanges/okx/okx.go +++ b/exchanges/okx/okx.go @@ -3166,7 +3166,6 @@ func (ok *Okx) GetCandlestickData(ctx context.Context, instrumentID string, inte return nil, errMissingInstrumentID } params.Set("instId", instrumentID) - var resp [][7]string params.Set("limit", strconv.FormatInt(limit, 10)) if !before.IsZero() { params.Set("before", strconv.FormatInt(before.UnixMilli(), 10)) @@ -3178,6 +3177,7 @@ func (ok *Okx) GetCandlestickData(ctx context.Context, instrumentID string, inte if bar != "" { params.Set("bar", bar) } + var resp [][7]string err := ok.SendHTTPRequest(ctx, exchange.RestSpot, rateLimit, http.MethodGet, common.EncodeURLValues(route, params), nil, &resp, false) if err != nil { return nil, err diff --git a/exchanges/okx/okx_types.go b/exchanges/okx/okx_types.go index e87b1a68648..b7b0e1dc054 100644 --- a/exchanges/okx/okx_types.go +++ b/exchanges/okx/okx_types.go @@ -1241,7 +1241,7 @@ type AccountDetail struct { AvailableBalance types.Number `json:"availBal"` AvailableEquity types.Number `json:"availEq"` CashBalance types.Number `json:"cashBal"` // Cash Balance - Currency string `json:"ccy"` + Currency currency.Code `json:"ccy"` CrossLiab types.Number `json:"crossLiab"` DiscountEquity types.Number `json:"disEq"` EquityOfCurrency types.Number `json:"eq"` @@ -1270,7 +1270,7 @@ type AccountPosition struct { AvailablePosition string `json:"availPos"` // Position that can be closed Only applicable to MARGIN, FUTURES/SWAP in the long-short mode, OPTION in Simple and isolated OPTION in margin Account. AveragePrice types.Number `json:"avgPx"` CreationTime okxUnixMilliTime `json:"cTime"` - Currency string `json:"ccy"` + Currency currency.Code `json:"ccy"` DeltaBS string `json:"deltaBS"` // delta:Black-Scholes Greeks in dollars,only applicable to OPTION DeltaPA string `json:"deltaPA"` // delta:Greeks in coins,only applicable to OPTION GammaBS string `json:"gammaBS"` // gamma:Black-Scholes Greeks in dollars,only applicable to OPTION diff --git a/exchanges/okx/okx_wrapper.go b/exchanges/okx/okx_wrapper.go index d29b7285b9b..383a4124977 100644 --- a/exchanges/okx/okx_wrapper.go +++ b/exchanges/okx/okx_wrapper.go @@ -63,6 +63,11 @@ func (ok *Okx) SetDefaults() { // Fill out the capabilities/features that the exchange supports ok.Features = exchange.Features{ + Translation: currency.NewTranslations(map[currency.Code]currency.Code{ + currency.NewCode("USDT-SWAP"): currency.USDT, + currency.NewCode("USD-SWAP"): currency.USD, + currency.NewCode("USDC-SWAP"): currency.USDC, + }), Supports: exchange.FeaturesSupported{ REST: true, Websocket: true, @@ -518,7 +523,7 @@ func (ok *Okx) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (acc for i := range accountBalances { for j := range accountBalances[i].Details { currencyBalances = append(currencyBalances, account.Balance{ - Currency: currency.NewCode(accountBalances[i].Details[j].Currency), + Currency: accountBalances[i].Details[j].Currency, Total: accountBalances[i].Details[j].EquityOfCurrency.Float64(), Hold: accountBalances[i].Details[j].FrozenBalance.Float64(), Free: accountBalances[i].Details[j].AvailableBalance.Float64(), @@ -1848,7 +1853,7 @@ func (ok *Okx) GetFuturesPositionSummary(ctx context.Context, req *futures.Posit ) for i := range acc[0].Details { - if acc[0].Details[i].Currency != positionSummary.Currency { + if !acc[0].Details[i].Currency.Equal(positionSummary.Currency) { continue } freeCollateral = acc[0].Details[i].AvailableBalance.Decimal() @@ -1877,7 +1882,7 @@ func (ok *Okx) GetFuturesPositionSummary(ctx context.Context, req *futures.Posit Asset: req.Asset, MarginType: marginMode, CollateralMode: collateralMode, - Currency: currency.NewCode(positionSummary.Currency), + Currency: positionSummary.Currency, AvailableEquity: availableEquity, CashBalance: cashBalance, DiscountEquity: discountEquity, From 85f804bad3c902e24e4f39ebe04b79c991c14098 Mon Sep 17 00:00:00 2001 From: Ryan O'Hara-Reid Date: Fri, 5 Jul 2024 16:31:38 +1000 Subject: [PATCH 09/11] cherry-pick: fix --- exchanges/okx/okx_wrapper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exchanges/okx/okx_wrapper.go b/exchanges/okx/okx_wrapper.go index 383a4124977..d4056f571b2 100644 --- a/exchanges/okx/okx_wrapper.go +++ b/exchanges/okx/okx_wrapper.go @@ -63,7 +63,7 @@ func (ok *Okx) SetDefaults() { // Fill out the capabilities/features that the exchange supports ok.Features = exchange.Features{ - Translation: currency.NewTranslations(map[currency.Code]currency.Code{ + CurrencyTranslations: currency.NewTranslations(map[currency.Code]currency.Code{ currency.NewCode("USDT-SWAP"): currency.USDT, currency.NewCode("USD-SWAP"): currency.USD, currency.NewCode("USDC-SWAP"): currency.USDC, From aa19be7dfde7934b3d95e5bfca21be25450efebd Mon Sep 17 00:00:00 2001 From: Ryan O'Hara-Reid Date: Tue, 16 Jul 2024 15:58:33 +1000 Subject: [PATCH 10/11] glorious: todos --- currency/translation.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/currency/translation.go b/currency/translation.go index eb0ec4c55b6..eecf57576c3 100644 --- a/currency/translation.go +++ b/currency/translation.go @@ -28,6 +28,7 @@ type Translations map[*Item]Code // representation and the value indicates the internal representation/common/standard // representation. e.g. XBT as key and BTC as value, this is useful for exchanges // that use different naming conventions. +// TODO: Expand for specific assets. func NewTranslations(t map[Code]Code) Translations { lookup := make(map[*Item]Code) for k, v := range t { @@ -39,6 +40,8 @@ func NewTranslations(t map[Code]Code) Translations { // Translate returns the translated currency code, usually used to convert // exchange specific currency codes to common currency codes. If no translation // is found it will return the original currency code. +// TODO: Add TranslateToCommon and TranslateToExchange methods to allow for +// translation to and from exchange specific currency codes. func (t Translations) Translate(incoming Code) Code { if len(t) == 0 { return incoming From 59d61926939e3cbe3653e3414cf217582f3e9a47 Mon Sep 17 00:00:00 2001 From: Ryan O'Hara-Reid Date: Fri, 16 Aug 2024 16:10:55 +1000 Subject: [PATCH 11/11] thrasher: nits --- currency/translation_test.go | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/currency/translation_test.go b/currency/translation_test.go index cded0ae496a..728f701eafa 100644 --- a/currency/translation_test.go +++ b/currency/translation_test.go @@ -3,6 +3,7 @@ package currency import ( "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -46,14 +47,8 @@ func TestNewTranslations(t *testing.T) { USDM: USD, }) require.NotNil(t, translations) - - if !translationsTest.Translate(XBT).Equal(BTC) { - t.Error("NewTranslations: translation failed") - } - - if !translationsTest.Translate(LTC).Equal(LTC) { - t.Error("NewTranslations: translation failed") - } + assert.Equal(t, BTC, translationsTest.Translate(XBT), "Translate should return BTC") + assert.Equal(t, LTC, translationsTest.Translate(LTC), "Translate should return LTC") } func TestFindMatchingPairsBetween(t *testing.T) { @@ -78,9 +73,7 @@ func TestFindMatchingPairsBetween(t *testing.T) { matchingPairs := FindMatchingPairsBetween(PairsWithTranslation{spotPairs, nil}, PairsWithTranslation{futuresPairs, nil}) require.Len(t, matchingPairs, 1) - if !matchingPairs[ltcusd].Equal(ltcusd) { - t.Error("FindMatchingPairsBetween: matching pair not found") - } + assert.True(t, ltcusd.Equal(matchingPairs[ltcusd]), "Pairs should match") translationsTest := NewTranslations(map[Code]Code{ XBT: BTC, @@ -101,18 +94,14 @@ func TestFindMatchingPairsBetween(t *testing.T) { require.Len(t, matchingPairs, 4) for k, v := range matchingPairs { - if !expected[k.keyPair()].Equal(v) { - t.Error("FindMatchingPairsBetween: translation failed") - } + assert.True(t, v.Equal(expected[k.keyPair()]), "Pairs should match") } matchingPairs = FindMatchingPairsBetween(PairsWithTranslation{spotPairs, translationsTest}, PairsWithTranslation{futuresPairs, translationsTest}) require.Len(t, matchingPairs, 4) for k, v := range matchingPairs { - if !expected[k.keyPair()].Equal(v) { - t.Error("FindMatchingPairsBetween: translation failed") - } + assert.True(t, v.Equal(expected[k.keyPair()]), "Pairs should match") } expected = map[keyPair]Pair{ @@ -123,9 +112,7 @@ func TestFindMatchingPairsBetween(t *testing.T) { require.Len(t, matchingPairs, 1) for k, v := range matchingPairs { - if !expected[k.keyPair()].Equal(v) { - t.Error("FindMatchingPairsBetween: translation failed") - } + assert.True(t, v.Equal(expected[k.keyPair()]), "Pairs should match") } }