From ca402e209c4138ef040b21d0b7ae672f8fbfca72 Mon Sep 17 00:00:00 2001 From: Keven Li Date: Thu, 10 Oct 2024 10:05:23 +0800 Subject: [PATCH 1/8] add Label field to user_trade Deribit Doc missed a "label" field on user.trade.xxx event data. The "label" contains corresponding `label` parameter of the buy/sell request. --- models/user_trade.go | 1 + 1 file changed, 1 insertion(+) diff --git a/models/user_trade.go b/models/user_trade.go index f2054c7..5eb9ef0 100644 --- a/models/user_trade.go +++ b/models/user_trade.go @@ -18,4 +18,5 @@ type UserTrade struct { Fee float64 `json:"fee"` Direction string `json:"direction"` Amount float64 `json:"amount"` + Label string `json:"label"` } From 0cdbc901594e8e0d1ebb646b021da8fcd0b5469d Mon Sep 17 00:00:00 2001 From: kevenli Date: Mon, 21 Oct 2024 02:10:45 +0000 Subject: [PATCH 2/8] implement CancelByLabel --- api_trading.go | 5 +++++ client_test.go | 36 +++++++++++++++++++++++++++++++++++- models/cancel_by_label.go | 6 ++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 models/cancel_by_label.go diff --git a/api_trading.go b/api_trading.go index d367516..478c17e 100644 --- a/api_trading.go +++ b/api_trading.go @@ -39,6 +39,11 @@ func (c *Client) CancelAllByInstrument(params *models.CancelAllByInstrumentParam return } +func (c *Client) CancelByLabel(params *models.CancelByLabelParams) (result int, err error) { + err = c.Call("private/cancel_by_label", params, &result) + return +} + func (c *Client) ClosePosition(params *models.ClosePositionParams) (result models.ClosePositionResponse, err error) { err = c.Call("private/close_position", params, &result) return diff --git a/client_test.go b/client_test.go index d33088f..68fc9d4 100644 --- a/client_test.go +++ b/client_test.go @@ -2,9 +2,10 @@ package deribit import ( "encoding/json" + "testing" + "github.com/frankrap/deribit-api/models" "github.com/stretchr/testify/assert" - "testing" ) func newClient() *Client { @@ -135,6 +136,39 @@ func TestClient_Buy(t *testing.T) { t.Logf("%#v", result) } +func TestClient_CancelByLabel(t *testing.T) { + client := newClient() + params := &models.BuyParams{ + InstrumentName: "BTC-PERPETUAL", + Amount: 10, + Price: 20000.0, + Type: "limit", + Label: "TestClient_CancelByLabel", + } + result, err := client.Buy(params) + if err != nil { + t.Fatal(err) + return + } + + t.Logf("%#v", result) + + cancelByLabelParams := &models.CancelByLabelParams{ + Label: "TestClient_CancelByLabel", + } + + cancelResult, err := client.CancelByLabel(cancelByLabelParams) + if err != nil { + t.Errorf("Cancel failed, %s", err) + } + + if cancelResult <= 0 { + t.Errorf("Cancel order count should be greater than 0, actual=%d", cancelResult) + } + + t.Logf("%#v", cancelResult) +} + func TestJsonOmitempty(t *testing.T) { params := &models.BuyParams{ InstrumentName: "BTC-PERPETUAL", diff --git a/models/cancel_by_label.go b/models/cancel_by_label.go new file mode 100644 index 0000000..09d3b44 --- /dev/null +++ b/models/cancel_by_label.go @@ -0,0 +1,6 @@ +package models + +type CancelByLabelParams struct { + Label string `json:"label"` + Currency string `json:"currency,omitempty"` +} From 77de4b5185a16e710bf8fd1c1595f6e892b4805b Mon Sep 17 00:00:00 2001 From: kevenli Date: Mon, 21 Oct 2024 02:10:45 +0000 Subject: [PATCH 3/8] implement CancelByLabel --- api_trading.go | 5 +++++ client_test.go | 36 +++++++++++++++++++++++++++++++++++- models/cancel_by_label.go | 6 ++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 models/cancel_by_label.go diff --git a/api_trading.go b/api_trading.go index d367516..478c17e 100644 --- a/api_trading.go +++ b/api_trading.go @@ -39,6 +39,11 @@ func (c *Client) CancelAllByInstrument(params *models.CancelAllByInstrumentParam return } +func (c *Client) CancelByLabel(params *models.CancelByLabelParams) (result int, err error) { + err = c.Call("private/cancel_by_label", params, &result) + return +} + func (c *Client) ClosePosition(params *models.ClosePositionParams) (result models.ClosePositionResponse, err error) { err = c.Call("private/close_position", params, &result) return diff --git a/client_test.go b/client_test.go index d33088f..68fc9d4 100644 --- a/client_test.go +++ b/client_test.go @@ -2,9 +2,10 @@ package deribit import ( "encoding/json" + "testing" + "github.com/frankrap/deribit-api/models" "github.com/stretchr/testify/assert" - "testing" ) func newClient() *Client { @@ -135,6 +136,39 @@ func TestClient_Buy(t *testing.T) { t.Logf("%#v", result) } +func TestClient_CancelByLabel(t *testing.T) { + client := newClient() + params := &models.BuyParams{ + InstrumentName: "BTC-PERPETUAL", + Amount: 10, + Price: 20000.0, + Type: "limit", + Label: "TestClient_CancelByLabel", + } + result, err := client.Buy(params) + if err != nil { + t.Fatal(err) + return + } + + t.Logf("%#v", result) + + cancelByLabelParams := &models.CancelByLabelParams{ + Label: "TestClient_CancelByLabel", + } + + cancelResult, err := client.CancelByLabel(cancelByLabelParams) + if err != nil { + t.Errorf("Cancel failed, %s", err) + } + + if cancelResult <= 0 { + t.Errorf("Cancel order count should be greater than 0, actual=%d", cancelResult) + } + + t.Logf("%#v", cancelResult) +} + func TestJsonOmitempty(t *testing.T) { params := &models.BuyParams{ InstrumentName: "BTC-PERPETUAL", diff --git a/models/cancel_by_label.go b/models/cancel_by_label.go new file mode 100644 index 0000000..09d3b44 --- /dev/null +++ b/models/cancel_by_label.go @@ -0,0 +1,6 @@ +package models + +type CancelByLabelParams struct { + Label string `json:"label"` + Currency string `json:"currency,omitempty"` +} From f1153835e7f694088ae9e02d721ecf5d3db6dc1b Mon Sep 17 00:00:00 2001 From: kevenli Date: Mon, 21 Oct 2024 14:43:21 +0000 Subject: [PATCH 4/8] fix GetUserTradesByOrder, returns wrong type --- api_trading.go | 2 +- client_test.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/api_trading.go b/api_trading.go index 478c17e..59e6895 100644 --- a/api_trading.go +++ b/api_trading.go @@ -109,7 +109,7 @@ func (c *Client) GetUserTradesByInstrumentAndTime(params *models.GetUserTradesBy return } -func (c *Client) GetUserTradesByOrder(params *models.GetUserTradesByOrderParams) (result models.GetUserTradesResponse, err error) { +func (c *Client) GetUserTradesByOrder(params *models.GetUserTradesByOrderParams) (result []models.Trade, err error) { err = c.Call("private/get_user_trades_by_order", params, &result) return } diff --git a/client_test.go b/client_test.go index 68fc9d4..e2e7251 100644 --- a/client_test.go +++ b/client_test.go @@ -169,6 +169,56 @@ func TestClient_CancelByLabel(t *testing.T) { t.Logf("%#v", cancelResult) } +func TestClient_GetUserTradesByOrder(t *testing.T) { + client := newClient() + params := &models.BuyParams{ + InstrumentName: "BTC-PERPETUAL", + Amount: 10, + Price: 20000.0, + Type: "market", + Label: "TestClient_CancelByLabel", + } + buyResult, err := client.Buy(params) + if err != nil { + t.Fatal(err) + } + + t.Logf("%#v", buyResult) + + getTradesRes, err := client.GetUserTradesByOrder(&models.GetUserTradesByOrderParams{OrderID: buyResult.Order.OrderID}) + + if err != nil { + t.Fatal(err) + } + + if actualTradesCount := len(getTradesRes); actualTradesCount == 0 { + t.Errorf("no Trades") + } + + if expectTradesCount, actualTradesCount := len(buyResult.Trades), len(getTradesRes); expectTradesCount != actualTradesCount { + t.Fatalf("Expected trades count %d, actual: %d", expectTradesCount, actualTradesCount) + } + + for i, trade := range buyResult.Trades { + if trade.TradeSeq != getTradesRes[i].TradeSeq { + t.Errorf("Expected TradeSeq %d, actual %d", trade.TradeSeq, getTradesRes[i].TradeSeq) + } + + if trade.TradeID != getTradesRes[i].TradeID { + t.Errorf("Expected TradeID %s, actual %s", trade.TradeID, getTradesRes[i].TradeID) + } + + if trade.Amount != getTradesRes[i].Amount { + t.Errorf("Expected Amount %f, actual %f", trade.Amount, getTradesRes[i].Amount) + } + + if trade.Price != getTradesRes[i].Price { + t.Errorf("Expected Price %f, actual %f", trade.Price, getTradesRes[i].Price) + } + } + +} + func TestJsonOmitempty(t *testing.T) { params := &models.BuyParams{ InstrumentName: "BTC-PERPETUAL", From 94d066381f03652ce7a7f40a498b7c62317e2f04 Mon Sep 17 00:00:00 2001 From: kevenli Date: Mon, 21 Oct 2024 14:43:21 +0000 Subject: [PATCH 5/8] fix GetUserTradesByOrder returning wrong type --- api_trading.go | 2 +- client_test.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/api_trading.go b/api_trading.go index d367516..a42b883 100644 --- a/api_trading.go +++ b/api_trading.go @@ -104,7 +104,7 @@ func (c *Client) GetUserTradesByInstrumentAndTime(params *models.GetUserTradesBy return } -func (c *Client) GetUserTradesByOrder(params *models.GetUserTradesByOrderParams) (result models.GetUserTradesResponse, err error) { +func (c *Client) GetUserTradesByOrder(params *models.GetUserTradesByOrderParams) (result []models.Trade, err error) { err = c.Call("private/get_user_trades_by_order", params, &result) return } diff --git a/client_test.go b/client_test.go index d33088f..05a81be 100644 --- a/client_test.go +++ b/client_test.go @@ -2,9 +2,10 @@ package deribit import ( "encoding/json" + "testing" + "github.com/frankrap/deribit-api/models" "github.com/stretchr/testify/assert" - "testing" ) func newClient() *Client { @@ -135,6 +136,56 @@ func TestClient_Buy(t *testing.T) { t.Logf("%#v", result) } +func TestClient_GetUserTradesByOrder(t *testing.T) { + client := newClient() + params := &models.BuyParams{ + InstrumentName: "BTC-PERPETUAL", + Amount: 10, + Price: 20000.0, + Type: "market", + Label: "TestClient_CancelByLabel", + } + buyResult, err := client.Buy(params) + if err != nil { + t.Fatal(err) + } + + t.Logf("%#v", buyResult) + + getTradesRes, err := client.GetUserTradesByOrder(&models.GetUserTradesByOrderParams{OrderID: buyResult.Order.OrderID}) + + if err != nil { + t.Fatal(err) + } + + if actualTradesCount := len(getTradesRes); actualTradesCount == 0 { + t.Errorf("no Trades") + } + + if expectTradesCount, actualTradesCount := len(buyResult.Trades), len(getTradesRes); expectTradesCount != actualTradesCount { + t.Fatalf("Expected trades count %d, actual: %d", expectTradesCount, actualTradesCount) + } + + for i, trade := range buyResult.Trades { + if trade.TradeSeq != getTradesRes[i].TradeSeq { + t.Errorf("Expected TradeSeq %d, actual %d", trade.TradeSeq, getTradesRes[i].TradeSeq) + } + + if trade.TradeID != getTradesRes[i].TradeID { + t.Errorf("Expected TradeID %s, actual %s", trade.TradeID, getTradesRes[i].TradeID) + } + + if trade.Amount != getTradesRes[i].Amount { + t.Errorf("Expected Amount %f, actual %f", trade.Amount, getTradesRes[i].Amount) + } + + if trade.Price != getTradesRes[i].Price { + t.Errorf("Expected Price %f, actual %f", trade.Price, getTradesRes[i].Price) + } + } + +} + func TestJsonOmitempty(t *testing.T) { params := &models.BuyParams{ InstrumentName: "BTC-PERPETUAL", From 100b9d188d8c922000b837ddc05ea8c525c980c4 Mon Sep 17 00:00:00 2001 From: kevenli Date: Tue, 22 Oct 2024 01:34:24 +0000 Subject: [PATCH 6/8] GetOrderStateByLabel --- api_trading.go | 5 ++++ client_test.go | 46 ++++++++++++++++++++++++++++++ models/get_order_state_by_label.go | 6 ++++ 3 files changed, 57 insertions(+) create mode 100644 models/get_order_state_by_label.go diff --git a/api_trading.go b/api_trading.go index 59e6895..75b3ff3 100644 --- a/api_trading.go +++ b/api_trading.go @@ -84,6 +84,11 @@ func (c *Client) GetOrderState(params *models.GetOrderStateParams) (result model return } +func (c *Client) GetOrderStateByLabel(params *models.GetOrderStateByLabelParams) (result []models.Order, err error) { + err = c.Call("/private/get_order_state_by_label", params, &result) + return +} + func (c *Client) GetStopOrderHistory(params *models.GetStopOrderHistoryParams) (result models.GetStopOrderHistoryResponse, err error) { err = c.Call("private/get_stop_order_history", params, &result) return diff --git a/client_test.go b/client_test.go index e2e7251..8000cc5 100644 --- a/client_test.go +++ b/client_test.go @@ -2,6 +2,7 @@ package deribit import ( "encoding/json" + "math/rand" "testing" "github.com/frankrap/deribit-api/models" @@ -219,6 +220,51 @@ func TestClient_GetUserTradesByOrder(t *testing.T) { } +var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + +func randSeq(n int) string { + b := make([]rune, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return string(b) +} + +func TestClient_GetOrderByLabel(t *testing.T) { + client := newClient() + orderLabel := "TestClient_GetOrderByLabel" + randSeq(6) + params := &models.BuyParams{ + InstrumentName: "BTC-PERPETUAL", + Amount: 10, + Price: 20000.0, + Type: "limit", + Label: orderLabel, + } + newOrder, err := client.Buy(params) + if err != nil { + t.Fatal(err) + } + + fetchedOrders, err := client.GetOrderStateByLabel(&models.GetOrderStateByLabelParams{Label: orderLabel, Currency: "BTC"}) + if err != nil { + t.Error(err) + } else { + if len(fetchedOrders) == 0 { + t.Error("Count of fetchedOrders is equal to 0.") + } else { + lastOrder := fetchedOrders[len(fetchedOrders)-1] + if lastOrder.OrderID != newOrder.Order.OrderID { + t.Errorf("fetchedOrder OrderID != new OrderID, %s <> %s", lastOrder.OrderID, newOrder.Order.OrderID) + } + } + } + + cancelResult, err := client.CancelByLabel(&models.CancelByLabelParams{Label: orderLabel, Currency: "BTC"}) + if err != nil { + t.Fatal(err, cancelResult) + } +} + func TestJsonOmitempty(t *testing.T) { params := &models.BuyParams{ InstrumentName: "BTC-PERPETUAL", diff --git a/models/get_order_state_by_label.go b/models/get_order_state_by_label.go new file mode 100644 index 0000000..6a9271b --- /dev/null +++ b/models/get_order_state_by_label.go @@ -0,0 +1,6 @@ +package models + +type GetOrderStateByLabelParams struct { + Currency string `json:"currency"` + Label string `json:"label"` +} From 862b21bec6c49aafb848a43a035a6d26ecde62de Mon Sep 17 00:00:00 2001 From: kevenli Date: Tue, 22 Oct 2024 02:31:29 +0000 Subject: [PATCH 7/8] GetInstruments add CounterCurrency, InstrumentType fields --- client_test.go | 38 ++++++++++++++++++++++++++++++++++++++ models/instrument.go | 2 ++ 2 files changed, 40 insertions(+) diff --git a/client_test.go b/client_test.go index 8000cc5..1ce734c 100644 --- a/client_test.go +++ b/client_test.go @@ -38,6 +38,44 @@ func TestClient_Test(t *testing.T) { t.Logf("%v", result) } +func TestClient_GetInstruments_spot(t *testing.T) { + client := newClient() + instruments, err := client.GetInstruments(&models.GetInstrumentsParams{Currency: "BTC", Kind: "spot"}) + assert.Nil(t, err) + assert.True(t, len(instruments) > 0, "No instrument gotten") + for _, instrument := range instruments { + assert.NotEmpty(t, instrument.InstrumentName) + assert.NotEmpty(t, instrument.BaseCurrency) + assert.NotEmpty(t, instrument.ContractSize) + assert.NotEmpty(t, instrument.InstrumentType) + assert.NotEmpty(t, instrument.CounterCurrency) + assert.NotEmpty(t, instrument.CreationTimestamp) + assert.NotEmpty(t, instrument.ExpirationTimestamp) + assert.NotEmpty(t, instrument.Kind) + assert.Truef(t, instrument.CounterCurrency == "BTC" || instrument.BaseCurrency == "BTC", "%+v", instrument) + assert.Equal(t, instrument.Kind, "spot") + } +} + +func TestClient_GetInstruments_future(t *testing.T) { + client := newClient() + instruments, err := client.GetInstruments(&models.GetInstrumentsParams{Currency: "BTC", Kind: "future"}) + assert.Nil(t, err) + assert.True(t, len(instruments) > 0, "No instrument gotten") + for _, instrument := range instruments { + assert.NotEmpty(t, instrument.InstrumentName) + assert.NotEmpty(t, instrument.BaseCurrency) + assert.NotEmpty(t, instrument.ContractSize) + assert.NotEmpty(t, instrument.InstrumentType) + assert.NotEmpty(t, instrument.CounterCurrency) + assert.NotEmpty(t, instrument.CreationTimestamp) + assert.NotEmpty(t, instrument.ExpirationTimestamp) + assert.NotEmpty(t, instrument.Kind) + assert.Truef(t, instrument.CounterCurrency == "BTC" || instrument.BaseCurrency == "BTC", "%+v", instrument) + assert.Equal(t, instrument.Kind, "future") + } +} + func TestClient_GetBookSummaryByCurrency(t *testing.T) { client := newClient() params := &models.GetBookSummaryByCurrencyParams{ diff --git a/models/instrument.go b/models/instrument.go index 09712e0..6a06a80 100644 --- a/models/instrument.go +++ b/models/instrument.go @@ -14,4 +14,6 @@ type Instrument struct { CreationTimestamp int64 `json:"creation_timestamp"` ContractSize float64 `json:"contract_size"` BaseCurrency string `json:"base_currency"` + CounterCurrency string `json:"counter_currency"` + InstrumentType string `json:"instrument_type"` } From 7bcef0d66d4bfd7b569451c8efb9d2ed711a4ba5 Mon Sep 17 00:00:00 2001 From: kevenli Date: Tue, 22 Oct 2024 05:21:31 +0000 Subject: [PATCH 8/8] GetInstruments add SettlementCurrency --- client_test.go | 2 ++ models/instrument.go | 1 + 2 files changed, 3 insertions(+) diff --git a/client_test.go b/client_test.go index 1ce734c..66f7668 100644 --- a/client_test.go +++ b/client_test.go @@ -70,8 +70,10 @@ func TestClient_GetInstruments_future(t *testing.T) { assert.NotEmpty(t, instrument.CounterCurrency) assert.NotEmpty(t, instrument.CreationTimestamp) assert.NotEmpty(t, instrument.ExpirationTimestamp) + assert.NotEmpty(t, instrument.SettlementCurrency) assert.NotEmpty(t, instrument.Kind) assert.Truef(t, instrument.CounterCurrency == "BTC" || instrument.BaseCurrency == "BTC", "%+v", instrument) + assert.Truef(t, instrument.SettlementCurrency == "BTC", "%+v", instrument) assert.Equal(t, instrument.Kind, "future") } } diff --git a/models/instrument.go b/models/instrument.go index 6a06a80..327c02d 100644 --- a/models/instrument.go +++ b/models/instrument.go @@ -16,4 +16,5 @@ type Instrument struct { BaseCurrency string `json:"base_currency"` CounterCurrency string `json:"counter_currency"` InstrumentType string `json:"instrument_type"` + SettlementCurrency string `json:"settlement_currency"` }