exchanges: Update Bybit exchange to V5 (#1301)

* Adding Bybit public endpoints

* Completed adding market endpoints

* Added trade endpoints

* Adding position endpoints

* completing position endpoints

* Adding Pre-upgrade endpoints

* Completed adding Pre-upgrade and Account endpoints

* Added asset endpoints

* Added user endpoints and unit tests

* Adding spot leverage and margin trade endpoints

* spot margin trade added

* added spot-margin-trade, institutional lending, c2c lending, and broker

* Adding wrapper funnctions

* Working on wrapper public methods

* Added wrapper functions and unit tests

* Added websocket support with unit tests

* Update websocket handlers and added rate-limiter

* wrapper function, websocket handlers, and linter issues fixe

* unit tests fixes and codespell correction

* Update documentation

* Minor websocket handling fix and URL consts merging

* types, unit test other updates

* Updated websocket and methods based on review

* Added GetFeeByType method with unit test and fixes

* add filter for Unified and Normal endpoints

* Mock recording and unit tests update

* minor linter issue fix

* websocket and rest tests and fix

* change asset types and wrapper methods update

* rm: forgotten panic message

* endpoints, websocket  and unit tests update

* Added and updated endpoints and unit test

* linter and spell fix

* unit test and orders update

* Update on endpoints, fields, config pairs and formating, and unit tests

* minor update on responses

* Fix unit test and types

* Unit tests, models, and wrapper issues fix and mock test recording

* rm print statement

* Fix issue, add FundingRate wrapper func, mock record

* minor type and unit test update

* Update on order handling and unit test

* Minor test

* Minor fix in wrapper

* unit tests update, recording, and documentation update

* Unit tests and minor wrapper function update

* minor unit test fix

* Added newly added endpoints, unit tests, and mock recording

* Rename GetInstruments -> GetInstrumentInfo

* doc update

* Minor unit tests update

* Minor unit test and wrapper update

* Fix linter issue

* Add unit test and minor updates

* Revert websocket error declaration

* Revert websocket error declaration

* Balace --> Balance

* Fix config issues

* Added next funding time minor fix

* Update GetLatestFundingRates and record mock test data

* Added LatestFundingRate time

* Fix test issue of TestAllExchangeWrappers

* config pairs update

* configtest spot pairs update

* Minor update on options UpdateOrderExecutionLimits wrapper func

* Linter issue fix and added new currency codes

* Added new currency codes

* Update bybit pairs in config_example

* config assets pair format update
This commit is contained in:
Samuael A
2024-01-11 04:35:46 +03:00
committed by GitHub
parent db4f4bf63c
commit fb6d12ac69
32 changed files with 435046 additions and 104692 deletions

View File

@@ -143,20 +143,20 @@ Binaries will be published once the codebase reaches a stable condition.
|User|Contribution Amount|
|--|--|
| [thrasher-](https://github.com/thrasher-) | 683 |
| [shazbert](https://github.com/shazbert) | 313 |
| [dependabot[bot]](https://github.com/apps/dependabot) | 227 |
| [thrasher-](https://github.com/thrasher-) | 684 |
| [shazbert](https://github.com/shazbert) | 315 |
| [dependabot[bot]](https://github.com/apps/dependabot) | 228 |
| [gloriousCode](https://github.com/gloriousCode) | 224 |
| [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) | 88 |
| [xtda](https://github.com/xtda) | 47 |
| [gbjk](https://github.com/gbjk) | 40 |
| [gbjk](https://github.com/gbjk) | 42 |
| [lrascao](https://github.com/lrascao) | 27 |
| [Rots](https://github.com/Rots) | 15 |
| [vazha](https://github.com/vazha) | 15 |
| [ydm](https://github.com/ydm) | 15 |
| [ermalguni](https://github.com/ermalguni) | 14 |
| [MadCozBadd](https://github.com/MadCozBadd) | 13 |
| [Beadko](https://github.com/Beadko) | 10 |
| [Beadko](https://github.com/Beadko) | 11 |
| [vadimzhukck](https://github.com/vadimzhukck) | 10 |
| [140am](https://github.com/140am) | 8 |
| [marcofranssen](https://github.com/marcofranssen) | 8 |

File diff suppressed because one or more lines are too long

View File

@@ -3021,6 +3021,51 @@ var (
FI = NewCode("FI")
USDM = NewCode("USDM")
USDTM = NewCode("USDTM")
LEVER = NewCode("LEVER")
NESS = NewCode("NESS")
KAS = NewCode("KAS")
NEXT = NewCode("NEXT")
VEXT = NewCode("VEXT")
PYUSD = NewCode("PYUSD")
SAIL = NewCode("SAIL")
VV = NewCode("VV")
ORDI = NewCode("ORDI")
CYBER = NewCode("CYBER")
SEILOR = NewCode("SEILOR")
TAVA = NewCode("TAVA")
DSRUN = NewCode("DSRUN")
CWAR = NewCode("CWAR")
GALFT = NewCode("GALFT")
LADYS10000 = NewCode("10000LADYS")
NFT10000 = NewCode("10000NFT")
BONK1000 = NewCode("1000BONK")
BTT1000 = NewCode("1000BTT")
FLOKI1000 = NewCode("1000FLOKI")
LUNC1000 = NewCode("1000LUNC")
PEPE1000 = NewCode("1000PEPE")
XEC1000 = NewCode("1000XEC")
ARKM = NewCode("ARKM")
BICO = NewCode("BICO")
BIGTIME = NewCode("BIGTIME")
BLUR = NewCode("BLUR")
CEEK = NewCode("CEEK")
ETHW = NewCode("ETHW")
FITFI = NewCode("FITFI")
GLMR = NewCode("GLMR")
HIFI = NewCode("HIFI")
HOOK = NewCode("HOOK")
LOOKS = NewCode("LOOKS")
LQTY = NewCode("LQTY")
LUNA2 = NewCode("LUNA2")
MAGIC = NewCode("MAGIC")
PENDLE = NewCode("PENDLE")
PEOPLE = NewCode("PEOPLE")
RDNT = NewCode("RDNT")
RNDR = NewCode("RNDR")
RSS3 = NewCode("RSS3")
SHIB1000 = NewCode("SHIB1000")
SWEAT = NewCode("SWEAT")
TOMI = NewCode("TOMI")
stables = Currencies{
USDT,

View File

@@ -204,6 +204,7 @@ Yes means supported, No means not yet implemented and NA means protocol unsuppor
| Bitstamp | Yes | Yes | No |
| BTCMarkets | Yes | No | NA |
| BTSE | Yes | Yes | NA |
| Bybit | Yes | Yes | NA |
| COINUT | Yes | Yes | NA |
| Exmo | Yes | NA | NA |
| FTX | Yes | Yes | No | // <-------- new exchange
@@ -234,6 +235,7 @@ var Exchanges = []string{
"bitstamp",
"btc markets",
"btse",
"bybit",
"coinbasepro",
"coinut",
"exmo",

View File

@@ -42,8 +42,11 @@ const (
USDCMarginedFutures
Options
futuresFlag = PerpetualContract | PerpetualSwap | Futures | DeliveryFutures | UpsideProfitContract | DownsideProfitContract | CoinMarginedFutures | USDTMarginedFutures | USDCMarginedFutures
supportedFlag = Spot | Margin | CrossMargin | MarginFunding | Index | Binary | PerpetualContract | PerpetualSwap | Futures | DeliveryFutures | UpsideProfitContract | DownsideProfitContract | CoinMarginedFutures | USDTMarginedFutures | USDCMarginedFutures | Options
// Added to represent a USDT and USDC based linear derivatives(futures/perpetual) assets in Bybit V5.
LinearContract
futuresFlag = PerpetualContract | PerpetualSwap | Futures | DeliveryFutures | UpsideProfitContract | DownsideProfitContract | CoinMarginedFutures | USDTMarginedFutures | USDCMarginedFutures | LinearContract
supportedFlag = Spot | Margin | CrossMargin | MarginFunding | Index | Binary | PerpetualContract | PerpetualSwap | Futures | DeliveryFutures | UpsideProfitContract | DownsideProfitContract | CoinMarginedFutures | USDTMarginedFutures | USDCMarginedFutures | Options | LinearContract
spot = "spot"
margin = "margin"
@@ -65,7 +68,7 @@ const (
)
var (
supportedList = Items{Spot, Margin, CrossMargin, MarginFunding, Index, Binary, PerpetualContract, PerpetualSwap, Futures, DeliveryFutures, UpsideProfitContract, DownsideProfitContract, CoinMarginedFutures, USDTMarginedFutures, USDCMarginedFutures, Options}
supportedList = Items{Spot, Margin, CrossMargin, MarginFunding, Index, Binary, PerpetualContract, PerpetualSwap, Futures, DeliveryFutures, UpsideProfitContract, DownsideProfitContract, CoinMarginedFutures, USDTMarginedFutures, USDCMarginedFutures, Options, LinearContract}
)
// Supported returns a list of supported asset types

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
package bybit
import "encoding/json"
// UnmarshalJSON deserializes incoming data into orderbookResponse instance.
func (a *orderbookResponse) UnmarshalJSON(data []byte) error {
type Alias orderbookResponse
child := &struct {
*Alias
}{
Alias: (*Alias)(a),
}
err := json.Unmarshal(data, child)
if err != nil {
var resp []interface{}
err = json.Unmarshal(data, &resp)
if err != nil {
return err
}
}
return nil
}

View File

@@ -1,757 +0,0 @@
package bybit
import (
"context"
"net/http"
"net/url"
"strconv"
"time"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
)
const (
// auth endpoint
futuresCreateOrder = "/futures/private/order/create"
futuresGetActiveOrders = "/futures/private/order/list"
futuresCancelActiveOrder = "/futures/private/order/cancel"
futuresCancelAllActiveOrders = "/futures/private/order/cancelAll"
futuresReplaceActiveOrder = "/futures/private/order/replace"
futuresGetActiveRealtimeOrders = "/futures/private/order"
futuresCreateConditionalOrder = "/futures/private/stop-order/create"
futuresGetConditionalOrders = "/futures/private/stop-order/list"
futuresCancelConditionalOrder = "/futures/private/stop-order/cancel"
futuresCancelAllConditionalOrders = "/futures/private/stop-order/cancelAll"
futuresReplaceConditionalOrder = "/futures/private/stop-order/replace"
futuresGetConditionalRealtimeOrders = "/futures/private/stop-order"
futuresPosition = "/futures/private/position/list"
futuresUpdateMargin = "/futures/private/position/change-position-margin"
futuresSetTradingStop = "/futures/private/position/trading-stop"
futuresSetLeverage = "/futures/private/position/leverage/save"
futuresSwitchPositionMode = "/futures/private/position/switch-mode"
futuresSwitchPosition = "/futures/private/tpsl/switch-mode"
futuresSwitchMargin = "/futures/private/position/switch-isolated"
futuresGetTrades = "/futures/private/execution/list"
futuresGetClosedTrades = "/futures/private/trade/closed-pnl/list"
futuresSetRiskLimit = "/futures/private/position/risk-limit"
)
// CreateFuturesOrder sends a new futures order to the exchange
func (by *Bybit) CreateFuturesOrder(ctx context.Context, positionMode int64, symbol currency.Pair, side, orderType, timeInForce,
orderLinkID, takeProfitTriggerBy, stopLossTriggerBy string,
quantity, price, takeProfit, stopLoss float64, closeOnTrigger, reduceOnly bool) (FuturesOrderDataResp, error) {
resp := struct {
Result FuturesOrderDataResp `json:"result"`
Error
}{}
params := url.Values{}
if positionMode < 0 || positionMode > 2 {
return resp.Result, errInvalidPositionMode
}
params.Set("position_idx", strconv.FormatInt(positionMode, 10))
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp.Result, err
}
params.Set("symbol", symbolValue)
params.Set("side", side)
params.Set("order_type", orderType)
if quantity <= 0 {
return resp.Result, errInvalidQuantity
}
params.Set("qty", strconv.FormatFloat(quantity, 'f', -1, 64))
if price != 0 {
params.Set("price", strconv.FormatFloat(price, 'f', -1, 64))
}
if timeInForce == "" {
return resp.Result, errInvalidTimeInForce
}
params.Set("time_in_force", timeInForce)
if closeOnTrigger {
params.Set("close_on_trigger", "true")
}
if orderLinkID != "" {
params.Set("order_link_id", orderLinkID)
}
if takeProfit != 0 {
params.Set("take_profit", strconv.FormatFloat(takeProfit, 'f', -1, 64))
}
if stopLoss != 0 {
params.Set("stop_loss", strconv.FormatFloat(stopLoss, 'f', -1, 64))
}
if takeProfitTriggerBy != "" {
params.Set("tp_trigger_by", takeProfitTriggerBy)
}
if stopLossTriggerBy != "" {
params.Set("sl_trigger_by", stopLossTriggerBy)
}
if reduceOnly {
params.Set("reduce_only", "true")
}
return resp.Result, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresCreateOrder, params, nil, &resp, futuresCreateOrderRate)
}
// GetActiveFuturesOrders gets list of futures active orders
func (by *Bybit) GetActiveFuturesOrders(ctx context.Context, symbol currency.Pair, orderStatus, direction, cursor string, limit int64) ([]FuturesActiveOrder, error) {
resp := struct {
Result struct {
Data []FuturesActiveOrder `json:"data"`
Cursor string `json:"cursor"`
} `json:"result"`
Error
}{}
params := url.Values{}
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp.Result.Data, err
}
params.Set("symbol", symbolValue)
if orderStatus != "" {
params.Set("order_status", orderStatus)
}
if direction != "" {
params.Set("direction", direction)
}
if limit > 0 {
params.Set("limit", strconv.FormatInt(limit, 10))
}
if cursor != "" {
params.Set("cursor", cursor)
}
return resp.Result.Data, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodGet, futuresGetActiveOrders, params, nil, &resp, futuresGetActiveOrderRate)
}
// CancelActiveFuturesOrders cancels futures unfilled or partially filled orders
func (by *Bybit) CancelActiveFuturesOrders(ctx context.Context, symbol currency.Pair, orderID, orderLinkID string) (FuturesOrderCancelResp, error) {
resp := struct {
Result FuturesOrderCancelResp `json:"result"`
Error
}{}
params := url.Values{}
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp.Result, err
}
params.Set("symbol", symbolValue)
if orderID == "" && orderLinkID == "" {
return resp.Result, errOrderOrOrderLinkIDMissing
}
if orderID != "" {
params.Set("order_id", orderID)
}
if orderLinkID != "" {
params.Set("order_link_id", orderLinkID)
}
return resp.Result, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresCancelActiveOrder, params, nil, &resp, futuresCancelOrderRate)
}
// CancelAllActiveFuturesOrders cancels all futures unfilled or partially filled orders
func (by *Bybit) CancelAllActiveFuturesOrders(ctx context.Context, symbol currency.Pair) ([]FuturesCancelOrderData, error) {
resp := struct {
Result []FuturesCancelOrderData `json:"result"`
Error
}{}
params := url.Values{}
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp.Result, err
}
params.Set("symbol", symbolValue)
return resp.Result, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresCancelAllActiveOrders, params, nil, &resp, futuresCancelAllOrderRate)
}
// ReplaceActiveFuturesOrders modify unfilled or partially filled orders
func (by *Bybit) ReplaceActiveFuturesOrders(ctx context.Context, symbol currency.Pair, orderID, orderLinkID, takeProfitTriggerBy, stopLossTriggerBy string,
updatedQty, updatedPrice, takeProfitPrice, stopLossPrice float64) (string, error) {
resp := struct {
Result struct {
OrderID string `json:"order_id"`
} `json:"result"`
Error
}{}
params := url.Values{}
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return "", err
}
params.Set("symbol", symbolValue)
if orderID == "" && orderLinkID == "" {
return "", errOrderOrOrderLinkIDMissing
}
if orderID != "" {
params.Set("order_id", orderID)
}
if orderLinkID != "" {
params.Set("order_link_id", orderLinkID)
}
if updatedQty != 0 {
params.Set("p_r_qty", strconv.FormatFloat(updatedQty, 'f', -1, 64))
}
if updatedPrice != 0 {
params.Set("p_r_price", strconv.FormatFloat(updatedPrice, 'f', -1, 64))
}
if takeProfitPrice != 0 {
params.Set("take_profit", strconv.FormatFloat(takeProfitPrice, 'f', -1, 64))
}
if stopLossPrice != 0 {
params.Set("stop_loss", strconv.FormatFloat(stopLossPrice, 'f', -1, 64))
}
if takeProfitTriggerBy != "" {
params.Set("tp_trigger_by", takeProfitTriggerBy)
}
if stopLossTriggerBy != "" {
params.Set("sl_trigger_by", stopLossTriggerBy)
}
return resp.Result.OrderID, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresReplaceActiveOrder, params, nil, &resp, futuresReplaceOrderRate)
}
// GetActiveRealtimeOrders query real time order data
func (by *Bybit) GetActiveRealtimeOrders(ctx context.Context, symbol currency.Pair, orderID, orderLinkID string) ([]FuturesActiveRealtimeOrder, error) {
var data []FuturesActiveRealtimeOrder
params := url.Values{}
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return data, err
}
params.Set("symbol", symbolValue)
if orderID != "" {
params.Set("order_id", orderID)
}
if orderLinkID != "" {
params.Set("order_link_id", orderLinkID)
}
if orderID == "" && orderLinkID == "" {
resp := struct {
Result []FuturesActiveRealtimeOrder `json:"result"`
Error
}{}
err = by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodGet, futuresGetActiveRealtimeOrders, params, nil, &resp, futuresGetActiveRealtimeOrderRate)
if err != nil {
return data, err
}
data = append(data, resp.Result...)
} else {
resp := struct {
Result FuturesActiveRealtimeOrder `json:"result"`
Error
}{}
err = by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodGet, futuresGetActiveRealtimeOrders, params, nil, &resp, futuresGetActiveRealtimeOrderRate)
if err != nil {
return data, err
}
data = append(data, resp.Result)
}
return data, nil
}
// CreateConditionalFuturesOrder sends a new conditional futures order to the exchange
func (by *Bybit) CreateConditionalFuturesOrder(ctx context.Context, positionMode int64, symbol currency.Pair, side, orderType, timeInForce,
orderLinkID, takeProfitTriggerBy, stopLossTriggerBy, triggerBy string,
quantity, price, takeProfit, stopLoss, basePrice, stopPrice float64, closeOnTrigger bool) (FuturesConditionalOrderResp, error) {
resp := struct {
Result FuturesConditionalOrderResp `json:"result"`
Error
}{}
params := url.Values{}
if positionMode < 0 || positionMode > 2 {
return resp.Result, errInvalidPositionMode
}
params.Set("position_idx", strconv.FormatInt(positionMode, 10))
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp.Result, err
}
params.Set("symbol", symbolValue)
params.Set("side", side)
params.Set("order_type", orderType)
if quantity <= 0 {
return resp.Result, errInvalidQuantity
}
params.Set("qty", strconv.FormatFloat(quantity, 'f', -1, 64))
if price != 0 {
params.Set("price", strconv.FormatFloat(price, 'f', -1, 64))
}
if basePrice <= 0 {
return resp.Result, errInvalidBasePrice
}
params.Set("base_price", strconv.FormatFloat(basePrice, 'f', -1, 64))
if stopPrice <= 0 {
return resp.Result, errInvalidStopPrice
}
params.Set("stop_px", strconv.FormatFloat(stopPrice, 'f', -1, 64))
if timeInForce == "" {
return resp.Result, errInvalidTimeInForce
}
params.Set("time_in_force", timeInForce)
if triggerBy != "" {
params.Set("trigger_by", triggerBy)
}
if closeOnTrigger {
params.Set("close_on_trigger", "true")
}
if orderLinkID != "" {
params.Set("order_link_id", orderLinkID)
}
if takeProfit != 0 {
params.Set("take_profit", strconv.FormatFloat(takeProfit, 'f', -1, 64))
}
if stopLoss != 0 {
params.Set("stop_loss", strconv.FormatFloat(stopLoss, 'f', -1, 64))
}
if takeProfitTriggerBy != "" {
params.Set("tp_trigger_by", takeProfitTriggerBy)
}
if stopLossTriggerBy != "" {
params.Set("sl_trigger_by", stopLossTriggerBy)
}
return resp.Result, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresCreateConditionalOrder, params, nil, &resp, futuresCreateConditionalOrderRate)
}
// GetConditionalFuturesOrders gets list of futures conditional orders
func (by *Bybit) GetConditionalFuturesOrders(ctx context.Context, symbol currency.Pair, stopOrderStatus, direction, cursor string, limit int64) ([]FuturesConditionalOrders, error) {
resp := struct {
Result struct {
Result []FuturesConditionalOrders `json:"data"`
Cursor string `json:"cursor"`
} `json:"result"`
Error
}{}
params := url.Values{}
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp.Result.Result, err
}
params.Set("symbol", symbolValue)
if stopOrderStatus != "" {
params.Set("stop_order_status", stopOrderStatus)
}
if direction != "" {
params.Set("direction", direction)
}
if limit > 0 {
params.Set("limit", strconv.FormatInt(limit, 10))
}
if cursor != "" {
params.Set("cursor", cursor)
}
return resp.Result.Result, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodGet, futuresGetConditionalOrders, params, nil, &resp, futuresGetConditionalOrderRate)
}
// CancelConditionalFuturesOrders cancels untriggered conditional orders
func (by *Bybit) CancelConditionalFuturesOrders(ctx context.Context, symbol currency.Pair, stopOrderID, orderLinkID string) (string, error) {
resp := struct {
Result struct {
StopOrderID string `json:"stop_order_id"`
} `json:"result"`
Error
}{}
params := url.Values{}
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return "", err
}
params.Set("symbol", symbolValue)
if stopOrderID == "" && orderLinkID == "" {
return "", errStopOrderOrOrderLinkIDMissing
}
if stopOrderID != "" {
params.Set("stop_order_id", stopOrderID)
}
if orderLinkID != "" {
params.Set("order_link_id", orderLinkID)
}
return resp.Result.StopOrderID, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresCancelConditionalOrder, params, nil, &resp, futuresCancelConditionalOrderRate)
}
// CancelAllConditionalFuturesOrders cancels all untriggered conditional orders
func (by *Bybit) CancelAllConditionalFuturesOrders(ctx context.Context, symbol currency.Pair) ([]FuturesCancelOrderResp, error) {
resp := struct {
Result []FuturesCancelOrderResp `json:"result"`
Error
}{}
params := url.Values{}
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp.Result, err
}
params.Set("symbol", symbolValue)
return resp.Result, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresCancelAllConditionalOrders, params, nil, &resp, futuresCancelAllConditionalOrderRate)
}
// ReplaceConditionalFuturesOrders modify unfilled or partially filled conditional orders
func (by *Bybit) ReplaceConditionalFuturesOrders(ctx context.Context, symbol currency.Pair, stopOrderID, orderLinkID, takeProfitTriggerBy, stopLossTriggerBy string,
updatedQty, updatedPrice, takeProfitPrice, stopLossPrice, orderTriggerPrice float64) (string, error) {
resp := struct {
Result struct {
OrderID string `json:"stop_order_id"`
} `json:"result"`
Error
}{}
params := url.Values{}
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return "", err
}
params.Set("symbol", symbolValue)
if stopOrderID == "" && orderLinkID == "" {
return "", errStopOrderOrOrderLinkIDMissing
}
if stopOrderID != "" {
params.Set("stop_order_id", stopOrderID)
}
if orderLinkID != "" {
params.Set("order_link_id", orderLinkID)
}
if updatedQty != 0 {
params.Set("p_r_qty", strconv.FormatFloat(updatedQty, 'f', -1, 64))
}
if updatedPrice != 0 {
params.Set("p_r_price", strconv.FormatFloat(updatedPrice, 'f', -1, 64))
}
if orderTriggerPrice != 0 {
params.Set("p_r_trigger_price", strconv.FormatFloat(orderTriggerPrice, 'f', -1, 64))
}
if takeProfitPrice != 0 {
params.Set("take_profit", strconv.FormatFloat(takeProfitPrice, 'f', -1, 64))
}
if stopLossPrice != 0 {
params.Set("stop_loss", strconv.FormatFloat(stopLossPrice, 'f', -1, 64))
}
if takeProfitTriggerBy != "" {
params.Set("tp_trigger_by", takeProfitTriggerBy)
}
if stopLossTriggerBy != "" {
params.Set("sl_trigger_by", stopLossTriggerBy)
}
return resp.Result.OrderID, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresReplaceConditionalOrder, params, nil, &resp, futuresReplaceConditionalOrderRate)
}
// GetConditionalRealtimeOrders query real time conditional order data
func (by *Bybit) GetConditionalRealtimeOrders(ctx context.Context, symbol currency.Pair, stopOrderID, orderLinkID string) ([]FuturesConditionalRealtimeOrder, error) {
var data []FuturesConditionalRealtimeOrder
params := url.Values{}
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return data, err
}
params.Set("symbol", symbolValue)
if stopOrderID != "" {
params.Set("stop_order_id", stopOrderID)
}
if orderLinkID != "" {
params.Set("order_link_id", orderLinkID)
}
if stopOrderID == "" && orderLinkID == "" {
resp := struct {
Result []FuturesConditionalRealtimeOrder `json:"result"`
Error
}{}
err = by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodGet, futuresGetConditionalRealtimeOrders, params, nil, &resp, futuresGetConditionalRealtimeOrderRate)
if err != nil {
return data, err
}
data = append(data, resp.Result...)
} else {
resp := struct {
Result FuturesConditionalRealtimeOrder `json:"result"`
Error
}{}
err = by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodGet, futuresGetConditionalRealtimeOrders, params, nil, &resp, futuresGetConditionalRealtimeOrderRate)
if err != nil {
return data, err
}
data = append(data, resp.Result)
}
return data, nil
}
// GetPositions returns list of user positions
func (by *Bybit) GetPositions(ctx context.Context, symbol currency.Pair) ([]PositionResp, error) {
params := url.Values{}
resp := struct {
Result []struct {
Data PositionResp `json:"data"`
IsValid bool `json:"is_valid"`
} `json:"result"`
Error
}{}
if !symbol.IsEmpty() {
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return nil, err
}
params.Set("symbol", symbolValue)
}
err := by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodGet, futuresPosition, params, nil, &resp, futuresPositionRate)
if err != nil {
return nil, err
}
data := make([]PositionResp, len(resp.Result))
for x := range resp.Result {
data[x] = resp.Result[x].Data
}
return data, nil
}
// SetMargin updates margin
func (by *Bybit) SetMargin(ctx context.Context, positionMode int64, symbol currency.Pair, margin string) (float64, error) {
resp := struct {
Result float64 `json:"result"`
Error
}{}
params := url.Values{}
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp.Result, err
}
params.Set("symbol", symbolValue)
if positionMode < 0 || positionMode > 2 {
return resp.Result, errInvalidPositionMode
}
params.Set("position_idx", strconv.FormatInt(positionMode, 10))
if margin == "" {
return resp.Result, errInvalidMargin
}
params.Set("margin", margin)
return resp.Result, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresUpdateMargin, params, nil, &resp, futuresUpdateMarginRate)
}
// SetTradingAndStop sets take profit, stop loss, and trailing stop for your open position
func (by *Bybit) SetTradingAndStop(ctx context.Context, positionMode int64, symbol currency.Pair, takeProfit, stopLoss, trailingStop, newTrailingActive, stopLossQty, takeProfitQty float64, takeProfitTriggerBy, stopLossTriggerBy string) (SetTradingAndStopResp, error) {
resp := struct {
Result SetTradingAndStopResp `json:"result"`
Error
}{}
params := url.Values{}
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp.Result, err
}
params.Set("symbol", symbolValue)
if positionMode < 0 || positionMode > 2 {
return resp.Result, errInvalidPositionMode
}
params.Set("position_idx", strconv.FormatInt(positionMode, 10))
if takeProfit >= 0 {
params.Set("take_profit", strconv.FormatFloat(takeProfit, 'f', -1, 64))
}
if stopLoss >= 0 {
params.Set("stop_loss", strconv.FormatFloat(stopLoss, 'f', -1, 64))
}
if trailingStop >= 0 {
params.Set("trailing_stop", strconv.FormatFloat(trailingStop, 'f', -1, 64))
}
if newTrailingActive != 0 {
params.Set("new_trailing_active", strconv.FormatFloat(newTrailingActive, 'f', -1, 64))
}
if stopLossQty != 0 {
params.Set("sl_size", strconv.FormatFloat(stopLossQty, 'f', -1, 64))
}
if takeProfitQty != 0 {
params.Set("tp_size", strconv.FormatFloat(takeProfitQty, 'f', -1, 64))
}
if takeProfitTriggerBy != "" {
params.Set("tp_trigger_by", takeProfitTriggerBy)
}
if stopLossTriggerBy != "" {
params.Set("sl_trigger_by", stopLossTriggerBy)
}
return resp.Result, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresSetTradingStop, params, nil, &resp, futuresSetTradingStopRate)
}
// SetLeverageLevel sets leverage
func (by *Bybit) SetLeverageLevel(ctx context.Context, symbol currency.Pair, buyLeverage, sellLeverage float64) (float64, error) {
resp := struct {
Result float64 `json:"result"`
Error
}{}
params := url.Values{}
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp.Result, err
}
params.Set("symbol", symbolValue)
params.Set("buy_leverage", strconv.FormatFloat(buyLeverage, 'f', -1, 64))
params.Set("sell_leverage", strconv.FormatFloat(sellLeverage, 'f', -1, 64))
return resp.Result, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresSetLeverage, params, nil, &resp, futuresSetLeverageRate)
}
// ChangePositionMode switches mode between One-Way or Hedge Mode
func (by *Bybit) ChangePositionMode(ctx context.Context, symbol currency.Pair, mode int64) error {
params := url.Values{}
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return err
}
params.Set("symbol", symbolValue)
params.Set("mode", strconv.FormatInt(mode, 10))
return by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresSwitchPositionMode, params, nil, nil, futuresSwitchPositionModeRate)
}
// ChangeMode switches mode between full or partial position
func (by *Bybit) ChangeMode(ctx context.Context, symbol currency.Pair, takeProfitStopLoss string) (string, error) {
resp := struct {
Result struct {
Mode string `json:"tp_sl_mode"`
} `json:"result"`
Error
}{}
params := url.Values{}
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp.Result.Mode, err
}
params.Set("symbol", symbolValue)
if takeProfitStopLoss == "" {
return resp.Result.Mode, errInvalidTakeProfitStopLoss
}
params.Set("tp_sl_mode", takeProfitStopLoss)
return resp.Result.Mode, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresSwitchPosition, params, nil, &resp, futuresSwitchPositionRate)
}
// ChangeMargin switches margin between cross or isolated
func (by *Bybit) ChangeMargin(ctx context.Context, symbol currency.Pair, buyLeverage, sellLeverage float64, isIsolated bool) error {
params := url.Values{}
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return err
}
params.Set("symbol", symbolValue)
params.Set("buy_leverage", strconv.FormatFloat(buyLeverage, 'f', -1, 64))
params.Set("sell_leverage", strconv.FormatFloat(sellLeverage, 'f', -1, 64))
if isIsolated {
params.Set("is_isolated", "true")
} else {
params.Set("is_isolated", "false")
}
return by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresSwitchMargin, params, nil, nil, futuresSwitchMarginRate)
}
// GetTradeRecords returns list of user trades
func (by *Bybit) GetTradeRecords(ctx context.Context, symbol currency.Pair, orderID, order string, startTime, page, limit int64) ([]TradeResp, error) {
params := url.Values{}
resp := struct {
Data struct {
OrderID string `json:"order_id"`
Trades []TradeResp `json:"trade_list"`
} `json:"result"`
Error
}{}
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp.Data.Trades, err
}
params.Set("symbol", symbolValue)
if orderID != "" {
params.Set("order_id", orderID)
}
if order != "" {
params.Set("order", order)
}
if startTime != 0 {
params.Set("start_time", strconv.FormatInt(startTime, 10))
}
if page != 0 {
params.Set("page", strconv.FormatInt(page, 10))
}
if limit > 0 {
params.Set("limit", strconv.FormatInt(limit, 10))
}
return resp.Data.Trades, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodGet, futuresGetTrades, params, nil, &resp, futuresGetTradeRate)
}
// GetClosedTrades returns closed profit and loss records
func (by *Bybit) GetClosedTrades(ctx context.Context, symbol currency.Pair, executionType string, startTime, endTime time.Time, page, limit int64) ([]ClosedTrades, error) {
params := url.Values{}
resp := struct {
Data struct {
CurrentPage int64 `json:"current_page"`
Trades []ClosedTrades `json:"data"`
} `json:"result"`
Error
}{}
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp.Data.Trades, err
}
params.Set("symbol", symbolValue)
if executionType != "" {
params.Set("execution_type", executionType)
}
if !startTime.IsZero() {
params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10))
}
if !endTime.IsZero() {
params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10))
}
if page > 0 && page <= 50 {
params.Set("page", strconv.FormatInt(page, 10))
}
if limit > 0 {
params.Set("limit", strconv.FormatInt(limit, 10))
}
return resp.Data.Trades, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodGet, futuresGetClosedTrades, params, nil, &resp, futuresDefaultRate)
}
// SetRiskLimit sets risk limit
func (by *Bybit) SetRiskLimit(ctx context.Context, symbol currency.Pair, riskID, positionMode int64) (int64, error) {
resp := struct {
Result struct {
RiskID int64 `json:"risk_id"`
} `json:"result"`
Error
}{}
params := url.Values{}
symbolValue, err := by.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp.Result.RiskID, err
}
params.Set("symbol", symbolValue)
if riskID <= 0 {
return resp.Result.RiskID, errInvalidRiskID
}
params.Set("risk_id", strconv.FormatInt(riskID, 10))
if positionMode < 0 || positionMode > 2 {
return resp.Result.RiskID, errInvalidPositionMode
}
params.Set("position_idx", strconv.FormatInt(positionMode, 10))
return resp.Result.RiskID, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresSetRiskLimit, params, nil, &resp, futuresDefaultRate)
}

