From 8695a773c94c3c9803db1609c0148c6fa8037a7c Mon Sep 17 00:00:00 2001 From: wuhewuhe Date: Sun, 5 Nov 2023 17:33:56 +0100 Subject: [PATCH 1/2] place order post example --- bybit_api_client.go | 111 ++++++++------ consts.go | 6 +- examples/Trade/place_order.go | 21 +++ examples/market/market_kline.go | 24 +++ market.go | 77 ++++++++++ request.go | 26 ++-- trade.go | 250 ++++++++++++++++++++++++++++++++ 7 files changed, 457 insertions(+), 58 deletions(-) create mode 100644 examples/Trade/place_order.go create mode 100644 examples/market/market_kline.go diff --git a/bybit_api_client.go b/bybit_api_client.go index d9b807d..8cbc2ee 100644 --- a/bybit_api_client.go +++ b/bybit_api_client.go @@ -5,23 +5,33 @@ import ( "context" "crypto/hmac" "crypto/sha256" + "encoding/hex" "encoding/json" "fmt" - handlers "github.com/wuhewuhe/bybit.go.api/handlers" + "github.com/wuhewuhe/bybit.go.api/handlers" "io" "log" "net/http" "os" + "strconv" "time" ) +type ServerResponse struct { + RetCode int `json:"retCode"` + RetMsg string `json:"retMsg"` + Result interface{} `json:"result"` + RetExtInfo struct{} `json:"retExtInfo"` + Time int64 `json:"time"` +} + // TimeInForceType define time in force type of order type TimeInForceType string // Client define API client type Client struct { APIKey string - SecretKey string + APISecret string BaseURL string HTTPClient *http.Client Debug bool @@ -39,29 +49,13 @@ func WithDebug(debug bool) ClientOption { } } +// WithBaseURL is a client option to set the base URL of the Bybit HTTP client. func WithBaseURL(baseURL string) ClientOption { return func(c *Client) { c.BaseURL = baseURL } } -func currentTimestamp() int64 { - return FormatTimestamp(time.Now()) -} - -// FormatTimestamp formats a time into Unix timestamp in milliseconds, as requested by Bybit. -func FormatTimestamp(t time.Time) int64 { - return t.UnixNano() / int64(time.Millisecond) -} - -type ServerResponse struct { - RetCode int `json:"retCode"` - RetMsg string `json:"retMsg"` - Result interface{} `json:"result"` - RetExtInfo struct{} `json:"retExtInfo"` - Time int64 `json:"time"` -} - func PrettyPrint(i interface{}) string { s, _ := json.MarshalIndent(i, "", "\t") return string(s) @@ -74,11 +68,11 @@ func (c *Client) debug(format string, v ...interface{}) { } // NewBybitHttpClient NewClient Create client function for initialising new Bybit client -func NewBybitHttpClient(apiKey string, secretKey string, options ...ClientOption) *Client { +func NewBybitHttpClient(apiKey string, APISecret string, options ...ClientOption) *Client { c := &Client{ APIKey: apiKey, - SecretKey: secretKey, - BaseURL: "https://api.bybit.com", + APISecret: APISecret, + BaseURL: MAINNET, HTTPClient: http.DefaultClient, Logger: log.New(os.Stderr, Name, log.LstdFlags), } @@ -104,49 +98,47 @@ func (c *Client) parseRequest(r *request, opts ...RequestOption) (err error) { fullURL := fmt.Sprintf("%s%s", c.BaseURL, r.endpoint) queryString := r.query.Encode() - body := &bytes.Buffer{} - bodyString := r.form.Encode() header := http.Header{} + body := &bytes.Buffer{} + if r.params != nil { + body = bytes.NewBuffer(r.params) + } if r.header != nil { header = r.header.Clone() } header.Set("User-Agent", fmt.Sprintf("%s/%s", Name, Version)) - if bodyString != "" { - header.Set("Content-Type", "application/json") - body = bytes.NewBufferString(bodyString) - } + if r.secType == secTypeSigned { + now := time.Now() + unixNano := now.UnixNano() + timeStamp := unixNano / 1000000 header.Set(signTypeKey, "2") header.Set(apiRequestKey, c.APIKey) - header.Set(timestampKey, fmt.Sprintf("%d", currentTimestamp())) + header.Set(timestampKey, strconv.FormatInt(timeStamp, 10)) if r.recvWindow == "" { - header.Set(recvWindowKey, "5000") + r.recvWindow = "5000" + } + header.Set(recvWindowKey, r.recvWindow) + + var signatureBase []byte + if r.method == "POST" { + header.Set("Content-Type", "application/json") + signatureBase = []byte(strconv.FormatInt(timeStamp, 10) + c.APIKey + r.recvWindow + string(r.params[:])) } else { - header.Set(recvWindowKey, r.recvWindow) + signatureBase = []byte(strconv.FormatInt(timeStamp, 10) + c.APIKey + r.recvWindow + queryString) } - - var signatureBase string - if r.method == "GET" { - signatureBase = fmt.Sprintf("%d%s%s%s", FormatTimestamp(time.Now()), c.APIKey, r.recvWindow, queryString) - } else if r.method == "POST" { - signatureBase = fmt.Sprintf("%d%s%s%s", FormatTimestamp(time.Now()), c.APIKey, r.recvWindow, bodyString) - } - - mac := hmac.New(sha256.New, []byte(c.SecretKey)) - _, err = mac.Write([]byte(signatureBase)) - if err != nil { - return err - } - signatureValue := fmt.Sprintf("%x", mac.Sum(nil)) - header.Set(signatureKey, signatureValue) + hmac256 := hmac.New(sha256.New, []byte(c.APISecret)) + hmac256.Write(signatureBase) + signature := hex.EncodeToString(hmac256.Sum(nil)) + header.Set(signatureKey, signature) } if queryString != "" { fullURL = fmt.Sprintf("%s?%s", fullURL, queryString) } - c.debug("full url: %s, body: %s", fullURL, bodyString) + c.debug("full url: %s, body: %s", fullURL, body) r.fullURL = fullURL - r.header = header r.body = body + r.header = header return nil } @@ -200,3 +192,26 @@ func (c *Client) callAPI(ctx context.Context, r *request, opts ...RequestOption) func (c *Client) NewServerTimeService() *ServerTime { return &ServerTime{c: c} } + +// NewMarketKlineService Market Endpoints +func (c *Client) NewMarketKlineService(klineType, category, symbol, interval string) *Klines { + return &Klines{ + c: c, + category: category, + symbol: symbol, + interval: interval, + klineType: klineType, + } +} + +// NewPlaceOrderService Trade Endpoints +func (c *Client) NewPlaceOrderService(category, symbol, side, orderType, qty string) *Order { + return &Order{ + c: c, + category: category, + symbol: symbol, + side: side, + orderType: orderType, + qty: qty, + } +} diff --git a/consts.go b/consts.go index e8f1889..02028cc 100644 --- a/consts.go +++ b/consts.go @@ -3,6 +3,10 @@ package bybit_connector const ( Name = "bybit.api.go" Version = "1.0.0" + // Https + MAINNET = "https://api.bybit.com" + MAINNET_BACKT = "https://api.bytick.com" + TESTNET = "https://api-testnet.bybit.com" // WebSocket public channel - Mainnet SPOT_MAINNET = "wss://stream.bybit.com/v5/public/spot" LINEAR_MAINNET = "wss://stream.bybit.com/v5/public/linear" @@ -29,5 +33,5 @@ const ( signatureKey = "X-BAPI-SIGN" apiRequestKey = "X-BAPI-API-KEY" recvWindowKey = "X-BAPI-RECV-WINDOW" - signTypeKey = "2" + signTypeKey = "X-BAPI-SIGN-TYPE" ) diff --git a/examples/Trade/place_order.go b/examples/Trade/place_order.go new file mode 100644 index 0000000..e640850 --- /dev/null +++ b/examples/Trade/place_order.go @@ -0,0 +1,21 @@ +package main + +import ( + "context" + "fmt" + bybit "github.com/wuhewuhe/bybit.go.api" +) + +func main() { + PlaceOrder() +} + +func PlaceOrder() { + client := bybit.NewBybitHttpClient("8wYkmpLsMg10eNQyPm", "Ouxc34myDnXvei54XsBZgoQzfGxO4bkr2Zsj", bybit.WithBaseURL(bybit.TESTNET)) + orderResult, err := client.NewPlaceOrderService("linear", "XRPUSDT", "Buy", "Market", "10").Do(context.Background()) + if err != nil { + fmt.Println(err) + return + } + fmt.Println(bybit.PrettyPrint(orderResult)) +} diff --git a/examples/market/market_kline.go b/examples/market/market_kline.go new file mode 100644 index 0000000..579b9fe --- /dev/null +++ b/examples/market/market_kline.go @@ -0,0 +1,24 @@ +package main + +import ( + "context" + "fmt" + bybit "github.com/wuhewuhe/bybit.go.api" +) + +func main() { + MarketKline() +} + +func MarketKline() { + + client := bybit.NewBybitHttpClient("", "") + + // NewServerTimeService + marketKline, err := client.NewMarketKlineService("premium-index-price-kline", "linear", "BTCUSDT", "1").Limit(10).Do(context.Background()) + if err != nil { + fmt.Println(err) + return + } + fmt.Println(bybit.PrettyPrint(marketKline)) +} diff --git a/market.go b/market.go index aba27bd..b519089 100644 --- a/market.go +++ b/market.go @@ -34,3 +34,80 @@ func (s *ServerTime) Do(ctx context.Context, opts ...RequestOption) (res *Server } return res, nil } + +type MarketKlineCandle struct { + StartTime string `json:"startTime"` + OpenPrice string `json:"openPrice"` + HighPrice string `json:"highPrice"` + LowPrice string `json:"lowPrice"` + ClosePrice string `json:"closePrice"` + Volume string `json:"volume"` + Turnover string `json:"turnover"` +} + +type MarketKlineResponse struct { + Category string `json:"category"` + Symbol string `json:"symbol"` + List []MarketKlineCandle `json:"list"` +} + +// Klines Market Kline (GET /v5/market/kline) +type Klines struct { + c *Client + klineType string + category string + symbol string + interval string + limit *int + start *uint64 + end *uint64 +} + +// Limit set limit +func (s *Klines) Limit(limit int) *Klines { + s.limit = &limit + return s +} + +// Start set startTime +func (s *Klines) Start(startTime uint64) *Klines { + s.start = &startTime + return s +} + +// End set endTime +func (s *Klines) End(endTime uint64) *Klines { + s.end = &endTime + return s +} + +// Do Send the request +func (s *Klines) Do(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + r := &request{ + method: http.MethodGet, + endpoint: "/v5/market/" + s.klineType, + secType: secTypeNone, + } + r.setParam("category", s.category) + r.setParam("symbol", s.symbol) + r.setParam("interval", s.interval) + if s.limit != nil { + r.setParam("limit", *s.limit) + } + if s.start != nil { + r.setParam("start", *s.start) + } + if s.end != nil { + r.setParam("end", *s.end) + } + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} diff --git a/request.go b/request.go index da54f9a..49ecd9e 100644 --- a/request.go +++ b/request.go @@ -1,8 +1,10 @@ package bybit_connector import ( + "encoding/json" "fmt" "io" + "log" "net/http" "net/url" ) @@ -21,12 +23,12 @@ type request struct { method string endpoint string query url.Values - form url.Values recvWindow string secType secType header http.Header - body io.Reader + params []byte fullURL string + body io.Reader } // addParam add param with key/value to query string @@ -47,11 +49,20 @@ func (r *request) setParam(key string, value interface{}) *request { return r } -// setParams set params with key/values to query string +// setParams set params with key/values to query string or body func (r *request) setParams(m params) *request { - for k, v := range m { - r.setParam(k, v) + if r.method == http.MethodGet { + for k, v := range m { + r.setParam(k, v) + } + } else if r.method == http.MethodPost { + jsonData, err := json.Marshal(m) + if err != nil { + log.Fatal(err) + } + r.params = jsonData } + return r } @@ -59,13 +70,10 @@ func (r *request) validate() (err error) { if r.query == nil { r.query = url.Values{} } - if r.form == nil { - r.form = url.Values{} - } return nil } -// Append `WithRecvWindow(insert_recvwindow)` to request to modify the default recvWindow value +// WithRecvWindow Append `WithRecvWindow(insert_recvWindow)` to request to modify the default recvWindow value func WithRecvWindow(recvWindow string) RequestOption { return func(r *request) { r.recvWindow = recvWindow diff --git a/trade.go b/trade.go index 06c9e41..530c031 100644 --- a/trade.go +++ b/trade.go @@ -1 +1,251 @@ package bybit_connector + +import ( + "context" + "encoding/json" + "net/http" +) + +type OrderResult struct { + OrderId string `json:"orderId"` + OrderLinkId string `json:"orderLinkId"` +} + +type Order struct { + c *Client + category string + symbol string + isLeverage *int + side string + orderType string + qty string + price *string + triggerDirection *int + orderFilter *string + triggerPrice *string + triggerBy *string + orderIv *string + timeInForce *string + positionIdx *int + orderLinkId *string + takeProfit *string + stopLoss *string + tpTriggerBy *string + slTriggerBy *string + reduceOnly *bool + closeOnTrigger *bool + smpType *string + mmp *bool + tpslMode *string + tpLimitPrice *string + slLimitPrice *string + tpOrderType *string + slOrderType *string +} + +func (o *Order) TimeInForce(tif string) *Order { + o.timeInForce = &tif + return o +} + +func (s *Order) IsLeverage(isLeverage int) *Order { + s.isLeverage = &isLeverage + return s +} + +func (s *Order) TriggerPrice(triggerPrice string) *Order { + s.triggerPrice = &triggerPrice + return s +} + +func (s *Order) OrderLinkId(orderLinkId string) *Order { + s.orderLinkId = &orderLinkId + return s +} + +func (o *Order) Price(price string) *Order { + o.price = &price + return o +} + +func (o *Order) TriggerDirection(direction int) *Order { + o.triggerDirection = &direction + return o +} + +func (o *Order) OrderFilter(filter string) *Order { + o.orderFilter = &filter + return o +} + +func (o *Order) TriggerBy(triggerBy string) *Order { + o.triggerBy = &triggerBy + return o +} + +func (o *Order) OrderIv(iv string) *Order { + o.orderIv = &iv + return o +} + +func (o *Order) PositionIdx(idx int) *Order { + o.positionIdx = &idx + return o +} + +func (o *Order) TakeProfit(profit string) *Order { + o.takeProfit = &profit + return o +} + +func (o *Order) StopLoss(loss string) *Order { + o.stopLoss = &loss + return o +} + +func (o *Order) TpTriggerBy(triggerBy string) *Order { + o.tpTriggerBy = &triggerBy + return o +} + +func (o *Order) SlTriggerBy(triggerBy string) *Order { + o.slTriggerBy = &triggerBy + return o +} + +func (o *Order) ReduceOnly(reduce bool) *Order { + o.reduceOnly = &reduce + return o +} + +func (o *Order) CloseOnTrigger(close bool) *Order { + o.closeOnTrigger = &close + return o +} + +func (o *Order) SmpType(smp string) *Order { + o.smpType = &smp + return o +} + +func (o *Order) Mmp(mmp bool) *Order { + o.mmp = &mmp + return o +} + +func (o *Order) TpslMode(mode string) *Order { + o.tpslMode = &mode + return o +} + +func (o *Order) TpLimitPrice(price string) *Order { + o.tpLimitPrice = &price + return o +} + +func (o *Order) SlLimitPrice(price string) *Order { + o.slLimitPrice = &price + return o +} + +func (o *Order) TpOrderType(orderType string) *Order { + o.tpOrderType = &orderType + return o +} + +func (o *Order) SlOrderType(orderType string) *Order { + o.slOrderType = &orderType + return o +} + +func (s *Order) Do(ctx context.Context, opts ...RequestOption) (*ServerResponse, error) { + r := &request{ + method: http.MethodPost, + endpoint: "/v5/order/create", + secType: secTypeSigned, + } + m := params{ + "category": s.category, + "symbol": s.symbol, + "side": s.side, + "orderType": s.orderType, + "qty": s.qty, + } + if s.price != nil { + m["price"] = *s.price + } + if s.triggerDirection != nil { + m["triggerDirection"] = *s.triggerDirection + } + if s.orderFilter != nil { + m["orderFilter"] = *s.orderFilter + } + if s.triggerPrice != nil { + m["triggerPrice"] = *s.triggerPrice + } + if s.triggerBy != nil { + m["triggerBy"] = *s.triggerBy + } + if s.orderIv != nil { + m["orderIv"] = *s.orderIv + } + if s.timeInForce != nil { + m["timeInForce"] = *s.timeInForce + } + if s.positionIdx != nil { + m["positionIdx"] = *s.positionIdx + } + if s.orderLinkId != nil { + m["orderLinkId"] = *s.orderLinkId + } + if s.takeProfit != nil { + m["takeProfit"] = *s.takeProfit + } + if s.stopLoss != nil { + m["stopLoss"] = *s.stopLoss + } + if s.tpTriggerBy != nil { + m["tpTriggerBy"] = *s.tpTriggerBy + } + if s.slTriggerBy != nil { + m["slTriggerBy"] = *s.slTriggerBy + } + if s.reduceOnly != nil { + m["reduceOnly"] = *s.reduceOnly + } + if s.closeOnTrigger != nil { + m["closeOnTrigger"] = *s.closeOnTrigger + } + if s.smpType != nil { + m["smpType"] = *s.smpType + } + if s.mmp != nil { + m["mmp"] = *s.mmp + } + if s.tpslMode != nil { + m["tpslMode"] = *s.tpslMode + } + if s.tpLimitPrice != nil { + m["tpLimitPrice"] = *s.tpLimitPrice + } + if s.slLimitPrice != nil { + m["slLimitPrice"] = *s.slLimitPrice + } + if s.tpOrderType != nil { + m["tpOrderType"] = *s.tpOrderType + } + if s.slOrderType != nil { + m["slOrderType"] = *s.slOrderType + } + r.setParams(m) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res := new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} From 19e79e0676ba1298b4bc584f72ba88447871e402 Mon Sep 17 00:00:00 2001 From: wuhewuhe Date: Sun, 5 Nov 2023 22:35:56 +0100 Subject: [PATCH 2/2] market endpoints --- bybit_api_client.go | 44 +++- examples/Trade/place_trade.go | 22 ++ examples/market/Instrument_info.go | 25 +++ examples/market/index_kline.go | 25 +++ examples/market/server_time.go | 2 +- examples/websocket/private_ws.go | 2 +- examples/websocket/public_ws.go | 2 +- handlers/params_verification.go | 23 ++ market.go | 323 ++++++++++++++++++++++------- market_klines.go | 67 ++++++ models/marketResponse.go | 197 ++++++++++++++++++ trade.go | 270 +++++++++++++----------- 12 files changed, 800 insertions(+), 202 deletions(-) create mode 100644 examples/Trade/place_trade.go create mode 100644 examples/market/Instrument_info.go create mode 100644 examples/market/index_kline.go create mode 100644 handlers/params_verification.go create mode 100644 market_klines.go create mode 100644 models/marketResponse.go diff --git a/bybit_api_client.go b/bybit_api_client.go index 8cbc2ee..20f8a4a 100644 --- a/bybit_api_client.go +++ b/bybit_api_client.go @@ -67,6 +67,13 @@ func (c *Client) debug(format string, v ...interface{}) { } } +func GetCurrentTime() int64 { + now := time.Now() + unixNano := now.UnixNano() + timeStamp := unixNano / int64(time.Millisecond) + return timeStamp +} + // NewBybitHttpClient NewClient Create client function for initialising new Bybit client func NewBybitHttpClient(apiKey string, APISecret string, options ...ClientOption) *Client { c := &Client{ @@ -109,9 +116,7 @@ func (c *Client) parseRequest(r *request, opts ...RequestOption) (err error) { header.Set("User-Agent", fmt.Sprintf("%s/%s", Name, Version)) if r.secType == secTypeSigned { - now := time.Now() - unixNano := now.UnixNano() - timeStamp := unixNano / 1000000 + timeStamp := GetCurrentTime() header.Set(signTypeKey, "2") header.Set(apiRequestKey, c.APIKey) header.Set(timestampKey, strconv.FormatInt(timeStamp, 10)) @@ -188,11 +193,6 @@ func (c *Client) callAPI(ctx context.Context, r *request, opts ...RequestOption) return data, nil } -// NewServerTimeService Market Endpoints -func (c *Client) NewServerTimeService() *ServerTime { - return &ServerTime{c: c} -} - // NewMarketKlineService Market Endpoints func (c *Client) NewMarketKlineService(klineType, category, symbol, interval string) *Klines { return &Klines{ @@ -204,6 +204,27 @@ func (c *Client) NewMarketKlineService(klineType, category, symbol, interval str } } +func (c *Client) NewMarketKLinesService(klineType string, params map[string]interface{}) *MarketClient { + return &MarketClient{ + c: c, + klineType: klineType, + params: params, + } +} + +func (c *Client) NewMarketInfoServiceNoParams() *MarketClient { + return &MarketClient{ + c: c, + } +} + +func (c *Client) NewMarketInfoService(params map[string]interface{}) *MarketClient { + return &MarketClient{ + c: c, + params: params, + } +} + // NewPlaceOrderService Trade Endpoints func (c *Client) NewPlaceOrderService(category, symbol, side, orderType, qty string) *Order { return &Order{ @@ -215,3 +236,10 @@ func (c *Client) NewPlaceOrderService(category, symbol, side, orderType, qty str qty: qty, } } + +func (c *Client) NewPlaceTradeService(params map[string]interface{}) *Trade { + return &Trade{ + c: c, + params: params, + } +} diff --git a/examples/Trade/place_trade.go b/examples/Trade/place_trade.go new file mode 100644 index 0000000..4da2bb4 --- /dev/null +++ b/examples/Trade/place_trade.go @@ -0,0 +1,22 @@ +package main + +import ( + "context" + "fmt" + bybit "github.com/wuhewuhe/bybit.go.api" +) + +func main() { + PlaceTrade() +} + +func PlaceTrade() { + client := bybit.NewBybitHttpClient("8wYkmpLsMg10eNQyPm", "Ouxc34myDnXvei54XsBZgoQzfGxO4bkr2Zsj", bybit.WithBaseURL(bybit.TESTNET)) + params := map[string]interface{}{"category": "linear", "symbol": "BTCUSDT", "side": "Buy", "positionIdx": 0, "orderType": "Limit", "qty": "0.001", "price": "10000", "timeInForce": "GTC"} + orderResult, err := client.NewPlaceTradeService(params).Do(context.Background()) + if err != nil { + fmt.Println(err) + return + } + fmt.Println(bybit.PrettyPrint(orderResult)) +} diff --git a/examples/market/Instrument_info.go b/examples/market/Instrument_info.go new file mode 100644 index 0000000..a65f0d8 --- /dev/null +++ b/examples/market/Instrument_info.go @@ -0,0 +1,25 @@ +package main + +import ( + "context" + "fmt" + bybit "github.com/wuhewuhe/bybit.go.api" +) + +func main() { + InstrumentInfo() +} + +func InstrumentInfo() { + + client := bybit.NewBybitHttpClient("", "") + + // NewServerTimeService + params := map[string]interface{}{"category": "linear", "symbol": "BTCUSDT", "status": "Trading"} + marketKline, err := client.NewMarketInfoService(params).GetInstrumentInfo(context.Background()) + if err != nil { + fmt.Println(err) + return + } + fmt.Println(bybit.PrettyPrint(marketKline)) +} diff --git a/examples/market/index_kline.go b/examples/market/index_kline.go new file mode 100644 index 0000000..725475e --- /dev/null +++ b/examples/market/index_kline.go @@ -0,0 +1,25 @@ +package main + +import ( + "context" + "fmt" + bybit "github.com/wuhewuhe/bybit.go.api" +) + +func main() { + IndexKline() +} + +func IndexKline() { + + client := bybit.NewBybitHttpClient("", "") + + // NewServerTimeService + params := map[string]interface{}{"category": "linear", "symbol": "BTCUSDT", "interval": "1", "Limit": 2} + marketKline, err := client.NewMarketKLinesService("index-price-kline", params).GetMarketKline(context.Background()) + if err != nil { + fmt.Println(err) + return + } + fmt.Println(bybit.PrettyPrint(marketKline)) +} diff --git a/examples/market/server_time.go b/examples/market/server_time.go index 054cb5f..22ccabe 100644 --- a/examples/market/server_time.go +++ b/examples/market/server_time.go @@ -15,7 +15,7 @@ func ServerTime() { client := bybit.NewBybitHttpClient("", "") // NewServerTimeService - serverTime, err := client.NewServerTimeService().Do(context.Background()) + serverTime, err := client.NewMarketInfoServiceNoParams().GetServerTime(context.Background()) if err != nil { fmt.Println(err) return diff --git a/examples/websocket/private_ws.go b/examples/websocket/private_ws.go index b0bf487..fe00528 100644 --- a/examples/websocket/private_ws.go +++ b/examples/websocket/private_ws.go @@ -1,8 +1,8 @@ package main import ( - bybit "bybit.go.api" "fmt" + bybit "github.com/wuhewuhe/bybit.go.api" ) func main() { diff --git a/examples/websocket/public_ws.go b/examples/websocket/public_ws.go index 30e23ef..4524bd5 100644 --- a/examples/websocket/public_ws.go +++ b/examples/websocket/public_ws.go @@ -1,8 +1,8 @@ package main import ( - bybit "bybit.go.api" "fmt" + bybit "github.com/wuhewuhe/bybit.go.api" ) func main() { diff --git a/handlers/params_verification.go b/handlers/params_verification.go new file mode 100644 index 0000000..6ced07b --- /dev/null +++ b/handlers/params_verification.go @@ -0,0 +1,23 @@ +package handlers + +import ( + "fmt" +) + +func ValidateParams(params map[string]interface{}) error { + seenKeys := make(map[string]bool) + + for key, value := range params { + if key == "" { + return fmt.Errorf("empty key found in parameters") + } + if seenKeys[key] { + return fmt.Errorf("duplicate key found in parameters: %s", key) + } + if value == nil { + return fmt.Errorf("parameter for key '%s' is nil", key) + } + seenKeys[key] = true + } + return nil +} diff --git a/market.go b/market.go index b519089..55f7996 100644 --- a/market.go +++ b/market.go @@ -3,21 +3,17 @@ package bybit_connector import ( "context" "encoding/json" + "github.com/wuhewuhe/bybit.go.api/handlers" "net/http" ) -type ServerTimeResult struct { - TimeSecond string `json:"timeSecond"` - TimeNano string `json:"timeNano"` +type MarketClient struct { + c *Client + klineType string + params map[string]interface{} } -// ServerTime Binance Check Server Time endpoint (GET /v5/market/time) -type ServerTime struct { - c *Client -} - -// Do Send the request -func (s *ServerTime) Do(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { +func (s *MarketClient) GetServerTime(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { r := &request{ method: http.MethodGet, endpoint: "/v5/market/time", @@ -35,71 +31,258 @@ func (s *ServerTime) Do(ctx context.Context, opts ...RequestOption) (res *Server return res, nil } -type MarketKlineCandle struct { - StartTime string `json:"startTime"` - OpenPrice string `json:"openPrice"` - HighPrice string `json:"highPrice"` - LowPrice string `json:"lowPrice"` - ClosePrice string `json:"closePrice"` - Volume string `json:"volume"` - Turnover string `json:"turnover"` -} - -type MarketKlineResponse struct { - Category string `json:"category"` - Symbol string `json:"symbol"` - List []MarketKlineCandle `json:"list"` -} - -// Klines Market Kline (GET /v5/market/kline) -type Klines struct { - c *Client - klineType string - category string - symbol string - interval string - limit *int - start *uint64 - end *uint64 -} - -// Limit set limit -func (s *Klines) Limit(limit int) *Klines { - s.limit = &limit - return s -} - -// Start set startTime -func (s *Klines) Start(startTime uint64) *Klines { - s.start = &startTime - return s -} - -// End set endTime -func (s *Klines) End(endTime uint64) *Klines { - s.end = &endTime - return s -} - -// Do Send the request -func (s *Klines) Do(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { +func (s *MarketClient) GetMarketKline(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } r := &request{ method: http.MethodGet, endpoint: "/v5/market/" + s.klineType, secType: secTypeNone, } - r.setParam("category", s.category) - r.setParam("symbol", s.symbol) - r.setParam("interval", s.interval) - if s.limit != nil { - r.setParam("limit", *s.limit) - } - if s.start != nil { - r.setParam("start", *s.start) - } - if s.end != nil { - r.setParam("end", *s.end) - } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *MarketClient) GetInstrumentInfo(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodGet, + endpoint: "/v5/market/instruments-info", + secType: secTypeNone, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *MarketClient) GetOrderBookInfo(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodGet, + endpoint: "/v5/market/orderbook", + secType: secTypeNone, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *MarketClient) GetMarketTickers(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodGet, + endpoint: "/v5/market/tickers", + secType: secTypeNone, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *MarketClient) GetFundingRates(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodGet, + endpoint: "/v5/market/tickers", + secType: secTypeNone, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *MarketClient) GetPublicRecentTrades(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodGet, + endpoint: "/v5/market/recent-trade", + secType: secTypeNone, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *MarketClient) GetOpenInterests(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodGet, + endpoint: "/v5/market/open-interest", + secType: secTypeNone, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *MarketClient) GetHistoricalVolatility(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodGet, + endpoint: "/v5/market/historical-volatility", + secType: secTypeNone, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *MarketClient) GetInsuranceInfo(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodGet, + endpoint: "/v5/market/insurance", + secType: secTypeNone, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *MarketClient) GetRiskLimit(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodGet, + endpoint: "/v5/market/risk-limit", + secType: secTypeNone, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *MarketClient) GetDeliveryPrice(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodGet, + endpoint: "/v5/market/delivery-price", + secType: secTypeNone, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *MarketClient) GetMarketLSRatio(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodGet, + endpoint: "/v5/market/account-ratio", + secType: secTypeNone, + } + r.setParams(s.params) data, err := s.c.callAPI(ctx, r, opts...) if err != nil { return nil, err diff --git a/market_klines.go b/market_klines.go new file mode 100644 index 0000000..c0929d8 --- /dev/null +++ b/market_klines.go @@ -0,0 +1,67 @@ +package bybit_connector + +import ( + "context" + "encoding/json" + "net/http" +) + +// Klines Market Kline (GET /v5/market/kline) +type Klines struct { + c *Client + klineType string + category string + symbol string + interval string + limit *int + start *uint64 + end *uint64 +} + +// Limit set limit +func (s *Klines) Limit(limit int) *Klines { + s.limit = &limit + return s +} + +// Start set startTime +func (s *Klines) Start(startTime uint64) *Klines { + s.start = &startTime + return s +} + +// End set endTime +func (s *Klines) End(endTime uint64) *Klines { + s.end = &endTime + return s +} + +func (s *Klines) Do(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + r := &request{ + method: http.MethodGet, + endpoint: "/v5/market/" + s.klineType, + secType: secTypeNone, + } + r.setParam("category", s.category) + r.setParam("symbol", s.symbol) + r.setParam("interval", s.interval) + if s.limit != nil { + r.setParam("limit", *s.limit) + } + if s.start != nil { + r.setParam("start", *s.start) + } + if s.end != nil { + r.setParam("end", *s.end) + } + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} diff --git a/models/marketResponse.go b/models/marketResponse.go new file mode 100644 index 0000000..65e7a0c --- /dev/null +++ b/models/marketResponse.go @@ -0,0 +1,197 @@ +package models + +type ServerTimeResult struct { + TimeSecond string `json:"timeSecond"` + TimeNano string `json:"timeNano"` +} + +type MarketKlineCandle struct { + StartTime string `json:"startTime"` + OpenPrice string `json:"openPrice"` + HighPrice string `json:"highPrice"` + LowPrice string `json:"lowPrice"` + ClosePrice string `json:"closePrice"` + Volume string `json:"volume"` + Turnover string `json:"turnover"` +} + +type MarketKlineResponse struct { + Category string `json:"category"` + Symbol string `json:"symbol"` + List []MarketKlineCandle `json:"list"` +} + +type InstrumentInfo struct { + Category string `json:"category"` + NextPageCursor string `json:"nextPageCursor"` + List []Instrument `json:"list"` +} + +type Instrument struct { + Symbol string `json:"symbol"` + ContractType string `json:"contractType"` + Status string `json:"status"` + BaseCoin string `json:"baseCoin"` + QuoteCoin string `json:"quoteCoin"` + LaunchTime string `json:"launchTime"` + DeliveryTime string `json:"deliveryTime"` + DeliveryFeeRate string `json:"deliveryFeeRate"` + PriceScale string `json:"priceScale"` + LeverageFilter LeverageFilter `json:"leverageFilter"` + PriceFilter PriceFilter `json:"priceFilter"` + LotSizeFilter LotSizeFilter `json:"lotSizeFilter"` + UnifiedMarginTrade bool `json:"unifiedMarginTrade"` + FundingInterval int `json:"fundingInterval"` + SettleCoin string `json:"settleCoin"` + CopyTrading string `json:"copyTrading"` +} + +type LeverageFilter struct { + MinLeverage string `json:"minLeverage"` + MaxLeverage string `json:"maxLeverage"` + LeverageStep string `json:"leverageStep"` +} + +type PriceFilter struct { + MinPrice string `json:"minPrice"` + MaxPrice string `json:"maxPrice"` + TickSize string `json:"tickSize"` +} + +type LotSizeFilter struct { + MaxOrderQty string `json:"maxOrderQty"` + MinOrderQty string `json:"minOrderQty"` + QtyStep string `json:"qtyStep"` + PostOnlyMaxOrderQty string `json:"postOnlyMaxOrderQty"` +} + +type OrderBookEntry struct { + Price string `json:"0"` + Size string `json:"1"` +} + +type OrderBookInfo struct { + Symbol string `json:"s"` + Bids []OrderBookEntry `json:"b"` + Asks []OrderBookEntry `json:"a"` + Timestamp int64 `json:"ts"` + UpdateID int64 `json:"u"` +} + +type TickerInfo struct { + Symbol string `json:"symbol"` + LastPrice string `json:"lastPrice"` + IndexPrice string `json:"indexPrice"` + MarkPrice string `json:"markPrice"` + PrevPrice24h string `json:"prevPrice24h"` + Price24hPcnt string `json:"price24hPcnt"` + HighPrice24h string `json:"highPrice24h"` + LowPrice24h string `json:"lowPrice24h"` + PrevPrice1h string `json:"prevPrice1h"` + OpenInterest string `json:"openInterest"` + OpenInterestValue string `json:"openInterestValue"` + Turnover24h string `json:"turnover24h"` + Volume24h string `json:"volume24h"` + FundingRate string `json:"fundingRate"` + NextFundingTime string `json:"nextFundingTime"` + PredictedDeliveryPrice string `json:"predictedDeliveryPrice"` + BasisRate string `json:"basisRate"` + Basis string `json:"basis"` + DeliveryFeeRate string `json:"deliveryFeeRate"` + DeliveryTime string `json:"deliveryTime"` + Ask1Size string `json:"ask1Size"` + Bid1Price string `json:"bid1Price"` + Ask1Price string `json:"ask1Price"` + Bid1Size string `json:"bid1Size"` +} + +type MarketTickers struct { + Category string `json:"category"` + List []TickerInfo `json:"list"` +} + +type FundingRateInfo struct { + Symbol string `json:"symbol"` + FundingRate string `json:"fundingRate"` + FundingRateTimestamp string `json:"fundingRateTimestamp"` +} + +type FundingRate struct { + Category string `json:"category"` + List []FundingRateInfo `json:"list"` +} + +type TradeInfo struct { + ExecId string `json:"execId"` + Symbol string `json:"symbol"` + Price string `json:"price"` + Size string `json:"size"` + Side string `json:"side"` + Time string `json:"time"` + IsBlockTrade bool `json:"isBlockTrade"` +} + +type PublicRecentTradeHistory struct { + Category string `json:"category"` + List []TradeInfo `json:"list"` +} + +type VolatilityData struct { + Period int `json:"period"` + Value string `json:"value"` + Time string `json:"time"` +} + +type HistoricalVolatilityInfo struct { + Category string `json:"category"` + List []VolatilityData `json:"list"` +} + +type InsuranceData struct { + Coin string `json:"coin"` + Balance string `json:"balance"` + Value string `json:"value"` +} + +type MarketInsuranceInfo struct { + UpdatedTime string `json:"updatedTime"` + List []InsuranceData `json:"list"` +} + +type RiskLimitData struct { + Id int `json:"id"` + Symbol string `json:"symbol"` + RiskLimitValue string `json:"riskLimitValue"` + MaintenanceMargin float64 `json:"maintenanceMargin"` + InitialMargin float64 `json:"initialMargin"` + IsLowestRisk int `json:"isLowestRisk"` + MaxLeverage string `json:"maxLeverage"` +} + +type MarketRiskLimitInfo struct { + Category string `json:"category"` + List []RiskLimitData `json:"list"` +} + +type DeliveryPriceData struct { + Symbol string `json:"symbol"` + DeliveryPrice string `json:"deliveryPrice"` + DeliveryTime string `json:"deliveryTime"` +} + +type DeliveryPriceInfo struct { + Category string `json:"category"` + List []DeliveryPriceData `json:"list"` + NextPageCursor string `json:"nextPageCursor"` +} + +type LongShortRatioData struct { + Symbol string `json:"symbol"` + BuyRatio string `json:"buyRatio"` + SellRatio string `json:"sellRatio"` + Timestamp string `json:"timestamp"` +} + +type MarketLongShortRatioInfo struct { + List []LongShortRatioData `json:"list"` +} diff --git a/trade.go b/trade.go index 530c031..0d89be2 100644 --- a/trade.go +++ b/trade.go @@ -3,6 +3,7 @@ package bybit_connector import ( "context" "encoding/json" + "github.com/wuhewuhe/bybit.go.api/handlers" "net/http" ) @@ -11,6 +12,11 @@ type OrderResult struct { OrderLinkId string `json:"orderLinkId"` } +type Trade struct { + c *Client + params map[string]interface{} +} + type Order struct { c *Client category string @@ -43,206 +49,228 @@ type Order struct { slOrderType *string } -func (o *Order) TimeInForce(tif string) *Order { - o.timeInForce = &tif - return o +func (order *Order) TimeInForce(tif string) *Order { + order.timeInForce = &tif + return order } -func (s *Order) IsLeverage(isLeverage int) *Order { - s.isLeverage = &isLeverage - return s +func (order *Order) IsLeverage(isLeverage int) *Order { + order.isLeverage = &isLeverage + return order } -func (s *Order) TriggerPrice(triggerPrice string) *Order { - s.triggerPrice = &triggerPrice - return s +func (order *Order) TriggerPrice(triggerPrice string) *Order { + order.triggerPrice = &triggerPrice + return order } -func (s *Order) OrderLinkId(orderLinkId string) *Order { - s.orderLinkId = &orderLinkId - return s +func (order *Order) OrderLinkId(orderLinkId string) *Order { + order.orderLinkId = &orderLinkId + return order } -func (o *Order) Price(price string) *Order { - o.price = &price - return o +func (order *Order) Price(price string) *Order { + order.price = &price + return order } -func (o *Order) TriggerDirection(direction int) *Order { - o.triggerDirection = &direction - return o +func (order *Order) TriggerDirection(direction int) *Order { + order.triggerDirection = &direction + return order } -func (o *Order) OrderFilter(filter string) *Order { - o.orderFilter = &filter - return o +func (order *Order) OrderFilter(filter string) *Order { + order.orderFilter = &filter + return order } -func (o *Order) TriggerBy(triggerBy string) *Order { - o.triggerBy = &triggerBy - return o +func (order *Order) TriggerBy(triggerBy string) *Order { + order.triggerBy = &triggerBy + return order } -func (o *Order) OrderIv(iv string) *Order { - o.orderIv = &iv - return o +func (order *Order) OrderIv(iv string) *Order { + order.orderIv = &iv + return order } -func (o *Order) PositionIdx(idx int) *Order { - o.positionIdx = &idx - return o +func (order *Order) PositionIdx(idx int) *Order { + order.positionIdx = &idx + return order } -func (o *Order) TakeProfit(profit string) *Order { - o.takeProfit = &profit - return o +func (order *Order) TakeProfit(profit string) *Order { + order.takeProfit = &profit + return order } -func (o *Order) StopLoss(loss string) *Order { - o.stopLoss = &loss - return o +func (order *Order) StopLoss(loss string) *Order { + order.stopLoss = &loss + return order } -func (o *Order) TpTriggerBy(triggerBy string) *Order { - o.tpTriggerBy = &triggerBy - return o +func (order *Order) TpTriggerBy(triggerBy string) *Order { + order.tpTriggerBy = &triggerBy + return order } -func (o *Order) SlTriggerBy(triggerBy string) *Order { - o.slTriggerBy = &triggerBy - return o +func (order *Order) SlTriggerBy(triggerBy string) *Order { + order.slTriggerBy = &triggerBy + return order } -func (o *Order) ReduceOnly(reduce bool) *Order { - o.reduceOnly = &reduce - return o +func (order *Order) ReduceOnly(reduce bool) *Order { + order.reduceOnly = &reduce + return order } -func (o *Order) CloseOnTrigger(close bool) *Order { - o.closeOnTrigger = &close - return o +func (order *Order) CloseOnTrigger(close bool) *Order { + order.closeOnTrigger = &close + return order } -func (o *Order) SmpType(smp string) *Order { - o.smpType = &smp - return o +func (order *Order) SmpType(smp string) *Order { + order.smpType = &smp + return order } -func (o *Order) Mmp(mmp bool) *Order { - o.mmp = &mmp - return o +func (order *Order) Mmp(mmp bool) *Order { + order.mmp = &mmp + return order } -func (o *Order) TpslMode(mode string) *Order { - o.tpslMode = &mode - return o +func (order *Order) TpslMode(mode string) *Order { + order.tpslMode = &mode + return order } -func (o *Order) TpLimitPrice(price string) *Order { - o.tpLimitPrice = &price - return o +func (order *Order) TpLimitPrice(price string) *Order { + order.tpLimitPrice = &price + return order } -func (o *Order) SlLimitPrice(price string) *Order { - o.slLimitPrice = &price - return o +func (order *Order) SlLimitPrice(price string) *Order { + order.slLimitPrice = &price + return order } -func (o *Order) TpOrderType(orderType string) *Order { - o.tpOrderType = &orderType - return o +func (order *Order) TpOrderType(orderType string) *Order { + order.tpOrderType = &orderType + return order } -func (o *Order) SlOrderType(orderType string) *Order { - o.slOrderType = &orderType - return o +func (order *Order) SlOrderType(orderType string) *Order { + order.slOrderType = &orderType + return order } -func (s *Order) Do(ctx context.Context, opts ...RequestOption) (*ServerResponse, error) { +func (order *Order) Do(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { r := &request{ method: http.MethodPost, endpoint: "/v5/order/create", secType: secTypeSigned, } m := params{ - "category": s.category, - "symbol": s.symbol, - "side": s.side, - "orderType": s.orderType, - "qty": s.qty, + "category": order.category, + "symbol": order.symbol, + "side": order.side, + "orderType": order.orderType, + "qty": order.qty, } - if s.price != nil { - m["price"] = *s.price + if order.price != nil { + m["price"] = *order.price } - if s.triggerDirection != nil { - m["triggerDirection"] = *s.triggerDirection + if order.triggerDirection != nil { + m["triggerDirection"] = *order.triggerDirection } - if s.orderFilter != nil { - m["orderFilter"] = *s.orderFilter + if order.orderFilter != nil { + m["orderFilter"] = *order.orderFilter } - if s.triggerPrice != nil { - m["triggerPrice"] = *s.triggerPrice + if order.triggerPrice != nil { + m["triggerPrice"] = *order.triggerPrice } - if s.triggerBy != nil { - m["triggerBy"] = *s.triggerBy + if order.triggerBy != nil { + m["triggerBy"] = *order.triggerBy } - if s.orderIv != nil { - m["orderIv"] = *s.orderIv + if order.orderIv != nil { + m["orderIv"] = *order.orderIv } - if s.timeInForce != nil { - m["timeInForce"] = *s.timeInForce + if order.timeInForce != nil { + m["timeInForce"] = *order.timeInForce } - if s.positionIdx != nil { - m["positionIdx"] = *s.positionIdx + if order.positionIdx != nil { + m["positionIdx"] = *order.positionIdx } - if s.orderLinkId != nil { - m["orderLinkId"] = *s.orderLinkId + if order.orderLinkId != nil { + m["orderLinkId"] = *order.orderLinkId } - if s.takeProfit != nil { - m["takeProfit"] = *s.takeProfit + if order.takeProfit != nil { + m["takeProfit"] = *order.takeProfit } - if s.stopLoss != nil { - m["stopLoss"] = *s.stopLoss + if order.stopLoss != nil { + m["stopLoss"] = *order.stopLoss } - if s.tpTriggerBy != nil { - m["tpTriggerBy"] = *s.tpTriggerBy + if order.tpTriggerBy != nil { + m["tpTriggerBy"] = *order.tpTriggerBy } - if s.slTriggerBy != nil { - m["slTriggerBy"] = *s.slTriggerBy + if order.slTriggerBy != nil { + m["slTriggerBy"] = *order.slTriggerBy } - if s.reduceOnly != nil { - m["reduceOnly"] = *s.reduceOnly + if order.reduceOnly != nil { + m["reduceOnly"] = *order.reduceOnly } - if s.closeOnTrigger != nil { - m["closeOnTrigger"] = *s.closeOnTrigger + if order.closeOnTrigger != nil { + m["closeOnTrigger"] = *order.closeOnTrigger } - if s.smpType != nil { - m["smpType"] = *s.smpType + if order.smpType != nil { + m["smpType"] = *order.smpType } - if s.mmp != nil { - m["mmp"] = *s.mmp + if order.mmp != nil { + m["mmp"] = *order.mmp } - if s.tpslMode != nil { - m["tpslMode"] = *s.tpslMode + if order.tpslMode != nil { + m["tpslMode"] = *order.tpslMode } - if s.tpLimitPrice != nil { - m["tpLimitPrice"] = *s.tpLimitPrice + if order.tpLimitPrice != nil { + m["tpLimitPrice"] = *order.tpLimitPrice } - if s.slLimitPrice != nil { - m["slLimitPrice"] = *s.slLimitPrice + if order.slLimitPrice != nil { + m["slLimitPrice"] = *order.slLimitPrice } - if s.tpOrderType != nil { - m["tpOrderType"] = *s.tpOrderType + if order.tpOrderType != nil { + m["tpOrderType"] = *order.tpOrderType } - if s.slOrderType != nil { - m["slOrderType"] = *s.slOrderType + if order.slOrderType != nil { + m["slOrderType"] = *order.slOrderType } r.setParams(m) - data, err := s.c.callAPI(ctx, r, opts...) + data, err := order.c.callAPI(ctx, r, opts...) if err != nil { return nil, err } - res := new(ServerResponse) + res = new(ServerResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +func (s *Trade) Do(ctx context.Context, opts ...RequestOption) (res *ServerResponse, err error) { + if err = handlers.ValidateParams(s.params); err != nil { + return nil, err + } + r := &request{ + method: http.MethodPost, + endpoint: "/v5/order/create", + secType: secTypeSigned, + } + r.setParams(s.params) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ServerResponse) err = json.Unmarshal(data, res) if err != nil { return nil, err