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
This commit is contained in:
Adrian Gallagher
2025-12-19 09:44:58 +11:00
committed by GitHub
parent a2c7837a63
commit 7bc0a06d6f
5 changed files with 47 additions and 53 deletions

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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"

View File

@@ -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()
}

View File

@@ -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 == "" {