View File

@@ -0,0 +1,78 @@
package bybit
import (
"net/http"
"github.com/gorilla/websocket"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/stream"
)
// WsInverseConnect connects to inverse websocket feed
func (by *Bybit) WsInverseConnect() error {
if !by.Websocket.IsEnabled() || !by.IsEnabled() || !by.IsAssetWebsocketSupported(asset.CoinMarginedFutures) {
return errWebsocketNotEnabled
}
by.Websocket.Conn.SetURL(inversePublic)
var dialer websocket.Dialer
err := by.Websocket.Conn.Dial(&dialer, http.Header{})
if err != nil {
return err
}
by.Websocket.Conn.SetupPingHandler(stream.PingHandler{
MessageType: websocket.TextMessage,
Message: []byte(`{"op": "ping"}`),
Delay: bybitWebsocketTimer,
})
by.Websocket.Wg.Add(1)
go by.wsReadData(asset.CoinMarginedFutures, by.Websocket.Conn)
return nil
}
// GenerateInverseDefaultSubscriptions generates default subscription
func (by *Bybit) GenerateInverseDefaultSubscriptions() ([]stream.ChannelSubscription, error) {
var subscriptions []stream.ChannelSubscription
var channels = []string{chanOrderbook, chanPublicTrade, chanPublicTicker}
pairs, err := by.GetEnabledPairs(asset.CoinMarginedFutures)
if err != nil {
return nil, err
}
for z := range pairs {
for x := range channels {
subscriptions = append(subscriptions,
stream.ChannelSubscription{
Channel: channels[x],
Currency: pairs[z],
Asset: asset.CoinMarginedFutures,
})
}
}
return subscriptions, nil
}
// InverseSubscribe sends a subscription message to linear public channels.
func (by *Bybit) InverseSubscribe(channelSubscriptions []stream.ChannelSubscription) error {
return by.handleInversePayloadSubscription("subscribe", channelSubscriptions)
}
// InverseUnsubscribe sends an unsubscription messages through linear public channels.
func (by *Bybit) InverseUnsubscribe(channelSubscriptions []stream.ChannelSubscription) error {
return by.handleInversePayloadSubscription("unsubscribe", channelSubscriptions)
}
func (by *Bybit) handleInversePayloadSubscription(operation string, channelSubscriptions []stream.ChannelSubscription) error {
payloads, err := by.handleSubscriptions(asset.CoinMarginedFutures, operation, channelSubscriptions)
if err != nil {
return err
}
for a := range payloads {
// The options connection does not send the subscription request id back with the subscription notification payload
// therefore the code doesn't wait for the response to check whether the subscription is successful or not.
err = by.Websocket.Conn.SendJSONMessage(payloads[a])
if err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,98 @@
package bybit
import (
"context"
"net/http"
"github.com/gorilla/websocket"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/stream"
)
// WsLinearConnect connects to linear a websocket feed
func (by *Bybit) WsLinearConnect() error {
if !by.Websocket.IsEnabled() || !by.IsEnabled() || !by.IsAssetWebsocketSupported(asset.LinearContract) {
return errWebsocketNotEnabled
}
by.Websocket.Conn.SetURL(linearPublic)
var dialer websocket.Dialer
err := by.Websocket.Conn.Dial(&dialer, http.Header{})
if err != nil {
return err
}
by.Websocket.Conn.SetupPingHandler(stream.PingHandler{
MessageType: websocket.TextMessage,
Message: []byte(`{"op": "ping"}`),
Delay: bybitWebsocketTimer,
})
by.Websocket.Wg.Add(1)
go by.wsReadData(asset.LinearContract, by.Websocket.Conn)
if by.IsWebsocketAuthenticationSupported() {
err = by.WsAuth(context.TODO())
if err != nil {
by.Websocket.DataHandler <- err
by.Websocket.SetCanUseAuthenticatedEndpoints(false)
}
}
return nil
}
// GenerateLinearDefaultSubscriptions generates default subscription
func (by *Bybit) GenerateLinearDefaultSubscriptions() ([]stream.ChannelSubscription, error) {
var subscriptions []stream.ChannelSubscription
var channels = []string{chanOrderbook, chanPublicTrade, chanPublicTicker}
pairs, err := by.GetEnabledPairs(asset.USDTMarginedFutures)
if err != nil {
return nil, err
}
linearPairMap := map[asset.Item]currency.Pairs{
asset.USDTMarginedFutures: pairs,
}
usdcPairs, err := by.GetEnabledPairs(asset.USDCMarginedFutures)
if err != nil {
return nil, err
}
linearPairMap[asset.USDCMarginedFutures] = usdcPairs
pairs = append(pairs, usdcPairs...)
for a := range linearPairMap {
for p := range linearPairMap[a] {
for x := range channels {
subscriptions = append(subscriptions,
stream.ChannelSubscription{
Channel: channels[x],
Currency: pairs[p],
Asset: a,
})
}
}
}
return subscriptions, nil
}
// LinearSubscribe sends a subscription message to linear public channels.
func (by *Bybit) LinearSubscribe(channelSubscriptions []stream.ChannelSubscription) error {
return by.handleLinearPayloadSubscription("subscribe", channelSubscriptions)
}
// LinearUnsubscribe sends an unsubscription messages through linear public channels.
func (by *Bybit) LinearUnsubscribe(channelSubscriptions []stream.ChannelSubscription) error {
return by.handleLinearPayloadSubscription("unsubscribe", channelSubscriptions)
}
func (by *Bybit) handleLinearPayloadSubscription(operation string, channelSubscriptions []stream.ChannelSubscription) error {
payloads, err := by.handleSubscriptions(asset.USDTMarginedFutures, operation, channelSubscriptions)
if err != nil {
return err
}
for a := range payloads {
// The options connection does not send the subscription request id back with the subscription notification payload
// therefore the code doesn't wait for the response to check whether the subscription is successful or not.
err = by.Websocket.Conn.SendJSONMessage(payloads[a])
if err != nil {
return err
}
}
return nil
}

View File

@@ -1,4 +1,4 @@
//go:build !mock_test_off
//go:build mock_test_off
// This will build if build tag mock_test_off is parsed and will do live testing
// using all tests in (exchange)_test.go
@@ -13,50 +13,40 @@ import (
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
gctlog "github.com/thrasher-corp/gocryptotrader/log"
)
var mockTests = true
var mockTests = false
func TestMain(m *testing.M) {
b.SetDefaults()
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
log.Fatal("Bybit load config error", err)
}
bybitConfig, err := cfg.GetExchangeConfig("Bybit")
if err != nil {
log.Fatal("Bybit Setup() init error", err)
log.Fatal(err)
}
bybitConfig.API.AuthenticatedSupport = true
bybitConfig.API.Credentials.Key = apiKey
bybitConfig.API.Credentials.Secret = apiSecret
b.SetDefaults()
exchCfg, err := cfg.GetExchangeConfig("Bybit")
if err != nil {
log.Fatal(err)
}
exchCfg.API.Credentials.Key = apiKey
exchCfg.API.Credentials.Secret = apiSecret
exchCfg.API.AuthenticatedSupport = true
exchCfg.API.AuthenticatedWebsocketSupport = true
b.Websocket = sharedtestvalues.NewTestWebsocket()
err = b.Setup(bybitConfig)
if err != nil {
log.Fatal("Bybit setup error", err)
}
request.MaxRequestJobs = 100
b.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
log.Printf(sharedtestvalues.LiveTesting, b.Name)
err = b.UpdateTradablePairs(context.Background(), true)
err = b.Setup(exchCfg)
if err != nil {
log.Fatal("Bybit setup error", err)
log.Fatal(err)
}
// Turn on all pairs for testing
supportedAssets := b.GetAssetTypes(false)
for x := range supportedAssets {
avail, err := b.GetAvailablePairs(supportedAssets[x])
if err != nil {
log.Fatal(err)
}
err = b.CurrencyPairs.StorePairs(supportedAssets[x], avail, true)
if err != nil {
log.Fatal(err)
}
err = instantiateTradablePairs()
if err != nil {
log.Fatalf("%s %v", b.Name, err)
}
err = b.RetrieveAndSetAccountType(context.Background())
if err != nil {
gctlog.Errorf(gctlog.ExchangeSys, "RetrieveAndSetAccountType: %v", err)
}
os.Exit(m.Run())
}

View File

@@ -1,4 +1,4 @@
//go:build mock_test_off
//go:build !mock_test_off
// This will build if build tag mock_test_off is not parsed and will try to mock
// all tests in _test.go
@@ -11,6 +11,8 @@ import (
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/mock"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
@@ -21,20 +23,24 @@ const mockfile = "../../testdata/http_mock/bybit/bybit.json"
var mockTests = true
func TestMain(m *testing.M) {
b.SetDefaults()
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
log.Fatal("Bybit load config error", err)
log.Fatal(err)
}
bybitConfig, err := cfg.GetExchangeConfig("Bybit")
if err != nil {
log.Fatal("Bybit Setup() init error", err)
}
b.SkipAuthCheck = true
bybitConfig.API.AuthenticatedSupport = true
request.MaxRequestJobs = 100
bybitConfig.API.Credentials.Key = apiKey
bybitConfig.API.Credentials.Secret = apiSecret
b.SetDefaults()
bybitConfig.API.AuthenticatedSupport = true
bybitConfig.API.AuthenticatedWebsocketSupport = true
b.Websocket = sharedtestvalues.NewTestWebsocket()
err = b.Setup(bybitConfig)
if err != nil {
@@ -57,21 +63,42 @@ func TestMain(m *testing.M) {
}
}
request.MaxRequestJobs = 100
log.Printf(sharedtestvalues.MockTesting, b.Name)
err = b.UpdateTradablePairs(context.Background(), true)
if err != nil {
log.Fatal("Bybit setup error", err)
}
// Turn on all pairs for testing
supportedAssets := b.GetAssetTypes(false)
for x := range supportedAssets {
avail, err := b.GetAvailablePairs(supportedAssets[x])
spotTradablePair = currency.Pair{Base: currency.BTC, Quote: currency.USDT}
okay, err := b.IsPairEnabled(spotTradablePair, asset.Spot)
if !okay || err != nil {
err = b.CurrencyPairs.EnablePair(asset.Spot, spotTradablePair)
if err != nil {
log.Fatal(err)
}
err = b.CurrencyPairs.StorePairs(supportedAssets[x], avail, true)
}
usdtMarginedTradablePair = currency.Pair{Base: currency.NewCode("10000LADYS"), Quote: currency.USDT}
if okay, err = b.IsPairEnabled(usdtMarginedTradablePair, asset.USDTMarginedFutures); !okay || err != nil {
err = b.CurrencyPairs.EnablePair(asset.USDTMarginedFutures, usdtMarginedTradablePair)
if err != nil {
log.Fatal(err)
}
}
usdcMarginedTradablePair = currency.Pair{Base: currency.ETH, Quote: currency.PERP}
if okay, err = b.IsPairEnabled(usdcMarginedTradablePair, asset.USDCMarginedFutures); !okay || err != nil {
err = b.CurrencyPairs.EnablePair(asset.USDCMarginedFutures, usdcMarginedTradablePair)
if err != nil {
log.Fatal(err)
}
}
inverseTradablePair = currency.Pair{Base: currency.ADA, Quote: currency.USD}
if okay, err = b.IsPairEnabled(inverseTradablePair, asset.CoinMarginedFutures); !okay || err != nil {
err = b.CurrencyPairs.EnablePair(asset.CoinMarginedFutures, inverseTradablePair)
if err != nil {
log.Fatal(err)
}
}
optionsTradablePair = currency.Pair{Base: currency.BTC, Delimiter: currency.DashDelimiter, Quote: currency.NewCode("29DEC23-80000-C")}
if okay, err = b.IsPairEnabled(optionsTradablePair, asset.Options); !okay || err != nil {
err = b.CurrencyPairs.EnablePair(asset.Options, optionsTradablePair)
if err != nil {
log.Fatal(err)
}

View File

@@ -0,0 +1,85 @@
package bybit
import (
"encoding/json"
"net/http"
"strconv"
"github.com/gorilla/websocket"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/stream"
)
// WsOptionsConnect connects to options a websocket feed
func (by *Bybit) WsOptionsConnect() error {
if !by.Websocket.IsEnabled() || !by.IsEnabled() || !by.IsAssetWebsocketSupported(asset.Options) {
return errWebsocketNotEnabled
}
by.Websocket.Conn.SetURL(optionPublic)
var dialer websocket.Dialer
err := by.Websocket.Conn.Dial(&dialer, http.Header{})
if err != nil {
return err
}
pingMessage := PingMessage{Operation: "ping", RequestID: strconv.FormatInt(by.Websocket.Conn.GenerateMessageID(false), 10)}
pingData, err := json.Marshal(pingMessage)
if err != nil {
return err
}
by.Websocket.Conn.SetupPingHandler(stream.PingHandler{
MessageType: websocket.TextMessage,
Message: pingData,
Delay: bybitWebsocketTimer,
})
by.Websocket.Wg.Add(1)
go by.wsReadData(asset.Options, by.Websocket.Conn)
return nil
}
// GenerateOptionsDefaultSubscriptions generates default subscription
func (by *Bybit) GenerateOptionsDefaultSubscriptions() ([]stream.ChannelSubscription, error) {
var subscriptions []stream.ChannelSubscription
var channels = []string{chanOrderbook, chanPublicTrade, chanPublicTicker}
pairs, err := by.GetEnabledPairs(asset.Options)
if err != nil {
return nil, err
}
for z := range pairs {
for x := range channels {
subscriptions = append(subscriptions,
stream.ChannelSubscription{
Channel: channels[x],
Currency: pairs[z],
Asset: asset.Options,
})
}
}
return subscriptions, nil
}
// OptionSubscribe sends a subscription message to options public channels.
func (by *Bybit) OptionSubscribe(channelSubscriptions []stream.ChannelSubscription) error {
return by.handleOptionsPayloadSubscription("subscribe", channelSubscriptions)
}
// OptionUnsubscribe sends an unsubscription messages through options public channels.
func (by *Bybit) OptionUnsubscribe(channelSubscriptions []stream.ChannelSubscription) error {
return by.handleOptionsPayloadSubscription("unsubscribe", channelSubscriptions)
}
func (by *Bybit) handleOptionsPayloadSubscription(operation string, channelSubscriptions []stream.ChannelSubscription) error {
payloads, err := by.handleSubscriptions(asset.Options, operation, channelSubscriptions)
if err != nil {
return err
}
for a := range payloads {
// The options connection does not send the subscription request id back with the subscription notification payload
// therefore the code doesn't wait for the response to check whether the subscription is successful or not.
err = by.Websocket.Conn.SendJSONMessage(payloads[a])
if err != nil {
return err
}
}
return nil
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,801 +0,0 @@
package bybit
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"strings"
"time"
"github.com/gorilla/websocket"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-corp/gocryptotrader/exchanges/stream"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/trade"
"github.com/thrasher-corp/gocryptotrader/log"
)
const (
wsCoinMarginedPath = "realtime"
subscribe = "subscribe"
unsubscribe = "unsubscribe"
dot = "."
// public endpoints
wsOrder25 = "orderBookL2_25"
wsOrder200 = "orderBook_200"
wsTrade = "trade"
wsInsurance = "insurance"
wsInstrument = "instrument_info"
wsCoinMarket = "klineV2"
wsLiquidation = "liquidation"
wsOperationSnapshot = "snapshot"
wsOperationDelta = "delta"
wsOrderbookActionDelete = "delete"
wsOrderbookActionUpdate = "update"
wsOrderbookActionInsert = "insert"
wsKlineV2 = "klineV2"
// private endpoints
wsPosition = "position"
wsExecution = "execution"
wsOrder = "order"
wsStopOrder = "stop_order"
wsWallet = "wallet"
)
var pingRequest = WsFuturesReq{Topic: stream.Ping}
// WsCoinConnect connects to a CMF websocket feed
func (by *Bybit) WsCoinConnect() error {
if !by.Websocket.IsEnabled() || !by.IsEnabled() {
return errors.New(stream.WebsocketNotEnabled)
}
var dialer websocket.Dialer
err := by.Websocket.Conn.Dial(&dialer, http.Header{})
if err != nil {
return err
}
pingMsg, err := json.Marshal(pingRequest)
if err != nil {
return err
}
by.Websocket.Conn.SetupPingHandler(stream.PingHandler{
Message: pingMsg,
MessageType: websocket.PingMessage,
Delay: bybitWebsocketTimer,
})
if by.Verbose {
log.Debugf(log.ExchangeSys, "%s Connected to Websocket.\n", by.Name)
}
by.Websocket.Wg.Add(1)
go by.wsCoinReadData()
if by.IsWebsocketAuthenticationSupported() {
err = by.WsCoinAuth(context.TODO())
if err != nil {
by.Websocket.DataHandler <- err
by.Websocket.SetCanUseAuthenticatedEndpoints(false)
}
}
by.Websocket.Wg.Add(1)
go by.WsDataHandler()
return nil
}
// WsCoinAuth sends an authentication message to receive auth data
func (by *Bybit) WsCoinAuth(ctx context.Context) error {
creds, err := by.GetCredentials(ctx)
if err != nil {
return err
}
intNonce := (time.Now().Unix() + 1) * 1000
strNonce := strconv.FormatInt(intNonce, 10)
hmac, err := crypto.GetHMAC(
crypto.HashSHA256,
[]byte("GET/realtime"+strNonce),
[]byte(creds.Secret),
)
if err != nil {
return err
}
sign := crypto.HexEncodeToString(hmac)
req := Authenticate{
Operation: "auth",
Args: []interface{}{creds.Key, intNonce, sign},
}
return by.Websocket.Conn.SendJSONMessage(req)
}
// SubscribeCoin sends a websocket message to receive data from the channel
func (by *Bybit) SubscribeCoin(channelsToSubscribe []stream.ChannelSubscription) error {
var errs error
for i := range channelsToSubscribe {
var sub WsFuturesReq
sub.Topic = subscribe
sub.Args = append(sub.Args, formatArgs(channelsToSubscribe[i].Channel, channelsToSubscribe[i].Params))
err := by.Websocket.Conn.SendJSONMessage(sub)
if err != nil {
errs = common.AppendError(errs, err)
continue
}
by.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i])
}
if errs != nil {
return errs
}
return nil
}
func formatArgs(channel string, params map[string]interface{}) string {
argStr := channel
for x := range params {
argStr += dot + fmt.Sprintf("%v", params[x])
}
return argStr
}
// UnsubscribeCoin sends a websocket message to stop receiving data from the channel
func (by *Bybit) UnsubscribeCoin(channelsToUnsubscribe []stream.ChannelSubscription) error {
var errs error
for i := range channelsToUnsubscribe {
var unSub WsFuturesReq
unSub.Topic = unsubscribe
formattedPair, err := by.FormatExchangeCurrency(channelsToUnsubscribe[i].Currency, asset.CoinMarginedFutures)
if err != nil {
errs = common.AppendError(errs, err)
continue
}
unSub.Args = append(unSub.Args, channelsToUnsubscribe[i].Channel+dot+formattedPair.String())
err = by.Websocket.Conn.SendJSONMessage(unSub)
if err != nil {
errs = common.AppendError(errs, err)
continue
}
by.Websocket.RemoveSubscriptions(channelsToUnsubscribe[i])
}
return errs
}
// wsCoinReadData gets and passes on websocket messages for processing
func (by *Bybit) wsCoinReadData() {
by.Websocket.Wg.Add(1)
defer by.Websocket.Wg.Done()
for {
select {
case <-by.Websocket.ShutdownC:
return
default:
resp := by.Websocket.Conn.ReadMessage()
if resp.Raw == nil {
return
}
err := by.wsCoinHandleData(resp.Raw)
if err != nil {
by.Websocket.DataHandler <- err
}
}
}
}
func (by *Bybit) wsCoinHandleData(respRaw []byte) error {
var multiStreamData map[string]interface{}
err := json.Unmarshal(respRaw, &multiStreamData)
if err != nil {
return err
}
t, ok := multiStreamData["topic"].(string)
if !ok {
log.Errorf(log.ExchangeSys, "%s Received unhandle message on websocket: %v\n", by.Name, multiStreamData)
return nil
}
topics := strings.Split(t, dot)
if len(topics) < 1 {
return errors.New(by.Name + " - topic could not be extracted from response")
}
if wsType, ok := multiStreamData["type"].(string); ok {
switch topics[0] {
case wsOrder25, wsOrder200:
var enabled bool
switch wsType {
case wsOperationSnapshot:
var response WsFuturesOrderbook
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
var p currency.Pair
p, enabled, err = by.MatchSymbolCheckEnabled(response.OBData[0].Symbol, asset.CoinMarginedFutures, false)
if err != nil {
return err
}
if !enabled {
return nil
}
err = by.processOrderbook(response.OBData,
response.Type,
p,
asset.CoinMarginedFutures)
if err != nil {
return err
}
case wsOperationDelta:
var response WsCoinDeltaOrderbook
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
if len(response.OBData.Delete) > 0 {
var p currency.Pair
p, enabled, err = by.MatchSymbolCheckEnabled(response.OBData.Delete[0].Symbol, asset.CoinMarginedFutures, false)
if err != nil {
return err
}
if !enabled {
return nil
}
err = by.processOrderbook(response.OBData.Delete,
wsOrderbookActionDelete,
p,
asset.CoinMarginedFutures)
if err != nil {
return err
}
}
if len(response.OBData.Update) > 0 {
var p currency.Pair
p, enabled, err = by.MatchSymbolCheckEnabled(response.OBData.Update[0].Symbol, asset.CoinMarginedFutures, false)
if err != nil {
return err
}
if !enabled {
return nil
}
err = by.processOrderbook(response.OBData.Update,
wsOrderbookActionUpdate,
p,
asset.CoinMarginedFutures)
if err != nil {
return err
}
}
if len(response.OBData.Insert) > 0 {
var p currency.Pair
p, enabled, err = by.MatchSymbolCheckEnabled(response.OBData.Insert[0].Symbol, asset.CoinMarginedFutures, false)
if err != nil {
return err
}
if !enabled {
return nil
}
err = by.processOrderbook(response.OBData.Insert,
wsOrderbookActionInsert,
p,
asset.CoinMarginedFutures)
if err != nil {
return err
}
}
default:
by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + "unsupported orderbook operation"}
}
case wsTrades:
if !by.IsSaveTradeDataEnabled() {
return nil
}
var response WsFuturesTrade
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
counter := 0
trades := make([]trade.Data, len(response.TradeData))
for i := range response.TradeData {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.TradeData[0].Symbol, asset.CoinMarginedFutures, false)
if err != nil {
return err
}
var oSide order.Side
oSide, err = order.StringToOrderSide(response.TradeData[i].Side)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
Err: err,
}
}
trades[counter] = trade.Data{
TID: response.TradeData[i].ID,
Exchange: by.Name,
CurrencyPair: p,
AssetType: asset.CoinMarginedFutures,
Side: oSide,
Price: response.TradeData[i].Price,
Amount: response.TradeData[i].Size,
Timestamp: response.TradeData[i].Time,
}
counter++
}
return by.AddTradesToBuffer(trades...)
case wsKlineV2:
var response WsFuturesKline
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(topics[len(topics)-1], asset.CoinMarginedFutures, false)
if err != nil {
return err
}
for i := range response.KlineData {
by.Websocket.DataHandler <- stream.KlineData{
Pair: p,
AssetType: asset.CoinMarginedFutures,
Exchange: by.Name,
OpenPrice: response.KlineData[i].Open,
HighPrice: response.KlineData[i].High,
LowPrice: response.KlineData[i].Low,
ClosePrice: response.KlineData[i].Close,
Volume: response.KlineData[i].Volume,
Timestamp: response.KlineData[i].Timestamp.Time(),
}
}
case wsInsurance:
var response WsInsurance
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
by.Websocket.DataHandler <- response.Data
case wsInstrument:
if wsType, ok := multiStreamData["type"].(string); ok {
switch wsType {
case wsOperationSnapshot:
var response WsTicker
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Ticker.Symbol, asset.CoinMarginedFutures, false)
if err != nil {
return err
}
by.Websocket.DataHandler <- &ticker.Price{
ExchangeName: by.Name,
Last: response.Ticker.LastPrice.Float64(),
High: response.Ticker.HighPrice24h.Float64(),
Low: response.Ticker.LowPrice24h.Float64(),
Bid: response.Ticker.BidPrice,
Ask: response.Ticker.AskPrice,
Volume: response.Ticker.Volume24h,
Close: response.Ticker.PrevPrice24h.Float64(),
LastUpdated: response.Ticker.UpdateAt,
AssetType: asset.CoinMarginedFutures,
Pair: p,
}
case wsOperationDelta:
var response WsDeltaTicker
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
if len(response.Data.Delete) > 0 {
for x := range response.Data.Delete {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Data.Delete[x].Symbol, asset.CoinMarginedFutures, false)
if err != nil {
return err
}
by.Websocket.DataHandler <- &ticker.Price{
ExchangeName: by.Name,
Last: response.Data.Delete[x].LastPrice.Float64(),
High: response.Data.Delete[x].HighPrice24h.Float64(),
Low: response.Data.Delete[x].LowPrice24h.Float64(),
Bid: response.Data.Delete[x].BidPrice,
Ask: response.Data.Delete[x].AskPrice,
Volume: response.Data.Delete[x].Volume24h,
Close: response.Data.Delete[x].PrevPrice24h.Float64(),
LastUpdated: response.Data.Delete[x].UpdateAt,
AssetType: asset.CoinMarginedFutures,
Pair: p,
}
}
}
if len(response.Data.Update) > 0 {
for x := range response.Data.Update {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Data.Update[x].Symbol, asset.CoinMarginedFutures, false)
if err != nil {
return err
}
by.Websocket.DataHandler <- &ticker.Price{
ExchangeName: by.Name,
Last: response.Data.Update[x].LastPrice.Float64(),
High: response.Data.Update[x].HighPrice24h.Float64(),
Low: response.Data.Update[x].LowPrice24h.Float64(),
Bid: response.Data.Update[x].BidPrice,
Ask: response.Data.Update[x].AskPrice,
Volume: response.Data.Update[x].Volume24h,
Close: response.Data.Update[x].PrevPrice24h.Float64(),
LastUpdated: response.Data.Update[x].UpdateAt,
AssetType: asset.CoinMarginedFutures,
Pair: p,
}
}
}
if len(response.Data.Insert) > 0 {
for x := range response.Data.Insert {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Data.Insert[x].Symbol, asset.CoinMarginedFutures, false)
if err != nil {
return err
}
by.Websocket.DataHandler <- &ticker.Price{
ExchangeName: by.Name,
Last: response.Data.Insert[x].LastPrice.Float64(),
High: response.Data.Insert[x].HighPrice24h.Float64(),
Low: response.Data.Insert[x].LowPrice24h.Float64(),
Bid: response.Data.Insert[x].BidPrice,
Ask: response.Data.Insert[x].AskPrice,
Volume: response.Data.Insert[x].Volume24h,
Close: response.Data.Insert[x].PrevPrice24h.Float64(),
LastUpdated: response.Data.Insert[x].UpdateAt,
AssetType: asset.CoinMarginedFutures,
Pair: p,
}
}
}
default:
by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + "unsupported ticker operation"}
}
}
case wsLiquidation:
var response WsFuturesLiquidation
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
by.Websocket.DataHandler <- response.Data
case wsPosition:
var response WsFuturesPosition
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
by.Websocket.DataHandler <- response.Data
case wsExecution:
var response WsFuturesExecution
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
for i := range response.Data {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Data[i].Symbol, asset.CoinMarginedFutures, false)
if err != nil {
return err
}
var oSide order.Side
oSide, err = order.StringToOrderSide(response.Data[i].Side)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[i].OrderID,
Err: err,
}
}
var oStatus order.Status
oStatus, err = order.StringToOrderStatus(response.Data[i].ExecutionType)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[i].OrderID,
Err: err,
}
}
by.Websocket.DataHandler <- &order.Detail{
Exchange: by.Name,
OrderID: response.Data[i].OrderID,
AssetType: asset.CoinMarginedFutures,
Pair: p,
Price: response.Data[i].Price.Float64(),
Amount: response.Data[i].OrderQty,
Side: oSide,
Status: oStatus,
Trades: []order.TradeHistory{
{
Price: response.Data[i].Price.Float64(),
Amount: response.Data[i].OrderQty,
Exchange: by.Name,
Side: oSide,
Timestamp: response.Data[i].Time,
TID: response.Data[i].ExecutionID,
IsMaker: response.Data[i].IsMaker,
},
},
}
}
case wsOrder:
var response WsOrder
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
for x := range response.Data {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Data[x].Symbol, asset.CoinMarginedFutures, false)
if err != nil {
return err
}
var oSide order.Side
oSide, err = order.StringToOrderSide(response.Data[x].Side)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Err: err,
}
}
var oType order.Type
oType, err = order.StringToOrderType(response.Data[x].OrderType)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Err: err,
}
}
var oStatus order.Status
oStatus, err = order.StringToOrderStatus(response.Data[x].OrderStatus)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Err: err,
}
}
by.Websocket.DataHandler <- &order.Detail{
Price: response.Data[x].Price.Float64(),
Amount: response.Data[x].OrderQty,
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Type: oType,
Side: oSide,
Status: oStatus,
AssetType: asset.CoinMarginedFutures,
Date: response.Data[x].Time,
Pair: p,
Trades: []order.TradeHistory{
{
Price: response.Data[x].Price.Float64(),
Amount: response.Data[x].OrderQty,
Exchange: by.Name,
Side: oSide,
Timestamp: response.Data[x].Time,
},
},
}
}
case wsStopOrder:
var response WsFuturesStopOrder
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
for x := range response.Data {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Data[x].Symbol, asset.CoinMarginedFutures, false)
if err != nil {
return err
}
var oSide order.Side
oSide, err = order.StringToOrderSide(response.Data[x].Side)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Err: err,
}
}
var oType order.Type
oType, err = order.StringToOrderType(response.Data[x].OrderType)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Err: err,
}
}
var oStatus order.Status
oStatus, err = order.StringToOrderStatus(response.Data[x].OrderStatus)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Err: err,
}
}
by.Websocket.DataHandler <- &order.Detail{
Price: response.Data[x].Price.Float64(),
Amount: response.Data[x].OrderQty,
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
AccountID: strconv.FormatInt(response.Data[x].UserID, 10),
Type: oType,
Side: oSide,
Status: oStatus,
AssetType: asset.CoinMarginedFutures,
Date: response.Data[x].Time,
Pair: p,
Trades: []order.TradeHistory{
{
Price: response.Data[x].Price.Float64(),
Amount: response.Data[x].OrderQty,
Exchange: by.Name,
Side: oSide,
Timestamp: response.Data[x].Time,
},
},
}
}
case wsWallet:
var response WsFuturesWallet
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
by.Websocket.DataHandler <- response.Data
default:
by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + string(respRaw)}
}
}
return nil
}
// processOrderbook processes orderbook updates
func (by *Bybit) processOrderbook(data []WsFuturesOrderbookData, action string, p currency.Pair, a asset.Item) error {
if len(data) < 1 {
return errors.New("no orderbook data")
}
switch action {
case wsOperationSnapshot:
var book orderbook.Base
for i := range data {
item := orderbook.Item{
Price: data[i].Price.Float64(),
Amount: data[i].Size,
ID: data[i].ID,
}
switch {
case strings.EqualFold(data[i].Side, sideSell):
book.Asks = append(book.Asks, item)
case strings.EqualFold(data[i].Side, sideBuy):
book.Bids = append(book.Bids, item)
default:
return fmt.Errorf("could not process websocket orderbook update, order side could not be matched for %s",
data[i].Side)
}
}
book.Asset = a
book.Pair = p
book.Exchange = by.Name
book.VerifyOrderbook = by.CanVerifyOrderbook
err := by.Websocket.Orderbook.LoadSnapshot(&book)
if err != nil {
return fmt.Errorf("process orderbook error - %s", err)
}
default:
updateAction, err := by.GetActionFromString(action)
if err != nil {
return err
}
var asks, bids []orderbook.Item
for i := range data {
item := orderbook.Item{
Price: data[i].Price.Float64(),
Amount: data[i].Size,
ID: data[i].ID,
}
switch {
case strings.EqualFold(data[i].Side, sideSell):
asks = append(asks, item)
case strings.EqualFold(data[i].Side, sideBuy):
bids = append(bids, item)
default:
return fmt.Errorf("could not process websocket orderbook update, order side could not be matched for %s",
data[i].Side)
}
}
err = by.Websocket.Orderbook.Update(&orderbook.Update{
Bids: bids,
Asks: asks,
Pair: p,
Asset: a,
Action: updateAction,
})
if err != nil {
return err
}
}
return nil
}
// GetActionFromString matches a string action to an internal action.
func (by *Bybit) GetActionFromString(s string) (orderbook.Action, error) {
switch s {
case wsOrderbookActionUpdate:
return orderbook.Amend, nil
case wsOrderbookActionDelete:
return orderbook.Delete, nil
case wsOrderbookActionInsert:
return orderbook.Insert, nil
}
return 0, fmt.Errorf("%s %w", s, orderbook.ErrInvalidAction)
}

