mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
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:
10
README.md
10
README.md
@@ -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
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
22
exchanges/bybit/bybit_convert.go
Normal file
22
exchanges/bybit/bybit_convert.go
Normal 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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
78
exchanges/bybit/bybit_inverse_websocket.go
Normal file
78
exchanges/bybit/bybit_inverse_websocket.go
Normal 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
|
||||
}
|
||||
98
exchanges/bybit/bybit_linear_websocket.go
Normal file
98
exchanges/bybit/bybit_linear_websocket.go
Normal 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
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
85
exchanges/bybit/bybit_options_websocket.go
Normal file
85
exchanges/bybit/bybit_options_websocket.go
Normal 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
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"`
|
||||
}
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
190
testdata/configtest.json
vendored
190
testdata/configtest.json
vendored
File diff suppressed because one or more lines are too long
1
testdata/exchangelist.csv
vendored
1
testdata/exchangelist.csv
vendored
@@ -7,6 +7,7 @@ bitmex,
|
||||
bitstamp,
|
||||
btc markets,
|
||||
btse,
|
||||
bybit,
|
||||
coinbasepro,
|
||||
coinut,
|
||||
exmo,
|
||||
|
||||
|
514907
testdata/http_mock/bybit/bybit.json
vendored
514907
testdata/http_mock/bybit/bybit.json
vendored
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user