From 7bc0a06d6f9549901a8203725d863ece2fd466e0 Mon Sep 17 00:00:00 2001 From: Adrian Gallagher Date: Fri, 19 Dec 2025 09:44:58 +1100 Subject: [PATCH] Deribit: Add futureComboPairToString func, remove redundant endpoints and fix test issues (#2134) * Deribit: Add futureComboPairToString func, remove redundant endpoints and fix test issues * Deribit: Remove redundant getCurrencyIndexPrice constant * Deribit: Improve futureComboPairToString speed and add test cases * Deribit: Refactor futureComboPairToString for improved readability and performance --- exchanges/deribit/deribit.go | 35 ++++++++++++------ exchanges/deribit/deribit_test.go | 45 +++++++++++------------ exchanges/deribit/deribit_websocket.go | 4 +- exchanges/deribit/deribit_wrapper.go | 2 + exchanges/deribit/deribit_ws_endpoints.go | 14 ------- 5 files changed, 47 insertions(+), 53 deletions(-) diff --git a/exchanges/deribit/deribit.go b/exchanges/deribit/deribit.go index f020db7b..6e7737d1 100644 --- a/exchanges/deribit/deribit.go +++ b/exchanges/deribit/deribit.go @@ -50,7 +50,6 @@ const ( getFundingRateHistory = "public/get_funding_rate_history" getFundingRateValue = "public/get_funding_rate_value" getHistoricalVolatility = "public/get_historical_volatility" - getCurrencyIndexPrice = "public/get_index" getIndexPrice = "public/get_index_price" getIndexPriceNames = "public/get_index_price_names" getInstrument = "public/get_instrument" @@ -292,17 +291,6 @@ func (e *Exchange) GetHistoricalVolatility(ctx context.Context, ccy currency.Cod return data, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL, common.EncodeURLValues(getHistoricalVolatility, params), &data) } -// GetCurrencyIndexPrice retrieves the current index price for the instruments, for the selected currency. -func (e *Exchange) GetCurrencyIndexPrice(ctx context.Context, ccy currency.Code) (*IndexPrice, error) { - if ccy.IsEmpty() { - return nil, currency.ErrCurrencyCodeEmpty - } - params := url.Values{} - params.Set("currency", ccy.String()) - var resp *IndexPrice - return resp, e.SendHTTPRequest(ctx, exchange.RestFutures, nonMatchingEPL, common.EncodeURLValues(getCurrencyIndexPrice, params), &resp) -} - // GetIndexPrice gets price data for the requested index func (e *Exchange) GetIndexPrice(ctx context.Context, index string) (*IndexPriceData, error) { if index == "" { @@ -2727,3 +2715,26 @@ func optionComboPairToString(pair currency.Pair) string { // * length == 4 with USDC as second token) return parts[0] + "_" + strings.Join(parts[1:], "-") } + +// futureComboPairToString formats a future combo pair to deribit request format +// e.g. ETH-USDC-FS-28NOV25_PERP -> ETH_USDC-FS-28NOV25_PERP (linear) +// e.g. BTC-FS-28NOV25_PERP -> BTC-FS-28NOV25_PERP (inverse, unchanged) +func futureComboPairToString(pair currency.Pair) string { + s := pair.String() + before, after, found := strings.Cut(s, "-") + if !found { + return s + } + + // Must have "USDC-" immediately after first dash (linear combo) + if len(after) < 5 || after[:5] != "USDC-" { + return s + } + + // Need at least one more dash after "USDC-" to have 4+ parts + if !strings.Contains(after[5:], "-") { + return s + } + + return before + "_" + after +} diff --git a/exchanges/deribit/deribit_test.go b/exchanges/deribit/deribit_test.go index 7d1afdd3..c81456d4 100644 --- a/exchanges/deribit/deribit_test.go +++ b/exchanges/deribit/deribit_test.go @@ -256,7 +256,7 @@ func TestGetMarkPriceHistory(t *testing.T) { optionPairToString(optionsTradablePair), spotTradablePair.String(), btcPerpInstrument, - futureComboTradablePair.String(), + futureComboPairToString(futureComboTradablePair), } { result, err = e.GetMarkPriceHistory(t.Context(), ps, time.Now().Add(-5*time.Minute), time.Now()) require.NoErrorf(t, err, "expected nil, got %v for pair %s", err, ps) @@ -274,7 +274,7 @@ func TestWSRetrieveMarkPriceHistory(t *testing.T) { optionPairToString(optionsTradablePair), spotTradablePair.String(), btcPerpInstrument, - futureComboTradablePair.String(), + futureComboPairToString(futureComboTradablePair), } { result, err = e.WSRetrieveMarkPriceHistory(t.Context(), ps, time.Now().Add(-4*time.Hour), time.Now()) require.NoErrorf(t, err, "expected %v, got %v currency pair %v", nil, err, ps) @@ -315,7 +315,7 @@ func TestGetBookSummaryByInstrument(t *testing.T) { for _, ps := range []string{ btcPerpInstrument, spotTradablePair.String(), - futureComboTradablePair.String(), + futureComboPairToString(futureComboTradablePair), optionPairToString(optionsTradablePair), optionComboPairToString(optionComboTradablePair), } { @@ -336,7 +336,7 @@ func TestWSRetrieveBookSummaryByInstrument(t *testing.T) { for _, ps := range []string{ btcPerpInstrument, spotTradablePair.String(), - futureComboTradablePair.String(), + futureComboPairToString(futureComboTradablePair), optionPairToString(optionsTradablePair), optionComboPairToString(optionComboTradablePair), } { @@ -494,24 +494,6 @@ func TestWSRetrieveHistoricalVolatility(t *testing.T) { assert.NotNil(t, result) } -func TestGetCurrencyIndexPrice(t *testing.T) { - t.Parallel() - _, err := e.GetCurrencyIndexPrice(t.Context(), currency.EMPTYCODE) - require.ErrorIs(t, err, currency.ErrCurrencyCodeEmpty) - result, err := e.GetCurrencyIndexPrice(t.Context(), currency.BTC) - require.NoError(t, err) - assert.NotNil(t, result) -} - -func TestWSRetrieveCurrencyIndexPrice(t *testing.T) { - t.Parallel() - _, err := e.WSRetrieveCurrencyIndexPrice(t.Context(), currency.EMPTYCODE) - require.ErrorIs(t, err, currency.ErrCurrencyCodeEmpty) - result, err := e.WSRetrieveCurrencyIndexPrice(t.Context(), currency.BTC) - require.NoError(t, err) - assert.NotNil(t, result) -} - func TestGetIndexPrice(t *testing.T) { t.Parallel() _, err := e.GetIndexPrice(t.Context(), "") @@ -2730,7 +2712,7 @@ func TestGetComboDetails(t *testing.T) { _, err := e.GetComboDetails(t.Context(), "") require.ErrorIs(t, err, errInvalidComboID) - result, err := e.GetComboDetails(t.Context(), futureComboTradablePair.String()) + result, err := e.GetComboDetails(t.Context(), futureComboPairToString(futureComboTradablePair)) require.NoError(t, err) assert.NotNil(t, result) } @@ -2740,7 +2722,7 @@ func TestWSRetrieveComboDetails(t *testing.T) { _, err := e.WSRetrieveComboDetails(t.Context(), "") require.ErrorIs(t, err, errInvalidComboID) - result, err := e.WSRetrieveComboDetails(t.Context(), futureComboTradablePair.String()) + result, err := e.WSRetrieveComboDetails(t.Context(), futureComboPairToString(futureComboTradablePair)) require.NoError(t, err) assert.NotNil(t, result) } @@ -3779,6 +3761,21 @@ func TestOptionPairToString(t *testing.T) { } } +func TestFutureComboPairToString(t *testing.T) { + t.Parallel() + for pair, exp := range map[currency.Pair]string{ + {Delimiter: currency.DashDelimiter, Base: currency.BTC, Quote: currency.NewCode("FS-28NOV25_PERP")}: "BTC-FS-28NOV25_PERP", + {Delimiter: currency.DashDelimiter, Base: currency.ETH, Quote: currency.NewCode("FS-28NOV25_PERP")}: "ETH-FS-28NOV25_PERP", + {Delimiter: currency.DashDelimiter, Base: currency.BTC, Quote: currency.NewCode("FS-30JAN26_26DEC25")}: "BTC-FS-30JAN26_26DEC25", + {Delimiter: currency.DashDelimiter, Base: currency.BTC, Quote: currency.NewCode("USDC-FS-28NOV25_PERP")}: "BTC_USDC-FS-28NOV25_PERP", + {Delimiter: currency.DashDelimiter, Base: currency.ETH, Quote: currency.NewCode("USDC-FS-28NOV25_PERP")}: "ETH_USDC-FS-28NOV25_PERP", + {Base: currency.BTC, Quote: currency.USDT}: "BTCUSDT", // no dash at all + {Delimiter: currency.DashDelimiter, Base: currency.BTC, Quote: currency.NewCode("USDC-PERPETUAL")}: "BTC-USDC-PERPETUAL", // USDC- prefix but no dash after (3 parts) + } { + assert.Equal(t, exp, futureComboPairToString(pair), "futureComboPairToString should return correctly") + } +} + func TestWSRetrieveCombos(t *testing.T) { t.Parallel() _, err := e.WSRetrieveCombos(t.Context(), currency.EMPTYCODE) diff --git a/exchanges/deribit/deribit_websocket.go b/exchanges/deribit/deribit_websocket.go index 7e301329..8efdd5b6 100644 --- a/exchanges/deribit/deribit_websocket.go +++ b/exchanges/deribit/deribit_websocket.go @@ -32,9 +32,7 @@ import ( var deribitWebsocketAddress = "wss://www.deribit.com/ws" + deribitAPIVersion const ( - rpcVersion = "2.0" - rateLimit = 20 - errAuthFailed = 1002 + rpcVersion = "2.0" // public websocket channels announcementsChannel = "announcements" diff --git a/exchanges/deribit/deribit_wrapper.go b/exchanges/deribit/deribit_wrapper.go index 9691a820..f517fa7c 100644 --- a/exchanges/deribit/deribit_wrapper.go +++ b/exchanges/deribit/deribit_wrapper.go @@ -1539,6 +1539,8 @@ func formatPairString(assetType asset.Item, pair currency.Pair) string { return optionPairToString(pair) case asset.OptionCombo: return optionComboPairToString(pair) + case asset.FutureCombo: + return futureComboPairToString(pair) } return pair.String() } diff --git a/exchanges/deribit/deribit_ws_endpoints.go b/exchanges/deribit/deribit_ws_endpoints.go index 6e9b4d21..3b85ca10 100644 --- a/exchanges/deribit/deribit_ws_endpoints.go +++ b/exchanges/deribit/deribit_ws_endpoints.go @@ -163,20 +163,6 @@ func (e *Exchange) WSRetrieveHistoricalVolatility(ctx context.Context, ccy curre return data, e.SendWSRequest(ctx, nonMatchingEPL, getHistoricalVolatility, input, &data, false) } -// WSRetrieveCurrencyIndexPrice the current index price for the instruments, for the selected currency through the websocket connection. -func (e *Exchange) WSRetrieveCurrencyIndexPrice(ctx context.Context, ccy currency.Code) (map[string]float64, error) { - if ccy.IsEmpty() { - return nil, currency.ErrCurrencyCodeEmpty - } - input := &struct { - Currency currency.Code `json:"currency"` - }{ - Currency: ccy, - } - var resp map[string]float64 - return resp, e.SendWSRequest(ctx, nonMatchingEPL, getCurrencyIndexPrice, input, &resp, false) -} - // WSRetrieveIndexPrice retrieves price data for the requested index through the websocket connection. func (e *Exchange) WSRetrieveIndexPrice(ctx context.Context, index string) (*IndexPriceData, error) { if index == "" {