Files
gocryptotrader/exchanges/bybit/bybit_usdcfutures.go
Jaydeep Rajpurohit 247da918a8 exchanges: Add ByBit support (#887)
* few fixes and add ratelimiter

* adds test

* revert configtest.json changes

* configtest updated

* WIP: adds public endpoint support

* WIP: adds public endpoint support

* adds public endpoint support

* WIP: adds auth. endpoint support

* adds test for auth. endpoint

* fixes

* adds auth. endpoint support

* WIP: ws support

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* Testing

* Complete WS spot testing

* adds support for ws events

* minor change

* WIP: adds REST support for CoinMarginedFutures

* Fixes

* WIP: adds REST support for CoinMarginedFutures

* Fixes

* improvement in SPOT REST

* Typo fix

* WIP: add REST support for CMF Account API

* minor fixes

* WIP: add support for CMF conditional orders and few minor fixes

* complete support for CMF conditional orders

* adds support for public CMF endpoint

* adds support for CMF position API

* Complete REST CMF support

* WIP

* Testing REST CMF support

* Testing REST CMF support

* Testing REST CMF support completed

* WIP: add support for UMF

* completed non-auth UMF

* WIP: add support for REST Auth. UMF

* WIP: add support for REST Auth. UMF and some improvements

* WIP

* WIP

* WIP

* completed REST UMF

* renaming

* adds REST support for futures

* add testcases for UMF and some optimizations

* add testcases for futures

* Testing UMF, futures and its changes

* Fixes

* Fixes after testing

* WIP

* WIP

* WIP

* completed ws USDT futures support

* WIP: ws support for futures

* fixes in WS futures

* fixes in WS support

* roll back changes made for WS CMF, USDT and Futures

* fixes

* WIP

* WIP

* fixes

* Steps for new PR

* WIP

* WIP

* WIP

* WIP

* complete PR setup

* fixes for successfully running tests

* update in symbol for futures pair in test file

* WIP

* Fixes in test file and other minor fix

* fix testdata/configtest.json

* reset CONTRIBUTORS file

* review changes

* remove unwanted file

* remove redundant code

* improvisation

* adds comment for exported functions

* remove unwanted TODO and commented code

* fix

* improvisation

* fix

* defined errors

* improvisation

* improvisation

* improvisation

* updates test

* adds comment for exported types

* review changes

* review changes

* fix

* fixes

* Changes for making BYBIT compatible with existing code base

* Test file changes

* Changes for making BYBIT compatible with existing code base

* Changes for making BYBIT compatible with existing code base

* fix lint issues

* fix

* review changes

* review changes

* review changes

* review changes

* review changes

* review changes

* review changes

* review changes

* review changes

* review changes

* WIP

* add test cases for new API's

* minor improvements

* add missing API and their tests

* minor fixes

* add bybitTime

* add bybitTimeSec, bybitTimeMilliSec, bybitTimeNanoSec and necessary support

* fix GetTradeHistory function

* error handling

* test fixes

* add GetServerTime API

* adds GetHistoricCandlesExtended and review changes

* test fixes

* minor fix

* integrating CMF Bybit recent change log

* minor fixes

* adds extractCurrencyPair

* minor fixes

* minor fix

* review changes

* adds variable declaration of error

* review commit

* adds embeddable type in API response for all API and integrate it

* fixes

* adds authentication WS connection

* review changes

* review changes

* compatible changes

* adds asset to GetWithdrawalsHistory

* adds asset_type in rpc.proto

* adds asset argument in gctcli withdrawal request command

* improve error handling in exchange API error

* web socket fix

* review changes

* improvements

* improvements

* minor fix

* review changes

* fixing wrapper issues

* fixes

* fixes

* review changes

* add test cases

* fix for GetActiveOrders

* lint fixes

* fixes in websocket

* adds wrapper testcases

* adds wrapper testcases

* adds wrapper testcases

* fixes

* fix issue with GetHistoricCandlesExtended

* fix merge issues

* improving error reporting

* adds wrapper testcases and a minor fix

* gctrpc changes

* adds test cases
fixes in websocket

* review changes for ws

* review changes in WS

* fix gctrpc

* merge fixes

* review changes

* WIP

* updates pair in configs

* adds new asset USDCMarginedFutures

* adds URL const for USDCMarginedFutures

* adds API support

* minor fixes

* adds kline API

* minor fix

* adds API

* adds API

* adds API

* WIP

* WIP

* WIP

* adds support for USDC auth requests to SendAuthHTTPRequest

* adds SendUSDCAuthHTTPRequest

* run test and fix them

* rollback support added for Auth. USDC request inside SendAuthHTTPRequest

* adds API and test cases

* adds API and test cases

* adds APIs and test cases

* adds APIs

* adds rate limit for USDC

* adds USDCMarginedFutures to wrapper

* adds USDC testcases in wrapper and fix few issues

* minor test fixes

* minor test fixes

* fix lint issues

* WIP

* Merge changes

* minor fixes

* remove "else" and optimize

* review changes

* review changes

* review changes

* fix lint issue

* merge fix

* fix test

* fix templates and run them

* changes after merge

* review changes and improvements

* code improvement

* fixes with respect to changes in API response in documentation

* fixed review change in test

* adds check in CancelExistingOrder

* update exchange template

* review changes

* adds GetDepositAddress API

* WIP: adds GetOrderHistory

* complete GetOrderHistory

* fixes

* adds test case

* fixes and add WithdrawFund API

* WIP

* WIP

* updating all SendAuthHTTPRequest call

* adds WithdrawCryptocurrencyFunds

* update test cases

* fix lint issues

* fixes after merge

* adds GetAvailableTransferChains and few fixes

* minor fix in GetDepositAddress

* minor fix with WS ping/pong handling

* add ping handler for WS Auth.

* fix typo mistake

* update doc
2022-08-08 11:29:43 +10:00

1115 lines
33 KiB
Go

package bybit
import (
"bytes"
"context"
"encoding/json"
"errors"
"net/http"
"net/url"
"strconv"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
)
const (
// public endpoint
usdcfuturesGetOrderbook = "/perpetual/usdc/openapi/public/v1/order-book"
usdcfuturesGetContracts = "/perpetual/usdc/openapi/public/v1/symbols"
usdcfuturesGetSymbols = "/perpetual/usdc/openapi/public/v1/tick"
usdcfuturesGetKlines = "/perpetual/usdc/openapi/public/v1/kline/list"
usdcfuturesGetMarkPriceKlines = "/perpetual/usdc/openapi/public/v1/mark-price-kline"
usdcfuturesGetIndexPriceKlines = "/perpetual/usdc/openapi/public/v1/index-price-kline"
usdcfuturesGetPremiumIndexKlines = "/perpetual/usdc/openapi/public/v1/premium-index-kline"
usdcfuturesGetOpenInterest = "/perpetual/usdc/openapi/public/v1/open-interest"
usdcfuturesGetLargeOrders = "/perpetual/usdc/openapi/public/v1/big-deal"
usdcfuturesGetAccountRatio = "/perpetual/usdc/openapi/public/v1/account-ratio"
usdcfuturesGetLatestTrades = "/option/usdc/openapi/public/v1/query-trade-latest"
usdcfuturesGetLastFundingRate = "/perpetual/usdc/openapi/public/v1/prev-funding-rate"
usdcfuturesGetRiskLimit = "/perpetual/usdc/openapi/public/v1/risk-limit/list"
// auth endpoint
usdcfuturesPlaceOrder = "/perpetual/usdc/openapi/private/v1/place-order"
usdcfuturesModifyOrder = "/perpetual/usdc/openapi/private/v1/replace-order"
usdcfuturesCancelOrder = "/perpetual/usdc/openapi/private/v1/cancel-order"
usdcfuturesCancelAllActiveOrder = "/perpetual/usdc/openapi/private/v1/cancel-all"
usdcfuturesGetActiveOrder = "/option/usdc/openapi/private/v1/query-active-orders"
usdcfuturesGetOrderHistory = "/option/usdc/openapi/private/v1/query-order-history"
usdcfuturesGetTradeHistory = "/option/usdc/openapi/private/v1/execution-list"
usdcfuturesGetTransactionLog = "/option/usdc/openapi/private/v1/query-transaction-log"
usdcfuturesGetWalletBalance = "/option/usdc/openapi/private/v1/query-wallet-balance"
usdcfuturesGetAssetInfo = "/option/usdc/openapi/private/v1/query-asset-info"
usdcfuturesGetMarginInfo = "/option/usdc/openapi/private/v1/query-margin-info"
usdcfuturesGetPosition = "/option/usdc/openapi/private/v1/query-position"
usdcfuturesSetLeverage = "/perpetual/usdc/openapi/private/v1/position/leverage/save"
usdcfuturesGetSettlementHistory = "/option/usdc/openapi/private/v1/session-settlement"
usdcfuturesSetRiskLimit = "/perpetual/usdc/openapi/private/v1/position/set-risk-limit"
usdcfuturesGetPredictedFundingRate = "/perpetual/usdc/openapi/private/v1/predicted-funding"
)
// GetUSDCFuturesOrderbook gets orderbook data for USDCMarginedFutures.
func (by *Bybit) GetUSDCFuturesOrderbook(ctx context.Context, symbol currency.Pair) (*Orderbook, error) {
var resp Orderbook
data := struct {
Result []USDCOrderbookData `json:"result"`
USDCError
}{}
params := url.Values{}
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return nil, err
}
params.Set("symbol", symbolValue)
err = by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetOrderbook, params), usdcPublicRate, &data)
if err != nil {
return nil, err
}
for x := range data.Result {
switch data.Result[x].Side {
case sideBuy:
resp.Bids = append(resp.Bids, orderbook.Item{
Price: data.Result[x].Price,
Amount: data.Result[x].Size,
})
case sideSell:
resp.Asks = append(resp.Asks, orderbook.Item{
Price: data.Result[x].Price,
Amount: data.Result[x].Size,
})
default:
return nil, errInvalidSide
}
}
return &resp, nil
}
// GetUSDCContracts gets all contract information for USDCMarginedFutures.
func (by *Bybit) GetUSDCContracts(ctx context.Context, symbol currency.Pair, direction string, limit int64) ([]USDCContract, error) {
resp := struct {
Data []USDCContract `json:"result"`
USDCError
}{}
params := url.Values{}
if !symbol.IsEmpty() {
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Data, err
}
params.Set("symbol", symbolValue)
}
if direction != "" {
params.Set("direction", direction)
}
if limit > 0 && limit <= 200 {
params.Set("limit", strconv.FormatInt(limit, 10))
}
return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetContracts, params), usdcPublicRate, &resp)
}
// GetUSDCSymbols gets all symbol information for USDCMarginedFutures.
func (by *Bybit) GetUSDCSymbols(ctx context.Context, symbol currency.Pair) (USDCSymbol, error) {
resp := struct {
Data USDCSymbol `json:"result"`
USDCError
}{}
params := url.Values{}
if symbol.IsEmpty() {
return USDCSymbol{}, errSymbolMissing
}
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Data, err
}
params.Set("symbol", symbolValue)
return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetSymbols, params), usdcPublicRate, &resp)
}
// GetUSDCKlines gets kline of symbol for USDCMarginedFutures.
func (by *Bybit) GetUSDCKlines(ctx context.Context, symbol currency.Pair, period string, startTime time.Time, limit int64) ([]USDCKline, error) {
resp := struct {
Data []USDCKline `json:"result"`
USDCError
}{}
params := url.Values{}
if symbol.IsEmpty() {
return nil, errSymbolMissing
}
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Data, err
}
params.Set("symbol", symbolValue)
if !common.StringDataCompare(validFuturesIntervals, period) {
return resp.Data, errInvalidPeriod
}
params.Set("period", period)
if startTime.IsZero() {
return nil, errInvalidStartTime
}
params.Set("startTime", strconv.FormatInt(startTime.Unix(), 10))
if limit > 0 && limit <= 200 {
params.Set("limit", strconv.FormatInt(limit, 10))
}
return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetKlines, params), usdcPublicRate, &resp)
}
// GetUSDCMarkPriceKlines gets mark price kline of symbol for USDCMarginedFutures.
func (by *Bybit) GetUSDCMarkPriceKlines(ctx context.Context, symbol currency.Pair, period string, startTime time.Time, limit int64) ([]USDCKlineBase, error) {
resp := struct {
Data []USDCKlineBase `json:"result"`
USDCError
}{}
params := url.Values{}
if symbol.IsEmpty() {
return nil, errSymbolMissing
}
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Data, err
}
params.Set("symbol", symbolValue)
if !common.StringDataCompare(validFuturesIntervals, period) {
return resp.Data, errInvalidPeriod
}
params.Set("period", period)
if startTime.IsZero() {
return nil, errInvalidStartTime
}
params.Set("startTime", strconv.FormatInt(startTime.Unix(), 10))
if limit > 0 && limit <= 200 {
params.Set("limit", strconv.FormatInt(limit, 10))
}
return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetMarkPriceKlines, params), usdcPublicRate, &resp)
}
// GetUSDCIndexPriceKlines gets index price kline of symbol for USDCMarginedFutures.
func (by *Bybit) GetUSDCIndexPriceKlines(ctx context.Context, symbol currency.Pair, period string, startTime time.Time, limit int64) ([]USDCKlineBase, error) {
resp := struct {
Data []USDCKlineBase `json:"result"`
USDCError
}{}
params := url.Values{}
if symbol.IsEmpty() {
return nil, errSymbolMissing
}
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Data, err
}
params.Set("symbol", symbolValue)
if !common.StringDataCompare(validFuturesIntervals, period) {
return resp.Data, errInvalidPeriod
}
params.Set("period", period)
if startTime.IsZero() {
return nil, errInvalidStartTime
}
params.Set("startTime", strconv.FormatInt(startTime.Unix(), 10))
if limit > 0 && limit <= 200 {
params.Set("limit", strconv.FormatInt(limit, 10))
}
return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetIndexPriceKlines, params), usdcPublicRate, &resp)
}
// GetUSDCPremiumIndexKlines gets premium index kline of symbol for USDCMarginedFutures.
func (by *Bybit) GetUSDCPremiumIndexKlines(ctx context.Context, symbol currency.Pair, period string, startTime time.Time, limit int64) ([]USDCKlineBase, error) {
resp := struct {
Data []USDCKlineBase `json:"result"`
USDCError
}{}
params := url.Values{}
if symbol.IsEmpty() {
return nil, errSymbolMissing
}
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Data, err
}
params.Set("symbol", symbolValue)
if !common.StringDataCompare(validFuturesIntervals, period) {
return resp.Data, errInvalidPeriod
}
params.Set("period", period)
if startTime.IsZero() {
return nil, errInvalidStartTime
}
params.Set("startTime", strconv.FormatInt(startTime.Unix(), 10))
if limit > 0 && limit <= 200 {
params.Set("limit", strconv.FormatInt(limit, 10))
}
return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetPremiumIndexKlines, params), usdcPublicRate, &resp)
}
// GetUSDCOpenInterest gets open interest of symbol for USDCMarginedFutures.
func (by *Bybit) GetUSDCOpenInterest(ctx context.Context, symbol currency.Pair, period string, limit int64) ([]USDCOpenInterest, error) {
resp := struct {
Data []USDCOpenInterest `json:"result"`
USDCError
}{}
params := url.Values{}
if symbol.IsEmpty() {
return nil, errSymbolMissing
}
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Data, err
}
params.Set("symbol", symbolValue)
if !common.StringDataCompare(validFuturesPeriods, period) {
return resp.Data, errInvalidPeriod
}
params.Set("period", period)
if limit > 0 && limit <= 200 {
params.Set("limit", strconv.FormatInt(limit, 10))
}
return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetOpenInterest, params), usdcPublicRate, &resp)
}
// GetUSDCLargeOrders gets large order of symbol for USDCMarginedFutures.
func (by *Bybit) GetUSDCLargeOrders(ctx context.Context, symbol currency.Pair, limit int64) ([]USDCLargeOrder, error) {
resp := struct {
Data []USDCLargeOrder `json:"result"`
USDCError
}{}
params := url.Values{}
if symbol.IsEmpty() {
return nil, errSymbolMissing
}
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Data, err
}
params.Set("symbol", symbolValue)
if limit > 0 && limit <= 100 {
params.Set("limit", strconv.FormatInt(limit, 10))
}
return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetLargeOrders, params), usdcPublicRate, &resp)
}
// GetUSDCAccountRatio gets account long short ratio of symbol for USDCMarginedFutures.
func (by *Bybit) GetUSDCAccountRatio(ctx context.Context, symbol currency.Pair, period string, limit int64) ([]USDCAccountRatio, error) {
resp := struct {
Data []USDCAccountRatio `json:"result"`
USDCError
}{}
params := url.Values{}
if symbol.IsEmpty() {
return nil, errSymbolMissing
}
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Data, err
}
params.Set("symbol", symbolValue)
if !common.StringDataCompare(validFuturesPeriods, period) {
return resp.Data, errInvalidPeriod
}
params.Set("period", period)
if limit > 0 && limit <= 500 {
params.Set("limit", strconv.FormatInt(limit, 10))
}
return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetAccountRatio, params), usdcPublicRate, &resp)
}
// GetUSDCLatestTrades gets latest 500 trades for USDCMarginedFutures.
func (by *Bybit) GetUSDCLatestTrades(ctx context.Context, symbol currency.Pair, category string, limit int64) ([]USDCTrade, error) {
resp := struct {
Result struct {
ResultSize int64 `json:"resultTotalSize"`
Cursor string `json:"cursor"`
Data []USDCTrade `json:"dataList"`
} `json:"result"`
USDCError
}{}
params := url.Values{}
if category == "" {
return nil, errInvalidCategory
}
params.Set("category", category)
if !symbol.IsEmpty() {
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Result.Data, err
}
params.Set("symbol", symbolValue)
}
if limit > 0 && limit <= 500 {
params.Set("limit", strconv.FormatInt(limit, 10))
}
return resp.Result.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetLatestTrades, params), usdcPublicRate, &resp)
}
// PlaceUSDCOrder create new USDC derivatives order.
func (by *Bybit) PlaceUSDCOrder(ctx context.Context, symbol currency.Pair, orderType, orderFilter, side, timeInForce, orderLinkID string, orderPrice, orderQty, takeProfit, stopLoss, tptriggerby, slTriggerBy, triggerPrice, triggerBy float64, reduceOnly, closeOnTrigger, mmp bool) (USDCCreateOrderResp, error) {
resp := struct {
Result USDCCreateOrderResp `json:"result"`
USDCError
}{}
req := make(map[string]interface{})
if symbol.IsEmpty() {
return USDCCreateOrderResp{}, errSymbolMissing
}
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Result, err
}
req["symbol"] = symbolValue
if orderType == "" {
return USDCCreateOrderResp{}, errInvalidOrderType
}
req["orderType"] = orderType
if orderFilter == "" {
return USDCCreateOrderResp{}, errInvalidOrderFilter
}
req["orderFilter"] = orderFilter
if side == "" {
return USDCCreateOrderResp{}, errInvalidSide
}
req["side"] = side
if orderQty == 0 {
return USDCCreateOrderResp{}, errInvalidQuantity
}
req["orderQty"] = strconv.FormatFloat(orderQty, 'f', -1, 64)
if orderPrice != 0 {
req["orderPrice"] = strconv.FormatFloat(orderPrice, 'f', -1, 64)
}
if timeInForce != "" {
req["timeInForce"] = timeInForce
}
if orderLinkID != "" {
req["orderLinkId"] = orderLinkID
}
if reduceOnly {
req["reduceOnly"] = true
} else {
req["reduceOnly"] = false
}
if closeOnTrigger {
req["closeOnTrigger"] = true
} else {
req["closeOnTrigger"] = false
}
if mmp {
req["mmp"] = true
} else {
req["mmp"] = false
}
if takeProfit != 0 {
req["takeProfit"] = strconv.FormatFloat(takeProfit, 'f', -1, 64)
}
if stopLoss != 0 {
req["stopLoss"] = strconv.FormatFloat(stopLoss, 'f', -1, 64)
}
if tptriggerby != 0 {
req["tptriggerby"] = tptriggerby
}
if slTriggerBy != 0 {
req["slTriggerBy"] = strconv.FormatFloat(slTriggerBy, 'f', -1, 64)
}
if triggerPrice != 0 {
req["triggerPrice"] = strconv.FormatFloat(triggerPrice, 'f', -1, 64)
}
if triggerBy != 0 {
req["triggerBy"] = triggerBy
}
return resp.Result, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesPlaceOrder, req, &resp, usdcPlaceOrderRate)
}
// ModifyUSDCOrder modifies USDC derivatives order.
func (by *Bybit) ModifyUSDCOrder(ctx context.Context, symbol currency.Pair, orderFilter, orderID, orderLinkID string, orderPrice, orderQty, takeProfit, stopLoss, tptriggerby, slTriggerBy, triggerPrice float64) (string, error) {
resp := struct {
Result struct {
OrderID string `json:"orderId"`
OrderLinkedID string `json:"orderLinkId"`
} `json:"result"`
USDCError
}{}
req := make(map[string]interface{})
if symbol.IsEmpty() {
return resp.Result.OrderID, errSymbolMissing
}
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Result.OrderID, err
}
req["symbol"] = symbolValue
if orderFilter == "" {
return resp.Result.OrderID, errInvalidOrderFilter
}
req["orderFilter"] = orderFilter
if orderID == "" && orderLinkID == "" {
return resp.Result.OrderID, errOrderOrOrderLinkIDMissing
}
if orderID != "" {
req["orderId"] = orderID
}
if orderLinkID != "" {
req["orderLinkId"] = orderLinkID
}
if orderPrice != 0 {
req["orderPrice"] = strconv.FormatFloat(orderPrice, 'f', -1, 64)
}
if orderQty != 0 {
req["orderQty"] = strconv.FormatFloat(orderQty, 'f', -1, 64)
}
if takeProfit != 0 {
req["takeProfit"] = strconv.FormatFloat(takeProfit, 'f', -1, 64)
}
if stopLoss != 0 {
req["stopLoss"] = strconv.FormatFloat(stopLoss, 'f', -1, 64)
}
if tptriggerby != 0 {
req["tptriggerby"] = strconv.FormatFloat(tptriggerby, 'f', -1, 64)
}
if slTriggerBy != 0 {
req["slTriggerBy"] = strconv.FormatFloat(slTriggerBy, 'f', -1, 64)
}
if triggerPrice != 0 {
req["triggerPrice"] = strconv.FormatFloat(triggerPrice, 'f', -1, 64)
}
return resp.Result.OrderID, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesModifyOrder, req, &resp, usdcModifyOrderRate)
}
// CancelUSDCOrder cancels USDC derivatives order.
func (by *Bybit) CancelUSDCOrder(ctx context.Context, symbol currency.Pair, orderFilter, orderID, orderLinkID string) (string, error) {
resp := struct {
Result struct {
OrderID string `json:"orderId"`
} `json:"result"`
USDCError
}{}
req := make(map[string]interface{})
if symbol.IsEmpty() {
return resp.Result.OrderID, errSymbolMissing
}
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Result.OrderID, err
}
req["symbol"] = symbolValue
if orderFilter == "" {
return resp.Result.OrderID, errInvalidOrderFilter
}
req["orderFilter"] = orderFilter
if orderID == "" && orderLinkID == "" {
return resp.Result.OrderID, errOrderOrOrderLinkIDMissing
}
if orderID != "" {
req["orderId"] = orderID
}
if orderLinkID != "" {
req["orderLinkId"] = orderLinkID
}
return resp.Result.OrderID, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesCancelOrder, req, &resp, usdcCancelOrderRate)
}
// CancelAllActiveUSDCOrder cancels all active USDC derivatives order.
func (by *Bybit) CancelAllActiveUSDCOrder(ctx context.Context, symbol currency.Pair, orderFilter string) error {
req := make(map[string]interface{})
if symbol.IsEmpty() {
return errSymbolMissing
}
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return err
}
req["symbol"] = symbolValue
if orderFilter == "" {
return errInvalidOrderFilter
}
req["orderFilter"] = orderFilter
return by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesCancelAllActiveOrder, req, nil, usdcCancelAllOrderRate)
}
// GetActiveUSDCOrder gets all active USDC derivatives order.
func (by *Bybit) GetActiveUSDCOrder(ctx context.Context, symbol currency.Pair, category, orderID, orderLinkID, orderFilter, direction, cursor string, limit int64) ([]USDCOrder, error) {
resp := struct {
Result struct {
Cursor string `json:"cursor"`
ResultTotalSize int64 `json:"resultTotalSize"`
Data []USDCOrder `json:"dataList"`
} `json:"result"`
USDCError
}{}
req := make(map[string]interface{})
if !symbol.IsEmpty() {
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Result.Data, err
}
req["symbol"] = symbolValue
}
if category == "" {
return nil, errInvalidCategory
}
req["category"] = category
if orderID != "" {
req["orderId"] = orderID
}
if orderLinkID != "" {
req["orderLinkId"] = orderLinkID
}
if orderFilter != "" {
req["orderFilter"] = orderFilter
}
if direction != "" {
req["direction"] = direction
}
if limit != 0 {
req["limit"] = strconv.FormatInt(limit, 10)
}
if cursor != "" {
req["cursor"] = cursor
}
return resp.Result.Data, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetActiveOrder, req, &resp, usdcGetOrderRate)
}
// GetUSDCOrderHistory gets order history with support of last 30 days of USDC derivatives order.
func (by *Bybit) GetUSDCOrderHistory(ctx context.Context, symbol currency.Pair, category, orderID, orderLinkID, orderStatus, direction, cursor string, limit int64) ([]USDCOrderHistory, error) {
resp := struct {
Result struct {
Cursor string `json:"cursor"`
ResultTotalSize int64 `json:"resultTotalSize"`
Data []USDCOrderHistory `json:"dataList"`
} `json:"result"`
USDCError
}{}
req := make(map[string]interface{})
if !symbol.IsEmpty() {
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Result.Data, err
}
req["symbol"] = symbolValue
}
if category == "" {
return nil, errInvalidCategory
}
req["category"] = category
if orderID != "" {
req["orderId"] = orderID
}
if orderLinkID != "" {
req["orderLinkId"] = orderLinkID
}
if orderStatus != "" {
req["orderStatus"] = orderStatus
}
if direction != "" {
req["direction"] = direction
}
if limit != 0 {
req["limit"] = strconv.FormatInt(limit, 10)
}
if cursor != "" {
req["cursor"] = cursor
}
return resp.Result.Data, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetOrderHistory, req, &resp, usdcGetOrderHistoryRate)
}
// GetUSDCTradeHistory gets trade history with support of last 30 days of USDC derivatives trades.
func (by *Bybit) GetUSDCTradeHistory(ctx context.Context, symbol currency.Pair, category, orderID, orderLinkID, direction, cursor string, limit int64, startTime time.Time) ([]USDCTradeHistory, error) {
resp := struct {
Result struct {
Cursor string `json:"cursor"`
ResultTotalSize int64 `json:"resultTotalSize"`
Data []USDCTradeHistory `json:"dataList"`
} `json:"result"`
USDCError
}{}
req := make(map[string]interface{})
if !symbol.IsEmpty() {
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Result.Data, err
}
req["symbol"] = symbolValue
}
if category == "" {
return nil, errInvalidCategory
}
req["category"] = category
if orderID == "" && orderLinkID == "" {
return nil, errOrderOrOrderLinkIDMissing
}
if orderID != "" {
req["orderId"] = orderID
}
if orderLinkID != "" {
req["orderLinkId"] = orderLinkID
}
if startTime.IsZero() {
return nil, errInvalidStartTime
}
req["startTime"] = strconv.FormatInt(startTime.Unix(), 10)
if direction != "" {
req["direction"] = direction
}
if limit > 0 && limit <= 50 {
req["limit"] = strconv.FormatInt(limit, 10)
}
if cursor != "" {
req["cursor"] = cursor
}
return resp.Result.Data, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetTradeHistory, req, &resp, usdcGetTradeHistoryRate)
}
// GetUSDCTransactionLog gets transaction logs with support of last 30 days of USDC derivatives trades.
func (by *Bybit) GetUSDCTransactionLog(ctx context.Context, startTime, endTime time.Time, txType, category, direction, cursor string, limit int64) ([]USDCTxLog, error) {
resp := struct {
Result struct {
Cursor string `json:"cursor"`
ResultTotalSize int64 `json:"resultTotalSize"`
Data []USDCTxLog `json:"dataList"`
} `json:"result"`
USDCError
}{}
req := make(map[string]interface{})
if !startTime.IsZero() {
req["startTime"] = strconv.FormatInt(startTime.Unix(), 10)
}
if !endTime.IsZero() {
req["endTime"] = strconv.FormatInt(endTime.Unix(), 10)
}
if txType == "" {
return nil, errors.New("type missing")
}
req["type"] = txType
if category != "" {
req["category"] = category
}
if direction != "" {
req["direction"] = direction
}
if limit > 0 && limit <= 50 {
req["limit"] = strconv.FormatInt(limit, 10)
}
if cursor != "" {
req["cursor"] = cursor
}
return resp.Result.Data, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetTransactionLog, req, &resp, usdcGetTransactionRate)
}
// GetUSDCWalletBalance gets USDC wallet balance.
func (by *Bybit) GetUSDCWalletBalance(ctx context.Context) (USDCWalletBalance, error) {
resp := struct {
Result USDCWalletBalance `json:"result"`
USDCError
}{}
return resp.Result, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetWalletBalance, nil, &resp, usdcGetWalletRate)
}
// GetUSDCAssetInfo gets USDC asset information.
func (by *Bybit) GetUSDCAssetInfo(ctx context.Context, baseCoin string) ([]USDCAssetInfo, error) {
resp := struct {
Result struct {
ResultTotalSize int64 `json:"resultTotalSize"`
Data []USDCAssetInfo `json:"dataList"`
} `json:"result"`
USDCError
}{}
req := make(map[string]interface{})
if baseCoin != "" {
req["baseCoin"] = baseCoin
}
return resp.Result.Data, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetAssetInfo, req, &resp, usdcGetAssetRate)
}
// GetUSDCMarginInfo gets USDC account margin information.
func (by *Bybit) GetUSDCMarginInfo(ctx context.Context) (string, error) {
resp := struct {
Result struct {
MarginMode string `json:"marginMode"`
} `json:"result"`
USDCError
}{}
return resp.Result.MarginMode, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetMarginInfo, nil, &resp, usdcGetMarginRate)
}
// GetUSDCPosition gets USDC position information.
func (by *Bybit) GetUSDCPosition(ctx context.Context, symbol currency.Pair, category, direction, cursor string, limit int64) ([]USDCPosition, error) {
resp := struct {
Result struct {
Cursor string `json:"cursor"`
ResultTotalSize int64 `json:"resultTotalSize"`
Data []USDCPosition `json:"dataList"`
} `json:"result"`
USDCError
}{}
req := make(map[string]interface{})
if !symbol.IsEmpty() {
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Result.Data, err
}
req["symbol"] = symbolValue
}
if category == "" {
return nil, errInvalidCategory
}
req["category"] = category
if cursor != "" {
req["cursor"] = cursor
}
if direction != "" {
req["direction"] = direction
}
if limit > 0 && limit <= 50 {
req["limit"] = strconv.FormatInt(limit, 10)
}
return resp.Result.Data, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetPosition, req, &resp, usdcGetPositionRate)
}
// SetUSDCLeverage sets USDC leverage.
func (by *Bybit) SetUSDCLeverage(ctx context.Context, symbol currency.Pair, leverage float64) (float64, error) {
resp := struct {
Result struct {
Leverage float64 `json:"leverage,string"`
} `json:"result"`
USDCError
}{}
req := make(map[string]interface{})
if symbol.IsEmpty() {
return 0, errSymbolMissing
}
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Result.Leverage, err
}
req["symbol"] = symbolValue
if leverage <= 0 {
return 0, errInvalidLeverage
}
req["leverage"] = strconv.FormatFloat(leverage, 'f', -1, 64)
return resp.Result.Leverage, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesSetLeverage, req, &resp, usdcSetLeverageRate)
}
// GetUSDCSettlementHistory gets USDC settlement history with support of last 30 days.
func (by *Bybit) GetUSDCSettlementHistory(ctx context.Context, symbol currency.Pair, direction, cursor string, limit int64) ([]USDCSettlementHistory, error) {
resp := struct {
Result struct {
Cursor string `json:"cursor"`
ResultTotalSize int64 `json:"resultTotalSize"`
Data []USDCSettlementHistory `json:"dataList"`
} `json:"result"`
USDCError
}{}
req := make(map[string]interface{})
if symbol.IsEmpty() {
return resp.Result.Data, errSymbolMissing
}
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Result.Data, err
}
req["symbol"] = symbolValue
if cursor != "" {
req["cursor"] = cursor
}
if direction != "" {
req["direction"] = direction
}
if limit > 0 && limit <= 50 {
req["limit"] = strconv.FormatInt(limit, 10)
}
return resp.Result.Data, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetSettlementHistory, req, &resp, usdcGetSettlementRate)
}
// GetUSDCRiskLimit gets USDC risk limits data.
func (by *Bybit) GetUSDCRiskLimit(ctx context.Context, symbol currency.Pair) ([]USDCRiskLimit, error) {
resp := struct {
Result []USDCRiskLimit `json:"result"`
USDCError
}{}
params := url.Values{}
if symbol.IsEmpty() {
return nil, errSymbolMissing
}
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Result, err
}
params.Set("symbol", symbolValue)
return resp.Result, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetRiskLimit, params), usdcPublicRate, &resp)
}
// SetUSDCRiskLimit sets USDC risk limit.
func (by *Bybit) SetUSDCRiskLimit(ctx context.Context, symbol currency.Pair, riskID int64) (string, error) {
resp := struct {
Result struct {
RiskID string `json:"riskId"`
} `json:"result"`
USDCError
}{}
req := make(map[string]interface{})
if symbol.IsEmpty() {
return resp.Result.RiskID, errSymbolMissing
}
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Result.RiskID, err
}
req["symbol"] = symbolValue
if riskID <= 0 {
return resp.Result.RiskID, errInvalidRiskID
}
req["riskId"] = riskID
return resp.Result.RiskID, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesSetRiskLimit, req, &resp, usdcSetRiskRate)
}
// GetUSDCLastFundingRate gets USDC last funding rates.
func (by *Bybit) GetUSDCLastFundingRate(ctx context.Context, symbol currency.Pair) (USDCFundingInfo, error) {
resp := struct {
Result USDCFundingInfo `json:"result"`
USDCError
}{}
params := url.Values{}
if symbol.IsEmpty() {
return resp.Result, errSymbolMissing
}
symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Result, err
}
params.Set("symbol", symbolValue)
return resp.Result, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetLastFundingRate, params), usdcPublicRate, &resp)
}
// GetUSDCPredictedFundingRate gets predicted funding rates and my predicted funding fee.
func (by *Bybit) GetUSDCPredictedFundingRate(ctx context.Context, symbol currency.Pair) (predictedFundingRate, predictedFundingFee float64, err error) {
resp := struct {
Result struct {
PredictedFundingRate float64 `json:"predictedFundingRate,string"`
PredictedFundingFee float64 `json:"predictedFundingFee,string"`
} `json:"result"`
USDCError
}{}
req := make(map[string]interface{})
var symbolValue string
if symbol.IsEmpty() {
return resp.Result.PredictedFundingRate, resp.Result.PredictedFundingFee, errSymbolMissing
}
symbolValue, err = by.FormatSymbol(symbol, asset.USDCMarginedFutures)
if err != nil {
return resp.Result.PredictedFundingRate, resp.Result.PredictedFundingFee, err
}
req["symbol"] = symbolValue
err = by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetPredictedFundingRate, req, &resp, usdcGetPredictedFundingRate)
predictedFundingRate = resp.Result.PredictedFundingRate
predictedFundingFee = resp.Result.PredictedFundingFee
return
}
// SendUSDCAuthHTTPRequest sends an authenticated HTTP request
func (by *Bybit) SendUSDCAuthHTTPRequest(ctx context.Context, ePath exchange.URL, method, path string, data interface{}, result UnmarshalTo, f request.EndpointLimit) error {
creds, err := by.GetCredentials(ctx)
if err != nil {
return err
}
if result == nil {
result = &USDCError{}
}
endpointPath, err := by.API.Endpoints.GetURL(ePath)
if err != nil {
return err
}
err = by.SendPayload(ctx, f, func() (*request.Item, error) {
nowTimeInMilli := strconv.FormatInt(time.Now().UnixMilli(), 10)
headers := make(map[string]string)
var payload, hmacSigned []byte
if data != nil {
d, ok := data.(map[string]interface{})
if !ok {
return nil, common.GetAssertError("map[string]interface{}", data)
}
payload, err = json.Marshal(d)
if err != nil {
return nil, err
}
}
signInput := nowTimeInMilli + creds.Key + defaultRecvWindow + string(payload)
hmacSigned, err = crypto.GetHMAC(crypto.HashSHA256, []byte(signInput), []byte(creds.Secret))
if err != nil {
return nil, err
}
headers["Content-Type"] = "application/json"
headers["X-BAPI-API-KEY"] = creds.Key
headers["X-BAPI-SIGN"] = crypto.HexEncodeToString(hmacSigned)
headers["X-BAPI-SIGN-TYPE"] = "2"
headers["X-BAPI-TIMESTAMP"] = nowTimeInMilli
headers["X-BAPI-RECV-WINDOW"] = defaultRecvWindow
return &request.Item{
Method: method,
Path: endpointPath + path,
Headers: headers,
Body: bytes.NewBuffer(payload),
Result: &result,
AuthRequest: true,
Verbose: by.Verbose,
HTTPDebugging: by.HTTPDebugging,
HTTPRecording: by.HTTPRecording}, nil
})
if err != nil {
return err
}
return result.GetError()
}
// USDCError defines all error information for each USDC request
type USDCError struct {
ReturnCode int64 `json:"retCode"`
ReturnMsg string `json:"retMsg"`
}
// GetError checks and returns an error if it is supplied.
func (e USDCError) GetError() error {
if e.ReturnCode != 0 && e.ReturnMsg != "" {
return errors.New(e.ReturnMsg)
}
return nil
}