Skip to content

Commit

Permalink
Deribit: Add subscription configuration (#1636)
Browse files Browse the repository at this point in the history
* Kline: Fix Raw Short, Marshal and Unmarshal

* Deribit: Rename GenerateDefaultSubs

* Deribit: Remove custom GetDefaultConfig

Moved to exchange base by #1472

* Deribit: Straight Rename of eps to endpoints

Since I had to ask what this abbreviation meant, I think we should
abandon it

* Deribit: Add Subscription configuration

* Deribit: Fix race on Setup with optionsRegex

Calling Setup twice would race on the assignment to this package var.

There was an option to just move the assignment to the package var declaration, but this
change improves the performance and allocations:
```
BenchmarkOptionPairToString-8            1000000              1239 ns/op             485 B/op         10 allocs/op
BenchmarkOptionPairToString2-8           3473804               656.2 ns/op           348 B/op          7 allocs/op
```

I've also removed the t.Run because even success the -v output from
tests would be very noisy, and I don't think we were getting any benefit
from it at all:
```
=== RUN   TestOptionPairToString
=== PAUSE TestOptionPairToString
=== CONT  TestOptionPairToString
=== RUN   TestOptionPairToString/BTC-30MAY24-61000-C
=== PAUSE TestOptionPairToString/BTC-30MAY24-61000-C
=== RUN   TestOptionPairToString/ETH-1JUN24-3200-P
=== PAUSE TestOptionPairToString/ETH-1JUN24-3200-P
=== RUN   TestOptionPairToString/SOL_USDC-31MAY24-162-P
=== PAUSE TestOptionPairToString/SOL_USDC-31MAY24-162-P
=== RUN   TestOptionPairToString/MATIC_USDC-6APR24-0d98-P
=== PAUSE TestOptionPairToString/MATIC_USDC-6APR24-0d98-P
=== CONT  TestOptionPairToString/BTC-30MAY24-61000-C
=== CONT  TestOptionPairToString/SOL_USDC-31MAY24-162-P
=== CONT  TestOptionPairToString/ETH-1JUN24-3200-P
=== CONT  TestOptionPairToString/MATIC_USDC-6APR24-0d98-P
--- PASS: TestOptionPairToString (0.00s)
    --- PASS: TestOptionPairToString/BTC-30MAY24-61000-C (0.00s)
    --- PASS: TestOptionPairToString/ETH-1JUN24-3200-P (0.00s)
    --- PASS: TestOptionPairToString/SOL_USDC-31MAY24-162-P (0.00s)
    --- PASS: TestOptionPairToString/MATIC_USDC-6APR24-0d98-P (0.00s)
```

( And that got worse with me adding more tests )
  • Loading branch information
gbjk authored Jan 2, 2025
1 parent b209b0e commit 4fcee84
Show file tree
Hide file tree
Showing 9 changed files with 308 additions and 528 deletions.
21 changes: 16 additions & 5 deletions cmd/documentation/exchanges_templates/deribit.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,23 @@ if err != nil {
}
```

### How to do Websocket public/private calls
### Subscriptions

```go
// Exchanges will be abstracted out in further updates and examples will be
// supplied then
```
All default subscriptions are for all enabled assets.

Default Public Subscriptions:
- Candles ( Timeframe: 1 day )
- Orderbook ( Full depth @ Interval: 100ms )
- Ticker ( Interval: 100ms )
- All Trades ( Interval: 100ms )

Default Authenticated Subscriptions:
- My Account Orders
- My Account Trades

kline.Raw Interval configurable for a raw orderbook subscription when authenticated

Subscriptions are subject to enabled assets and pairs.

### Please click GoDocs chevron above to view current GoDoc information for this package
{{template "contributions"}}
Expand Down
21 changes: 16 additions & 5 deletions exchanges/deribit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,23 @@ if err != nil {
}
```

### How to do Websocket public/private calls
### Subscriptions

```go
// Exchanges will be abstracted out in further updates and examples will be
// supplied then
```
All default subscriptions are for all enabled assets.

Default Public Subscriptions:
- Candles ( Timeframe: 1 day )
- Orderbook ( Full depth @ Interval: 100ms )
- Ticker ( Interval: 100ms )
- All Trades ( Interval: 100ms )

Default Authenticated Subscriptions:
- My Account Orders
- My Account Trades

kline.Raw Interval configurable for a raw orderbook subscription when authenticated

Subscriptions are subject to enabled assets and pairs.

### Please click GoDocs chevron above to view current GoDoc information for this package

Expand Down
33 changes: 12 additions & 21 deletions exchanges/deribit/deribit.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"fmt"
"net/http"
"net/url"
"regexp"
"strconv"
"strings"
"time"
Expand All @@ -28,12 +27,6 @@ type Deribit struct {
exchange.Base
}

var (
// optionRegex compiles optionDecimalRegex at startup and is used to help set
// option currency lower-case d eg MATIC-USDC-3JUN24-0d64-P
optionRegex *regexp.Regexp
)

const (
deribitAPIVersion = "/api/v2"
tradeBaseURL = "https://www.deribit.com/"
Expand All @@ -43,8 +36,7 @@ const (
tradeFuturesCombo = "futures-spreads/"
tradeOptionsCombo = "combos/"

perpString = "PERPETUAL"
optionDecimalRegex = `\d+(D)\d+`
perpString = "PERPETUAL"

// Public endpoints
// Market Data
Expand Down Expand Up @@ -2837,20 +2829,19 @@ func (d *Deribit) formatFuturesTradablePair(pair currency.Pair) string {
return instrumentID
}

// optionPairToString to format and return an Options currency pairs with the following format: MATIC_USDC-6APR24-0d98-P
// it has both uppercase or lowercase characters, which we can not achieve with the Upper=true or Upper=false
// optionPairToString formats an options pair as: SYMBOL-EXPIRE-STRIKE-TYPE
// SYMBOL may be a currency (BTC) or a pair (XRP_USDC)
// EXPIRE is DDMMMYY
// STRIKE may include a d for decimal point in linear options
// TYPE is Call or Put
func (d *Deribit) optionPairToString(pair currency.Pair) string {
subCodes := strings.Split(pair.Quote.String(), currency.DashDelimiter)
initialDelimiter := currency.DashDelimiter
if subCodes[0] == "USDC" {
q := pair.Quote.String()
if strings.HasPrefix(q, "USDC") && len(q) > 11 { // Linear option
initialDelimiter = currency.UnderscoreDelimiter
// Replace a capital D with d for decimal place in Strike price
// Char 11 is either the hyphen before Strike price or first digit
q = q[:11] + strings.Replace(q[11:], "D", "d", -1)
}
for i := range subCodes {
if match := optionRegex.MatchString(subCodes[i]); match {
subCodes[i] = strings.ToLower(subCodes[i])
break
}
}

return pair.Base.String() + initialDelimiter + strings.Join(subCodes, currency.DashDelimiter)
return pair.Base.String() + initialDelimiter + q
}
92 changes: 81 additions & 11 deletions exchanges/deribit/deribit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ import (
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
"github.com/thrasher-corp/gocryptotrader/exchanges/subscription"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/trade"
testexch "github.com/thrasher-corp/gocryptotrader/internal/testing/exchange"
testsubs "github.com/thrasher-corp/gocryptotrader/internal/testing/subscriptions"
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
)

Expand Down Expand Up @@ -3266,11 +3268,82 @@ func setupWs() {
}
}

func TestGenerateDefaultSubscriptions(t *testing.T) {
func TestGenerateSubscriptions(t *testing.T) {
t.Parallel()
result, err := d.GenerateDefaultSubscriptions()

d := new(Deribit) //nolint:govet // Intentional lexical scope shadow
require.NoError(t, testexch.Setup(d), "Test instance Setup must not error")

d.Websocket.SetCanUseAuthenticatedEndpoints(true)
subs, err := d.generateSubscriptions()
require.NoError(t, err)
assert.NotNil(t, result)
exp := subscription.List{}
for _, s := range d.Features.Subscriptions {
for _, a := range d.GetAssetTypes(true) {
if !d.IsAssetWebsocketSupported(a) {
continue
}
pairs, err := d.GetEnabledPairs(a)
require.NoErrorf(t, err, "GetEnabledPairs %s must not error", a)
s := s.Clone() //nolint:govet // Intentional lexical scope shadow
s.Asset = a
if isSymbolChannel(s) {
for i, p := range pairs {
s := s.Clone() //nolint:govet // Intentional lexical scope shadow
s.QualifiedChannel = channelName(s) + "." + p.String()
if s.Interval != 0 {
s.QualifiedChannel += "." + channelInterval(s)
}
s.Pairs = pairs[i : i+1]
exp = append(exp, s)
}
} else {
s.Pairs = pairs
s.QualifiedChannel = channelName(s)
exp = append(exp, s)
}
}
}
testsubs.EqualLists(t, exp, subs)
}

func TestChannelInterval(t *testing.T) {
t.Parallel()

for _, i := range []int64{1, 3, 5, 10, 15, 30, 60, 120, 180, 360, 720} {
a := channelInterval(&subscription.Subscription{Channel: subscription.CandlesChannel, Interval: kline.Interval(i * int64(time.Minute))})
assert.Equal(t, strconv.Itoa(int(i)), a)
}

a := channelInterval(&subscription.Subscription{Channel: subscription.CandlesChannel, Interval: kline.OneDay})
assert.Equal(t, "1D", a)

assert.Panics(t, func() {
channelInterval(&subscription.Subscription{Channel: subscription.CandlesChannel, Interval: kline.OneMonth})
})

a = channelInterval(&subscription.Subscription{Channel: subscription.OrderbookChannel, Interval: kline.ThousandMilliseconds})
assert.Equal(t, "agg2", a, "1 second should expand to agg2")

a = channelInterval(&subscription.Subscription{Channel: subscription.OrderbookChannel, Interval: kline.HundredMilliseconds})
assert.Equal(t, "100ms", a, "100ms should expand correctly")

a = channelInterval(&subscription.Subscription{Channel: subscription.OrderbookChannel, Interval: kline.Raw})
assert.Equal(t, "raw", a, "raw should expand correctly")

assert.Panics(t, func() {
channelInterval(&subscription.Subscription{Channel: subscription.OrderbookChannel, Interval: kline.OneMonth})
})

a = channelInterval(&subscription.Subscription{Channel: userAccessLogChannel})
assert.Empty(t, a, "Anything else should return empty")
}

func TestChannelName(t *testing.T) {
t.Parallel()
assert.Equal(t, tickerChannel, channelName(&subscription.Subscription{Channel: subscription.TickerChannel}))
assert.Equal(t, userLockChannel, channelName(&subscription.Subscription{Channel: userLockChannel}))
assert.Panics(t, func() { channelName(&subscription.Subscription{Channel: "wibble"}) }, "Unknown channels should panic")
}

func TestFetchTicker(t *testing.T) {
Expand Down Expand Up @@ -3681,18 +3754,15 @@ func TestFormatFuturesTradablePair(t *testing.T) {

func TestOptionPairToString(t *testing.T) {
t.Parallel()
optionsList := map[currency.Pair]string{
for pair, exp := range map[currency.Pair]string{
{Delimiter: currency.DashDelimiter, Base: currency.BTC, Quote: currency.NewCode("30MAY24-61000-C")}: "BTC-30MAY24-61000-C",
{Delimiter: currency.DashDelimiter, Base: currency.ETH, Quote: currency.NewCode("1JUN24-3200-P")}: "ETH-1JUN24-3200-P",
{Delimiter: currency.DashDelimiter, Base: currency.SOL, Quote: currency.NewCode("USDC-31MAY24-162-P")}: "SOL_USDC-31MAY24-162-P",
{Delimiter: currency.DashDelimiter, Base: currency.MATIC, Quote: currency.NewCode("USDC-6APR24-0d98-P")}: "MATIC_USDC-6APR24-0d98-P",
}
for pair, instrumentID := range optionsList {
t.Run(instrumentID, func(t *testing.T) {
t.Parallel()
instrument := d.optionPairToString(pair)
require.Equal(t, instrumentID, instrument)
})
{Delimiter: currency.DashDelimiter, Base: currency.MATIC, Quote: currency.NewCode("USDC-8JUN24-0D99-P")}: "MATIC_USDC-8JUN24-0d99-P",
{Delimiter: currency.DashDelimiter, Base: currency.MATIC, Quote: currency.NewCode("USDC-6DEC29-0D87-C")}: "MATIC_USDC-6DEC29-0d87-C",
} {
assert.Equal(t, exp, d.optionPairToString(pair), "optionPairToString should return correctly")
}
}

Expand Down
Loading

0 comments on commit 4fcee84

Please sign in to comment.