View File

@@ -1,634 +0,0 @@
package bybit
import (
"context"
"encoding/json"
"errors"
"net/http"
"strconv"
"strings"
"time"
"github.com/gorilla/websocket"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/stream"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/trade"
"github.com/thrasher-corp/gocryptotrader/log"
)
// WsFuturesConnect connects to a Futures websocket feed
func (by *Bybit) WsFuturesConnect() error {
if !by.Websocket.IsEnabled() || !by.IsEnabled() {
return errors.New(stream.WebsocketNotEnabled)
}
var dialer websocket.Dialer
err := by.Websocket.Conn.Dial(&dialer, http.Header{})
if err != nil {
return err
}
pingMsg, err := json.Marshal(pingRequest)
if err != nil {
return err
}
by.Websocket.Conn.SetupPingHandler(stream.PingHandler{
Message: pingMsg,
MessageType: websocket.PingMessage,
Delay: bybitWebsocketTimer,
})
if by.Verbose {
log.Debugf(log.ExchangeSys, "%s Connected to Websocket.\n", by.Name)
}
go by.wsFuturesReadData()
if by.IsWebsocketAuthenticationSupported() {
err = by.WsFuturesAuth(context.TODO())
if err != nil {
by.Websocket.DataHandler <- err
by.Websocket.SetCanUseAuthenticatedEndpoints(false)
}
}
return nil
}
// WsFuturesAuth sends an authentication message to receive auth data
func (by *Bybit) WsFuturesAuth(ctx context.Context) error {
creds, err := by.GetCredentials(ctx)
if err != nil {
return err
}
intNonce := (time.Now().Unix() + 1) * 1000
strNonce := strconv.FormatInt(intNonce, 10)
hmac, err := crypto.GetHMAC(
crypto.HashSHA256,
[]byte("GET/realtime"+strNonce),
[]byte(creds.Secret),
)
if err != nil {
return err
}
sign := crypto.HexEncodeToString(hmac)
req := Authenticate{
Operation: "auth",
Args: []interface{}{creds.Key, intNonce, sign},
}
return by.Websocket.Conn.SendJSONMessage(req)
}
// SubscribeFutures sends a websocket message to receive data from the channel
func (by *Bybit) SubscribeFutures(channelsToSubscribe []stream.ChannelSubscription) error {
var errs error
for i := range channelsToSubscribe {
var sub WsFuturesReq
sub.Topic = subscribe
sub.Args = append(sub.Args, formatArgs(channelsToSubscribe[i].Channel, channelsToSubscribe[i].Params))
err := by.Websocket.Conn.SendJSONMessage(sub)
if err != nil {
errs = common.AppendError(errs, err)
continue
}
by.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i])
}
return errs
}
// UnsubscribeFutures sends a websocket message to stop receiving data from the channel
func (by *Bybit) UnsubscribeFutures(channelsToUnsubscribe []stream.ChannelSubscription) error {
var errs error
for i := range channelsToUnsubscribe {
var unSub WsFuturesReq
unSub.Topic = unsubscribe
formattedPair, err := by.FormatExchangeCurrency(channelsToUnsubscribe[i].Currency, asset.Futures)
if err != nil {
errs = common.AppendError(errs, err)
continue
}
unSub.Args = append(unSub.Args, channelsToUnsubscribe[i].Channel+dot+formattedPair.String())
err = by.Websocket.Conn.SendJSONMessage(unSub)
if err != nil {
errs = common.AppendError(errs, err)
continue
}
by.Websocket.RemoveSubscriptions(channelsToUnsubscribe[i])
}
return errs
}
// wsFuturesReadData gets and passes on websocket messages for processing
func (by *Bybit) wsFuturesReadData() {
by.Websocket.Wg.Add(1)
defer by.Websocket.Wg.Done()
for {
select {
case <-by.Websocket.ShutdownC:
return
default:
resp := by.Websocket.Conn.ReadMessage()
if resp.Raw == nil {
return
}
err := by.wsFuturesHandleData(resp.Raw)
if err != nil {
by.Websocket.DataHandler <- err
}
}
}
}
func (by *Bybit) wsFuturesHandleData(respRaw []byte) error {
var multiStreamData map[string]interface{}
err := json.Unmarshal(respRaw, &multiStreamData)
if err != nil {
return err
}
t, ok := multiStreamData["topic"].(string)
if !ok {
log.Errorf(log.ExchangeSys, "%s Received unhandle message on websocket: %v\n", by.Name, multiStreamData)
return nil
}
topics := strings.Split(t, dot)
if len(topics) < 1 {
return errors.New(by.Name + " - topic could not be extracted from response")
}
switch topics[0] {
case wsOrder25, wsOrder200:
if wsType, ok := multiStreamData["type"].(string); ok {
switch wsType {
case wsOperationSnapshot:
var response WsFuturesOrderbook
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.OBData[0].Symbol, asset.Futures, false)
if err != nil {
return err
}
err = by.processOrderbook(response.OBData,
response.Type,
p,
asset.Futures)
if err != nil {
return err
}
case wsOperationDelta:
var response WsCoinDeltaOrderbook
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
if len(response.OBData.Delete) > 0 {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.OBData.Delete[0].Symbol, asset.Futures, false)
if err != nil {
return err
}
err = by.processOrderbook(response.OBData.Delete,
wsOrderbookActionDelete,
p,
asset.Futures)
if err != nil {
return err
}
}
if len(response.OBData.Update) > 0 {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.OBData.Update[0].Symbol, asset.Futures, false)
if err != nil {
return err
}
err = by.processOrderbook(response.OBData.Update,
wsOrderbookActionUpdate,
p,
asset.Futures)
if err != nil {
return err
}
}
if len(response.OBData.Insert) > 0 {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.OBData.Insert[0].Symbol, asset.Futures, false)
if err != nil {
return err
}
err = by.processOrderbook(response.OBData.Insert,
wsOrderbookActionInsert,
p,
asset.Futures)
if err != nil {
return err
}
}
default:
by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + "unsupported orderbook operation"}
}
}
case wsTrades:
if !by.IsSaveTradeDataEnabled() {
return nil
}
var response WsFuturesTrade
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
counter := 0
trades := make([]trade.Data, len(response.TradeData))
for i := range response.TradeData {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.TradeData[0].Symbol, asset.Futures, false)
if err != nil {
return err
}
var oSide order.Side
oSide, err = order.StringToOrderSide(response.TradeData[i].Side)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
Err: err,
}
}
trades[counter] = trade.Data{
TID: response.TradeData[i].ID,
Exchange: by.Name,
CurrencyPair: p,
AssetType: asset.Futures,
Side: oSide,
Price: response.TradeData[i].Price,
Amount: response.TradeData[i].Size,
Timestamp: response.TradeData[i].Time,
}
counter++
}
return by.AddTradesToBuffer(trades...)
case wsKlineV2:
var response WsFuturesKline
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(topics[len(topics)-1], asset.Futures, false)
if err != nil {
return err
}
for i := range response.KlineData {
by.Websocket.DataHandler <- stream.KlineData{
Pair: p,
AssetType: asset.Futures,
Exchange: by.Name,
OpenPrice: response.KlineData[i].Open,
HighPrice: response.KlineData[i].High,
LowPrice: response.KlineData[i].Low,
ClosePrice: response.KlineData[i].Close,
Volume: response.KlineData[i].Volume,
Timestamp: response.KlineData[i].Timestamp.Time(),
}
}
case wsInstrument:
if wsType, ok := multiStreamData["type"].(string); ok {
switch wsType {
case wsOperationSnapshot:
var response WsFuturesTicker
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Ticker.Symbol, asset.Futures, false)
if err != nil {
return err
}
by.Websocket.DataHandler <- &ticker.Price{
ExchangeName: by.Name,
Last: response.Ticker.LastPrice.Float64(),
High: response.Ticker.HighPrice24h.Float64(),
Low: response.Ticker.LowPrice24h.Float64(),
Bid: response.Ticker.BidPrice,
Ask: response.Ticker.AskPrice,
Volume: response.Ticker.Volume24h,
Close: response.Ticker.PrevPrice24h.Float64(),
LastUpdated: response.Ticker.UpdateAt,
AssetType: asset.Futures,
Pair: p,
}
case wsOperationDelta:
var response WsDeltaFuturesTicker
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
if len(response.Data.Delete) > 0 {
for x := range response.Data.Delete {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Data.Delete[x].Symbol, asset.Futures, false)
if err != nil {
return err
}
by.Websocket.DataHandler <- &ticker.Price{
ExchangeName: by.Name,
Last: response.Data.Delete[x].LastPrice.Float64(),
High: response.Data.Delete[x].HighPrice24h.Float64(),
Low: response.Data.Delete[x].LowPrice24h.Float64(),
Bid: response.Data.Delete[x].BidPrice,
Ask: response.Data.Delete[x].AskPrice,
Volume: response.Data.Delete[x].Volume24h,
Close: response.Data.Delete[x].PrevPrice24h.Float64(),
LastUpdated: response.Data.Delete[x].UpdateAt,
AssetType: asset.Futures,
Pair: p,
}
}
}
if len(response.Data.Update) > 0 {
for x := range response.Data.Update {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Data.Update[x].Symbol, asset.Futures, false)
if err != nil {
return err
}
by.Websocket.DataHandler <- &ticker.Price{
ExchangeName: by.Name,
Last: response.Data.Update[x].LastPrice.Float64(),
High: response.Data.Update[x].HighPrice24h.Float64(),
Low: response.Data.Update[x].LowPrice24h.Float64(),
Bid: response.Data.Update[x].BidPrice,
Ask: response.Data.Update[x].AskPrice,
Volume: response.Data.Update[x].Volume24h,
Close: response.Data.Update[x].PrevPrice24h.Float64(),
LastUpdated: response.Data.Update[x].UpdateAt,
AssetType: asset.Futures,
Pair: p,
}
}
}
if len(response.Data.Insert) > 0 {
for x := range response.Data.Insert {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Data.Insert[x].Symbol, asset.Futures, false)
if err != nil {
return err
}
by.Websocket.DataHandler <- &ticker.Price{
ExchangeName: by.Name,
Last: response.Data.Insert[x].LastPrice.Float64(),
High: response.Data.Insert[x].HighPrice24h.Float64(),
Low: response.Data.Insert[x].LowPrice24h.Float64(),
Bid: response.Data.Insert[x].BidPrice,
Ask: response.Data.Insert[x].AskPrice,
Volume: response.Data.Insert[x].Volume24h,
Close: response.Data.Insert[x].PrevPrice24h.Float64(),
LastUpdated: response.Data.Insert[x].UpdateAt,
AssetType: asset.Futures,
Pair: p,
}
}
}
default:
by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + "unsupported ticker operation"}
}
}
case wsInsurance:
var response WsInsurance
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
by.Websocket.DataHandler <- response.Data
case wsPosition:
var response WsFuturesPosition
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
by.Websocket.DataHandler <- response.Data
case wsExecution:
var response WsFuturesExecution
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
for i := range response.Data {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Data[i].Symbol, asset.Futures, false)
if err != nil {
return err
}
var oSide order.Side
oSide, err = order.StringToOrderSide(response.Data[i].Side)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[i].OrderID,
Err: err,
}
}
var oStatus order.Status
oStatus, err = order.StringToOrderStatus(response.Data[i].ExecutionType)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[i].OrderID,
Err: err,
}
}
by.Websocket.DataHandler <- &order.Detail{
Exchange: by.Name,
OrderID: response.Data[i].OrderID,
AssetType: asset.Futures,
Pair: p,
Price: response.Data[i].Price.Float64(),
Amount: response.Data[i].OrderQty,
Side: oSide,
Status: oStatus,
Trades: []order.TradeHistory{
{
Price: response.Data[i].Price.Float64(),
Amount: response.Data[i].OrderQty,
Exchange: by.Name,
Side: oSide,
Timestamp: response.Data[i].Time,
},
},
}
}
case wsOrder:
var response WsOrder
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
for x := range response.Data {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Data[x].Symbol, asset.Futures, false)
if err != nil {
return err
}
var oSide order.Side
oSide, err = order.StringToOrderSide(response.Data[x].Side)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Err: err,
}
}
var oType order.Type
oType, err = order.StringToOrderType(response.Data[x].OrderType)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Err: err,
}
}
var oStatus order.Status
oStatus, err = order.StringToOrderStatus(response.Data[x].OrderStatus)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Err: err,
}
}
by.Websocket.DataHandler <- &order.Detail{
Price: response.Data[x].Price.Float64(),
Amount: response.Data[x].OrderQty,
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Type: oType,
Side: oSide,
Status: oStatus,
AssetType: asset.Futures,
Date: response.Data[x].Time,
Pair: p,
Trades: []order.TradeHistory{
{
Price: response.Data[x].Price.Float64(),
Amount: response.Data[x].OrderQty,
Exchange: by.Name,
Side: oSide,
Timestamp: response.Data[x].Time,
},
},
}
}
case wsStopOrder:
var response WsFuturesStopOrder
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
for x := range response.Data {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Data[x].Symbol, asset.Futures, false)
if err != nil {
return err
}
var oSide order.Side
oSide, err = order.StringToOrderSide(response.Data[x].Side)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Err: err,
}
}
var oType order.Type
oType, err = order.StringToOrderType(response.Data[x].OrderType)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Err: err,
}
}
var oStatus order.Status
oStatus, err = order.StringToOrderStatus(response.Data[x].OrderStatus)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Err: err,
}
}
by.Websocket.DataHandler <- &order.Detail{
Price: response.Data[x].Price.Float64(),
Amount: response.Data[x].OrderQty,
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
AccountID: strconv.FormatInt(response.Data[x].UserID, 10),
Type: oType,
Side: oSide,
Status: oStatus,
AssetType: asset.Futures,
Date: response.Data[x].Time,
Pair: p,
Trades: []order.TradeHistory{
{
Price: response.Data[x].Price.Float64(),
Amount: response.Data[x].OrderQty,
Exchange: by.Name,
Side: oSide,
Timestamp: response.Data[x].Time,
},
},
}
}
default:
by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + string(respRaw)}
}
return nil
}

