From fd08b4eecb075b7aa95291be2b40d4c375536c17 Mon Sep 17 00:00:00 2001 From: Atik Date: Wed, 4 Nov 2020 17:26:41 +0200 Subject: [PATCH 01/34] Added ReplaceStopOrder, and fixes. --- examples/rest/main.go | 5 ++- rest/api_order.go | 28 +++++++++++++++++ rest/result.go | 71 +++++++++++++++++++++++++------------------ 3 files changed, 72 insertions(+), 32 deletions(-) diff --git a/examples/rest/main.go b/examples/rest/main.go index 39923a7..666bbc0 100644 --- a/examples/rest/main.go +++ b/examples/rest/main.go @@ -9,7 +9,7 @@ func main() { //baseURL := "https://api.bybit.com/" // 主网络 baseURL := "https://api-testnet.bybit.com/" // 测试网络 b := rest.New(nil, - baseURL, "YIxOY2RhFkylPudq96", "Bg9G2oFOb3aaIMguD3FOvOJJVBycaoXqXNcI") + baseURL, "YIxOY2RhFkylPudq96", "Bg9G2oFOb3aaIMguD3FOvOJJVBycaoXqXNcI", true) // 获取持仓 positions, err := b.GetPositions() @@ -27,9 +27,8 @@ func main() { qty := 30 price := 7000.0 timeInForce := "GoodTillCancel" - reduceOnly := false order, err := b.CreateOrder(side, - orderType, price, qty, timeInForce, reduceOnly, symbol) + orderType, price, qty, timeInForce, false, symbol) if err != nil { log.Println(err) return diff --git a/rest/api_order.go b/rest/api_order.go index 8822229..04b00e7 100644 --- a/rest/api_order.go +++ b/rest/api_order.go @@ -256,6 +256,34 @@ func (b *ByBit) GetStopOrders(orderID string, orderLinkID string, stopOrderStatu return } +// ReplaceStopOrder +func (b *ByBit) ReplaceStopOrder(symbol string, orderID string, qty int, price float64, triggerPrice float64) (result Order, err error) { + var cResult ReplaceStopOrderResult + params := map[string]interface{}{} + params["stop_order_id"] = orderID + params["symbol"] = symbol + if qty > 0 { + params["p_r_qty"] = qty + } + if price > 0 { + params["p_r_price"] = price + } + if triggerPrice > 0 { + params["p_r_trigger_price"] = triggerPrice + } + var resp []byte + resp, err = b.SignedRequest(http.MethodPost, "open-api/stop-order/replace", params, &cResult) + if err != nil { + return + } + if cResult.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) + return + } + result.StopOrderID = cResult.Result.StopOrderID + return +} + // GetOrderByID func (b *ByBit) GetOrderByID(orderID string, orderLinkID string, symbol string) (result OrderV2, err error) { var cResult QueryOrderResult diff --git a/rest/result.go b/rest/result.go index 2f57f2a..9336565 100644 --- a/rest/result.go +++ b/rest/result.go @@ -77,12 +77,12 @@ type Ticker struct { Price1HPcnt float64 `json:"price_1h_pcnt,string"` MarkPrice float64 `json:"mark_price,string"` IndexPrice float64 `json:"index_price,string"` - OpenInterest int `json:"open_interest"` + OpenInterest float64 `json:"open_interest"` OpenValue float64 `json:"open_value,string"` TotalTurnover float64 `json:"total_turnover,string"` Turnover24H float64 `json:"turnover_24h,string"` - TotalVolume int64 `json:"total_volume"` - Volume24H int64 `json:"volume_24h"` + TotalVolume float64 `json:"total_volume"` + Volume24H float64 `json:"volume_24h"` FundingRate float64 `json:"funding_rate,string"` PredictedFundingRate float64 `json:"predicted_funding_rate,string"` NextFundingTime time.Time `json:"next_funding_time"` @@ -204,6 +204,10 @@ type OrderLite struct { OrderID string `json:"order_id"` } +type StopOrderLite struct { + StopOrderID string `json:"stop_order_id"` +} + type ReplaceOrderResult struct { RetCode int `json:"ret_code"` RetMsg string `json:"ret_msg"` @@ -213,6 +217,15 @@ type ReplaceOrderResult struct { RateLimitStatus int `json:"rate_limit_status"` } +type ReplaceStopOrderResult struct { + RetCode int `json:"ret_code"` + RetMsg string `json:"ret_msg"` + ExtCode string `json:"ext_code"` + Result StopOrderLite `json:"result"` + TimeNow string `json:"time_now"` + RateLimitStatus int `json:"rate_limit_status"` +} + type CancelOrderResult struct { RetCode int `json:"ret_code"` RetMsg string `json:"ret_msg"` @@ -239,35 +252,35 @@ type OrderListResult struct { // Order ... type Order struct { - OrderID string `json:"order_id"` + OrderID string `json:"order_id"` StopOrderID string `json:"stop_order_id"` - UserID int `json:"user_id"` - Symbol string `json:"symbol"` - Side string `json:"side"` - OrderType string `json:"order_type"` - Price float64 `json:"price"` - Qty float64 `json:"qty"` - TimeInForce string `json:"time_in_force"` - //StopOrderType string `json:"stop_order_type,omitempty"` - //StopPx sjson.Number `json:"stop_px,omitempty"` - OrderStatus string `json:"order_status"` - //StopOrderStatus string `json:"stop_order_status"` - LastExecTime string `json:"last_exec_time"` - LastExecPrice float64 `json:"last_exec_price"` - LeavesQty float64 `json:"leaves_qty"` - CumExecQty float64 `json:"cum_exec_qty"` - CumExecValue float64 `json:"cum_exec_value"` - CumExecFee float64 `json:"cum_exec_fee"` - RejectReason string `json:"reject_reason"` - OrderLinkID string `json:"order_link_id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - ExtFields *ExtFields `json:"ext_fields,omitempty"` + UserID int `json:"user_id"` + Symbol string `json:"symbol"` + Side string `json:"side"` + OrderType string `json:"order_type"` + Price sjson.Number `json:"price"` + Qty float64 `json:"qty"` + TimeInForce string `json:"time_in_force"` + StopOrderType string `json:"stop_order_type,omitempty"` + StopPx sjson.Number `json:"stop_px,omitempty"` + OrderStatus string `json:"order_status"` + StopOrderStatus string `json:"stop_order_status"` + LastExecTime sjson.Number `json:"last_exec_time"` + LastExecPrice sjson.Number `json:"last_exec_price"` + LeavesQty float64 `json:"leaves_qty"` + CumExecQty float64 `json:"cum_exec_qty"` + CumExecValue sjson.Number `json:"cum_exec_value"` + CumExecFee sjson.Number `json:"cum_exec_fee"` + RejectReason string `json:"reject_reason"` + OrderLinkID string `json:"order_link_id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + ExtFields *ExtFields `json:"ext_fields,omitempty"` } type ExtFields struct { - ReduceOnly bool `json:"reduce_only"` - OpFrom string `json:"op_from"` + ReduceOnly bool `json:"reduce_only"` + //OpFrom sjson.Number `json:"op_from"` Remark string `json:"remark"` OReqNum int64 `json:"o_req_num"` XreqType string `json:"xreq_type"` @@ -295,7 +308,7 @@ func (e *ExtFields) UnmarshalJSON(b []byte) error { o := InExtFields{} if err := json.Unmarshal(b, &o); err == nil { e.ReduceOnly = o.ReduceOnly - e.OpFrom = o.OpFrom + //e.OpFrom = o.OpFrom e.Remark = o.Remark e.OReqNum = o.OReqNum e.XreqType = o.XreqType From 7d11ff5c71291c0088a262da4c901ebe869d117e Mon Sep 17 00:00:00 2001 From: Atik Date: Thu, 5 Nov 2020 09:33:02 +0200 Subject: [PATCH 02/34] Fixes. --- rest/result.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rest/result.go b/rest/result.go index 9336565..8a6ca07 100644 --- a/rest/result.go +++ b/rest/result.go @@ -275,12 +275,12 @@ type Order struct { OrderLinkID string `json:"order_link_id"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` - ExtFields *ExtFields `json:"ext_fields,omitempty"` + //ExtFields *ExtFields `json:"ext_fields,omitempty"` } type ExtFields struct { - ReduceOnly bool `json:"reduce_only"` - //OpFrom sjson.Number `json:"op_from"` + ReduceOnly bool `json:"reduce_only"` + OpFrom string `json:"op_from"` Remark string `json:"remark"` OReqNum int64 `json:"o_req_num"` XreqType string `json:"xreq_type"` @@ -308,7 +308,7 @@ func (e *ExtFields) UnmarshalJSON(b []byte) error { o := InExtFields{} if err := json.Unmarshal(b, &o); err == nil { e.ReduceOnly = o.ReduceOnly - //e.OpFrom = o.OpFrom + e.OpFrom = o.OpFrom e.Remark = o.Remark e.OReqNum = o.OReqNum e.XreqType = o.XreqType From c38bb07931506cabf8d4bc6f710df92f549c7125 Mon Sep 17 00:00:00 2001 From: atik-lab Date: Wed, 11 Nov 2020 12:06:59 +0200 Subject: [PATCH 03/34] Adding funding api. --- go.sum | 4 ---- rest/api2_public.go | 55 +++++++++++++++++++++++++++++++++++++++++++++ rest/api2_test.go | 29 ++++++++++++++++++++++++ rest/api_test.go | 9 ++------ 4 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 rest/api2_public.go create mode 100644 rest/api2_test.go diff --git a/go.sum b/go.sum index 43a44dd..05c7e90 100644 --- a/go.sum +++ b/go.sum @@ -23,12 +23,8 @@ github.com/recws-org/recws v1.2.1 h1:bYocRkAsS71hlQ9AMCVS+hYXHEgEyQsAbYKXf394gZ8 github.com/recws-org/recws v1.2.1/go.mod h1:SxTgwQU/jqYSzEgUh4ifDxq/7enApS150f8nZ5Sczk8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/tidwall/gjson v1.3.5 h1:2oW9FBNu8qt9jy5URgrzsVx/T/KSn3qn/smJQ0crlDQ= -github.com/tidwall/gjson v1.3.5/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc= github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc= diff --git a/rest/api2_public.go b/rest/api2_public.go new file mode 100644 index 0000000..c71717d --- /dev/null +++ b/rest/api2_public.go @@ -0,0 +1,55 @@ +package rest + +import ( + sjson "encoding/json" + "net/http" + "time" +) + +type FundingResult struct { + RetCode int `json:"ret_code"` + RetMsg string `json:"ret_msg"` + ExtCode string `json:"ext_code"` + ExtInfo string `json:"ext_info"` + Result Funding `json:"result"` + TimeNow string `json:"time_now"` +} + +type Funding struct { + CurrentPage int `json:"current_page"` + Data []FundingData `json:"data"` + FirstPageUrl string `json:"first_page_url"` + From int `json:"from"` + LastPage int `json:"last_page"` + LastPageUrl string `json:"last_page_url"` + NextPageUrl string `json:"next_page_url"` + Path string `json:"path"` + PerPage sjson.Number `json:"per_page"` + PrevPageUrl string `json:"prev_page_url"` + To int `json:"to"` + Total int `json:"total"` +} + +type FundingData struct { + Id int `json:"id"` + Symbol string `json:"symbol"` + Value sjson.Number `json:"value"` + Time time.Time `json:"time"` +} + +// https://api2.bybit.com/funding-rate/list?symbol=BTCUSD&date=&export=false&page=1&limit=20 +// To use this you will need to set b.BaseUrl to api2.bybit.com or api2-testnet.bybit.com +func (b *ByBit) GetFunding(symbol string, page int) (result []FundingData, e error) { + var ret FundingResult + params := map[string]interface{}{} + params["symbol"] = symbol + params["page"] = page + params["limit"] = 20 // fixed limit 20 + params["export"] = false // fixed export + _, e = b.PublicRequest(http.MethodGet, "funding-rate/list", params, &ret) + if e != nil { + return + } + result = ret.Result.Data + return +} diff --git a/rest/api2_test.go b/rest/api2_test.go new file mode 100644 index 0000000..b416f81 --- /dev/null +++ b/rest/api2_test.go @@ -0,0 +1,29 @@ +package rest_test + +import ( + "github.com/frankrap/bybit-api/rest" + "log" + "testing" +) + +func newByBit2() *rest.ByBit { + baseURL := "https://api2-testnet.bybit.com/" + apiKey := "6IASD6KDBdunn5qLpT" + secretKey := "nXjZMUiB3aMiPaQ9EUKYFloYNd0zM39RjRWF" + b := rest.New(nil, baseURL, apiKey, secretKey, true) + err := b.SetCorrectServerTime() + if err != nil { + log.Printf("%v", err) + } + return b +} + +func TestByBit_GetFunding(t *testing.T) { + b := newByBit2() + funding, e := b.GetFunding("BTCUSD", 1) + if e != nil { + t.Error(e) + return + } + t.Logf("Funding: %v", funding) +} diff --git a/rest/api_test.go b/rest/api_test.go index 3ad6eff..bf065d1 100644 --- a/rest/api_test.go +++ b/rest/api_test.go @@ -235,12 +235,7 @@ func TestByBit_GetOrders(t *testing.T) { assert.Nil(t, err) //t.Logf("%#v", orders) for _, order := range orders { - if order.ExtFields != nil { - t.Logf("%#v %v", order, *order.ExtFields) - t.Logf("ReduceOnly: %v", order.ExtFields.ReduceOnly) - } else { - t.Logf("%#v", order) - } + t.Logf("%#v", order) } } @@ -334,7 +329,7 @@ func TestByBit_GetLeverages(t *testing.T) { func TestByBit_SetLeverage(t *testing.T) { b := newByBit() - b.SetLeverage(3, "BTCUSD") + _ = b.SetLeverage(3, "BTCUSD") } func TestByBit_GetPositions(t *testing.T) { From 435a3496bc4b00553b3cc84c525413aff1262582 Mon Sep 17 00:00:00 2001 From: atik-lab Date: Wed, 11 Nov 2020 15:38:53 +0200 Subject: [PATCH 04/34] Adding getting conditional orders. --- rest/api_order.go | 20 ++++++++++++++------ rest/result.go | 32 ++++++++++++++++---------------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/rest/api_order.go b/rest/api_order.go index 04b00e7..2900b1b 100644 --- a/rest/api_order.go +++ b/rest/api_order.go @@ -213,15 +213,22 @@ func (b *ByBit) getOrders(orderID string, orderLinkID string, sort string, order return } -// GetStopOrders 查询条件委托单 +// GetStopOrders +func (b *ByBit) GetStopOrders(order string, page int, limit int, stopOrderStatus string, + symbol string) ([]StopOrder, error) { + return b.getStopOrders("", "", stopOrderStatus, order, page, limit, symbol) +} + +// getStopOrders 查询条件委托单 // orderID: 条件委托单ID // orderLinkID: 机构自定义订单ID // order: 排序字段为created_at,升序降序,默认降序 (desc asc ) // page: 页码,默认取第一页数据 // stopOrderStatus 条件单状态: Untriggered: 等待市价触发条件单; Triggered: 市价已触发条件单; Cancelled: 取消; Active: 条件单触发成功且下单成功; Rejected: 条件触发成功但下单失败 // limit: 一页数量,默认一页展示20条数据;最大支持50条每页 -func (b *ByBit) GetStopOrders(orderID string, orderLinkID string, stopOrderStatus string, order string, - page int, limit int, symbol string) (result GetStopOrdersResult, err error) { +func (b *ByBit) getStopOrders(orderID string, orderLinkID string, stopOrderStatus string, order string, + page int, limit int, symbol string) (result []StopOrder, err error) { + var cResult GetStopOrdersResult if limit == 0 { limit = 20 @@ -244,15 +251,16 @@ func (b *ByBit) GetStopOrders(orderID string, orderLinkID string, stopOrderStatu params["page"] = page params["limit"] = limit var resp []byte - resp, err = b.SignedRequest(http.MethodGet, "open-api/stop-order/list", params, &result) + resp, err = b.SignedRequest(http.MethodGet, "open-api/stop-order/list", params, &cResult) if err != nil { return } - if result.RetCode != 0 { - err = fmt.Errorf("%v body: [%v]", result.RetMsg, string(resp)) + if cResult.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) return } + result = cResult.Result.Data return } diff --git a/rest/result.go b/rest/result.go index 8a6ca07..837e5d2 100644 --- a/rest/result.go +++ b/rest/result.go @@ -525,22 +525,22 @@ type CancelStopOrdersV2Result struct { } type StopOrder struct { - UserID int64 `json:"user_id"` - StopOrderStatus string `json:"stop_order_status"` - Symbol string `json:"symbol"` - Side string `json:"side"` - OrderType string `json:"order_type"` - Price float64 `json:"price"` - Qty float64 `json:"qty"` - TimeInForce string `json:"time_in_force"` - StopOrderType string `json:"stop_order_type"` - TriggerBy string `json:"trigger_by"` - BasePrice float64 `json:"base_price"` - OrderLinkID string `json:"order_link_id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - StopPx float64 `json:"stop_px"` - StopOrderID string `json:"stop_order_id"` + UserID int64 `json:"user_id"` + StopOrderStatus string `json:"stop_order_status"` + Symbol string `json:"symbol"` + Side string `json:"side"` + OrderType string `json:"order_type"` + Price sjson.Number `json:"price"` + Qty sjson.Number `json:"qty"` + TimeInForce string `json:"time_in_force"` + StopOrderType string `json:"stop_order_type"` + TriggerBy string `json:"trigger_by"` + BasePrice sjson.Number `json:"base_price"` + OrderLinkID string `json:"order_link_id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + StopPx sjson.Number `json:"stop_px"` + StopOrderID string `json:"stop_order_id"` } type GetStopOrdersResultData struct { From 99279e45eb0d854cbf1570aa9f0d0a11d1884674 Mon Sep 17 00:00:00 2001 From: Atik Date: Wed, 11 Nov 2020 19:12:42 +0200 Subject: [PATCH 05/34] Adding limit to get funding. --- rest/api2_public.go | 4 ++-- rest/api2_test.go | 2 +- rest/api_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rest/api2_public.go b/rest/api2_public.go index c71717d..825878b 100644 --- a/rest/api2_public.go +++ b/rest/api2_public.go @@ -39,12 +39,12 @@ type FundingData struct { // https://api2.bybit.com/funding-rate/list?symbol=BTCUSD&date=&export=false&page=1&limit=20 // To use this you will need to set b.BaseUrl to api2.bybit.com or api2-testnet.bybit.com -func (b *ByBit) GetFunding(symbol string, page int) (result []FundingData, e error) { +func (b *ByBit) GetFunding(symbol string, page int, limit int) (result []FundingData, e error) { var ret FundingResult params := map[string]interface{}{} params["symbol"] = symbol params["page"] = page - params["limit"] = 20 // fixed limit 20 + params["limit"] = limit // fixed limit 20 params["export"] = false // fixed export _, e = b.PublicRequest(http.MethodGet, "funding-rate/list", params, &ret) if e != nil { diff --git a/rest/api2_test.go b/rest/api2_test.go index b416f81..14e543d 100644 --- a/rest/api2_test.go +++ b/rest/api2_test.go @@ -20,7 +20,7 @@ func newByBit2() *rest.ByBit { func TestByBit_GetFunding(t *testing.T) { b := newByBit2() - funding, e := b.GetFunding("BTCUSD", 1) + funding, e := b.GetFunding("BTCUSD", 1, 200) if e != nil { t.Error(e) return diff --git a/rest/api_test.go b/rest/api_test.go index bf065d1..ab34fc5 100644 --- a/rest/api_test.go +++ b/rest/api_test.go @@ -257,10 +257,10 @@ func TestByBit_GetStopOrders(t *testing.T) { symbol := "BTCUSD" // Untriggered: 等待市价触发条件单; Triggered: 市价已触发条件单; Cancelled: 取消; Active: 条件单触发成功且下单成功; Rejected: 条件触发成功但下单失败 status := "Untriggered,Triggered,Active" - result, err := b.GetStopOrders("", "", status, "", 0, 10, symbol) + result, err := b.GetStopOrders("", 1, 1, status, symbol) assert.Nil(t, err) //t.Logf("%#v", orders) - for _, order := range result.Result.Data { + for _, order := range result { //if order.ExtFields != nil { // t.Logf("%#v %v", order, *order.ExtFields) //} else { From baa1d74a106859ecd39b1d658e535d1687c2835e Mon Sep 17 00:00:00 2001 From: Atik Date: Thu, 12 Nov 2020 17:53:38 +0200 Subject: [PATCH 06/34] Adding limit to get funding. --- rest/api2_test.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/rest/api2_test.go b/rest/api2_test.go index 14e543d..4a68f0e 100644 --- a/rest/api2_test.go +++ b/rest/api2_test.go @@ -2,7 +2,6 @@ package rest_test import ( "github.com/frankrap/bybit-api/rest" - "log" "testing" ) @@ -11,10 +10,6 @@ func newByBit2() *rest.ByBit { apiKey := "6IASD6KDBdunn5qLpT" secretKey := "nXjZMUiB3aMiPaQ9EUKYFloYNd0zM39RjRWF" b := rest.New(nil, baseURL, apiKey, secretKey, true) - err := b.SetCorrectServerTime() - if err != nil { - log.Printf("%v", err) - } return b } From 4499f9eec6b76e3ac7938173164b09a710c29ce8 Mon Sep 17 00:00:00 2001 From: atik-lab Date: Tue, 17 Nov 2020 12:37:15 +0200 Subject: [PATCH 07/34] Update for the RestAPI update. https://bybit-exchange.github.io/docs/inverse/#2020-11-10 --- examples/rest/main.go | 3 +- rest/api.go | 86 ++------- rest/api_order.go | 352 ++++++++++-------------------------- rest/api_test.go | 139 +++----------- rest/result.go | 408 +++++++++--------------------------------- 5 files changed, 228 insertions(+), 760 deletions(-) diff --git a/examples/rest/main.go b/examples/rest/main.go index 666bbc0..d94c447 100644 --- a/examples/rest/main.go +++ b/examples/rest/main.go @@ -27,8 +27,7 @@ func main() { qty := 30 price := 7000.0 timeInForce := "GoodTillCancel" - order, err := b.CreateOrder(side, - orderType, price, qty, timeInForce, false, symbol) + order, err := b.CreateOrder(side, orderType, price, qty, timeInForce, 0, 0, false, false, "", symbol) if err != nil { log.Println(err) return diff --git a/rest/api.go b/rest/api.go index b08fe7e..1802235 100644 --- a/rest/api.go +++ b/rest/api.go @@ -74,39 +74,13 @@ func (b *ByBit) GetWalletBalance(coin string) (result Balance, err error) { return } -// GetLeverages 获取用户杠杆 -func (b *ByBit) GetLeverages() (result map[string]LeverageItem, err error) { - var r GetLeverageResult - params := map[string]interface{}{} - _, err = b.SignedRequest(http.MethodGet, "user/leverage", params, &r) - if err != nil { - return - } - result = r.Result - return -} - -// SetLeverage 设置杠杆 -func (b *ByBit) SetLeverage(leverage int, symbol string) (err error) { - var r BaseResult - params := map[string]interface{}{} - params["symbol"] = symbol - params["leverage"] = fmt.Sprintf("%v", leverage) - _, err = b.SignedRequest(http.MethodPost, "user/leverage", params, &r) - if err != nil { - return - } - log.Println(r) - return -} - // GetPositions 获取我的仓位 func (b *ByBit) GetPositions() (result []Position, err error) { - var r PositionListResult + var r PositionArrayResponse params := map[string]interface{}{} var resp []byte - resp, err = b.SignedRequest(http.MethodGet, "position/list", params, &r) + resp, err = b.SignedRequest(http.MethodGet, "v2/private/position/list", params, &r) if err != nil { return } @@ -115,51 +89,13 @@ func (b *ByBit) GetPositions() (result []Position, err error) { return } - for _, v := range r.Result { - result = append(result, b.convertPositionV1(v)) - } - return -} - -func (b *ByBit) convertPositionV1(position PositionV1) (result Position) { - result.ID = position.ID - result.UserID = position.UserID - result.RiskID = position.RiskID - result.Symbol = position.Symbol - result.Size = position.Size - result.Side = position.Side - result.EntryPrice = position.EntryPrice - result.LiqPrice = position.LiqPrice - result.BustPrice = position.BustPrice - result.TakeProfit = position.TakeProfit - result.StopLoss = position.StopLoss - result.TrailingStop = position.TrailingStop - result.PositionValue = position.PositionValue - result.Leverage = position.Leverage - result.PositionStatus = position.PositionStatus - result.AutoAddMargin = position.AutoAddMargin - result.OrderMargin = position.OrderMargin - result.PositionMargin = position.PositionMargin - result.OccClosingFee = position.OccClosingFee - result.OccFundingFee = position.OccFundingFee - result.ExtFields = position.ExtFields - result.WalletBalance = position.WalletBalance - result.CumRealisedPnl = position.CumRealisedPnl - result.CumCommission = position.CumCommission - result.RealisedPnl = position.RealisedPnl - result.DeleverageIndicator = position.DeleverageIndicator - result.OcCalcData = position.OcCalcData - result.CrossSeq = position.CrossSeq - result.PositionSeq = position.PositionSeq - result.CreatedAt = position.CreatedAt - result.UpdatedAt = position.UpdatedAt - result.UnrealisedPnl = position.UnrealisedPnl + result = r.Result return } // GetPosition 获取我的仓位 func (b *ByBit) GetPosition(symbol string) (result Position, err error) { - var r GetPositionResult + var r PositionResponse params := map[string]interface{}{} params["symbol"] = symbol @@ -176,6 +112,20 @@ func (b *ByBit) GetPosition(symbol string) (result Position, err error) { return } +// SetLeverage 设置杠杆 +func (b *ByBit) SetLeverage(leverage int, symbol string) (err error) { + var r BaseResult + params := map[string]interface{}{} + params["symbol"] = symbol + params["leverage"] = fmt.Sprintf("%v", leverage) + _, err = b.SignedRequest(http.MethodPost, "user/leverage/save", params, &r) + if err != nil { + return + } + log.Println(r) + return +} + func (b *ByBit) PublicRequest(method string, apiURL string, params map[string]interface{}, result interface{}) (resp []byte, err error) { var keys []string for k := range params { diff --git a/rest/api_order.go b/rest/api_order.go index 2900b1b..9e4f15b 100644 --- a/rest/api_order.go +++ b/rest/api_order.go @@ -1,41 +1,32 @@ package rest import ( - "errors" "fmt" "net/http" ) -func (b *ByBit) CreateOrderV2(side string, orderType string, price float64, - qty int, timeInForce string, takeProfit float64, stopLoss float64, reduceOnly bool, - closeOnTrigger bool, orderLinkID string, symbol string) (result OrderV2, err error) { - var cResult CreateOrderV2Result +// getOrders 查询活动委托 +func (b *ByBit) GetOrders(symbol string, orderStatus string, direction string, limit int, cursor string) (result OrderListResponseResult, err error) { + var cResult OrderListResponse + + if limit == 0 { + limit = 20 + } + params := map[string]interface{}{} - params["side"] = side params["symbol"] = symbol - params["order_type"] = orderType - params["qty"] = qty - if price > 0 { - params["price"] = price - } - params["time_in_force"] = timeInForce - if takeProfit > 0 { - params["take_profit"] = takeProfit - } - if stopLoss > 0 { - params["stop_loss"] = stopLoss - } - if reduceOnly { - params["reduce_only"] = true + if orderStatus != "" { + params["order_status"] = orderStatus } - if closeOnTrigger { - params["close_on_trigger"] = true + if direction != "" { + params["direction"] = direction } - if orderLinkID != "" { - params["order_link_id"] = orderLinkID + params["limit"] = limit + if cursor != "" { + params["cursor"] = cursor } var resp []byte - resp, err = b.SignedRequest(http.MethodPost, "v2/private/order/create", params, &cResult) + resp, err = b.SignedRequest(http.MethodGet, "v2/private/order/list", params, &cResult) if err != nil { return } @@ -43,33 +34,41 @@ func (b *ByBit) CreateOrderV2(side string, orderType string, price float64, err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) return } + result = cResult.Result return } -// CreateOrder 创建委托单 -// symbol: 产品类型, 有效选项:BTCUSD,ETHUSD (BTCUSD ETHUSD) -// side: 方向, 有效选项:Buy, Sell (Buy Sell) -// orderType: Limit/Market -// price: 委托价格, 在没有仓位时,做多的委托价格需高于市价的10%、低于1百万。如有仓位时则需优于强平价。单笔价格增减最小单位为0.5。 -// qty: 委托数量, 单笔最大1百万 -// timeInForce: 执行策略, 有效选项:GoodTillCancel,ImmediateOrCancel,FillOrKill,PostOnly -// reduceOnly: 只减仓 -// symbol: 产品类型, 有效选项:BTCUSD,ETHUSD (BTCUSD ETHUSD) -func (b *ByBit) CreateOrder(side string, orderType string, price float64, qty int, timeInForce string, reduceOnly bool, symbol string) (result Order, err error) { - var cResult CreateOrderResult +func (b *ByBit) CreateOrder(side string, orderType string, price float64, + qty int, timeInForce string, takeProfit float64, stopLoss float64, reduceOnly bool, + closeOnTrigger bool, orderLinkID string, symbol string) (result Order, err error) { + var cResult OrderResponse params := map[string]interface{}{} params["side"] = side params["symbol"] = symbol params["order_type"] = orderType params["qty"] = qty - params["price"] = price + if price > 0 { + params["price"] = price + } params["time_in_force"] = timeInForce + if takeProfit > 0 { + params["take_profit"] = takeProfit + } + if stopLoss > 0 { + params["stop_loss"] = stopLoss + } if reduceOnly { params["reduce_only"] = true } + if closeOnTrigger { + params["close_on_trigger"] = true + } + if orderLinkID != "" { + params["order_link_id"] = orderLinkID + } var resp []byte - resp, err = b.SignedRequest(http.MethodPost, "open-api/order/create", params, &cResult) + resp, err = b.SignedRequest(http.MethodPost, "v2/private/order/create", params, &cResult) if err != nil { return } @@ -81,8 +80,9 @@ func (b *ByBit) CreateOrder(side string, orderType string, price float64, qty in return } +// ReplaceOrder func (b *ByBit) ReplaceOrder(symbol string, orderID string, qty int, price float64) (result Order, err error) { - var cResult ReplaceOrderResult + var cResult OrderResponse params := map[string]interface{}{} params["order_id"] = orderID params["symbol"] = symbol @@ -93,7 +93,7 @@ func (b *ByBit) ReplaceOrder(symbol string, orderID string, qty int, price float params["p_r_price"] = price } var resp []byte - resp, err = b.SignedRequest(http.MethodPost, "open-api/order/replace", params, &cResult) + resp, err = b.SignedRequest(http.MethodPost, "v2/private/order/replace", params, &cResult) if err != nil { return } @@ -105,41 +105,16 @@ func (b *ByBit) ReplaceOrder(symbol string, orderID string, qty int, price float return } -// CreateStopOrder 创建条件委托单 -// https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/zh_cn/rest_api.md#open-apistop-ordercreatepost -// symbol: 产品类型, 有效选项:BTCUSD,ETHUSD (BTCUSD ETHUSD) -// side: 方向, 有效选项:Buy, Sell (Buy Sell) -// orderType: Limit/Market -// price: 委托价格, 在没有仓位时,做多的委托价格需高于市价的10%、低于1百万。如有仓位时则需优于强平价。单笔价格增减最小单位为0.5。 -// qty: 委托数量, 单笔最大1百万 -// basePrice: 当前市价。用于和stop_px值进行比较,确定当前条件委托是看空到stop_px时触发还是看多到stop_px触发。主要是用来标识当前条件单预期的方向 -// stopPx: 条件委托下单时市价 -// triggerBy: 触发价格类型. 默认为上一笔成交价格 -// timeInForce: 执行策略, 有效选项:GoodTillCancel,ImmediateOrCancel,FillOrKill,PostOnly -// reduceOnly: 只减仓 -// symbol: 产品类型, 有效选项:BTCUSD,ETHUSD (BTCUSD ETHUSD) -func (b *ByBit) CreateStopOrder(side string, orderType string, price float64, basePrice float64, stopPx float64, - qty int, triggerBy string, timeInForce string, reduceOnly bool, symbol string) (result Order, err error) { - var cResult CreateOrderResult +// CancelOrder 撤销活动委托单 +func (b *ByBit) CancelOrder(orderID string, symbol string) (result Order, err error) { + var cResult OrderResponse params := map[string]interface{}{} - params["side"] = side params["symbol"] = symbol - params["order_type"] = orderType - params["qty"] = qty - if price > 0 { - params["price"] = price - } - params["base_price"] = basePrice - params["stop_px"] = stopPx - params["time_in_force"] = timeInForce - if reduceOnly { - params["reduce_only"] = true - } - if triggerBy != "" { - params["trigger_by"] = triggerBy + if orderID != "" { + params["order_id"] = orderID } var resp []byte - resp, err = b.SignedRequest(http.MethodPost, "open-api/stop-order/create", params, &cResult) + resp, err = b.SignedRequest(http.MethodPost, "v2/private/order/cancel", params, &cResult) if err != nil { return } @@ -147,60 +122,18 @@ func (b *ByBit) CreateStopOrder(side string, orderType string, price float64, ba err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) return } + result = cResult.Result return } -// GetOrders 查询活动委托 -// symbol -// orderID: 订单ID -// orderLinkID: 机构自定义订单ID -// sort: 排序字段,默认按创建时间排序 (created_at cum_exec_qty qty last_exec_price price cum_exec_value cum_exec_fee) -// order: 升序降序, 默认降序 (desc asc) -// page: 页码,默认取第一页数据 -// limit: 一页数量,一页默认展示20条数据 -func (b *ByBit) GetOrders(sort string, order string, page int, - limit int, orderStatus string, symbol string) (result []Order, err error) { - return b.getOrders("", "", sort, order, page, limit, orderStatus, symbol) -} - -// getOrders 查询活动委托 -// symbol -// orderID: 订单ID -// orderLinkID: 机构自定义订单ID -// sort: 排序字段,默认按创建时间排序 (created_at cum_exec_qty qty last_exec_price price cum_exec_value cum_exec_fee) -// order: 升序降序, 默认降序 (desc asc) -// page: 页码,默认取第一页数据 -// limit: 一页数量,一页默认展示20条数据 -func (b *ByBit) getOrders(orderID string, orderLinkID string, sort string, order string, page int, - limit int, orderStatus string, symbol string) (result []Order, err error) { - var cResult OrderListResult - - if limit == 0 { - limit = 20 - } - +// CancelAllOrder Cancel All Active Orders +func (b *ByBit) CancelAllOrder(symbol string) (result []Order, err error) { + var cResult OrderArrayResponse params := map[string]interface{}{} params["symbol"] = symbol - if orderID != "" { - params["order_id"] = orderID - } - if orderLinkID != "" { - params["order_link_id"] = orderLinkID - } - if sort != "" { - params["sort"] = sort - } - if order != "" { - params["order"] = order - } - params["page"] = page - params["limit"] = limit - if orderStatus != "" { - params["order_status"] = orderStatus - } var resp []byte - resp, err = b.SignedRequest(http.MethodGet, "open-api/order/list", params, &cResult) + resp, err = b.SignedRequest(http.MethodPost, "v2/private/order/cancelAll", params, &cResult) if err != nil { return } @@ -209,26 +142,13 @@ func (b *ByBit) getOrders(orderID string, orderLinkID string, sort string, order return } - result = cResult.Result.Data + result = cResult.Result return } -// GetStopOrders -func (b *ByBit) GetStopOrders(order string, page int, limit int, stopOrderStatus string, - symbol string) ([]StopOrder, error) { - return b.getStopOrders("", "", stopOrderStatus, order, page, limit, symbol) -} - // getStopOrders 查询条件委托单 -// orderID: 条件委托单ID -// orderLinkID: 机构自定义订单ID -// order: 排序字段为created_at,升序降序,默认降序 (desc asc ) -// page: 页码,默认取第一页数据 -// stopOrderStatus 条件单状态: Untriggered: 等待市价触发条件单; Triggered: 市价已触发条件单; Cancelled: 取消; Active: 条件单触发成功且下单成功; Rejected: 条件触发成功但下单失败 -// limit: 一页数量,默认一页展示20条数据;最大支持50条每页 -func (b *ByBit) getStopOrders(orderID string, orderLinkID string, stopOrderStatus string, order string, - page int, limit int, symbol string) (result []StopOrder, err error) { - var cResult GetStopOrdersResult +func (b *ByBit) GetStopOrders(symbol string, stopOrderStatus string, direction string, limit int, cursor string) (result StopOrderListResponseResult, err error) { + var cResult StopOrderListResponse if limit == 0 { limit = 20 @@ -236,22 +156,18 @@ func (b *ByBit) getStopOrders(orderID string, orderLinkID string, stopOrderStatu params := map[string]interface{}{} params["symbol"] = symbol - if orderID != "" { - params["stop_order_id"] = orderID - } - if orderLinkID != "" { - params["order_link_id"] = orderLinkID - } if stopOrderStatus != "" { params["stop_order_status"] = stopOrderStatus } - if order != "" { - params["order"] = order + if direction != "" { + params["direction"] = direction } - params["page"] = page params["limit"] = limit + if cursor != "" { + params["cursor"] = cursor + } var resp []byte - resp, err = b.SignedRequest(http.MethodGet, "open-api/stop-order/list", params, &cResult) + resp, err = b.SignedRequest(http.MethodGet, "v2/private/stop-order/list", params, &cResult) if err != nil { return } @@ -260,89 +176,33 @@ func (b *ByBit) getStopOrders(orderID string, orderLinkID string, stopOrderStatu return } - result = cResult.Result.Data + result = cResult.Result return } -// ReplaceStopOrder -func (b *ByBit) ReplaceStopOrder(symbol string, orderID string, qty int, price float64, triggerPrice float64) (result Order, err error) { - var cResult ReplaceStopOrderResult +// CreateStopOrder 创建条件委托单 +func (b *ByBit) CreateStopOrder(side string, orderType string, price float64, basePrice float64, stopPx float64, + qty int, triggerBy string, timeInForce string, closeOnTrigger bool, symbol string) (result StopOrder, err error) { + var cResult StopOrderResponse params := map[string]interface{}{} - params["stop_order_id"] = orderID + params["side"] = side params["symbol"] = symbol - if qty > 0 { - params["p_r_qty"] = qty - } + params["order_type"] = orderType + params["qty"] = qty if price > 0 { - params["p_r_price"] = price - } - if triggerPrice > 0 { - params["p_r_trigger_price"] = triggerPrice - } - var resp []byte - resp, err = b.SignedRequest(http.MethodPost, "open-api/stop-order/replace", params, &cResult) - if err != nil { - return - } - if cResult.RetCode != 0 { - err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) - return - } - result.StopOrderID = cResult.Result.StopOrderID - return -} - -// GetOrderByID -func (b *ByBit) GetOrderByID(orderID string, orderLinkID string, symbol string) (result OrderV2, err error) { - var cResult QueryOrderResult - - params := map[string]interface{}{} - params["symbol"] = symbol - if orderID != "" { - params["order_id"] = orderID - } - if orderLinkID != "" { - params["order_link_id"] = orderLinkID - } - var resp []byte - resp, err = b.SignedRequest(http.MethodGet, "v2/private/order", params, &cResult) - if err != nil { - return - } - if cResult.RetCode != 0 { - err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) - return + params["price"] = price } - - result = cResult.Result - return -} - -// GetOrderByOrderLinkID ... -func (b *ByBit) GetOrderByOrderLinkID(orderLinkID string, symbol string) (result Order, err error) { - var orders []Order - orders, err = b.getOrders("", orderLinkID, "", "", 0, 20, "", symbol) - if err != nil { - return + params["base_price"] = basePrice + params["stop_px"] = stopPx + params["time_in_force"] = timeInForce + if closeOnTrigger { + params["close_on_trigger"] = true } - if len(orders) != 1 { - err = errors.New("not found") - return + if triggerBy != "" { + params["trigger_by"] = triggerBy } - result = orders[0] - return -} - -// CancelOrder 撤销活动委托单 -// orderID: 活动委托单ID, 数据来自创建活动委托单返回的订单唯一ID -// symbol: -func (b *ByBit) CancelOrder(orderID string, symbol string) (result Order, err error) { - var cResult CancelOrderResult - params := map[string]interface{}{} - params["symbol"] = symbol - params["order_id"] = orderID var resp []byte - resp, err = b.SignedRequest(http.MethodPost, "open-api/order/cancel", params, &cResult) + resp, err = b.SignedRequest(http.MethodPost, "v2/private/stop-order/create", params, &cResult) if err != nil { return } @@ -350,45 +210,27 @@ func (b *ByBit) CancelOrder(orderID string, symbol string) (result Order, err er err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) return } - result = cResult.Result return } -// CancelOrder 撤销活动委托单 -// orderID: 活动委托单ID, 数据来自创建活动委托单返回的订单唯一ID -// symbol: -func (b *ByBit) CancelOrderV2(orderID string, orderLinkID string, symbol string) (result OrderV2, err error) { - var cResult CancelOrderV2Result +// ReplaceStopOrder +func (b *ByBit) ReplaceStopOrder(symbol string, orderID string, qty int, price float64, triggerPrice float64) (result StopOrder, err error) { + var cResult StopOrderResponse params := map[string]interface{}{} + params["stop_order_id"] = orderID params["symbol"] = symbol - if orderID != "" { - params["order_id"] = orderID - } - if orderLinkID != "" { - params["order_link_id"] = orderLinkID + if qty > 0 { + params["p_r_qty"] = qty } - var resp []byte - resp, err = b.SignedRequest(http.MethodPost, "v2/private/order/cancel", params, &cResult) - if err != nil { - return + if price > 0 { + params["p_r_price"] = price } - if cResult.RetCode != 0 { - err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) - return + if triggerPrice > 0 { + params["p_r_trigger_price"] = triggerPrice } - - result = cResult.Result - return -} - -// CancelAllOrder Cancel All Active Orders -func (b *ByBit) CancelAllOrder(symbol string) (result []OrderV2, err error) { - var cResult CancelAllOrderV2Result - params := map[string]interface{}{} - params["symbol"] = symbol var resp []byte - resp, err = b.SignedRequest(http.MethodPost, "v2/private/order/cancelAll", params, &cResult) + resp, err = b.SignedRequest(http.MethodPost, "v2/private/stop-order/replace", params, &cResult) if err != nil { return } @@ -396,21 +238,18 @@ func (b *ByBit) CancelAllOrder(symbol string) (result []OrderV2, err error) { err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) return } - - result = cResult.Result + result.StopOrderId = cResult.Result.StopOrderId return } // CancelStopOrder 撤销活动条件委托单 -// orderID: 活动条件委托单ID, 数据来自创建活动委托单返回的订单唯一ID -// symbol: -func (b *ByBit) CancelStopOrder(orderID string, symbol string) (result Order, err error) { - var cResult CancelOrderResult +func (b *ByBit) CancelStopOrder(orderID string, symbol string) (result StopOrder, err error) { + var cResult StopOrderResponse params := map[string]interface{}{} params["symbol"] = symbol params["stop_order_id"] = orderID var resp []byte - resp, err = b.SignedRequest(http.MethodPost, "open-api/stop-order/cancel", params, &cResult) + resp, err = b.SignedRequest(http.MethodPost, "v2/private/stop-order/cancel", params, &cResult) if err != nil { return } @@ -419,14 +258,13 @@ func (b *ByBit) CancelStopOrder(orderID string, symbol string) (result Order, er return } - result = cResult.Result + result.StopOrderId = cResult.Result.StopOrderId return } // CancelAllStopOrders 撤消全部条件委托单 -// symbol: -func (b *ByBit) CancelAllStopOrders(symbol string) (result []StopOrderV2, err error) { - var cResult CancelStopOrdersV2Result +func (b *ByBit) CancelAllStopOrders(symbol string) (result []StopOrder, err error) { + var cResult StopOrderArrayResponse params := map[string]interface{}{} params["symbol"] = symbol var resp []byte diff --git a/rest/api_test.go b/rest/api_test.go index ab34fc5..5f9526d 100644 --- a/rest/api_test.go +++ b/rest/api_test.go @@ -143,7 +143,18 @@ func TestByBit_GetWalletBalance(t *testing.T) { t.Logf("%#v", balance) } -func TestByBit_CreateOrderV2(t *testing.T) { +func TestByBit_GetOrders(t *testing.T) { + b := newByBit() + symbol := "BTCUSD" + orders, err := b.GetOrders(symbol, "", "next", 20, "") + assert.Nil(t, err) + //t.Logf("%#v", orders) + for _, order := range orders.Data { + t.Logf("%#v", order) + } +} + +func TestByBit_CreateOrder(t *testing.T) { b := newByBit() symbol := "BTCUSD" side := "Buy" // Buy Sell @@ -152,7 +163,7 @@ func TestByBit_CreateOrderV2(t *testing.T) { price := 5000.0 timeInForce := "GoodTillCancel" // {"ret_code":0,"ret_msg":"ok","ext_code":"","result":{"user_id":103061,"symbol":"BTCUSD","side":"Buy","order_type":"Limit","price":"7000","qty":30,"time_in_force":"GoodTillCancel","order_status":"Created","ext_fields":{"cross_status":"PendingNew","xreq_type":"x_create","xreq_offset":148672558},"leaves_qty":30,"leaves_value":"0.00428571","reject_reason":"","cross_seq":-1,"created_at":"2019-07-23T08:54:54.000Z","updated_at":"2019-07-23T08:54:54.000Z","last_exec_time":"0.000000","last_exec_price":0,"order_id":"603c41e0-c9fb-450c-90b6-ea870d5b0180"},"ext_info":null,"time_now":"1563872094.895918","rate_limit_status":98} - order, err := b.CreateOrderV2( + order, err := b.CreateOrder( side, orderType, price, @@ -171,40 +182,30 @@ func TestByBit_CreateOrderV2(t *testing.T) { t.Logf("%#v", order) } -func TestByBit_CreateOrder(t *testing.T) { +func TestByBit_CancelOrder(t *testing.T) { b := newByBit() + orderID := "c5b96b82-6a79-4b15-a797-361fe2ca0260" symbol := "BTCUSD" - side := "Buy" // Buy Sell - orderType := "Limit" - qty := 30 - price := 7000.0 - timeInForce := "GoodTillCancel" - // {"ret_code":0,"ret_msg":"ok","ext_code":"","result":{"user_id":103061,"symbol":"BTCUSD","side":"Buy","order_type":"Limit","price":"7000","qty":30,"time_in_force":"GoodTillCancel","order_status":"Created","ext_fields":{"cross_status":"PendingNew","xreq_type":"x_create","xreq_offset":148672558},"leaves_qty":30,"leaves_value":"0.00428571","reject_reason":"","cross_seq":-1,"created_at":"2019-07-23T08:54:54.000Z","updated_at":"2019-07-23T08:54:54.000Z","last_exec_time":"0.000000","last_exec_price":0,"order_id":"603c41e0-c9fb-450c-90b6-ea870d5b0180"},"ext_info":null,"time_now":"1563872094.895918","rate_limit_status":98} - order, err := b.CreateOrder(side, orderType, price, qty, timeInForce, false, symbol) - if err != nil { - t.Error(err) - return - } + order, err := b.CancelOrder(orderID, symbol) + assert.Nil(t, err) t.Logf("%#v", order) - // Created:创建订单;Rejected:订单被拒绝;New:订单待成交;PartiallyFilled:订单部分成交;Filled:订单全部成交,Cancelled:订单被取消 } -func TestByBit_CreateOrder2(t *testing.T) { +func TestByBit_GetStopOrders(t *testing.T) { b := newByBit() symbol := "BTCUSD" - side := "Sell" // Buy Sell - orderType := "Limit" - qty := 30 - price := 9000.0 - timeInForce := "GoodTillCancel" - // {"ret_code":0,"ret_msg":"ok","ext_code":"","result":{"user_id":103061,"symbol":"BTCUSD","side":"Buy","order_type":"Limit","price":"7000","qty":30,"time_in_force":"GoodTillCancel","order_status":"Created","ext_fields":{"cross_status":"PendingNew","xreq_type":"x_create","xreq_offset":148672558},"leaves_qty":30,"leaves_value":"0.00428571","reject_reason":"","cross_seq":-1,"created_at":"2019-07-23T08:54:54.000Z","updated_at":"2019-07-23T08:54:54.000Z","last_exec_time":"0.000000","last_exec_price":0,"order_id":"603c41e0-c9fb-450c-90b6-ea870d5b0180"},"ext_info":null,"time_now":"1563872094.895918","rate_limit_status":98} - order, err := b.CreateOrder(side, orderType, price, qty, timeInForce, true, symbol) - if err != nil { - t.Error(err) - return + // Untriggered: 等待市价触发条件单; Triggered: 市价已触发条件单; Cancelled: 取消; Active: 条件单触发成功且下单成功; Rejected: 条件触发成功但下单失败 + status := "Untriggered,Triggered,Active" + result, err := b.GetStopOrders(symbol, status, "next", 20, "") + assert.Nil(t, err) + //t.Logf("%#v", orders) + for _, order := range result.Data { + //if order.ExtFields != nil { + // t.Logf("%#v %v", order, *order.ExtFields) + //} else { + t.Logf("CreatedAt: %v %#v", order.CreatedAt.Local(), order) + //} } - t.Logf("%#v", order) - // Created:创建订单;Rejected:订单被拒绝;New:订单待成交;PartiallyFilled:订单部分成交;Filled:订单全部成交,Cancelled:订单被取消 } func TestByBit_CreateStopOrder(t *testing.T) { @@ -228,81 +229,6 @@ func TestByBit_CreateStopOrder(t *testing.T) { t.Logf("%#v", order) } -func TestByBit_GetOrders(t *testing.T) { - b := newByBit() - symbol := "BTCUSD" - orders, err := b.GetOrders("", "", 1, 20, "New", symbol) - assert.Nil(t, err) - //t.Logf("%#v", orders) - for _, order := range orders { - t.Logf("%#v", order) - } -} - -func TestByBit_GetOrder(t *testing.T) { - b := newByBit() - order, err := b.GetOrderByID( - "9d468e94-14b2-4d2e-88b9-590adaee3549", - "", - "BTCUSD") - if err != nil { - t.Error(err) - return - } - t.Logf("%#v", order) -} - -func TestByBit_GetStopOrders(t *testing.T) { - b := newByBit() - symbol := "BTCUSD" - // Untriggered: 等待市价触发条件单; Triggered: 市价已触发条件单; Cancelled: 取消; Active: 条件单触发成功且下单成功; Rejected: 条件触发成功但下单失败 - status := "Untriggered,Triggered,Active" - result, err := b.GetStopOrders("", 1, 1, status, symbol) - assert.Nil(t, err) - //t.Logf("%#v", orders) - for _, order := range result { - //if order.ExtFields != nil { - // t.Logf("%#v %v", order, *order.ExtFields) - //} else { - t.Logf("CreatedAt: %v %#v", order.CreatedAt.Local(), order) - //} - } -} - -func TestByBit_GetStopOrders2(t *testing.T) { - b := newByBit() - symbol := "BTCUSD" - //stopOrderID := "8a84cd9b-a3d4-4354-b2d7-e3b805369b77" - stopOrderID := "ccdcbdae-2eb8-4b8f-92de-32cc5ee18de4" - order, err := b.GetOrderByID(stopOrderID, "", symbol) - if err != nil { - t.Error(err) - return - } - t.Logf("%#v", order) -} - -func TestByBit_CancelOrder(t *testing.T) { - b := newByBit() - orderID := "c5b96b82-6a79-4b15-a797-361fe2ca0260" - symbol := "BTCUSD" - order, err := b.CancelOrder(orderID, symbol) - assert.Nil(t, err) - t.Logf("%#v", order) -} - -func TestByBit_CancelOrderV2(t *testing.T) { - b := newByBit() - orderID := "02f0e920-9bc9-4d87-a010-95923a2c430e" - symbol := "BTCUSD" - order, err := b.CancelOrderV2(orderID, "", symbol) - if err != nil { - t.Error(err) - return - } - t.Logf("%#v", order) -} - func TestByBit_CancelStopOrder(t *testing.T) { b := newByBit() orderID := "c6e535a9-6900-4b64-b983-3b220f6f41f8" @@ -320,13 +246,6 @@ func TestByBit_CancelAllStopOrders(t *testing.T) { t.Logf("%#v", orders) } -func TestByBit_GetLeverages(t *testing.T) { - b := newByBit() - l, err := b.GetLeverages() - assert.Nil(t, err) - t.Logf("%#v", l) -} - func TestByBit_SetLeverage(t *testing.T) { b := newByBit() _ = b.SetLeverage(3, "BTCUSD") diff --git a/rest/result.go b/rest/result.go index 837e5d2..c2c04b6 100644 --- a/rest/result.go +++ b/rest/result.go @@ -2,17 +2,19 @@ package rest import ( sjson "encoding/json" - "strings" "time" ) type BaseResult struct { - RetCode int `json:"ret_code"` - RetMsg string `json:"ret_msg"` - ExtCode string `json:"ext_code"` - Result interface{} `json:"result"` - TimeNow string `json:"time_now"` - RateLimitStatus int `json:"rate_limit_status"` + RetCode int `json:"ret_code"` + RetMsg string `json:"ret_msg"` + ExtCode string `json:"ext_code"` + ExtInfo string `json:"ext_info"` + Result interface{} `json:"result"` + TimeNow string `json:"time_now"` + RateLimitStatus int `json:"rate_limit_status"` + RateLimitResetMs int64 `json:"rate_limit_reset_ms"` + RateLimit int `json:"rate_limit"` } type Item struct { @@ -191,234 +193,51 @@ type GetBalanceResultData struct { USDT Balance `json:"USDT"` } -type CreateOrderResult struct { - RetCode int `json:"ret_code"` - RetMsg string `json:"ret_msg"` - ExtCode string `json:"ext_code"` - Result Order `json:"result"` - TimeNow string `json:"time_now"` - RateLimitStatus int `json:"rate_limit_status"` -} - -type OrderLite struct { - OrderID string `json:"order_id"` -} - -type StopOrderLite struct { - StopOrderID string `json:"stop_order_id"` -} - -type ReplaceOrderResult struct { - RetCode int `json:"ret_code"` - RetMsg string `json:"ret_msg"` - ExtCode string `json:"ext_code"` - Result OrderLite `json:"result"` - TimeNow string `json:"time_now"` - RateLimitStatus int `json:"rate_limit_status"` -} - -type ReplaceStopOrderResult struct { - RetCode int `json:"ret_code"` - RetMsg string `json:"ret_msg"` - ExtCode string `json:"ext_code"` - Result StopOrderLite `json:"result"` - TimeNow string `json:"time_now"` - RateLimitStatus int `json:"rate_limit_status"` -} - -type CancelOrderResult struct { - RetCode int `json:"ret_code"` - RetMsg string `json:"ret_msg"` - ExtCode string `json:"ext_code"` - Result Order `json:"result"` - TimeNow string `json:"time_now"` - RateLimitStatus int `json:"rate_limit_status"` -} - -type OrderListResultData struct { - Data []Order `json:"data"` - CurrentPage int `json:"current_page"` - LastPage int `json:"last_page"` -} - -type OrderListResult struct { - RetCode int `json:"ret_code"` - RetMsg string `json:"ret_msg"` - ExtCode string `json:"ext_code"` - Result OrderListResultData `json:"result"` - TimeNow string `json:"time_now"` - RateLimitStatus int `json:"rate_limit_status"` -} - -// Order ... -type Order struct { - OrderID string `json:"order_id"` - StopOrderID string `json:"stop_order_id"` - UserID int `json:"user_id"` - Symbol string `json:"symbol"` - Side string `json:"side"` - OrderType string `json:"order_type"` - Price sjson.Number `json:"price"` - Qty float64 `json:"qty"` - TimeInForce string `json:"time_in_force"` - StopOrderType string `json:"stop_order_type,omitempty"` - StopPx sjson.Number `json:"stop_px,omitempty"` - OrderStatus string `json:"order_status"` - StopOrderStatus string `json:"stop_order_status"` - LastExecTime sjson.Number `json:"last_exec_time"` - LastExecPrice sjson.Number `json:"last_exec_price"` - LeavesQty float64 `json:"leaves_qty"` - CumExecQty float64 `json:"cum_exec_qty"` - CumExecValue sjson.Number `json:"cum_exec_value"` - CumExecFee sjson.Number `json:"cum_exec_fee"` - RejectReason string `json:"reject_reason"` - OrderLinkID string `json:"order_link_id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - //ExtFields *ExtFields `json:"ext_fields,omitempty"` -} - -type ExtFields struct { - ReduceOnly bool `json:"reduce_only"` - OpFrom string `json:"op_from"` - Remark string `json:"remark"` - OReqNum int64 `json:"o_req_num"` - XreqType string `json:"xreq_type"` - CrossStatus string `json:"cross_status,omitempty"` -} - -type InExtFields struct { - ReduceOnly bool `json:"reduce_only"` - OpFrom string `json:"op_from"` - Remark string `json:"remark"` - OReqNum int64 `json:"o_req_num"` - XreqType string `json:"xreq_type"` - CrossStatus string `json:"cross_status,omitempty"` -} - -func (e *ExtFields) MarshalJSON() ([]byte, error) { - return json.Marshal(e) -} - -func (e *ExtFields) UnmarshalJSON(b []byte) error { - s := string(b) - if strings.HasPrefix(s, "[") { - return nil - } - o := InExtFields{} - if err := json.Unmarshal(b, &o); err == nil { - e.ReduceOnly = o.ReduceOnly - e.OpFrom = o.OpFrom - e.Remark = o.Remark - e.OReqNum = o.OReqNum - e.XreqType = o.XreqType - e.CrossStatus = o.CrossStatus - return nil - } else { - return err - } -} - -type GetLeverageResult struct { - RetCode int `json:"ret_code"` - RetMsg string `json:"ret_msg"` - ExtCode string `json:"ext_code"` - Result map[string]LeverageItem `json:"result"` - TimeNow string `json:"time_now"` - RateLimitStatus int `json:"rate_limit_status"` -} - -type LeverageItem struct { - Leverage int `json:"leverage"` -} - -type PositionV1 struct { - ID int `json:"id"` - UserID int `json:"user_id"` - RiskID int `json:"risk_id"` - Symbol string `json:"symbol"` - Size float64 `json:"size"` - Side string `json:"side"` - EntryPrice float64 `json:"entry_price"` - LiqPrice float64 `json:"liq_price"` - BustPrice float64 `json:"bust_price"` - TakeProfit float64 `json:"take_profit"` - StopLoss float64 `json:"stop_loss"` - TrailingStop float64 `json:"trailing_stop"` - PositionValue float64 `json:"position_value"` - Leverage float64 `json:"leverage"` - PositionStatus string `json:"position_status"` - AutoAddMargin float64 `json:"auto_add_margin"` - OrderMargin float64 `json:"order_margin"` - PositionMargin float64 `json:"position_margin"` - OccClosingFee float64 `json:"occ_closing_fee"` - OccFundingFee float64 `json:"occ_funding_fee"` - ExtFields *PositionExtFields `json:"ext_fields"` - WalletBalance float64 `json:"wallet_balance"` - CumRealisedPnl float64 `json:"cum_realised_pnl"` - CumCommission float64 `json:"cum_commission"` - RealisedPnl float64 `json:"realised_pnl"` - DeleverageIndicator float64 `json:"deleverage_indicator"` - OcCalcData string `json:"oc_calc_data"` - CrossSeq float64 `json:"cross_seq"` - PositionSeq float64 `json:"position_seq"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - UnrealisedPnl float64 `json:"unrealised_pnl"` +type PositionResponse struct { + BaseResult + Result Position `json:"result"` } -type Position struct { - ID int `json:"id"` - UserID int `json:"user_id"` - RiskID int `json:"risk_id"` - Symbol string `json:"symbol"` - Size float64 `json:"size"` - Side string `json:"side"` - EntryPrice float64 `json:"entry_price,string"` - LiqPrice float64 `json:"liq_price,string"` - BustPrice float64 `json:"bust_price,string"` - TakeProfit float64 `json:"take_profit,string"` - StopLoss float64 `json:"stop_loss,string"` - TrailingStop float64 `json:"trailing_stop,string"` - PositionValue float64 `json:"position_value,string"` - Leverage float64 `json:"leverage,string"` - PositionStatus string `json:"position_status"` - AutoAddMargin float64 `json:"auto_add_margin"` - OrderMargin float64 `json:"order_margin,string"` - PositionMargin float64 `json:"position_margin,string"` - OccClosingFee float64 `json:"occ_closing_fee,string"` - OccFundingFee float64 `json:"occ_funding_fee,string"` - ExtFields *PositionExtFields `json:"ext_fields"` - WalletBalance float64 `json:"wallet_balance,string"` - CumRealisedPnl float64 `json:"cum_realised_pnl,string"` - CumCommission float64 `json:"cum_commission,string"` - RealisedPnl float64 `json:"realised_pnl,string"` - DeleverageIndicator float64 `json:"deleverage_indicator"` - OcCalcData string `json:"oc_calc_data"` - CrossSeq float64 `json:"cross_seq"` - PositionSeq float64 `json:"position_seq"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - UnrealisedPnl float64 `json:"unrealised_pnl"` -} - -type PositionExtFields struct { - Remark string `json:"_remark"` -} - -type PositionListResult struct { +type PositionArrayResponse struct { BaseResult - ExtInfo interface{} `json:"ext_info"` - Result []PositionV1 `json:"result"` + Result []Position `json:"result"` } -type GetPositionResult struct { - BaseResult - ExtInfo interface{} `json:"ext_info"` - Result Position `json:"result"` +type Position struct { + ID int `json:"id"` + UserID int `json:"user_id"` + RiskID int `json:"risk_id"` + Symbol string `json:"symbol"` + Size float64 `json:"size"` + Side string `json:"side"` + EntryPrice float64 `json:"entry_price,string"` + LiqPrice float64 `json:"liq_price,string"` + BustPrice float64 `json:"bust_price,string"` + TakeProfit float64 `json:"take_profit,string"` + StopLoss float64 `json:"stop_loss,string"` + TrailingStop float64 `json:"trailing_stop,string"` + PositionValue float64 `json:"position_value,string"` + Leverage float64 `json:"leverage,string"` + PositionStatus string `json:"position_status"` + AutoAddMargin float64 `json:"auto_add_margin"` + OrderMargin float64 `json:"order_margin,string"` + PositionMargin float64 `json:"position_margin,string"` + OccClosingFee float64 `json:"occ_closing_fee,string"` + OccFundingFee float64 `json:"occ_funding_fee,string"` + WalletBalance float64 `json:"wallet_balance,string"` + CumRealisedPnl float64 `json:"cum_realised_pnl,string"` + CumCommission float64 `json:"cum_commission,string"` + RealisedPnl float64 `json:"realised_pnl,string"` + DeleverageIndicator float64 `json:"deleverage_indicator"` + OcCalcData string `json:"oc_calc_data"` + CrossSeq float64 `json:"cross_seq"` + PositionSeq float64 `json:"position_seq"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + UnrealisedPnl float64 `json:"unrealised_pnl"` } -type OrderV2 struct { +type Order struct { UserID int `json:"user_id"` OrderID string `json:"order_id"` Symbol string `json:"symbol"` @@ -440,56 +259,28 @@ type OrderV2 struct { UpdatedAt time.Time `json:"updated_at"` } -type CreateOrderV2Result struct { - RetCode int `json:"ret_code"` - RetMsg string `json:"ret_msg"` - ExtCode string `json:"ext_code"` - ExtInfo string `json:"ext_info"` - Result OrderV2 `json:"result"` - TimeNow string `json:"time_now"` - RateLimitStatus int `json:"rate_limit_status"` - RateLimitResetMs int64 `json:"rate_limit_reset_ms"` - RateLimit int `json:"rate_limit"` -} - -type CancelOrderV2Result struct { - RetCode int `json:"ret_code"` - RetMsg string `json:"ret_msg"` - ExtCode string `json:"ext_code"` - ExtInfo string `json:"ext_info"` - Result OrderV2 `json:"result"` - TimeNow string `json:"time_now"` - RateLimitStatus int `json:"rate_limit_status"` - RateLimitResetMs int64 `json:"rate_limit_reset_ms"` - RateLimit int `json:"rate_limit"` -} - -type CancelAllOrderV2Result struct { - RetCode int `json:"ret_code"` - RetMsg string `json:"ret_msg"` - ExtCode string `json:"ext_code"` - ExtInfo string `json:"ext_info"` - Result []OrderV2 `json:"result"` - TimeNow string `json:"time_now"` - RateLimitStatus int `json:"rate_limit_status"` - RateLimitResetMs int64 `json:"rate_limit_reset_ms"` - RateLimit int `json:"rate_limit"` -} - -type QueryOrderResult struct { - RetCode int `json:"ret_code"` - RetMsg string `json:"ret_msg"` - ExtCode string `json:"ext_code"` - ExtInfo string `json:"ext_info"` - Result OrderV2 `json:"result"` - TimeNow string `json:"time_now"` - RateLimitStatus int `json:"rate_limit_status"` - RateLimitResetMs int64 `json:"rate_limit_reset_ms"` - RateLimit int `json:"rate_limit"` -} - -type StopOrderV2 struct { - ClOrdID string `json:"clOrdID"` +type OrderListResponse struct { + BaseResult + Result OrderListResponseResult `json:"result"` +} + +type OrderListResponseResult struct { + Data []Order `json:"data"` + Cursor string `json:"cursor"` +} + +type OrderResponse struct { + BaseResult + Result Order `json:"result"` +} + +type OrderArrayResponse struct { + BaseResult + Result []Order `json:"result"` +} + +type StopOrder struct { + StopOrderId string `json:"StopOrderId"` UserID int64 `json:"user_id"` Symbol string `json:"symbol"` Side string `json:"side"` @@ -512,51 +303,22 @@ type StopOrderV2 struct { ExpectedDirection string `json:"expected_direction"` } -type CancelStopOrdersV2Result struct { - RetCode int `json:"ret_code"` - RetMsg string `json:"ret_msg"` - ExtCode string `json:"ext_code"` - ExtInfo string `json:"ext_info"` - Result []StopOrderV2 `json:"result"` - TimeNow string `json:"time_now"` - RateLimitStatus int `json:"rate_limit_status"` - RateLimitResetMs int64 `json:"rate_limit_reset_ms"` - RateLimit int `json:"rate_limit"` +type StopOrderListResponse struct { + BaseResult + Result StopOrderListResponseResult `json:"result"` } -type StopOrder struct { - UserID int64 `json:"user_id"` - StopOrderStatus string `json:"stop_order_status"` - Symbol string `json:"symbol"` - Side string `json:"side"` - OrderType string `json:"order_type"` - Price sjson.Number `json:"price"` - Qty sjson.Number `json:"qty"` - TimeInForce string `json:"time_in_force"` - StopOrderType string `json:"stop_order_type"` - TriggerBy string `json:"trigger_by"` - BasePrice sjson.Number `json:"base_price"` - OrderLinkID string `json:"order_link_id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - StopPx sjson.Number `json:"stop_px"` - StopOrderID string `json:"stop_order_id"` -} - -type GetStopOrdersResultData struct { - CurrentPage int `json:"current_page"` - LastPage int `json:"last_page"` - Data []StopOrder `json:"data"` -} - -type GetStopOrdersResult struct { - RetCode int `json:"ret_code"` - RetMsg string `json:"ret_msg"` - ExtCode string `json:"ext_code"` - Result GetStopOrdersResultData `json:"result"` - ExtInfo interface{} `json:"ext_info"` - TimeNow string `json:"time_now"` - RateLimitStatus int `json:"rate_limit_status"` - RateLimitResetMs int64 `json:"rate_limit_reset_ms"` - RateLimit int `json:"rate_limit"` +type StopOrderListResponseResult struct { + Data []StopOrder `json:"data"` + Cursor string `json:"cursor"` +} + +type StopOrderResponse struct { + BaseResult + Result StopOrder `json:"result"` +} + +type StopOrderArrayResponse struct { + BaseResult + Result []StopOrder `json:"result"` } From e8f5092706c7d05ba75703c950901bfb1ba71494 Mon Sep 17 00:00:00 2001 From: atik-lab Date: Sat, 21 Nov 2020 15:08:56 +0200 Subject: [PATCH 08/34] Review bybit new news about depreacted endpoints and make sure it works, november 30 last day. --- rest/api.go | 2 +- rest/result.go | 21 +++++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/rest/api.go b/rest/api.go index 1802235..3c6b05d 100644 --- a/rest/api.go +++ b/rest/api.go @@ -75,7 +75,7 @@ func (b *ByBit) GetWalletBalance(coin string) (result Balance, err error) { } // GetPositions 获取我的仓位 -func (b *ByBit) GetPositions() (result []Position, err error) { +func (b *ByBit) GetPositions() (result []PositionData, err error) { var r PositionArrayResponse params := map[string]interface{}{} diff --git a/rest/result.go b/rest/result.go index c2c04b6..14662a8 100644 --- a/rest/result.go +++ b/rest/result.go @@ -200,7 +200,12 @@ type PositionResponse struct { type PositionArrayResponse struct { BaseResult - Result []Position `json:"result"` + Result []PositionData `json:"result"` +} + +type PositionData struct { + IsValid bool `json:"is_valid"` + Data Position `json:"data"` } type Position struct { @@ -244,13 +249,13 @@ type Order struct { Side string `json:"side"` OrderType string `json:"order_type"` Price sjson.Number `json:"price"` - Qty float64 `json:"qty"` + Qty sjson.Number `json:"qty"` TimeInForce string `json:"time_in_force"` OrderStatus string `json:"order_status"` LastExecTime sjson.Number `json:"last_exec_time"` LastExecPrice sjson.Number `json:"last_exec_price"` - LeavesQty float64 `json:"leaves_qty"` - CumExecQty float64 `json:"cum_exec_qty"` + LeavesQty sjson.Number `json:"leaves_qty"` + CumExecQty sjson.Number `json:"cum_exec_qty"` CumExecValue sjson.Number `json:"cum_exec_value"` CumExecFee sjson.Number `json:"cum_exec_fee"` RejectReason string `json:"reject_reason"` @@ -280,23 +285,23 @@ type OrderArrayResponse struct { } type StopOrder struct { - StopOrderId string `json:"StopOrderId"` + StopOrderId string `json:"stop_order_id"` UserID int64 `json:"user_id"` Symbol string `json:"symbol"` Side string `json:"side"` OrderType string `json:"order_type"` Price sjson.Number `json:"price"` - Qty float64 `json:"qty"` + Qty sjson.Number `json:"qty"` TimeInForce string `json:"time_in_force"` CreateType string `json:"create_type"` CancelType string `json:"cancel_type"` OrderStatus string `json:"order_status"` - LeavesQty float64 `json:"leaves_qty"` + LeavesQty sjson.Number `json:"leaves_qty"` LeavesValue string `json:"leaves_value"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` CrossStatus string `json:"cross_status"` - CrossSeq float64 `json:"cross_seq"` + CrossSeq sjson.Number `json:"cross_seq"` StopOrderType string `json:"stop_order_type"` TriggerBy string `json:"trigger_by"` BasePrice sjson.Number `json:"base_price"` From 18f1a254c8407550d39b51856cd78bfd9ca6e4b1 Mon Sep 17 00:00:00 2001 From: atik-lab Date: Wed, 25 Nov 2020 13:45:38 +0200 Subject: [PATCH 09/34] Added fullURL to be returned for better logging. --- examples/rest/main.go | 4 ++-- rest/api.go | 26 +++++++++++++------------- rest/api2_public.go | 6 ++++-- rest/api2_test.go | 2 +- rest/api_order.go | 40 ++++++++++++++++++++-------------------- rest/api_public.go | 24 ++++++++++++------------ rest/api_public2.go | 4 ++-- rest/api_public2_test.go | 2 +- rest/api_test.go | 36 ++++++++++++++++++------------------ ws/ws.go | 11 ++++++++--- 10 files changed, 81 insertions(+), 74 deletions(-) diff --git a/examples/rest/main.go b/examples/rest/main.go index d94c447..6eb4044 100644 --- a/examples/rest/main.go +++ b/examples/rest/main.go @@ -12,7 +12,7 @@ func main() { baseURL, "YIxOY2RhFkylPudq96", "Bg9G2oFOb3aaIMguD3FOvOJJVBycaoXqXNcI", true) // 获取持仓 - positions, err := b.GetPositions() + _, positions, err := b.GetPositions() if err != nil { log.Printf("%v", err) return @@ -27,7 +27,7 @@ func main() { qty := 30 price := 7000.0 timeInForce := "GoodTillCancel" - order, err := b.CreateOrder(side, orderType, price, qty, timeInForce, 0, 0, false, false, "", symbol) + _, order, err := b.CreateOrder(side, orderType, price, qty, timeInForce, 0, 0, false, false, "", symbol) if err != nil { log.Println(err) return diff --git a/rest/api.go b/rest/api.go index 3c6b05d..581896d 100644 --- a/rest/api.go +++ b/rest/api.go @@ -41,7 +41,7 @@ func New(httpClient *http.Client, baseURL string, apiKey string, secretKey strin // SetCorrectServerTime 校正服务器时间 func (b *ByBit) SetCorrectServerTime() (err error) { var timeNow int64 - timeNow, err = b.GetServerTime() + _, timeNow, err = b.GetServerTime() if err != nil { return } @@ -51,11 +51,11 @@ func (b *ByBit) SetCorrectServerTime() (err error) { // GetBalance Get Wallet Balance // coin: BTC,EOS,XRP,ETH,USDT -func (b *ByBit) GetWalletBalance(coin string) (result Balance, err error) { +func (b *ByBit) GetWalletBalance(coin string) (query string, result Balance, err error) { var ret GetBalanceResult params := map[string]interface{}{} params["coin"] = coin - _, err = b.SignedRequest(http.MethodGet, "v2/private/wallet/balance", params, &ret) // v2/private/wallet/balance + query, _, err = b.SignedRequest(http.MethodGet, "v2/private/wallet/balance", params, &ret) // v2/private/wallet/balance if err != nil { return } @@ -75,12 +75,12 @@ func (b *ByBit) GetWalletBalance(coin string) (result Balance, err error) { } // GetPositions 获取我的仓位 -func (b *ByBit) GetPositions() (result []PositionData, err error) { +func (b *ByBit) GetPositions() (query string, result []PositionData, err error) { var r PositionArrayResponse params := map[string]interface{}{} var resp []byte - resp, err = b.SignedRequest(http.MethodGet, "v2/private/position/list", params, &r) + query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/position/list", params, &r) if err != nil { return } @@ -94,13 +94,13 @@ func (b *ByBit) GetPositions() (result []PositionData, err error) { } // GetPosition 获取我的仓位 -func (b *ByBit) GetPosition(symbol string) (result Position, err error) { +func (b *ByBit) GetPosition(symbol string) (query string, result Position, err error) { var r PositionResponse params := map[string]interface{}{} params["symbol"] = symbol var resp []byte - resp, err = b.SignedRequest(http.MethodGet, "v2/private/position/list", params, &r) + query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/position/list", params, &r) if err != nil { return } @@ -113,12 +113,12 @@ func (b *ByBit) GetPosition(symbol string) (result Position, err error) { } // SetLeverage 设置杠杆 -func (b *ByBit) SetLeverage(leverage int, symbol string) (err error) { +func (b *ByBit) SetLeverage(leverage int, symbol string) (query string, err error) { var r BaseResult params := map[string]interface{}{} params["symbol"] = symbol params["leverage"] = fmt.Sprintf("%v", leverage) - _, err = b.SignedRequest(http.MethodPost, "user/leverage/save", params, &r) + query, _, err = b.SignedRequest(http.MethodPost, "user/leverage/save", params, &r) if err != nil { return } @@ -126,7 +126,7 @@ func (b *ByBit) SetLeverage(leverage int, symbol string) (err error) { return } -func (b *ByBit) PublicRequest(method string, apiURL string, params map[string]interface{}, result interface{}) (resp []byte, err error) { +func (b *ByBit) PublicRequest(method string, apiURL string, params map[string]interface{}, result interface{}) (fullURL string, resp []byte, err error) { var keys []string for k := range params { keys = append(keys, k) @@ -139,7 +139,7 @@ func (b *ByBit) PublicRequest(method string, apiURL string, params map[string]in } param := strings.Join(p, "&") - fullURL := b.baseURL + apiURL + fullURL = b.baseURL + apiURL if param != "" { fullURL += "?" + param } @@ -175,7 +175,7 @@ func (b *ByBit) PublicRequest(method string, apiURL string, params map[string]in return } -func (b *ByBit) SignedRequest(method string, apiURL string, params map[string]interface{}, result interface{}) (resp []byte, err error) { +func (b *ByBit) SignedRequest(method string, apiURL string, params map[string]interface{}, result interface{}) (fullURL string, resp []byte, err error) { timestamp := time.Now().UnixNano()/1e6 + b.serverTimeOffset params["api_key"] = b.apiKey @@ -196,7 +196,7 @@ func (b *ByBit) SignedRequest(method string, apiURL string, params map[string]in signature := b.getSigned(param) param += "&sign=" + signature - fullURL := b.baseURL + apiURL + "?" + param + fullURL = b.baseURL + apiURL + "?" + param if b.debugMode { log.Printf("SignedRequest: %v", fullURL) } diff --git a/rest/api2_public.go b/rest/api2_public.go index 825878b..25430a5 100644 --- a/rest/api2_public.go +++ b/rest/api2_public.go @@ -39,17 +39,19 @@ type FundingData struct { // https://api2.bybit.com/funding-rate/list?symbol=BTCUSD&date=&export=false&page=1&limit=20 // To use this you will need to set b.BaseUrl to api2.bybit.com or api2-testnet.bybit.com -func (b *ByBit) GetFunding(symbol string, page int, limit int) (result []FundingData, e error) { +func (b *ByBit) GetFunding(symbol string, page int, limit int) (query string, result []FundingData, e error) { var ret FundingResult params := map[string]interface{}{} params["symbol"] = symbol params["page"] = page params["limit"] = limit // fixed limit 20 params["export"] = false // fixed export - _, e = b.PublicRequest(http.MethodGet, "funding-rate/list", params, &ret) + query, _, e = b.PublicRequest(http.MethodGet, "funding-rate/list", params, &ret) if e != nil { return } result = ret.Result.Data return } + +// for linear: https://api2.bybit.com/linear/funding-rate/list?symbol=BTCUSDT&date=&export=false&page=1 diff --git a/rest/api2_test.go b/rest/api2_test.go index 4a68f0e..d35ff76 100644 --- a/rest/api2_test.go +++ b/rest/api2_test.go @@ -15,7 +15,7 @@ func newByBit2() *rest.ByBit { func TestByBit_GetFunding(t *testing.T) { b := newByBit2() - funding, e := b.GetFunding("BTCUSD", 1, 200) + _, funding, e := b.GetFunding("BTCUSD", 1, 200) if e != nil { t.Error(e) return diff --git a/rest/api_order.go b/rest/api_order.go index 9e4f15b..2bf3957 100644 --- a/rest/api_order.go +++ b/rest/api_order.go @@ -6,7 +6,7 @@ import ( ) // getOrders 查询活动委托 -func (b *ByBit) GetOrders(symbol string, orderStatus string, direction string, limit int, cursor string) (result OrderListResponseResult, err error) { +func (b *ByBit) GetOrders(symbol string, orderStatus string, direction string, limit int, cursor string) (query string, result OrderListResponseResult, err error) { var cResult OrderListResponse if limit == 0 { @@ -26,7 +26,7 @@ func (b *ByBit) GetOrders(symbol string, orderStatus string, direction string, l params["cursor"] = cursor } var resp []byte - resp, err = b.SignedRequest(http.MethodGet, "v2/private/order/list", params, &cResult) + query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/order/list", params, &cResult) if err != nil { return } @@ -41,7 +41,7 @@ func (b *ByBit) GetOrders(symbol string, orderStatus string, direction string, l func (b *ByBit) CreateOrder(side string, orderType string, price float64, qty int, timeInForce string, takeProfit float64, stopLoss float64, reduceOnly bool, - closeOnTrigger bool, orderLinkID string, symbol string) (result Order, err error) { + closeOnTrigger bool, orderLinkID string, symbol string) (query string, result Order, err error) { var cResult OrderResponse params := map[string]interface{}{} params["side"] = side @@ -68,7 +68,7 @@ func (b *ByBit) CreateOrder(side string, orderType string, price float64, params["order_link_id"] = orderLinkID } var resp []byte - resp, err = b.SignedRequest(http.MethodPost, "v2/private/order/create", params, &cResult) + query, resp, err = b.SignedRequest(http.MethodPost, "v2/private/order/create", params, &cResult) if err != nil { return } @@ -81,7 +81,7 @@ func (b *ByBit) CreateOrder(side string, orderType string, price float64, } // ReplaceOrder -func (b *ByBit) ReplaceOrder(symbol string, orderID string, qty int, price float64) (result Order, err error) { +func (b *ByBit) ReplaceOrder(symbol string, orderID string, qty int, price float64) (query string, result Order, err error) { var cResult OrderResponse params := map[string]interface{}{} params["order_id"] = orderID @@ -93,7 +93,7 @@ func (b *ByBit) ReplaceOrder(symbol string, orderID string, qty int, price float params["p_r_price"] = price } var resp []byte - resp, err = b.SignedRequest(http.MethodPost, "v2/private/order/replace", params, &cResult) + query, resp, err = b.SignedRequest(http.MethodPost, "v2/private/order/replace", params, &cResult) if err != nil { return } @@ -106,7 +106,7 @@ func (b *ByBit) ReplaceOrder(symbol string, orderID string, qty int, price float } // CancelOrder 撤销活动委托单 -func (b *ByBit) CancelOrder(orderID string, symbol string) (result Order, err error) { +func (b *ByBit) CancelOrder(orderID string, symbol string) (query string, result Order, err error) { var cResult OrderResponse params := map[string]interface{}{} params["symbol"] = symbol @@ -114,7 +114,7 @@ func (b *ByBit) CancelOrder(orderID string, symbol string) (result Order, err er params["order_id"] = orderID } var resp []byte - resp, err = b.SignedRequest(http.MethodPost, "v2/private/order/cancel", params, &cResult) + query, resp, err = b.SignedRequest(http.MethodPost, "v2/private/order/cancel", params, &cResult) if err != nil { return } @@ -128,12 +128,12 @@ func (b *ByBit) CancelOrder(orderID string, symbol string) (result Order, err er } // CancelAllOrder Cancel All Active Orders -func (b *ByBit) CancelAllOrder(symbol string) (result []Order, err error) { +func (b *ByBit) CancelAllOrder(symbol string) (query string, result []Order, err error) { var cResult OrderArrayResponse params := map[string]interface{}{} params["symbol"] = symbol var resp []byte - resp, err = b.SignedRequest(http.MethodPost, "v2/private/order/cancelAll", params, &cResult) + query, resp, err = b.SignedRequest(http.MethodPost, "v2/private/order/cancelAll", params, &cResult) if err != nil { return } @@ -147,7 +147,7 @@ func (b *ByBit) CancelAllOrder(symbol string) (result []Order, err error) { } // getStopOrders 查询条件委托单 -func (b *ByBit) GetStopOrders(symbol string, stopOrderStatus string, direction string, limit int, cursor string) (result StopOrderListResponseResult, err error) { +func (b *ByBit) GetStopOrders(symbol string, stopOrderStatus string, direction string, limit int, cursor string) (query string, result StopOrderListResponseResult, err error) { var cResult StopOrderListResponse if limit == 0 { @@ -167,7 +167,7 @@ func (b *ByBit) GetStopOrders(symbol string, stopOrderStatus string, direction s params["cursor"] = cursor } var resp []byte - resp, err = b.SignedRequest(http.MethodGet, "v2/private/stop-order/list", params, &cResult) + query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/stop-order/list", params, &cResult) if err != nil { return } @@ -182,7 +182,7 @@ func (b *ByBit) GetStopOrders(symbol string, stopOrderStatus string, direction s // CreateStopOrder 创建条件委托单 func (b *ByBit) CreateStopOrder(side string, orderType string, price float64, basePrice float64, stopPx float64, - qty int, triggerBy string, timeInForce string, closeOnTrigger bool, symbol string) (result StopOrder, err error) { + qty int, triggerBy string, timeInForce string, closeOnTrigger bool, symbol string) (query string, result StopOrder, err error) { var cResult StopOrderResponse params := map[string]interface{}{} params["side"] = side @@ -202,7 +202,7 @@ func (b *ByBit) CreateStopOrder(side string, orderType string, price float64, ba params["trigger_by"] = triggerBy } var resp []byte - resp, err = b.SignedRequest(http.MethodPost, "v2/private/stop-order/create", params, &cResult) + query, resp, err = b.SignedRequest(http.MethodPost, "v2/private/stop-order/create", params, &cResult) if err != nil { return } @@ -215,7 +215,7 @@ func (b *ByBit) CreateStopOrder(side string, orderType string, price float64, ba } // ReplaceStopOrder -func (b *ByBit) ReplaceStopOrder(symbol string, orderID string, qty int, price float64, triggerPrice float64) (result StopOrder, err error) { +func (b *ByBit) ReplaceStopOrder(symbol string, orderID string, qty int, price float64, triggerPrice float64) (query string, result StopOrder, err error) { var cResult StopOrderResponse params := map[string]interface{}{} params["stop_order_id"] = orderID @@ -230,7 +230,7 @@ func (b *ByBit) ReplaceStopOrder(symbol string, orderID string, qty int, price f params["p_r_trigger_price"] = triggerPrice } var resp []byte - resp, err = b.SignedRequest(http.MethodPost, "v2/private/stop-order/replace", params, &cResult) + query, resp, err = b.SignedRequest(http.MethodPost, "v2/private/stop-order/replace", params, &cResult) if err != nil { return } @@ -243,13 +243,13 @@ func (b *ByBit) ReplaceStopOrder(symbol string, orderID string, qty int, price f } // CancelStopOrder 撤销活动条件委托单 -func (b *ByBit) CancelStopOrder(orderID string, symbol string) (result StopOrder, err error) { +func (b *ByBit) CancelStopOrder(orderID string, symbol string) (query string, result StopOrder, err error) { var cResult StopOrderResponse params := map[string]interface{}{} params["symbol"] = symbol params["stop_order_id"] = orderID var resp []byte - resp, err = b.SignedRequest(http.MethodPost, "v2/private/stop-order/cancel", params, &cResult) + query, resp, err = b.SignedRequest(http.MethodPost, "v2/private/stop-order/cancel", params, &cResult) if err != nil { return } @@ -263,12 +263,12 @@ func (b *ByBit) CancelStopOrder(orderID string, symbol string) (result StopOrder } // CancelAllStopOrders 撤消全部条件委托单 -func (b *ByBit) CancelAllStopOrders(symbol string) (result []StopOrder, err error) { +func (b *ByBit) CancelAllStopOrders(symbol string) (query string, result []StopOrder, err error) { var cResult StopOrderArrayResponse params := map[string]interface{}{} params["symbol"] = symbol var resp []byte - resp, err = b.SignedRequest(http.MethodPost, "v2/private/stop-order/cancelAll", params, &cResult) + query, resp, err = b.SignedRequest(http.MethodPost, "v2/private/stop-order/cancelAll", params, &cResult) if err != nil { return } diff --git a/rest/api_public.go b/rest/api_public.go index 7788350..46a76ba 100644 --- a/rest/api_public.go +++ b/rest/api_public.go @@ -8,10 +8,10 @@ import ( ) // GetServerTime Get server time. -func (b *ByBit) GetServerTime() (timeNow int64, err error) { +func (b *ByBit) GetServerTime() (query string, timeNow int64, err error) { params := map[string]interface{}{} var ret BaseResult - _, err = b.PublicRequest(http.MethodGet, "v2/public/time", params, &ret) + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/time", params, &ret) if err != nil { return } @@ -26,11 +26,11 @@ func (b *ByBit) GetServerTime() (timeNow int64, err error) { // GetOrderBook Get the orderbook // 正反向合约通用 -func (b *ByBit) GetOrderBook(symbol string) (result OrderBook, err error) { +func (b *ByBit) GetOrderBook(symbol string) (query string, result OrderBook, err error) { var ret GetOrderBookResult params := map[string]interface{}{} params["symbol"] = symbol - _, err = b.PublicRequest(http.MethodGet, "v2/public/orderBook/L2", params, &ret) + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/orderBook/L2", params, &ret) if err != nil { return } @@ -71,7 +71,7 @@ func (b *ByBit) GetOrderBook(symbol string) (result OrderBook, err error) { // interval: 1 3 5 15 30 60 120 240 360 720 "D" "M" "W" "Y" // from: From timestamp in seconds // limit: Limit for data size per page, max size is 200. Default as showing 200 pieces of data per page -func (b *ByBit) GetKLine(symbol string, interval string, from int64, limit int) (result []OHLC, err error) { +func (b *ByBit) GetKLine(symbol string, interval string, from int64, limit int) (query string, result []OHLC, err error) { var ret GetKlineResult params := map[string]interface{}{} params["symbol"] = symbol @@ -80,7 +80,7 @@ func (b *ByBit) GetKLine(symbol string, interval string, from int64, limit int) if limit > 0 { params["limit"] = limit } - _, err = b.PublicRequest(http.MethodGet, "v2/public/kline/list", params, &ret) + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/kline/list", params, &ret) if err != nil { return } @@ -88,11 +88,11 @@ func (b *ByBit) GetKLine(symbol string, interval string, from int64, limit int) return } -func (b *ByBit) GetTickers() (result []Ticker, err error) { +func (b *ByBit) GetTickers() (query string, result []Ticker, err error) { // https://api-testnet.bybit.com/v2/public/tickers var ret GetTickersResult params := map[string]interface{}{} - _, err = b.PublicRequest(http.MethodGet, "v2/public/tickers", params, &ret) + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/tickers", params, &ret) if err != nil { return } @@ -102,7 +102,7 @@ func (b *ByBit) GetTickers() (result []Ticker, err error) { // from: From ID. Default: return latest data // limit: Number of results. Default 500; max 1000 -func (b *ByBit) GetTradingRecords(symbol string, from int64, limit int) (result []TradingRecord, err error) { +func (b *ByBit) GetTradingRecords(symbol string, from int64, limit int) (query string, result []TradingRecord, err error) { var ret GetTradingRecordsResult params := map[string]interface{}{} params["symbol"] = symbol @@ -112,7 +112,7 @@ func (b *ByBit) GetTradingRecords(symbol string, from int64, limit int) (result if limit > 0 { params["limit"] = limit } - _, err = b.PublicRequest(http.MethodGet, "v2/public/trading-records", params, &ret) + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/trading-records", params, &ret) if err != nil { return } @@ -120,10 +120,10 @@ func (b *ByBit) GetTradingRecords(symbol string, from int64, limit int) (result return } -func (b *ByBit) GetSymbols() (result []SymbolInfo, err error) { +func (b *ByBit) GetSymbols() (query string, result []SymbolInfo, err error) { var ret GetSymbolsResult params := map[string]interface{}{} - _, err = b.PublicRequest(http.MethodGet, "v2/public/symbols", params, &ret) + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/symbols", params, &ret) if err != nil { return } diff --git a/rest/api_public2.go b/rest/api_public2.go index 6547532..e8bd928 100644 --- a/rest/api_public2.go +++ b/rest/api_public2.go @@ -9,7 +9,7 @@ import ( // interval: 1 3 5 15 30 60 120 240 360 720 "D" "M" "W" "Y" // from: From timestamp in seconds // limit: Limit for data size per page, max size is 200. Default as showing 200 pieces of data per page -func (b *ByBit) GetKLine2(symbol string, interval string, from int64, limit int) (result []OHLC2, err error) { +func (b *ByBit) GetKLine2(symbol string, interval string, from int64, limit int) (query string, result []OHLC2, err error) { var ret GetKlineResult2 params := map[string]interface{}{} params["symbol"] = symbol @@ -18,7 +18,7 @@ func (b *ByBit) GetKLine2(symbol string, interval string, from int64, limit int) if limit > 0 { params["limit"] = limit } - _, err = b.PublicRequest(http.MethodGet, "public/linear/kline", params, &ret) + query, _, err = b.PublicRequest(http.MethodGet, "public/linear/kline", params, &ret) if err != nil { return } diff --git a/rest/api_public2_test.go b/rest/api_public2_test.go index 402c7c5..2fccae5 100644 --- a/rest/api_public2_test.go +++ b/rest/api_public2_test.go @@ -8,7 +8,7 @@ import ( func TestGetKLine2(t *testing.T) { b := newByBit() from := time.Now().Add(-1 * time.Hour).Unix() - ohlcs, err := b.GetKLine2( + _, ohlcs, err := b.GetKLine2( "BTCUSDT", "1", from, diff --git a/rest/api_test.go b/rest/api_test.go index 5f9526d..d8a0f08 100644 --- a/rest/api_test.go +++ b/rest/api_test.go @@ -26,7 +26,7 @@ func newByBit() *ByBit { func TestByBit_GetServerTime(t *testing.T) { b := newByBit() - timeNow, err := b.GetServerTime() + _, timeNow, err := b.GetServerTime() if err != nil { t.Error(err) return @@ -49,7 +49,7 @@ func TestByBit_SetCorrectServerTime(t *testing.T) { func TestByBit_GetOrderBook(t *testing.T) { b := newByBit() - ob, err := b.GetOrderBook("BTCUSD") + _, ob, err := b.GetOrderBook("BTCUSD") if err != nil { t.Error(err) return @@ -65,7 +65,7 @@ func TestByBit_GetOrderBook(t *testing.T) { func TestByBit_GetOrderBook2(t *testing.T) { b := newByBit() - ob, err := b.GetOrderBook("BTCUSDT") + _, ob, err := b.GetOrderBook("BTCUSDT") if err != nil { t.Error(err) return @@ -82,7 +82,7 @@ func TestByBit_GetOrderBook2(t *testing.T) { func TestByBit_GetKLine(t *testing.T) { b := newByBit() from := time.Now().Add(-1 * time.Hour).Unix() - ohlcs, err := b.GetKLine( + _, ohlcs, err := b.GetKLine( "BTCUSD", "1", from, @@ -99,7 +99,7 @@ func TestByBit_GetKLine(t *testing.T) { func TestByBit_GetTickers(t *testing.T) { b := newByBit() - tickers, err := b.GetTickers() + _, tickers, err := b.GetTickers() if err != nil { t.Error() return @@ -111,7 +111,7 @@ func TestByBit_GetTickers(t *testing.T) { func TestByBit_GetTradingRecords(t *testing.T) { b := newByBit() - records, err := b.GetTradingRecords("BTCUSD", 0, 0) + _, records, err := b.GetTradingRecords("BTCUSD", 0, 0) if err != nil { t.Error(err) return @@ -123,7 +123,7 @@ func TestByBit_GetTradingRecords(t *testing.T) { func TestByBit_GetSymbols(t *testing.T) { b := newByBit() - symbols, err := b.GetSymbols() + _, symbols, err := b.GetSymbols() if err != nil { t.Error(err) return @@ -135,7 +135,7 @@ func TestByBit_GetSymbols(t *testing.T) { func TestByBit_GetWalletBalance(t *testing.T) { b := newByBit() - balance, err := b.GetWalletBalance("BTC") + _, balance, err := b.GetWalletBalance("BTC") if err != nil { t.Error(err) return @@ -146,7 +146,7 @@ func TestByBit_GetWalletBalance(t *testing.T) { func TestByBit_GetOrders(t *testing.T) { b := newByBit() symbol := "BTCUSD" - orders, err := b.GetOrders(symbol, "", "next", 20, "") + _, orders, err := b.GetOrders(symbol, "", "next", 20, "") assert.Nil(t, err) //t.Logf("%#v", orders) for _, order := range orders.Data { @@ -163,7 +163,7 @@ func TestByBit_CreateOrder(t *testing.T) { price := 5000.0 timeInForce := "GoodTillCancel" // {"ret_code":0,"ret_msg":"ok","ext_code":"","result":{"user_id":103061,"symbol":"BTCUSD","side":"Buy","order_type":"Limit","price":"7000","qty":30,"time_in_force":"GoodTillCancel","order_status":"Created","ext_fields":{"cross_status":"PendingNew","xreq_type":"x_create","xreq_offset":148672558},"leaves_qty":30,"leaves_value":"0.00428571","reject_reason":"","cross_seq":-1,"created_at":"2019-07-23T08:54:54.000Z","updated_at":"2019-07-23T08:54:54.000Z","last_exec_time":"0.000000","last_exec_price":0,"order_id":"603c41e0-c9fb-450c-90b6-ea870d5b0180"},"ext_info":null,"time_now":"1563872094.895918","rate_limit_status":98} - order, err := b.CreateOrder( + _, order, err := b.CreateOrder( side, orderType, price, @@ -186,7 +186,7 @@ func TestByBit_CancelOrder(t *testing.T) { b := newByBit() orderID := "c5b96b82-6a79-4b15-a797-361fe2ca0260" symbol := "BTCUSD" - order, err := b.CancelOrder(orderID, symbol) + _, order, err := b.CancelOrder(orderID, symbol) assert.Nil(t, err) t.Logf("%#v", order) } @@ -196,7 +196,7 @@ func TestByBit_GetStopOrders(t *testing.T) { symbol := "BTCUSD" // Untriggered: 等待市价触发条件单; Triggered: 市价已触发条件单; Cancelled: 取消; Active: 条件单触发成功且下单成功; Rejected: 条件触发成功但下单失败 status := "Untriggered,Triggered,Active" - result, err := b.GetStopOrders(symbol, status, "next", 20, "") + _, result, err := b.GetStopOrders(symbol, status, "next", 20, "") assert.Nil(t, err) //t.Logf("%#v", orders) for _, order := range result.Data { @@ -220,7 +220,7 @@ func TestByBit_CreateStopOrder(t *testing.T) { triggerBy := "" timeInForce := "GoodTillCancel" // {"ret_code":0,"ret_msg":"ok","ext_code":"","result":{"user_id":103061,"symbol":"BTCUSD","side":"Buy","order_type":"Limit","price":"7000","qty":30,"time_in_force":"GoodTillCancel","order_status":"Created","ext_fields":{"cross_status":"PendingNew","xreq_type":"x_create","xreq_offset":148672558},"leaves_qty":30,"leaves_value":"0.00428571","reject_reason":"","cross_seq":-1,"created_at":"2019-07-23T08:54:54.000Z","updated_at":"2019-07-23T08:54:54.000Z","last_exec_time":"0.000000","last_exec_price":0,"order_id":"603c41e0-c9fb-450c-90b6-ea870d5b0180"},"ext_info":null,"time_now":"1563872094.895918","rate_limit_status":98} - order, err := b.CreateStopOrder(side, + _, order, err := b.CreateStopOrder(side, orderType, price, basePrice, stopPx, qty, triggerBy, timeInForce, true, symbol) if err != nil { t.Error(err) @@ -233,7 +233,7 @@ func TestByBit_CancelStopOrder(t *testing.T) { b := newByBit() orderID := "c6e535a9-6900-4b64-b983-3b220f6f41f8" symbol := "BTCUSD" - order, err := b.CancelStopOrder(orderID, symbol) + _, order, err := b.CancelStopOrder(orderID, symbol) assert.Nil(t, err) t.Logf("%#v", order) } @@ -241,26 +241,26 @@ func TestByBit_CancelStopOrder(t *testing.T) { func TestByBit_CancelAllStopOrders(t *testing.T) { b := newByBit() symbol := "BTCUSD" - orders, err := b.CancelAllStopOrders(symbol) + _, orders, err := b.CancelAllStopOrders(symbol) assert.Nil(t, err) t.Logf("%#v", orders) } func TestByBit_SetLeverage(t *testing.T) { b := newByBit() - _ = b.SetLeverage(3, "BTCUSD") + _, _ = b.SetLeverage(3, "BTCUSD") } func TestByBit_GetPositions(t *testing.T) { b := newByBit() - positions, err := b.GetPositions() + _, positions, err := b.GetPositions() assert.Nil(t, err) t.Logf("%#v", positions) } func TestByBit_GetPosition(t *testing.T) { b := newByBit() - position, err := b.GetPosition("BTCUSD") + _, position, err := b.GetPosition("BTCUSD") assert.Nil(t, err) t.Logf("%#v", position) } diff --git a/ws/ws.go b/ws/ws.go index 9ac7399..6d3101f 100644 --- a/ws/ws.go +++ b/ws/ws.go @@ -82,13 +82,16 @@ func New(config *Configuration) *ByBitWS { b.ctx, b.cancel = context.WithCancel(context.Background()) b.conn = &recws.RecConn{ KeepAliveTimeout: 60 * time.Second, + NonVerbose: true, } b.conn.SubscribeHandler = b.subscribeHandler return b } func (b *ByBitWS) subscribeHandler() error { - log.Printf("subscribeHandler") + if b.cfg.DebugMode { + log.Printf("subscribeHandler") + } b.mu.Lock() defer b.mu.Unlock() @@ -111,8 +114,10 @@ func (b *ByBitWS) subscribeHandler() error { } func (b *ByBitWS) closeHandler(code int, text string) error { - log.Printf("close handle executed code=%v text=%v", - code, text) + if b.cfg.DebugMode { + log.Printf("close handle executed code=%v text=%v", + code, text) + } return nil } From b39e527e7131487e0faab5d3bff12e7438e50ba4 Mon Sep 17 00:00:00 2001 From: Atik Date: Wed, 25 Nov 2020 20:46:22 +0200 Subject: [PATCH 10/34] Added stop order status. --- rest/result.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rest/result.go b/rest/result.go index 14662a8..cb444b0 100644 --- a/rest/result.go +++ b/rest/result.go @@ -290,6 +290,8 @@ type StopOrder struct { Symbol string `json:"symbol"` Side string `json:"side"` OrderType string `json:"order_type"` + StopOrderType string `json:"stop_order_type"` + StopOrderStatus string `json:"stop_order_status"` Price sjson.Number `json:"price"` Qty sjson.Number `json:"qty"` TimeInForce string `json:"time_in_force"` @@ -302,7 +304,6 @@ type StopOrder struct { UpdatedAt time.Time `json:"updated_at"` CrossStatus string `json:"cross_status"` CrossSeq sjson.Number `json:"cross_seq"` - StopOrderType string `json:"stop_order_type"` TriggerBy string `json:"trigger_by"` BasePrice sjson.Number `json:"base_price"` ExpectedDirection string `json:"expected_direction"` From e6c21a5fece9d80e048ebb0259c7ec3ab2ba062d Mon Sep 17 00:00:00 2001 From: atik-lab Date: Fri, 27 Nov 2020 18:44:51 +0200 Subject: [PATCH 11/34] Added StopPx. --- rest/result.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rest/result.go b/rest/result.go index cb444b0..963f076 100644 --- a/rest/result.go +++ b/rest/result.go @@ -286,12 +286,13 @@ type OrderArrayResponse struct { type StopOrder struct { StopOrderId string `json:"stop_order_id"` + StopOrderType string `json:"stop_order_type"` + StopOrderStatus string `json:"stop_order_status"` + StopPx sjson.Number `json:"stop_px"` UserID int64 `json:"user_id"` Symbol string `json:"symbol"` Side string `json:"side"` OrderType string `json:"order_type"` - StopOrderType string `json:"stop_order_type"` - StopOrderStatus string `json:"stop_order_status"` Price sjson.Number `json:"price"` Qty sjson.Number `json:"qty"` TimeInForce string `json:"time_in_force"` From 15b6bac59ff387c62cea49fe035a708097ddd063 Mon Sep 17 00:00:00 2001 From: atik-lab Date: Sat, 28 Nov 2020 18:39:59 +0200 Subject: [PATCH 12/34] Fix with the new tickers without funding. --- rest/result.go | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/rest/result.go b/rest/result.go index 963f076..245dd3c 100644 --- a/rest/result.go +++ b/rest/result.go @@ -66,29 +66,28 @@ type GetKlineResult struct { } type Ticker struct { - Symbol string `json:"symbol"` - BidPrice float64 `json:"bid_price,string"` - AskPrice float64 `json:"ask_price,string"` - LastPrice float64 `json:"last_price,string"` - LastTickDirection string `json:"last_tick_direction"` - PrevPrice24H float64 `json:"prev_price_24h,string"` - Price24HPcnt float64 `json:"price_24h_pcnt,string"` - HighPrice24H float64 `json:"high_price_24h,string"` - LowPrice24H float64 `json:"low_price_24h,string"` - PrevPrice1H float64 `json:"prev_price_1h,string"` - Price1HPcnt float64 `json:"price_1h_pcnt,string"` - MarkPrice float64 `json:"mark_price,string"` - IndexPrice float64 `json:"index_price,string"` - OpenInterest float64 `json:"open_interest"` - OpenValue float64 `json:"open_value,string"` - TotalTurnover float64 `json:"total_turnover,string"` - Turnover24H float64 `json:"turnover_24h,string"` - TotalVolume float64 `json:"total_volume"` - Volume24H float64 `json:"volume_24h"` - FundingRate float64 `json:"funding_rate,string"` - PredictedFundingRate float64 `json:"predicted_funding_rate,string"` - NextFundingTime time.Time `json:"next_funding_time"` - CountdownHour int `json:"countdown_hour"` + Symbol string `json:"symbol"` + BidPrice float64 `json:"bid_price,string"` + AskPrice float64 `json:"ask_price,string"` + LastPrice float64 `json:"last_price,string"` + LastTickDirection string `json:"last_tick_direction"` + PrevPrice24H float64 `json:"prev_price_24h,string"` + Price24HPcnt float64 `json:"price_24h_pcnt,string"` + HighPrice24H float64 `json:"high_price_24h,string"` + LowPrice24H float64 `json:"low_price_24h,string"` + PrevPrice1H float64 `json:"prev_price_1h,string"` + Price1HPcnt float64 `json:"price_1h_pcnt,string"` + MarkPrice float64 `json:"mark_price,string"` + IndexPrice float64 `json:"index_price,string"` + OpenInterest float64 `json:"open_interest"` + OpenValue float64 `json:"open_value,string"` + TotalTurnover float64 `json:"total_turnover,string"` + Turnover24H float64 `json:"turnover_24h,string"` + TotalVolume float64 `json:"total_volume"` + Volume24H float64 `json:"volume_24h"` + FundingRate float64 `json:"funding_rate,string"` + PredictedFundingRate float64 `json:"predicted_funding_rate,string"` + CountdownHour int `json:"countdown_hour"` } type GetTickersResult struct { From 3f7871e73669351a1b7b8e97e124389126e6e01c Mon Sep 17 00:00:00 2001 From: Atik Date: Sun, 6 Dec 2020 17:55:28 +0200 Subject: [PATCH 13/34] NextFundingTime as string. --- rest/result.go | 1 + 1 file changed, 1 insertion(+) diff --git a/rest/result.go b/rest/result.go index 245dd3c..2dccf43 100644 --- a/rest/result.go +++ b/rest/result.go @@ -87,6 +87,7 @@ type Ticker struct { Volume24H float64 `json:"volume_24h"` FundingRate float64 `json:"funding_rate,string"` PredictedFundingRate float64 `json:"predicted_funding_rate,string"` + NextFundingTime string `json:"next_funding_time"` // string because can be empty, parse it with "2006-01-02T15:04:05Z07:00" CountdownHour int `json:"countdown_hour"` } From f15edb4782714bd993efb03d10d2beccb9aef1fa Mon Sep 17 00:00:00 2001 From: Atik Date: Fri, 18 Dec 2020 11:05:38 +0200 Subject: [PATCH 14/34] Added active orders and stop orders. --- rest/api_order.go | 46 +++++++++++++++++++++++++++++++++++++++++++--- rest/result.go | 17 +++++++++-------- 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/rest/api_order.go b/rest/api_order.go index 2bf3957..3b700de 100644 --- a/rest/api_order.go +++ b/rest/api_order.go @@ -5,7 +5,7 @@ import ( "net/http" ) -// getOrders 查询活动委托 +// getOrders, async so they are not real-time func (b *ByBit) GetOrders(symbol string, orderStatus string, direction string, limit int, cursor string) (query string, result OrderListResponseResult, err error) { var cResult OrderListResponse @@ -39,6 +39,26 @@ func (b *ByBit) GetOrders(symbol string, orderStatus string, direction string, l return } +// getActiveOrders +func (b *ByBit) GetActiveOrders(symbol string) (query string, result OrderArrayResponse, err error) { + var cResult OrderArrayResponse + + params := map[string]interface{}{} + params["symbol"] = symbol + var resp []byte + query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/order", params, &cResult) + if err != nil { + return + } + if cResult.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) + return + } + + result = cResult + return +} + func (b *ByBit) CreateOrder(side string, orderType string, price float64, qty int, timeInForce string, takeProfit float64, stopLoss float64, reduceOnly bool, closeOnTrigger bool, orderLinkID string, symbol string) (query string, result Order, err error) { @@ -101,7 +121,7 @@ func (b *ByBit) ReplaceOrder(symbol string, orderID string, qty int, price float err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) return } - result.OrderID = cResult.Result.OrderID + result.OrderId = cResult.Result.OrderId return } @@ -146,7 +166,7 @@ func (b *ByBit) CancelAllOrder(symbol string) (query string, result []Order, err return } -// getStopOrders 查询条件委托单 +// getStopOrders, this are async so not updated, for time-sensitive use real-time func (b *ByBit) GetStopOrders(symbol string, stopOrderStatus string, direction string, limit int, cursor string) (query string, result StopOrderListResponseResult, err error) { var cResult StopOrderListResponse @@ -180,6 +200,26 @@ func (b *ByBit) GetStopOrders(symbol string, stopOrderStatus string, direction s return } +// getActiveOrders, real time +func (b *ByBit) GetActiveStopOrders(symbol string) (query string, result StopOrderArrayResponse, err error) { + var cResult StopOrderArrayResponse + + params := map[string]interface{}{} + params["symbol"] = symbol + var resp []byte + query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/stop-order", params, &cResult) + if err != nil { + return + } + if cResult.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) + return + } + + result = cResult + return +} + // CreateStopOrder 创建条件委托单 func (b *ByBit) CreateStopOrder(side string, orderType string, price float64, basePrice float64, stopPx float64, qty int, triggerBy string, timeInForce string, closeOnTrigger bool, symbol string) (query string, result StopOrder, err error) { diff --git a/rest/result.go b/rest/result.go index 2dccf43..17832bb 100644 --- a/rest/result.go +++ b/rest/result.go @@ -209,9 +209,9 @@ type PositionData struct { } type Position struct { - ID int `json:"id"` - UserID int `json:"user_id"` - RiskID int `json:"risk_id"` + Id int `json:"id"` + UserId int `json:"user_id"` + RiskId int `json:"risk_id"` Symbol string `json:"symbol"` Size float64 `json:"size"` Side string `json:"side"` @@ -243,8 +243,8 @@ type Position struct { } type Order struct { - UserID int `json:"user_id"` - OrderID string `json:"order_id"` + UserId int `json:"user_id"` + OrderId string `json:"order_id"` Symbol string `json:"symbol"` Side string `json:"side"` OrderType string `json:"order_type"` @@ -285,20 +285,21 @@ type OrderArrayResponse struct { } type StopOrder struct { + OrderId string `json:"order_id"` + OrderType string `json:"order_type"` + OrderStatus string `json:"order_status"` StopOrderId string `json:"stop_order_id"` StopOrderType string `json:"stop_order_type"` StopOrderStatus string `json:"stop_order_status"` StopPx sjson.Number `json:"stop_px"` - UserID int64 `json:"user_id"` + UserId int64 `json:"user_id"` Symbol string `json:"symbol"` Side string `json:"side"` - OrderType string `json:"order_type"` Price sjson.Number `json:"price"` Qty sjson.Number `json:"qty"` TimeInForce string `json:"time_in_force"` CreateType string `json:"create_type"` CancelType string `json:"cancel_type"` - OrderStatus string `json:"order_status"` LeavesQty sjson.Number `json:"leaves_qty"` LeavesValue string `json:"leaves_value"` CreatedAt time.Time `json:"created_at"` From 3ce8a461075fa670e428d8b193caa39ee0afd099 Mon Sep 17 00:00:00 2001 From: atik-lab Date: Sun, 27 Dec 2020 15:02:56 +0200 Subject: [PATCH 15/34] GetWalletRecords. --- rest/api_order.go | 29 +++++++++++++++++++++++++++++ rest/result.go | 23 +++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/rest/api_order.go b/rest/api_order.go index 3b700de..3a6100c 100644 --- a/rest/api_order.go +++ b/rest/api_order.go @@ -320,3 +320,32 @@ func (b *ByBit) CancelAllStopOrders(symbol string) (query string, result []StopO result = cResult.Result return } + +// GetWalletFunds WalletRecords +func (b *ByBit) WalletRecords(symbol string, page int, limit int) (query string, result []WalletFundRecord, err error) { + var r WalletFundRecordResponse + params := map[string]interface{}{} + if symbol != "" { + params["currency"] = symbol + } + if page > 0 { + params["page"] = page + } + if limit > 0 { + params["limit"] = limit + } + var resp []byte + // https://api2-testnet.bybit.com/v3/private/wallet/fund/records + // {"ret_code":0,"ret_msg":"OK","ext_code":"","result":{"list":[{"id":"741422","coin":"BTC","type":"RealisedPNL","amountE8":"4","walletBalanceE8":"2150270","execTimeE0":"1609027214","address":"BTCUSD"} + query, resp, err = b.SignedRequest(http.MethodPost, "v2/private/wallet/fund/records", params, &r) + if err != nil { + return + } + if r.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", r.RetMsg, string(resp)) + return + } + + result = r.Result.Data + return +} diff --git a/rest/result.go b/rest/result.go index 17832bb..39380d0 100644 --- a/rest/result.go +++ b/rest/result.go @@ -330,3 +330,26 @@ type StopOrderArrayResponse struct { BaseResult Result []StopOrder `json:"result"` } + +type WalletFundRecordResponse struct { + BaseResult + Result OrderListResponseArray `json:"result"` +} + +type OrderListResponseArray struct { + Data []WalletFundRecord `json:"data"` +} + +type WalletFundRecord struct { + Id string `json:"id"` + UserId string `json:"user_id"` + Coin string `json:"coin"` + WalletId string `json:"wallet_id"` + Type string `json:"type"` + Amount string `json:"amount"` + TxId string `json:"tx_id"` + Address string `json:"address"` + WalletBalance string `json:"wallet_balance"` + ExecTime string `json:"exec_time"` + CrossSeq string `json:"cross_seq"` +} From 8871aeb46757dce27b3752000ecf44ed40da2eee Mon Sep 17 00:00:00 2001 From: Atik Date: Sun, 27 Dec 2020 16:26:38 +0200 Subject: [PATCH 16/34] Added GetWalletFunds History. --- rest/api_order.go | 4 +--- rest/result.go | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/rest/api_order.go b/rest/api_order.go index 3a6100c..715e658 100644 --- a/rest/api_order.go +++ b/rest/api_order.go @@ -335,9 +335,7 @@ func (b *ByBit) WalletRecords(symbol string, page int, limit int) (query string, params["limit"] = limit } var resp []byte - // https://api2-testnet.bybit.com/v3/private/wallet/fund/records - // {"ret_code":0,"ret_msg":"OK","ext_code":"","result":{"list":[{"id":"741422","coin":"BTC","type":"RealisedPNL","amountE8":"4","walletBalanceE8":"2150270","execTimeE0":"1609027214","address":"BTCUSD"} - query, resp, err = b.SignedRequest(http.MethodPost, "v2/private/wallet/fund/records", params, &r) + query, resp, err = b.SignedRequest(http.MethodGet, "open-api/wallet/fund/records", params, &r) if err != nil { return } diff --git a/rest/result.go b/rest/result.go index 39380d0..621adb1 100644 --- a/rest/result.go +++ b/rest/result.go @@ -341,15 +341,15 @@ type OrderListResponseArray struct { } type WalletFundRecord struct { - Id string `json:"id"` - UserId string `json:"user_id"` - Coin string `json:"coin"` - WalletId string `json:"wallet_id"` - Type string `json:"type"` - Amount string `json:"amount"` - TxId string `json:"tx_id"` - Address string `json:"address"` - WalletBalance string `json:"wallet_balance"` - ExecTime string `json:"exec_time"` - CrossSeq string `json:"cross_seq"` + Id int `json:"id"` + UserId int `json:"user_id"` + Coin string `json:"coin"` + WalletId int `json:"wallet_id"` + Type string `json:"type"` + Amount string `json:"amount"` + TxId string `json:"tx_id"` + Address string `json:"address"` + WalletBalance string `json:"wallet_balance"` + ExecTime string `json:"exec_time"` + CrossSeq sjson.Number `json:"cross_seq"` } From 911182ad66d19b847ea34dc0eb28df40b0470c98 Mon Sep 17 00:00:00 2001 From: Atik Date: Sun, 27 Dec 2020 21:01:51 +0200 Subject: [PATCH 17/34] Fix. --- rest/result.go | 52 +++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/rest/result.go b/rest/result.go index 621adb1..3feba7f 100644 --- a/rest/result.go +++ b/rest/result.go @@ -66,29 +66,29 @@ type GetKlineResult struct { } type Ticker struct { - Symbol string `json:"symbol"` - BidPrice float64 `json:"bid_price,string"` - AskPrice float64 `json:"ask_price,string"` - LastPrice float64 `json:"last_price,string"` - LastTickDirection string `json:"last_tick_direction"` - PrevPrice24H float64 `json:"prev_price_24h,string"` - Price24HPcnt float64 `json:"price_24h_pcnt,string"` - HighPrice24H float64 `json:"high_price_24h,string"` - LowPrice24H float64 `json:"low_price_24h,string"` - PrevPrice1H float64 `json:"prev_price_1h,string"` - Price1HPcnt float64 `json:"price_1h_pcnt,string"` - MarkPrice float64 `json:"mark_price,string"` - IndexPrice float64 `json:"index_price,string"` - OpenInterest float64 `json:"open_interest"` - OpenValue float64 `json:"open_value,string"` - TotalTurnover float64 `json:"total_turnover,string"` - Turnover24H float64 `json:"turnover_24h,string"` - TotalVolume float64 `json:"total_volume"` - Volume24H float64 `json:"volume_24h"` - FundingRate float64 `json:"funding_rate,string"` - PredictedFundingRate float64 `json:"predicted_funding_rate,string"` - NextFundingTime string `json:"next_funding_time"` // string because can be empty, parse it with "2006-01-02T15:04:05Z07:00" - CountdownHour int `json:"countdown_hour"` + Symbol string `json:"symbol"` + BidPrice sjson.Number `json:"bid_price,string"` + AskPrice sjson.Number `json:"ask_price,string"` + LastPrice float64 `json:"last_price,string"` + LastTickDirection string `json:"last_tick_direction"` + PrevPrice24H float64 `json:"prev_price_24h,string"` + Price24HPcnt float64 `json:"price_24h_pcnt,string"` + HighPrice24H float64 `json:"high_price_24h,string"` + LowPrice24H float64 `json:"low_price_24h,string"` + PrevPrice1H float64 `json:"prev_price_1h,string"` + Price1HPcnt float64 `json:"price_1h_pcnt,string"` + MarkPrice float64 `json:"mark_price,string"` + IndexPrice float64 `json:"index_price,string"` + OpenInterest float64 `json:"open_interest"` + OpenValue float64 `json:"open_value,string"` + TotalTurnover float64 `json:"total_turnover,string"` + Turnover24H float64 `json:"turnover_24h,string"` + TotalVolume float64 `json:"total_volume"` + Volume24H float64 `json:"volume_24h"` + FundingRate float64 `json:"funding_rate,string"` + PredictedFundingRate float64 `json:"predicted_funding_rate,string"` + NextFundingTime string `json:"next_funding_time"` // string because can be empty, parse it with "2006-01-02T15:04:05Z07:00" + CountdownHour int `json:"countdown_hour"` } type GetTickersResult struct { @@ -346,10 +346,10 @@ type WalletFundRecord struct { Coin string `json:"coin"` WalletId int `json:"wallet_id"` Type string `json:"type"` - Amount string `json:"amount"` + Amount sjson.Number `json:"amount"` TxId string `json:"tx_id"` Address string `json:"address"` - WalletBalance string `json:"wallet_balance"` - ExecTime string `json:"exec_time"` + WalletBalance sjson.Number `json:"wallet_balance"` + ExecTime sjson.Number `json:"exec_time"` CrossSeq sjson.Number `json:"cross_seq"` } From 9ee88e38c4e66f610105a8e86f7f63aabb978379 Mon Sep 17 00:00:00 2001 From: atik-lab Date: Mon, 28 Dec 2020 12:26:05 +0200 Subject: [PATCH 18/34] BidPrice and AskPrice in ticker fix. --- rest/result.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rest/result.go b/rest/result.go index 3feba7f..b1d4a98 100644 --- a/rest/result.go +++ b/rest/result.go @@ -67,8 +67,8 @@ type GetKlineResult struct { type Ticker struct { Symbol string `json:"symbol"` - BidPrice sjson.Number `json:"bid_price,string"` - AskPrice sjson.Number `json:"ask_price,string"` + BidPrice sjson.Number `json:"bid_price"` + AskPrice sjson.Number `json:"ask_price"` LastPrice float64 `json:"last_price,string"` LastTickDirection string `json:"last_tick_direction"` PrevPrice24H float64 `json:"prev_price_24h,string"` From 5d2481be729d78ef5c9bc4a86b445e2899e68621 Mon Sep 17 00:00:00 2001 From: Atik Date: Tue, 29 Dec 2020 13:58:54 +0200 Subject: [PATCH 19/34] Return the response. --- rest/api.go | 12 +++++------- rest/api2_public.go | 2 +- rest/api_order.go | 39 +++++++++++++-------------------------- rest/api_public.go | 12 ++++++------ rest/api_public2.go | 2 +- 5 files changed, 26 insertions(+), 41 deletions(-) diff --git a/rest/api.go b/rest/api.go index 581896d..cde8d83 100644 --- a/rest/api.go +++ b/rest/api.go @@ -41,7 +41,7 @@ func New(httpClient *http.Client, baseURL string, apiKey string, secretKey strin // SetCorrectServerTime 校正服务器时间 func (b *ByBit) SetCorrectServerTime() (err error) { var timeNow int64 - _, timeNow, err = b.GetServerTime() + _, _, timeNow, err = b.GetServerTime() if err != nil { return } @@ -51,7 +51,7 @@ func (b *ByBit) SetCorrectServerTime() (err error) { // GetBalance Get Wallet Balance // coin: BTC,EOS,XRP,ETH,USDT -func (b *ByBit) GetWalletBalance(coin string) (query string, result Balance, err error) { +func (b *ByBit) GetWalletBalance(coin string) (query string, resp []byte, result Balance, err error) { var ret GetBalanceResult params := map[string]interface{}{} params["coin"] = coin @@ -75,11 +75,10 @@ func (b *ByBit) GetWalletBalance(coin string) (query string, result Balance, err } // GetPositions 获取我的仓位 -func (b *ByBit) GetPositions() (query string, result []PositionData, err error) { +func (b *ByBit) GetPositions() (query string, resp []byte, result []PositionData, err error) { var r PositionArrayResponse params := map[string]interface{}{} - var resp []byte query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/position/list", params, &r) if err != nil { return @@ -94,12 +93,11 @@ func (b *ByBit) GetPositions() (query string, result []PositionData, err error) } // GetPosition 获取我的仓位 -func (b *ByBit) GetPosition(symbol string) (query string, result Position, err error) { +func (b *ByBit) GetPosition(symbol string) (query string, resp []byte, result Position, err error) { var r PositionResponse params := map[string]interface{}{} params["symbol"] = symbol - var resp []byte query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/position/list", params, &r) if err != nil { return @@ -113,7 +111,7 @@ func (b *ByBit) GetPosition(symbol string) (query string, result Position, err e } // SetLeverage 设置杠杆 -func (b *ByBit) SetLeverage(leverage int, symbol string) (query string, err error) { +func (b *ByBit) SetLeverage(leverage int, symbol string) (query string, resp []byte, err error) { var r BaseResult params := map[string]interface{}{} params["symbol"] = symbol diff --git a/rest/api2_public.go b/rest/api2_public.go index 25430a5..026eaab 100644 --- a/rest/api2_public.go +++ b/rest/api2_public.go @@ -39,7 +39,7 @@ type FundingData struct { // https://api2.bybit.com/funding-rate/list?symbol=BTCUSD&date=&export=false&page=1&limit=20 // To use this you will need to set b.BaseUrl to api2.bybit.com or api2-testnet.bybit.com -func (b *ByBit) GetFunding(symbol string, page int, limit int) (query string, result []FundingData, e error) { +func (b *ByBit) GetFunding(symbol string, page int, limit int) (query string, resp []byte, result []FundingData, e error) { var ret FundingResult params := map[string]interface{}{} params["symbol"] = symbol diff --git a/rest/api_order.go b/rest/api_order.go index 715e658..319b75c 100644 --- a/rest/api_order.go +++ b/rest/api_order.go @@ -6,7 +6,7 @@ import ( ) // getOrders, async so they are not real-time -func (b *ByBit) GetOrders(symbol string, orderStatus string, direction string, limit int, cursor string) (query string, result OrderListResponseResult, err error) { +func (b *ByBit) GetOrders(symbol string, orderStatus string, direction string, limit int, cursor string) (query string, resp []byte, result OrderListResponseResult, err error) { var cResult OrderListResponse if limit == 0 { @@ -25,7 +25,6 @@ func (b *ByBit) GetOrders(symbol string, orderStatus string, direction string, l if cursor != "" { params["cursor"] = cursor } - var resp []byte query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/order/list", params, &cResult) if err != nil { return @@ -40,12 +39,11 @@ func (b *ByBit) GetOrders(symbol string, orderStatus string, direction string, l } // getActiveOrders -func (b *ByBit) GetActiveOrders(symbol string) (query string, result OrderArrayResponse, err error) { +func (b *ByBit) GetActiveOrders(symbol string) (query string, resp []byte, result OrderArrayResponse, err error) { var cResult OrderArrayResponse params := map[string]interface{}{} params["symbol"] = symbol - var resp []byte query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/order", params, &cResult) if err != nil { return @@ -61,7 +59,7 @@ func (b *ByBit) GetActiveOrders(symbol string) (query string, result OrderArrayR func (b *ByBit) CreateOrder(side string, orderType string, price float64, qty int, timeInForce string, takeProfit float64, stopLoss float64, reduceOnly bool, - closeOnTrigger bool, orderLinkID string, symbol string) (query string, result Order, err error) { + closeOnTrigger bool, orderLinkID string, symbol string) (query string, resp []byte, result Order, err error) { var cResult OrderResponse params := map[string]interface{}{} params["side"] = side @@ -87,7 +85,6 @@ func (b *ByBit) CreateOrder(side string, orderType string, price float64, if orderLinkID != "" { params["order_link_id"] = orderLinkID } - var resp []byte query, resp, err = b.SignedRequest(http.MethodPost, "v2/private/order/create", params, &cResult) if err != nil { return @@ -101,7 +98,7 @@ func (b *ByBit) CreateOrder(side string, orderType string, price float64, } // ReplaceOrder -func (b *ByBit) ReplaceOrder(symbol string, orderID string, qty int, price float64) (query string, result Order, err error) { +func (b *ByBit) ReplaceOrder(symbol string, orderID string, qty int, price float64) (query string, resp []byte, result Order, err error) { var cResult OrderResponse params := map[string]interface{}{} params["order_id"] = orderID @@ -112,7 +109,6 @@ func (b *ByBit) ReplaceOrder(symbol string, orderID string, qty int, price float if price > 0 { params["p_r_price"] = price } - var resp []byte query, resp, err = b.SignedRequest(http.MethodPost, "v2/private/order/replace", params, &cResult) if err != nil { return @@ -126,14 +122,13 @@ func (b *ByBit) ReplaceOrder(symbol string, orderID string, qty int, price float } // CancelOrder 撤销活动委托单 -func (b *ByBit) CancelOrder(orderID string, symbol string) (query string, result Order, err error) { +func (b *ByBit) CancelOrder(orderID string, symbol string) (query string, resp []byte, result Order, err error) { var cResult OrderResponse params := map[string]interface{}{} params["symbol"] = symbol if orderID != "" { params["order_id"] = orderID } - var resp []byte query, resp, err = b.SignedRequest(http.MethodPost, "v2/private/order/cancel", params, &cResult) if err != nil { return @@ -148,11 +143,10 @@ func (b *ByBit) CancelOrder(orderID string, symbol string) (query string, result } // CancelAllOrder Cancel All Active Orders -func (b *ByBit) CancelAllOrder(symbol string) (query string, result []Order, err error) { +func (b *ByBit) CancelAllOrder(symbol string) (query string, resp []byte, result []Order, err error) { var cResult OrderArrayResponse params := map[string]interface{}{} params["symbol"] = symbol - var resp []byte query, resp, err = b.SignedRequest(http.MethodPost, "v2/private/order/cancelAll", params, &cResult) if err != nil { return @@ -167,7 +161,7 @@ func (b *ByBit) CancelAllOrder(symbol string) (query string, result []Order, err } // getStopOrders, this are async so not updated, for time-sensitive use real-time -func (b *ByBit) GetStopOrders(symbol string, stopOrderStatus string, direction string, limit int, cursor string) (query string, result StopOrderListResponseResult, err error) { +func (b *ByBit) GetStopOrders(symbol string, stopOrderStatus string, direction string, limit int, cursor string) (query string, resp []byte, result StopOrderListResponseResult, err error) { var cResult StopOrderListResponse if limit == 0 { @@ -186,7 +180,6 @@ func (b *ByBit) GetStopOrders(symbol string, stopOrderStatus string, direction s if cursor != "" { params["cursor"] = cursor } - var resp []byte query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/stop-order/list", params, &cResult) if err != nil { return @@ -201,12 +194,11 @@ func (b *ByBit) GetStopOrders(symbol string, stopOrderStatus string, direction s } // getActiveOrders, real time -func (b *ByBit) GetActiveStopOrders(symbol string) (query string, result StopOrderArrayResponse, err error) { +func (b *ByBit) GetActiveStopOrders(symbol string) (query string, resp []byte, result StopOrderArrayResponse, err error) { var cResult StopOrderArrayResponse params := map[string]interface{}{} params["symbol"] = symbol - var resp []byte query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/stop-order", params, &cResult) if err != nil { return @@ -222,7 +214,7 @@ func (b *ByBit) GetActiveStopOrders(symbol string) (query string, result StopOrd // CreateStopOrder 创建条件委托单 func (b *ByBit) CreateStopOrder(side string, orderType string, price float64, basePrice float64, stopPx float64, - qty int, triggerBy string, timeInForce string, closeOnTrigger bool, symbol string) (query string, result StopOrder, err error) { + qty int, triggerBy string, timeInForce string, closeOnTrigger bool, symbol string) (query string, resp []byte, result StopOrder, err error) { var cResult StopOrderResponse params := map[string]interface{}{} params["side"] = side @@ -241,7 +233,6 @@ func (b *ByBit) CreateStopOrder(side string, orderType string, price float64, ba if triggerBy != "" { params["trigger_by"] = triggerBy } - var resp []byte query, resp, err = b.SignedRequest(http.MethodPost, "v2/private/stop-order/create", params, &cResult) if err != nil { return @@ -255,7 +246,7 @@ func (b *ByBit) CreateStopOrder(side string, orderType string, price float64, ba } // ReplaceStopOrder -func (b *ByBit) ReplaceStopOrder(symbol string, orderID string, qty int, price float64, triggerPrice float64) (query string, result StopOrder, err error) { +func (b *ByBit) ReplaceStopOrder(symbol string, orderID string, qty int, price float64, triggerPrice float64) (query string, resp []byte, result StopOrder, err error) { var cResult StopOrderResponse params := map[string]interface{}{} params["stop_order_id"] = orderID @@ -269,7 +260,6 @@ func (b *ByBit) ReplaceStopOrder(symbol string, orderID string, qty int, price f if triggerPrice > 0 { params["p_r_trigger_price"] = triggerPrice } - var resp []byte query, resp, err = b.SignedRequest(http.MethodPost, "v2/private/stop-order/replace", params, &cResult) if err != nil { return @@ -283,12 +273,11 @@ func (b *ByBit) ReplaceStopOrder(symbol string, orderID string, qty int, price f } // CancelStopOrder 撤销活动条件委托单 -func (b *ByBit) CancelStopOrder(orderID string, symbol string) (query string, result StopOrder, err error) { +func (b *ByBit) CancelStopOrder(orderID string, symbol string) (query string, resp []byte, result StopOrder, err error) { var cResult StopOrderResponse params := map[string]interface{}{} params["symbol"] = symbol params["stop_order_id"] = orderID - var resp []byte query, resp, err = b.SignedRequest(http.MethodPost, "v2/private/stop-order/cancel", params, &cResult) if err != nil { return @@ -303,11 +292,10 @@ func (b *ByBit) CancelStopOrder(orderID string, symbol string) (query string, re } // CancelAllStopOrders 撤消全部条件委托单 -func (b *ByBit) CancelAllStopOrders(symbol string) (query string, result []StopOrder, err error) { +func (b *ByBit) CancelAllStopOrders(symbol string) (query string, resp []byte, result []StopOrder, err error) { var cResult StopOrderArrayResponse params := map[string]interface{}{} params["symbol"] = symbol - var resp []byte query, resp, err = b.SignedRequest(http.MethodPost, "v2/private/stop-order/cancelAll", params, &cResult) if err != nil { return @@ -322,7 +310,7 @@ func (b *ByBit) CancelAllStopOrders(symbol string) (query string, result []StopO } // GetWalletFunds WalletRecords -func (b *ByBit) WalletRecords(symbol string, page int, limit int) (query string, result []WalletFundRecord, err error) { +func (b *ByBit) WalletRecords(symbol string, page int, limit int) (query string, resp []byte, result []WalletFundRecord, err error) { var r WalletFundRecordResponse params := map[string]interface{}{} if symbol != "" { @@ -334,7 +322,6 @@ func (b *ByBit) WalletRecords(symbol string, page int, limit int) (query string, if limit > 0 { params["limit"] = limit } - var resp []byte query, resp, err = b.SignedRequest(http.MethodGet, "open-api/wallet/fund/records", params, &r) if err != nil { return diff --git a/rest/api_public.go b/rest/api_public.go index 46a76ba..a764abe 100644 --- a/rest/api_public.go +++ b/rest/api_public.go @@ -8,7 +8,7 @@ import ( ) // GetServerTime Get server time. -func (b *ByBit) GetServerTime() (query string, timeNow int64, err error) { +func (b *ByBit) GetServerTime() (query string, resp []byte, timeNow int64, err error) { params := map[string]interface{}{} var ret BaseResult query, _, err = b.PublicRequest(http.MethodGet, "v2/public/time", params, &ret) @@ -26,7 +26,7 @@ func (b *ByBit) GetServerTime() (query string, timeNow int64, err error) { // GetOrderBook Get the orderbook // 正反向合约通用 -func (b *ByBit) GetOrderBook(symbol string) (query string, result OrderBook, err error) { +func (b *ByBit) GetOrderBook(symbol string) (query string, resp []byte, result OrderBook, err error) { var ret GetOrderBookResult params := map[string]interface{}{} params["symbol"] = symbol @@ -71,7 +71,7 @@ func (b *ByBit) GetOrderBook(symbol string) (query string, result OrderBook, err // interval: 1 3 5 15 30 60 120 240 360 720 "D" "M" "W" "Y" // from: From timestamp in seconds // limit: Limit for data size per page, max size is 200. Default as showing 200 pieces of data per page -func (b *ByBit) GetKLine(symbol string, interval string, from int64, limit int) (query string, result []OHLC, err error) { +func (b *ByBit) GetKLine(symbol string, interval string, from int64, limit int) (query string, resp []byte, result []OHLC, err error) { var ret GetKlineResult params := map[string]interface{}{} params["symbol"] = symbol @@ -88,7 +88,7 @@ func (b *ByBit) GetKLine(symbol string, interval string, from int64, limit int) return } -func (b *ByBit) GetTickers() (query string, result []Ticker, err error) { +func (b *ByBit) GetTickers() (query string, resp []byte, result []Ticker, err error) { // https://api-testnet.bybit.com/v2/public/tickers var ret GetTickersResult params := map[string]interface{}{} @@ -102,7 +102,7 @@ func (b *ByBit) GetTickers() (query string, result []Ticker, err error) { // from: From ID. Default: return latest data // limit: Number of results. Default 500; max 1000 -func (b *ByBit) GetTradingRecords(symbol string, from int64, limit int) (query string, result []TradingRecord, err error) { +func (b *ByBit) GetTradingRecords(symbol string, from int64, limit int) (query string, resp []byte, result []TradingRecord, err error) { var ret GetTradingRecordsResult params := map[string]interface{}{} params["symbol"] = symbol @@ -120,7 +120,7 @@ func (b *ByBit) GetTradingRecords(symbol string, from int64, limit int) (query s return } -func (b *ByBit) GetSymbols() (query string, result []SymbolInfo, err error) { +func (b *ByBit) GetSymbols() (query string, resp []byte, result []SymbolInfo, err error) { var ret GetSymbolsResult params := map[string]interface{}{} query, _, err = b.PublicRequest(http.MethodGet, "v2/public/symbols", params, &ret) diff --git a/rest/api_public2.go b/rest/api_public2.go index e8bd928..3d3a495 100644 --- a/rest/api_public2.go +++ b/rest/api_public2.go @@ -9,7 +9,7 @@ import ( // interval: 1 3 5 15 30 60 120 240 360 720 "D" "M" "W" "Y" // from: From timestamp in seconds // limit: Limit for data size per page, max size is 200. Default as showing 200 pieces of data per page -func (b *ByBit) GetKLine2(symbol string, interval string, from int64, limit int) (query string, result []OHLC2, err error) { +func (b *ByBit) GetKLine2(symbol string, interval string, from int64, limit int) (query string, resp []byte, result []OHLC2, err error) { var ret GetKlineResult2 params := map[string]interface{}{} params["symbol"] = symbol From 3375558c68714676c27c23bd1ee1bcb1fed6c14f Mon Sep 17 00:00:00 2001 From: Atik Date: Wed, 6 Jan 2021 19:11:16 +0200 Subject: [PATCH 20/34] Refactoring for linear. --- rest/api.go | 198 +++++---- rest/api.yaml | 571 -------------------------- rest/{api2_public.go => api2.go} | 21 +- rest/api2_test.go | 2 +- rest/{api_order.go => api_inverse.go} | 150 +++++-- rest/api_linear.go | 21 + rest/api_public.go | 132 ------ rest/api_public2.go | 27 -- rest/api_public2_test.go | 24 -- rest/api_test.go | 37 +- rest/generate.bat | 10 - rest/result2.go | 25 -- 12 files changed, 294 insertions(+), 924 deletions(-) delete mode 100644 rest/api.yaml rename rest/{api2_public.go => api2.go} (69%) rename rest/{api_order.go => api_inverse.go} (75%) create mode 100644 rest/api_linear.go delete mode 100644 rest/api_public.go delete mode 100644 rest/api_public2.go delete mode 100644 rest/api_public2_test.go delete mode 100644 rest/generate.bat delete mode 100644 rest/result2.go diff --git a/rest/api.go b/rest/api.go index cde8d83..c26b959 100644 --- a/rest/api.go +++ b/rest/api.go @@ -10,19 +10,22 @@ import ( "log" "net/http" "sort" + "strconv" "strings" "time" ) +// Bybit type ByBit struct { baseURL string // https://api-testnet.bybit.com/open-api/ apiKey string secretKey string - serverTimeOffset int64 // 时间偏差(ms) + serverTimeOffset int64 client *http.Client debugMode bool } +// New func New(httpClient *http.Client, baseURL string, apiKey string, secretKey string, debugMode bool) *ByBit { if httpClient == nil { httpClient = &http.Client{ @@ -38,7 +41,7 @@ func New(httpClient *http.Client, baseURL string, apiKey string, secretKey strin } } -// SetCorrectServerTime 校正服务器时间 +// SetCorrectServerTime func (b *ByBit) SetCorrectServerTime() (err error) { var timeNow int64 _, _, timeNow, err = b.GetServerTime() @@ -49,81 +52,7 @@ func (b *ByBit) SetCorrectServerTime() (err error) { return } -// GetBalance Get Wallet Balance -// coin: BTC,EOS,XRP,ETH,USDT -func (b *ByBit) GetWalletBalance(coin string) (query string, resp []byte, result Balance, err error) { - var ret GetBalanceResult - params := map[string]interface{}{} - params["coin"] = coin - query, _, err = b.SignedRequest(http.MethodGet, "v2/private/wallet/balance", params, &ret) // v2/private/wallet/balance - if err != nil { - return - } - switch coin { - case "BTC": - result = ret.Result.BTC - case "ETH": - result = ret.Result.ETH - case "EOS": - result = ret.Result.EOS - case "XRP": - result = ret.Result.XRP - case "USDT": - result = ret.Result.USDT - } - return -} - -// GetPositions 获取我的仓位 -func (b *ByBit) GetPositions() (query string, resp []byte, result []PositionData, err error) { - var r PositionArrayResponse - - params := map[string]interface{}{} - query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/position/list", params, &r) - if err != nil { - return - } - if r.RetCode != 0 { - err = fmt.Errorf("%v body: [%v]", r.RetMsg, string(resp)) - return - } - - result = r.Result - return -} - -// GetPosition 获取我的仓位 -func (b *ByBit) GetPosition(symbol string) (query string, resp []byte, result Position, err error) { - var r PositionResponse - - params := map[string]interface{}{} - params["symbol"] = symbol - query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/position/list", params, &r) - if err != nil { - return - } - if r.RetCode != 0 { - err = fmt.Errorf("%v body: [%v]", r.RetMsg, string(resp)) - return - } - result = r.Result - return -} - -// SetLeverage 设置杠杆 -func (b *ByBit) SetLeverage(leverage int, symbol string) (query string, resp []byte, err error) { - var r BaseResult - params := map[string]interface{}{} - params["symbol"] = symbol - params["leverage"] = fmt.Sprintf("%v", leverage) - query, _, err = b.SignedRequest(http.MethodPost, "user/leverage/save", params, &r) - if err != nil { - return - } - log.Println(r) - return -} - +// PublicRequest func (b *ByBit) PublicRequest(method string, apiURL string, params map[string]interface{}, result interface{}) (fullURL string, resp []byte, err error) { var keys []string for k := range params { @@ -173,6 +102,7 @@ func (b *ByBit) PublicRequest(method string, apiURL string, params map[string]in return } +// SignedRequest func (b *ByBit) SignedRequest(method string, apiURL string, params map[string]interface{}, result interface{}) (fullURL string, resp []byte, err error) { timestamp := time.Now().UnixNano()/1e6 + b.serverTimeOffset @@ -222,14 +152,126 @@ func (b *ByBit) SignedRequest(method string, apiURL string, params map[string]in if b.debugMode { log.Printf("SignedRequest: %v", string(resp)) } - err = json.Unmarshal(resp, result) return } +// getSigned func (b *ByBit) getSigned(param string) string { sig := hmac.New(sha256.New, []byte(b.secretKey)) sig.Write([]byte(param)) signature := hex.EncodeToString(sig.Sum(nil)) return signature } + +// GetServerTime +func (b *ByBit) GetServerTime() (query string, resp []byte, timeNow int64, err error) { + params := map[string]interface{}{} + var ret BaseResult + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/time", params, &ret) + if err != nil { + return + } + var t float64 + t, err = strconv.ParseFloat(ret.TimeNow, 64) + if err != nil { + return + } + timeNow = int64(t * 1000) + return +} + +// GetWalletBalance +func (b *ByBit) GetWalletBalance(coin string) (query string, resp []byte, result Balance, err error) { + var ret GetBalanceResult + params := map[string]interface{}{} + params["coin"] = coin + query, _, err = b.SignedRequest(http.MethodGet, "v2/private/wallet/balance", params, &ret) + if err != nil { + return + } + switch coin { + case "BTC": + result = ret.Result.BTC + case "ETH": + result = ret.Result.ETH + case "EOS": + result = ret.Result.EOS + case "XRP": + result = ret.Result.XRP + case "USDT": + result = ret.Result.USDT + } + return +} + +// GetPositions +func (b *ByBit) GetPositions() (query string, resp []byte, result []PositionData, err error) { + var r PositionArrayResponse + params := map[string]interface{}{} + query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/position/list", params, &r) + if err != nil { + return + } + if r.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", r.RetMsg, string(resp)) + return + } + result = r.Result + return +} + +// GetPosition +func (b *ByBit) GetPosition(symbol string) (query string, resp []byte, result Position, err error) { + var r PositionResponse + params := map[string]interface{}{} + params["symbol"] = symbol + query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/position/list", params, &r) + if err != nil { + return + } + if r.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", r.RetMsg, string(resp)) + return + } + result = r.Result + return +} + +// SetLeverage +func (b *ByBit) SetLeverage(leverage int, symbol string) (query string, resp []byte, err error) { + var r BaseResult + params := map[string]interface{}{} + params["symbol"] = symbol + params["leverage"] = fmt.Sprintf("%v", leverage) + query, _, err = b.SignedRequest(http.MethodPost, "user/leverage/save", params, &r) + if err != nil { + return + } + return +} + +// WalletRecords +func (b *ByBit) WalletRecords(symbol string, page int, limit int) (query string, resp []byte, result []WalletFundRecord, err error) { + var r WalletFundRecordResponse + params := map[string]interface{}{} + if symbol != "" { + params["currency"] = symbol + } + if page > 0 { + params["page"] = page + } + if limit > 0 { + params["limit"] = limit + } + query, resp, err = b.SignedRequest(http.MethodGet, "open-api/wallet/fund/records", params, &r) + if err != nil { + return + } + if r.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", r.RetMsg, string(resp)) + return + } + result = r.Result.Data + return +} diff --git a/rest/api.yaml b/rest/api.yaml deleted file mode 100644 index e66f3f0..0000000 --- a/rest/api.yaml +++ /dev/null @@ -1,571 +0,0 @@ -openapi: 3.0.0 -info: - version: 1.1.0 - title: Bybit - description: | - ## Overview - bybit-official-api-docs -servers: - - url: 'https://api-testnet.bybit.com' - description: For Testnet - - url: ' https://api.bybit.com' - description: For Mainnet - -x-tagGroups: - - name: Public Rest API1 - tags: - - public - - name: Private Rest API - tags: - - order - -paths: - # v1版本接口 - open-api/order/create: - post: - summary: place an order(will deprecat) - #deprecated: true - tags: - - order - description: | - Parameters of 'side', 'symbol', 'order_type', 'qty', 'price', 'time_in_force' are required for all active orders. Other parameters are optional unless specified. - - Market price active order: A traditional market price order, will be filled at the best available price. 'price' and 'time_in_force' can set to be "" if and only if you are placing market price order. - - Limit price active order: You can set an execution price for your order. Only when last traded price reaches the order price, the system will fill your order. - - Take profit/Stop loss: You may only set a take-profit/stop-loss conditional order upon opening the position. Once you hold a position, the take profit and stop loss information u sent when placing an order will no longer be valid. - - Order quantity: This parameter indicates the quantity of perpetual contracts you want to buy or sell, currently Bybit only support order quantity in an integer. - - Order price: This parameter indicates the price of perpetual contracts you want to buy or sell, currently Bybit only support price increment of every 0.5. - - Customize conditional order ID: You may customize order IDs for active orders. We will link it to the system order ID , and return the unique system order ID to you after the active order is created successfully. You may use this order ID to cancel your active order. The customized order ID is asked to be unique, with a maximum length of 36 characters. - - Note: Each account can hold up to 200 active orders yet to be filled entirely simultaneously. - - requestBody: - $ref: '#/components/requestBodies/CreateOrderReqV1' - responses: - 200: - description: cancel order response - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/CreateOrderRespV1' - - $ref: '#/components/schemas/ErrorResponse' - - # v2版本接口 - - v2/public/symbols: - get: - operationId: query_symbol - summary: symbol - tags: - - public - description: Query Symbols - responses: - 200: - description: response - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/QuerySymbolResp' - - $ref: '#/components/schemas/ErrorResponse' - - v2/public/kline/list: - get: - operationId: query_kline - summary: kline - tags: - - public - description: Query historical kline. - parameters: - - $ref: '#/components/parameters/symbol_in_path' - - $ref: '#/components/parameters/interval' - - $ref: '#/components/parameters/from' - - $ref: '#/components/parameters/limit' - responses: - 200: - description: response - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/QueryKLineResp' - - $ref: '#/components/schemas/ErrorResponse' - - v2/private/order/create: - post: - tags: - - order - summary: Place an order V2(developing) - operationId: create_order - requestBody: - $ref: '#/components/requestBodies/CreateOrderReqV2' - responses: - 200: - description: response - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/CreateOrderRespV2' - - $ref: '#/components/schemas/ErrorResponse' - - v2/private/order/cancel: - post: - tags: - - order - summary: Cancel an order V2(developing) - requestBody: - $ref: '#/components/requestBodies/CancelOrderReq' - responses: - 200: - description: cancel order response - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/CancelOrderResp' - - $ref: '#/components/schemas/ErrorResponse' - -components: - parameters: - symbol_in_query: - name: symbol - in: query - required: true - schema: - $ref: '#/components/schemas/Symbol' - - symbol_in_path: - name: symbol - in: path - required: true - schema: - $ref: '#/components/schemas/Symbol' - - side_in_query: - name: side - in: query - required: true - schema: - $ref: '#/components/schemas/Side' - - order_type_in_query: - name: order_type - in: query - required: true - schema: - $ref: '#/components/schemas/OrderType' - - interval: - name: interval - in: query - required: true - schema: - $ref: '#/components/schemas/Interval' - - from: - name: from - in: query - required: true - schema: - type: integer - limit: - name: limit - in: query - description: less than or equal 200 - schema: - type: integer - - requestBodies: - CreateOrderReqV1: - content: - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/CreateOrderReqV1' - application/json: - schema: - $ref: '#/components/schemas/CreateOrderReqV1' - - CreateOrderReqV2: - content: - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/CreateOrderReqV2' - application/json: - schema: - $ref: '#/components/schemas/CreateOrderReqV2' - - CancelOrderReq: - content: - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/CancelOrderReq' - application/json: - schema: - $ref: '#/components/schemas/CancelOrderReq' - - schemas: - # Enum - Symbol: - type: string - enum: - - BTCUSD - - ETHUSD - - XRPUSD - - EOSUSD - - Side: - type: string - enum: - - Buy - - Sell - - OrderType: - type: string - enum: - - Market - - Limit - - TimeInForce: - type: string - enum: - - GoodTillCancel - - ImmediateOrCancel - - FillOrKill - - PostOnly - - Interval: - type: string - enum: - - 1 - - 3 - - 5 - - 15 - - 30 - - 60 - - 120 - - 240 - - 360 - - 720 - - D - - M - - W - - Y - - OrderStatus: - type: string - enum: [Rejected, New, PartiallyFilled, Filled, Cancelled] - - # Requests - CreateOrderReqV1: - type: object - properties: - symbol: - $ref: '#/components/schemas/Symbol' - side: - $ref: '#/components/schemas/Side' - order_type: - $ref: '#/components/schemas/OrderType' - qty: - type: int64 - example: 10 - price: - type: number - example: 5000.5 - time_in_force: - $ref: '#/components/schemas/TimeInForce' - take_profit: - type: number - example: 5001 - stop_loss: - type: number - example: 4500.5 - order_link_id: - type: string - example: abcd-1234 - required: - - side - - symbol - - order_type - - qty - - price - - time_in_force - - CreateOrderReqV2: - type: object - properties: - symbol: - $ref: '#/components/schemas/Symbol' - side: - $ref: '#/components/schemas/Side' - order_type: - $ref: '#/components/schemas/OrderType' - qty: - type: int64 - example: 10 - price: - type: number - example: 5000.5 - time_in_force: - $ref: '#/components/schemas/TimeInForce' - order_link_id: - type: string - example: abcd-1234 - - CancelOrderReq: - type: object - properties: - order_id: - type: string - example: 1234-abcd - - # Responses - - SuccessResponse: - type: object - properties: - ret_code: - type: integer - description: ret_code - example: 0 - ret_msg: - type: string - description: 'will always be ok' - example: 'ok' - time_now: - type: string - description: utc timestamp of server - example: '1539778407.210858' - - required: - - ret_code - - ErrorResponse: - type: object - properties: - ret_code: - type: integer - description: ret_code - example: -1 - ret_msg: - type: string - description: Error message. - example: 'An error occurred.' - ext_code: - type: string - example: '' - result: - type: object - description: 'will be Null' - example: null - time_now: - type: string - description: utc timestamp of server - example: '1539778407.210858' - required: - - ret_code - - ret_msg - - QuerySymbolResp: - allOf: - - $ref: '#/components/schemas/SuccessResponse' - - type: object - properties: - result: - type: array - items: - $ref: '#/components/schemas/SymbolInfo' - - QueryKLineResp: - allOf: - - $ref: '#/components/schemas/SuccessResponse' - - type: object - properties: - result: - type: array - items: - $ref: '#/components/schemas/KLine' - required: - - result - - CreateOrderRespV1: - allOf: - - $ref: '#/components/schemas/SuccessResponse' - - type: object - properties: - result: - type: object - properties: - order_id: - type: string - example: abcd-1234 - user_id: - type: number - example: 1234 - side: - $ref: '#/components/schemas/Side' - order_type: - $ref: '#/components/schemas/OrderType' - price: - type: number - example: 5000.05 - qty: - type: number - example: 100 - time_in_force: - $ref: '#/components/schemas/TimeInForce' - order_status: - $ref: '#/components/schemas/OrderStatus' - last_exec_time: - type: number - example: 0.000000 - last_exec_price: - type: number - example: 0 - leaves_qty: - type: number - example: 0 - cum_exec_qty: - type: number - example: 0 - cum_exec_value: - type: number - example: 0 - cum_exec_fee: - type: number - example: 0 - reject_reason: - type: string - example: "" - order_link_id: - type: string - example: "" - created_at: - type: string - example: 2018-10-15T04:12:19.000Z - updated_at: - type: string - example: 2018-10-15T04:12:19.000Z - - CreateOrderRespV2: - allOf: - - $ref: '#/components/schemas/SuccessResponse' - - type: object - properties: - result: - type: object - properties: - order_id: - type: string - example: 1234-abcd - - CancelOrderResp: - allOf: - - $ref: '#/components/schemas/SuccessResponse' - - type: object - properties: - result: - type: string - nullable: true - example: null - - # Model - - SymbolInfo: - type: object - properties: - name: - type: string - example: BTCUSD - base_currency: - type: string - example: BTC - description: > - For currency pairs only. This field contains the first currency of the pair. - For example, base currency for `BTCUSD` ticker is `BTC`. - quote_currency: - type: string - example: USD - description: - For example, quote currency for `BTCUSD` ticker is `USD`. - price_scale: - type: number - example: 2 - description: > - Indicates how many decimal points the price has. - For example, if the price has 2 decimal points (ex., 300.01), then `price_scale` is `2`. - If it has 3 decimals, then `prices_cale` is `3` etc. - price_filter: - type: object - properties: - min_price: - type: number - example: 0.50 - description: when place an order, the min price should be greater than last_price/10 - max_price: - type: number - example: 1000000 - tick_size: - type: number - example: 0.50 - lot_size_filter: - type: object - properties: - min_trading_qty: - type: number - example: 1 - max_trading_qty: - type: number - example: 1000000 - qty_step: - type: number - example: 1 - - KLine: - type: object - properties: - symbol: - allOf: - - $ref: '#/components/schemas/Symbol' - interval: - type: string - enum: - - 1 - - 3 - - 5 - - 15 - - 30 - - 60 - - 120 - - 240 - - 360 - - 720 - - D - - M - - W - - Y - open_time: - type: integer - example: 1553846940 - open: - type: string - example: "5000.5" - high: - type: string - example: "5500.5" - low: - type: string - example: "4500" - close: - type: string - example: "5000.5" - volume: - type: string - example: "1100" - diff --git a/rest/api2_public.go b/rest/api2.go similarity index 69% rename from rest/api2_public.go rename to rest/api2.go index 026eaab..4f46ffd 100644 --- a/rest/api2_public.go +++ b/rest/api2.go @@ -37,8 +37,10 @@ type FundingData struct { Time time.Time `json:"time"` } +// To use this functions will need to set b.BaseUrl to api2.bybit.com or api2-testnet.bybit.com + +// GetFunding // https://api2.bybit.com/funding-rate/list?symbol=BTCUSD&date=&export=false&page=1&limit=20 -// To use this you will need to set b.BaseUrl to api2.bybit.com or api2-testnet.bybit.com func (b *ByBit) GetFunding(symbol string, page int, limit int) (query string, resp []byte, result []FundingData, e error) { var ret FundingResult params := map[string]interface{}{} @@ -54,4 +56,19 @@ func (b *ByBit) GetFunding(symbol string, page int, limit int) (query string, re return } -// for linear: https://api2.bybit.com/linear/funding-rate/list?symbol=BTCUSDT&date=&export=false&page=1 +// LinearGetFunding +// https://api2.bybit.com/linear/funding-rate/list?symbol=BTCUSDT&date=&export=false&page=1 +func (b *ByBit) LinearGetFunding(symbol string, page int, limit int) (query string, resp []byte, result []FundingData, e error) { + var ret FundingResult + params := map[string]interface{}{} + params["symbol"] = symbol + params["page"] = page + params["limit"] = limit // fixed limit 20 + params["export"] = false // fixed export + query, _, e = b.PublicRequest(http.MethodGet, "linear/funding-rate/list", params, &ret) + if e != nil { + return + } + result = ret.Result.Data + return +} diff --git a/rest/api2_test.go b/rest/api2_test.go index d35ff76..ddb3bb6 100644 --- a/rest/api2_test.go +++ b/rest/api2_test.go @@ -15,7 +15,7 @@ func newByBit2() *rest.ByBit { func TestByBit_GetFunding(t *testing.T) { b := newByBit2() - _, funding, e := b.GetFunding("BTCUSD", 1, 200) + _, _, funding, e := b.GetFunding("BTCUSD", 1, 200) if e != nil { t.Error(e) return diff --git a/rest/api_order.go b/rest/api_inverse.go similarity index 75% rename from rest/api_order.go rename to rest/api_inverse.go index 319b75c..1732711 100644 --- a/rest/api_order.go +++ b/rest/api_inverse.go @@ -3,9 +3,114 @@ package rest import ( "fmt" "net/http" + "sort" + "strconv" + "time" ) -// getOrders, async so they are not real-time +// GetOrderBook +func (b *ByBit) GetOrderBook(symbol string) (query string, resp []byte, result OrderBook, err error) { + var ret GetOrderBookResult + params := map[string]interface{}{} + params["symbol"] = symbol + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/orderBook/L2", params, &ret) + if err != nil { + return + } + + for _, v := range ret.Result { + if v.Side == "Sell" { + result.Asks = append(result.Asks, Item{ + Price: v.Price, + Size: v.Size, + }) + } else if v.Side == "Buy" { + result.Bids = append(result.Bids, Item{ + Price: v.Price, + Size: v.Size, + }) + } + } + + sort.Slice(result.Asks, func(i, j int) bool { + return result.Asks[i].Price < result.Asks[j].Price + }) + + sort.Slice(result.Bids, func(i, j int) bool { + return result.Bids[i].Price > result.Bids[j].Price + }) + + var timeNow float64 + timeNow, err = strconv.ParseFloat(ret.TimeNow, 64) // 1582011750.433202 + if err != nil { + return + } + result.Time = time.Unix(0, int64(timeNow*1e9)) + return +} + +// GetKLine +func (b *ByBit) GetKLine(symbol string, interval string, from int64, limit int) (query string, resp []byte, result []OHLC, err error) { + var ret GetKlineResult + params := map[string]interface{}{} + params["symbol"] = symbol + params["interval"] = interval + params["from"] = from + if limit > 0 { + params["limit"] = limit + } + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/kline/list", params, &ret) + if err != nil { + return + } + result = ret.Result + return +} + +// GetTickers +func (b *ByBit) GetTickers() (query string, resp []byte, result []Ticker, err error) { + var ret GetTickersResult + params := map[string]interface{}{} + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/tickers", params, &ret) + if err != nil { + return + } + result = ret.Result + return +} + +// GetTradingRecords +func (b *ByBit) GetTradingRecords(symbol string, from int64, limit int) (query string, resp []byte, result []TradingRecord, err error) { + var ret GetTradingRecordsResult + params := map[string]interface{}{} + params["symbol"] = symbol + if from > 0 { + params["from"] = from + } + if limit > 0 { + params["limit"] = limit + } + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/trading-records", params, &ret) + if err != nil { + return + } + result = ret.Result + return +} + +// GetSymbols +func (b *ByBit) GetSymbols() (query string, resp []byte, result []SymbolInfo, err error) { + var ret GetSymbolsResult + params := map[string]interface{}{} + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/symbols", params, &ret) + if err != nil { + return + } + result = ret.Result + return +} + +// GetOrders func (b *ByBit) GetOrders(symbol string, orderStatus string, direction string, limit int, cursor string) (query string, resp []byte, result OrderListResponseResult, err error) { var cResult OrderListResponse @@ -38,7 +143,7 @@ func (b *ByBit) GetOrders(symbol string, orderStatus string, direction string, l return } -// getActiveOrders +// GetActiveOrders func (b *ByBit) GetActiveOrders(symbol string) (query string, resp []byte, result OrderArrayResponse, err error) { var cResult OrderArrayResponse @@ -57,6 +162,7 @@ func (b *ByBit) GetActiveOrders(symbol string) (query string, resp []byte, resul return } +// CreateOrder func (b *ByBit) CreateOrder(side string, orderType string, price float64, qty int, timeInForce string, takeProfit float64, stopLoss float64, reduceOnly bool, closeOnTrigger bool, orderLinkID string, symbol string) (query string, resp []byte, result Order, err error) { @@ -121,7 +227,7 @@ func (b *ByBit) ReplaceOrder(symbol string, orderID string, qty int, price float return } -// CancelOrder 撤销活动委托单 +// CancelOrder func (b *ByBit) CancelOrder(orderID string, symbol string) (query string, resp []byte, result Order, err error) { var cResult OrderResponse params := map[string]interface{}{} @@ -142,7 +248,7 @@ func (b *ByBit) CancelOrder(orderID string, symbol string) (query string, resp [ return } -// CancelAllOrder Cancel All Active Orders +// CancelAllOrder func (b *ByBit) CancelAllOrder(symbol string) (query string, resp []byte, result []Order, err error) { var cResult OrderArrayResponse params := map[string]interface{}{} @@ -160,7 +266,7 @@ func (b *ByBit) CancelAllOrder(symbol string) (query string, resp []byte, result return } -// getStopOrders, this are async so not updated, for time-sensitive use real-time +// GetStopOrders func (b *ByBit) GetStopOrders(symbol string, stopOrderStatus string, direction string, limit int, cursor string) (query string, resp []byte, result StopOrderListResponseResult, err error) { var cResult StopOrderListResponse @@ -193,7 +299,7 @@ func (b *ByBit) GetStopOrders(symbol string, stopOrderStatus string, direction s return } -// getActiveOrders, real time +// GetActiveStopOrders func (b *ByBit) GetActiveStopOrders(symbol string) (query string, resp []byte, result StopOrderArrayResponse, err error) { var cResult StopOrderArrayResponse @@ -212,7 +318,7 @@ func (b *ByBit) GetActiveStopOrders(symbol string) (query string, resp []byte, r return } -// CreateStopOrder 创建条件委托单 +// CreateStopOrder func (b *ByBit) CreateStopOrder(side string, orderType string, price float64, basePrice float64, stopPx float64, qty int, triggerBy string, timeInForce string, closeOnTrigger bool, symbol string) (query string, resp []byte, result StopOrder, err error) { var cResult StopOrderResponse @@ -272,7 +378,7 @@ func (b *ByBit) ReplaceStopOrder(symbol string, orderID string, qty int, price f return } -// CancelStopOrder 撤销活动条件委托单 +// CancelStopOrder func (b *ByBit) CancelStopOrder(orderID string, symbol string) (query string, resp []byte, result StopOrder, err error) { var cResult StopOrderResponse params := map[string]interface{}{} @@ -291,7 +397,7 @@ func (b *ByBit) CancelStopOrder(orderID string, symbol string) (query string, re return } -// CancelAllStopOrders 撤消全部条件委托单 +// CancelAllStopOrders func (b *ByBit) CancelAllStopOrders(symbol string) (query string, resp []byte, result []StopOrder, err error) { var cResult StopOrderArrayResponse params := map[string]interface{}{} @@ -308,29 +414,3 @@ func (b *ByBit) CancelAllStopOrders(symbol string) (query string, resp []byte, r result = cResult.Result return } - -// GetWalletFunds WalletRecords -func (b *ByBit) WalletRecords(symbol string, page int, limit int) (query string, resp []byte, result []WalletFundRecord, err error) { - var r WalletFundRecordResponse - params := map[string]interface{}{} - if symbol != "" { - params["currency"] = symbol - } - if page > 0 { - params["page"] = page - } - if limit > 0 { - params["limit"] = limit - } - query, resp, err = b.SignedRequest(http.MethodGet, "open-api/wallet/fund/records", params, &r) - if err != nil { - return - } - if r.RetCode != 0 { - err = fmt.Errorf("%v body: [%v]", r.RetMsg, string(resp)) - return - } - - result = r.Result.Data - return -} diff --git a/rest/api_linear.go b/rest/api_linear.go new file mode 100644 index 0000000..6ad2d33 --- /dev/null +++ b/rest/api_linear.go @@ -0,0 +1,21 @@ +package rest + +import "net/http" + +// LinearGetKLine +func (b *ByBit) LinearGetKLine(symbol string, interval string, from int64, limit int) (query string, resp []byte, result []OHLC, err error) { + var ret GetKlineResult + params := map[string]interface{}{} + params["symbol"] = symbol + params["interval"] = interval + params["from"] = from + if limit > 0 { + params["limit"] = limit + } + query, _, err = b.PublicRequest(http.MethodGet, "public/linear/kline", params, &ret) + if err != nil { + return + } + result = ret.Result + return +} diff --git a/rest/api_public.go b/rest/api_public.go deleted file mode 100644 index a764abe..0000000 --- a/rest/api_public.go +++ /dev/null @@ -1,132 +0,0 @@ -package rest - -import ( - "net/http" - "sort" - "strconv" - "time" -) - -// GetServerTime Get server time. -func (b *ByBit) GetServerTime() (query string, resp []byte, timeNow int64, err error) { - params := map[string]interface{}{} - var ret BaseResult - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/time", params, &ret) - if err != nil { - return - } - var t float64 - t, err = strconv.ParseFloat(ret.TimeNow, 64) - if err != nil { - return - } - timeNow = int64(t * 1000) - return -} - -// GetOrderBook Get the orderbook -// 正反向合约通用 -func (b *ByBit) GetOrderBook(symbol string) (query string, resp []byte, result OrderBook, err error) { - var ret GetOrderBookResult - params := map[string]interface{}{} - params["symbol"] = symbol - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/orderBook/L2", params, &ret) - if err != nil { - return - } - - for _, v := range ret.Result { - if v.Side == "Sell" { - result.Asks = append(result.Asks, Item{ - Price: v.Price, - Size: v.Size, - }) - } else if v.Side == "Buy" { - result.Bids = append(result.Bids, Item{ - Price: v.Price, - Size: v.Size, - }) - } - } - - sort.Slice(result.Asks, func(i, j int) bool { - return result.Asks[i].Price < result.Asks[j].Price - }) - - sort.Slice(result.Bids, func(i, j int) bool { - return result.Bids[i].Price > result.Bids[j].Price - }) - - var timeNow float64 - timeNow, err = strconv.ParseFloat(ret.TimeNow, 64) // 1582011750.433202 - if err != nil { - return - } - result.Time = time.Unix(0, int64(timeNow*1e9)) - return -} - -// GetKLine -// https://bybit-exchange.github.io/docs/inverse/#t-httprequest-2 -// interval: 1 3 5 15 30 60 120 240 360 720 "D" "M" "W" "Y" -// from: From timestamp in seconds -// limit: Limit for data size per page, max size is 200. Default as showing 200 pieces of data per page -func (b *ByBit) GetKLine(symbol string, interval string, from int64, limit int) (query string, resp []byte, result []OHLC, err error) { - var ret GetKlineResult - params := map[string]interface{}{} - params["symbol"] = symbol - params["interval"] = interval - params["from"] = from - if limit > 0 { - params["limit"] = limit - } - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/kline/list", params, &ret) - if err != nil { - return - } - result = ret.Result - return -} - -func (b *ByBit) GetTickers() (query string, resp []byte, result []Ticker, err error) { - // https://api-testnet.bybit.com/v2/public/tickers - var ret GetTickersResult - params := map[string]interface{}{} - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/tickers", params, &ret) - if err != nil { - return - } - result = ret.Result - return -} - -// from: From ID. Default: return latest data -// limit: Number of results. Default 500; max 1000 -func (b *ByBit) GetTradingRecords(symbol string, from int64, limit int) (query string, resp []byte, result []TradingRecord, err error) { - var ret GetTradingRecordsResult - params := map[string]interface{}{} - params["symbol"] = symbol - if from > 0 { - params["from"] = from - } - if limit > 0 { - params["limit"] = limit - } - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/trading-records", params, &ret) - if err != nil { - return - } - result = ret.Result - return -} - -func (b *ByBit) GetSymbols() (query string, resp []byte, result []SymbolInfo, err error) { - var ret GetSymbolsResult - params := map[string]interface{}{} - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/symbols", params, &ret) - if err != nil { - return - } - result = ret.Result - return -} diff --git a/rest/api_public2.go b/rest/api_public2.go deleted file mode 100644 index 3d3a495..0000000 --- a/rest/api_public2.go +++ /dev/null @@ -1,27 +0,0 @@ -package rest - -import ( - "net/http" -) - -// GetKLine2 (USDT永续) -// https://bybit-exchange.github.io/docs/zh-cn/linear/#t-querykline -// interval: 1 3 5 15 30 60 120 240 360 720 "D" "M" "W" "Y" -// from: From timestamp in seconds -// limit: Limit for data size per page, max size is 200. Default as showing 200 pieces of data per page -func (b *ByBit) GetKLine2(symbol string, interval string, from int64, limit int) (query string, resp []byte, result []OHLC2, err error) { - var ret GetKlineResult2 - params := map[string]interface{}{} - params["symbol"] = symbol - params["interval"] = interval - params["from"] = from - if limit > 0 { - params["limit"] = limit - } - query, _, err = b.PublicRequest(http.MethodGet, "public/linear/kline", params, &ret) - if err != nil { - return - } - result = ret.Result - return -} diff --git a/rest/api_public2_test.go b/rest/api_public2_test.go deleted file mode 100644 index 2fccae5..0000000 --- a/rest/api_public2_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package rest - -import ( - "testing" - "time" -) - -func TestGetKLine2(t *testing.T) { - b := newByBit() - from := time.Now().Add(-1 * time.Hour).Unix() - _, ohlcs, err := b.GetKLine2( - "BTCUSDT", - "1", - from, - 0, - ) - if err != nil { - t.Error(err) - return - } - for _, v := range ohlcs { - t.Logf("%#v", v) - } -} diff --git a/rest/api_test.go b/rest/api_test.go index d8a0f08..5102305 100644 --- a/rest/api_test.go +++ b/rest/api_test.go @@ -7,7 +7,6 @@ import ( "time" ) -// 开发电报群: // https://t.me/Bybitapi func newByBit() *ByBit { @@ -26,7 +25,7 @@ func newByBit() *ByBit { func TestByBit_GetServerTime(t *testing.T) { b := newByBit() - _, timeNow, err := b.GetServerTime() + _, _, timeNow, err := b.GetServerTime() if err != nil { t.Error(err) return @@ -49,7 +48,7 @@ func TestByBit_SetCorrectServerTime(t *testing.T) { func TestByBit_GetOrderBook(t *testing.T) { b := newByBit() - _, ob, err := b.GetOrderBook("BTCUSD") + _, _, ob, err := b.GetOrderBook("BTCUSD") if err != nil { t.Error(err) return @@ -65,7 +64,7 @@ func TestByBit_GetOrderBook(t *testing.T) { func TestByBit_GetOrderBook2(t *testing.T) { b := newByBit() - _, ob, err := b.GetOrderBook("BTCUSDT") + _, _, ob, err := b.GetOrderBook("BTCUSDT") if err != nil { t.Error(err) return @@ -82,7 +81,7 @@ func TestByBit_GetOrderBook2(t *testing.T) { func TestByBit_GetKLine(t *testing.T) { b := newByBit() from := time.Now().Add(-1 * time.Hour).Unix() - _, ohlcs, err := b.GetKLine( + _, _, ohlcs, err := b.GetKLine( "BTCUSD", "1", from, @@ -99,7 +98,7 @@ func TestByBit_GetKLine(t *testing.T) { func TestByBit_GetTickers(t *testing.T) { b := newByBit() - _, tickers, err := b.GetTickers() + _, _, tickers, err := b.GetTickers() if err != nil { t.Error() return @@ -111,7 +110,7 @@ func TestByBit_GetTickers(t *testing.T) { func TestByBit_GetTradingRecords(t *testing.T) { b := newByBit() - _, records, err := b.GetTradingRecords("BTCUSD", 0, 0) + _, _, records, err := b.GetTradingRecords("BTCUSD", 0, 0) if err != nil { t.Error(err) return @@ -123,7 +122,7 @@ func TestByBit_GetTradingRecords(t *testing.T) { func TestByBit_GetSymbols(t *testing.T) { b := newByBit() - _, symbols, err := b.GetSymbols() + _, _, symbols, err := b.GetSymbols() if err != nil { t.Error(err) return @@ -135,7 +134,7 @@ func TestByBit_GetSymbols(t *testing.T) { func TestByBit_GetWalletBalance(t *testing.T) { b := newByBit() - _, balance, err := b.GetWalletBalance("BTC") + _, _, balance, err := b.GetWalletBalance("BTC") if err != nil { t.Error(err) return @@ -146,7 +145,7 @@ func TestByBit_GetWalletBalance(t *testing.T) { func TestByBit_GetOrders(t *testing.T) { b := newByBit() symbol := "BTCUSD" - _, orders, err := b.GetOrders(symbol, "", "next", 20, "") + _, _, orders, err := b.GetOrders(symbol, "", "next", 20, "") assert.Nil(t, err) //t.Logf("%#v", orders) for _, order := range orders.Data { @@ -163,7 +162,7 @@ func TestByBit_CreateOrder(t *testing.T) { price := 5000.0 timeInForce := "GoodTillCancel" // {"ret_code":0,"ret_msg":"ok","ext_code":"","result":{"user_id":103061,"symbol":"BTCUSD","side":"Buy","order_type":"Limit","price":"7000","qty":30,"time_in_force":"GoodTillCancel","order_status":"Created","ext_fields":{"cross_status":"PendingNew","xreq_type":"x_create","xreq_offset":148672558},"leaves_qty":30,"leaves_value":"0.00428571","reject_reason":"","cross_seq":-1,"created_at":"2019-07-23T08:54:54.000Z","updated_at":"2019-07-23T08:54:54.000Z","last_exec_time":"0.000000","last_exec_price":0,"order_id":"603c41e0-c9fb-450c-90b6-ea870d5b0180"},"ext_info":null,"time_now":"1563872094.895918","rate_limit_status":98} - _, order, err := b.CreateOrder( + _, _, order, err := b.CreateOrder( side, orderType, price, @@ -186,7 +185,7 @@ func TestByBit_CancelOrder(t *testing.T) { b := newByBit() orderID := "c5b96b82-6a79-4b15-a797-361fe2ca0260" symbol := "BTCUSD" - _, order, err := b.CancelOrder(orderID, symbol) + _, _, order, err := b.CancelOrder(orderID, symbol) assert.Nil(t, err) t.Logf("%#v", order) } @@ -196,7 +195,7 @@ func TestByBit_GetStopOrders(t *testing.T) { symbol := "BTCUSD" // Untriggered: 等待市价触发条件单; Triggered: 市价已触发条件单; Cancelled: 取消; Active: 条件单触发成功且下单成功; Rejected: 条件触发成功但下单失败 status := "Untriggered,Triggered,Active" - _, result, err := b.GetStopOrders(symbol, status, "next", 20, "") + _, _, result, err := b.GetStopOrders(symbol, status, "next", 20, "") assert.Nil(t, err) //t.Logf("%#v", orders) for _, order := range result.Data { @@ -220,7 +219,7 @@ func TestByBit_CreateStopOrder(t *testing.T) { triggerBy := "" timeInForce := "GoodTillCancel" // {"ret_code":0,"ret_msg":"ok","ext_code":"","result":{"user_id":103061,"symbol":"BTCUSD","side":"Buy","order_type":"Limit","price":"7000","qty":30,"time_in_force":"GoodTillCancel","order_status":"Created","ext_fields":{"cross_status":"PendingNew","xreq_type":"x_create","xreq_offset":148672558},"leaves_qty":30,"leaves_value":"0.00428571","reject_reason":"","cross_seq":-1,"created_at":"2019-07-23T08:54:54.000Z","updated_at":"2019-07-23T08:54:54.000Z","last_exec_time":"0.000000","last_exec_price":0,"order_id":"603c41e0-c9fb-450c-90b6-ea870d5b0180"},"ext_info":null,"time_now":"1563872094.895918","rate_limit_status":98} - _, order, err := b.CreateStopOrder(side, + _, _, order, err := b.CreateStopOrder(side, orderType, price, basePrice, stopPx, qty, triggerBy, timeInForce, true, symbol) if err != nil { t.Error(err) @@ -233,7 +232,7 @@ func TestByBit_CancelStopOrder(t *testing.T) { b := newByBit() orderID := "c6e535a9-6900-4b64-b983-3b220f6f41f8" symbol := "BTCUSD" - _, order, err := b.CancelStopOrder(orderID, symbol) + _, _, order, err := b.CancelStopOrder(orderID, symbol) assert.Nil(t, err) t.Logf("%#v", order) } @@ -241,26 +240,26 @@ func TestByBit_CancelStopOrder(t *testing.T) { func TestByBit_CancelAllStopOrders(t *testing.T) { b := newByBit() symbol := "BTCUSD" - _, orders, err := b.CancelAllStopOrders(symbol) + _, _, orders, err := b.CancelAllStopOrders(symbol) assert.Nil(t, err) t.Logf("%#v", orders) } func TestByBit_SetLeverage(t *testing.T) { b := newByBit() - _, _ = b.SetLeverage(3, "BTCUSD") + _, _, _ = b.SetLeverage(3, "BTCUSD") } func TestByBit_GetPositions(t *testing.T) { b := newByBit() - _, positions, err := b.GetPositions() + _, _, positions, err := b.GetPositions() assert.Nil(t, err) t.Logf("%#v", positions) } func TestByBit_GetPosition(t *testing.T) { b := newByBit() - _, position, err := b.GetPosition("BTCUSD") + _, _, position, err := b.GetPosition("BTCUSD") assert.Nil(t, err) t.Logf("%#v", position) } diff --git a/rest/generate.bat b/rest/generate.bat deleted file mode 100644 index a03f35b..0000000 --- a/rest/generate.bat +++ /dev/null @@ -1,10 +0,0 @@ -swagger generate client /C api.yaml - -# https://openapi-generator.tech -# 安装: -# npm install @openapitools/openapi-generator-cli -g -openapi-generator generate -g go --additional-properties=prependFormOrBodyParameters=true \ - -o out -i petstore.yaml - -npx openapi-generator generate -g go -o ./out -i api.yaml -# npx openapi-generator generate -i petstore.yaml -g ruby -o /tmp/test/ diff --git a/rest/result2.go b/rest/result2.go deleted file mode 100644 index 3fad506..0000000 --- a/rest/result2.go +++ /dev/null @@ -1,25 +0,0 @@ -package rest - -type OHLC2 struct { - ID int64 `json:"id"` - Symbol string `json:"symbol"` - Period string `json:"period"` - Interval string `json:"interval"` - StartAt int64 `json:"start_at"` - OpenTime int64 `json:"open_time"` - Volume float64 `json:"volume"` - Open float64 `json:"open"` - High float64 `json:"high"` - Low float64 `json:"low"` - Close float64 `json:"close"` - Turnover float64 `json:"turnover"` -} - -type GetKlineResult2 struct { - RetCode int `json:"ret_code"` - RetMsg string `json:"ret_msg"` - ExtCode string `json:"ext_code"` - ExtInfo string `json:"ext_info"` - Result []OHLC2 `json:"result"` - TimeNow string `json:"time_now"` -} From a8ab44751e0b6d1c459e278174e8d6826c3aff6c Mon Sep 17 00:00:00 2001 From: Atik Date: Wed, 6 Jan 2021 20:20:35 +0200 Subject: [PATCH 21/34] Refactoring for linear. --- rest/api.go | 43 +++++++++++ rest/api_inverse.go | 43 ----------- rest/api_inverse_test.go | 158 +++++++++++++++++++++++++++++++++++++++ rest/api_test.go | 153 ------------------------------------- 4 files changed, 201 insertions(+), 196 deletions(-) create mode 100644 rest/api_inverse_test.go diff --git a/rest/api.go b/rest/api.go index c26b959..0124ca0 100644 --- a/rest/api.go +++ b/rest/api.go @@ -275,3 +275,46 @@ func (b *ByBit) WalletRecords(symbol string, page int, limit int) (query string, result = r.Result.Data return } + +// GetTickers +func (b *ByBit) GetTickers() (query string, resp []byte, result []Ticker, err error) { + var ret GetTickersResult + params := map[string]interface{}{} + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/tickers", params, &ret) + if err != nil { + return + } + result = ret.Result + return +} + +// GetTradingRecords +func (b *ByBit) GetTradingRecords(symbol string, from int64, limit int) (query string, resp []byte, result []TradingRecord, err error) { + var ret GetTradingRecordsResult + params := map[string]interface{}{} + params["symbol"] = symbol + if from > 0 { + params["from"] = from + } + if limit > 0 { + params["limit"] = limit + } + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/trading-records", params, &ret) + if err != nil { + return + } + result = ret.Result + return +} + +// GetSymbols +func (b *ByBit) GetSymbols() (query string, resp []byte, result []SymbolInfo, err error) { + var ret GetSymbolsResult + params := map[string]interface{}{} + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/symbols", params, &ret) + if err != nil { + return + } + result = ret.Result + return +} diff --git a/rest/api_inverse.go b/rest/api_inverse.go index 1732711..a790c22 100644 --- a/rest/api_inverse.go +++ b/rest/api_inverse.go @@ -67,49 +67,6 @@ func (b *ByBit) GetKLine(symbol string, interval string, from int64, limit int) return } -// GetTickers -func (b *ByBit) GetTickers() (query string, resp []byte, result []Ticker, err error) { - var ret GetTickersResult - params := map[string]interface{}{} - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/tickers", params, &ret) - if err != nil { - return - } - result = ret.Result - return -} - -// GetTradingRecords -func (b *ByBit) GetTradingRecords(symbol string, from int64, limit int) (query string, resp []byte, result []TradingRecord, err error) { - var ret GetTradingRecordsResult - params := map[string]interface{}{} - params["symbol"] = symbol - if from > 0 { - params["from"] = from - } - if limit > 0 { - params["limit"] = limit - } - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/trading-records", params, &ret) - if err != nil { - return - } - result = ret.Result - return -} - -// GetSymbols -func (b *ByBit) GetSymbols() (query string, resp []byte, result []SymbolInfo, err error) { - var ret GetSymbolsResult - params := map[string]interface{}{} - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/symbols", params, &ret) - if err != nil { - return - } - result = ret.Result - return -} - // GetOrders func (b *ByBit) GetOrders(symbol string, orderStatus string, direction string, limit int, cursor string) (query string, resp []byte, result OrderListResponseResult, err error) { var cResult OrderListResponse diff --git a/rest/api_inverse_test.go b/rest/api_inverse_test.go new file mode 100644 index 0000000..ffe6b3c --- /dev/null +++ b/rest/api_inverse_test.go @@ -0,0 +1,158 @@ +package rest + +import ( + "github.com/stretchr/testify/assert" + "testing" + "time" +) + +func TestByBit_GetOrderBook(t *testing.T) { + b := newByBit() + _, _, ob, err := b.GetOrderBook("BTCUSD") + if err != nil { + t.Error(err) + return + } + for _, v := range ob.Asks { + t.Logf("Ask: %#v", v) + } + for _, v := range ob.Bids { + t.Logf("Bid: %#v", v) + } + t.Logf("%v", ob.Time) +} + +func TestByBit_GetOrderBook2(t *testing.T) { + b := newByBit() + _, _, ob, err := b.GetOrderBook("BTCUSDT") + if err != nil { + t.Error(err) + return + } + for _, v := range ob.Asks { + t.Logf("Ask: %#v", v) + } + for _, v := range ob.Bids { + t.Logf("Bid: %#v", v) + } + t.Logf("%v", ob.Time) +} + +func TestByBit_GetKLine(t *testing.T) { + b := newByBit() + from := time.Now().Add(-1 * time.Hour).Unix() + _, _, ohlcs, err := b.GetKLine( + "BTCUSD", + "1", + from, + 0, + ) + if err != nil { + t.Error(err) + return + } + for _, v := range ohlcs { + t.Logf("%#v", v) + } +} + +func TestByBit_GetOrders(t *testing.T) { + b := newByBit() + symbol := "BTCUSD" + _, _, orders, err := b.GetOrders(symbol, "", "next", 20, "") + assert.Nil(t, err) + //t.Logf("%#v", orders) + for _, order := range orders.Data { + t.Logf("%#v", order) + } +} + +func TestByBit_CreateOrder(t *testing.T) { + b := newByBit() + symbol := "BTCUSD" + side := "Buy" // Buy Sell + orderType := "Limit" + qty := 30 + price := 5000.0 + timeInForce := "GoodTillCancel" + _, _, order, err := b.CreateOrder( + side, + orderType, + price, + qty, + timeInForce, + 0, + 0, + false, + false, + "", + symbol) + if err != nil { + t.Error(err) + return + } + t.Logf("%#v", order) +} + +func TestByBit_CancelOrder(t *testing.T) { + b := newByBit() + orderID := "c5b96b82-6a79-4b15-a797-361fe2ca0260" + symbol := "BTCUSD" + _, _, order, err := b.CancelOrder(orderID, symbol) + assert.Nil(t, err) + t.Logf("%#v", order) +} + +func TestByBit_GetStopOrders(t *testing.T) { + b := newByBit() + symbol := "BTCUSD" + status := "Untriggered,Triggered,Active" + _, _, result, err := b.GetStopOrders(symbol, status, "next", 20, "") + assert.Nil(t, err) + //t.Logf("%#v", orders) + for _, order := range result.Data { + //if order.ExtFields != nil { + // t.Logf("%#v %v", order, *order.ExtFields) + //} else { + t.Logf("CreatedAt: %v %#v", order.CreatedAt.Local(), order) + //} + } +} + +func TestByBit_CreateStopOrder(t *testing.T) { + b := newByBit() + symbol := "BTCUSD" + side := "Buy" // Buy Sell + orderType := "Limit" + qty := 30 + price := 10000.0 + basePrice := 7100.0 + stopPx := 10000.0 + triggerBy := "" + timeInForce := "GoodTillCancel" + // {"ret_code":0,"ret_msg":"ok","ext_code":"","result":{"user_id":103061,"symbol":"BTCUSD","side":"Buy","order_type":"Limit","price":"7000","qty":30,"time_in_force":"GoodTillCancel","order_status":"Created","ext_fields":{"cross_status":"PendingNew","xreq_type":"x_create","xreq_offset":148672558},"leaves_qty":30,"leaves_value":"0.00428571","reject_reason":"","cross_seq":-1,"created_at":"2019-07-23T08:54:54.000Z","updated_at":"2019-07-23T08:54:54.000Z","last_exec_time":"0.000000","last_exec_price":0,"order_id":"603c41e0-c9fb-450c-90b6-ea870d5b0180"},"ext_info":null,"time_now":"1563872094.895918","rate_limit_status":98} + _, _, order, err := b.CreateStopOrder(side, + orderType, price, basePrice, stopPx, qty, triggerBy, timeInForce, true, symbol) + if err != nil { + t.Error(err) + return + } + t.Logf("%#v", order) +} + +func TestByBit_CancelStopOrder(t *testing.T) { + b := newByBit() + orderID := "c6e535a9-6900-4b64-b983-3b220f6f41f8" + symbol := "BTCUSD" + _, _, order, err := b.CancelStopOrder(orderID, symbol) + assert.Nil(t, err) + t.Logf("%#v", order) +} + +func TestByBit_CancelAllStopOrders(t *testing.T) { + b := newByBit() + symbol := "BTCUSD" + _, _, orders, err := b.CancelAllStopOrders(symbol) + assert.Nil(t, err) + t.Logf("%#v", orders) +} diff --git a/rest/api_test.go b/rest/api_test.go index 5102305..b7c0f82 100644 --- a/rest/api_test.go +++ b/rest/api_test.go @@ -46,56 +46,6 @@ func TestByBit_SetCorrectServerTime(t *testing.T) { } } -func TestByBit_GetOrderBook(t *testing.T) { - b := newByBit() - _, _, ob, err := b.GetOrderBook("BTCUSD") - if err != nil { - t.Error(err) - return - } - for _, v := range ob.Asks { - t.Logf("Ask: %#v", v) - } - for _, v := range ob.Bids { - t.Logf("Bid: %#v", v) - } - t.Logf("%v", ob.Time) -} - -func TestByBit_GetOrderBook2(t *testing.T) { - b := newByBit() - _, _, ob, err := b.GetOrderBook("BTCUSDT") - if err != nil { - t.Error(err) - return - } - for _, v := range ob.Asks { - t.Logf("Ask: %#v", v) - } - for _, v := range ob.Bids { - t.Logf("Bid: %#v", v) - } - t.Logf("%v", ob.Time) -} - -func TestByBit_GetKLine(t *testing.T) { - b := newByBit() - from := time.Now().Add(-1 * time.Hour).Unix() - _, _, ohlcs, err := b.GetKLine( - "BTCUSD", - "1", - from, - 0, - ) - if err != nil { - t.Error(err) - return - } - for _, v := range ohlcs { - t.Logf("%#v", v) - } -} - func TestByBit_GetTickers(t *testing.T) { b := newByBit() _, _, tickers, err := b.GetTickers() @@ -142,109 +92,6 @@ func TestByBit_GetWalletBalance(t *testing.T) { t.Logf("%#v", balance) } -func TestByBit_GetOrders(t *testing.T) { - b := newByBit() - symbol := "BTCUSD" - _, _, orders, err := b.GetOrders(symbol, "", "next", 20, "") - assert.Nil(t, err) - //t.Logf("%#v", orders) - for _, order := range orders.Data { - t.Logf("%#v", order) - } -} - -func TestByBit_CreateOrder(t *testing.T) { - b := newByBit() - symbol := "BTCUSD" - side := "Buy" // Buy Sell - orderType := "Limit" - qty := 30 - price := 5000.0 - timeInForce := "GoodTillCancel" - // {"ret_code":0,"ret_msg":"ok","ext_code":"","result":{"user_id":103061,"symbol":"BTCUSD","side":"Buy","order_type":"Limit","price":"7000","qty":30,"time_in_force":"GoodTillCancel","order_status":"Created","ext_fields":{"cross_status":"PendingNew","xreq_type":"x_create","xreq_offset":148672558},"leaves_qty":30,"leaves_value":"0.00428571","reject_reason":"","cross_seq":-1,"created_at":"2019-07-23T08:54:54.000Z","updated_at":"2019-07-23T08:54:54.000Z","last_exec_time":"0.000000","last_exec_price":0,"order_id":"603c41e0-c9fb-450c-90b6-ea870d5b0180"},"ext_info":null,"time_now":"1563872094.895918","rate_limit_status":98} - _, _, order, err := b.CreateOrder( - side, - orderType, - price, - qty, - timeInForce, - 0, - 0, - false, - false, - "", - symbol) - if err != nil { - t.Error(err) - return - } - t.Logf("%#v", order) -} - -func TestByBit_CancelOrder(t *testing.T) { - b := newByBit() - orderID := "c5b96b82-6a79-4b15-a797-361fe2ca0260" - symbol := "BTCUSD" - _, _, order, err := b.CancelOrder(orderID, symbol) - assert.Nil(t, err) - t.Logf("%#v", order) -} - -func TestByBit_GetStopOrders(t *testing.T) { - b := newByBit() - symbol := "BTCUSD" - // Untriggered: 等待市价触发条件单; Triggered: 市价已触发条件单; Cancelled: 取消; Active: 条件单触发成功且下单成功; Rejected: 条件触发成功但下单失败 - status := "Untriggered,Triggered,Active" - _, _, result, err := b.GetStopOrders(symbol, status, "next", 20, "") - assert.Nil(t, err) - //t.Logf("%#v", orders) - for _, order := range result.Data { - //if order.ExtFields != nil { - // t.Logf("%#v %v", order, *order.ExtFields) - //} else { - t.Logf("CreatedAt: %v %#v", order.CreatedAt.Local(), order) - //} - } -} - -func TestByBit_CreateStopOrder(t *testing.T) { - b := newByBit() - symbol := "BTCUSD" - side := "Buy" // Buy Sell - orderType := "Limit" - qty := 30 - price := 10000.0 - basePrice := 7100.0 // 当前市场价 - stopPx := 10000.0 // 触发价 - triggerBy := "" - timeInForce := "GoodTillCancel" - // {"ret_code":0,"ret_msg":"ok","ext_code":"","result":{"user_id":103061,"symbol":"BTCUSD","side":"Buy","order_type":"Limit","price":"7000","qty":30,"time_in_force":"GoodTillCancel","order_status":"Created","ext_fields":{"cross_status":"PendingNew","xreq_type":"x_create","xreq_offset":148672558},"leaves_qty":30,"leaves_value":"0.00428571","reject_reason":"","cross_seq":-1,"created_at":"2019-07-23T08:54:54.000Z","updated_at":"2019-07-23T08:54:54.000Z","last_exec_time":"0.000000","last_exec_price":0,"order_id":"603c41e0-c9fb-450c-90b6-ea870d5b0180"},"ext_info":null,"time_now":"1563872094.895918","rate_limit_status":98} - _, _, order, err := b.CreateStopOrder(side, - orderType, price, basePrice, stopPx, qty, triggerBy, timeInForce, true, symbol) - if err != nil { - t.Error(err) - return - } - t.Logf("%#v", order) -} - -func TestByBit_CancelStopOrder(t *testing.T) { - b := newByBit() - orderID := "c6e535a9-6900-4b64-b983-3b220f6f41f8" - symbol := "BTCUSD" - _, _, order, err := b.CancelStopOrder(orderID, symbol) - assert.Nil(t, err) - t.Logf("%#v", order) -} - -func TestByBit_CancelAllStopOrders(t *testing.T) { - b := newByBit() - symbol := "BTCUSD" - _, _, orders, err := b.CancelAllStopOrders(symbol) - assert.Nil(t, err) - t.Logf("%#v", orders) -} - func TestByBit_SetLeverage(t *testing.T) { b := newByBit() _, _, _ = b.SetLeverage(3, "BTCUSD") From 0fd1ca277a0d108a61858e628dc264ae6ea6255f Mon Sep 17 00:00:00 2001 From: atik-lab Date: Thu, 7 Jan 2021 11:20:48 +0200 Subject: [PATCH 22/34] Integrate bybit linear in the rest library. Bybit add indexes and premium index. --- rest/api.go | 159 +----------------------- rest/api2.go | 68 +++++++++++ rest/api_all.go | 196 ++++++++++++++++++++++++++++++ rest/api_inverse.go | 18 --- rest/api_linear.go | 288 +++++++++++++++++++++++++++++++++++++++++++- rest/base.go | 5 - rest/result.go | 104 +++++++++++----- 7 files changed, 627 insertions(+), 211 deletions(-) create mode 100644 rest/api_all.go delete mode 100644 rest/base.go diff --git a/rest/api.go b/rest/api.go index 0124ca0..eccdbc2 100644 --- a/rest/api.go +++ b/rest/api.go @@ -6,15 +6,17 @@ import ( "crypto/sha256" "encoding/hex" "fmt" + "github.com/json-iterator/go" "io/ioutil" "log" "net/http" "sort" - "strconv" "strings" "time" ) +var json = jsoniter.ConfigCompatibleWithStandardLibrary + // Bybit type ByBit struct { baseURL string // https://api-testnet.bybit.com/open-api/ @@ -163,158 +165,3 @@ func (b *ByBit) getSigned(param string) string { signature := hex.EncodeToString(sig.Sum(nil)) return signature } - -// GetServerTime -func (b *ByBit) GetServerTime() (query string, resp []byte, timeNow int64, err error) { - params := map[string]interface{}{} - var ret BaseResult - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/time", params, &ret) - if err != nil { - return - } - var t float64 - t, err = strconv.ParseFloat(ret.TimeNow, 64) - if err != nil { - return - } - timeNow = int64(t * 1000) - return -} - -// GetWalletBalance -func (b *ByBit) GetWalletBalance(coin string) (query string, resp []byte, result Balance, err error) { - var ret GetBalanceResult - params := map[string]interface{}{} - params["coin"] = coin - query, _, err = b.SignedRequest(http.MethodGet, "v2/private/wallet/balance", params, &ret) - if err != nil { - return - } - switch coin { - case "BTC": - result = ret.Result.BTC - case "ETH": - result = ret.Result.ETH - case "EOS": - result = ret.Result.EOS - case "XRP": - result = ret.Result.XRP - case "USDT": - result = ret.Result.USDT - } - return -} - -// GetPositions -func (b *ByBit) GetPositions() (query string, resp []byte, result []PositionData, err error) { - var r PositionArrayResponse - params := map[string]interface{}{} - query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/position/list", params, &r) - if err != nil { - return - } - if r.RetCode != 0 { - err = fmt.Errorf("%v body: [%v]", r.RetMsg, string(resp)) - return - } - result = r.Result - return -} - -// GetPosition -func (b *ByBit) GetPosition(symbol string) (query string, resp []byte, result Position, err error) { - var r PositionResponse - params := map[string]interface{}{} - params["symbol"] = symbol - query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/position/list", params, &r) - if err != nil { - return - } - if r.RetCode != 0 { - err = fmt.Errorf("%v body: [%v]", r.RetMsg, string(resp)) - return - } - result = r.Result - return -} - -// SetLeverage -func (b *ByBit) SetLeverage(leverage int, symbol string) (query string, resp []byte, err error) { - var r BaseResult - params := map[string]interface{}{} - params["symbol"] = symbol - params["leverage"] = fmt.Sprintf("%v", leverage) - query, _, err = b.SignedRequest(http.MethodPost, "user/leverage/save", params, &r) - if err != nil { - return - } - return -} - -// WalletRecords -func (b *ByBit) WalletRecords(symbol string, page int, limit int) (query string, resp []byte, result []WalletFundRecord, err error) { - var r WalletFundRecordResponse - params := map[string]interface{}{} - if symbol != "" { - params["currency"] = symbol - } - if page > 0 { - params["page"] = page - } - if limit > 0 { - params["limit"] = limit - } - query, resp, err = b.SignedRequest(http.MethodGet, "open-api/wallet/fund/records", params, &r) - if err != nil { - return - } - if r.RetCode != 0 { - err = fmt.Errorf("%v body: [%v]", r.RetMsg, string(resp)) - return - } - result = r.Result.Data - return -} - -// GetTickers -func (b *ByBit) GetTickers() (query string, resp []byte, result []Ticker, err error) { - var ret GetTickersResult - params := map[string]interface{}{} - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/tickers", params, &ret) - if err != nil { - return - } - result = ret.Result - return -} - -// GetTradingRecords -func (b *ByBit) GetTradingRecords(symbol string, from int64, limit int) (query string, resp []byte, result []TradingRecord, err error) { - var ret GetTradingRecordsResult - params := map[string]interface{}{} - params["symbol"] = symbol - if from > 0 { - params["from"] = from - } - if limit > 0 { - params["limit"] = limit - } - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/trading-records", params, &ret) - if err != nil { - return - } - result = ret.Result - return -} - -// GetSymbols -func (b *ByBit) GetSymbols() (query string, resp []byte, result []SymbolInfo, err error) { - var ret GetSymbolsResult - params := map[string]interface{}{} - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/symbols", params, &ret) - if err != nil { - return - } - result = ret.Result - return -} diff --git a/rest/api2.go b/rest/api2.go index 4f46ffd..eb94dbc 100644 --- a/rest/api2.go +++ b/rest/api2.go @@ -56,6 +56,40 @@ func (b *ByBit) GetFunding(symbol string, page int, limit int) (query string, re return } +// GetPriceIndex +// https://api2.bybit.com/api/price/index?symbol=BTCUSD&resolution=1&from=1605087277&to=1605173738 +func (b *ByBit) GetPriceIndex(symbol string, resolution int, from string, to string) (query string, resp []byte, result []FundingData, e error) { + var ret FundingResult + params := map[string]interface{}{} + params["symbol"] = symbol + params["resolution"] = resolution + params["from"] = from + params["to"] = to + query, _, e = b.PublicRequest(http.MethodGet, "api/price/index", params, &ret) + if e != nil { + return + } + result = ret.Result.Data + return +} + +// GetPremiumIndex +// https://api2.bybit.com/api/premium-index-price/index?symbol=BTCUSD&from=1605087277&resolution=1&to=1605173738 +func (b *ByBit) GetPremiumIndex(symbol string, resolution int, from string, to string) (query string, resp []byte, result []FundingData, e error) { + var ret FundingResult + params := map[string]interface{}{} + params["symbol"] = symbol + params["resolution"] = resolution + params["from"] = from + params["to"] = to + query, _, e = b.PublicRequest(http.MethodGet, "api/premium-index-price/index", params, &ret) + if e != nil { + return + } + result = ret.Result.Data + return +} + // LinearGetFunding // https://api2.bybit.com/linear/funding-rate/list?symbol=BTCUSDT&date=&export=false&page=1 func (b *ByBit) LinearGetFunding(symbol string, page int, limit int) (query string, resp []byte, result []FundingData, e error) { @@ -72,3 +106,37 @@ func (b *ByBit) LinearGetFunding(symbol string, page int, limit int) (query stri result = ret.Result.Data return } + +// LinearGetPriceIndex +// https://api2.bybit.com/api/linear/public/kline/price?symbol=BTCUSDT&from=1607360460&to=1610006520&resolution=30 +func (b *ByBit) LinearGetPriceIndex(symbol string, resolution int, from string, to string) (query string, resp []byte, result []FundingData, e error) { + var ret FundingResult + params := map[string]interface{}{} + params["symbol"] = symbol + params["resolution"] = resolution + params["from"] = from + params["to"] = to + query, _, e = b.PublicRequest(http.MethodGet, "api/linear/public/kline/price", params, &ret) + if e != nil { + return + } + result = ret.Result.Data + return +} + +// LinearGetPremiumIndex +// https://api2.bybit.com/api/linear/public/kline/premium-price?symbol=BTCUSDT&from=1607364960&to=1610011020&resolution=30 +func (b *ByBit) LinearGetPremiumIndex(symbol string, resolution int, from string, to string) (query string, resp []byte, result []FundingData, e error) { + var ret FundingResult + params := map[string]interface{}{} + params["symbol"] = symbol + params["resolution"] = resolution + params["from"] = from + params["to"] = to + query, _, e = b.PublicRequest(http.MethodGet, "api/linear/public/kline/premium-price", params, &ret) + if e != nil { + return + } + result = ret.Result.Data + return +} diff --git a/rest/api_all.go b/rest/api_all.go new file mode 100644 index 0000000..89b6d80 --- /dev/null +++ b/rest/api_all.go @@ -0,0 +1,196 @@ +package rest + +import ( + "fmt" + "net/http" + "strconv" +) + +// GetServerTime +func (b *ByBit) GetServerTime() (query string, resp []byte, timeNow int64, err error) { + params := map[string]interface{}{} + var ret BaseResult + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/time", params, &ret) + if err != nil { + return + } + var t float64 + t, err = strconv.ParseFloat(ret.TimeNow, 64) + if err != nil { + return + } + timeNow = int64(t * 1000) + return +} + +// GetWalletBalance +func (b *ByBit) GetWalletBalance(coin string) (query string, resp []byte, result Balance, err error) { + var ret GetBalanceResult + params := map[string]interface{}{} + params["coin"] = coin + query, _, err = b.SignedRequest(http.MethodGet, "v2/private/wallet/balance", params, &ret) + if err != nil { + return + } + switch coin { + case "BTC": + result = ret.Result.BTC + case "ETH": + result = ret.Result.ETH + case "EOS": + result = ret.Result.EOS + case "XRP": + result = ret.Result.XRP + case "USDT": + result = ret.Result.USDT + } + return +} + +// GetPositions +func (b *ByBit) GetPositions() (query string, resp []byte, result []PositionData, err error) { + var r PositionArrayResponse + params := map[string]interface{}{} + query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/position/list", params, &r) + if err != nil { + return + } + if r.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", r.RetMsg, string(resp)) + return + } + result = r.Result + return +} + +// GetPosition +func (b *ByBit) GetPosition(symbol string) (query string, resp []byte, result Position, err error) { + var r PositionResponse + params := map[string]interface{}{} + params["symbol"] = symbol + query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/position/list", params, &r) + if err != nil { + return + } + if r.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", r.RetMsg, string(resp)) + return + } + result = r.Result + return +} + +// SetLeverage +func (b *ByBit) SetLeverage(leverage int, symbol string) (query string, resp []byte, err error) { + var r BaseResult + params := map[string]interface{}{} + params["symbol"] = symbol + params["leverage"] = fmt.Sprintf("%v", leverage) + query, _, err = b.SignedRequest(http.MethodPost, "user/leverage/save", params, &r) + if err != nil { + return + } + return +} + +// WalletRecords +func (b *ByBit) WalletRecords(symbol string, page int, limit int) (query string, resp []byte, result []WalletFundRecord, err error) { + var r WalletFundRecordResponse + params := map[string]interface{}{} + if symbol != "" { + params["currency"] = symbol + } + if page > 0 { + params["page"] = page + } + if limit > 0 { + params["limit"] = limit + } + query, resp, err = b.SignedRequest(http.MethodGet, "open-api/wallet/fund/records", params, &r) + if err != nil { + return + } + if r.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", r.RetMsg, string(resp)) + return + } + result = r.Result.Data + return +} + +// GetTickers +func (b *ByBit) GetTickers() (query string, resp []byte, result []Ticker, err error) { + var ret GetTickersResult + params := map[string]interface{}{} + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/tickers", params, &ret) + if err != nil { + return + } + result = ret.Result + return +} + +// GetTradingRecords +func (b *ByBit) GetTradingRecords(symbol string, from int64, limit int) (query string, resp []byte, result []TradingRecord, err error) { + var ret GetTradingRecordsResult + params := map[string]interface{}{} + params["symbol"] = symbol + if from > 0 { + params["from"] = from + } + if limit > 0 { + params["limit"] = limit + } + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/trading-records", params, &ret) + if err != nil { + return + } + result = ret.Result + return +} + +// GetSymbols +func (b *ByBit) GetSymbols() (query string, resp []byte, result []SymbolInfo, err error) { + var ret GetSymbolsResult + params := map[string]interface{}{} + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/symbols", params, &ret) + if err != nil { + return + } + result = ret.Result + return +} + +// GetOpenInterest, limit max 200 +func (b *ByBit) GetOpenInterest(symbol string, period string, limit int) (query string, resp []byte, result []OpenInterest, err error) { + var ret GetOpenInterestResult + params := map[string]interface{}{} + params["symbol"] = symbol + params["period"] = period + if limit > 0 { + params["limit"] = limit + } + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/open-interest", params, &ret) + if err != nil { + return + } + result = ret.Result + return +} + +// GetAccountRatio, limit max 200 +func (b *ByBit) GetAccountRatio(symbol string, period string, limit int) (query string, resp []byte, result []AccountRatio, err error) { + var ret GetAccountRatioResult + params := map[string]interface{}{} + params["symbol"] = symbol + params["period"] = period + if limit > 0 { + params["limit"] = limit + } + query, _, err = b.PublicRequest(http.MethodGet, "v2/public/account-ratio", params, &ret) + if err != nil { + return + } + result = ret.Result + return +} diff --git a/rest/api_inverse.go b/rest/api_inverse.go index a790c22..d76e1c9 100644 --- a/rest/api_inverse.go +++ b/rest/api_inverse.go @@ -17,7 +17,6 @@ func (b *ByBit) GetOrderBook(symbol string) (query string, resp []byte, result O if err != nil { return } - for _, v := range ret.Result { if v.Side == "Sell" { result.Asks = append(result.Asks, Item{ @@ -31,15 +30,12 @@ func (b *ByBit) GetOrderBook(symbol string) (query string, resp []byte, result O }) } } - sort.Slice(result.Asks, func(i, j int) bool { return result.Asks[i].Price < result.Asks[j].Price }) - sort.Slice(result.Bids, func(i, j int) bool { return result.Bids[i].Price > result.Bids[j].Price }) - var timeNow float64 timeNow, err = strconv.ParseFloat(ret.TimeNow, 64) // 1582011750.433202 if err != nil { @@ -70,11 +66,9 @@ func (b *ByBit) GetKLine(symbol string, interval string, from int64, limit int) // GetOrders func (b *ByBit) GetOrders(symbol string, orderStatus string, direction string, limit int, cursor string) (query string, resp []byte, result OrderListResponseResult, err error) { var cResult OrderListResponse - if limit == 0 { limit = 20 } - params := map[string]interface{}{} params["symbol"] = symbol if orderStatus != "" { @@ -95,7 +89,6 @@ func (b *ByBit) GetOrders(symbol string, orderStatus string, direction string, l err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) return } - result = cResult.Result return } @@ -103,7 +96,6 @@ func (b *ByBit) GetOrders(symbol string, orderStatus string, direction string, l // GetActiveOrders func (b *ByBit) GetActiveOrders(symbol string) (query string, resp []byte, result OrderArrayResponse, err error) { var cResult OrderArrayResponse - params := map[string]interface{}{} params["symbol"] = symbol query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/order", params, &cResult) @@ -114,7 +106,6 @@ func (b *ByBit) GetActiveOrders(symbol string) (query string, resp []byte, resul err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) return } - result = cResult return } @@ -200,7 +191,6 @@ func (b *ByBit) CancelOrder(orderID string, symbol string) (query string, resp [ err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) return } - result = cResult.Result return } @@ -218,7 +208,6 @@ func (b *ByBit) CancelAllOrder(symbol string) (query string, resp []byte, result err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) return } - result = cResult.Result return } @@ -226,11 +215,9 @@ func (b *ByBit) CancelAllOrder(symbol string) (query string, resp []byte, result // GetStopOrders func (b *ByBit) GetStopOrders(symbol string, stopOrderStatus string, direction string, limit int, cursor string) (query string, resp []byte, result StopOrderListResponseResult, err error) { var cResult StopOrderListResponse - if limit == 0 { limit = 20 } - params := map[string]interface{}{} params["symbol"] = symbol if stopOrderStatus != "" { @@ -251,7 +238,6 @@ func (b *ByBit) GetStopOrders(symbol string, stopOrderStatus string, direction s err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) return } - result = cResult.Result return } @@ -259,7 +245,6 @@ func (b *ByBit) GetStopOrders(symbol string, stopOrderStatus string, direction s // GetActiveStopOrders func (b *ByBit) GetActiveStopOrders(symbol string) (query string, resp []byte, result StopOrderArrayResponse, err error) { var cResult StopOrderArrayResponse - params := map[string]interface{}{} params["symbol"] = symbol query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/stop-order", params, &cResult) @@ -270,7 +255,6 @@ func (b *ByBit) GetActiveStopOrders(symbol string) (query string, resp []byte, r err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) return } - result = cResult return } @@ -349,7 +333,6 @@ func (b *ByBit) CancelStopOrder(orderID string, symbol string) (query string, re err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) return } - result.StopOrderId = cResult.Result.StopOrderId return } @@ -367,7 +350,6 @@ func (b *ByBit) CancelAllStopOrders(symbol string) (query string, resp []byte, r err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) return } - result = cResult.Result return } diff --git a/rest/api_linear.go b/rest/api_linear.go index 6ad2d33..a0665d5 100644 --- a/rest/api_linear.go +++ b/rest/api_linear.go @@ -1,6 +1,9 @@ package rest -import "net/http" +import ( + "fmt" + "net/http" +) // LinearGetKLine func (b *ByBit) LinearGetKLine(symbol string, interval string, from int64, limit int) (query string, resp []byte, result []OHLC, err error) { @@ -19,3 +22,286 @@ func (b *ByBit) LinearGetKLine(symbol string, interval string, from int64, limit result = ret.Result return } + +// GetOrders +func (b *ByBit) LinearGetOrders(symbol string, orderStatus string, limit int, page int) (query string, resp []byte, result OrderListResponsePaginated, err error) { + var cResult OrderListResponsePaginated + if limit == 0 { + limit = 20 + } + params := map[string]interface{}{} + params["symbol"] = symbol + params["page"] = page + params["limit"] = limit + if orderStatus != "" { + params["order_status"] = orderStatus + } + query, resp, err = b.SignedRequest(http.MethodGet, "private/linear/order/list", params, &cResult) + if err != nil { + return + } + if cResult.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) + return + } + result = cResult + return +} + +// GetActiveOrders +func (b *ByBit) LinearGetActiveOrders(symbol string) (query string, resp []byte, result OrderArrayResponse, err error) { + var cResult OrderArrayResponse + params := map[string]interface{}{} + params["symbol"] = symbol + query, resp, err = b.SignedRequest(http.MethodGet, "private/linear/order/search", params, &cResult) + if err != nil { + return + } + if cResult.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) + return + } + result = cResult + return +} + +// CreateOrder +func (b *ByBit) LinearCreateOrder(side string, orderType string, price float64, + qty int, timeInForce string, takeProfit float64, stopLoss float64, reduceOnly bool, + closeOnTrigger bool, orderLinkID string, symbol string) (query string, resp []byte, result Order, err error) { + var cResult OrderResponse + params := map[string]interface{}{} + params["side"] = side + params["symbol"] = symbol + params["order_type"] = orderType + params["qty"] = qty + if price > 0 { + params["price"] = price + } + params["time_in_force"] = timeInForce + if takeProfit > 0 { + params["take_profit"] = takeProfit + } + if stopLoss > 0 { + params["stop_loss"] = stopLoss + } + if reduceOnly { + params["reduce_only"] = true + } + if closeOnTrigger { + params["close_on_trigger"] = true + } + if orderLinkID != "" { + params["order_link_id"] = orderLinkID + } + query, resp, err = b.SignedRequest(http.MethodPost, "private/linear/order/create", params, &cResult) + if err != nil { + return + } + if cResult.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) + return + } + result = cResult.Result + return +} + +// ReplaceOrder +func (b *ByBit) LinearReplaceOrder(symbol string, orderID string, qty int, price float64) (query string, resp []byte, result Order, err error) { + var cResult OrderResponse + params := map[string]interface{}{} + params["order_id"] = orderID + params["symbol"] = symbol + if qty > 0 { + params["p_r_qty"] = qty + } + if price > 0 { + params["p_r_price"] = price + } + query, resp, err = b.SignedRequest(http.MethodPost, "private/linear/order/replace", params, &cResult) + if err != nil { + return + } + if cResult.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) + return + } + result.OrderId = cResult.Result.OrderId + return +} + +// CancelOrder +func (b *ByBit) LinearCancelOrder(orderID string, symbol string) (query string, resp []byte, result Order, err error) { + var cResult OrderResponse + params := map[string]interface{}{} + params["symbol"] = symbol + if orderID != "" { + params["order_id"] = orderID + } + query, resp, err = b.SignedRequest(http.MethodPost, "private/linear/order/cancel", params, &cResult) + if err != nil { + return + } + if cResult.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) + return + } + + result = cResult.Result + return +} + +// CancelAllOrder +func (b *ByBit) LinearCancelAllOrder(symbol string) (query string, resp []byte, result []string, err error) { + var cResult ResultStringArrayResponse + params := map[string]interface{}{} + params["symbol"] = symbol + query, resp, err = b.SignedRequest(http.MethodPost, "private/linear/order/cancel-all", params, &cResult) + if err != nil { + return + } + if cResult.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) + return + } + + result = cResult.Result + return +} + +// GetStopOrders +func (b *ByBit) LinearGetStopOrders(symbol string, stopOrderStatus string, limit int, page string) (query string, resp []byte, result StopOrderListResponseResult, err error) { + var cResult StopOrderListResponse + if limit == 0 { + limit = 20 + } + params := map[string]interface{}{} + params["symbol"] = symbol + if stopOrderStatus != "" { + params["stop_order_status"] = stopOrderStatus + } + params["page"] = page + params["limit"] = limit + query, resp, err = b.SignedRequest(http.MethodGet, "private/linear/stop-order/list", params, &cResult) + if err != nil { + return + } + if cResult.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) + return + } + result = cResult.Result + return +} + +// GetActiveStopOrders +func (b *ByBit) LinearGetActiveStopOrders(symbol string) (query string, resp []byte, result StopOrderArrayResponse, err error) { + var cResult StopOrderArrayResponse + params := map[string]interface{}{} + params["symbol"] = symbol + query, resp, err = b.SignedRequest(http.MethodGet, "private/linear/order/search", params, &cResult) + if err != nil { + return + } + if cResult.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) + return + } + result = cResult + return +} + +// CreateStopOrder +func (b *ByBit) LinearCreateStopOrder(side string, orderType string, price float64, basePrice float64, stopPx float64, + qty int, triggerBy string, timeInForce string, closeOnTrigger bool, symbol string) (query string, resp []byte, result StopOrder, err error) { + var cResult StopOrderResponse + params := map[string]interface{}{} + params["side"] = side + params["symbol"] = symbol + params["order_type"] = orderType + params["qty"] = qty + if price > 0 { + params["price"] = price + } + params["base_price"] = basePrice + params["stop_px"] = stopPx + params["time_in_force"] = timeInForce + if closeOnTrigger { + params["close_on_trigger"] = true + } + if triggerBy != "" { + params["trigger_by"] = triggerBy + } + query, resp, err = b.SignedRequest(http.MethodPost, "private/linear/stop-order/create", params, &cResult) + if err != nil { + return + } + if cResult.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) + return + } + result = cResult.Result + return +} + +// ReplaceStopOrder +func (b *ByBit) LinearReplaceStopOrder(symbol string, orderID string, qty int, price float64, triggerPrice float64) (query string, resp []byte, result StopOrder, err error) { + var cResult StopOrderResponse + params := map[string]interface{}{} + params["stop_order_id"] = orderID + params["symbol"] = symbol + if qty > 0 { + params["p_r_qty"] = qty + } + if price > 0 { + params["p_r_price"] = price + } + if triggerPrice > 0 { + params["p_r_trigger_price"] = triggerPrice + } + query, resp, err = b.SignedRequest(http.MethodPost, "private/linear/stop-order/replace", params, &cResult) + if err != nil { + return + } + if cResult.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) + return + } + result.StopOrderId = cResult.Result.StopOrderId + return +} + +// CancelStopOrder +func (b *ByBit) LinearCancelStopOrder(orderID string, symbol string) (query string, resp []byte, result StopOrder, err error) { + var cResult StopOrderResponse + params := map[string]interface{}{} + params["symbol"] = symbol + params["stop_order_id"] = orderID + query, resp, err = b.SignedRequest(http.MethodPost, "private/linear/stop-order/cancel", params, &cResult) + if err != nil { + return + } + if cResult.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) + return + } + result.StopOrderId = cResult.Result.StopOrderId + return +} + +// CancelAllStopOrders +func (b *ByBit) LinearCancelAllStopOrders(symbol string) (query string, resp []byte, result []string, err error) { + var cResult ResultStringArrayResponse + params := map[string]interface{}{} + params["symbol"] = symbol + query, resp, err = b.SignedRequest(http.MethodPost, "private/linear/stop-order/cancel-all", params, &cResult) + if err != nil { + return + } + if cResult.RetCode != 0 { + err = fmt.Errorf("%v body: [%v]", cResult.RetMsg, string(resp)) + return + } + result = cResult.Result + return +} diff --git a/rest/base.go b/rest/base.go deleted file mode 100644 index 9d78a9e..0000000 --- a/rest/base.go +++ /dev/null @@ -1,5 +0,0 @@ -package rest - -import "github.com/json-iterator/go" - -var json = jsoniter.ConfigCompatibleWithStandardLibrary diff --git a/rest/result.go b/rest/result.go index b1d4a98..0f84ff9 100644 --- a/rest/result.go +++ b/rest/result.go @@ -17,6 +17,11 @@ type BaseResult struct { RateLimit int `json:"rate_limit"` } +type ResultStringArrayResponse struct { + BaseResult + Result []string `json:"result"` +} + type Item struct { Price float64 `json:"price,string"` Size float64 `json:"size"` @@ -36,12 +41,8 @@ type RawItem struct { } type GetOrderBookResult struct { - RetCode int `json:"ret_code"` - RetMsg string `json:"ret_msg"` - ExtCode string `json:"ext_code"` - ExtInfo string `json:"ext_info"` - Result []RawItem `json:"result"` - TimeNow string `json:"time_now"` + BaseResult + Result []RawItem `json:"result"` } type OHLC struct { @@ -57,12 +58,31 @@ type OHLC struct { } type GetKlineResult struct { - RetCode int `json:"ret_code"` - RetMsg string `json:"ret_msg"` - ExtCode string `json:"ext_code"` - ExtInfo string `json:"ext_info"` - Result []OHLC `json:"result"` - TimeNow string `json:"time_now"` + BaseResult + Result []OHLC `json:"result"` +} + +type OpenInterest struct { + Symbol string `json:"symbol"` + OpenInterest sjson.Number `json:"open_interest"` + Timestamp sjson.Number `json:"timestamp"` +} + +type GetOpenInterestResult struct { + BaseResult + Result []OpenInterest `json:"result"` +} + +type AccountRatio struct { + Symbol string `json:"symbol"` + BuyRatio sjson.Number `json:"buy_ratio"` + SellRatio sjson.Number `json:"sell_ratio"` + Timestamp sjson.Number `json:"timestamp"` +} + +type GetAccountRatioResult struct { + BaseResult + Result []AccountRatio `json:"result"` } type Ticker struct { @@ -193,21 +213,6 @@ type GetBalanceResultData struct { USDT Balance `json:"USDT"` } -type PositionResponse struct { - BaseResult - Result Position `json:"result"` -} - -type PositionArrayResponse struct { - BaseResult - Result []PositionData `json:"result"` -} - -type PositionData struct { - IsValid bool `json:"is_valid"` - Data Position `json:"data"` -} - type Position struct { Id int `json:"id"` UserId int `json:"user_id"` @@ -242,6 +247,21 @@ type Position struct { UnrealisedPnl float64 `json:"unrealised_pnl"` } +type PositionResponse struct { + BaseResult + Result Position `json:"result"` +} + +type PositionArrayResponse struct { + BaseResult + Result []PositionData `json:"result"` +} + +type PositionData struct { + IsValid bool `json:"is_valid"` + Data Position `json:"data"` +} + type Order struct { UserId int `json:"user_id"` OrderId string `json:"order_id"` @@ -274,6 +294,17 @@ type OrderListResponseResult struct { Cursor string `json:"cursor"` } +type OrderListResponsePaginated struct { + BaseResult + Result OrderListResponseResultPaginated `json:"result"` +} + +type OrderListResponseResultPaginated struct { + CurrentPage string `json:"current_page"` + LastPage string `json:"last_page"` + Data []Order `json:"data"` +} + type OrderResponse struct { BaseResult Result Order `json:"result"` @@ -331,13 +362,15 @@ type StopOrderArrayResponse struct { Result []StopOrder `json:"result"` } -type WalletFundRecordResponse struct { +type StopOrderListResponsePaginated struct { BaseResult - Result OrderListResponseArray `json:"result"` + Result StopOrderListResponseResultPaginated `json:"result"` } -type OrderListResponseArray struct { - Data []WalletFundRecord `json:"data"` +type StopOrderListResponseResultPaginated struct { + CurrentPage string `json:"current_page"` + LastPage string `json:"last_page"` + Data []StopOrder `json:"data"` } type WalletFundRecord struct { @@ -353,3 +386,12 @@ type WalletFundRecord struct { ExecTime sjson.Number `json:"exec_time"` CrossSeq sjson.Number `json:"cross_seq"` } + +type WalletFundRecordResponse struct { + BaseResult + Result OrderListResponseArray `json:"result"` +} + +type OrderListResponseArray struct { + Data []WalletFundRecord `json:"data"` +} From d381c41e8dfc0ba36d77b41961819c4e2dde2c18 Mon Sep 17 00:00:00 2001 From: atik-lab Date: Thu, 7 Jan 2021 12:30:32 +0200 Subject: [PATCH 23/34] Integrate bybit linear in the rest library. Bybit add indexes and premium index. --- rest/api2.go | 57 +++++++++++--------------------------------------- rest/result.go | 43 +++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 45 deletions(-) diff --git a/rest/api2.go b/rest/api2.go index eb94dbc..995f98a 100644 --- a/rest/api2.go +++ b/rest/api2.go @@ -1,42 +1,9 @@ package rest import ( - sjson "encoding/json" "net/http" - "time" ) -type FundingResult struct { - RetCode int `json:"ret_code"` - RetMsg string `json:"ret_msg"` - ExtCode string `json:"ext_code"` - ExtInfo string `json:"ext_info"` - Result Funding `json:"result"` - TimeNow string `json:"time_now"` -} - -type Funding struct { - CurrentPage int `json:"current_page"` - Data []FundingData `json:"data"` - FirstPageUrl string `json:"first_page_url"` - From int `json:"from"` - LastPage int `json:"last_page"` - LastPageUrl string `json:"last_page_url"` - NextPageUrl string `json:"next_page_url"` - Path string `json:"path"` - PerPage sjson.Number `json:"per_page"` - PrevPageUrl string `json:"prev_page_url"` - To int `json:"to"` - Total int `json:"total"` -} - -type FundingData struct { - Id int `json:"id"` - Symbol string `json:"symbol"` - Value sjson.Number `json:"value"` - Time time.Time `json:"time"` -} - // To use this functions will need to set b.BaseUrl to api2.bybit.com or api2-testnet.bybit.com // GetFunding @@ -58,8 +25,8 @@ func (b *ByBit) GetFunding(symbol string, page int, limit int) (query string, re // GetPriceIndex // https://api2.bybit.com/api/price/index?symbol=BTCUSD&resolution=1&from=1605087277&to=1605173738 -func (b *ByBit) GetPriceIndex(symbol string, resolution int, from string, to string) (query string, resp []byte, result []FundingData, e error) { - var ret FundingResult +func (b *ByBit) GetPriceIndex(symbol string, resolution int, from string, to string) (query string, resp []byte, result []IndexOHLC, e error) { + var ret IndexOHLCResult params := map[string]interface{}{} params["symbol"] = symbol params["resolution"] = resolution @@ -69,14 +36,14 @@ func (b *ByBit) GetPriceIndex(symbol string, resolution int, from string, to str if e != nil { return } - result = ret.Result.Data + result = ret.Result return } // GetPremiumIndex // https://api2.bybit.com/api/premium-index-price/index?symbol=BTCUSD&from=1605087277&resolution=1&to=1605173738 -func (b *ByBit) GetPremiumIndex(symbol string, resolution int, from string, to string) (query string, resp []byte, result []FundingData, e error) { - var ret FundingResult +func (b *ByBit) GetPremiumIndex(symbol string, resolution int, from string, to string) (query string, resp []byte, result []IndexOHLC, e error) { + var ret IndexOHLCResult params := map[string]interface{}{} params["symbol"] = symbol params["resolution"] = resolution @@ -86,7 +53,7 @@ func (b *ByBit) GetPremiumIndex(symbol string, resolution int, from string, to s if e != nil { return } - result = ret.Result.Data + result = ret.Result return } @@ -109,8 +76,8 @@ func (b *ByBit) LinearGetFunding(symbol string, page int, limit int) (query stri // LinearGetPriceIndex // https://api2.bybit.com/api/linear/public/kline/price?symbol=BTCUSDT&from=1607360460&to=1610006520&resolution=30 -func (b *ByBit) LinearGetPriceIndex(symbol string, resolution int, from string, to string) (query string, resp []byte, result []FundingData, e error) { - var ret FundingResult +func (b *ByBit) LinearGetPriceIndex(symbol string, resolution int, from string, to string) (query string, resp []byte, result []IndexOHLC, e error) { + var ret IndexOHLCResult params := map[string]interface{}{} params["symbol"] = symbol params["resolution"] = resolution @@ -120,14 +87,14 @@ func (b *ByBit) LinearGetPriceIndex(symbol string, resolution int, from string, if e != nil { return } - result = ret.Result.Data + result = ret.Result return } // LinearGetPremiumIndex // https://api2.bybit.com/api/linear/public/kline/premium-price?symbol=BTCUSDT&from=1607364960&to=1610011020&resolution=30 -func (b *ByBit) LinearGetPremiumIndex(symbol string, resolution int, from string, to string) (query string, resp []byte, result []FundingData, e error) { - var ret FundingResult +func (b *ByBit) LinearGetPremiumIndex(symbol string, resolution int, from string, to string) (query string, resp []byte, result []IndexOHLC, e error) { + var ret IndexOHLCResult params := map[string]interface{}{} params["symbol"] = symbol params["resolution"] = resolution @@ -137,6 +104,6 @@ func (b *ByBit) LinearGetPremiumIndex(symbol string, resolution int, from string if e != nil { return } - result = ret.Result.Data + result = ret.Result return } diff --git a/rest/result.go b/rest/result.go index 0f84ff9..c83f60b 100644 --- a/rest/result.go +++ b/rest/result.go @@ -395,3 +395,46 @@ type WalletFundRecordResponse struct { type OrderListResponseArray struct { Data []WalletFundRecord `json:"data"` } + +type Funding struct { + CurrentPage int `json:"current_page"` + Data []FundingData `json:"data"` + FirstPageUrl string `json:"first_page_url"` + From int `json:"from"` + LastPage int `json:"last_page"` + LastPageUrl string `json:"last_page_url"` + NextPageUrl string `json:"next_page_url"` + Path string `json:"path"` + PerPage sjson.Number `json:"per_page"` + PrevPageUrl string `json:"prev_page_url"` + To int `json:"to"` + Total int `json:"total"` +} + +type FundingResult struct { + BaseResult + Result Funding `json:"result"` +} + +type FundingData struct { + Id int `json:"id"` + Symbol string `json:"symbol"` + Value sjson.Number `json:"value"` + Time time.Time `json:"time"` +} + +type IndexOHLC struct { + Id int `json:"id"` + Symbol string `json:"symbol"` + Open sjson.Number `json:"open"` + High sjson.Number `json:"high"` + Low sjson.Number `json:"low"` + Close sjson.Number `json:"close"` + StartAt int `json:"start_at"` + Period string `json:"period"` +} + +type IndexOHLCResult struct { + BaseResult + Result []IndexOHLC `json:"result"` +} From a50cfc678d0ece0203c51cd221f9ff5b72b040ca Mon Sep 17 00:00:00 2001 From: atik-lab Date: Thu, 7 Jan 2021 18:25:40 +0200 Subject: [PATCH 24/34] Integrate bybit linear in the rest library. Bybit add indexes and premium index. --- rest/api2.go | 20 ++++++++++---------- rest/api_all.go | 16 ++++++++-------- rest/api_inverse.go | 4 ++-- rest/api_linear.go | 2 +- rest/result.go | 12 ++++++------ 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/rest/api2.go b/rest/api2.go index 995f98a..fc9c319 100644 --- a/rest/api2.go +++ b/rest/api2.go @@ -15,7 +15,7 @@ func (b *ByBit) GetFunding(symbol string, page int, limit int) (query string, re params["page"] = page params["limit"] = limit // fixed limit 20 params["export"] = false // fixed export - query, _, e = b.PublicRequest(http.MethodGet, "funding-rate/list", params, &ret) + query, resp, e = b.PublicRequest(http.MethodGet, "funding-rate/list", params, &ret) if e != nil { return } @@ -25,14 +25,14 @@ func (b *ByBit) GetFunding(symbol string, page int, limit int) (query string, re // GetPriceIndex // https://api2.bybit.com/api/price/index?symbol=BTCUSD&resolution=1&from=1605087277&to=1605173738 -func (b *ByBit) GetPriceIndex(symbol string, resolution int, from string, to string) (query string, resp []byte, result []IndexOHLC, e error) { +func (b *ByBit) GetPriceIndex(symbol string, resolution string, from int64, to int64) (query string, resp []byte, result []IndexOHLC, e error) { var ret IndexOHLCResult params := map[string]interface{}{} params["symbol"] = symbol params["resolution"] = resolution params["from"] = from params["to"] = to - query, _, e = b.PublicRequest(http.MethodGet, "api/price/index", params, &ret) + query, resp, e = b.PublicRequest(http.MethodGet, "api/price/index", params, &ret) if e != nil { return } @@ -42,14 +42,14 @@ func (b *ByBit) GetPriceIndex(symbol string, resolution int, from string, to str // GetPremiumIndex // https://api2.bybit.com/api/premium-index-price/index?symbol=BTCUSD&from=1605087277&resolution=1&to=1605173738 -func (b *ByBit) GetPremiumIndex(symbol string, resolution int, from string, to string) (query string, resp []byte, result []IndexOHLC, e error) { +func (b *ByBit) GetPremiumIndex(symbol string, resolution string, from int64, to int64) (query string, resp []byte, result []IndexOHLC, e error) { var ret IndexOHLCResult params := map[string]interface{}{} params["symbol"] = symbol params["resolution"] = resolution params["from"] = from params["to"] = to - query, _, e = b.PublicRequest(http.MethodGet, "api/premium-index-price/index", params, &ret) + query, resp, e = b.PublicRequest(http.MethodGet, "api/premium-index-price/index", params, &ret) if e != nil { return } @@ -66,7 +66,7 @@ func (b *ByBit) LinearGetFunding(symbol string, page int, limit int) (query stri params["page"] = page params["limit"] = limit // fixed limit 20 params["export"] = false // fixed export - query, _, e = b.PublicRequest(http.MethodGet, "linear/funding-rate/list", params, &ret) + query, resp, e = b.PublicRequest(http.MethodGet, "linear/funding-rate/list", params, &ret) if e != nil { return } @@ -76,14 +76,14 @@ func (b *ByBit) LinearGetFunding(symbol string, page int, limit int) (query stri // LinearGetPriceIndex // https://api2.bybit.com/api/linear/public/kline/price?symbol=BTCUSDT&from=1607360460&to=1610006520&resolution=30 -func (b *ByBit) LinearGetPriceIndex(symbol string, resolution int, from string, to string) (query string, resp []byte, result []IndexOHLC, e error) { +func (b *ByBit) LinearGetPriceIndex(symbol string, resolution string, from int64, to int64) (query string, resp []byte, result []IndexOHLC, e error) { var ret IndexOHLCResult params := map[string]interface{}{} params["symbol"] = symbol params["resolution"] = resolution params["from"] = from params["to"] = to - query, _, e = b.PublicRequest(http.MethodGet, "api/linear/public/kline/price", params, &ret) + query, resp, e = b.PublicRequest(http.MethodGet, "api/linear/public/kline/price", params, &ret) if e != nil { return } @@ -93,14 +93,14 @@ func (b *ByBit) LinearGetPriceIndex(symbol string, resolution int, from string, // LinearGetPremiumIndex // https://api2.bybit.com/api/linear/public/kline/premium-price?symbol=BTCUSDT&from=1607364960&to=1610011020&resolution=30 -func (b *ByBit) LinearGetPremiumIndex(symbol string, resolution int, from string, to string) (query string, resp []byte, result []IndexOHLC, e error) { +func (b *ByBit) LinearGetPremiumIndex(symbol string, resolution string, from int64, to int64) (query string, resp []byte, result []IndexOHLC, e error) { var ret IndexOHLCResult params := map[string]interface{}{} params["symbol"] = symbol params["resolution"] = resolution params["from"] = from params["to"] = to - query, _, e = b.PublicRequest(http.MethodGet, "api/linear/public/kline/premium-price", params, &ret) + query, resp, e = b.PublicRequest(http.MethodGet, "api/linear/public/kline/premium-price", params, &ret) if e != nil { return } diff --git a/rest/api_all.go b/rest/api_all.go index 89b6d80..9a41cc3 100644 --- a/rest/api_all.go +++ b/rest/api_all.go @@ -10,7 +10,7 @@ import ( func (b *ByBit) GetServerTime() (query string, resp []byte, timeNow int64, err error) { params := map[string]interface{}{} var ret BaseResult - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/time", params, &ret) + query, resp, err = b.PublicRequest(http.MethodGet, "v2/public/time", params, &ret) if err != nil { return } @@ -28,7 +28,7 @@ func (b *ByBit) GetWalletBalance(coin string) (query string, resp []byte, result var ret GetBalanceResult params := map[string]interface{}{} params["coin"] = coin - query, _, err = b.SignedRequest(http.MethodGet, "v2/private/wallet/balance", params, &ret) + query, resp, err = b.SignedRequest(http.MethodGet, "v2/private/wallet/balance", params, &ret) if err != nil { return } @@ -86,7 +86,7 @@ func (b *ByBit) SetLeverage(leverage int, symbol string) (query string, resp []b params := map[string]interface{}{} params["symbol"] = symbol params["leverage"] = fmt.Sprintf("%v", leverage) - query, _, err = b.SignedRequest(http.MethodPost, "user/leverage/save", params, &r) + query, resp, err = b.SignedRequest(http.MethodPost, "user/leverage/save", params, &r) if err != nil { return } @@ -122,7 +122,7 @@ func (b *ByBit) WalletRecords(symbol string, page int, limit int) (query string, func (b *ByBit) GetTickers() (query string, resp []byte, result []Ticker, err error) { var ret GetTickersResult params := map[string]interface{}{} - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/tickers", params, &ret) + query, resp, err = b.PublicRequest(http.MethodGet, "v2/public/tickers", params, &ret) if err != nil { return } @@ -141,7 +141,7 @@ func (b *ByBit) GetTradingRecords(symbol string, from int64, limit int) (query s if limit > 0 { params["limit"] = limit } - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/trading-records", params, &ret) + query, resp, err = b.PublicRequest(http.MethodGet, "v2/public/trading-records", params, &ret) if err != nil { return } @@ -153,7 +153,7 @@ func (b *ByBit) GetTradingRecords(symbol string, from int64, limit int) (query s func (b *ByBit) GetSymbols() (query string, resp []byte, result []SymbolInfo, err error) { var ret GetSymbolsResult params := map[string]interface{}{} - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/symbols", params, &ret) + query, resp, err = b.PublicRequest(http.MethodGet, "v2/public/symbols", params, &ret) if err != nil { return } @@ -170,7 +170,7 @@ func (b *ByBit) GetOpenInterest(symbol string, period string, limit int) (query if limit > 0 { params["limit"] = limit } - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/open-interest", params, &ret) + query, resp, err = b.PublicRequest(http.MethodGet, "v2/public/open-interest", params, &ret) if err != nil { return } @@ -187,7 +187,7 @@ func (b *ByBit) GetAccountRatio(symbol string, period string, limit int) (query if limit > 0 { params["limit"] = limit } - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/account-ratio", params, &ret) + query, resp, err = b.PublicRequest(http.MethodGet, "v2/public/account-ratio", params, &ret) if err != nil { return } diff --git a/rest/api_inverse.go b/rest/api_inverse.go index d76e1c9..91712b1 100644 --- a/rest/api_inverse.go +++ b/rest/api_inverse.go @@ -13,7 +13,7 @@ func (b *ByBit) GetOrderBook(symbol string) (query string, resp []byte, result O var ret GetOrderBookResult params := map[string]interface{}{} params["symbol"] = symbol - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/orderBook/L2", params, &ret) + query, resp, err = b.PublicRequest(http.MethodGet, "v2/public/orderBook/L2", params, &ret) if err != nil { return } @@ -55,7 +55,7 @@ func (b *ByBit) GetKLine(symbol string, interval string, from int64, limit int) if limit > 0 { params["limit"] = limit } - query, _, err = b.PublicRequest(http.MethodGet, "v2/public/kline/list", params, &ret) + query, resp, err = b.PublicRequest(http.MethodGet, "v2/public/kline/list", params, &ret) if err != nil { return } diff --git a/rest/api_linear.go b/rest/api_linear.go index a0665d5..9395983 100644 --- a/rest/api_linear.go +++ b/rest/api_linear.go @@ -15,7 +15,7 @@ func (b *ByBit) LinearGetKLine(symbol string, interval string, from int64, limit if limit > 0 { params["limit"] = limit } - query, _, err = b.PublicRequest(http.MethodGet, "public/linear/kline", params, &ret) + query, resp, err = b.PublicRequest(http.MethodGet, "public/linear/kline", params, &ret) if err != nil { return } diff --git a/rest/result.go b/rest/result.go index c83f60b..940cd10 100644 --- a/rest/result.go +++ b/rest/result.go @@ -411,16 +411,16 @@ type Funding struct { Total int `json:"total"` } -type FundingResult struct { - BaseResult - Result Funding `json:"result"` -} - type FundingData struct { Id int `json:"id"` Symbol string `json:"symbol"` Value sjson.Number `json:"value"` - Time time.Time `json:"time"` + Time string `json:"time"` +} + +type FundingResult struct { + BaseResult + Result Funding `json:"result"` } type IndexOHLC struct { From 521b177538f8cbd957590a75406214b9b6fed023 Mon Sep 17 00:00:00 2001 From: atik-lab Date: Fri, 8 Jan 2021 16:10:28 +0200 Subject: [PATCH 25/34] Integrate bybit linear in the rest library. Bybit add indexes and premium index. --- rest/api_linear.go | 25 ++++++++++--------------- rest/result.go | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/rest/api_linear.go b/rest/api_linear.go index 9395983..21e5b87 100644 --- a/rest/api_linear.go +++ b/rest/api_linear.go @@ -6,8 +6,8 @@ import ( ) // LinearGetKLine -func (b *ByBit) LinearGetKLine(symbol string, interval string, from int64, limit int) (query string, resp []byte, result []OHLC, err error) { - var ret GetKlineResult +func (b *ByBit) LinearGetKLine(symbol string, interval string, from int64, limit int) (query string, resp []byte, result []OHLCLinear, err error) { + var ret GetLinearKlineResult params := map[string]interface{}{} params["symbol"] = symbol params["interval"] = interval @@ -67,7 +67,7 @@ func (b *ByBit) LinearGetActiveOrders(symbol string) (query string, resp []byte, // CreateOrder func (b *ByBit) LinearCreateOrder(side string, orderType string, price float64, - qty int, timeInForce string, takeProfit float64, stopLoss float64, reduceOnly bool, + qty float64, timeInForce string, takeProfit float64, stopLoss float64, reduceOnly bool, closeOnTrigger bool, orderLinkID string, symbol string) (query string, resp []byte, result Order, err error) { var cResult OrderResponse params := map[string]interface{}{} @@ -85,12 +85,8 @@ func (b *ByBit) LinearCreateOrder(side string, orderType string, price float64, if stopLoss > 0 { params["stop_loss"] = stopLoss } - if reduceOnly { - params["reduce_only"] = true - } - if closeOnTrigger { - params["close_on_trigger"] = true - } + params["reduce_only"] = reduceOnly + params["close_on_trigger"] = closeOnTrigger if orderLinkID != "" { params["order_link_id"] = orderLinkID } @@ -107,7 +103,7 @@ func (b *ByBit) LinearCreateOrder(side string, orderType string, price float64, } // ReplaceOrder -func (b *ByBit) LinearReplaceOrder(symbol string, orderID string, qty int, price float64) (query string, resp []byte, result Order, err error) { +func (b *ByBit) LinearReplaceOrder(symbol string, orderID string, qty float64, price float64) (query string, resp []byte, result Order, err error) { var cResult OrderResponse params := map[string]interface{}{} params["order_id"] = orderID @@ -213,7 +209,7 @@ func (b *ByBit) LinearGetActiveStopOrders(symbol string) (query string, resp []b // CreateStopOrder func (b *ByBit) LinearCreateStopOrder(side string, orderType string, price float64, basePrice float64, stopPx float64, - qty int, triggerBy string, timeInForce string, closeOnTrigger bool, symbol string) (query string, resp []byte, result StopOrder, err error) { + qty float64, triggerBy string, timeInForce string, closeOnTrigger bool, symbol string, reduceOnly bool) (query string, resp []byte, result StopOrder, err error) { var cResult StopOrderResponse params := map[string]interface{}{} params["side"] = side @@ -226,9 +222,8 @@ func (b *ByBit) LinearCreateStopOrder(side string, orderType string, price float params["base_price"] = basePrice params["stop_px"] = stopPx params["time_in_force"] = timeInForce - if closeOnTrigger { - params["close_on_trigger"] = true - } + params["close_on_trigger"] = closeOnTrigger + params["reduce_only"] = reduceOnly if triggerBy != "" { params["trigger_by"] = triggerBy } @@ -245,7 +240,7 @@ func (b *ByBit) LinearCreateStopOrder(side string, orderType string, price float } // ReplaceStopOrder -func (b *ByBit) LinearReplaceStopOrder(symbol string, orderID string, qty int, price float64, triggerPrice float64) (query string, resp []byte, result StopOrder, err error) { +func (b *ByBit) LinearReplaceStopOrder(symbol string, orderID string, qty float64, price float64, triggerPrice float64) (query string, resp []byte, result StopOrder, err error) { var cResult StopOrderResponse params := map[string]interface{}{} params["stop_order_id"] = orderID diff --git a/rest/result.go b/rest/result.go index 940cd10..4216e22 100644 --- a/rest/result.go +++ b/rest/result.go @@ -62,6 +62,23 @@ type GetKlineResult struct { Result []OHLC `json:"result"` } +type OHLCLinear struct { + Symbol string `json:"symbol"` + Period string `json:"period"` + OpenTime int64 `json:"open_time"` + Open float64 `json:"open"` + High float64 `json:"high"` + Low float64 `json:"low"` + Close float64 `json:"close"` + Volume float64 `json:"volume"` + Turnover float64 `json:"turnover"` +} + +type GetLinearKlineResult struct { + BaseResult + Result []OHLCLinear `json:"result"` +} + type OpenInterest struct { Symbol string `json:"symbol"` OpenInterest sjson.Number `json:"open_interest"` From a461bdb3d18b18ccb7c3519987d5686a8c60f41c Mon Sep 17 00:00:00 2001 From: atik-lab Date: Fri, 8 Jan 2021 19:18:28 +0200 Subject: [PATCH 26/34] Integrate bybit linear in the rest library. Bybit add indexes and premium index. --- rest/api_linear.go | 4 ++-- rest/result.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rest/api_linear.go b/rest/api_linear.go index 21e5b87..4d4d017 100644 --- a/rest/api_linear.go +++ b/rest/api_linear.go @@ -166,7 +166,7 @@ func (b *ByBit) LinearCancelAllOrder(symbol string) (query string, resp []byte, } // GetStopOrders -func (b *ByBit) LinearGetStopOrders(symbol string, stopOrderStatus string, limit int, page string) (query string, resp []byte, result StopOrderListResponseResult, err error) { +func (b *ByBit) LinearGetStopOrders(symbol string, stopOrderStatus string, limit int, page int) (query string, resp []byte, result StopOrderListResponseResult, err error) { var cResult StopOrderListResponse if limit == 0 { limit = 20 @@ -195,7 +195,7 @@ func (b *ByBit) LinearGetActiveStopOrders(symbol string) (query string, resp []b var cResult StopOrderArrayResponse params := map[string]interface{}{} params["symbol"] = symbol - query, resp, err = b.SignedRequest(http.MethodGet, "private/linear/order/search", params, &cResult) + query, resp, err = b.SignedRequest(http.MethodGet, "private/linear/stop-order/search", params, &cResult) if err != nil { return } diff --git a/rest/result.go b/rest/result.go index 4216e22..2f5b75e 100644 --- a/rest/result.go +++ b/rest/result.go @@ -317,9 +317,9 @@ type OrderListResponsePaginated struct { } type OrderListResponseResultPaginated struct { - CurrentPage string `json:"current_page"` - LastPage string `json:"last_page"` - Data []Order `json:"data"` + CurrentPage sjson.Number `json:"current_page"` + LastPage sjson.Number `json:"last_page"` + Data []Order `json:"data"` } type OrderResponse struct { From 969da5a2310191715a4cc234ce314196af470f0f Mon Sep 17 00:00:00 2001 From: atik-lab Date: Wed, 27 Jan 2021 06:20:38 +0200 Subject: [PATCH 27/34] Added recws to modify it. --- recws/keepalive.go | 25 +++ recws/recws.go | 471 +++++++++++++++++++++++++++++++++++++++++++++ ws/ws.go | 6 +- 3 files changed, 499 insertions(+), 3 deletions(-) create mode 100644 recws/keepalive.go create mode 100644 recws/recws.go diff --git a/recws/keepalive.go b/recws/keepalive.go new file mode 100644 index 0000000..bb14889 --- /dev/null +++ b/recws/keepalive.go @@ -0,0 +1,25 @@ +package recws + +import ( + "sync" + "time" +) + +type keepAliveResponse struct { + lastResponse time.Time + sync.RWMutex +} + +func (k *keepAliveResponse) setLastResponse() { + k.Lock() + defer k.Unlock() + + k.lastResponse = time.Now() +} + +func (k *keepAliveResponse) getLastResponse() time.Time { + k.RLock() + defer k.RUnlock() + + return k.lastResponse +} diff --git a/recws/recws.go b/recws/recws.go new file mode 100644 index 0000000..0addaa1 --- /dev/null +++ b/recws/recws.go @@ -0,0 +1,471 @@ +// Package recws provides websocket client based on gorilla/websocket +// that will automatically reconnect if the connection is dropped. +package recws + +import ( + "crypto/tls" + "errors" + "log" + "math/rand" + "net/http" + "net/url" + "sync" + "time" + + "github.com/gorilla/websocket" + "github.com/jpillora/backoff" +) + +// ErrNotConnected is returned when the application read/writes +// a message and the connection is closed +var ErrNotConnected = errors.New("websocket: not connected") + +// The RecConn type represents a Reconnecting WebSocket connection. +type RecConn struct { + // RecIntvlMin specifies the initial reconnecting interval, + // default to 2 seconds + RecIntvlMin time.Duration + // RecIntvlMax specifies the maximum reconnecting interval, + // default to 30 seconds + RecIntvlMax time.Duration + // RecIntvlFactor specifies the rate of increase of the reconnection + // interval, default to 1.5 + RecIntvlFactor float64 + // HandshakeTimeout specifies the duration for the handshake to complete, + // default to 2 seconds + HandshakeTimeout time.Duration + // Proxy specifies the proxy function for the dialer + // defaults to ProxyFromEnvironment + Proxy func(*http.Request) (*url.URL, error) + // Client TLS config to use on reconnect + TLSClientConfig *tls.Config + // SubscribeHandler fires after the connection successfully establish. + SubscribeHandler func() error + // KeepAliveTimeout is an interval for sending ping/pong messages + // disabled if 0 + KeepAliveTimeout time.Duration + // NonVerbose suppress connecting/reconnecting messages. + NonVerbose bool + + isConnected bool + mu sync.RWMutex + url string + reqHeader http.Header + httpResp *http.Response + dialErr error + dialer *websocket.Dialer + close chan (bool) + + *websocket.Conn +} + +// CloseAndReconnect will try to reconnect. +func (rc *RecConn) CloseAndReconnect() { + rc.Close() + go rc.connect() +} + +// setIsConnected sets state for isConnected +func (rc *RecConn) setIsConnected(state bool) { + rc.mu.Lock() + defer rc.mu.Unlock() + + rc.isConnected = state +} + +func (rc *RecConn) getConn() *websocket.Conn { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.Conn +} + +// Close closes the underlying network connection without +// sending or waiting for a close frame. +func (rc *RecConn) Close() { + if rc.getConn() != nil { + rc.mu.Lock() + rc.Conn.Close() + rc.mu.Unlock() + } + rc.close <- true + rc.setIsConnected(false) +} + +// ReadMessage is a helper method for getting a reader +// using NextReader and reading from that reader to a buffer. +// +// If the connection is closed ErrNotConnected is returned +func (rc *RecConn) ReadMessage() (messageType int, message []byte, err error) { + err = ErrNotConnected + if rc.IsConnected() { + messageType, message, err = rc.Conn.ReadMessage() + if err != nil { + rc.CloseAndReconnect() + } + } + + return +} + +// WriteMessage is a helper method for getting a writer using NextWriter, +// writing the message and closing the writer. +// +// If the connection is closed ErrNotConnected is returned +func (rc *RecConn) WriteMessage(messageType int, data []byte) error { + err := ErrNotConnected + if rc.IsConnected() { + rc.mu.Lock() + err = rc.Conn.WriteMessage(messageType, data) + rc.mu.Unlock() + if err != nil { + rc.CloseAndReconnect() + } + } + + return err +} + +// WriteJSON writes the JSON encoding of v to the connection. +// +// See the documentation for encoding/json Marshal for details about the +// conversion of Go values to JSON. +// +// If the connection is closed ErrNotConnected is returned +func (rc *RecConn) WriteJSON(v interface{}) error { + err := ErrNotConnected + if rc.IsConnected() { + rc.mu.Lock() + err = rc.Conn.WriteJSON(v) + rc.mu.Unlock() + if err != nil { + rc.CloseAndReconnect() + } + } + + return err +} + +// ReadJSON reads the next JSON-encoded message from the connection and stores +// it in the value pointed to by v. +// +// See the documentation for the encoding/json Unmarshal function for details +// about the conversion of JSON to a Go value. +// +// If the connection is closed ErrNotConnected is returned +func (rc *RecConn) ReadJSON(v interface{}) error { + err := ErrNotConnected + if rc.IsConnected() { + err = rc.Conn.ReadJSON(v) + if err != nil { + rc.CloseAndReconnect() + } + } + + return err +} + +func (rc *RecConn) setURL(url string) { + rc.mu.Lock() + defer rc.mu.Unlock() + + rc.url = url +} + +func (rc *RecConn) setReqHeader(reqHeader http.Header) { + rc.mu.Lock() + defer rc.mu.Unlock() + + rc.reqHeader = reqHeader +} + +// parseURL parses current url +func (rc *RecConn) parseURL(urlStr string) (string, error) { + if urlStr == "" { + return "", errors.New("dial: url cannot be empty") + } + + u, err := url.Parse(urlStr) + + if err != nil { + return "", errors.New("url: " + err.Error()) + } + + if u.Scheme != "ws" && u.Scheme != "wss" { + return "", errors.New("url: websocket uris must start with ws or wss scheme") + } + + if u.User != nil { + return "", errors.New("url: user name and password are not allowed in websocket URIs") + } + + return urlStr, nil +} + +func (rc *RecConn) setDefaultRecIntvlMin() { + rc.mu.Lock() + defer rc.mu.Unlock() + + if rc.RecIntvlMin == 0 { + rc.RecIntvlMin = 2 * time.Second + } +} + +func (rc *RecConn) setDefaultRecIntvlMax() { + rc.mu.Lock() + defer rc.mu.Unlock() + + if rc.RecIntvlMax == 0 { + rc.RecIntvlMax = 30 * time.Second + } +} + +func (rc *RecConn) setDefaultRecIntvlFactor() { + rc.mu.Lock() + defer rc.mu.Unlock() + + if rc.RecIntvlFactor == 0 { + rc.RecIntvlFactor = 1.5 + } +} + +func (rc *RecConn) setDefaultHandshakeTimeout() { + rc.mu.Lock() + defer rc.mu.Unlock() + + if rc.HandshakeTimeout == 0 { + rc.HandshakeTimeout = 2 * time.Second + } +} + +func (rc *RecConn) setDefaultProxy() { + rc.mu.Lock() + defer rc.mu.Unlock() + + if rc.Proxy == nil { + rc.Proxy = http.ProxyFromEnvironment + } +} + +func (rc *RecConn) setDefaultDialer(tlsClientConfig *tls.Config, handshakeTimeout time.Duration) { + rc.mu.Lock() + defer rc.mu.Unlock() + + rc.dialer = &websocket.Dialer{ + HandshakeTimeout: handshakeTimeout, + Proxy: rc.Proxy, + TLSClientConfig: tlsClientConfig, + } +} + +func (rc *RecConn) getHandshakeTimeout() time.Duration { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.HandshakeTimeout +} + +func (rc *RecConn) getTLSClientConfig() *tls.Config { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.TLSClientConfig +} + +func (rc *RecConn) SetTLSClientConfig(tlsClientConfig *tls.Config) { + rc.mu.Lock() + defer rc.mu.Unlock() + + rc.TLSClientConfig = tlsClientConfig +} + +// Dial creates a new client connection. +// The URL url specifies the host and request URI. Use requestHeader to specify +// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies +// (Cookie). Use GetHTTPResponse() method for the response.Header to get +// the selected subprotocol (Sec-WebSocket-Protocol) and cookies (Set-Cookie). +func (rc *RecConn) Dial(urlStr string, reqHeader http.Header) { + urlStr, err := rc.parseURL(urlStr) + + if err != nil { + log.Fatalf("Dial: %v", err) + } + + // Close channel + rc.close = make(chan bool, 1) + + // Config + rc.setURL(urlStr) + rc.setReqHeader(reqHeader) + rc.setDefaultRecIntvlMin() + rc.setDefaultRecIntvlMax() + rc.setDefaultRecIntvlFactor() + rc.setDefaultHandshakeTimeout() + rc.setDefaultProxy() + rc.setDefaultDialer(rc.getTLSClientConfig(), rc.getHandshakeTimeout()) + + // Connect + go rc.connect() + + // wait on first attempt + time.Sleep(rc.getHandshakeTimeout()) +} + +// GetURL returns current connection url +func (rc *RecConn) GetURL() string { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.url +} + +func (rc *RecConn) getNonVerbose() bool { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.NonVerbose +} + +func (rc *RecConn) getBackoff() *backoff.Backoff { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return &backoff.Backoff{ + Min: rc.RecIntvlMin, + Max: rc.RecIntvlMax, + Factor: rc.RecIntvlFactor, + Jitter: true, + } +} + +func (rc *RecConn) hasSubscribeHandler() bool { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.SubscribeHandler != nil +} + +func (rc *RecConn) getKeepAliveTimeout() time.Duration { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.KeepAliveTimeout +} + +func (rc *RecConn) writeControlPingMessage() error { + rc.mu.Lock() + defer rc.mu.Unlock() + + return rc.Conn.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(10*time.Second)) +} + +func (rc *RecConn) keepAlive() { + var ( + keepAliveResponse = new(keepAliveResponse) + ticker = time.NewTicker(rc.getKeepAliveTimeout()) + ) + + rc.mu.Lock() + rc.Conn.SetPongHandler(func(msg string) error { + keepAliveResponse.setLastResponse() + return nil + }) + rc.mu.Unlock() + + go func() { + defer ticker.Stop() + + for { + if !rc.IsConnected() { + continue + } + + if err := rc.writeControlPingMessage(); err != nil { + log.Println(err) + } + + <-ticker.C + if time.Since(keepAliveResponse.getLastResponse()) > rc.getKeepAliveTimeout() { + rc.CloseAndReconnect() + return + } + } + }() +} + +func (rc *RecConn) connect() { + b := rc.getBackoff() + rand.Seed(time.Now().UTC().UnixNano()) + + for { + select { + case <-rc.close: + return + default: + nextItvl := b.Duration() + wsConn, httpResp, err := rc.dialer.Dial(rc.url, rc.reqHeader) + + rc.mu.Lock() + rc.Conn = wsConn + rc.dialErr = err + rc.isConnected = err == nil + rc.httpResp = httpResp + rc.mu.Unlock() + + if err == nil { + if !rc.getNonVerbose() { + log.Printf("Dial: connection was successfully established with %s\n", rc.url) + } + + if rc.hasSubscribeHandler() { + if err := rc.SubscribeHandler(); err != nil { + log.Fatalf("Dial: connect handler failed with %s", err.Error()) + } + if !rc.getNonVerbose() { + log.Printf("Dial: connect handler was successfully established with %s\n", rc.url) + } + } + + if rc.getKeepAliveTimeout() != 0 { + rc.keepAlive() + } + + return + } + + if !rc.getNonVerbose() { + log.Println(err) + log.Println("Dial: will try again in", nextItvl, "seconds.") + } + + time.Sleep(nextItvl) + } + } +} + +// GetHTTPResponse returns the http response from the handshake. +// Useful when WebSocket handshake fails, +// so that callers can handle redirects, authentication, etc. +func (rc *RecConn) GetHTTPResponse() *http.Response { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.httpResp +} + +// GetDialError returns the last dialer error. +// nil on successful connection. +func (rc *RecConn) GetDialError() error { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.dialErr +} + +// IsConnected returns the WebSocket connection state +func (rc *RecConn) IsConnected() bool { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.isConnected +} diff --git a/ws/ws.go b/ws/ws.go index 6d3101f..7a0d1ac 100644 --- a/ws/ws.go +++ b/ws/ws.go @@ -8,8 +8,8 @@ import ( "errors" "fmt" "github.com/chuckpreslar/emission" + "github.com/frankrap/bybit-api/recws" "github.com/gorilla/websocket" - "github.com/recws-org/recws" "github.com/tidwall/gjson" "log" "strings" @@ -367,6 +367,6 @@ func (b *ByBitWS) handlePong() (err error) { return nil } -func (b *ByBitWS) Close() { - b.conn.Close() +func (b *ByBitWS) CloseAndReconnect() { + b.conn.CloseAndReconnect() } From fca2f528fe0e3234c65d965c06b40d5cf9e9ee47 Mon Sep 17 00:00:00 2001 From: atik-lab Date: Wed, 27 Jan 2021 06:21:02 +0200 Subject: [PATCH 28/34] Added recws to modify it. --- go.mod | 1 + 1 file changed, 1 insertion(+) diff --git a/go.mod b/go.mod index 99598c9..11daabc 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.13 require ( github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 github.com/gorilla/websocket v1.4.2 + github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7 github.com/json-iterator/go v1.1.9 github.com/recws-org/recws v1.2.1 github.com/stretchr/testify v1.5.1 From 1213be1b799f0e8be4d280998a23906b35303a4a Mon Sep 17 00:00:00 2001 From: atik-lab Date: Wed, 27 Jan 2021 06:22:26 +0200 Subject: [PATCH 29/34] Avoid burning the CPU. --- recws/recws.go | 1 + 1 file changed, 1 insertion(+) diff --git a/recws/recws.go b/recws/recws.go index 0addaa1..8dcb06d 100644 --- a/recws/recws.go +++ b/recws/recws.go @@ -377,6 +377,7 @@ func (rc *RecConn) keepAlive() { for { if !rc.IsConnected() { + time.Sleep(time.Millisecond * 100) // avoid burning CPU continue } From 5820df51a37253053353f37793b099379fa25d9a Mon Sep 17 00:00:00 2001 From: atik-lab Date: Wed, 27 Jan 2021 12:17:19 +0200 Subject: [PATCH 30/34] Avoid burning the CPU. --- recws/recws.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/recws/recws.go b/recws/recws.go index 8dcb06d..6cad8cc 100644 --- a/recws/recws.go +++ b/recws/recws.go @@ -377,8 +377,8 @@ func (rc *RecConn) keepAlive() { for { if !rc.IsConnected() { - time.Sleep(time.Millisecond * 100) // avoid burning CPU - continue + rc.CloseAndReconnect() + return } if err := rc.writeControlPingMessage(); err != nil { From 5debd22568f7112413113007d26097c23af524b2 Mon Sep 17 00:00:00 2001 From: atik-lab Date: Wed, 27 Jan 2021 14:25:10 +0200 Subject: [PATCH 31/34] Avoid burning the CPU. --- recws/recws.go | 73 ++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 41 deletions(-) diff --git a/recws/recws.go b/recws/recws.go index 6cad8cc..30bcb15 100644 --- a/recws/recws.go +++ b/recws/recws.go @@ -54,7 +54,6 @@ type RecConn struct { httpResp *http.Response dialErr error dialer *websocket.Dialer - close chan (bool) *websocket.Conn } @@ -88,7 +87,7 @@ func (rc *RecConn) Close() { rc.Conn.Close() rc.mu.Unlock() } - rc.close <- true + rc.setIsConnected(false) } @@ -291,9 +290,6 @@ func (rc *RecConn) Dial(urlStr string, reqHeader http.Header) { log.Fatalf("Dial: %v", err) } - // Close channel - rc.close = make(chan bool, 1) - // Config rc.setURL(urlStr) rc.setReqHeader(reqHeader) @@ -377,8 +373,8 @@ func (rc *RecConn) keepAlive() { for { if !rc.IsConnected() { - rc.CloseAndReconnect() - return + time.Sleep(time.Millisecond * 100) + continue } if err := rc.writeControlPingMessage(); err != nil { @@ -399,48 +395,43 @@ func (rc *RecConn) connect() { rand.Seed(time.Now().UTC().UnixNano()) for { - select { - case <-rc.close: - return - default: - nextItvl := b.Duration() - wsConn, httpResp, err := rc.dialer.Dial(rc.url, rc.reqHeader) - - rc.mu.Lock() - rc.Conn = wsConn - rc.dialErr = err - rc.isConnected = err == nil - rc.httpResp = httpResp - rc.mu.Unlock() - - if err == nil { - if !rc.getNonVerbose() { - log.Printf("Dial: connection was successfully established with %s\n", rc.url) - } + nextItvl := b.Duration() + wsConn, httpResp, err := rc.dialer.Dial(rc.url, rc.reqHeader) - if rc.hasSubscribeHandler() { - if err := rc.SubscribeHandler(); err != nil { - log.Fatalf("Dial: connect handler failed with %s", err.Error()) - } - if !rc.getNonVerbose() { - log.Printf("Dial: connect handler was successfully established with %s\n", rc.url) - } - } + rc.mu.Lock() + rc.Conn = wsConn + rc.dialErr = err + rc.isConnected = err == nil + rc.httpResp = httpResp + rc.mu.Unlock() - if rc.getKeepAliveTimeout() != 0 { - rc.keepAlive() - } + if err == nil { + if !rc.getNonVerbose() { + log.Printf("Dial: connection was successfully established with %s\n", rc.url) + } - return + if rc.hasSubscribeHandler() { + if err := rc.SubscribeHandler(); err != nil { + log.Fatalf("Dial: connect handler failed with %s", err.Error()) + } + if !rc.getNonVerbose() { + log.Printf("Dial: connect handler was successfully established with %s\n", rc.url) + } } - if !rc.getNonVerbose() { - log.Println(err) - log.Println("Dial: will try again in", nextItvl, "seconds.") + if rc.getKeepAliveTimeout() != 0 { + rc.keepAlive() } - time.Sleep(nextItvl) + return } + + if !rc.getNonVerbose() { + log.Println(err) + log.Println("Dial: will try again in", nextItvl, "seconds.") + } + + time.Sleep(nextItvl) } } From ecb11dd79ee68fa2d8c35f9106a6362263fe7d67 Mon Sep 17 00:00:00 2001 From: atik-lab Date: Wed, 27 Jan 2021 16:50:18 +0200 Subject: [PATCH 32/34] Avoid burning the CPU. --- recws/recws.go | 73 ++++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/recws/recws.go b/recws/recws.go index 30bcb15..6cad8cc 100644 --- a/recws/recws.go +++ b/recws/recws.go @@ -54,6 +54,7 @@ type RecConn struct { httpResp *http.Response dialErr error dialer *websocket.Dialer + close chan (bool) *websocket.Conn } @@ -87,7 +88,7 @@ func (rc *RecConn) Close() { rc.Conn.Close() rc.mu.Unlock() } - + rc.close <- true rc.setIsConnected(false) } @@ -290,6 +291,9 @@ func (rc *RecConn) Dial(urlStr string, reqHeader http.Header) { log.Fatalf("Dial: %v", err) } + // Close channel + rc.close = make(chan bool, 1) + // Config rc.setURL(urlStr) rc.setReqHeader(reqHeader) @@ -373,8 +377,8 @@ func (rc *RecConn) keepAlive() { for { if !rc.IsConnected() { - time.Sleep(time.Millisecond * 100) - continue + rc.CloseAndReconnect() + return } if err := rc.writeControlPingMessage(); err != nil { @@ -395,43 +399,48 @@ func (rc *RecConn) connect() { rand.Seed(time.Now().UTC().UnixNano()) for { - nextItvl := b.Duration() - wsConn, httpResp, err := rc.dialer.Dial(rc.url, rc.reqHeader) - - rc.mu.Lock() - rc.Conn = wsConn - rc.dialErr = err - rc.isConnected = err == nil - rc.httpResp = httpResp - rc.mu.Unlock() - - if err == nil { - if !rc.getNonVerbose() { - log.Printf("Dial: connection was successfully established with %s\n", rc.url) - } + select { + case <-rc.close: + return + default: + nextItvl := b.Duration() + wsConn, httpResp, err := rc.dialer.Dial(rc.url, rc.reqHeader) + + rc.mu.Lock() + rc.Conn = wsConn + rc.dialErr = err + rc.isConnected = err == nil + rc.httpResp = httpResp + rc.mu.Unlock() + + if err == nil { + if !rc.getNonVerbose() { + log.Printf("Dial: connection was successfully established with %s\n", rc.url) + } - if rc.hasSubscribeHandler() { - if err := rc.SubscribeHandler(); err != nil { - log.Fatalf("Dial: connect handler failed with %s", err.Error()) + if rc.hasSubscribeHandler() { + if err := rc.SubscribeHandler(); err != nil { + log.Fatalf("Dial: connect handler failed with %s", err.Error()) + } + if !rc.getNonVerbose() { + log.Printf("Dial: connect handler was successfully established with %s\n", rc.url) + } } - if !rc.getNonVerbose() { - log.Printf("Dial: connect handler was successfully established with %s\n", rc.url) + + if rc.getKeepAliveTimeout() != 0 { + rc.keepAlive() } - } - if rc.getKeepAliveTimeout() != 0 { - rc.keepAlive() + return } - return - } + if !rc.getNonVerbose() { + log.Println(err) + log.Println("Dial: will try again in", nextItvl, "seconds.") + } - if !rc.getNonVerbose() { - log.Println(err) - log.Println("Dial: will try again in", nextItvl, "seconds.") + time.Sleep(nextItvl) } - - time.Sleep(nextItvl) } } From b2f19bab28f7c29528a1aa2392bf01e6f1132d2b Mon Sep 17 00:00:00 2001 From: Atik Date: Fri, 29 Jan 2021 19:57:28 +0200 Subject: [PATCH 33/34] Reconnect if fails. Adding more info when logging. --- recws/recws.go | 13 +++++-------- ws/ws.go | 42 ++++++++++++++++++++++-------------------- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/recws/recws.go b/recws/recws.go index 6cad8cc..33d4e4f 100644 --- a/recws/recws.go +++ b/recws/recws.go @@ -377,12 +377,11 @@ func (rc *RecConn) keepAlive() { for { if !rc.IsConnected() { - rc.CloseAndReconnect() return } if err := rc.writeControlPingMessage(); err != nil { - log.Println(err) + log.Printf("BybitRecws KeepAlive Error: %s", err.Error()) } <-ticker.C @@ -415,28 +414,26 @@ func (rc *RecConn) connect() { if err == nil { if !rc.getNonVerbose() { - log.Printf("Dial: connection was successfully established with %s\n", rc.url) + log.Printf("BybitRecws Dial: connection was successfully established with %s\n", rc.url) } if rc.hasSubscribeHandler() { if err := rc.SubscribeHandler(); err != nil { - log.Fatalf("Dial: connect handler failed with %s", err.Error()) + log.Fatalf("BybitRecws Dial: connect handler failed with %s", err.Error()) } if !rc.getNonVerbose() { - log.Printf("Dial: connect handler was successfully established with %s\n", rc.url) + log.Printf("BybitRecws Dial: connect handler was successfully established with %s\n", rc.url) } } if rc.getKeepAliveTimeout() != 0 { rc.keepAlive() } - return } if !rc.getNonVerbose() { - log.Println(err) - log.Println("Dial: will try again in", nextItvl, "seconds.") + log.Printf("BybitRecws Dial: will try again in %v seconds. Error: %s", nextItvl, err.Error()) } time.Sleep(nextItvl) diff --git a/ws/ws.go b/ws/ws.go index 7a0d1ac..24f29b0 100644 --- a/ws/ws.go +++ b/ws/ws.go @@ -90,7 +90,7 @@ func New(config *Configuration) *ByBitWS { func (b *ByBitWS) subscribeHandler() error { if b.cfg.DebugMode { - log.Printf("subscribeHandler") + log.Printf("BybitWs subscribeHandler") } b.mu.Lock() @@ -99,14 +99,14 @@ func (b *ByBitWS) subscribeHandler() error { if b.cfg.ApiKey != "" && b.cfg.SecretKey != "" { err := b.Auth() if err != nil { - log.Printf("auth error: %v", err) + log.Printf("BybitWs auth error: %v", err) } } for _, cmd := range b.subscribeCmds { err := b.SendCmd(cmd) if err != nil { - log.Printf("SendCmd return error: %v", err) + log.Printf("BybitWs SendCmd return error: %v", err) } } @@ -115,8 +115,7 @@ func (b *ByBitWS) subscribeHandler() error { func (b *ByBitWS) closeHandler(code int, text string) error { if b.cfg.DebugMode { - log.Printf("close handle executed code=%v text=%v", - code, text) + log.Printf("BybitWs close handle executed code=%v text=%v", code, text) } return nil } @@ -146,7 +145,7 @@ func (b *ByBitWS) SendCmd(cmd Cmd) error { func (b *ByBitWS) Send(msg string) (err error) { defer func() { if r := recover(); r != nil { - err = errors.New(fmt.Sprintf("send error: %v", r)) + err = errors.New(fmt.Sprintf("BybitWs send error: %v", r)) } }() @@ -178,8 +177,11 @@ func (b *ByBitWS) Start() error { for { messageType, data, err := b.conn.ReadMessage() if err != nil { - log.Printf("Read error: %v", err) - time.Sleep(100 * time.Millisecond) + log.Printf("BybitWs Read error, we will close connection: %v", err) + b.conn.Close() + go func() { + _ = b.Start() + }() return } @@ -197,7 +199,7 @@ func (b *ByBitWS) connect() { func (b *ByBitWS) ping() { defer func() { if r := recover(); r != nil { - log.Printf("ping error: %v", r) + log.Printf("BybitWs ping error: %v", r) } }() @@ -206,7 +208,7 @@ func (b *ByBitWS) ping() { } err := b.conn.WriteMessage(websocket.TextMessage, []byte(`{"op":"ping"}`)) if err != nil { - log.Printf("ping error: %v", err) + log.Printf("BybitWs ping error: %v", err) } } @@ -235,7 +237,7 @@ func (b *ByBitWS) processMessage(messageType int, data []byte) { ret := gjson.ParseBytes(data) if b.cfg.DebugMode { - log.Printf("%v", string(data)) + log.Printf("BybitWs %v", string(data)) } // 处理心跳包 @@ -255,7 +257,7 @@ func (b *ByBitWS) processMessage(messageType int, data []byte) { var data []*OrderBookL2 err := json.Unmarshal([]byte(raw), &data) if err != nil { - log.Printf("%v", err) + log.Printf("BybitWs %v", err) return } b.processOrderBookSnapshot(symbol, data...) @@ -263,7 +265,7 @@ func (b *ByBitWS) processMessage(messageType int, data []byte) { var delta OrderBookL2Delta err := json.Unmarshal([]byte(raw), &delta) if err != nil { - log.Printf("%v", err) + log.Printf("BybitWs %v", err) return } b.processOrderBookDelta(symbol, &delta) @@ -274,7 +276,7 @@ func (b *ByBitWS) processMessage(messageType int, data []byte) { var data []*Trade err := json.Unmarshal([]byte(raw), &data) if err != nil { - log.Printf("%v", err) + log.Printf("BybitWs %v", err) return } b.processTrade(symbol, data...) @@ -289,7 +291,7 @@ func (b *ByBitWS) processMessage(messageType int, data []byte) { var data KLine err := json.Unmarshal([]byte(raw), &data) if err != nil { - log.Printf("%v", err) + log.Printf("BybitWs %v", err) return } b.processKLine(symbol, data) @@ -304,7 +306,7 @@ func (b *ByBitWS) processMessage(messageType int, data []byte) { var data []*Insurance err := json.Unmarshal([]byte(raw), &data) if err != nil { - log.Printf("%v", err) + log.Printf("BybitWs %v", err) return } b.processInsurance(currency, data...) @@ -318,7 +320,7 @@ func (b *ByBitWS) processMessage(messageType int, data []byte) { var data []*Instrument err := json.Unmarshal([]byte(raw), &data) if err != nil { - log.Printf("%v", err) + log.Printf("BybitWs %v", err) return } b.processInstrument(symbol, data...) @@ -327,7 +329,7 @@ func (b *ByBitWS) processMessage(messageType int, data []byte) { var data []*Position err := json.Unmarshal([]byte(raw), &data) if err != nil { - log.Printf("%v", err) + log.Printf("BybitWs %v", err) return } b.processPosition(data...) @@ -336,7 +338,7 @@ func (b *ByBitWS) processMessage(messageType int, data []byte) { var data []*Execution err := json.Unmarshal([]byte(raw), &data) if err != nil { - log.Printf("%v", err) + log.Printf("BybitWs %v", err) return } b.processExecution(data...) @@ -345,7 +347,7 @@ func (b *ByBitWS) processMessage(messageType int, data []byte) { var data []*Order err := json.Unmarshal([]byte(raw), &data) if err != nil { - log.Printf("%v", err) + log.Printf("BybitWs %v", err) return } b.processOrder(data...) From c7c786148d136dd9d4d490412ce1da40b7ce37e6 Mon Sep 17 00:00:00 2001 From: Atik Date: Mon, 1 Feb 2021 21:14:07 +0200 Subject: [PATCH 34/34] Create a flag of ended that actives when closing. --- ws/ws.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ws/ws.go b/ws/ws.go index 24f29b0..d490157 100644 --- a/ws/ws.go +++ b/ws/ws.go @@ -66,6 +66,7 @@ type ByBitWS struct { cancel context.CancelFunc conn *recws.RecConn mu sync.RWMutex + Ended bool subscribeCmds []Cmd orderBookLocals map[string]*OrderBookLocal // key: symbol @@ -177,11 +178,9 @@ func (b *ByBitWS) Start() error { for { messageType, data, err := b.conn.ReadMessage() if err != nil { - log.Printf("BybitWs Read error, we will close connection: %v", err) + log.Printf("BybitWs Read error, closing connection: %v", err) b.conn.Close() - go func() { - _ = b.Start() - }() + b.Ended = true return }