Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deribit: Add Subscription Configuration #1636

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 make 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 q[:4] == "USDC" && len(q) > 11 { // Linear option
gbjk marked this conversation as resolved.
Show resolved Hide resolved
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
}
91 changes: 79 additions & 12 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,79 @@ func setupWs() {
}
}

func TestGenerateDefaultSubscriptions(t *testing.T) {
func TestGenerateSubscriptions(t *testing.T) {
gbjk marked this conversation as resolved.
Show resolved Hide resolved
t.Parallel()

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)
exp := subscription.List{}
for _, s := range d.Features.Subscriptions {
for _, a := range d.GetAssetTypes(true) {
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()
result, err := d.GenerateDefaultSubscriptions()
require.NoError(t, err)
assert.NotNil(t, result)

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 +3751,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
Loading