View File

@@ -1,648 +0,0 @@
package bybit
import (
"context"
"encoding/json"
"errors"
"net/http"
"strconv"
"strings"
"time"
"github.com/gorilla/websocket"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/stream"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/trade"
"github.com/thrasher-corp/gocryptotrader/log"
)
const wsUSDTKline = "candle"
// WsUSDTConnect connects to a USDT websocket feed
func (by *Bybit) WsUSDTConnect() error {
if !by.Websocket.IsEnabled() || !by.IsEnabled() {
return errors.New(stream.WebsocketNotEnabled)
}
var dialer websocket.Dialer
err := by.Websocket.Conn.Dial(&dialer, http.Header{})
if err != nil {
return err
}
pingMsg, err := json.Marshal(pingRequest)
if err != nil {
return err
}
by.Websocket.Conn.SetupPingHandler(stream.PingHandler{
Message: pingMsg,
MessageType: websocket.PingMessage,
Delay: bybitWebsocketTimer,
})
if by.Verbose {
log.Debugf(log.ExchangeSys, "%s Connected to Websocket.\n", by.Name)
}
go by.wsUSDTReadData()
if by.IsWebsocketAuthenticationSupported() {
err = by.WsUSDTAuth(context.TODO())
if err != nil {
by.Websocket.DataHandler <- err
by.Websocket.SetCanUseAuthenticatedEndpoints(false)
}
}
return nil
}
// WsUSDTAuth sends an authentication message to receive auth data
func (by *Bybit) WsUSDTAuth(ctx context.Context) error {
creds, err := by.GetCredentials(ctx)
if err != nil {
return err
}
intNonce := (time.Now().Unix() + 1) * 1000
strNonce := strconv.FormatInt(intNonce, 10)
hmac, err := crypto.GetHMAC(
crypto.HashSHA256,
[]byte("GET/realtime"+strNonce),
[]byte(creds.Secret),
)
if err != nil {
return err
}
sign := crypto.HexEncodeToString(hmac)
req := Authenticate{
Operation: "auth",
Args: []interface{}{creds.Key, intNonce, sign},
}
return by.Websocket.Conn.SendJSONMessage(req)
}
// SubscribeUSDT sends a websocket message to receive data from the channel
func (by *Bybit) SubscribeUSDT(channelsToSubscribe []stream.ChannelSubscription) error {
var errs error
for i := range channelsToSubscribe {
var sub WsFuturesReq
sub.Topic = subscribe
sub.Args = append(sub.Args, formatArgs(channelsToSubscribe[i].Channel, channelsToSubscribe[i].Params))
err := by.Websocket.Conn.SendJSONMessage(sub)
if err != nil {
errs = common.AppendError(errs, err)
continue
}
by.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i])
}
if errs != nil {
return errs
}
return nil
}
// UnsubscribeUSDT sends a websocket message to stop receiving data from the channel
func (by *Bybit) UnsubscribeUSDT(channelsToUnsubscribe []stream.ChannelSubscription) error {
var errs error
for i := range channelsToUnsubscribe {
var unSub WsFuturesReq
unSub.Topic = unsubscribe
formattedPair, err := by.FormatExchangeCurrency(channelsToUnsubscribe[i].Currency, asset.USDTMarginedFutures)
if err != nil {
errs = common.AppendError(errs, err)
continue
}
unSub.Args = append(unSub.Args, channelsToUnsubscribe[i].Channel+dot+formattedPair.String())
err = by.Websocket.Conn.SendJSONMessage(unSub)
if err != nil {
errs = common.AppendError(errs, err)
continue
}
by.Websocket.RemoveSubscriptions(channelsToUnsubscribe[i])
}
if errs != nil {
return errs
}
return nil
}
// wsUSDTReadData gets and passes on websocket messages for processing
func (by *Bybit) wsUSDTReadData() {
by.Websocket.Wg.Add(1)
defer by.Websocket.Wg.Done()
for {
select {
case <-by.Websocket.ShutdownC:
return
default:
resp := by.Websocket.Conn.ReadMessage()
if resp.Raw == nil {
return
}
err := by.wsUSDTHandleData(resp.Raw)
if err != nil {
by.Websocket.DataHandler <- err
}
}
}
}
func (by *Bybit) wsUSDTHandleData(respRaw []byte) error {
var multiStreamData map[string]interface{}
err := json.Unmarshal(respRaw, &multiStreamData)
if err != nil {
return err
}
t, ok := multiStreamData["topic"].(string)
if !ok {
log.Errorf(log.ExchangeSys, "%s Received unhandle message on websocket: %v\n", by.Name, multiStreamData)
return nil
}
topics := strings.Split(t, dot)
if len(topics) < 1 {
return errors.New(by.Name + " - topic could not be extracted from response")
}
switch topics[0] {
case wsOrder25, wsOrder200:
if wsType, ok := multiStreamData["type"].(string); ok {
switch wsType {
case wsOperationSnapshot:
var response WsUSDTOrderbook
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Data.OBData[0].Symbol, asset.USDTMarginedFutures, false)
if err != nil {
return err
}
err = by.processOrderbook(response.Data.OBData,
response.Type,
p,
asset.USDTMarginedFutures)
if err != nil {
return err
}
case wsOperationDelta:
var response WsCoinDeltaOrderbook
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
if len(response.OBData.Delete) > 0 {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.OBData.Delete[0].Symbol, asset.USDTMarginedFutures, false)
if err != nil {
return err
}
err = by.processOrderbook(response.OBData.Delete,
wsOrderbookActionDelete,
p,
asset.USDTMarginedFutures)
if err != nil {
return err
}
}
if len(response.OBData.Update) > 0 {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.OBData.Update[0].Symbol, asset.USDTMarginedFutures, false)
if err != nil {
return err
}
err = by.processOrderbook(response.OBData.Update,
wsOrderbookActionUpdate,
p,
asset.USDTMarginedFutures)
if err != nil {
return err
}
}
if len(response.OBData.Insert) > 0 {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.OBData.Insert[0].Symbol, asset.USDTMarginedFutures, false)
if err != nil {
return err
}
err = by.processOrderbook(response.OBData.Insert,
wsOrderbookActionInsert,
p,
asset.USDTMarginedFutures)
if err != nil {
return err
}
}
default:
by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + "unsupported orderbook operation"}
}
}
case wsTrades:
if !by.IsSaveTradeDataEnabled() {
return nil
}
var response WsFuturesTrade
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
trades := make([]trade.Data, len(response.TradeData))
for i := range response.TradeData {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.TradeData[0].Symbol, asset.USDTMarginedFutures, false)
if err != nil {
return err
}
var oSide order.Side
oSide, err = order.StringToOrderSide(response.TradeData[i].Side)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
Err: err,
}
}
trades[i] = trade.Data{
TID: response.TradeData[i].ID,
Exchange: by.Name,
CurrencyPair: p,
AssetType: asset.USDTMarginedFutures,
Side: oSide,
Price: response.TradeData[i].Price,
Amount: response.TradeData[i].Size,
Timestamp: response.TradeData[i].Time,
}
}
return by.AddTradesToBuffer(trades...)
case wsUSDTKline:
var response WsFuturesKline
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(topics[len(topics)-1], asset.USDTMarginedFutures, false)
if err != nil {
return err
}
for i := range response.KlineData {
by.Websocket.DataHandler <- stream.KlineData{
Pair: p,
AssetType: asset.USDTMarginedFutures,
Exchange: by.Name,
OpenPrice: response.KlineData[i].Open,
HighPrice: response.KlineData[i].High,
LowPrice: response.KlineData[i].Low,
ClosePrice: response.KlineData[i].Close,
Volume: response.KlineData[i].Volume,
Timestamp: response.KlineData[i].Timestamp.Time(),
}
}
case wsInstrument:
if wsType, ok := multiStreamData["type"].(string); ok {
switch wsType {
case wsOperationSnapshot:
var response WsTicker
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Ticker.Symbol, asset.USDTMarginedFutures, false)
if err != nil {
return err
}
by.Websocket.DataHandler <- &ticker.Price{
ExchangeName: by.Name,
Last: response.Ticker.LastPrice.Float64(),
High: response.Ticker.HighPrice24h.Float64(),
Low: response.Ticker.LowPrice24h.Float64(),
Bid: response.Ticker.BidPrice,
Ask: response.Ticker.AskPrice,
Volume: response.Ticker.Volume24h,
Close: response.Ticker.PrevPrice24h.Float64(),
LastUpdated: response.Ticker.UpdateAt,
AssetType: asset.USDTMarginedFutures,
Pair: p,
}
case wsOperationDelta:
var response WsDeltaTicker
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
if len(response.Data.Delete) > 0 {
for x := range response.Data.Delete {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Data.Delete[x].Symbol, asset.USDTMarginedFutures, false)
if err != nil {
return err
}
by.Websocket.DataHandler <- &ticker.Price{
ExchangeName: by.Name,
Last: response.Data.Delete[x].LastPrice.Float64(),
High: response.Data.Delete[x].HighPrice24h.Float64(),
Low: response.Data.Delete[x].LowPrice24h.Float64(),
Bid: response.Data.Delete[x].BidPrice,
Ask: response.Data.Delete[x].AskPrice,
Volume: response.Data.Delete[x].Volume24h,
Close: response.Data.Delete[x].PrevPrice24h.Float64(),
LastUpdated: response.Data.Delete[x].UpdateAt,
AssetType: asset.USDTMarginedFutures,
Pair: p,
}
}
}
if len(response.Data.Update) > 0 {
for x := range response.Data.Update {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Data.Update[x].Symbol, asset.USDTMarginedFutures, false)
if err != nil {
return err
}
by.Websocket.DataHandler <- &ticker.Price{
ExchangeName: by.Name,
Last: response.Data.Update[x].LastPrice.Float64(),
High: response.Data.Update[x].HighPrice24h.Float64(),
Low: response.Data.Update[x].LowPrice24h.Float64(),
Bid: response.Data.Update[x].BidPrice,
Ask: response.Data.Update[x].AskPrice,
Volume: response.Data.Update[x].Volume24h,
Close: response.Data.Update[x].PrevPrice24h.Float64(),
LastUpdated: response.Data.Update[x].UpdateAt,
AssetType: asset.USDTMarginedFutures,
Pair: p,
}
}
}
if len(response.Data.Insert) > 0 {
for x := range response.Data.Insert {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Data.Insert[x].Symbol, asset.USDTMarginedFutures, false)
if err != nil {
return err
}
by.Websocket.DataHandler <- &ticker.Price{
ExchangeName: by.Name,
Last: response.Data.Insert[x].LastPrice.Float64(),
High: response.Data.Insert[x].HighPrice24h.Float64(),
Low: response.Data.Insert[x].LowPrice24h.Float64(),
Bid: response.Data.Insert[x].BidPrice,
Ask: response.Data.Insert[x].AskPrice,
Volume: response.Data.Insert[x].Volume24h,
Close: response.Data.Insert[x].PrevPrice24h.Float64(),
LastUpdated: response.Data.Insert[x].UpdateAt,
AssetType: asset.USDTMarginedFutures,
Pair: p,
}
}
}
default:
by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + "unsupported ticker operation"}
}
}
case wsLiquidation:
var response WsFuturesLiquidation
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
by.Websocket.DataHandler <- response.Data
case wsPosition:
var response WsFuturesPosition
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
by.Websocket.DataHandler <- response.Data
case wsExecution:
var response WsFuturesExecution
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
for i := range response.Data {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Data[i].Symbol, asset.USDTMarginedFutures, false)
if err != nil {
return err
}
var oSide order.Side
oSide, err = order.StringToOrderSide(response.Data[i].Side)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[i].OrderID,
Err: err,
}
}
var oStatus order.Status
oStatus, err = order.StringToOrderStatus(response.Data[i].ExecutionType)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[i].OrderID,
Err: err,
}
}
by.Websocket.DataHandler <- &order.Detail{
Exchange: by.Name,
OrderID: response.Data[i].OrderID,
AssetType: asset.USDTMarginedFutures,
Pair: p,
Side: oSide,
Status: oStatus,
Price: response.Data[i].Price.Float64(),
Amount: response.Data[i].OrderQty,
Trades: []order.TradeHistory{
{
Price: response.Data[i].Price.Float64(),
Amount: response.Data[i].OrderQty,
Exchange: by.Name,
Side: oSide,
Timestamp: response.Data[i].Time,
},
},
}
}
case wsOrder:
var response WsOrder
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
for x := range response.Data {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Data[x].Symbol, asset.USDTMarginedFutures, false)
if err != nil {
return err
}
var oSide order.Side
oSide, err = order.StringToOrderSide(response.Data[x].Side)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Err: err,
}
}
var oType order.Type
oType, err = order.StringToOrderType(response.Data[x].OrderType)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Err: err,
}
}
var oStatus order.Status
oStatus, err = order.StringToOrderStatus(response.Data[x].OrderStatus)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Err: err,
}
}
by.Websocket.DataHandler <- &order.Detail{
Price: response.Data[x].Price.Float64(),
Amount: response.Data[x].OrderQty,
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Type: oType,
Side: oSide,
Status: oStatus,
AssetType: asset.USDTMarginedFutures,
Date: response.Data[x].CreateTime,
Pair: p,
Trades: []order.TradeHistory{
{
Price: response.Data[x].Price.Float64(),
Amount: response.Data[x].OrderQty,
Exchange: by.Name,
Side: oSide,
Timestamp: response.Data[x].Time,
},
},
}
}
case wsStopOrder:
var response WsUSDTFuturesStopOrder
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
for x := range response.Data {
var p currency.Pair
p, err = by.MatchSymbolWithAvailablePairs(response.Data[x].Symbol, asset.USDTMarginedFutures, false)
if err != nil {
return err
}
var oSide order.Side
oSide, err = order.StringToOrderSide(response.Data[x].Side)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Err: err,
}
}
var oType order.Type
oType, err = order.StringToOrderType(response.Data[x].OrderType)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Err: err,
}
}
var oStatus order.Status
oStatus, err = order.StringToOrderStatus(response.Data[x].OrderStatus)
if err != nil {
by.Websocket.DataHandler <- order.ClassificationError{
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
Err: err,
}
}
by.Websocket.DataHandler <- &order.Detail{
Price: response.Data[x].Price.Float64(),
Amount: response.Data[x].OrderQty,
Exchange: by.Name,
OrderID: response.Data[x].OrderID,
AccountID: strconv.FormatInt(response.Data[x].UserID, 10),
Type: oType,
Side: oSide,
Status: oStatus,
AssetType: asset.USDTMarginedFutures,
Date: response.Data[x].CreateTime,
Pair: p,
Trades: []order.TradeHistory{
{
Price: response.Data[x].Price.Float64(),
Amount: response.Data[x].OrderQty,
Exchange: by.Name,
Side: oSide,
Timestamp: response.Data[x].CreateTime,
},
},
}
}
case wsWallet:
var response WsFuturesWallet
err = json.Unmarshal(respRaw, &response)
if err != nil {
return err
}
by.Websocket.DataHandler <- response.Data
default:
by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + string(respRaw)}
}
return nil
}

View File

@@ -1,977 +0,0 @@
package bybit
import (
"time"
"github.com/thrasher-corp/gocryptotrader/types"
)
var (
validFuturesIntervals = []string{
"1", "3", "5", "15", "30", "60", "120", "240", "360", "720",
"D", "M", "W", "d", "m", "w",
}
validFuturesPeriods = []string{
"5min", "15min", "30min", "1h", "4h", "1d",
}
)
// OrderbookData stores ob data for cmargined futures
type OrderbookData struct {
Symbol string `json:"symbol"`
Price types.Number `json:"price"`
Size float64 `json:"size"`
Side string `json:"side"`
}
// FuturesCandleStick holds kline data
type FuturesCandleStick struct {
ID int64 `json:"id"`
Symbol string `json:"symbol"`
Interval string `json:"interval"`
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"`
}
// FuturesCandleStickWithStringParam holds kline data
type FuturesCandleStickWithStringParam struct {
ID int64 `json:"id"`
Symbol string `json:"symbol"`
Interval string `json:"interval"`
OpenTime int64 `json:"open_time"`
Open types.Number `json:"open"`
High types.Number `json:"high"`
Low types.Number `json:"low"`
Close types.Number `json:"close"`
Volume types.Number `json:"volume"`
TurnOver types.Number `json:"turnover"`
}
// SymbolPriceTicker stores ticker price stats
type SymbolPriceTicker struct {
Symbol string `json:"symbol"`
BidPrice types.Number `json:"bid_price"`
AskPrice types.Number `json:"ask_price"`
LastPrice types.Number `json:"last_price"`
LastTickDirection string `json:"last_tick_direction"`
Price24hAgo types.Number `json:"prev_price_24h"`
PricePcntChange24h types.Number `json:"price_24h_pcnt"`
HighPrice24h types.Number `json:"high_price_24h"`
LowPrice24h types.Number `json:"low_price_24h"`
Price1hAgo types.Number `json:"prev_price_1h"`
PricePcntChange1h types.Number `json:"price_1h_pcnt"`
MarkPrice types.Number `json:"mark_price"`
IndexPrice types.Number `json:"index_price"`
OpenInterest float64 `json:"open_interest"`
OpenValue types.Number `json:"open_value"`
TotalTurnover types.Number `json:"total_turnover"`
Turnover24h types.Number `json:"turnover_24h"`
TotalVolume float64 `json:"total_volume"`
Volume24h float64 `json:"volume_24h"`
FundingRate types.Number `json:"funding_rate"`
PredictedFundingRate types.Number `json:"predicted_funding_rate"`
NextFundingTime string `json:"next_funding_time"`
CountdownHour int64 `json:"countdown_hour"`
DeliveryFeeRate types.Number `json:"delivery_fee_rate"`
PredictedDeliveryPrice types.Number `json:"predicted_delivery_price"`
DeliveryTime string `json:"delivery_time"`
}
// FuturesPublicTradesData stores recent public trades for futures
type FuturesPublicTradesData struct {
Symbol string `json:"symbol"`
Price float64 `json:"price"`
Qty float64 `json:"qty"`
Time time.Time `json:"time"`
Side string `json:"side"`
TimeInMilliSec int64 `json:"trade_time_ms"`
}
// SymbolInfo stores symbol information for futures pair
type SymbolInfo struct {
Name string `json:"name"`
Alias string `json:"alias"`
Status string `json:"status"`
BaseCurrency string `json:"base_currency"`
QuoteCurrency string `json:"quote_currency"`
PriceScale float64 `json:"price_scale"`
TakerFee string `json:"taker_fee"`
MakerFee string `json:"maker_fee"`
FundingFeeInterval int64 `json:"funding_interval"`
LeverageFilter struct {
MinLeverage float64 `json:"min_leverage"`
MaxLeverage float64 `json:"max_leverage"`
LeverageStep types.Number `json:"leverage_step"`
} `json:"leverage_filter"`
PriceFilter struct {
MinPrice types.Number `json:"min_price"`
MaxPrice types.Number `json:"max_price"`
TickSize types.Number `json:"tick_size"`
} `json:"price_filter"`
LotSizeFilter struct {
MinTradeQty float64 `json:"min_trading_qty"`
MaxTradeQty float64 `json:"max_trading_qty"`
QtyStep float64 `json:"qty_step"`
} `json:"lot_size_filter"`
}
// MarkPriceKlineData stores mark price kline data
type MarkPriceKlineData struct {
ID int64 `json:"id"`
Symbol string `json:"symbol"`
Interval string `json:"period"`
StartAt int64 `json:"start_at"`
Open float64 `json:"open"`
High float64 `json:"high"`
Low float64 `json:"low"`
Close float64 `json:"close"`
}
// IndexPriceKlineData stores index price kline data
type IndexPriceKlineData struct {
Symbol string `json:"symbol"`
Interval string `json:"period"`
StartAt int64 `json:"open_time"`
Open types.Number `json:"open"`
High types.Number `json:"high"`
Low types.Number `json:"low"`
Close types.Number `json:"close"`
}
// OpenInterestData stores open interest data
type OpenInterestData struct {
OpenInterest float64 `json:"open_interest"`
Symbol string `json:"symbol"`
Time int64 `json:"time"`
}
// BigDealData stores big deal data
type BigDealData struct {
ID int64 `json:"id"`
Symbol string `json:"symbol"`
Side string `json:"side"`
Time int64 `json:"timestamp"`
Value float64 `json:"value"`
}
// AccountRatioData stores user accounts long short ratio
type AccountRatioData struct {
Symbol string `json:"symbol"`
BuyRatio float64 `json:"buy_ratio"`
SellRatio float64 `json:"sell_ratio"`
Time int64 `json:"timestamp"`
}
// BaseFuturesOrder is base future order structure
type BaseFuturesOrder struct {
UserID int64 `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"`
}
// FuturesOrderData stores futures order data
type FuturesOrderData struct {
BaseFuturesOrder
OrderStatus string `json:"order_status"`
OrderLinkID string `json:"order_link_id"`
OrderID string `json:"order_id"`
LeavesQty float64 `json:"leaves_qty"`
CumulativeQty float64 `json:"cum_exec_qty"`
CumulativeValue float64 `json:"cum_exec_value"`
CumulativeFee float64 `json:"cum_exec_fee"`
RejectReason string `json:"reject_reason"`
CreatedAt time.Time `json:"create_at"`
}
// FuturesOrderCancelResp stores future order cancel response
type FuturesOrderCancelResp struct {
FuturesOrderData
LastExecutionTime string `json:"last_exec_time"`
LastExecutionPrice float64 `json:"last_exec_price"`
UpdateAt string `json:"updated_at"`
}
// FuturesOrderDataResp stores future order response
type FuturesOrderDataResp struct {
FuturesOrderCancelResp
TakeProfit float64 `json:"take_profit"`
StopLoss float64 `json:"stop_loss"`
TakeProfitTriggerBy string `json:"tp_trigger_by"`
StopLossTriggerBy string `json:"sl_trigger_by"`
}
// FuturesActiveOrderData stores future active order data
type FuturesActiveOrderData struct {
FuturesOrderData
LeaveValue float64 `json:"leaves_value"`
}
// FuturesActiveOrderResp stores future active order response
type FuturesActiveOrderResp struct {
FuturesActiveOrderData
TakeProfit float64 `json:"take_profit"`
StopLoss float64 `json:"stop_loss"`
TakeProfitTriggerBy string `json:"tp_trigger_by"`
StopLossTriggerBy string `json:"sl_trigger_by"`
}
// FuturesActiveOrder stores future active order
type FuturesActiveOrder struct {
FuturesActiveOrderData
PositionID int64 `json:"position_idx"`
UpdatedAt string `json:"updated_at"`
}
// FuturesRealtimeOrderData stores futures realtime order data
type FuturesRealtimeOrderData struct {
BaseFuturesOrder
OrderStatus string `json:"order_status"`
OrderLinkID string `json:"order_link_id"`
TakeProfit float64 `json:"take_profit"`
StopLoss float64 `json:"stop_loss"`
TakeProfitTriggerBy string `json:"tp_trigger_by"`
StopLossTriggerBy string `json:"sl_trigger_by"`
}
// FuturesActiveRealtimeOrder stores future active realtime order
type FuturesActiveRealtimeOrder struct {
FuturesRealtimeOrderData
ExtensionField map[string]interface{} `json:"ext_fields"`
LastExecutionTime string `json:"last_exec_time"`
LastExecutionPrice float64 `json:"last_exec_price"`
LeavesQty float64 `json:"leaves_qty"`
LeaveValue types.Number `json:"leaves_value"`
CumulativeQty types.Number `json:"cum_exec_qty"`
CumulativeValue types.Number `json:"cum_exec_value"`
CumulativeFee types.Number `json:"cum_exec_fee"`
RejectReason string `json:"reject_reason"`
CancelType string `json:"cancel_type"`
CreatedAt time.Time `json:"create_at"`
UpdatedAt time.Time `json:"updated_at"`
OrderID string `json:"order_id"`
}
// CoinFuturesConditionalRealtimeOrder stores CMF future coinditional realtime order
type CoinFuturesConditionalRealtimeOrder struct {
FuturesRealtimeOrderData
ExtensionField map[string]interface{} `json:"ext_fields"`
LeavesQty float64 `json:"leaves_qty"`
LeaveValue types.Number `json:"leaves_value"`
CumulativeQty types.Number `json:"cum_exec_qty"`
CumulativeValue types.Number `json:"cum_exec_value"`
CumulativeFee types.Number `json:"cum_exec_fee"`
RejectReason string `json:"reject_reason"`
CancelType string `json:"cancel_type"`
CreatedAt string `json:"create_at"`
UpdatedAt string `json:"updated_at"`
OrderID string `json:"order_id"`
}
// FuturesConditionalRealtimeOrder stores future conditional realtime order
type FuturesConditionalRealtimeOrder struct {
CoinFuturesConditionalRealtimeOrder
PositionID int64 `json:"position_idx"`
}
// USDTFuturesConditionalRealtimeOrder stores USDT future conditional realtime order
type USDTFuturesConditionalRealtimeOrder struct {
FuturesRealtimeOrderData
StopOrderID string `json:"stop_order_id"`
OrderStatus string `json:"order_status"`
TriggerPrice float64 `json:"trigger_price"`
CreatedAt string `json:"created_time"`
UpdatedAt string `json:"updated_time"`
BasePrice float64 `json:"base_price"`
TriggerBy string `json:"trigger_by"`
ReduceOnly bool `json:"reduce_only"`
CloseOnTrigger bool `json:"close_on_trigger"`
}
// FuturesConditionalOrderData stores futures conditional order data
type FuturesConditionalOrderData struct {
BaseFuturesOrder
TriggerBy string `json:"trigger_by"`
BasePrice float64 `json:"base_price"`
StopOrderID string `json:"stop_order_id"`
OrderLinkID string `json:"order_link_id"`
TakeProfitTriggerBy string `json:"tp_trigger_by"`
StopLossTriggerBy string `json:"sl_trigger_by"`
}
// FuturesConditionalOrderResp stores futures conditional order response
type FuturesConditionalOrderResp struct {
FuturesConditionalOrderData
Remark string `json:"remark"`
RejectReason string `json:"reject_reason"`
StopPrice float64 `json:"stop_px"`
TakeProfit float64 `json:"take_profit"`
StopLoss float64 `json:"stop_loss"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// USDTFuturesConditionalOrderResp stores USDT futures conditional order response
type USDTFuturesConditionalOrderResp struct {
FuturesConditionalOrderData
OrderStatus string `json:"order_status"`
TriggerPrice float64 `json:"trigger_price"`
ReduceOnly bool `json:"reduce_only"`
CloseOnTrigger bool `json:"close_on_trigger"`
CreatedAt string `json:"created_time"`
UpdatedAt string `json:"updated_time"`
}
// CoinFuturesConditionalOrders stores CMF future conditional order
type CoinFuturesConditionalOrders struct {
FuturesConditionalOrderData
StopOrderStatus string `json:"stop_order_status"`
StopOrderType string `json:"stop_order_type"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
StopPrice float64 `json:"stop_px"`
StopOrderID string `json:"stop_order_id"`
TakeProfit float64 `json:"take_profit"`
StopLoss float64 `json:"stop_loss"`
}
// FuturesConditionalOrders stores future conditional order
type FuturesConditionalOrders struct {
CoinFuturesConditionalOrders
PositionID int64 `json:"position_idx"`
}
// USDTFuturesConditionalOrders stores USDT futures conditional order
type USDTFuturesConditionalOrders struct {
FuturesConditionalOrderData
OrderStatus string `json:"order_status"`
TriggerPrice float64 `json:"trigger_price"`
CreatedAt string `json:"created_time"`
UpdatedAt string `json:"updated_time"`
TakeProfit float64 `json:"take_profit"`
StopLoss float64 `json:"stop_loss"`
}
// FuturesCancelOrderData stores future cancel order data
type FuturesCancelOrderData struct {
CancelOrderID string `json:"clOrdID"`
BaseFuturesOrder
CreateType string `json:"create_type"`
CancelType string `json:"cancel_type"`
OrderStatus string `json:"order_status"`
LeavesQty float64 `json:"leaves_qty"`
LeavesValue float64 `json:"leaves_value"`
CreatedAt string `json:"create_at"`
UpdateAt string `json:"updated_at"`
CrossStatus string `json:"cross_status"`
CrossSeq int64 `json:"cross_seq"`
}
// FuturesCancelOrderResp stores future cancel order response
type FuturesCancelOrderResp struct {
FuturesCancelOrderData
StopOrderType string `json:"stop_order_type"`
TriggerBy string `json:"trigger_by"`
BasePrice types.Number `json:"base_price"`
ExpectedDirection string `json:"expected_direction"`
}
// RiskInfo stores risk information
type RiskInfo struct {
ID int64 `json:"id"`
Symbol string `json:"symbol"`
Limit int64 `json:"limit"`
MaintainMargin float64 `json:"maintain_margin"`
StartingMargin float64 `json:"starting_margin"`
Section []string `json:"section"`
IsLowestRisk int64 `json:"is_lowest_risk"`
CreatedAt string `json:"create_at"`
UpdateAt string `json:"updated_at"`
MaxLeverage float64 `json:"max_leverage"`
}
// RiskInfoWithStringParam stores risk information where string params
type RiskInfoWithStringParam struct {
ID int64 `json:"id"`
Symbol string `json:"symbol"`
Limit int64 `json:"limit"`
MaintainMargin types.Number `json:"maintain_margin"`
StartingMargin types.Number `json:"starting_margin"`
Section []string `json:"section"`
IsLowestRisk int64 `json:"is_lowest_risk"`
CreatedAt string `json:"create_at"`
UpdateAt string `json:"updated_at"`
MaxLeverage types.Number `json:"max_leverage"`
}
// FundingInfo stores funding information
type FundingInfo struct {
Symbol string `json:"symbol"`
FundingRate types.Number `json:"funding_rate"`
FundingRateTimestamp int64 `json:"funding_rate_timestamp"`
}
// USDTFundingInfo stores USDT funding information
type USDTFundingInfo struct {
Symbol string `json:"symbol"`
FundingRate float64 `json:"funding_rate"`
FundingRateTimestamp string `json:"funding_rate_timestamp"`
}
// AnnouncementInfo stores announcement information
type AnnouncementInfo struct {
ID int64 `json:"id"`
Title string `json:"title"`
Link string `json:"link"`
Summary string `json:"summary"`
CreatedAt string `json:"created_at"`
}
// Position stores position
type Position struct {
UserID int64 `json:"user_id"`
Symbol string `json:"symbol"`
Side string `json:"side"`
Size float64 `json:"size"`
PositionValue float64 `json:"position_value"`
EntryPrice float64 `json:"entry_price"`
LiquidationPrice float64 `json:"liq_price"`
BankruptcyPrice float64 `json:"bust_price"`
Leverage float64 `json:"leverage"`
PositionMargin float64 `json:"position_margin"`
OccupiedClosingFee float64 `json:"occ_closing_fee"`
RealisedPNL float64 `json:"realised_pnl"`
AccumulatedRealisedPNL float64 `json:"cum_realised_pnl"`
}
// PositionWithStringParam stores position with string params
type PositionWithStringParam struct {
UserID int64 `json:"user_id"`
Symbol string `json:"symbol"`
Side string `json:"side"`
Size float64 `json:"size"`
PositionValue types.Number `json:"position_value"`
EntryPrice types.Number `json:"entry_price"`
LiquidationPrice types.Number `json:"liq_price"`
BankruptcyPrice types.Number `json:"bust_price"`
Leverage types.Number `json:"leverage"`
PositionMargin types.Number `json:"position_margin"`
OccupiedClosingFee types.Number `json:"occ_closing_fee"`
RealisedPNL types.Number `json:"realised_pnl"`
AccumulatedRealisedPNL types.Number `json:"cum_realised_pnl"`
}
// PositionData stores position data
type PositionData struct {
Position
IsIsolated bool `json:"is_isolated"`
AutoAddMargin int64 `json:"auto_add_margin"`
UnrealisedPNL float64 `json:"unrealised_pnl"`
DeleverageIndicator int64 `json:"deleverage_indicator"`
RiskID int64 `json:"risk_id"`
TakeProfit float64 `json:"take_profit"`
StopLoss float64 `json:"stop_loss"`
TrailingStop float64 `json:"trailing_stop"`
}
// PositionDataWithStringParam stores position data with string params
type PositionDataWithStringParam struct {
PositionWithStringParam
IsIsolated bool `json:"is_isolated"`
AutoAddMargin int64 `json:"auto_add_margin"`
UnrealisedPNL float64 `json:"unrealised_pnl"`
DeleverageIndicator int64 `json:"deleverage_indicator"`
RiskID int64 `json:"risk_id"`
TakeProfit types.Number `json:"take_profit"`
StopLoss types.Number `json:"stop_loss"`
TrailingStop types.Number `json:"trailing_stop"`
}
// PositionResp stores position response
type PositionResp struct {
PositionDataWithStringParam
PositionID int64 `json:"position_idx"`
Mode int64 `json:"mode"`
ID int64 `json:"id"`
EffectiveLeverage types.Number `json:"effective_leverage"`
OccupiedFundingFee types.Number `json:"occ_funding_fee"`
PositionStatus string `json:"position_status"`
CalculatedData string `json:"oc_calc_data"`
OrderMargin types.Number `json:"order_margin"`
WalletBalance types.Number `json:"wallet_balance"`
CrossSequence int64 `json:"cross_seq"`
PositionSequence int64 `json:"position_seq"`
TakeProfitStopLossMode string `json:"tp_sl_mode"`
CreatedAt string `json:"created_at"`
UpdateAt string `json:"updated_at"`
}
// SetTradingAndStopResp stores set trading and stop response
type SetTradingAndStopResp struct {
PositionData
ID int64 `json:"id"`
RiskID int64 `json:"risk_id"`
AutoAddMargin int64 `json:"auto_add_margin"`
OccupiedFundingFee types.Number `json:"occ_funding_fee"`
TakeProfit types.Number `json:"take_profit"`
StopLoss types.Number `json:"stop_loss"`
PositionStatus string `json:"position_status"`
DeleverageIndicator int64 `json:"deleverage_indicator"`
CalculatedData string `json:"oc_calc_data"`
OrderMargin types.Number `json:"order_margin"`
WalletBalance types.Number `json:"wallet_balance"`
CrossSequence int64 `json:"cross_seq"`
PositionSequence int64 `json:"position_seq"`
CreatedAt string `json:"created_at"`
UpdateAt string `json:"updated_at"`
ExtensionField map[string]interface{} `json:"ext_fields"`
}
// USDTPositionResp stores USDT position response
type USDTPositionResp struct {
PositionData
FreeQty float64 `json:"free_qty"`
TakeProfitStopLossMode string `json:"tp_sl_mode"`
}
// UpdateMarginResp stores update margin response
type UpdateMarginResp struct {
Position
FreeQty float64 `json:"free_qty"`
}
// TradeData stores trade data
type TradeData struct {
OrderID string `json:"order_id"`
OrderLinkedID string `json:"order_link_id"`
OrderSide string `json:"side"`
Symbol string `json:"symbol"`
ExecutionID string `json:"exec_id"`
OrderPrice float64 `json:"order_price"`
OrderQty float64 `json:"order_qty"`
OrderType string `json:"order_type"`
FeeRate float64 `json:"fee_rate"`
ExecutionFee types.Number `json:"exec_fee"`
ExecutionPrice types.Number `json:"exec_price"`
ExecutionQty float64 `json:"exec_qty"`
ExecutionType string `json:"exec_type"`
ExecutionValue types.Number `json:"exec_value"`
LeavesQty float64 `json:"leaves_qty"`
ClosedSize float64 `json:"closed_size"`
LastLiquidity string `json:"last_liquidity_ind"`
TradeTimeMs int64 `json:"trade_time_ms"`
}
// TradeResp stores trade response
type TradeResp struct {
TradeData
CrossSequence int64 `json:"cross_seq"`
NthFill int64 `json:"nth_fill"`
UserID int64 `json:"user_id"`
}
// ClosedTrades stores closed trades
type ClosedTrades struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
Symbol string `json:"symbol"`
OrderID string `json:"order_id"`
OrderSide string `json:"side"`
Qty float64 `json:"qty"`
OrderPrice float64 `json:"order_price"`
OrderType string `json:"order_type"`
ExecutionType string `json:"exec_type"`
ClosedSize float64 `json:"closed_size"`
CumulativeEntryValue float64 `json:"cum_entry_value"`
AvgEntryPrice float64 `json:"avg_entry_price"`
CumulativeExitValue float64 `json:"cum_exit_value"`
AvgEntryValue float64 `json:"avg_exit_price"`
ClosedProfitLoss float64 `json:"closed_pnl"`
FillCount int64 `json:"fill_count"`
Leverage float64 `json:"leverage"`
CreatedAt bybitTimeSec `json:"created_at"`
}
// FundingFee stores funding fee
type FundingFee struct {
Symbol string `json:"symbol"`
Side string `json:"side"`
Size float64 `json:"size"`
FundingRate float64 `json:"funding_rate"`
ExecutionFee float64 `json:"exec_fee"`
ExecutionTime int64 `json:"exec_timestamp"`
}
// APIKeyData stores API key data
type APIKeyData struct {
APIKey string `json:"api_key"`
Type string `json:"type"`
UserID int64 `json:"user_id"`
InviterID int64 `json:"inviter_id"`
IPs []string `json:"ips"`
Note string `json:"note"`
Permission []string `json:"permissions"`
CreatedAt string `json:"created_at"`
ExpiredAt string `json:"expired_at"`
ReadOnly bool `json:"read_only"`
VIPLevel string `json:"vip_level"`
MarketMakerLevel string `json:"mkt_maker_level"`
}
// LCPData stores LiquidityContributionPointsData data
type LCPData struct {
Date string `json:"date"`
SelfRatio float64 `json:"self_ratio"`
PlatformRatio float64 `json:"platform_ratio"`
Score float64 `json:"score"`
}
// WalletData stores wallet data
type WalletData struct {
Equity float64 `json:"equity"` // equity = wallet_balance + unrealised_pnl
AvailableBalance float64 `json:"available_balance"`
UserMargin float64 `json:"used_margin"`
OrderMargin float64 `json:"order_margin"`
PositionMargin float64 `json:"position_margin"`
PositionClosingFee float64 `json:"occ_closing_fee"`
PositionFundingFee float64 `json:"occ_funding_fee"`
WalletBalance float64 `json:"wallet_balance"`
RealisedPNL float64 `json:"realised_pnl"`
UnrealisedPNL float64 `json:"unrealised_pnl"`
CumulativeRealisedPNL float64 `json:"cum_realised_pnl"`
GivenCash float64 `json:"given_cash"`
ServiceCash float64 `json:"service_cash"`
}
// FundRecord stores funding records
type FundRecord struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
Coin string `json:"coin"`
Type string `json:"type"`
Amount types.Number `json:"amount"`
TxID string `json:"tx_id"`
Address string `json:"address"`
WalletBalance types.Number `json:"wallet_balance"`
ExecutionTime string `json:"exec_time"`
CrossSequence int64 `json:"cross_seq"`
}
// FundWithdrawalRecord stores funding withdrawal records
type FundWithdrawalRecord struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
Coin string `json:"coin"`
Status string `json:"status"`
Amount types.Number `json:"amount"`
Fee float64 `json:"fee"`
Address string `json:"address"`
TxID string `json:"tx_id"`
SubmittedAt time.Time `json:"submited_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// AssetExchangeRecord stores asset exchange records
type AssetExchangeRecord struct {
ID int64 `json:"id"`
FromCoin string `json:"from_coin"`
FromAmount float64 `json:"from_amount"`
ToCoin string `json:"to_coin"`
ToAmount float64 `json:"to_amount"`
ExchangeRate float64 `json:"exchange_rate"`
FromFee float64 `json:"from_fee"`
CreatedAt string `json:"created_at"`
}
// USDCOrderbookData stores orderbook data for USDCMarginedFutures
type USDCOrderbookData struct {
Price types.Number `json:"price"`
Size types.Number `json:"size"`
Side string `json:"side"`
}
// USDCContract stores contract data
type USDCContract struct {
Symbol string `json:"symbol"`
Status string `json:"status"`
BaseCoin string `json:"baseCoin"`
QuoteCoin string `json:"quoteCoin"`
TakerFeeRate types.Number `json:"takerFeeRate"`
MakerFeeRate types.Number `json:"makerFeeRate"`
MinLeverage types.Number `json:"minLeverage"`
MaxLeverage types.Number `json:"maxLeverage"`
LeverageStep types.Number `json:"leverageStep"`
MinPrice types.Number `json:"minPrice"`
MaxPrice types.Number `json:"maxPrice"`
TickSize types.Number `json:"tickSize"`
MaxTradingQty types.Number `json:"maxTradingQty"`
MinTradingQty types.Number `json:"minTradingQty"`
QtyStep types.Number `json:"qtyStep"`
DeliveryTime bybitTimeMilliSecStr `json:"deliveryTime"`
}
// USDCSymbol stores symbol data
type USDCSymbol struct {
Symbol string `json:"symbol"`
NextFundingTime string `json:"nextFundingTime"`
Bid types.Number `json:"bid"`
BidSize types.Number `json:"bidSize"`
Ask types.Number `json:"ask"`
AskSize types.Number `json:"askSize"`
LastPrice types.Number `json:"lastPrice"`
OpenInterest types.Number `json:"openInterest"`
IndexPrice types.Number `json:"indexPrice"`
MarkPrice types.Number `json:"markPrice"`
Change24h types.Number `json:"change24h"`
High24h types.Number `json:"high24h"`
Low24h types.Number `json:"low24h"`
Volume24h types.Number `json:"volume24h"`
Turnover24h types.Number `json:"turnover24h"`
TotalVolume types.Number `json:"totalVolume"`
TotalTurnover types.Number `json:"totalTurnover"`
FundingRate types.Number `json:"fundingRate"`
PredictedFundingRate types.Number `json:"predictedFundingRate"`
CountdownHour types.Number `json:"countdownHour"`
UnderlyingPrice string `json:"underlyingPrice"`
}
// USDCKlineBase stores Kline Base
type USDCKlineBase struct {
Symbol string `json:"symbol"`
Period string `json:"period"`
OpenTime bybitTimeSecStr `json:"openTime"`
Open types.Number `json:"open"`
High types.Number `json:"high"`
Low types.Number `json:"low"`
Close types.Number `json:"close"`
}
// USDCKline stores kline data
type USDCKline struct {
USDCKlineBase
Volume types.Number `json:"volume"`
Turnover types.Number `json:"turnover"`
}
// USDCOpenInterest stores open interest data
type USDCOpenInterest struct {
Symbol string `json:"symbol"`
Timestamp bybitTimeMilliSecStr `json:"timestamp"`
OpenInterest types.Number `json:"openInterest"`
}
// USDCLargeOrder stores large order data
type USDCLargeOrder struct {
Symbol string `json:"symbol"`
Side string `json:"side"`
Timestamp bybitTimeMilliSecStr `json:"timestamp"`
Value float64 `json:"value"`
}
// USDCAccountRatio stores long-short ratio data
type USDCAccountRatio struct {
Symbol string `json:"symbol"`
BuyRatio float64 `json:"buyRatio"`
SellRatio float64 `json:"sellRatio"`
Timestamp bybitTimeMilliSecStr `json:"timestamp"`
}
// USDCTrade stores trade data
type USDCTrade struct {
ID string `json:"id"`
Symbol string `json:"symbol"`
OrderPrice types.Number `json:"orderPrice"`
OrderQty types.Number `json:"orderQty"`
Side string `json:"side"`
Timestamp bybitTimeMilliSecStr `json:"time"`
}
// USDCCreateOrderResp stores create order response
type USDCCreateOrderResp struct {
ID string `json:"orderId"`
OrderLinkID string `json:"orderLinkId"`
Symbol string `json:"symbol"`
OrderPrice types.Number `json:"orderPrice"`
OrderQty types.Number `json:"orderQty"`
OrderType string `json:"orderType"`
Side string `json:"side"`
}
// USDCOrder store order data
type USDCOrder struct {
ID string `json:"orderId"`
OrderLinkID string `json:"orderLinkId"`
Symbol string `json:"symbol"`
OrderType string `json:"orderType"`
Side string `json:"side"`
Qty types.Number `json:"qty"`
Price types.Number `json:"price"`
TimeInForce string `json:"timeInForce"`
TotalOrderValue types.Number `json:"cumExecValue"`
TotalFilledQty types.Number `json:"cumExecQty"`
TotalFee types.Number `json:"cumExecFee"`
InitialMargin string `json:"orderIM"`
OrderStatus string `json:"orderStatus"`
TakeProfit types.Number `json:"takeProfit"`
StopLoss types.Number `json:"stopLoss"`
TPTriggerBy string `json:"tpTriggerBy"`
SLTriggerBy string `json:"slTriggerBy"`
LastExecPrice float64 `json:"lastExecPrice"`
BasePrice string `json:"basePrice"`
TriggerPrice types.Number `json:"triggerPrice"`
TriggerBy string `json:"triggerBy"`
ReduceOnly bool `json:"reduceOnly"`
StopOrderType string `json:"stopOrderType"`
CloseOnTrigger string `json:"closeOnTrigger"`
CreatedAt bybitTimeMilliSecStr `json:"createdAt"`
}
// USDCOrderHistory stores order history
type USDCOrderHistory struct {
USDCOrder
LeavesQty types.Number `json:"leavesQty"` // Est. unfilled order qty
CashFlow string `json:"cashFlow"`
RealisedPnl types.Number `json:"realisedPnl"`
UpdatedAt bybitTimeMilliSecStr `json:"updatedAt"`
}
// USDCTradeHistory stores trade history
type USDCTradeHistory struct {
ID string `json:"orderId"`
OrderLinkID string `json:"orderLinkId"`
Symbol string `json:"symbol"`
Side string `json:"side"`
TradeID string `json:"tradeId"`
ExecPrice types.Number `json:"execPrice"`
ExecQty types.Number `json:"execQty"`
ExecFee types.Number `json:"execFee"`
FeeRate types.Number `json:"feeRate"`
ExecType string `json:"execType"`
ExecValue types.Number `json:"execValue"`
TradeTime bybitTimeMilliSecStr `json:"tradeTime"`
LastLiquidityInd string `json:"lastLiquidityInd"`
}
// USDCTxLog stores transaction log data
type USDCTxLog struct {
TxTime bybitTimeMilliSecStr `json:"transactionTime"`
Symbol string `json:"symbol"`
Type string `json:"type"`
Side string `json:"side"`
Quantity types.Number `json:"qty"`
Size types.Number `json:"size"`
TradePrice types.Number `json:"tradePrice"`
Funding types.Number `json:"funding"`
Fee types.Number `json:"fee"`
CashFlow string `json:"cashFlow"`
Change types.Number `json:"change"`
WalletBalance types.Number `json:"walletBalance"`
FeeRate types.Number `json:"feeRate"`
TradeID string `json:"tradeId"`
OrderID string `json:"orderId"`
OrderLinkID string `json:"orderLinkId"`
Info string `json:"info"`
}
// USDCWalletBalance store USDC wallet balance
type USDCWalletBalance struct {
Equity types.Number `json:"equity"`
WalletBalance types.Number `json:"walletBalance"`
AvailableBalance types.Number `json:"availableBalance"`
AccountIM types.Number `json:"accountIM"`
AccountMM types.Number `json:"accountMM"`
TotalRPL types.Number `json:"totalRPL"`
TotalSessionUPL types.Number `json:"totalSessionUPL"`
TotalSessionRPL types.Number `json:"totalSessionRPL"`
}
// USDCAssetInfo stores USDC asset data
type USDCAssetInfo struct {
BaseCoin string `json:"baseCoin"`
TotalDelta types.Number `json:"totalDelta"`
TotalGamma types.Number `json:"totalGamma"`
TotalVega types.Number `json:"totalVega"`
TotalTheta types.Number `json:"totalTheta"`
TotalRPL types.Number `json:"totalRPL"`
SessionUPL types.Number `json:"sessionUPL"`
SessionRPL types.Number `json:"sessionRPL"`
IM types.Number `json:"im"`
MM types.Number `json:"mm"`
}
// USDCPosition store USDC position data
type USDCPosition struct {
Symbol string `json:"symbol"`
Leverage types.Number `json:"leverage"`
ClosingFee types.Number `json:"occClosingFee"`
LiquidPrice string `json:"liqPrice"`
Position float64 `json:"positionValue"`
TakeProfit types.Number `json:"takeProfit"`
RiskID string `json:"riskId"`
TrailingStop types.Number `json:"trailingStop"`
UnrealisedPnl types.Number `json:"unrealisedPnl"`
MarkPrice types.Number `json:"markPrice"`
CumRealisedPnl types.Number `json:"cumRealisedPnl"`
PositionMM types.Number `json:"positionMM"`
PositionIM types.Number `json:"positionIM"`
EntryPrice types.Number `json:"entryPrice"`
Size types.Number `json:"size"`
SessionRPL types.Number `json:"sessionRPL"`
SessionUPL types.Number `json:"sessionUPL"`
StopLoss types.Number `json:"stopLoss"`
OrderMargin types.Number `json:"orderMargin"`
SessionAvgPrice types.Number `json:"sessionAvgPrice"`
CreatedAt bybitTimeMilliSecStr `json:"createdAt"`
UpdatedAt bybitTimeMilliSecStr `json:"updatedAt"`
TpSLMode string `json:"tpSLMode"`
Side string `json:"side"`
BustPrice string `json:"bustPrice"`
PositionStatus string `json:"positionStatus"`
DeleverageIndicator int64 `json:"deleverageIndicator"`
}
// USDCSettlementHistory store USDC settlement history data
type USDCSettlementHistory struct {
Symbol string `json:"symbol"`
Side string `json:"side"`
Time bybitTimeMilliSecStr `json:"time"`
Size types.Number `json:"size"`
SessionAvgPrice types.Number `json:"sessionAvgPrice"`
MarkPrice types.Number `json:"markPrice"`
SessionRpl types.Number `json:"sessionRpl"`
}
// USDCRiskLimit store USDC risk limit data
type USDCRiskLimit struct {
RiskID string `json:"riskId"`
Symbol string `json:"symbol"`
Limit string `json:"limit"`
Section []string `json:"section"`
StartingMargin types.Number `json:"startingMargin"`
MaintainMargin types.Number `json:"maintainMargin"`
IsLowestRisk bool `json:"isLowestRisk"`
MaxLeverage types.Number `json:"maxLeverage"`
}
// USDCFundingInfo store USDC funding data
type USDCFundingInfo struct {
Symbol string `json:"symbol"`
Time bybitTimeMilliSecStr `json:"fundingRateTimestamp"`
Rate types.Number `json:"fundingRate"`
}
// CFuturesTradingFeeRate stores trading fee rate
type CFuturesTradingFeeRate struct {
TakerFeeRate types.Number `json:"taker_fee_rate"`
MakerFeeRate types.Number `json:"maker_fee_rate"`
UserID int64 `json:"user_id"`
}

View File

@@ -11,191 +11,132 @@ import (
const (
// See: https://bybit-exchange.github.io/docs/v5/rate-limit
spotInterval = time.Second * 5
spotRequestRate = 120
futuresPublicInterval = time.Second
futuresRequestRate = 50
spotPrivateInterval = time.Second
spotPrivateRequestRate = 20
spotPrivateFeeRequestRate = 10
futuresInterval = time.Minute
futuresDefaultRateCount = 100
futuresOrderRate = 100
futuresOrderListRate = 600
futuresExecutionRate = 120
futuresPositionRateCount = 75
futuresPositionListRate = 120
futuresFundingRate = 120
futuresWalletRate = 120
futuresAccountRate = 600
usdcPerpetualPublicRate = 50
usdcPerpetualCancelAllRate = 1
usdcPerpetualPrivateRate = 5
usdcPerpetualInterval = time.Second
spotInterval = time.Second * 5
)
const (
publicSpotRate request.EndpointLimit = iota
publicFuturesRate
privateSpotRate
privateFeeRate
cFuturesDefaultRate
cFuturesCancelActiveOrderRate
cFuturesCancelAllActiveOrderRate
cFuturesCreateConditionalOrderRate
cFuturesCancelConditionalOrderRate
cFuturesReplaceActiveOrderRate
cFuturesReplaceConditionalOrderRate
cFuturesCreateOrderRate
cFuturesCancelAllConditionalOrderRate
cFuturesGetActiveOrderRate
cFuturesGetConditionalOrderRate
cFuturesGetRealtimeOrderRate
cFuturesTradeRate
cFuturesSetLeverageRate
cFuturesUpdateMarginRate
cFuturesSetTradingRate
cFuturesSwitchPositionRate
cFuturesGetTradingFeeRate
cFuturesPositionRate
cFuturesWalletBalanceRate
cFuturesLastFundingFeeRate
cFuturesPredictFundingRate
cFuturesWalletFundRecordRate
cFuturesWalletWithdrawalRate
cFuturesAPIKeyInfoRate
uFuturesDefaultRate
uFuturesCreateOrderRate
uFuturesCancelOrderRate
uFuturesCancelAllOrderRate
uFuturesCreateConditionalOrderRate
uFuturesCancelConditionalOrderRate
uFuturesCancelAllConditionalOrderRate
uFuturesSetLeverageRate
uFuturesSwitchMargin
uFuturesSwitchPosition
uFuturesSetMarginRate
uFuturesSetTradingStopRate
uFuturesUpdateMarginRate
uFuturesPositionRate
uFuturesGetClosedTradesRate
uFuturesGetTradesRate
uFuturesGetActiveOrderRate
uFuturesGetActiveRealtimeOrderRate
uFuturesGetConditionalOrderRate
uFuturesGetConditionalRealtimeOrderRate
uFuturesGetMyLastFundingFeeRate
uFuturesPredictFundingRate
futuresDefaultRate
futuresCancelOrderRate
futuresCreateOrderRate
futuresReplaceOrderRate
futuresCancelAllOrderRate
futuresCancelAllConditionalOrderRate
futuresReplaceConditionalOrderRate
futuresCancelConditionalOrderRate
futuresCreateConditionalOrderRate
futuresGetActiveOrderRate
futuresGetConditionalOrderRate
futuresGetActiveRealtimeOrderRate
futuresGetConditionalRealtimeOrderRate
futuresGetTradeRate
futuresSetLeverageRate
futuresUpdateMarginRate
futuresSetTradingStopRate
futuresSwitchPositionModeRate
futuresSwitchMarginRate
futuresSwitchPositionRate
futuresPositionRate
usdcPublicRate
usdcCancelAllOrderRate
usdcPlaceOrderRate
usdcModifyOrderRate
usdcCancelOrderRate
usdcGetOrderRate
usdcGetOrderHistoryRate
usdcGetTradeHistoryRate
usdcGetTransactionRate
usdcGetWalletRate
usdcGetAssetRate
usdcGetMarginRate
usdcGetPositionRate
usdcSetLeverageRate
usdcGetSettlementRate
usdcSetRiskRate
usdcGetPredictedFundingRate
defaultEPL request.EndpointLimit = iota
createOrderEPL
createSpotOrderEPL
amendOrderEPL
cancelOrderEPL
cancelSpotEPL
cancelAllEPL
cancelAllSpotEPL
createBatchOrderEPL
amendBatchOrderEPL
cancelBatchOrderEPL
getOrderEPL
getOrderHistoryEPL
getPositionListEPL
getExecutionListEPL
getPositionClosedPNLEPL
postPositionSetLeverageEPL
setPositionTPLSModeEPL
setPositionRiskLimitEPL
stopTradingPositionEPL
getAccountWalletBalanceEPL
getAccountFeeEPL
getAssetTransferQueryInfoEPL
getAssetTransferQueryTransferCoinListEPL
getAssetTransferCoinListEPL
getAssetInterTransferListEPL
getSubMemberListEPL
getAssetUniversalTransferListEPL
getAssetAccountCoinBalanceEPL
getAssetDepositRecordsEPL
getAssetDepositSubMemberRecordsEPL
getAssetDepositSubMemberAddressEPL
getWithdrawRecordsEPL
getAssetCoinInfoEPL
getExchangeOrderRecordEPL
interTransferEPL
saveTransferSubMemberEPL
universalTransferEPL
createWithdrawalEPL
cancelWithdrawalEPL
userCreateSubMemberEPL
userCreateSubAPIKeyEPL
userFrozenSubMemberEPL
userUpdateAPIEPL
userUpdateSubAPIEPL
userDeleteAPIEPL
userDeleteSubAPIEPL
userQuerySubMembersEPL
userQueryAPIEPL
getSpotLeverageTokenOrderRecordsEPL
spotLeverageTokenPurchaseEPL
spotLeverTokenRedeemEPL
getSpotCrossMarginTradeLoanInfoEPL
getSpotCrossMarginTradeAccountEPL
getSpotCrossMarginTradeOrdersEPL
getSpotCrossMarginTradeRepayHistoryEPL
spotCrossMarginTradeLoanEPL
spotCrossMarginTradeRepayEPL
spotCrossMarginTradeSwitchEPL
)
// RateLimit implements the request.Limiter interface
type RateLimit struct {
SpotRate *rate.Limiter
FuturesRate *rate.Limiter
PrivateSpotRate *rate.Limiter
PrivateFeeRate *rate.Limiter
CMFuturesDefaultRate *rate.Limiter
CMFuturesOrderRate *rate.Limiter
CMFuturesOrderListRate *rate.Limiter
CMFuturesExecutionRate *rate.Limiter
CMFuturesPositionRate *rate.Limiter
CMFuturesPositionListRate *rate.Limiter
CMFuturesFundingRate *rate.Limiter
CMFuturesWalletRate *rate.Limiter
CMFuturesAccountRate *rate.Limiter
UFuturesDefaultRate *rate.Limiter
UFuturesOrderRate *rate.Limiter
UFuturesPositionRate *rate.Limiter
UFuturesPositionListRate *rate.Limiter
UFuturesOrderListRate *rate.Limiter
UFuturesFundingRate *rate.Limiter
FuturesDefaultRate *rate.Limiter
FuturesOrderRate *rate.Limiter
FuturesOrderListRate *rate.Limiter
FuturesExecutionRate *rate.Limiter
FuturesPositionRate *rate.Limiter
FuturesPositionListRate *rate.Limiter
USDCPublic *rate.Limiter
USDCPlaceOrderRate *rate.Limiter
USDCModifyOrderRate *rate.Limiter
USDCCancelOrderRate *rate.Limiter
USDCCancelAllOrderRate *rate.Limiter
USDCGetOrderRate *rate.Limiter
USDCGetOrderHistoryRate *rate.Limiter
USDCGetTradeHistoryRate *rate.Limiter
USDCGetTransactionRate *rate.Limiter
USDCGetWalletRate *rate.Limiter
USDCGetAssetRate *rate.Limiter
USDCGetMarginRate *rate.Limiter
USDCGetPositionRate *rate.Limiter
USDCSetLeverageRate *rate.Limiter
USDCGetSettlementRate *rate.Limiter
USDCSetRiskRate *rate.Limiter
USDCGetPredictedFundingRate *rate.Limiter
SpotRate *rate.Limiter
CreateOrderRate *rate.Limiter
CreateSpotOrderRate *rate.Limiter
AmendOrderRate *rate.Limiter
CancelOrderRate *rate.Limiter
CancelSpotRate *rate.Limiter
CancelAllRate *rate.Limiter
CancelAllSpotRate *rate.Limiter
CreateBatchOrderRate *rate.Limiter
AmendBatchOrderRate *rate.Limiter
CancelBatchOrderRate *rate.Limiter
GetOrderRate *rate.Limiter
GetOrderHistoryRate *rate.Limiter
GetPositionListRate *rate.Limiter
GetExecutionListRate *rate.Limiter
GetPositionClosedPNLRate *rate.Limiter
PostPositionSetLeverageRate *rate.Limiter
SetPositionTPLSModeRate *rate.Limiter
SetPositionRiskLimitRate *rate.Limiter
StopTradingPositionRate *rate.Limiter
GetAccountWalletBalanceRate *rate.Limiter
GetAccountFeeRate *rate.Limiter
GetAssetTransferQueryInfoRate *rate.Limiter
GetAssetTransferQueryTransferCoinListRate *rate.Limiter
GetAssetTransferCoinListRate *rate.Limiter
GetAssetInterTransferListRate *rate.Limiter
GetSubMemberListRate *rate.Limiter
GetAssetUniversalTransferListRate *rate.Limiter
GetAssetAccountCoinBalanceRate *rate.Limiter
GetAssetDepositRecordsRate *rate.Limiter
GetAssetDepositSubMemberRecordsRate *rate.Limiter
GetAssetDepositSubMemberAddressRate *rate.Limiter
GetWithdrawRecordsRate *rate.Limiter
GetAssetCoinInfoRate *rate.Limiter
GetExchangeOrderRecordRate *rate.Limiter
InterTransferRate *rate.Limiter
SaveTransferSubMemberRate *rate.Limiter
UniversalTransferRate *rate.Limiter
CreateWithdrawalRate *rate.Limiter
CancelWithdrawalRate *rate.Limiter
UserCreateSubMemberRate *rate.Limiter
UserCreateSubAPIKeyRate *rate.Limiter
UserFrozenSubMemberRate *rate.Limiter
UserUpdateAPIRate *rate.Limiter
UserUpdateSubAPIRate *rate.Limiter
UserDeleteAPIRate *rate.Limiter
UserDeleteSubAPIRate *rate.Limiter
UserQuerySubMembersRate *rate.Limiter
UserQueryAPIRate *rate.Limiter
GetSpotLeverageTokenOrderRecordsRate *rate.Limiter
SpotLeverageTokenPurchaseRate *rate.Limiter
SpotLeverTokenRedeemRate *rate.Limiter
GetSpotCrossMarginTradeLoanInfoRate *rate.Limiter
GetSpotCrossMarginTradeAccountRate *rate.Limiter
GetSpotCrossMarginTradeOrdersRate *rate.Limiter
GetSpotCrossMarginTradeRepayHistoryRate *rate.Limiter
SpotCrossMarginTradeLoanRate *rate.Limiter
SpotCrossMarginTradeRepayRate *rate.Limiter
SpotCrossMarginTradeSwitchRate *rate.Limiter
}
// Limit executes rate limiting functionality for Binance
@@ -203,96 +144,124 @@ func (r *RateLimit) Limit(ctx context.Context, f request.EndpointLimit) error {
var limiter *rate.Limiter
var tokens int
switch f {
case publicSpotRate:
case defaultEPL:
limiter, tokens = r.SpotRate, 1
case privateSpotRate:
limiter, tokens = r.PrivateSpotRate, 1
case privateFeeRate:
limiter, tokens = r.PrivateFeeRate, 1
case cFuturesDefaultRate:
limiter, tokens = r.CMFuturesDefaultRate, 1
case cFuturesCancelActiveOrderRate, cFuturesCreateConditionalOrderRate, cFuturesCancelConditionalOrderRate, cFuturesReplaceActiveOrderRate,
cFuturesReplaceConditionalOrderRate, cFuturesCreateOrderRate:
limiter, tokens = r.CMFuturesOrderRate, 1
case cFuturesCancelAllActiveOrderRate, cFuturesCancelAllConditionalOrderRate:
limiter, tokens = r.CMFuturesOrderRate, 10
case cFuturesGetActiveOrderRate, cFuturesGetConditionalOrderRate, cFuturesGetRealtimeOrderRate:
limiter, tokens = r.CMFuturesOrderListRate, 1
case cFuturesTradeRate:
limiter, tokens = r.CMFuturesExecutionRate, 1
case cFuturesSetLeverageRate, cFuturesUpdateMarginRate, cFuturesSetTradingRate, cFuturesSwitchPositionRate, cFuturesGetTradingFeeRate:
limiter, tokens = r.CMFuturesPositionRate, 1
case cFuturesPositionRate, cFuturesWalletBalanceRate:
limiter, tokens = r.CMFuturesPositionListRate, 1
case cFuturesLastFundingFeeRate, cFuturesPredictFundingRate:
limiter, tokens = r.CMFuturesFundingRate, 1
case cFuturesWalletFundRecordRate, cFuturesWalletWithdrawalRate:
limiter, tokens = r.CMFuturesWalletRate, 1
case cFuturesAPIKeyInfoRate:
limiter, tokens = r.CMFuturesAccountRate, 1
case uFuturesDefaultRate:
limiter, tokens = r.UFuturesDefaultRate, 1
case uFuturesCreateOrderRate, uFuturesCancelOrderRate, uFuturesCreateConditionalOrderRate, uFuturesCancelConditionalOrderRate:
limiter, tokens = r.UFuturesOrderRate, 1
case uFuturesCancelAllOrderRate, uFuturesCancelAllConditionalOrderRate:
limiter, tokens = r.UFuturesOrderRate, 10
case uFuturesSetLeverageRate, uFuturesSwitchMargin, uFuturesSwitchPosition, uFuturesSetMarginRate, uFuturesSetTradingStopRate, uFuturesUpdateMarginRate:
limiter, tokens = r.UFuturesPositionRate, 1
case uFuturesPositionRate, uFuturesGetClosedTradesRate, uFuturesGetTradesRate:
limiter, tokens = r.UFuturesPositionListRate, 1
case uFuturesGetActiveOrderRate, uFuturesGetActiveRealtimeOrderRate, uFuturesGetConditionalOrderRate, uFuturesGetConditionalRealtimeOrderRate:
limiter, tokens = r.UFuturesOrderListRate, 1
case uFuturesGetMyLastFundingFeeRate, uFuturesPredictFundingRate:
limiter, tokens = r.UFuturesFundingRate, 1
case futuresDefaultRate:
limiter, tokens = r.FuturesDefaultRate, 1
case futuresCancelOrderRate, futuresCreateOrderRate, futuresReplaceOrderRate, futuresReplaceConditionalOrderRate, futuresCancelConditionalOrderRate,
futuresCreateConditionalOrderRate:
limiter, tokens = r.FuturesOrderRate, 1
case futuresCancelAllOrderRate, futuresCancelAllConditionalOrderRate:
limiter, tokens = r.FuturesOrderRate, 10
case futuresGetActiveOrderRate, futuresGetConditionalOrderRate, futuresGetActiveRealtimeOrderRate, futuresGetConditionalRealtimeOrderRate:
limiter, tokens = r.FuturesOrderListRate, 1
case futuresGetTradeRate:
limiter, tokens = r.FuturesExecutionRate, 1
case futuresSetLeverageRate, futuresUpdateMarginRate, futuresSetTradingStopRate, futuresSwitchPositionModeRate, futuresSwitchMarginRate, futuresSwitchPositionRate:
limiter, tokens = r.FuturesPositionRate, 1
case futuresPositionRate:
limiter, tokens = r.FuturesPositionListRate, 1
case usdcPublicRate:
limiter, tokens = r.USDCPublic, 1
case usdcCancelAllOrderRate:
limiter, tokens = r.USDCCancelAllOrderRate, 1
case usdcPlaceOrderRate:
limiter, tokens = r.USDCPlaceOrderRate, 1
case usdcModifyOrderRate:
limiter, tokens = r.USDCModifyOrderRate, 1
case usdcCancelOrderRate:
limiter, tokens = r.USDCCancelOrderRate, 1
case usdcGetOrderRate:
limiter, tokens = r.USDCGetOrderRate, 1
case usdcGetOrderHistoryRate:
limiter, tokens = r.USDCGetOrderHistoryRate, 1
case usdcGetTradeHistoryRate:
limiter, tokens = r.USDCGetTradeHistoryRate, 1
case usdcGetTransactionRate:
limiter, tokens = r.USDCGetTransactionRate, 1
case usdcGetWalletRate:
limiter, tokens = r.USDCGetWalletRate, 1
case usdcGetAssetRate:
limiter, tokens = r.USDCGetAssetRate, 1
case usdcGetMarginRate:
limiter, tokens = r.USDCGetMarginRate, 1
case usdcGetPositionRate:
limiter, tokens = r.USDCGetPositionRate, 1
case usdcSetLeverageRate:
limiter, tokens = r.USDCSetLeverageRate, 1
case usdcGetSettlementRate:
limiter, tokens = r.USDCGetSettlementRate, 1
case usdcSetRiskRate:
limiter, tokens = r.USDCSetRiskRate, 1
case usdcGetPredictedFundingRate:
limiter, tokens = r.USDCGetPredictedFundingRate, 1
case createOrderEPL:
limiter, tokens = r.CreateOrderRate, 10
case createSpotOrderEPL:
limiter, tokens = r.CreateSpotOrderRate, 20
case amendOrderEPL:
limiter, tokens = r.AmendOrderRate, 10
case cancelOrderEPL:
limiter, tokens = r.CancelOrderRate, 10
case cancelSpotEPL:
limiter, tokens = r.CancelSpotRate, 20
case cancelAllEPL:
limiter, tokens = r.CancelAllRate, 1
case cancelAllSpotEPL:
limiter, tokens = r.CancelAllSpotRate, 20
case createBatchOrderEPL:
limiter, tokens = r.CreateBatchOrderRate, 10
case amendBatchOrderEPL:
limiter, tokens = r.AmendBatchOrderRate, 10
case cancelBatchOrderEPL:
limiter, tokens = r.CancelBatchOrderRate, 10
case getOrderEPL:
limiter, tokens = r.GetOrderRate, 10
case getOrderHistoryEPL:
limiter, tokens = r.GetOrderHistoryRate, 10
case getPositionListEPL:
limiter, tokens = r.GetPositionListRate, 10
case getExecutionListEPL:
limiter, tokens = r.GetExecutionListRate, 10
case getPositionClosedPNLEPL:
limiter, tokens = r.GetPositionClosedPNLRate, 10
case postPositionSetLeverageEPL:
limiter, tokens = r.PostPositionSetLeverageRate, 10
case setPositionTPLSModeEPL:
limiter, tokens = r.SetPositionTPLSModeRate, 10
case setPositionRiskLimitEPL:
limiter, tokens = r.SetPositionRiskLimitRate, 10
case stopTradingPositionEPL:
limiter, tokens = r.StopTradingPositionRate, 10
case getAccountWalletBalanceEPL:
limiter, tokens = r.GetAccountWalletBalanceRate, 10
case getAccountFeeEPL:
limiter, tokens = r.GetAccountFeeRate, 10
case getAssetTransferQueryInfoEPL:
limiter, tokens = r.GetAssetTransferQueryInfoRate, 1
case getAssetTransferQueryTransferCoinListEPL:
limiter, tokens = r.GetAssetTransferQueryTransferCoinListRate, 1
case getAssetTransferCoinListEPL:
limiter, tokens = r.GetAssetTransferCoinListRate, 1
case getAssetInterTransferListEPL:
limiter, tokens = r.GetAssetInterTransferListRate, 1
case getSubMemberListEPL:
limiter, tokens = r.GetSubMemberListRate, 1
case getAssetUniversalTransferListEPL:
limiter, tokens = r.GetAssetUniversalTransferListRate, 2
case getAssetAccountCoinBalanceEPL:
limiter, tokens = r.GetAssetAccountCoinBalanceRate, 2
case getAssetDepositRecordsEPL:
limiter, tokens = r.GetAssetDepositRecordsRate, 1
case getAssetDepositSubMemberRecordsEPL:
limiter, tokens = r.GetAssetDepositSubMemberRecordsRate, 1
case getAssetDepositSubMemberAddressEPL:
limiter, tokens = r.GetAssetDepositSubMemberAddressRate, 1
case getWithdrawRecordsEPL:
limiter, tokens = r.GetWithdrawRecordsRate, 1
case getAssetCoinInfoEPL:
limiter, tokens = r.GetAssetCoinInfoRate, 1
case getExchangeOrderRecordEPL:
limiter, tokens = r.GetExchangeOrderRecordRate, 1
case interTransferEPL:
limiter, tokens = r.InterTransferRate, 1
case saveTransferSubMemberEPL:
limiter, tokens = r.SaveTransferSubMemberRate, 1
case universalTransferEPL:
limiter, tokens = r.UniversalTransferRate, 5
case createWithdrawalEPL:
limiter, tokens = r.CreateWithdrawalRate, 1
case cancelWithdrawalEPL:
limiter, tokens = r.CancelWithdrawalRate, 1
case userCreateSubMemberEPL:
limiter, tokens = r.UserCreateSubMemberRate, 5
case userCreateSubAPIKeyEPL:
limiter, tokens = r.UserCreateSubAPIKeyRate, 5
case userFrozenSubMemberEPL:
limiter, tokens = r.UserFrozenSubMemberRate, 5
case userUpdateAPIEPL:
limiter, tokens = r.UserUpdateAPIRate, 5
case userUpdateSubAPIEPL:
limiter, tokens = r.UserUpdateSubAPIRate, 5
case userDeleteAPIEPL:
limiter, tokens = r.UserDeleteAPIRate, 5
case userDeleteSubAPIEPL:
limiter, tokens = r.UserDeleteSubAPIRate, 5
case userQuerySubMembersEPL:
limiter, tokens = r.UserQuerySubMembersRate, 10
case userQueryAPIEPL:
limiter, tokens = r.UserQueryAPIRate, 10
case getSpotLeverageTokenOrderRecordsEPL:
limiter, tokens = r.GetSpotLeverageTokenOrderRecordsRate, 50
case spotLeverageTokenPurchaseEPL:
limiter, tokens = r.SpotLeverageTokenPurchaseRate, 20
case spotLeverTokenRedeemEPL:
limiter, tokens = r.SpotLeverTokenRedeemRate, 20
case getSpotCrossMarginTradeLoanInfoEPL:
limiter, tokens = r.GetSpotCrossMarginTradeLoanInfoRate, 50
case getSpotCrossMarginTradeAccountEPL:
limiter, tokens = r.GetSpotCrossMarginTradeAccountRate, 50
case getSpotCrossMarginTradeOrdersEPL:
limiter, tokens = r.GetSpotCrossMarginTradeOrdersRate, 50
case getSpotCrossMarginTradeRepayHistoryEPL:
limiter, tokens = r.GetSpotCrossMarginTradeRepayHistoryRate, 50
case spotCrossMarginTradeLoanEPL:
limiter, tokens = r.SpotCrossMarginTradeLoanRate, 50
case spotCrossMarginTradeRepayEPL:
limiter, tokens = r.SpotCrossMarginTradeRepayRate, 50
case spotCrossMarginTradeSwitchEPL:
limiter, tokens = r.SpotCrossMarginTradeSwitchRate, 50
default:
limiter, tokens = r.SpotRate, 1
}
@@ -324,47 +293,64 @@ func (r *RateLimit) Limit(ctx context.Context, f request.EndpointLimit) error {
// SetRateLimit returns the rate limit for the exchange
func SetRateLimit() *RateLimit {
return &RateLimit{
SpotRate: request.NewRateLimit(spotInterval, spotRequestRate),
FuturesRate: request.NewRateLimit(futuresPublicInterval, futuresRequestRate),
PrivateSpotRate: request.NewRateLimit(spotPrivateInterval, spotPrivateRequestRate),
PrivateFeeRate: request.NewRateLimit(spotPrivateInterval, spotPrivateFeeRequestRate),
CMFuturesDefaultRate: request.NewRateLimit(futuresInterval, futuresDefaultRateCount),
CMFuturesOrderRate: request.NewRateLimit(futuresInterval, futuresOrderRate),
CMFuturesOrderListRate: request.NewRateLimit(futuresInterval, futuresOrderListRate),
CMFuturesExecutionRate: request.NewRateLimit(futuresInterval, futuresExecutionRate),
CMFuturesPositionRate: request.NewRateLimit(futuresInterval, futuresPositionRateCount),
CMFuturesPositionListRate: request.NewRateLimit(futuresInterval, futuresPositionListRate),
CMFuturesFundingRate: request.NewRateLimit(futuresInterval, futuresFundingRate),
CMFuturesWalletRate: request.NewRateLimit(futuresInterval, futuresWalletRate),
CMFuturesAccountRate: request.NewRateLimit(futuresInterval, futuresAccountRate),
UFuturesDefaultRate: request.NewRateLimit(futuresInterval, futuresDefaultRateCount),
UFuturesOrderRate: request.NewRateLimit(futuresInterval, futuresOrderRate),
UFuturesPositionRate: request.NewRateLimit(futuresInterval, futuresPositionRateCount),
UFuturesPositionListRate: request.NewRateLimit(futuresInterval, futuresPositionListRate),
UFuturesOrderListRate: request.NewRateLimit(futuresInterval, futuresOrderListRate),
UFuturesFundingRate: request.NewRateLimit(futuresInterval, futuresFundingRate),
FuturesDefaultRate: request.NewRateLimit(futuresInterval, futuresDefaultRateCount),
FuturesOrderRate: request.NewRateLimit(futuresInterval, futuresOrderRate),
FuturesOrderListRate: request.NewRateLimit(futuresInterval, futuresOrderListRate),
FuturesExecutionRate: request.NewRateLimit(futuresInterval, futuresExecutionRate),
FuturesPositionRate: request.NewRateLimit(futuresInterval, futuresPositionRateCount),
FuturesPositionListRate: request.NewRateLimit(futuresInterval, futuresPositionListRate),
USDCPublic: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPublicRate),
USDCPlaceOrderRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate),
USDCModifyOrderRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate),
USDCCancelOrderRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate),
USDCCancelAllOrderRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualCancelAllRate),
USDCGetOrderRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate),
USDCGetOrderHistoryRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate),
USDCGetTradeHistoryRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate),
USDCGetTransactionRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate),
USDCGetWalletRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate),
USDCGetAssetRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate),
USDCGetMarginRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate),
USDCGetPositionRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate),
USDCSetLeverageRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate),
USDCGetSettlementRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate),
USDCSetRiskRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate),
USDCGetPredictedFundingRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate),
SpotRate: request.NewRateLimit(spotInterval, 120),
CreateOrderRate: request.NewRateLimit(time.Second, 10),
CreateSpotOrderRate: request.NewRateLimit(time.Second, 20),
AmendOrderRate: request.NewRateLimit(time.Second, 10),
CancelOrderRate: request.NewRateLimit(time.Second, 10),
CancelSpotRate: request.NewRateLimit(time.Second, 20),
CancelAllRate: request.NewRateLimit(time.Second, 1),
CancelAllSpotRate: request.NewRateLimit(time.Second, 20),
CreateBatchOrderRate: request.NewRateLimit(time.Second, 10),
AmendBatchOrderRate: request.NewRateLimit(time.Second, 10),
CancelBatchOrderRate: request.NewRateLimit(time.Second, 10),
GetOrderRate: request.NewRateLimit(time.Second, 10),
GetOrderHistoryRate: request.NewRateLimit(time.Second, 10),
GetPositionListRate: request.NewRateLimit(time.Second, 10),
GetExecutionListRate: request.NewRateLimit(time.Second, 10),
GetPositionClosedPNLRate: request.NewRateLimit(time.Second, 10),
PostPositionSetLeverageRate: request.NewRateLimit(time.Second, 10),
SetPositionTPLSModeRate: request.NewRateLimit(time.Second, 10),
SetPositionRiskLimitRate: request.NewRateLimit(time.Second, 10),
StopTradingPositionRate: request.NewRateLimit(time.Second, 10),
GetAccountWalletBalanceRate: request.NewRateLimit(time.Second, 10),
GetAccountFeeRate: request.NewRateLimit(time.Second, 10),
GetAssetTransferQueryInfoRate: request.NewRateLimit(time.Minute, 60),
GetAssetTransferQueryTransferCoinListRate: request.NewRateLimit(time.Minute, 60),
GetAssetTransferCoinListRate: request.NewRateLimit(time.Minute, 60),
GetAssetInterTransferListRate: request.NewRateLimit(time.Minute, 60),
GetSubMemberListRate: request.NewRateLimit(time.Minute, 60),
GetAssetUniversalTransferListRate: request.NewRateLimit(time.Second, 2),
GetAssetAccountCoinBalanceRate: request.NewRateLimit(time.Second, 2),
GetAssetDepositRecordsRate: request.NewRateLimit(time.Minute, 300),
GetAssetDepositSubMemberRecordsRate: request.NewRateLimit(time.Minute, 300),
GetAssetDepositSubMemberAddressRate: request.NewRateLimit(time.Minute, 300),
GetWithdrawRecordsRate: request.NewRateLimit(time.Minute, 300),
GetAssetCoinInfoRate: request.NewRateLimit(time.Minute, 300),
GetExchangeOrderRecordRate: request.NewRateLimit(time.Minute, 300),
InterTransferRate: request.NewRateLimit(time.Minute, 20),
SaveTransferSubMemberRate: request.NewRateLimit(time.Minute, 20),
UniversalTransferRate: request.NewRateLimit(time.Second, 5),
CreateWithdrawalRate: request.NewRateLimit(time.Second, 1),
CancelWithdrawalRate: request.NewRateLimit(time.Minute, 60),
UserCreateSubMemberRate: request.NewRateLimit(time.Second, 5),
UserCreateSubAPIKeyRate: request.NewRateLimit(time.Second, 5),
UserFrozenSubMemberRate: request.NewRateLimit(time.Second, 5),
UserUpdateAPIRate: request.NewRateLimit(time.Second, 5),
UserUpdateSubAPIRate: request.NewRateLimit(time.Second, 5),
UserDeleteAPIRate: request.NewRateLimit(time.Second, 5),
UserDeleteSubAPIRate: request.NewRateLimit(time.Second, 5),
UserQuerySubMembersRate: request.NewRateLimit(time.Second, 10),
UserQueryAPIRate: request.NewRateLimit(time.Second, 10),
GetSpotLeverageTokenOrderRecordsRate: request.NewRateLimit(time.Second, 50),
SpotLeverageTokenPurchaseRate: request.NewRateLimit(time.Second, 20),
SpotLeverTokenRedeemRate: request.NewRateLimit(time.Second, 20),
GetSpotCrossMarginTradeLoanInfoRate: request.NewRateLimit(time.Second, 50),
GetSpotCrossMarginTradeAccountRate: request.NewRateLimit(time.Second, 50),
GetSpotCrossMarginTradeOrdersRate: request.NewRateLimit(time.Second, 50),
GetSpotCrossMarginTradeRepayHistoryRate: request.NewRateLimit(time.Second, 50),
SpotCrossMarginTradeLoanRate: request.NewRateLimit(time.Second, 20),
SpotCrossMarginTradeRepayRate: request.NewRateLimit(time.Second, 20),
SpotCrossMarginTradeSwitchRate: request.NewRateLimit(time.Second, 20),
}
}

View File

@@ -27,6 +27,7 @@ const (
ThreeHour = 3 * OneHour
FourHour = 4 * OneHour
SixHour = 6 * OneHour
SevenHour = 7 * OneHour
EightHour = 8 * OneHour
TwelveHour = 12 * OneHour
OneDay = 24 * OneHour
@@ -103,6 +104,7 @@ var (
ThreeHour,
FourHour,
SixHour,
SevenHour,
EightHour,
TwelveHour,
OneDay,

View File

@@ -9,17 +9,13 @@ import (
"golang.org/x/time/rate"
)
// Ratelimit intervals.
const (
// oneSecondInterval
oneSecondInterval = time.Second
// twoSecondInterval
twoSecondsInterval = 2 * time.Second
// threeSecondInterval
oneSecondInterval = time.Second
twoSecondsInterval = 2 * time.Second
threeSecondsInterval = 3 * time.Second
// fiveSecondsInterval
fiveSecondsInterval = 5 * time.Second
// tenSecondsInterval
tenSecondsInterval = 10 * time.Second
fiveSecondsInterval = 5 * time.Second
tenSecondsInterval = 10 * time.Second
)
// RateLimit implements the request.Limiter interface
@@ -224,6 +220,7 @@ const (
oneClickRepayCurrencyList = 1
tradeOneClickRepay = 1
getOneClickRepayHistory = 1
// Block Trading endpoints
getCounterpartiesRate = 5
createRfqRate = 5
@@ -242,6 +239,7 @@ const (
getTradesRate = 5
getTradesHistoryRate = 10
getPublicTradesRate = 5
// Funding
getCurrenciesRate = 6
getBalanceRate = 6
@@ -257,6 +255,7 @@ const (
cancelWithdrawalRate = 6
getWithdrawalHistoryRate = 6
smallAssetsConvertRate = 1
// Savings
getSavingBalanceRate = 6
savingsPurchaseRedemptionRate = 6
@@ -264,12 +263,14 @@ const (
getLendingHistoryRate = 6
getPublicBorrowInfoRate = 6
getPublicBorrowHistoryRate = 6
// Convert
getConvertCurrenciesRate = 6
getConvertCurrencyPairRate = 6
estimateQuoteRate = 10
convertTradeRate = 10
getConvertHistoryRate = 6
// Account
getAccountBalanceRate = 10
getPositionsRate = 10
@@ -297,6 +298,7 @@ const (
positionBuilderRate = 2
getGreeksRate = 10
getPMLimitation = 10
// Sub Account Endpoints
viewSubaccountListRate = 2
resetSubAccountAPIKey = 1
@@ -318,6 +320,7 @@ const (
computeMarginBalance = 20
adjustMarginBalance = 20
getGridAIParameter = 20
// Earn
getOffer = 3
purchase = 2
@@ -325,6 +328,7 @@ const (
cancelPurchaseOrRedemption = 2
getEarnActiveOrders = 3
getFundingOrderHistory = 3
// Market Data
getTickersRate = 20
getIndexTickersRate = 20
@@ -340,6 +344,7 @@ const (
getIndexComponentsRate = 20
getBlockTickersRate = 20
getBlockTradesRate = 20
// Public Data Endpoints
getInstrumentsRate = 20
getDeliveryExerciseHistoryRate = 40
@@ -359,6 +364,7 @@ const (
getUnderlyingRate = 20
getInsuranceFundRate = 10
unitConvertRate = 10
// Trading Data Endpoints
getSupportCoinRate = 5
getTakerVolumeRate = 5
@@ -369,6 +375,7 @@ const (
getPutCallRatioRate = 5
getOpenInterestAndVolumeRate = 5
getTakerFlowRate = 5
// Status Endpoints
getEventStatusRate = 1
)
@@ -889,6 +896,7 @@ func SetRateLimit() *RateLimit {
GetOneClickRepayHistory: request.NewRateLimit(twoSecondsInterval, getOneClickRepayHistory),
OneClickRepayCurrencyList: request.NewRateLimit(twoSecondsInterval, oneClickRepayCurrencyList),
TradeOneClickRepay: request.NewRateLimit(twoSecondsInterval, tradeOneClickRepay),
// Block Trading endpoints
GetCounterparties: request.NewRateLimit(twoSecondsInterval, getCounterpartiesRate),
CreateRfq: request.NewRateLimit(twoSecondsInterval, createRfqRate),
@@ -928,12 +936,14 @@ func SetRateLimit() *RateLimit {
GetLendingHistory: request.NewRateLimit(oneSecondInterval, getLendingHistoryRate),
GetPublicBorrowInfo: request.NewRateLimit(oneSecondInterval, getPublicBorrowInfoRate),
GetPublicBorrowHistory: request.NewRateLimit(oneSecondInterval, getPublicBorrowHistoryRate),
// Convert
GetConvertCurrencies: request.NewRateLimit(oneSecondInterval, getConvertCurrenciesRate),
GetConvertCurrencyPair: request.NewRateLimit(oneSecondInterval, getConvertCurrencyPairRate),
EstimateQuote: request.NewRateLimit(oneSecondInterval, estimateQuoteRate),
ConvertTrade: request.NewRateLimit(oneSecondInterval, convertTradeRate),
GetConvertHistory: request.NewRateLimit(oneSecondInterval, getConvertHistoryRate),
// Account
GetAccountBalance: request.NewRateLimit(twoSecondsInterval, getAccountBalanceRate),
GetPositions: request.NewRateLimit(twoSecondsInterval, getPositionsRate),
@@ -961,8 +971,8 @@ func SetRateLimit() *RateLimit {
PositionBuilder: request.NewRateLimit(twoSecondsInterval, positionBuilderRate),
GetGreeks: request.NewRateLimit(twoSecondsInterval, getGreeksRate),
GetPMLimitation: request.NewRateLimit(twoSecondsInterval, getPMLimitation),
// Sub Account Endpoints
// Sub Account Endpoints
ViewSubaccountList: request.NewRateLimit(twoSecondsInterval, viewSubaccountListRate),
ResetSubAccountAPIKey: request.NewRateLimit(oneSecondInterval, resetSubAccountAPIKey),
GetSubaccountTradingBalance: request.NewRateLimit(twoSecondsInterval, getSubaccountTradingBalanceRate),
@@ -971,8 +981,8 @@ func SetRateLimit() *RateLimit {
MasterAccountsManageTransfersBetweenSubaccount: request.NewRateLimit(oneSecondInterval, masterAccountsManageTransfersBetweenSubaccountRate),
SetPermissionOfTransferOut: request.NewRateLimit(oneSecondInterval, setPermissionOfTransferOutRate),
GetCustodyTradingSubaccountList: request.NewRateLimit(oneSecondInterval, getCustodyTradingSubaccountListRate),
// Grid Trading Endpoints
// Grid Trading Endpoints
GridTrading: request.NewRateLimit(twoSecondsInterval, gridTradingRate),
AmendGridAlgoOrder: request.NewRateLimit(twoSecondsInterval, amendGridAlgoOrderRate),
StopGridAlgoOrder: request.NewRateLimit(twoSecondsInterval, stopGridAlgoOrderRate),
@@ -985,6 +995,7 @@ func SetRateLimit() *RateLimit {
ComputeMarginBalance: request.NewRateLimit(twoSecondsInterval, computeMarginBalance),
AdjustMarginBalance: request.NewRateLimit(twoSecondsInterval, adjustMarginBalance),
GetGridAIParameter: request.NewRateLimit(twoSecondsInterval, getGridAIParameter),
// Earn
GetOffer: request.NewRateLimit(oneSecondInterval, getOffer),
Purchase: request.NewRateLimit(oneSecondInterval, purchase),
@@ -992,6 +1003,7 @@ func SetRateLimit() *RateLimit {
CancelPurchaseOrRedemption: request.NewRateLimit(oneSecondInterval, cancelPurchaseOrRedemption),
GetEarnActiveOrders: request.NewRateLimit(oneSecondInterval, getEarnActiveOrders),
GetFundingOrderHistory: request.NewRateLimit(oneSecondInterval, getFundingOrderHistory),
// Market Data
GetTickers: request.NewRateLimit(twoSecondsInterval, getTickersRate),
GetIndexTickers: request.NewRateLimit(twoSecondsInterval, getIndexTickersRate),
@@ -1009,7 +1021,6 @@ func SetRateLimit() *RateLimit {
GetBlockTrades: request.NewRateLimit(twoSecondsInterval, getBlockTradesRate),
// Public Data Endpoints
GetInstruments: request.NewRateLimit(twoSecondsInterval, getInstrumentsRate),
GetDeliveryExerciseHistory: request.NewRateLimit(twoSecondsInterval, getDeliveryExerciseHistoryRate),
GetOpenInterest: request.NewRateLimit(twoSecondsInterval, getOpenInterestRate),
@@ -1030,7 +1041,6 @@ func SetRateLimit() *RateLimit {
UnitConvert: request.NewRateLimit(twoSecondsInterval, unitConvertRate),
// Trading Data Endpoints
GetSupportCoin: request.NewRateLimit(twoSecondsInterval, getSupportCoinRate),
GetTakerVolume: request.NewRateLimit(twoSecondsInterval, getTakerVolumeRate),
GetMarginLendingRatio: request.NewRateLimit(twoSecondsInterval, getMarginLendingRatioRate),
@@ -1042,7 +1052,6 @@ func SetRateLimit() *RateLimit {
GetTakerFlow: request.NewRateLimit(twoSecondsInterval, getTakerFlowRate),
// Status Endpoints
GetEventStatus: request.NewRateLimit(fiveSecondsInterval, getEventStatusRate),
}
}

View File

@@ -63,9 +63,13 @@ type Submit struct {
QuoteAmount float64
// TriggerPrice is mandatory if order type `Stop, Stop Limit or Take Profit`
// See btcmarkets_wrapper.go.
TriggerPrice float64
ClientID string // TODO: Shift to credentials
ClientOrderID string
TriggerPrice float64
// added to represent a unified trigger price type information such as LastPrice, MarkPrice, and IndexPrice
// https://bybit-exchange.github.io/docs/v5/order/create-order
TriggerPriceType PriceType
ClientID string // TODO: Shift to credentials
ClientOrderID string
// The system will first borrow you funds at the optimal interest rate and then place an order for you.
// see kucoin_wrapper.go
@@ -80,7 +84,8 @@ type Submit struct {
RetrieveFees bool
// RetrieveFeeDelay some exchanges take time to properly save order data
// and cannot retrieve fees data immediately
RetrieveFeeDelay time.Duration
RetrieveFeeDelay time.Duration
RiskManagementModes RiskManagementModes
// Hidden when enabled orders not displaying in order book.
Hidden bool
@@ -142,6 +147,12 @@ type Modify struct {
Price float64
Amount float64
TriggerPrice float64
// added to represent a unified trigger price type information such as LastPrice, MarkPrice, and IndexPrice
// https://bybit-exchange.github.io/docs/v5/order/create-order
TriggerPriceType PriceType
RiskManagementModes RiskManagementModes
}
// ModifyResponse is an order modifying return type
@@ -303,6 +314,7 @@ const (
Active
PartiallyCancelled
PartiallyFilled
PartiallyFilledCancelled
Filled
Cancelled
PendingCancel
@@ -398,3 +410,34 @@ type ClassificationError struct {
// forcing required filter operations when calling method Filter() on
// MultiOrderRequest.
type FilteredOrders []Detail
// RiskManagement represents a risk management detail information.
type RiskManagement struct {
Enabled bool
TriggerPriceType PriceType
Price float64
// LimitPrice limit order price when stop-los or take-profit risk management method is triggered
LimitPrice float64
// OrderType order type when stop-loss or take-profit risk management method is triggered.
OrderType Type
}
// RiskManagementModes represents take-profit and stop-loss risk management methods.
type RiskManagementModes struct {
// Mode take-profit/stop-loss mode
Mode string
TakeProfit RiskManagement
StopLoss RiskManagement
}
// PriceType enforces a standard for price types used for take-profit and stop-loss trigger types
type PriceType uint8
// price types
const (
LastPrice PriceType = 0
IndexPrice PriceType = 1 << iota
MarkPrice
UnknownPriceType
)

View File

@@ -25,7 +25,7 @@ const (
shortSide = Short | Sell | Ask
longSide = Long | Buy | Bid
inactiveStatuses = Filled | Cancelled | InsufficientBalance | MarketUnavailable | Rejected | PartiallyCancelled | Expired | Closed | AnyStatus | Cancelling | Liquidated
inactiveStatuses = Filled | Cancelled | InsufficientBalance | MarketUnavailable | Rejected | PartiallyCancelled | PartiallyFilledCancelled | Expired | Closed | AnyStatus | Cancelling | Liquidated
activeStatuses = Active | Open | PartiallyFilled | New | PendingCancel | Hidden | AutoDeleverage | Pending
notPlaced = InsufficientBalance | MarketUnavailable | Rejected
)
@@ -37,6 +37,9 @@ var (
// ErrOrderNotFound is returned when no order is found
ErrOrderNotFound = errors.New("order not found")
// ErrUnknownPriceType returned when price type is unknown
ErrUnknownPriceType = errors.New("unknown price type")
errTimeInForceConflict = errors.New("multiple time in force options applied")
errUnrecognisedOrderType = errors.New("unrecognised order type")
errUnrecognisedOrderStatus = errors.New("unrecognised order status")
@@ -784,6 +787,8 @@ func (s Status) String() string {
return "PARTIALLY_CANCELLED"
case PartiallyFilled:
return "PARTIALLY_FILLED"
case PartiallyFilledCancelled:
return "PARTIALLY_FILLED_CANCELED"
case Filled:
return "FILLED"
case Cancelled:
@@ -1132,6 +1137,8 @@ func StringToOrderStatus(status string) (Status, error) {
return Filled, nil
case PartiallyCancelled.String(), "PARTIALLY CANCELLED", "ORDER_PARTIALLY_TRANSACTED":
return PartiallyCancelled, nil
case PartiallyFilledCancelled.String(), "PARTIALLYFILLEDCANCELED":
return PartiallyFilledCancelled, nil
case Open.String():
return Open, nil
case Closed.String():
@@ -1285,3 +1292,33 @@ func (m *Modify) Validate(opt ...validate.Checker) error {
}
return nil
}
// String implements the stringer interface
func (t PriceType) String() string {
switch t {
case LastPrice:
return "LastPrice"
case IndexPrice:
return "IndexPrice"
case MarkPrice:
return "MarkPrice"
default:
return ""
}
}
// StringToPriceType for converting case insensitive order side
// and returning a real Side
func (t PriceType) StringToPriceType(priceType string) (PriceType, error) {
priceType = strings.ToLower(priceType)
switch priceType {
case "lastprice", "":
return LastPrice, nil
case "indexprice":
return IndexPrice, nil
case "markprice":
return MarkPrice, nil
default:
return UnknownPriceType, ErrUnknownPriceType
}
}

File diff suppressed because one or more lines are too long

View File

@@ -7,6 +7,7 @@ bitmex,
bitstamp,
btc markets,
btse,
bybit,
coinbasepro,
coinut,
exmo,
1 binanceus
7 bitstamp
8 btc markets
9 btse
10 bybit
11 coinbasepro
12 coinut
13 exmo

File diff suppressed because it is too large Load Diff