mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-29 15:10:37 +00:00
exchanges: Add ByBit support (#887)
* few fixes and add ratelimiter * adds test * revert configtest.json changes * configtest updated * WIP: adds public endpoint support * WIP: adds public endpoint support * adds public endpoint support * WIP: adds auth. endpoint support * adds test for auth. endpoint * fixes * adds auth. endpoint support * WIP: ws support * WIP * WIP * WIP * WIP * WIP * WIP * WIP * Testing * Complete WS spot testing * adds support for ws events * minor change * WIP: adds REST support for CoinMarginedFutures * Fixes * WIP: adds REST support for CoinMarginedFutures * Fixes * improvement in SPOT REST * Typo fix * WIP: add REST support for CMF Account API * minor fixes * WIP: add support for CMF conditional orders and few minor fixes * complete support for CMF conditional orders * adds support for public CMF endpoint * adds support for CMF position API * Complete REST CMF support * WIP * Testing REST CMF support * Testing REST CMF support * Testing REST CMF support completed * WIP: add support for UMF * completed non-auth UMF * WIP: add support for REST Auth. UMF * WIP: add support for REST Auth. UMF and some improvements * WIP * WIP * WIP * completed REST UMF * renaming * adds REST support for futures * add testcases for UMF and some optimizations * add testcases for futures * Testing UMF, futures and its changes * Fixes * Fixes after testing * WIP * WIP * WIP * completed ws USDT futures support * WIP: ws support for futures * fixes in WS futures * fixes in WS support * roll back changes made for WS CMF, USDT and Futures * fixes * WIP * WIP * fixes * Steps for new PR * WIP * WIP * WIP * WIP * complete PR setup * fixes for successfully running tests * update in symbol for futures pair in test file * WIP * Fixes in test file and other minor fix * fix testdata/configtest.json * reset CONTRIBUTORS file * review changes * remove unwanted file * remove redundant code * improvisation * adds comment for exported functions * remove unwanted TODO and commented code * fix * improvisation * fix * defined errors * improvisation * improvisation * improvisation * updates test * adds comment for exported types * review changes * review changes * fix * fixes * Changes for making BYBIT compatible with existing code base * Test file changes * Changes for making BYBIT compatible with existing code base * Changes for making BYBIT compatible with existing code base * fix lint issues * fix * review changes * review changes * review changes * review changes * review changes * review changes * review changes * review changes * review changes * review changes * WIP * add test cases for new API's * minor improvements * add missing API and their tests * minor fixes * add bybitTime * add bybitTimeSec, bybitTimeMilliSec, bybitTimeNanoSec and necessary support * fix GetTradeHistory function * error handling * test fixes * add GetServerTime API * adds GetHistoricCandlesExtended and review changes * test fixes * minor fix * integrating CMF Bybit recent change log * minor fixes * adds extractCurrencyPair * minor fixes * minor fix * review changes * adds variable declaration of error * review commit * adds embeddable type in API response for all API and integrate it * fixes * adds authentication WS connection * review changes * review changes * compatible changes * adds asset to GetWithdrawalsHistory * adds asset_type in rpc.proto * adds asset argument in gctcli withdrawal request command * improve error handling in exchange API error * web socket fix * review changes * improvements * improvements * minor fix * review changes * fixing wrapper issues * fixes * fixes * review changes * add test cases * fix for GetActiveOrders * lint fixes * fixes in websocket * adds wrapper testcases * adds wrapper testcases * adds wrapper testcases * fixes * fix issue with GetHistoricCandlesExtended * fix merge issues * improving error reporting * adds wrapper testcases and a minor fix * gctrpc changes * adds test cases fixes in websocket * review changes for ws * review changes in WS * fix gctrpc * merge fixes * review changes * WIP * updates pair in configs * adds new asset USDCMarginedFutures * adds URL const for USDCMarginedFutures * adds API support * minor fixes * adds kline API * minor fix * adds API * adds API * adds API * WIP * WIP * WIP * adds support for USDC auth requests to SendAuthHTTPRequest * adds SendUSDCAuthHTTPRequest * run test and fix them * rollback support added for Auth. USDC request inside SendAuthHTTPRequest * adds API and test cases * adds API and test cases * adds APIs and test cases * adds APIs * adds rate limit for USDC * adds USDCMarginedFutures to wrapper * adds USDC testcases in wrapper and fix few issues * minor test fixes * minor test fixes * fix lint issues * WIP * Merge changes * minor fixes * remove "else" and optimize * review changes * review changes * review changes * fix lint issue * merge fix * fix test * fix templates and run them * changes after merge * review changes and improvements * code improvement * fixes with respect to changes in API response in documentation * fixed review change in test * adds check in CancelExistingOrder * update exchange template * review changes * adds GetDepositAddress API * WIP: adds GetOrderHistory * complete GetOrderHistory * fixes * adds test case * fixes and add WithdrawFund API * WIP * WIP * updating all SendAuthHTTPRequest call * adds WithdrawCryptocurrencyFunds * update test cases * fix lint issues * fixes after merge * adds GetAvailableTransferChains and few fixes * minor fix in GetDepositAddress * minor fix with WS ping/pong handling * add ping handler for WS Auth. * fix typo mistake * update doc
This commit is contained in:
committed by
GitHub
parent
67c512e92d
commit
247da918a8
@@ -236,7 +236,7 @@ func (a *Alphapoint) GetFundingHistory(ctx context.Context) ([]exchange.FundHist
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (a *Alphapoint) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (a *Alphapoint) GetWithdrawalsHistory(ctx context.Context, c currency.Code, as asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -33,9 +33,10 @@ const (
|
||||
DownsideProfitContract
|
||||
CoinMarginedFutures
|
||||
USDTMarginedFutures
|
||||
USDCMarginedFutures
|
||||
|
||||
futuresFlag = PerpetualContract | PerpetualSwap | Futures | UpsideProfitContract | DownsideProfitContract | CoinMarginedFutures | USDTMarginedFutures
|
||||
supportedFlag = Spot | Margin | MarginFunding | Index | Binary | PerpetualContract | PerpetualSwap | Futures | UpsideProfitContract | DownsideProfitContract | CoinMarginedFutures | USDTMarginedFutures
|
||||
futuresFlag = PerpetualContract | PerpetualSwap | Futures | UpsideProfitContract | DownsideProfitContract | CoinMarginedFutures | USDTMarginedFutures | USDCMarginedFutures
|
||||
supportedFlag = Spot | Margin | MarginFunding | Index | Binary | PerpetualContract | PerpetualSwap | Futures | UpsideProfitContract | DownsideProfitContract | CoinMarginedFutures | USDTMarginedFutures | USDCMarginedFutures
|
||||
|
||||
spot = "spot"
|
||||
margin = "margin"
|
||||
@@ -49,10 +50,11 @@ const (
|
||||
downsideProfitContract = "downsideprofitcontract"
|
||||
coinMarginedFutures = "coinmarginedfutures"
|
||||
usdtMarginedFutures = "usdtmarginedfutures"
|
||||
usdcMarginedFutures = "usdcmarginedfutures"
|
||||
)
|
||||
|
||||
var (
|
||||
supportedList = Items{Spot, Margin, MarginFunding, Index, Binary, PerpetualContract, PerpetualSwap, Futures, UpsideProfitContract, DownsideProfitContract, CoinMarginedFutures, USDTMarginedFutures}
|
||||
supportedList = Items{Spot, Margin, MarginFunding, Index, Binary, PerpetualContract, PerpetualSwap, Futures, UpsideProfitContract, DownsideProfitContract, CoinMarginedFutures, USDTMarginedFutures, USDCMarginedFutures}
|
||||
)
|
||||
|
||||
// Supported returns a list of supported asset types
|
||||
@@ -87,6 +89,8 @@ func (a Item) String() string {
|
||||
return coinMarginedFutures
|
||||
case USDTMarginedFutures:
|
||||
return usdtMarginedFutures
|
||||
case USDCMarginedFutures:
|
||||
return usdcMarginedFutures
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
@@ -180,6 +184,8 @@ func New(input string) (Item, error) {
|
||||
return CoinMarginedFutures, nil
|
||||
case usdtMarginedFutures:
|
||||
return USDTMarginedFutures, nil
|
||||
case usdcMarginedFutures:
|
||||
return USDCMarginedFutures, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("%w '%v', only supports %s",
|
||||
ErrNotSupported,
|
||||
|
||||
@@ -92,6 +92,7 @@ func TestNew(t *testing.T) {
|
||||
{Input: "DownsideProfitContract", Expected: DownsideProfitContract},
|
||||
{Input: "CoinMarginedFutures", Expected: CoinMarginedFutures},
|
||||
{Input: "USDTMarginedFutures", Expected: USDTMarginedFutures},
|
||||
{Input: "USDCMarginedFutures", Expected: USDCMarginedFutures},
|
||||
}
|
||||
|
||||
for x := range cases {
|
||||
@@ -177,6 +178,10 @@ func TestIsFutures(t *testing.T) {
|
||||
item: USDTMarginedFutures,
|
||||
isFutures: true,
|
||||
},
|
||||
{
|
||||
item: USDCMarginedFutures,
|
||||
isFutures: true,
|
||||
},
|
||||
}
|
||||
for _, s := range scenarios {
|
||||
testScenario := s
|
||||
|
||||
@@ -2073,7 +2073,7 @@ func TestWithdrawHistory(t *testing.T) {
|
||||
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
_, err := b.GetWithdrawalsHistory(context.Background(), currency.ETH)
|
||||
_, err := b.GetWithdrawalsHistory(context.Background(), currency.ETH, asset.Spot)
|
||||
switch {
|
||||
case areTestAPIKeysSet() && err != nil:
|
||||
t.Error("GetWithdrawalsHistory() error", err)
|
||||
|
||||
@@ -818,7 +818,7 @@ func (b *Binance) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (b *Binance) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (b *Binance) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
w, err := b.WithdrawHistory(ctx, c, "", time.Time{}, time.Time{}, 0, 10000)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -551,7 +551,7 @@ func (b *Bitfinex) GetFundingHistory(ctx context.Context) ([]exchange.FundHistor
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (b *Bitfinex) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (b *Bitfinex) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -338,7 +338,7 @@ func (b *Bitflyer) GetFundingHistory(_ context.Context) ([]exchange.FundHistory,
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (b *Bitflyer) GetWithdrawalsHistory(_ context.Context, _ currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (b *Bitflyer) GetWithdrawalsHistory(_ context.Context, _ currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -426,7 +426,7 @@ func (b *Bithumb) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (b *Bithumb) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (b *Bithumb) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -488,7 +488,7 @@ func (b *Bitmex) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory,
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (b *Bitmex) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (b *Bitmex) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -484,7 +484,7 @@ func (b *Bitstamp) GetFundingHistory(ctx context.Context) ([]exchange.FundHistor
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (b *Bitstamp) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (b *Bitstamp) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -506,7 +506,7 @@ func (b *Bittrex) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (b *Bittrex) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (b *Bittrex) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -490,7 +490,7 @@ func (b *BTCMarkets) GetFundingHistory(ctx context.Context) ([]exchange.FundHist
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (b *BTCMarkets) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (b *BTCMarkets) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -459,7 +459,7 @@ func (b *BTSE) withinLimits(pair currency.Pair, amount float64) bool {
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (b *BTSE) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (b *BTSE) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
140
exchanges/bybit/README.md
Normal file
140
exchanges/bybit/README.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# GoCryptoTrader package Bybit
|
||||
|
||||
<img src="/common/gctlogo.png?raw=true" width="350px" height="350px" hspace="70">
|
||||
|
||||
|
||||
[](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml)
|
||||
[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
|
||||
[](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/bybit)
|
||||
[](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
|
||||
[](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
|
||||
|
||||
|
||||
This bybit package is part of the GoCryptoTrader codebase.
|
||||
|
||||
## This is still in active development
|
||||
|
||||
You can track ideas, planned features and what's in progress on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader).
|
||||
|
||||
Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTc5ZDE1ZTNiOGM3ZGMyMmY1NTAxYWZhODE0MWM5N2JlZDk1NDU0YTViYzk4NTk3OTRiMDQzNGQ1YTc4YmRlMTk)
|
||||
|
||||
## Bybit Exchange
|
||||
|
||||
### Current Features
|
||||
|
||||
+ REST Support
|
||||
+ Websocket Support
|
||||
|
||||
### How to enable
|
||||
|
||||
+ [Enable via configuration](https://github.com/thrasher-corp/gocryptotrader/tree/master/config#enable-exchange-via-config-example)
|
||||
|
||||
+ Individual package example below:
|
||||
|
||||
```go
|
||||
// Exchanges will be abstracted out in further updates and examples will be
|
||||
// supplied then
|
||||
```
|
||||
|
||||
### How to do REST public/private calls
|
||||
|
||||
+ If enabled via "configuration".json file the exchange will be added to the
|
||||
IBotExchange array in the ```go var bot Bot``` and you will only be able to use
|
||||
the wrapper interface functions for accessing exchange data. View routines.go
|
||||
for an example of integration usage with GoCryptoTrader. Rudimentary example
|
||||
below:
|
||||
|
||||
main.go
|
||||
```go
|
||||
var b exchange.IBotExchange
|
||||
|
||||
for i := range bot.Exchanges {
|
||||
if bot.Exchanges[i].GetName() == "Bybit" {
|
||||
b = bot.Exchanges[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Public calls - wrapper functions
|
||||
|
||||
// Fetches current ticker information
|
||||
tick, err := b.FetchTicker()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Fetches current orderbook information
|
||||
ob, err := b.FetchOrderbook()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Private calls - wrapper functions - make sure your APIKEY and APISECRET are
|
||||
// set and AuthenticatedAPISupport is set to true
|
||||
|
||||
// Fetches current account information
|
||||
accountInfo, err := b.GetAccountInfo()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
```
|
||||
|
||||
+ If enabled via individually importing package, rudimentary example below:
|
||||
|
||||
```go
|
||||
// Public calls
|
||||
|
||||
// Fetches current ticker information
|
||||
ticker, err := b.GetTicker()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Fetches current orderbook information
|
||||
ob, err := b.GetOrderBook()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Private calls - make sure your APIKEY and APISECRET are set and
|
||||
// AuthenticatedAPISupport is set to true
|
||||
|
||||
// GetUserInfo returns account info
|
||||
accountInfo, err := b.GetUserInfo(...)
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Submits an order and the exchange and returns its tradeID
|
||||
tradeID, err := b.Trade(...)
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
```
|
||||
|
||||
### How to do Websocket public/private calls
|
||||
|
||||
```go
|
||||
// Exchanges will be abstracted out in further updates and examples will be
|
||||
// supplied then
|
||||
```
|
||||
|
||||
### Please click GoDocs chevron above to view current GoDoc information for this package
|
||||
|
||||
## Contribution
|
||||
|
||||
Please feel free to submit any pull requests or suggest any desired features to be added.
|
||||
|
||||
When submitting a PR, please abide by our coding guidelines:
|
||||
|
||||
+ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
|
||||
+ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
|
||||
+ Code must adhere to our [coding style](https://github.com/thrasher-corp/gocryptotrader/blob/master/doc/coding_style.md).
|
||||
+ Pull requests need to be based on and opened against the `master` branch.
|
||||
|
||||
## Donations
|
||||
|
||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
||||
|
||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||
|
||||
***bc1qk0jareu4jytc0cfrhr5wgshsq8282awpavfahc***
|
||||
998
exchanges/bybit/bybit.go
Normal file
998
exchanges/bybit/bybit.go
Normal file
@@ -0,0 +1,998 @@
|
||||
package bybit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/crypto"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
)
|
||||
|
||||
// Bybit is the overarching type across this package
|
||||
type Bybit struct {
|
||||
exchange.Base
|
||||
}
|
||||
|
||||
const (
|
||||
bybitAPIURL = "https://api.bybit.com"
|
||||
defaultRecvWindow = "5000" // 5000 milli second
|
||||
|
||||
sideBuy = "Buy"
|
||||
sideSell = "Sell"
|
||||
|
||||
// Public endpoints
|
||||
bybitSpotGetSymbols = "/spot/v1/symbols"
|
||||
bybitOrderBook = "/spot/quote/v1/depth"
|
||||
bybitMergedOrderBook = "/spot/quote/v1/depth/merged"
|
||||
bybitRecentTrades = "/spot/quote/v1/trades"
|
||||
bybitCandlestickChart = "/spot/quote/v1/kline"
|
||||
bybit24HrsChange = "/spot/quote/v1/ticker/24hr"
|
||||
bybitLastTradedPrice = "/spot/quote/v1/ticker/price"
|
||||
bybitBestBidAskPrice = "/spot/quote/v1/ticker/book_ticker"
|
||||
|
||||
// Authenticated endpoints
|
||||
bybitSpotOrder = "/spot/v1/order" // create, query, cancel
|
||||
bybitFastCancelSpotOrder = "/spot/v1/order/fast"
|
||||
bybitBatchCancelSpotOrder = "/spot/order/batch-cancel"
|
||||
bybitFastBatchCancelSpotOrder = "/spot/order/batch-fast-cancel"
|
||||
bybitOpenOrder = "/spot/v1/open-orders"
|
||||
bybitPastOrder = "/spot/v1/history-orders"
|
||||
bybitTradeHistory = "/spot/v1/myTrades"
|
||||
bybitWalletBalance = "/spot/v1/account"
|
||||
bybitServerTime = "/spot/v1/time"
|
||||
|
||||
// Account asset endpoint
|
||||
bybitGetDepositAddress = "/asset/v1/private/deposit/address"
|
||||
bybitWithdrawFund = "/asset/v1/private/withdraw"
|
||||
)
|
||||
|
||||
// GetAllSpotPairs gets all pairs on the exchange
|
||||
func (by *Bybit) GetAllSpotPairs(ctx context.Context) ([]PairData, error) {
|
||||
resp := struct {
|
||||
Data []PairData `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
return resp.Data, by.SendHTTPRequest(ctx, exchange.RestSpot, bybitSpotGetSymbols, publicSpotRate, &resp)
|
||||
}
|
||||
|
||||
func processOB(ob [][2]string) ([]orderbook.Item, error) {
|
||||
o := make([]orderbook.Item, len(ob))
|
||||
for x := range ob {
|
||||
var price, amount float64
|
||||
amount, err := strconv.ParseFloat(ob[x][1], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
price, err = strconv.ParseFloat(ob[x][0], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
o[x] = orderbook.Item{
|
||||
Price: price,
|
||||
Amount: amount,
|
||||
}
|
||||
}
|
||||
return o, nil
|
||||
}
|
||||
|
||||
func constructOrderbook(o *orderbookResponse) (*Orderbook, error) {
|
||||
var (
|
||||
s Orderbook
|
||||
err error
|
||||
)
|
||||
s.Bids, err = processOB(o.Data.Bids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Asks, err = processOB(o.Data.Asks)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Time = o.Data.Time.Time()
|
||||
return &s, err
|
||||
}
|
||||
|
||||
// GetOrderBook gets orderbook for a given market with a given depth (default depth 100)
|
||||
func (by *Bybit) GetOrderBook(ctx context.Context, symbol string, depth int64) (*Orderbook, error) {
|
||||
var o orderbookResponse
|
||||
strDepth := "100" // default depth
|
||||
if depth > 0 && depth < 100 {
|
||||
strDepth = strconv.FormatInt(depth, 10)
|
||||
}
|
||||
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
params.Set("limit", strDepth)
|
||||
path := common.EncodeURLValues(bybitOrderBook, params)
|
||||
err := by.SendHTTPRequest(ctx, exchange.RestSpot, path, publicSpotRate, &o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return constructOrderbook(&o)
|
||||
}
|
||||
|
||||
// GetMergedOrderBook gets orderbook for a given market with a given depth (default depth 100)
|
||||
func (by *Bybit) GetMergedOrderBook(ctx context.Context, symbol string, scale, depth int64) (*Orderbook, error) {
|
||||
var o orderbookResponse
|
||||
params := url.Values{}
|
||||
if scale > 0 {
|
||||
params.Set("scale", strconv.FormatInt(scale, 10))
|
||||
}
|
||||
|
||||
strDepth := "100" // default depth
|
||||
if depth > 0 && depth <= 200 {
|
||||
strDepth = strconv.FormatInt(depth, 10)
|
||||
}
|
||||
|
||||
params.Set("symbol", symbol)
|
||||
params.Set("limit", strDepth)
|
||||
path := common.EncodeURLValues(bybitMergedOrderBook, params)
|
||||
err := by.SendHTTPRequest(ctx, exchange.RestSpot, path, publicSpotRate, &o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return constructOrderbook(&o)
|
||||
}
|
||||
|
||||
// GetTrades gets recent trades from the exchange
|
||||
func (by *Bybit) GetTrades(ctx context.Context, symbol string, limit int64) ([]TradeItem, error) {
|
||||
resp := struct {
|
||||
Data []struct {
|
||||
Price float64 `json:"price,string"`
|
||||
Time bybitTimeMilliSec `json:"time"`
|
||||
Quantity float64 `json:"qty,string"`
|
||||
IsBuyerMaker bool `json:"isBuyerMaker"`
|
||||
} `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
|
||||
strLimit := "60" // default limit
|
||||
if limit > 0 && limit < 60 {
|
||||
strLimit = strconv.FormatInt(limit, 10)
|
||||
}
|
||||
params.Set("limit", strLimit)
|
||||
path := common.EncodeURLValues(bybitRecentTrades, params)
|
||||
err := by.SendHTTPRequest(ctx, exchange.RestSpot, path, publicSpotRate, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
trades := make([]TradeItem, len(resp.Data))
|
||||
for x := range resp.Data {
|
||||
var tradeSide string
|
||||
if resp.Data[x].IsBuyerMaker {
|
||||
tradeSide = order.Buy.String()
|
||||
} else {
|
||||
tradeSide = order.Sell.String()
|
||||
}
|
||||
|
||||
trades[x] = TradeItem{
|
||||
CurrencyPair: symbol,
|
||||
Price: resp.Data[x].Price,
|
||||
Side: tradeSide,
|
||||
Volume: resp.Data[x].Quantity,
|
||||
Time: resp.Data[x].Time.Time(),
|
||||
}
|
||||
}
|
||||
return trades, nil
|
||||
}
|
||||
|
||||
// GetKlines data returns the kline data for a specific symbol. Limitation: It only returns latest 3500 candles irrespective of interval passed
|
||||
func (by *Bybit) GetKlines(ctx context.Context, symbol, period string, limit int64, start, end time.Time) ([]KlineItem, error) {
|
||||
resp := struct {
|
||||
Data [][]interface{} `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
|
||||
v := url.Values{}
|
||||
v.Add("symbol", symbol)
|
||||
v.Add("interval", period)
|
||||
if !start.IsZero() {
|
||||
v.Add("startTime", strconv.FormatInt(start.UnixMilli(), 10))
|
||||
}
|
||||
if !end.IsZero() {
|
||||
v.Add("endTime", strconv.FormatInt(end.UnixMilli(), 10))
|
||||
}
|
||||
if limit <= 0 || limit > 1000 {
|
||||
limit = 1000
|
||||
}
|
||||
v.Add("limit", strconv.FormatInt(limit, 10))
|
||||
|
||||
path := common.EncodeURLValues(bybitCandlestickChart, v)
|
||||
if err := by.SendHTTPRequest(ctx, exchange.RestSpot, path, publicSpotRate, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
klines := make([]KlineItem, len(resp.Data))
|
||||
for x := range resp.Data {
|
||||
if len(resp.Data[x]) != 11 {
|
||||
return klines, fmt.Errorf("%v GetKlines: invalid response, array length not as expected, check api docs for updates", by.Name)
|
||||
}
|
||||
var kline KlineItem
|
||||
var err error
|
||||
startTime, ok := resp.Data[x][0].(float64)
|
||||
if !ok {
|
||||
return klines, fmt.Errorf("%v GetKlines: %w for StartTime", by.Name, errTypeAssert)
|
||||
}
|
||||
kline.StartTime = time.UnixMilli(int64(startTime))
|
||||
|
||||
open, ok := resp.Data[x][1].(string)
|
||||
if !ok {
|
||||
return klines, fmt.Errorf("%v GetKlines: %w for Open", by.Name, errTypeAssert)
|
||||
}
|
||||
kline.Open, err = strconv.ParseFloat(open, 64)
|
||||
if err != nil {
|
||||
return klines, fmt.Errorf("%v GetKlines: %w for Open", by.Name, errStrParsing)
|
||||
}
|
||||
|
||||
high, ok := resp.Data[x][2].(string)
|
||||
if !ok {
|
||||
return klines, fmt.Errorf("%v GetKlines: %w for High", by.Name, errTypeAssert)
|
||||
}
|
||||
kline.High, err = strconv.ParseFloat(high, 64)
|
||||
if err != nil {
|
||||
return klines, fmt.Errorf("%v GetKlines: %w for High", by.Name, errStrParsing)
|
||||
}
|
||||
|
||||
low, ok := resp.Data[x][3].(string)
|
||||
if !ok {
|
||||
return klines, fmt.Errorf("%v GetKlines: %w for Low", by.Name, errTypeAssert)
|
||||
}
|
||||
kline.Low, err = strconv.ParseFloat(low, 64)
|
||||
if err != nil {
|
||||
return klines, fmt.Errorf("%v GetKlines: %w for Low", by.Name, errStrParsing)
|
||||
}
|
||||
|
||||
c, ok := resp.Data[x][4].(string)
|
||||
if !ok {
|
||||
return klines, fmt.Errorf("%v GetKlines: %w for Close", by.Name, errTypeAssert)
|
||||
}
|
||||
kline.Close, err = strconv.ParseFloat(c, 64)
|
||||
if err != nil {
|
||||
return klines, fmt.Errorf("%v GetKlines: %w for Close", by.Name, errStrParsing)
|
||||
}
|
||||
|
||||
volume, ok := resp.Data[x][5].(string)
|
||||
if !ok {
|
||||
return klines, fmt.Errorf("%v GetKlines: %w for Volume", by.Name, errTypeAssert)
|
||||
}
|
||||
kline.Volume, err = strconv.ParseFloat(volume, 64)
|
||||
if err != nil {
|
||||
return klines, fmt.Errorf("%v GetKlines: %w for Volume", by.Name, errStrParsing)
|
||||
}
|
||||
|
||||
endTime, ok := resp.Data[x][6].(float64)
|
||||
if !ok {
|
||||
return klines, fmt.Errorf("%v GetKlines: %w for EndTime", by.Name, errTypeAssert)
|
||||
}
|
||||
kline.EndTime = time.UnixMilli(int64(endTime))
|
||||
quoteAssetVolume, ok := resp.Data[x][7].(string)
|
||||
if !ok {
|
||||
return klines, fmt.Errorf("%v GetKlines: %w for QuoteAssetVolume", by.Name, errTypeAssert)
|
||||
}
|
||||
kline.QuoteAssetVolume, err = strconv.ParseFloat(quoteAssetVolume, 64)
|
||||
if err != nil {
|
||||
return klines, fmt.Errorf("%v GetKlines: %w for QuoteAssetVolume", by.Name, errStrParsing)
|
||||
}
|
||||
|
||||
tradesCount, ok := resp.Data[x][8].(float64)
|
||||
if !ok {
|
||||
return klines, fmt.Errorf("%v GetKlines: %w for TradesCount", by.Name, errTypeAssert)
|
||||
}
|
||||
kline.TradesCount = int64(tradesCount)
|
||||
|
||||
takerBaseVolume, ok := resp.Data[x][9].(string)
|
||||
if !ok {
|
||||
return klines, fmt.Errorf("%v GetKlines: %w for TakerBaseVolume", by.Name, errTypeAssert)
|
||||
}
|
||||
kline.TakerBaseVolume, err = strconv.ParseFloat(takerBaseVolume, 64)
|
||||
if err != nil {
|
||||
return klines, fmt.Errorf("%v GetKlines: %w for TakerBaseVolume", by.Name, errStrParsing)
|
||||
}
|
||||
|
||||
takerQuoteVolume, ok := resp.Data[x][10].(string)
|
||||
if !ok {
|
||||
return klines, fmt.Errorf("%v GetKlines: %w for TakerQuoteVolume", by.Name, errTypeAssert)
|
||||
}
|
||||
kline.TakerQuoteVolume, err = strconv.ParseFloat(takerQuoteVolume, 64)
|
||||
if err != nil {
|
||||
return klines, fmt.Errorf("%v GetKlines: %w for TakerQuoteVolume", by.Name, errStrParsing)
|
||||
}
|
||||
|
||||
klines[x] = kline
|
||||
}
|
||||
return klines, nil
|
||||
}
|
||||
|
||||
// Get24HrsChange returns price change statistics for the last 24 hours
|
||||
// If symbol not passed then it will return price change statistics for all pairs
|
||||
func (by *Bybit) Get24HrsChange(ctx context.Context, symbol string) ([]PriceChangeStats, error) {
|
||||
type priceChangeStats struct {
|
||||
Time bybitTimeMilliSec `json:"time"`
|
||||
Symbol string `json:"symbol"`
|
||||
BestBidPrice float64 `json:"bestBidPrice,string"`
|
||||
BestAskPrice float64 `json:"bestAskPrice,string"`
|
||||
LastPrice float64 `json:"lastPrice,string"`
|
||||
OpenPrice float64 `json:"openPrice,string"`
|
||||
HighPrice float64 `json:"highPrice,string"`
|
||||
LowPrice float64 `json:"lowPrice,string"`
|
||||
Volume float64 `json:"volume,string"`
|
||||
QuoteVolume float64 `json:"quoteVolume,string"`
|
||||
}
|
||||
|
||||
var stats []PriceChangeStats
|
||||
if symbol != "" {
|
||||
resp := struct {
|
||||
Data priceChangeStats `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
path := common.EncodeURLValues(bybit24HrsChange, params)
|
||||
err := by.SendHTTPRequest(ctx, exchange.RestSpot, path, publicSpotRate, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stats = append(stats, PriceChangeStats{
|
||||
resp.Data.Time.Time(),
|
||||
resp.Data.Symbol,
|
||||
resp.Data.BestAskPrice,
|
||||
resp.Data.BestAskPrice,
|
||||
resp.Data.LastPrice,
|
||||
resp.Data.OpenPrice,
|
||||
resp.Data.HighPrice,
|
||||
resp.Data.LowPrice,
|
||||
resp.Data.Volume,
|
||||
resp.Data.QuoteVolume,
|
||||
})
|
||||
} else {
|
||||
resp := struct {
|
||||
Data []priceChangeStats `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
|
||||
err := by.SendHTTPRequest(ctx, exchange.RestSpot, bybit24HrsChange, publicSpotRate, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for x := range resp.Data {
|
||||
stats = append(stats, PriceChangeStats{
|
||||
resp.Data[x].Time.Time(),
|
||||
resp.Data[x].Symbol,
|
||||
resp.Data[x].BestAskPrice,
|
||||
resp.Data[x].BestAskPrice,
|
||||
resp.Data[x].LastPrice,
|
||||
resp.Data[x].OpenPrice,
|
||||
resp.Data[x].HighPrice,
|
||||
resp.Data[x].LowPrice,
|
||||
resp.Data[x].Volume,
|
||||
resp.Data[x].QuoteVolume,
|
||||
})
|
||||
}
|
||||
}
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// GetLastTradedPrice returns last trading price
|
||||
// If symbol not passed then it will return last trading price for all pairs
|
||||
func (by *Bybit) GetLastTradedPrice(ctx context.Context, symbol string) ([]LastTradePrice, error) {
|
||||
var lastTradePrices []LastTradePrice
|
||||
if symbol != "" {
|
||||
resp := struct {
|
||||
Data LastTradePrice `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
path := common.EncodeURLValues(bybitLastTradedPrice, params)
|
||||
err := by.SendHTTPRequest(ctx, exchange.RestSpot, path, publicSpotRate, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lastTradePrices = append(lastTradePrices, LastTradePrice{
|
||||
resp.Data.Symbol,
|
||||
resp.Data.Price,
|
||||
})
|
||||
} else {
|
||||
resp := struct {
|
||||
Data []LastTradePrice `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
|
||||
err := by.SendHTTPRequest(ctx, exchange.RestSpot, bybitLastTradedPrice, publicSpotRate, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for x := range resp.Data {
|
||||
lastTradePrices = append(lastTradePrices, LastTradePrice{
|
||||
resp.Data[x].Symbol,
|
||||
resp.Data[x].Price,
|
||||
})
|
||||
}
|
||||
}
|
||||
return lastTradePrices, nil
|
||||
}
|
||||
|
||||
// GetBestBidAskPrice returns best BID and ASK price
|
||||
// If symbol not passed then it will return best BID and ASK price for all pairs
|
||||
func (by *Bybit) GetBestBidAskPrice(ctx context.Context, symbol string) ([]TickerData, error) {
|
||||
type bestTicker struct {
|
||||
Symbol string `json:"symbol"`
|
||||
BidPrice float64 `json:"bidPrice,string"`
|
||||
BidQuantity float64 `json:"bidQty,string"`
|
||||
AskPrice float64 `json:"askPrice,string"`
|
||||
AskQuantity float64 `json:"askQty,string"`
|
||||
Time bybitTimeMilliSec `json:"time"`
|
||||
}
|
||||
|
||||
var tickers []TickerData
|
||||
if symbol != "" {
|
||||
resp := struct {
|
||||
Data bestTicker `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
|
||||
params := url.Values{}
|
||||
params.Set("symbol", symbol)
|
||||
path := common.EncodeURLValues(bybitBestBidAskPrice, params)
|
||||
err := by.SendHTTPRequest(ctx, exchange.RestSpot, path, publicSpotRate, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tickers = append(tickers, TickerData{
|
||||
resp.Data.Symbol,
|
||||
resp.Data.BidPrice,
|
||||
resp.Data.BidQuantity,
|
||||
resp.Data.AskPrice,
|
||||
resp.Data.AskQuantity,
|
||||
resp.Data.Time.Time(),
|
||||
})
|
||||
} else {
|
||||
resp := struct {
|
||||
Data []bestTicker `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
|
||||
err := by.SendHTTPRequest(ctx, exchange.RestSpot, bybitBestBidAskPrice, publicSpotRate, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for x := range resp.Data {
|
||||
tickers = append(tickers, TickerData{
|
||||
resp.Data[x].Symbol,
|
||||
resp.Data[x].BidPrice,
|
||||
resp.Data[x].BidQuantity,
|
||||
resp.Data[x].AskPrice,
|
||||
resp.Data[x].AskQuantity,
|
||||
resp.Data[x].Time.Time(),
|
||||
})
|
||||
}
|
||||
}
|
||||
return tickers, nil
|
||||
}
|
||||
|
||||
// CreatePostOrder create and post order
|
||||
func (by *Bybit) CreatePostOrder(ctx context.Context, o *PlaceOrderRequest) (*PlaceOrderResponse, error) {
|
||||
if o == nil {
|
||||
return nil, errInvalidOrderRequest
|
||||
}
|
||||
|
||||
params := url.Values{}
|
||||
params.Set("symbol", o.Symbol)
|
||||
params.Set("qty", strconv.FormatFloat(o.Quantity, 'f', -1, 64))
|
||||
params.Set("side", o.Side)
|
||||
params.Set("type", o.TradeType)
|
||||
|
||||
if o.TimeInForce != "" {
|
||||
params.Set("timeInForce", o.TimeInForce)
|
||||
}
|
||||
if (o.TradeType == BybitRequestParamsOrderLimit || o.TradeType == BybitRequestParamsOrderLimitMaker) && o.Price == 0 {
|
||||
return nil, errMissingPrice
|
||||
}
|
||||
if o.Price != 0 {
|
||||
params.Set("price", strconv.FormatFloat(o.Price, 'f', -1, 64))
|
||||
}
|
||||
if o.OrderLinkID != "" {
|
||||
params.Set("orderLinkId", o.OrderLinkID)
|
||||
}
|
||||
|
||||
resp := struct {
|
||||
Data PlaceOrderResponse `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
return &resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, bybitSpotOrder, params, nil, &resp, privateSpotRate)
|
||||
}
|
||||
|
||||
// QueryOrder returns order data based upon orderID or orderLinkID
|
||||
func (by *Bybit) QueryOrder(ctx context.Context, orderID, orderLinkID string) (*QueryOrderResponse, error) {
|
||||
if orderID == "" && orderLinkID == "" {
|
||||
return nil, errOrderOrOrderLinkIDMissing
|
||||
}
|
||||
|
||||
params := url.Values{}
|
||||
if orderID != "" {
|
||||
params.Set("orderId", orderID)
|
||||
}
|
||||
if orderLinkID != "" {
|
||||
params.Set("orderLinkId", orderLinkID)
|
||||
}
|
||||
|
||||
resp := struct {
|
||||
Data QueryOrderResponse `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
return &resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, bybitSpotOrder, params, nil, &resp, privateSpotRate)
|
||||
}
|
||||
|
||||
// CancelExistingOrder cancels existing order based upon orderID or orderLinkID
|
||||
func (by *Bybit) CancelExistingOrder(ctx context.Context, orderID, orderLinkID string) (*CancelOrderResponse, error) {
|
||||
if orderID == "" && orderLinkID == "" {
|
||||
return nil, errOrderOrOrderLinkIDMissing
|
||||
}
|
||||
|
||||
params := url.Values{}
|
||||
if orderID != "" {
|
||||
params.Set("orderId", orderID)
|
||||
}
|
||||
if orderLinkID != "" {
|
||||
params.Set("orderLinkId", orderLinkID)
|
||||
}
|
||||
|
||||
resp := struct {
|
||||
Data CancelOrderResponse `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
err := by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, bybitSpotOrder, params, nil, &resp, privateSpotRate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// In case open order is cancelled, this endpoint return status as NEW whereas if we try to cancel a already cancelled order then it's status is returned as CANCELED without any error. So this check is added to prevent this obscurity.
|
||||
if resp.Data.Status == "CANCELED" {
|
||||
return nil, fmt.Errorf("%s order already cancelled", resp.Data.OrderID)
|
||||
}
|
||||
return &resp.Data, nil
|
||||
}
|
||||
|
||||
// FastCancelExistingOrder cancels existing order based upon orderID or orderLinkID
|
||||
func (by *Bybit) FastCancelExistingOrder(ctx context.Context, symbol, orderID, orderLinkID string) (bool, error) {
|
||||
resp := struct {
|
||||
Data struct {
|
||||
IsCancelled bool `json:"isCancelled"`
|
||||
} `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
|
||||
if orderID == "" && orderLinkID == "" {
|
||||
return resp.Data.IsCancelled, errOrderOrOrderLinkIDMissing
|
||||
}
|
||||
|
||||
params := url.Values{}
|
||||
if symbol == "" {
|
||||
return resp.Data.IsCancelled, errSymbolMissing
|
||||
}
|
||||
params.Set("symbolId", symbol)
|
||||
|
||||
if orderID != "" {
|
||||
params.Set("orderId", orderID)
|
||||
}
|
||||
if orderLinkID != "" {
|
||||
params.Set("orderLinkId", orderLinkID)
|
||||
}
|
||||
|
||||
return resp.Data.IsCancelled, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, bybitFastCancelSpotOrder, params, nil, &resp, privateSpotRate)
|
||||
}
|
||||
|
||||
// BatchCancelOrder cancels orders in batch based upon symbol, side or orderType
|
||||
func (by *Bybit) BatchCancelOrder(ctx context.Context, symbol, side, orderTypes string) (bool, error) {
|
||||
params := url.Values{}
|
||||
if symbol != "" {
|
||||
params.Set("symbol", symbol)
|
||||
}
|
||||
if side != "" {
|
||||
params.Set("side", side)
|
||||
}
|
||||
if orderTypes != "" {
|
||||
params.Set("orderTypes", orderTypes)
|
||||
}
|
||||
|
||||
resp := struct {
|
||||
Result struct {
|
||||
Success bool `json:"success"`
|
||||
} `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
return resp.Result.Success, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, bybitBatchCancelSpotOrder, params, nil, &resp, privateSpotRate)
|
||||
}
|
||||
|
||||
// BatchFastCancelOrder cancels orders in batch based upon symbol, side or orderType
|
||||
func (by *Bybit) BatchFastCancelOrder(ctx context.Context, symbol, side, orderTypes string) (bool, error) {
|
||||
params := url.Values{}
|
||||
if symbol != "" {
|
||||
params.Set("symbol", symbol)
|
||||
}
|
||||
if side != "" {
|
||||
params.Set("side", side)
|
||||
}
|
||||
if orderTypes != "" {
|
||||
params.Set("orderTypes", orderTypes)
|
||||
}
|
||||
|
||||
resp := struct {
|
||||
Result struct {
|
||||
Success bool `json:"success"`
|
||||
} `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
return resp.Result.Success, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, bybitFastBatchCancelSpotOrder, params, nil, &resp, privateSpotRate)
|
||||
}
|
||||
|
||||
// BatchCancelOrderByIDs cancels orders in batch based on comma separated order id's
|
||||
func (by *Bybit) BatchCancelOrderByIDs(ctx context.Context, orderIDs []string) (bool, error) {
|
||||
params := url.Values{}
|
||||
if len(orderIDs) == 0 {
|
||||
return false, errEmptyOrderIDs
|
||||
}
|
||||
params.Set("orderIds", strings.Join(orderIDs, ","))
|
||||
|
||||
resp := struct {
|
||||
Result struct {
|
||||
Success bool `json:"success"`
|
||||
} `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
return resp.Result.Success, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, bybitFastBatchCancelSpotOrder, params, nil, &resp, privateSpotRate)
|
||||
}
|
||||
|
||||
// ListOpenOrders returns all open orders
|
||||
func (by *Bybit) ListOpenOrders(ctx context.Context, symbol, orderID string, limit int64) ([]QueryOrderResponse, error) {
|
||||
params := url.Values{}
|
||||
if symbol != "" {
|
||||
params.Set("symbol", symbol)
|
||||
}
|
||||
if orderID != "" {
|
||||
params.Set("orderId", orderID)
|
||||
}
|
||||
if limit != 0 {
|
||||
params.Set("limit", strconv.FormatInt(limit, 10))
|
||||
}
|
||||
|
||||
resp := struct {
|
||||
Data []QueryOrderResponse `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, bybitOpenOrder, params, nil, &resp, privateSpotRate)
|
||||
}
|
||||
|
||||
// GetPastOrders returns all past orders from history
|
||||
func (by *Bybit) GetPastOrders(ctx context.Context, symbol, orderID string, limit int64, startTime, endTime time.Time) ([]QueryOrderResponse, error) {
|
||||
params := url.Values{}
|
||||
if symbol != "" {
|
||||
params.Set("symbol", symbol)
|
||||
}
|
||||
if orderID != "" {
|
||||
params.Set("orderId", orderID)
|
||||
}
|
||||
if limit != 0 {
|
||||
params.Set("limit", strconv.FormatInt(limit, 10))
|
||||
}
|
||||
if !startTime.IsZero() {
|
||||
params.Set("startTime", strconv.FormatInt(startTime.UnixMilli(), 10))
|
||||
}
|
||||
if !endTime.IsZero() {
|
||||
params.Set("endTime", strconv.FormatInt(endTime.UnixMilli(), 10))
|
||||
}
|
||||
resp := struct {
|
||||
Data []QueryOrderResponse `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, bybitPastOrder, params, nil, &resp, privateSpotRate)
|
||||
}
|
||||
|
||||
// GetTradeHistory returns user trades
|
||||
func (by *Bybit) GetTradeHistory(ctx context.Context, limit int64, symbol, fromID, toID, orderID string, startTime, endTime time.Time) ([]HistoricalTrade, error) {
|
||||
params := url.Values{}
|
||||
if symbol != "" {
|
||||
params.Set("symbol", symbol)
|
||||
}
|
||||
if limit != 0 {
|
||||
params.Set("limit", strconv.FormatInt(limit, 10))
|
||||
}
|
||||
if fromID != "" {
|
||||
params.Set("fromTicketId", fromID)
|
||||
}
|
||||
if toID != "" {
|
||||
params.Set("toTicketId", toID)
|
||||
}
|
||||
if orderID != "" {
|
||||
params.Set("orderId", orderID)
|
||||
}
|
||||
if !startTime.IsZero() {
|
||||
params.Set("startTime", strconv.FormatInt(startTime.UnixMilli(), 10))
|
||||
}
|
||||
if !endTime.IsZero() {
|
||||
params.Set("endTime", strconv.FormatInt(endTime.UnixMilli(), 10))
|
||||
}
|
||||
resp := struct {
|
||||
Data []HistoricalTrade `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, bybitTradeHistory, params, nil, &resp, privateSpotRate)
|
||||
}
|
||||
|
||||
// GetWalletBalance returns user wallet balance
|
||||
func (by *Bybit) GetWalletBalance(ctx context.Context) ([]Balance, error) {
|
||||
resp := struct {
|
||||
Data struct {
|
||||
Balances []Balance `json:"balances"`
|
||||
} `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
return resp.Data.Balances, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, bybitWalletBalance, url.Values{}, nil, &resp, privateSpotRate)
|
||||
}
|
||||
|
||||
// GetSpotServerTime returns server time
|
||||
func (by *Bybit) GetSpotServerTime(ctx context.Context) (time.Time, error) {
|
||||
resp := struct {
|
||||
Result struct {
|
||||
ServerTime int64 `json:"serverTime"`
|
||||
} `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
err := by.SendHTTPRequest(ctx, exchange.RestSpot, bybitServerTime, publicSpotRate, &resp)
|
||||
return time.UnixMilli(resp.Result.ServerTime), err
|
||||
}
|
||||
|
||||
// GetDepositAddressForCurrency returns deposit wallet address based upon the coin.
|
||||
func (by *Bybit) GetDepositAddressForCurrency(ctx context.Context, coin string) (DepositWalletInfo, error) {
|
||||
resp := struct {
|
||||
Result DepositWalletInfo `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
|
||||
params := url.Values{}
|
||||
if coin == "" {
|
||||
return resp.Result, errInvalidCoin
|
||||
}
|
||||
params.Set("coin", strings.ToUpper(coin))
|
||||
return resp.Result, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, bybitGetDepositAddress, params, nil, &resp, publicSpotRate)
|
||||
}
|
||||
|
||||
// WithdrawFund creates request for fund withdrawal.
|
||||
func (by *Bybit) WithdrawFund(ctx context.Context, coin, chain, address, tag, amount string) (string, error) {
|
||||
resp := struct {
|
||||
Data struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"result"`
|
||||
Error
|
||||
}{}
|
||||
|
||||
params := make(map[string]interface{})
|
||||
params["coin"] = coin
|
||||
params["chain"] = chain
|
||||
params["address"] = address
|
||||
params["amount"] = amount
|
||||
if tag != "" {
|
||||
params["tag"] = tag
|
||||
}
|
||||
return resp.Data.ID, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, bybitWithdrawFund, nil, params, &resp, privateSpotRate)
|
||||
}
|
||||
|
||||
// SendHTTPRequest sends an unauthenticated request
|
||||
func (by *Bybit) SendHTTPRequest(ctx context.Context, ePath exchange.URL, path string, f request.EndpointLimit, result UnmarshalTo) error {
|
||||
endpointPath, err := by.API.Endpoints.GetURL(ePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = by.SendPayload(ctx, f, func() (*request.Item, error) {
|
||||
return &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpointPath + path,
|
||||
Result: result,
|
||||
Verbose: by.Verbose,
|
||||
HTTPDebugging: by.HTTPDebugging,
|
||||
HTTPRecording: by.HTTPRecording}, nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return result.GetError()
|
||||
}
|
||||
|
||||
// SendAuthHTTPRequest sends an authenticated HTTP request
|
||||
// If payload is non-nil then request is considered to be JSON
|
||||
func (by *Bybit) SendAuthHTTPRequest(ctx context.Context, ePath exchange.URL, method, path string, params url.Values, jsonPayload map[string]interface{}, result UnmarshalTo, f request.EndpointLimit) error {
|
||||
creds, err := by.GetCredentials(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
result = &Error{}
|
||||
}
|
||||
|
||||
endpointPath, err := by.API.Endpoints.GetURL(ePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if params == nil && jsonPayload == nil {
|
||||
params = url.Values{}
|
||||
}
|
||||
|
||||
if jsonPayload != nil {
|
||||
jsonPayload["recvWindow"] = defaultRecvWindow
|
||||
} else if params.Get("recvWindow") == "" {
|
||||
params.Set("recvWindow", defaultRecvWindow)
|
||||
}
|
||||
|
||||
err = by.SendPayload(ctx, f, func() (*request.Item, error) {
|
||||
var (
|
||||
payload []byte
|
||||
hmacSignedStr string
|
||||
)
|
||||
headers := make(map[string]string)
|
||||
|
||||
if jsonPayload != nil {
|
||||
headers["Content-Type"] = "application/json"
|
||||
jsonPayload["timestamp"] = strconv.FormatInt(time.Now().UnixMilli(), 10)
|
||||
jsonPayload["api_key"] = creds.Key
|
||||
hmacSignedStr, err = getJSONRequestSignature(jsonPayload, creds.Secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jsonPayload["sign"] = hmacSignedStr
|
||||
payload, err = json.Marshal(jsonPayload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
params.Set("timestamp", strconv.FormatInt(time.Now().UnixMilli(), 10))
|
||||
params.Set("api_key", creds.Key)
|
||||
hmacSignedStr, err = getSign(params.Encode(), creds.Secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
switch method {
|
||||
case http.MethodPost:
|
||||
params.Set("sign", hmacSignedStr)
|
||||
payload = []byte(params.Encode())
|
||||
default:
|
||||
path = common.EncodeURLValues(path, params)
|
||||
path += "&sign=" + hmacSignedStr
|
||||
}
|
||||
}
|
||||
|
||||
return &request.Item{
|
||||
Method: method,
|
||||
Path: endpointPath + path,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBuffer(payload),
|
||||
Result: &result,
|
||||
AuthRequest: true,
|
||||
Verbose: by.Verbose,
|
||||
HTTPDebugging: by.HTTPDebugging,
|
||||
HTTPRecording: by.HTTPRecording}, nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return result.GetError()
|
||||
}
|
||||
|
||||
// Error defines all error information for each request
|
||||
type Error struct {
|
||||
ReturnCode int64 `json:"ret_code"`
|
||||
ReturnMsg string `json:"ret_msg"`
|
||||
ExtCode string `json:"ext_code"`
|
||||
ExtMsg string `json:"ext_info"`
|
||||
}
|
||||
|
||||
// GetError checks and returns an error if it is supplied.
|
||||
func (e Error) GetError() error {
|
||||
if e.ReturnCode != 0 && e.ReturnMsg != "" {
|
||||
return errors.New(e.ReturnMsg)
|
||||
}
|
||||
if e.ExtCode != "" && e.ExtMsg != "" {
|
||||
return errors.New(e.ExtMsg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSide(side string) order.Side {
|
||||
switch side {
|
||||
case sideBuy:
|
||||
return order.Buy
|
||||
case sideSell:
|
||||
return order.Sell
|
||||
default:
|
||||
return order.UnknownSide
|
||||
}
|
||||
}
|
||||
|
||||
func getTradeType(tradeType string) order.Type {
|
||||
switch tradeType {
|
||||
case BybitRequestParamsOrderLimit:
|
||||
return order.Limit
|
||||
case BybitRequestParamsOrderMarket:
|
||||
return order.Market
|
||||
case BybitRequestParamsOrderLimitMaker:
|
||||
return order.Limit
|
||||
default:
|
||||
return order.UnknownType
|
||||
}
|
||||
}
|
||||
|
||||
func getOrderStatus(status string) order.Status {
|
||||
switch status {
|
||||
case "NEW":
|
||||
return order.New
|
||||
case "PARTIALLY_FILLED":
|
||||
return order.PartiallyFilled
|
||||
case "FILLED":
|
||||
return order.Filled
|
||||
case "CANCELED":
|
||||
return order.Cancelled
|
||||
case "PENDING_CANCEL":
|
||||
return order.PendingCancel
|
||||
case "PENDING_NEW":
|
||||
return order.Pending
|
||||
case "REJECTED":
|
||||
return order.Rejected
|
||||
default:
|
||||
return order.UnknownStatus
|
||||
}
|
||||
}
|
||||
|
||||
func getJSONRequestSignature(payload map[string]interface{}, secret string) (string, error) {
|
||||
payloadArr := make([]string, len(payload))
|
||||
var i int
|
||||
for p := range payload {
|
||||
payloadArr[i] = p
|
||||
i++
|
||||
}
|
||||
sort.Strings(payloadArr)
|
||||
var signStr string
|
||||
for _, key := range payloadArr {
|
||||
if value, found := payload[key]; found {
|
||||
if v, ok := value.(string); ok {
|
||||
signStr += key + "=" + v + "&"
|
||||
}
|
||||
} else {
|
||||
return "", errors.New("non-string payload parameter not expected")
|
||||
}
|
||||
}
|
||||
return getSign(signStr[:len(signStr)-1], secret)
|
||||
}
|
||||
|
||||
func getSign(sign, secret string) (string, error) {
|
||||
hmacSigned, err := crypto.GetHMAC(crypto.HashSHA256, []byte(sign), []byte(secret))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return crypto.HexEncodeToString(hmacSigned), nil
|
||||
}
|
||||
1310
exchanges/bybit/bybit_cfutures.go
Normal file
1310
exchanges/bybit/bybit_cfutures.go
Normal file
File diff suppressed because it is too large
Load Diff
757
exchanges/bybit/bybit_futures.go
Normal file
757
exchanges/bybit/bybit_futures.go
Normal file
@@ -0,0 +1,757 @@
|
||||
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 && limit <= 50 {
|
||||
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 && limit <= 50 {
|
||||
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)
|
||||
}
|
||||
|
||||
// SetLeverage sets leverage
|
||||
func (by *Bybit) SetLeverage(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 && limit <= 200 {
|
||||
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 && limit <= 50 {
|
||||
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)
|
||||
}
|
||||
3448
exchanges/bybit/bybit_test.go
Normal file
3448
exchanges/bybit/bybit_test.go
Normal file
File diff suppressed because it is too large
Load Diff
917
exchanges/bybit/bybit_types.go
Normal file
917
exchanges/bybit/bybit_types.go
Normal file
@@ -0,0 +1,917 @@
|
||||
package bybit
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
)
|
||||
|
||||
var (
|
||||
errTypeAssert = errors.New("type assertion failed")
|
||||
errStrParsing = errors.New("parsing string failed")
|
||||
errInvalidSide = errors.New("invalid side")
|
||||
errInvalidInterval = errors.New("invalid interval")
|
||||
errInvalidPeriod = errors.New("invalid period")
|
||||
errInvalidStartTime = errors.New("startTime can't be zero or missing")
|
||||
errInvalidQuantity = errors.New("quantity can't be zero or missing")
|
||||
errInvalidBasePrice = errors.New("basePrice can't be empty or missing")
|
||||
errInvalidStopPrice = errors.New("stopPrice can't be empty or missing")
|
||||
errInvalidTimeInForce = errors.New("timeInForce can't be empty or missing")
|
||||
errInvalidTakeProfitStopLoss = errors.New("takeProfitStopLoss can't be empty or missing")
|
||||
errInvalidMargin = errors.New("margin can't be empty")
|
||||
errInvalidLeverage = errors.New("leverage can't be zero or less then it")
|
||||
errInvalidRiskID = errors.New("riskID can't be zero or lesser")
|
||||
errInvalidPositionMode = errors.New("position mode is invalid")
|
||||
errInvalidOrderType = errors.New("orderType can't be empty or missing")
|
||||
errInvalidMode = errors.New("mode can't be empty or missing")
|
||||
errInvalidBuyLeverage = errors.New("buyLeverage can't be zero or less then it")
|
||||
errInvalidSellLeverage = errors.New("sellLeverage can't be zero or less then it")
|
||||
errInvalidOrderRequest = errors.New("order request param can't be nil")
|
||||
errInvalidOrderFilter = errors.New("orderFilter can't be empty or missing")
|
||||
errInvalidCategory = errors.New("invalid category")
|
||||
errInvalidCoin = errors.New("coin can't be empty")
|
||||
|
||||
errStopOrderOrOrderLinkIDMissing = errors.New("atleast one should be present among stopOrderID and orderLinkID")
|
||||
errOrderOrOrderLinkIDMissing = errors.New("atleast one should be present among orderID and orderLinkID")
|
||||
|
||||
errSymbolMissing = errors.New("symbol missing")
|
||||
errUnsupportedOrderType = errors.New("unsupported order type")
|
||||
errEmptyOrderIDs = errors.New("orderIDs can't be empty")
|
||||
errMissingPrice = errors.New("price should be present for Limit and LimitMaker orders")
|
||||
errExpectedOneOrder = errors.New("expected one order")
|
||||
)
|
||||
|
||||
// bybitTimeSec provides an internal conversion helper
|
||||
type bybitTimeSec time.Time
|
||||
|
||||
// UnmarshalJSON is custom json unmarshaller for bybitTimeSec
|
||||
func (b *bybitTimeSec) UnmarshalJSON(data []byte) error {
|
||||
var timestamp int64
|
||||
err := json.Unmarshal(data, ×tamp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*b = bybitTimeSec(time.Unix(timestamp, 0))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Time returns a time.Time object
|
||||
func (b bybitTimeSec) Time() time.Time {
|
||||
return time.Time(b)
|
||||
}
|
||||
|
||||
// bybitTimeSecStr provides an internal conversion helper
|
||||
type bybitTimeSecStr time.Time
|
||||
|
||||
// UnmarshalJSON is custom json unmarshaller for bybitTimeSec
|
||||
func (b *bybitTimeSecStr) UnmarshalJSON(data []byte) error {
|
||||
var timestamp string
|
||||
err := json.Unmarshal(data, ×tamp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t, err := strconv.ParseInt(timestamp, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*b = bybitTimeSecStr(time.Unix(t, 0))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Time returns a time.Time object
|
||||
func (b bybitTimeSecStr) Time() time.Time {
|
||||
return time.Time(b)
|
||||
}
|
||||
|
||||
// bybitTimeMilliSec provides an internal conversion helper
|
||||
type bybitTimeMilliSec time.Time
|
||||
|
||||
// UnmarshalJSON is custom type json unmarshaller for bybitTimeMilliSec
|
||||
func (b *bybitTimeMilliSec) UnmarshalJSON(data []byte) error {
|
||||
var timestamp int64
|
||||
err := json.Unmarshal(data, ×tamp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*b = bybitTimeMilliSec(time.UnixMilli(timestamp))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Time returns a time.Time object
|
||||
func (b bybitTimeMilliSec) Time() time.Time {
|
||||
return time.Time(b)
|
||||
}
|
||||
|
||||
// bybitTimeMilliSecStr provides an internal conversion helper
|
||||
type bybitTimeMilliSecStr time.Time
|
||||
|
||||
// UnmarshalJSON is custom type json unmarshaller for bybitTimeMilliSec
|
||||
func (b *bybitTimeMilliSecStr) UnmarshalJSON(data []byte) error {
|
||||
var timestamp string
|
||||
err := json.Unmarshal(data, ×tamp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t, err := strconv.ParseInt(timestamp, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*b = bybitTimeMilliSecStr(time.UnixMilli(t))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Time returns a time.Time object
|
||||
func (b bybitTimeMilliSecStr) Time() time.Time {
|
||||
return time.Time(b)
|
||||
}
|
||||
|
||||
// bybitTimeNanoSec provides an internal conversion helper
|
||||
type bybitTimeNanoSec time.Time
|
||||
|
||||
// UnmarshalJSON is custom type json unmarshaller for bybitTimeNanoSec
|
||||
func (b *bybitTimeNanoSec) UnmarshalJSON(data []byte) error {
|
||||
var timestamp int64
|
||||
err := json.Unmarshal(data, ×tamp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*b = bybitTimeNanoSec(time.Unix(0, timestamp))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Time returns a time.Time object
|
||||
func (b bybitTimeNanoSec) Time() time.Time {
|
||||
return time.Time(b)
|
||||
}
|
||||
|
||||
// UnmarshalTo acts as interface to exchange API response
|
||||
type UnmarshalTo interface {
|
||||
GetError() error
|
||||
}
|
||||
|
||||
// PairData stores pair data
|
||||
type PairData struct {
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
BaseCurrency string `json:"baseCurrency"`
|
||||
QuoteCurrency string `json:"quoteCurrency"`
|
||||
BasePrecision float64 `json:"basePrecision,string"`
|
||||
QuotePrecision float64 `json:"quotePrecision,string"`
|
||||
MinTradeQuantity float64 `json:"minTradeQuantity,string"`
|
||||
MinTradeAmount float64 `json:"minTradeAmount,string"`
|
||||
MinPricePrecision float64 `json:"minPricePrecision,string"`
|
||||
MaxTradeQuantity float64 `json:"maxTradeQuantity,string"`
|
||||
MaxTradeAmount float64 `json:"maxTradeAmount,string"`
|
||||
Category int64 `json:"category"`
|
||||
ShowStatus bool `json:"showStatus"`
|
||||
}
|
||||
|
||||
// Orderbook stores the orderbook data
|
||||
type Orderbook struct {
|
||||
Bids []orderbook.Item
|
||||
Asks []orderbook.Item
|
||||
Symbol string
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
// TradeItem stores a single trade
|
||||
type TradeItem struct {
|
||||
CurrencyPair string
|
||||
Price float64
|
||||
Side string
|
||||
Volume float64
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
// KlineItem stores an individual kline data item
|
||||
type KlineItem struct {
|
||||
StartTime time.Time
|
||||
EndTime time.Time
|
||||
Open float64
|
||||
Close float64
|
||||
High float64
|
||||
Low float64
|
||||
Volume float64
|
||||
QuoteAssetVolume float64
|
||||
TakerBaseVolume float64
|
||||
TakerQuoteVolume float64
|
||||
TradesCount int64
|
||||
}
|
||||
|
||||
// PriceChangeStats contains statistics for the last 24 hours trade
|
||||
type PriceChangeStats struct {
|
||||
Time time.Time
|
||||
Symbol string
|
||||
BestBidPrice float64
|
||||
BestAskPrice float64
|
||||
LastPrice float64
|
||||
OpenPrice float64
|
||||
HighPrice float64
|
||||
LowPrice float64
|
||||
Volume float64
|
||||
QuoteVolume float64
|
||||
}
|
||||
|
||||
// LastTradePrice contains price for last trade
|
||||
type LastTradePrice struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Price float64 `json:"price,string"`
|
||||
}
|
||||
|
||||
// TickerData stores ticker data
|
||||
type TickerData struct {
|
||||
Symbol string
|
||||
BidPrice float64
|
||||
BidQuantity float64
|
||||
AskPrice float64
|
||||
AskQuantity float64
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
var (
|
||||
// BybitRequestParamsOrderLimit Limit order
|
||||
BybitRequestParamsOrderLimit = "LIMIT"
|
||||
|
||||
// BybitRequestParamsOrderMarket Market order
|
||||
BybitRequestParamsOrderMarket = "MARKET"
|
||||
|
||||
// BybitRequestParamsOrderLimitMaker Limit Maker
|
||||
BybitRequestParamsOrderLimitMaker = "LIMIT_MAKER"
|
||||
)
|
||||
|
||||
var (
|
||||
// BybitRequestParamsTimeGTC Good Till Canceled
|
||||
BybitRequestParamsTimeGTC = "GTC"
|
||||
|
||||
// BybitRequestParamsTimeFOK Fill or Kill
|
||||
BybitRequestParamsTimeFOK = "FOK"
|
||||
|
||||
// BybitRequestParamsTimeIOC Immediate or Cancel
|
||||
BybitRequestParamsTimeIOC = "IOC"
|
||||
)
|
||||
|
||||
// PlaceOrderRequest store new order request type
|
||||
type PlaceOrderRequest struct {
|
||||
Symbol string
|
||||
Quantity float64
|
||||
Side string
|
||||
TradeType string
|
||||
TimeInForce string
|
||||
Price float64
|
||||
OrderLinkID string
|
||||
}
|
||||
|
||||
// PlaceOrderResponse store new order response type
|
||||
type PlaceOrderResponse struct {
|
||||
OrderID string `json:"orderId"`
|
||||
OrderLinkID string `json:"orderLinkId"`
|
||||
Symbol string `json:"symbol"`
|
||||
Time bybitTimeMilliSecStr `json:"transactTime"`
|
||||
Price float64 `json:"price,string"`
|
||||
Quantity float64 `json:"origQty,string"`
|
||||
TradeType string `json:"type"`
|
||||
Side string `json:"side"`
|
||||
Status string `json:"status"`
|
||||
TimeInForce string `json:"timeInForce"`
|
||||
AccountID string `json:"accountId"`
|
||||
SymbolName string `json:"symbolName"`
|
||||
ExecutedQty float64 `json:"executedQty,string"`
|
||||
}
|
||||
|
||||
// QueryOrderResponse holds query order data
|
||||
type QueryOrderResponse struct {
|
||||
AccountID string `json:"accountId"`
|
||||
ExchangeID string `json:"exchangeId"`
|
||||
Symbol string `json:"symbol"`
|
||||
SymbolName string `json:"symbolName"`
|
||||
OrderLinkID string `json:"orderLinkId"`
|
||||
OrderID string `json:"orderId"`
|
||||
Price float64 `json:"price,string"`
|
||||
Quantity float64 `json:"origQty,string"`
|
||||
ExecutedQty float64 `json:"executedQty,string"`
|
||||
CummulativeQuoteQty float64 `json:"cummulativeQuoteQty,string"`
|
||||
AveragePrice float64 `json:"avgPrice,string"`
|
||||
Status string `json:"status"`
|
||||
TimeInForce string `json:"timeInForce"`
|
||||
TradeType string `json:"type"`
|
||||
Side string `json:"side"`
|
||||
StopPrice float64 `json:"stopPrice,string"`
|
||||
IcebergQty float64 `json:"icebergQty,string"`
|
||||
Time bybitTimeMilliSecStr `json:"time"`
|
||||
UpdateTime bybitTimeMilliSecStr `json:"updateTime"`
|
||||
IsWorking bool `json:"isWorking"`
|
||||
}
|
||||
|
||||
// CancelOrderResponse is the return structured response from the exchange
|
||||
type CancelOrderResponse struct {
|
||||
OrderID string `json:"orderId"`
|
||||
OrderLinkID string `json:"orderLinkId"`
|
||||
Symbol string `json:"symbol"`
|
||||
Status string `json:"status"`
|
||||
AccountID string `json:"accountId"`
|
||||
Time bybitTimeMilliSecStr `json:"transactTime"`
|
||||
Price float64 `json:"price,string"`
|
||||
Quantity float64 `json:"origQty,string"`
|
||||
ExecutedQty float64 `json:"executedQty,string"`
|
||||
TimeInForce string `json:"timeInForce"`
|
||||
TradeType string `json:"type"`
|
||||
Side string `json:"side"`
|
||||
}
|
||||
|
||||
// HistoricalTrade holds recent trade data
|
||||
type HistoricalTrade struct {
|
||||
Symbol string `json:"symbol"`
|
||||
ID string `json:"id"`
|
||||
OrderID string `json:"orderId"`
|
||||
TicketID string `json:"ticketId"`
|
||||
Price float64 `json:"price,string"`
|
||||
Quantity float64 `json:"qty,string"`
|
||||
Commission float64 `json:"commission,string"`
|
||||
CommissionAsset float64 `json:"commissionAsset,string"`
|
||||
Time bybitTimeMilliSecStr `json:"time"`
|
||||
IsBuyer bool `json:"isBuyer"`
|
||||
IsMaker bool `json:"isMaker"`
|
||||
SymbolName string `json:"symbolName"`
|
||||
MatchOrderID string `json:"matchOrderId"`
|
||||
Fee FeeData `json:"fee"`
|
||||
FeeTokenID string `json:"feeTokenId"`
|
||||
FeeAmount float64 `json:"feeAmount,string"`
|
||||
MakerRebate float64 `json:"makerRebate,string"`
|
||||
}
|
||||
|
||||
// FeeData store fees data
|
||||
type FeeData struct {
|
||||
FeeTokenID int64 `json:"feeTokenId"`
|
||||
FeeTokenName string `json:"feeTokenName"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
}
|
||||
|
||||
// Balance holds wallet balance
|
||||
type Balance struct {
|
||||
Coin string `json:"coin"`
|
||||
CoinID string `json:"coinId"`
|
||||
CoinName string `json:"coinName"`
|
||||
Total float64 `json:"total,string"`
|
||||
Free float64 `json:"free,string"`
|
||||
Locked float64 `json:"locked,string"`
|
||||
}
|
||||
|
||||
type orderbookResponse struct {
|
||||
Data struct {
|
||||
Asks [][2]string `json:"asks"`
|
||||
Bids [][2]string `json:"bids"`
|
||||
Time bybitTimeMilliSec `json:"time"`
|
||||
} `json:"result"`
|
||||
Error
|
||||
}
|
||||
|
||||
type DepositWalletInfo struct {
|
||||
Coin string `json:"coin"`
|
||||
Chains []ChainInfo `json:"chains"`
|
||||
}
|
||||
|
||||
type ChainInfo struct {
|
||||
ChainType string `json:"chain_type"`
|
||||
DepositAddress string `json:"address_deposit"`
|
||||
DepositTag string `json:"tag_deposit"`
|
||||
Chain string `json:"chain"`
|
||||
}
|
||||
|
||||
// Websocket Structures
|
||||
|
||||
// Authenticate stores authentication variables required
|
||||
type Authenticate struct {
|
||||
Args []interface{} `json:"args"`
|
||||
Operation string `json:"op"`
|
||||
}
|
||||
|
||||
// WsReq has the data used for ws request
|
||||
type WsReq struct {
|
||||
Topic string `json:"topic"`
|
||||
Event string `json:"event"`
|
||||
Parameters interface{} `json:"params"`
|
||||
}
|
||||
|
||||
// WsFuturesReq stores futures ws request
|
||||
type WsFuturesReq struct {
|
||||
Topic string `json:"op"`
|
||||
Args []string `json:"args"`
|
||||
}
|
||||
|
||||
// WsParams store ws parameters
|
||||
type WsParams struct {
|
||||
Symbol string `json:"symbol"`
|
||||
IsBinary bool `json:"binary,string"`
|
||||
SymbolName string `json:"symbolName,omitempty"`
|
||||
KlineType string `json:"klineType,omitempty"` // only present in kline ws stream
|
||||
}
|
||||
|
||||
// WsSpotTickerData stores ws ticker data
|
||||
type WsSpotTickerData struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Bid float64 `json:"bidPrice,string"`
|
||||
Ask float64 `json:"askPrice,string"`
|
||||
BidSize float64 `json:"bidQty,string"`
|
||||
AskSize float64 `json:"askQty,string"`
|
||||
Time bybitTimeMilliSec `json:"time"`
|
||||
}
|
||||
|
||||
// WsSpotTicker stores ws ticker data
|
||||
type WsSpotTicker struct {
|
||||
Topic string `json:"topic"`
|
||||
Parameters WsParams `json:"params"`
|
||||
Ticker WsSpotTickerData `json:"data"`
|
||||
}
|
||||
|
||||
// KlineStreamData stores ws kline stream data
|
||||
type KlineStreamData struct {
|
||||
StartTime bybitTimeMilliSec `json:"t"`
|
||||
Symbol string `json:"s"`
|
||||
ClosePrice float64 `json:"c,string"`
|
||||
HighPrice float64 `json:"h,string"`
|
||||
LowPrice float64 `json:"l,string"`
|
||||
OpenPrice float64 `json:"o,string"`
|
||||
Volume float64 `json:"v,string"`
|
||||
}
|
||||
|
||||
// KlineStream holds the kline stream data
|
||||
type KlineStream struct {
|
||||
Topic string `json:"topic"`
|
||||
Parameters WsParams `json:"params"`
|
||||
Kline KlineStreamData `json:"data"`
|
||||
}
|
||||
|
||||
// WsOrderbookData stores ws orderbook data
|
||||
type WsOrderbookData struct {
|
||||
Symbol string `json:"s"`
|
||||
Time bybitTimeMilliSec `json:"t"`
|
||||
Version string `json:"v"`
|
||||
Bids [][2]string `json:"b"`
|
||||
Asks [][2]string `json:"a"`
|
||||
}
|
||||
|
||||
// WsOrderbook stores ws orderbook data
|
||||
type WsOrderbook struct {
|
||||
Topic string `json:"topic"`
|
||||
Parameters WsParams `json:"params"`
|
||||
OBData WsOrderbookData `json:"data"`
|
||||
}
|
||||
|
||||
// WsTradeData stores ws trade data
|
||||
type WsTradeData struct {
|
||||
Time bybitTimeMilliSec `json:"t"`
|
||||
ID string `json:"v"`
|
||||
Price float64 `json:"p,string"`
|
||||
Size float64 `json:"q,string"`
|
||||
Side bool `json:"m"`
|
||||
}
|
||||
|
||||
// WsTrade stores ws trades data
|
||||
type WsTrade struct {
|
||||
Topic string `json:"topic"`
|
||||
Parameters WsParams `json:"params"`
|
||||
TradeData WsTradeData `json:"data"`
|
||||
}
|
||||
|
||||
// wsAccount defines websocket account info data
|
||||
type wsAccount struct {
|
||||
EventType string `json:"e"`
|
||||
EventTime string `json:"E"`
|
||||
CanTrade bool `json:"T"`
|
||||
CanWithdraw bool `json:"W"`
|
||||
CanDeposit bool `json:"D"`
|
||||
Balance []Currencies `json:"B"`
|
||||
}
|
||||
|
||||
// Currencies stores currencies data
|
||||
type Currencies struct {
|
||||
Asset string `json:"a"`
|
||||
Available float64 `json:"f,string"`
|
||||
Locked float64 `json:"l,string"`
|
||||
}
|
||||
|
||||
// wsOrderUpdate defines websocket account order update data
|
||||
type wsOrderUpdate struct {
|
||||
EventType string `json:"e"`
|
||||
EventTime string `json:"E"`
|
||||
Symbol string `json:"s"`
|
||||
ClientOrderID string `json:"c"`
|
||||
Side string `json:"S"`
|
||||
OrderType string `json:"o"`
|
||||
TimeInForce string `json:"f"`
|
||||
Quantity float64 `json:"q,string"`
|
||||
Price float64 `json:"p,string"`
|
||||
OrderStatus string `json:"X"`
|
||||
OrderID string `json:"i"`
|
||||
OpponentOrderID string `json:"M"`
|
||||
LastExecutedQuantity float64 `json:"l,string"`
|
||||
CumulativeFilledQuantity float64 `json:"z,string"`
|
||||
LastExecutedPrice float64 `json:"L,string"`
|
||||
Commission float64 `json:"n,string"`
|
||||
CommissionAsset string `json:"N"`
|
||||
IsNormal bool `json:"u"`
|
||||
IsOnOrderBook bool `json:"w"`
|
||||
IsLimitMaker bool `json:"m"`
|
||||
OrderCreationTime bybitTimeMilliSecStr `json:"O"`
|
||||
CumulativeQuoteTransactedQuantity float64 `json:"Z,string"`
|
||||
AccountID string `json:"A"`
|
||||
IsClose bool `json:"C"`
|
||||
Leverage float64 `json:"v,string"`
|
||||
}
|
||||
|
||||
// wsOrderFilled defines websocket account order filled data
|
||||
type wsOrderFilled struct {
|
||||
EventType string `json:"e"`
|
||||
EventTime string `json:"E"`
|
||||
Symbol string `json:"s"`
|
||||
Quantity float64 `json:"q,string"`
|
||||
Timestamp bybitTimeMilliSecStr `json:"t"`
|
||||
Price float64 `json:"p,string"`
|
||||
TradeID string `json:"T"`
|
||||
OrderID string `json:"o"`
|
||||
UserGenOrderID string `json:"c"`
|
||||
OpponentOrderID string `json:"O"`
|
||||
AccountID string `json:"a"`
|
||||
OpponentAccountID string `json:"A"`
|
||||
IsMaker bool `json:"m"`
|
||||
Side string `json:"S"`
|
||||
}
|
||||
|
||||
// WsFuturesOrderbookData stores ws futures orderbook data
|
||||
type WsFuturesOrderbookData struct {
|
||||
Price float64 `json:"price,string"`
|
||||
Symbol string `json:"symbol"`
|
||||
ID int64 `json:"id"`
|
||||
Side string `json:"side"`
|
||||
Size float64 `json:"size"`
|
||||
}
|
||||
|
||||
// WsFuturesOrderbook stores ws futures orderbook
|
||||
type WsFuturesOrderbook struct {
|
||||
Topic string `json:"topic"`
|
||||
Type string `json:"string"`
|
||||
OBData []WsFuturesOrderbookData `json:"data"`
|
||||
}
|
||||
|
||||
// WsUSDTOrderbook stores ws usdt orderbook
|
||||
type WsUSDTOrderbook struct {
|
||||
Topic string `json:"topic"`
|
||||
Type string `json:"string"`
|
||||
Data struct {
|
||||
OBData []WsFuturesOrderbookData `json:"order_book"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// WsCoinDeltaOrderbook stores ws coinmargined orderbook
|
||||
type WsCoinDeltaOrderbook struct {
|
||||
Topic string `json:"topic"`
|
||||
Type string `json:"string"`
|
||||
OBData struct {
|
||||
Delete []WsFuturesOrderbookData `json:"delete"`
|
||||
Update []WsFuturesOrderbookData `json:"update"`
|
||||
Insert []WsFuturesOrderbookData `json:"insert"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// WsFuturesTradeData stores ws future trade data
|
||||
type WsFuturesTradeData struct {
|
||||
Time time.Time `json:"timestamp"`
|
||||
TimeInMilliseconds bybitTimeMilliSec `json:"trade_time_ms"`
|
||||
Symbol string `json:"symbol"`
|
||||
Side string `json:"side"`
|
||||
Size float64 `json:"size"`
|
||||
Price float64 `json:"price"`
|
||||
Direction string `json:"tick_direction"`
|
||||
ID string `json:"trade_id"`
|
||||
}
|
||||
|
||||
// WsFuturesTrade stores ws future trade
|
||||
type WsFuturesTrade struct {
|
||||
Topic string `json:"topic"`
|
||||
TradeData []WsFuturesTradeData `json:"data"`
|
||||
}
|
||||
|
||||
// WsFuturesKlineData stores ws future kline data
|
||||
type WsFuturesKlineData struct {
|
||||
StartTime bybitTimeSec `json:"start"`
|
||||
EndTime bybitTimeSec `json:"end"`
|
||||
Close float64 `json:"close"`
|
||||
Open float64 `json:"open"`
|
||||
High float64 `json:"high"`
|
||||
Low float64 `json:"low"`
|
||||
Volume float64 `json:"volume"`
|
||||
TurnOver float64 `json:"turnover"`
|
||||
Confirm bool `json:"confirm"`
|
||||
Timestamp bybitTimeMilliSec `json:"timestamp"`
|
||||
}
|
||||
|
||||
// WsFuturesKline stores ws future kline
|
||||
type WsFuturesKline struct {
|
||||
Topic string `json:"topic"`
|
||||
KlineData []WsFuturesKlineData `json:"data"`
|
||||
}
|
||||
|
||||
// WsInsuranceData stores ws insurance data
|
||||
type WsInsuranceData struct {
|
||||
Currency string `json:"currency"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
WalletBalance float64 `json:"wallet_balance"`
|
||||
}
|
||||
|
||||
// WsInsurance stores ws insurance
|
||||
type WsInsurance struct {
|
||||
Topic string `json:"topic"`
|
||||
Data []WsInsuranceData `json:"data"`
|
||||
}
|
||||
|
||||
// WsTickerData stores ws ticker data
|
||||
type WsTickerData struct {
|
||||
ID string `json:"id"`
|
||||
Symbol string `json:"symbol"`
|
||||
LastPrice float64 `json:"last_price,string"`
|
||||
BidPrice float64 `json:"bid1_price"`
|
||||
AskPrice float64 `json:"ask1_price"`
|
||||
LastDirection string `json:"last_tick_direction"`
|
||||
PrevPrice24h float64 `json:"prev_price_24h,string"`
|
||||
Price24hPercentChange float64 `json:"price_24h_pcnt_e6"`
|
||||
Price1hPercentChange float64 `json:"price_1h_pcnt_e6"`
|
||||
HighPrice24h float64 `json:"high_price_24h,string"`
|
||||
LowPrice24h float64 `json:"low_price_24h,string"`
|
||||
PrevPrice1h float64 `json:"prev_price_1h,string"`
|
||||
MarkPrice float64 `json:"mark_price,string"`
|
||||
IndexPrice float64 `json:"index_price,string"`
|
||||
OpenInterest float64 `json:"open_interest"`
|
||||
OpenValue float64 `json:"open_value_e8"`
|
||||
TotalTurnOver float64 `json:"total_turnover_e8"`
|
||||
TurnOver24h float64 `json:"turnover_24h_e8"`
|
||||
TotalVolume float64 `json:"total_volume"`
|
||||
Volume24h float64 `json:"volume_24h"`
|
||||
FundingRate int64 `json:"funding_rate_e6"`
|
||||
PredictedFundingRate float64 `json:"predicted_funding_rate_e6"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdateAt time.Time `json:"updated_at"`
|
||||
NextFundingAt time.Time `json:"next_funding_time"`
|
||||
CountDownHour int64 `json:"countdown_hour"`
|
||||
}
|
||||
|
||||
// WsTicker stores ws ticker
|
||||
type WsTicker struct {
|
||||
Topic string `json:"topic"`
|
||||
Ticker WsTickerData `json:"data"`
|
||||
}
|
||||
|
||||
// WsDeltaTicker stores ws ticker
|
||||
type WsDeltaTicker struct {
|
||||
Topic string `json:"topic"`
|
||||
Type string `json:"string"`
|
||||
Data struct {
|
||||
Delete []WsTickerData `json:"delete"`
|
||||
Update []WsTickerData `json:"update"`
|
||||
Insert []WsTickerData `json:"insert"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// WsFuturesTickerData stores ws future ticker data
|
||||
type WsFuturesTickerData struct {
|
||||
ID string `json:"id"`
|
||||
Symbol string `json:"symbol"`
|
||||
SymbolName string `json:"symbol_name"`
|
||||
SymbolYear int64 `json:"symbol_year"`
|
||||
ContractType string `json:"contract_type"`
|
||||
Coin string `json:"coin"`
|
||||
QuoteSymbol string `json:"quote_symbol"`
|
||||
Mode string `json:"mode"`
|
||||
IsUpBorrowable int64 `json:"is_up_borrowable"`
|
||||
ImportTime bybitTimeNanoSec `json:"import_time_e9"`
|
||||
StartTradingTime bybitTimeNanoSec `json:"start_trading_time_e9"`
|
||||
TimeToSettle bybitTimeNanoSec `json:"settle_time_e9"`
|
||||
SettleFeeRate int64 `json:"settle_fee_rate_e8"`
|
||||
ContractStatus string `json:"contract_status"`
|
||||
SystemSubsidy int64 `json:"system_subsidy_e8"`
|
||||
LastPrice float64 `json:"last_price,string"`
|
||||
BidPrice float64 `json:"bid1_price"`
|
||||
AskPrice float64 `json:"ask1_price"`
|
||||
LastDirection string `json:"last_tick_direction"`
|
||||
PrevPrice24h float64 `json:"prev_price_24h,string"`
|
||||
Price24hPercentChange float64 `json:"price_24h_pcnt_e6"`
|
||||
Price1hPercentChange float64 `json:"price_1h_pcnt_e6"`
|
||||
HighPrice24h float64 `json:"high_price_24h,string"`
|
||||
LowPrice24h float64 `json:"low_price_24h,string"`
|
||||
PrevPrice1h float64 `json:"prev_price_1h,string"`
|
||||
MarkPrice float64 `json:"mark_price,string"`
|
||||
IndexPrice float64 `json:"index_price,string"`
|
||||
OpenInterest float64 `json:"open_interest"`
|
||||
OpenValue float64 `json:"open_value_e8"`
|
||||
TotalTurnOver float64 `json:"total_turnover_e8"`
|
||||
TurnOver24h float64 `json:"turnover_24h_e8"`
|
||||
TotalVolume float64 `json:"total_volume"`
|
||||
Volume24h float64 `json:"volume_24h"`
|
||||
FairBasis float64 `json:"fair_basis_e8"`
|
||||
FairBasisRate float64 `json:"fair_basis_rate_e8"`
|
||||
BasisInYear float64 `json:"basis_in_year_e8"`
|
||||
ExpectPrice float64 `json:"expect_price,string"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdateAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// WsFuturesTicker stores ws future ticker
|
||||
type WsFuturesTicker struct {
|
||||
Topic string `json:"topic"`
|
||||
Ticker WsFuturesTickerData `json:"data"`
|
||||
}
|
||||
|
||||
// WsDeltaFuturesTicker stores ws delta future ticker
|
||||
type WsDeltaFuturesTicker struct {
|
||||
Topic string `json:"topic"`
|
||||
Type string `json:"string"`
|
||||
Data struct {
|
||||
Delete []WsFuturesTickerData `json:"delete"`
|
||||
Update []WsFuturesTickerData `json:"update"`
|
||||
Insert []WsFuturesTickerData `json:"insert"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// WsLiquidationData stores ws liquidation data
|
||||
type WsLiquidationData struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Side string `json:"side"`
|
||||
Price float64 `json:"price,string"`
|
||||
Qty float64 `json:"qty"`
|
||||
Timestamp bybitTimeMilliSec `json:"time"`
|
||||
}
|
||||
|
||||
// WsFuturesLiquidation stores ws future liquidation
|
||||
type WsFuturesLiquidation struct {
|
||||
Topic string `json:"topic"`
|
||||
Data WsLiquidationData `json:"data"`
|
||||
}
|
||||
|
||||
// WsFuturesPositionData stores ws future position data
|
||||
type WsFuturesPositionData struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
Symbol string `json:"symbol"`
|
||||
Side string `json:"side"`
|
||||
Size float64 `json:"size"`
|
||||
PositionID int64 `json:"position_idx"` // present in Futures position struct only
|
||||
Mode int64 `json:"mode"` // present in Futures position struct only
|
||||
Isolated bool `json:"isolated"` // present in Futures position struct only
|
||||
PositionValue float64 `json:"position_value,string"`
|
||||
EntryPrice float64 `json:"entry_price,string"`
|
||||
LiquidPrice float64 `json:"liq_price,string"`
|
||||
BustPrice float64 `json:"bust_price,string"`
|
||||
Leverage float64 `json:"leverage,string"`
|
||||
OrderMargin float64 `json:"order_margin,string"`
|
||||
PositionMargin float64 `json:"position_margin,string"`
|
||||
AvailableBalance float64 `json:"available_balance,string"`
|
||||
TakeProfit float64 `json:"take_profit,string"`
|
||||
TakeProfitTriggerBy string `json:"tp_trigger_by"`
|
||||
StopLoss float64 `json:"stop_loss,string"`
|
||||
StopLossTriggerBy string `json:"sl_trigger_by"`
|
||||
RealisedPNL float64 `json:"realised_pnl,string"`
|
||||
TrailingStop float64 `json:"trailing_stop,string"`
|
||||
TrailingActive float64 `json:"trailing_active,string"`
|
||||
WalletBalance float64 `json:"wallet_balance,string"`
|
||||
RiskID int64 `json:"risk_id"`
|
||||
ClosingFee float64 `json:"occ_closing_fee,string"`
|
||||
FundingFee float64 `json:"occ_funding_fee,string"`
|
||||
AutoAddMargin int64 `json:"auto_add_margin"`
|
||||
TotalPNL float64 `json:"cum_realised_pnl,string"`
|
||||
Status string `json:"position_status"`
|
||||
Version int64 `json:"position_seq"`
|
||||
}
|
||||
|
||||
// WsFuturesPosition stores ws future position
|
||||
type WsFuturesPosition struct {
|
||||
Topic string `json:"topic"`
|
||||
Action string `json:"action"`
|
||||
Data []WsFuturesPositionData `json:"data"`
|
||||
}
|
||||
|
||||
// WsFuturesExecutionData stores ws future execution data
|
||||
type WsFuturesExecutionData struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Side string `json:"side"`
|
||||
OrderID string `json:"order_id"`
|
||||
ExecutionID string `json:"exec_id"`
|
||||
OrderLinkID string `json:"order_link_id"`
|
||||
Price float64 `json:"price,string"`
|
||||
OrderQty float64 `json:"order_qty"`
|
||||
ExecutionType string `json:"exec_type"`
|
||||
ExecutionQty float64 `json:"exec_qty"`
|
||||
ExecutionFee float64 `json:"exec_fee,string"`
|
||||
LeavesQty float64 `json:"leaves_qty"`
|
||||
IsMaker bool `json:"is_maker"`
|
||||
Time time.Time `json:"trade_time"`
|
||||
}
|
||||
|
||||
// WsFuturesExecution stores ws future execution
|
||||
type WsFuturesExecution struct {
|
||||
Topic string `json:"topic"`
|
||||
Data []WsFuturesExecutionData `json:"data"`
|
||||
}
|
||||
|
||||
// WsOrderData stores ws order data
|
||||
type WsOrderData struct {
|
||||
OrderID string `json:"order_id"`
|
||||
OrderLinkID string `json:"order_link_id"`
|
||||
Symbol string `json:"symbol"`
|
||||
Side string `json:"side"`
|
||||
OrderType string `json:"order_type"`
|
||||
Price float64 `json:"price,string"`
|
||||
OrderQty float64 `json:"qty"`
|
||||
TimeInForce string `json:"time_in_force"`
|
||||
CreateType string `json:"create_type"`
|
||||
CancelType string `json:"cancel_type"`
|
||||
OrderStatus string `json:"order_status"`
|
||||
LeavesQty float64 `json:"leaves_qty"`
|
||||
CummulativeExecQty float64 `json:"cum_exec_qty"`
|
||||
CummulativeExecValue float64 `json:"cum_exec_value,string"`
|
||||
CummulativeExecFee float64 `json:"cum_exec_fee,string"`
|
||||
TakeProfit float64 `json:"take_profit,string"`
|
||||
StopLoss float64 `json:"stop_loss,string"`
|
||||
TrailingStop float64 `json:"trailing_stop,string"`
|
||||
TrailingActive float64 `json:"trailing_active,string"`
|
||||
LastExecPrice float64 `json:"last_exec_price,string"`
|
||||
ReduceOnly bool `json:"reduce_only"`
|
||||
CloseOnTrigger bool `json:"close_on_trigger"`
|
||||
Time time.Time `json:"timestamp"` // present in CoinMarginedFutures and Futures only
|
||||
CreateTime time.Time `json:"create_time"` // present in USDTMarginedFutures only
|
||||
UpdateTime time.Time `json:"update_time"` // present in USDTMarginedFutures only
|
||||
}
|
||||
|
||||
// WsOrder stores ws order
|
||||
type WsOrder struct {
|
||||
Topic string `json:"topic"`
|
||||
Data []WsOrderData `json:"data"`
|
||||
}
|
||||
|
||||
// WsStopOrderData stores ws stop order data
|
||||
type WsStopOrderData struct {
|
||||
OrderID string `json:"order_id"`
|
||||
OrderLinkID string `json:"order_link_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
Symbol string `json:"symbol"`
|
||||
Side string `json:"side"`
|
||||
OrderType string `json:"order_type"`
|
||||
Price float64 `json:"price,string"`
|
||||
OrderQty float64 `json:"qty"`
|
||||
TimeInForce string `json:"time_in_force"`
|
||||
CreateType string `json:"create_type"`
|
||||
CancelType string `json:"cancel_type"`
|
||||
OrderStatus string `json:"order_status"`
|
||||
StopOrderType string `json:"stop_order_type"`
|
||||
TriggerBy string `json:"trigger_by"`
|
||||
TriggerPrice float64 `json:"trigger_price,string"`
|
||||
Time time.Time `json:"timestamp"`
|
||||
CloseOnTrigger bool `json:"close_on_trigger"`
|
||||
}
|
||||
|
||||
// WsFuturesStopOrder stores ws future stop order
|
||||
type WsFuturesStopOrder struct {
|
||||
Topic string `json:"topic"`
|
||||
Data []WsStopOrderData `json:"data"`
|
||||
}
|
||||
|
||||
// WsUSDTStopOrderData stores ws USDT stop order data
|
||||
type WsUSDTStopOrderData struct {
|
||||
OrderID string `json:"stop_order_id"`
|
||||
OrderLinkID string `json:"order_link_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
Symbol string `json:"symbol"`
|
||||
Side string `json:"side"`
|
||||
OrderType string `json:"order_type"`
|
||||
Price float64 `json:"price,string"`
|
||||
OrderQty float64 `json:"qty"`
|
||||
TimeInForce string `json:"time_in_force"`
|
||||
OrderStatus string `json:"order_status"`
|
||||
StopOrderType string `json:"stop_order_type"`
|
||||
TriggerBy string `json:"trigger_by"`
|
||||
TriggerPrice float64 `json:"trigger_price,string"`
|
||||
ReduceOnly bool `json:"reduce_only"`
|
||||
CloseOnTrigger bool `json:"close_on_trigger"`
|
||||
CreateTime time.Time `json:"create_time"`
|
||||
UpdateTime time.Time `json:"update_time"`
|
||||
}
|
||||
|
||||
// WsUSDTFuturesStopOrder stores ws USDT stop order
|
||||
type WsUSDTFuturesStopOrder struct {
|
||||
Topic string `json:"topic"`
|
||||
Data []WsUSDTStopOrderData `json:"data"`
|
||||
}
|
||||
|
||||
// WsFuturesWalletData stores ws future wallet data
|
||||
type WsFuturesWalletData struct {
|
||||
WalletBalance float64 `json:"wallet_balance"`
|
||||
AvailableBalance float64 `json:"available_balance"`
|
||||
}
|
||||
|
||||
// WsFuturesWallet stores ws future wallet
|
||||
type WsFuturesWallet struct {
|
||||
Topic string `json:"topic"`
|
||||
Data []WsFuturesWalletData `json:"data"`
|
||||
}
|
||||
1048
exchanges/bybit/bybit_ufutures.go
Normal file
1048
exchanges/bybit/bybit_ufutures.go
Normal file
File diff suppressed because it is too large
Load Diff
1114
exchanges/bybit/bybit_usdcfutures.go
Normal file
1114
exchanges/bybit/bybit_usdcfutures.go
Normal file
File diff suppressed because it is too large
Load Diff
555
exchanges/bybit/bybit_websocket.go
Normal file
555
exchanges/bybit/bybit_websocket.go
Normal file
@@ -0,0 +1,555 @@
|
||||
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 (
|
||||
bybitWSBaseURL = "wss://stream.bybit.com/"
|
||||
wsSpotPublicTopicV2 = "spot/quote/ws/v2"
|
||||
wsSpotPrivate = "spot/ws"
|
||||
bybitWebsocketTimer = 20 * time.Second
|
||||
wsOrderbook = "depth"
|
||||
wsTicker = "bookTicker"
|
||||
wsTrades = "trade"
|
||||
wsKlines = "kline"
|
||||
|
||||
wsAccountInfo = "outboundAccountInfo"
|
||||
wsOrderExecution = "executionReport"
|
||||
wsTickerInfo = "ticketInfo"
|
||||
|
||||
sub = "sub" // event for subscribe
|
||||
cancel = "cancel" // event for unsubscribe
|
||||
)
|
||||
|
||||
var comms = make(chan stream.Response)
|
||||
|
||||
// WsConnect connects to a websocket feed
|
||||
func (by *Bybit) WsConnect() error {
|
||||
if !by.Websocket.IsEnabled() || !by.IsEnabled() || !by.IsAssetWebsocketSupported(asset.Spot) {
|
||||
return errors.New(stream.WebsocketNotEnabled)
|
||||
}
|
||||
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(by.Websocket.Conn)
|
||||
if by.IsWebsocketAuthenticationSupported() {
|
||||
err = by.WsAuth(context.TODO())
|
||||
if err != nil {
|
||||
by.Websocket.DataHandler <- err
|
||||
by.Websocket.SetCanUseAuthenticatedEndpoints(false)
|
||||
}
|
||||
}
|
||||
|
||||
by.Websocket.Wg.Add(1)
|
||||
go by.WsDataHandler()
|
||||
return nil
|
||||
}
|
||||
|
||||
// WsAuth sends an authentication message to receive auth data
|
||||
func (by *Bybit) WsAuth(ctx context.Context) error {
|
||||
var dialer websocket.Dialer
|
||||
err := by.Websocket.AuthConn.Dial(&dialer, http.Header{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
by.Websocket.AuthConn.SetupPingHandler(stream.PingHandler{
|
||||
MessageType: websocket.TextMessage,
|
||||
Message: []byte(`{"op":"ping"}`),
|
||||
Delay: bybitWebsocketTimer,
|
||||
})
|
||||
|
||||
by.Websocket.Wg.Add(1)
|
||||
go by.wsReadData(by.Websocket.AuthConn)
|
||||
|
||||
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.AuthConn.SendJSONMessage(req)
|
||||
}
|
||||
|
||||
// Subscribe sends a websocket message to receive data from the channel
|
||||
func (by *Bybit) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error {
|
||||
var errs common.Errors
|
||||
for i := range channelsToSubscribe {
|
||||
var subReq WsReq
|
||||
subReq.Topic = channelsToSubscribe[i].Channel
|
||||
subReq.Event = sub
|
||||
|
||||
formattedPair, err := by.FormatExchangeCurrency(channelsToSubscribe[i].Currency, asset.Spot)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
if channelsToSubscribe[i].Channel == wsKlines {
|
||||
subReq.Parameters = WsParams{
|
||||
Symbol: formattedPair.String(),
|
||||
IsBinary: true,
|
||||
KlineType: "1m",
|
||||
}
|
||||
} else {
|
||||
subReq.Parameters = WsParams{
|
||||
Symbol: formattedPair.String(),
|
||||
IsBinary: true,
|
||||
}
|
||||
}
|
||||
err = by.Websocket.Conn.SendJSONMessage(subReq)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
by.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i])
|
||||
}
|
||||
if errs != nil {
|
||||
return errs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unsubscribe sends a websocket message to stop receiving data from the channel
|
||||
func (by *Bybit) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscription) error {
|
||||
var errs common.Errors
|
||||
|
||||
for i := range channelsToUnsubscribe {
|
||||
var unSub WsReq
|
||||
unSub.Event = cancel
|
||||
unSub.Topic = channelsToUnsubscribe[i].Channel
|
||||
|
||||
formattedPair, err := by.FormatExchangeCurrency(channelsToUnsubscribe[i].Currency, asset.Spot)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
unSub.Parameters = WsParams{
|
||||
Symbol: formattedPair.String(),
|
||||
}
|
||||
err = by.Websocket.Conn.SendJSONMessage(unSub)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
by.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe[i])
|
||||
}
|
||||
if errs != nil {
|
||||
return errs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// wsReadData receives and passes on websocket messages for processing
|
||||
func (by *Bybit) wsReadData(ws stream.Connection) {
|
||||
defer by.Websocket.Wg.Done()
|
||||
for {
|
||||
resp := ws.ReadMessage()
|
||||
if resp.Raw == nil {
|
||||
return
|
||||
}
|
||||
comms <- resp
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateDefaultSubscriptions generates default subscription
|
||||
func (by *Bybit) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) {
|
||||
var subscriptions []stream.ChannelSubscription
|
||||
var channels = []string{wsTicker, wsTrades, wsOrderbook, wsKlines}
|
||||
pairs, err := by.GetEnabledPairs(asset.Spot)
|
||||
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.Spot,
|
||||
})
|
||||
}
|
||||
}
|
||||
return subscriptions, nil
|
||||
}
|
||||
|
||||
func stringToOrderStatus(status string) (order.Status, error) {
|
||||
switch status {
|
||||
case "NEW":
|
||||
return order.New, nil
|
||||
case "CANCELED":
|
||||
return order.Cancelled, nil
|
||||
case "REJECTED":
|
||||
return order.Rejected, nil
|
||||
case "TRADE":
|
||||
return order.PartiallyFilled, nil
|
||||
case "EXPIRED":
|
||||
return order.Expired, nil
|
||||
default:
|
||||
return order.UnknownStatus, errors.New(status + " not recognised as order status")
|
||||
}
|
||||
}
|
||||
|
||||
// WsDataHandler handles data from wsReadData
|
||||
func (by *Bybit) WsDataHandler() {
|
||||
defer by.Websocket.Wg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-by.Websocket.ShutdownC:
|
||||
return
|
||||
case resp := <-comms:
|
||||
err := by.wsHandleData(resp.Raw)
|
||||
if err != nil {
|
||||
by.Websocket.DataHandler <- err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (by *Bybit) wsHandleData(respRaw []byte) error {
|
||||
var result interface{}
|
||||
err := json.Unmarshal(respRaw, &result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch d := result.(type) {
|
||||
case map[string]interface{}:
|
||||
if method, ok := d["event"].(string); ok {
|
||||
if strings.EqualFold(method, sub) {
|
||||
return nil
|
||||
}
|
||||
if strings.EqualFold(method, cancel) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if t, ok := d["topic"].(string); ok {
|
||||
switch t {
|
||||
case wsOrderbook:
|
||||
var data WsOrderbook
|
||||
err := json.Unmarshal(respRaw, &data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p, err := by.extractCurrencyPair(data.OBData.Symbol, asset.Spot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = by.wsUpdateOrderbook(&data.OBData, p, asset.Spot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case wsTrades:
|
||||
if !by.IsSaveTradeDataEnabled() {
|
||||
return nil
|
||||
}
|
||||
var data WsTrade
|
||||
err := json.Unmarshal(respRaw, &data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p, err := by.extractCurrencyPair(data.Parameters.Symbol, asset.Spot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
side := order.Sell
|
||||
if data.TradeData.Side {
|
||||
side = order.Buy
|
||||
}
|
||||
|
||||
return trade.AddTradesToBuffer(by.Name, trade.Data{
|
||||
Timestamp: data.TradeData.Time.Time(),
|
||||
CurrencyPair: p,
|
||||
AssetType: asset.Spot,
|
||||
Exchange: by.Name,
|
||||
Price: data.TradeData.Price,
|
||||
Amount: data.TradeData.Size,
|
||||
Side: side,
|
||||
TID: data.TradeData.ID,
|
||||
})
|
||||
case wsTicker:
|
||||
var data WsSpotTicker
|
||||
err := json.Unmarshal(respRaw, &data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p, err := by.extractCurrencyPair(data.Ticker.Symbol, asset.Spot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
by.Websocket.DataHandler <- &ticker.Price{
|
||||
ExchangeName: by.Name,
|
||||
Bid: data.Ticker.Bid,
|
||||
Ask: data.Ticker.Ask,
|
||||
LastUpdated: data.Ticker.Time.Time(),
|
||||
AssetType: asset.Spot,
|
||||
Pair: p,
|
||||
}
|
||||
return nil
|
||||
case wsKlines:
|
||||
var data KlineStream
|
||||
err := json.Unmarshal(respRaw, &data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p, err := by.extractCurrencyPair(data.Kline.Symbol, asset.Spot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
by.Websocket.DataHandler <- stream.KlineData{
|
||||
Pair: p,
|
||||
AssetType: asset.Spot,
|
||||
Exchange: by.Name,
|
||||
StartTime: data.Kline.StartTime.Time(),
|
||||
Interval: data.Parameters.KlineType,
|
||||
OpenPrice: data.Kline.OpenPrice,
|
||||
ClosePrice: data.Kline.ClosePrice,
|
||||
HighPrice: data.Kline.HighPrice,
|
||||
LowPrice: data.Kline.LowPrice,
|
||||
Volume: data.Kline.Volume,
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + string(respRaw)}
|
||||
}
|
||||
}
|
||||
|
||||
if m, ok := d["auth"]; ok {
|
||||
log.Infof(log.WebsocketMgr, "%v received auth response: %v", by.Name, m)
|
||||
return nil
|
||||
}
|
||||
|
||||
if m, ok := d["pong"]; ok {
|
||||
log.Infof(log.WebsocketMgr, "%v received pong: %v", by.Name, m)
|
||||
return nil
|
||||
}
|
||||
case []interface{}:
|
||||
for i := range d {
|
||||
obj, ok := d[i].(map[string]interface{})
|
||||
if !ok {
|
||||
return common.GetAssertError("map[string]interface{}", d[i])
|
||||
}
|
||||
e, ok := obj["e"].(string)
|
||||
if !ok {
|
||||
return common.GetAssertError("string", obj["e"])
|
||||
}
|
||||
|
||||
switch e {
|
||||
case wsAccountInfo:
|
||||
var data []wsAccount
|
||||
err := json.Unmarshal(respRaw, &data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v - Could not convert to outboundAccountInfo structure %w",
|
||||
by.Name,
|
||||
err)
|
||||
}
|
||||
by.Websocket.DataHandler <- data
|
||||
return nil
|
||||
case wsOrderExecution:
|
||||
var data []wsOrderUpdate
|
||||
err := json.Unmarshal(respRaw, &data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v - Could not convert to executionReport structure %w",
|
||||
by.Name,
|
||||
err)
|
||||
}
|
||||
|
||||
for j := range data {
|
||||
oType, err := order.StringToOrderType(data[j].OrderType)
|
||||
if err != nil {
|
||||
by.Websocket.DataHandler <- order.ClassificationError{
|
||||
Exchange: by.Name,
|
||||
OrderID: data[j].OrderID,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
var oSide order.Side
|
||||
oSide, err = order.StringToOrderSide(data[j].Side)
|
||||
if err != nil {
|
||||
by.Websocket.DataHandler <- order.ClassificationError{
|
||||
Exchange: by.Name,
|
||||
OrderID: data[j].OrderID,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
var oStatus order.Status
|
||||
oStatus, err = stringToOrderStatus(data[j].OrderStatus)
|
||||
if err != nil {
|
||||
by.Websocket.DataHandler <- order.ClassificationError{
|
||||
Exchange: by.Name,
|
||||
OrderID: data[j].OrderID,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
p, err := by.extractCurrencyPair(data[j].Symbol, asset.Spot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
by.Websocket.DataHandler <- order.Detail{
|
||||
Price: data[j].Price,
|
||||
Amount: data[j].Quantity,
|
||||
ExecutedAmount: data[j].CumulativeFilledQuantity,
|
||||
RemainingAmount: data[j].Quantity - data[j].CumulativeFilledQuantity,
|
||||
Exchange: by.Name,
|
||||
OrderID: data[j].OrderID,
|
||||
Type: oType,
|
||||
Side: oSide,
|
||||
Status: oStatus,
|
||||
AssetType: asset.Spot,
|
||||
Date: data[j].OrderCreationTime.Time(),
|
||||
Pair: p,
|
||||
ClientOrderID: data[j].ClientOrderID,
|
||||
Trades: []order.TradeHistory{
|
||||
{
|
||||
Price: data[j].Price,
|
||||
Amount: data[j].Quantity,
|
||||
Exchange: by.Name,
|
||||
Timestamp: data[j].OrderCreationTime.Time(),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case wsTickerInfo:
|
||||
var data []wsOrderFilled
|
||||
err := json.Unmarshal(respRaw, &data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v - Could not convert to ticketInfo structure %w",
|
||||
by.Name,
|
||||
err)
|
||||
}
|
||||
|
||||
for j := range data {
|
||||
var oSide order.Side
|
||||
oSide, err = order.StringToOrderSide(data[j].Side)
|
||||
if err != nil {
|
||||
by.Websocket.DataHandler <- order.ClassificationError{
|
||||
Exchange: by.Name,
|
||||
OrderID: data[j].OrderID,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
p, err := by.extractCurrencyPair(data[j].Symbol, asset.Spot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
by.Websocket.DataHandler <- &order.Detail{
|
||||
Exchange: by.Name,
|
||||
OrderID: data[j].OrderID,
|
||||
Side: oSide,
|
||||
AssetType: asset.Spot,
|
||||
Pair: p,
|
||||
Price: data[j].Price,
|
||||
Amount: data[j].Quantity,
|
||||
Date: data[j].Timestamp.Time(),
|
||||
Trades: []order.TradeHistory{
|
||||
{
|
||||
Price: data[j].Price,
|
||||
Amount: data[j].Quantity,
|
||||
Exchange: by.Name,
|
||||
Timestamp: data[j].Timestamp.Time(),
|
||||
TID: data[j].TradeID,
|
||||
IsMaker: data[j].IsMaker,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("unhandled stream data %s", string(respRaw))
|
||||
}
|
||||
|
||||
func (by *Bybit) wsUpdateOrderbook(update *WsOrderbookData, p currency.Pair, assetType asset.Item) error {
|
||||
if update == nil || (len(update.Asks) == 0 && len(update.Bids) == 0) {
|
||||
return errors.New("no orderbook data")
|
||||
}
|
||||
asks := make([]orderbook.Item, len(update.Asks))
|
||||
for i := range update.Asks {
|
||||
target, err := strconv.ParseFloat(update.Asks[i][0], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
amount, err := strconv.ParseFloat(update.Asks[i][1], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
asks[i] = orderbook.Item{Price: target, Amount: amount}
|
||||
}
|
||||
bids := make([]orderbook.Item, len(update.Bids))
|
||||
for i := range update.Bids {
|
||||
target, err := strconv.ParseFloat(update.Bids[i][0], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
amount, err := strconv.ParseFloat(update.Bids[i][1], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bids[i] = orderbook.Item{Price: target, Amount: amount}
|
||||
}
|
||||
return by.Websocket.Orderbook.LoadSnapshot(&orderbook.Base{
|
||||
Bids: bids,
|
||||
Asks: asks,
|
||||
Pair: p,
|
||||
LastUpdated: update.Time.Time(),
|
||||
Asset: assetType,
|
||||
Exchange: by.Name,
|
||||
VerifyOrderbook: by.CanVerifyOrderbook,
|
||||
})
|
||||
}
|
||||
2059
exchanges/bybit/bybit_wrapper.go
Normal file
2059
exchanges/bybit/bybit_wrapper.go
Normal file
File diff suppressed because it is too large
Load Diff
789
exchanges/bybit/bybit_ws_cfutures.go
Normal file
789
exchanges/bybit/bybit_ws_cfutures.go
Normal file
@@ -0,0 +1,789 @@
|
||||
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 common.Errors
|
||||
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 = append(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 common.Errors
|
||||
|
||||
for i := range channelsToUnsubscribe {
|
||||
var unSub WsFuturesReq
|
||||
unSub.Topic = unsubscribe
|
||||
|
||||
formattedPair, err := by.FormatExchangeCurrency(channelsToUnsubscribe[i].Currency, asset.CoinMarginedFutures)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
unSub.Args = append(unSub.Args, channelsToUnsubscribe[i].Channel+dot+formattedPair.String())
|
||||
err = by.Websocket.Conn.SendJSONMessage(unSub)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
by.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe[i])
|
||||
}
|
||||
if errs != nil {
|
||||
return errs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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:
|
||||
switch wsType {
|
||||
case wsOperationSnapshot:
|
||||
var response WsFuturesOrderbook
|
||||
err = json.Unmarshal(respRaw, &response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var p currency.Pair
|
||||
p, err = by.extractCurrencyPair(response.OBData[0].Symbol, asset.CoinMarginedFutures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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, err = by.extractCurrencyPair(response.OBData.Delete[0].Symbol, asset.CoinMarginedFutures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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, err = by.extractCurrencyPair(response.OBData.Update[0].Symbol, asset.CoinMarginedFutures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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, err = by.extractCurrencyPair(response.OBData.Insert[0].Symbol, asset.CoinMarginedFutures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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.extractCurrencyPair(response.TradeData[0].Symbol, asset.CoinMarginedFutures)
|
||||
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.extractCurrencyPair(topics[len(topics)-1], asset.CoinMarginedFutures)
|
||||
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.extractCurrencyPair(response.Ticker.Symbol, asset.CoinMarginedFutures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
by.Websocket.DataHandler <- &ticker.Price{
|
||||
ExchangeName: by.Name,
|
||||
Last: response.Ticker.LastPrice,
|
||||
High: response.Ticker.HighPrice24h,
|
||||
Low: response.Ticker.LowPrice24h,
|
||||
Bid: response.Ticker.BidPrice,
|
||||
Ask: response.Ticker.AskPrice,
|
||||
Volume: response.Ticker.Volume24h,
|
||||
Close: response.Ticker.PrevPrice24h,
|
||||
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.extractCurrencyPair(response.Data.Delete[x].Symbol, asset.CoinMarginedFutures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
by.Websocket.DataHandler <- &ticker.Price{
|
||||
ExchangeName: by.Name,
|
||||
Last: response.Data.Delete[x].LastPrice,
|
||||
High: response.Data.Delete[x].HighPrice24h,
|
||||
Low: response.Data.Delete[x].LowPrice24h,
|
||||
Bid: response.Data.Delete[x].BidPrice,
|
||||
Ask: response.Data.Delete[x].AskPrice,
|
||||
Volume: response.Data.Delete[x].Volume24h,
|
||||
Close: response.Data.Delete[x].PrevPrice24h,
|
||||
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.extractCurrencyPair(response.Data.Update[x].Symbol, asset.CoinMarginedFutures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
by.Websocket.DataHandler <- &ticker.Price{
|
||||
ExchangeName: by.Name,
|
||||
Last: response.Data.Update[x].LastPrice,
|
||||
High: response.Data.Update[x].HighPrice24h,
|
||||
Low: response.Data.Update[x].LowPrice24h,
|
||||
Bid: response.Data.Update[x].BidPrice,
|
||||
Ask: response.Data.Update[x].AskPrice,
|
||||
Volume: response.Data.Update[x].Volume24h,
|
||||
Close: response.Data.Update[x].PrevPrice24h,
|
||||
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.extractCurrencyPair(response.Data.Insert[x].Symbol, asset.CoinMarginedFutures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
by.Websocket.DataHandler <- &ticker.Price{
|
||||
ExchangeName: by.Name,
|
||||
Last: response.Data.Insert[x].LastPrice,
|
||||
High: response.Data.Insert[x].HighPrice24h,
|
||||
Low: response.Data.Insert[x].LowPrice24h,
|
||||
Bid: response.Data.Insert[x].BidPrice,
|
||||
Ask: response.Data.Insert[x].AskPrice,
|
||||
Volume: response.Data.Insert[x].Volume24h,
|
||||
Close: response.Data.Insert[x].PrevPrice24h,
|
||||
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.extractCurrencyPair(response.Data[i].Symbol, asset.CoinMarginedFutures)
|
||||
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,
|
||||
Amount: response.Data[i].OrderQty,
|
||||
Side: oSide,
|
||||
Status: oStatus,
|
||||
Trades: []order.TradeHistory{
|
||||
{
|
||||
Price: response.Data[i].Price,
|
||||
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.extractCurrencyPair(response.Data[x].Symbol, asset.CoinMarginedFutures)
|
||||
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,
|
||||
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,
|
||||
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.extractCurrencyPair(response.Data[x].Symbol, asset.CoinMarginedFutures)
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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)
|
||||
}
|
||||
645
exchanges/bybit/bybit_ws_futures.go
Normal file
645
exchanges/bybit/bybit_ws_futures.go
Normal file
@@ -0,0 +1,645 @@
|
||||
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 (
|
||||
wsFuturesPath = "realtime"
|
||||
)
|
||||
|
||||
// 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 common.Errors
|
||||
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 = append(errs, err)
|
||||
continue
|
||||
}
|
||||
by.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i])
|
||||
}
|
||||
if errs != nil {
|
||||
return errs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnsubscribeFutures sends a websocket message to stop receiving data from the channel
|
||||
func (by *Bybit) UnsubscribeFutures(channelsToUnsubscribe []stream.ChannelSubscription) error {
|
||||
var errs common.Errors
|
||||
|
||||
for i := range channelsToUnsubscribe {
|
||||
var unSub WsFuturesReq
|
||||
unSub.Topic = unsubscribe
|
||||
|
||||
formattedPair, err := by.FormatExchangeCurrency(channelsToUnsubscribe[i].Currency, asset.Futures)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
unSub.Args = append(unSub.Args, channelsToUnsubscribe[i].Channel+dot+formattedPair.String())
|
||||
err = by.Websocket.Conn.SendJSONMessage(unSub)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
by.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe[i])
|
||||
}
|
||||
if errs != nil {
|
||||
return errs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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.extractCurrencyPair(response.OBData[0].Symbol, asset.Futures)
|
||||
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.extractCurrencyPair(response.OBData.Delete[0].Symbol, asset.Futures)
|
||||
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.extractCurrencyPair(response.OBData.Update[0].Symbol, asset.Futures)
|
||||
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.extractCurrencyPair(response.OBData.Insert[0].Symbol, asset.Futures)
|
||||
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.extractCurrencyPair(response.TradeData[0].Symbol, asset.Futures)
|
||||
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.extractCurrencyPair(topics[len(topics)-1], asset.Futures)
|
||||
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.extractCurrencyPair(response.Ticker.Symbol, asset.Futures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
by.Websocket.DataHandler <- &ticker.Price{
|
||||
ExchangeName: by.Name,
|
||||
Last: response.Ticker.LastPrice,
|
||||
High: response.Ticker.HighPrice24h,
|
||||
Low: response.Ticker.LowPrice24h,
|
||||
Bid: response.Ticker.BidPrice,
|
||||
Ask: response.Ticker.AskPrice,
|
||||
Volume: response.Ticker.Volume24h,
|
||||
Close: response.Ticker.PrevPrice24h,
|
||||
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.extractCurrencyPair(response.Data.Delete[x].Symbol, asset.Futures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
by.Websocket.DataHandler <- &ticker.Price{
|
||||
ExchangeName: by.Name,
|
||||
Last: response.Data.Delete[x].LastPrice,
|
||||
High: response.Data.Delete[x].HighPrice24h,
|
||||
Low: response.Data.Delete[x].LowPrice24h,
|
||||
Bid: response.Data.Delete[x].BidPrice,
|
||||
Ask: response.Data.Delete[x].AskPrice,
|
||||
Volume: response.Data.Delete[x].Volume24h,
|
||||
Close: response.Data.Delete[x].PrevPrice24h,
|
||||
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.extractCurrencyPair(response.Data.Update[x].Symbol, asset.Futures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
by.Websocket.DataHandler <- &ticker.Price{
|
||||
ExchangeName: by.Name,
|
||||
Last: response.Data.Update[x].LastPrice,
|
||||
High: response.Data.Update[x].HighPrice24h,
|
||||
Low: response.Data.Update[x].LowPrice24h,
|
||||
Bid: response.Data.Update[x].BidPrice,
|
||||
Ask: response.Data.Update[x].AskPrice,
|
||||
Volume: response.Data.Update[x].Volume24h,
|
||||
Close: response.Data.Update[x].PrevPrice24h,
|
||||
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.extractCurrencyPair(response.Data.Insert[x].Symbol, asset.Futures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
by.Websocket.DataHandler <- &ticker.Price{
|
||||
ExchangeName: by.Name,
|
||||
Last: response.Data.Insert[x].LastPrice,
|
||||
High: response.Data.Insert[x].HighPrice24h,
|
||||
Low: response.Data.Insert[x].LowPrice24h,
|
||||
Bid: response.Data.Insert[x].BidPrice,
|
||||
Ask: response.Data.Insert[x].AskPrice,
|
||||
Volume: response.Data.Insert[x].Volume24h,
|
||||
Close: response.Data.Insert[x].PrevPrice24h,
|
||||
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.extractCurrencyPair(response.Data[i].Symbol, asset.Futures)
|
||||
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,
|
||||
Amount: response.Data[i].OrderQty,
|
||||
Side: oSide,
|
||||
Status: oStatus,
|
||||
Trades: []order.TradeHistory{
|
||||
{
|
||||
Price: response.Data[i].Price,
|
||||
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.extractCurrencyPair(response.Data[x].Symbol, asset.Futures)
|
||||
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,
|
||||
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,
|
||||
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.extractCurrencyPair(response.Data[x].Symbol, asset.Futures)
|
||||
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,
|
||||
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,
|
||||
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
|
||||
}
|
||||
654
exchanges/bybit/bybit_ws_ufutures.go
Normal file
654
exchanges/bybit/bybit_ws_ufutures.go
Normal file
@@ -0,0 +1,654 @@
|
||||
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 (
|
||||
wsUSDTMarginedPathPublic = "realtime_public"
|
||||
wsUSDTMarginedPathPrivate = "realtime_private"
|
||||
|
||||
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 common.Errors
|
||||
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 = append(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 common.Errors
|
||||
|
||||
for i := range channelsToUnsubscribe {
|
||||
var unSub WsFuturesReq
|
||||
unSub.Topic = unsubscribe
|
||||
|
||||
formattedPair, err := by.FormatExchangeCurrency(channelsToUnsubscribe[i].Currency, asset.USDTMarginedFutures)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
unSub.Args = append(unSub.Args, channelsToUnsubscribe[i].Channel+dot+formattedPair.String())
|
||||
err = by.Websocket.Conn.SendJSONMessage(unSub)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
by.Websocket.RemoveSuccessfulUnsubscriptions(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.extractCurrencyPair(response.Data.OBData[0].Symbol, asset.USDTMarginedFutures)
|
||||
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.extractCurrencyPair(response.OBData.Delete[0].Symbol, asset.USDTMarginedFutures)
|
||||
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.extractCurrencyPair(response.OBData.Update[0].Symbol, asset.USDTMarginedFutures)
|
||||
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.extractCurrencyPair(response.OBData.Insert[0].Symbol, asset.USDTMarginedFutures)
|
||||
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.extractCurrencyPair(response.TradeData[0].Symbol, asset.USDTMarginedFutures)
|
||||
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.extractCurrencyPair(topics[len(topics)-1], asset.USDTMarginedFutures)
|
||||
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.extractCurrencyPair(response.Ticker.Symbol, asset.USDTMarginedFutures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
by.Websocket.DataHandler <- &ticker.Price{
|
||||
ExchangeName: by.Name,
|
||||
Last: response.Ticker.LastPrice,
|
||||
High: response.Ticker.HighPrice24h,
|
||||
Low: response.Ticker.LowPrice24h,
|
||||
Bid: response.Ticker.BidPrice,
|
||||
Ask: response.Ticker.AskPrice,
|
||||
Volume: response.Ticker.Volume24h,
|
||||
Close: response.Ticker.PrevPrice24h,
|
||||
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.extractCurrencyPair(response.Data.Delete[x].Symbol, asset.USDTMarginedFutures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
by.Websocket.DataHandler <- &ticker.Price{
|
||||
ExchangeName: by.Name,
|
||||
Last: response.Data.Delete[x].LastPrice,
|
||||
High: response.Data.Delete[x].HighPrice24h,
|
||||
Low: response.Data.Delete[x].LowPrice24h,
|
||||
Bid: response.Data.Delete[x].BidPrice,
|
||||
Ask: response.Data.Delete[x].AskPrice,
|
||||
Volume: response.Data.Delete[x].Volume24h,
|
||||
Close: response.Data.Delete[x].PrevPrice24h,
|
||||
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.extractCurrencyPair(response.Data.Update[x].Symbol, asset.USDTMarginedFutures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
by.Websocket.DataHandler <- &ticker.Price{
|
||||
ExchangeName: by.Name,
|
||||
Last: response.Data.Update[x].LastPrice,
|
||||
High: response.Data.Update[x].HighPrice24h,
|
||||
Low: response.Data.Update[x].LowPrice24h,
|
||||
Bid: response.Data.Update[x].BidPrice,
|
||||
Ask: response.Data.Update[x].AskPrice,
|
||||
Volume: response.Data.Update[x].Volume24h,
|
||||
Close: response.Data.Update[x].PrevPrice24h,
|
||||
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.extractCurrencyPair(response.Data.Insert[x].Symbol, asset.USDTMarginedFutures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
by.Websocket.DataHandler <- &ticker.Price{
|
||||
ExchangeName: by.Name,
|
||||
Last: response.Data.Insert[x].LastPrice,
|
||||
High: response.Data.Insert[x].HighPrice24h,
|
||||
Low: response.Data.Insert[x].LowPrice24h,
|
||||
Bid: response.Data.Insert[x].BidPrice,
|
||||
Ask: response.Data.Insert[x].AskPrice,
|
||||
Volume: response.Data.Insert[x].Volume24h,
|
||||
Close: response.Data.Insert[x].PrevPrice24h,
|
||||
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.extractCurrencyPair(response.Data[i].Symbol, asset.USDTMarginedFutures)
|
||||
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,
|
||||
Amount: response.Data[i].OrderQty,
|
||||
Trades: []order.TradeHistory{
|
||||
{
|
||||
Price: response.Data[i].Price,
|
||||
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.extractCurrencyPair(response.Data[x].Symbol, asset.USDTMarginedFutures)
|
||||
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,
|
||||
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,
|
||||
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.extractCurrencyPair(response.Data[x].Symbol, asset.USDTMarginedFutures)
|
||||
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,
|
||||
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,
|
||||
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
|
||||
}
|
||||
965
exchanges/bybit/futures_type.go
Normal file
965
exchanges/bybit/futures_type.go
Normal file
@@ -0,0 +1,965 @@
|
||||
package bybit
|
||||
|
||||
import "time"
|
||||
|
||||
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 float64 `json:"price,string"`
|
||||
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 float64 `json:"open,string"`
|
||||
High float64 `json:"high,string"`
|
||||
Low float64 `json:"low,string"`
|
||||
Close float64 `json:"close,string"`
|
||||
Volume float64 `json:"volume,string"`
|
||||
TurnOver float64 `json:"turnover,string"`
|
||||
}
|
||||
|
||||
// SymbolPriceTicker stores ticker price stats
|
||||
type SymbolPriceTicker struct {
|
||||
Symbol string `json:"symbol"`
|
||||
BidPrice float64 `json:"bid_price,string"`
|
||||
AskPrice float64 `json:"ask_price,string"`
|
||||
LastPrice float64 `json:"last_price,string"`
|
||||
LastTickDirection string `json:"last_tick_direction"`
|
||||
Price24hAgo float64 `json:"prev_price_24h,string"`
|
||||
PricePcntChange24h float64 `json:"price_24h_pcnt,string"`
|
||||
HighPrice24h float64 `json:"high_price_24h,string"`
|
||||
LowPrice24h float64 `json:"low_price_24h,string"`
|
||||
Price1hAgo float64 `json:"prev_price_1h,string"`
|
||||
PricePcntChange1h float64 `json:"price_1h_pcnt,string"`
|
||||
MarkPrice float64 `json:"mark_price,string"`
|
||||
IndexPrice float64 `json:"index_price,string"`
|
||||
OpenInterest float64 `json:"open_interest"`
|
||||
OpenValue float64 `json:"open_value,string"`
|
||||
TotalTurnover float64 `json:"total_turnover,string"`
|
||||
Turnover24h float64 `json:"turnover_24h,string"`
|
||||
TotalVolume float64 `json:"total_volume"`
|
||||
Volume24h float64 `json:"volume_24h"`
|
||||
FundingRate float64 `json:"funding_rate,string"`
|
||||
PredictedFundingRate float64 `json:"predicted_funding_rate,string"`
|
||||
NextFundingTime string `json:"next_funding_time"`
|
||||
CountdownHour int64 `json:"countdown_hour"`
|
||||
DeliveryFeeRate string `json:"delivery_fee_rate"` // type is string because it comes as empty string in API response sometime
|
||||
PredictedDeliveryPrice string `json:"predicted_delivery_price"` // type is string because it comes as empty string in API response sometime
|
||||
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 float64 `json:"leverage_step,string"`
|
||||
} `json:"leverage_filter"`
|
||||
PriceFilter struct {
|
||||
MinPrice float64 `json:"min_price,string"`
|
||||
MaxPrice float64 `json:"max_price,string"`
|
||||
TickSize float64 `json:"tick_size,string"`
|
||||
} `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 float64 `json:"open,string"`
|
||||
High float64 `json:"high,string"`
|
||||
Low float64 `json:"low,string"`
|
||||
Close float64 `json:"close,string"`
|
||||
}
|
||||
|
||||
// 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 {
|
||||
Symbol string `json:"symbol"`
|
||||
Side string `json:"side"`
|
||||
Time int64 `json:"timestamp"`
|
||||
Value int64 `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 float64 `json:"leaves_value,string"`
|
||||
CumulativeQty float64 `json:"cum_exec_qty,string"`
|
||||
CumulativeValue float64 `json:"cum_exec_value,string"`
|
||||
CumulativeFee float64 `json:"cum_exec_fee,string"`
|
||||
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 float64 `json:"leaves_value,string"`
|
||||
CumulativeQty float64 `json:"cum_exec_qty,string"`
|
||||
CumulativeValue float64 `json:"cum_exec_value,string"`
|
||||
CumulativeFee float64 `json:"cum_exec_fee,string"`
|
||||
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 float64 `json:"base_price,string"`
|
||||
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 float64 `json:"maintain_margin,string"`
|
||||
StartingMargin float64 `json:"starting_margin,string"`
|
||||
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,string"`
|
||||
}
|
||||
|
||||
// FundingInfo stores funding information
|
||||
type FundingInfo struct {
|
||||
Symbol string `json:"symbol"`
|
||||
FundingRate float64 `json:"funding_rate,string"`
|
||||
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 float64 `json:"position_value,string"`
|
||||
EntryPrice float64 `json:"entry_price,string"`
|
||||
LiquidationPrice float64 `json:"liq_price,string"`
|
||||
BankruptcyPrice float64 `json:"bust_price,string"`
|
||||
Leverage float64 `json:"leverage,string"`
|
||||
PositionMargin float64 `json:"position_margin,string"`
|
||||
OccupiedClosingFee float64 `json:"occ_closing_fee,string"`
|
||||
RealisedPNL float64 `json:"realised_pnl,string"`
|
||||
AccumulatedRealisedPNL float64 `json:"cum_realised_pnl,string"`
|
||||
}
|
||||
|
||||
// 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 float64 `json:"take_profit,string"`
|
||||
StopLoss float64 `json:"stop_loss,string"`
|
||||
TrailingStop float64 `json:"trailing_stop,string"`
|
||||
}
|
||||
|
||||
// PositionResp stores position response
|
||||
type PositionResp struct {
|
||||
PositionDataWithStringParam
|
||||
PositionID int64 `json:"position_idx"`
|
||||
Mode int64 `json:"mode"`
|
||||
ID int64 `json:"id"`
|
||||
EffectiveLeverage float64 `json:"effective_leverage,string"`
|
||||
OccupiedFundingFee float64 `json:"occ_funding_fee,string"`
|
||||
PositionStatus string `json:"position_status"`
|
||||
CalculatedData string `json:"oc_calc_data"`
|
||||
OrderMargin float64 `json:"order_margin,string"`
|
||||
WalletBalance float64 `json:"wallet_balance,string"`
|
||||
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 float64 `json:"occ_funding_fee,string"`
|
||||
TakeProfit float64 `json:"take_profit,string"`
|
||||
StopLoss float64 `json:"stop_loss,string"`
|
||||
PositionStatus string `json:"position_status"`
|
||||
DeleverageIndicator int64 `json:"deleverage_indicator"`
|
||||
CalculatedData string `json:"oc_calc_data"`
|
||||
OrderMargin float64 `json:"order_margin,string"`
|
||||
WalletBalance float64 `json:"wallet_balance,string"`
|
||||
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 float64 `json:"exec_fee,string"`
|
||||
ExecutionPrice float64 `json:"exec_price,string"`
|
||||
ExecutionQty float64 `json:"exec_qty"`
|
||||
ExecutionType string `json:"exec_type"`
|
||||
ExecutionValue float64 `json:"exec_value,string"`
|
||||
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 float64 `json:"amount,string"`
|
||||
TxID string `json:"tx_id"`
|
||||
Address string `json:"address"`
|
||||
WalletBalance float64 `json:"wallet_balance,string"`
|
||||
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 float64 `json:"amount,string"`
|
||||
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 float64 `json:"price,string"`
|
||||
Size float64 `json:"size,string"`
|
||||
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 float64 `json:"takerFeeRate,string"`
|
||||
MakerFeeRate float64 `json:"makerFeeRate,string"`
|
||||
MinLeverage float64 `json:"minLeverage,string"`
|
||||
MaxLeverage float64 `json:"maxLeverage,string"`
|
||||
LeverageStep float64 `json:"leverageStep,string"`
|
||||
MinPrice float64 `json:"minPrice,string"`
|
||||
MaxPrice float64 `json:"maxPrice,string"`
|
||||
TickSize float64 `json:"tickSize,string"`
|
||||
MaxTradingQty float64 `json:"maxTradingQty,string"`
|
||||
MinTradingQty float64 `json:"minTradingQty,string"`
|
||||
QtyStep float64 `json:"qtyStep,string"`
|
||||
DeliveryTime bybitTimeMilliSecStr `json:"deliveryTime"`
|
||||
}
|
||||
|
||||
// USDCSymbol stores symbol data
|
||||
type USDCSymbol struct {
|
||||
Symbol string `json:"symbol"`
|
||||
NextFundingTime string `json:"nextFundingTime"`
|
||||
Bid float64 `json:"bid,string"`
|
||||
BidSize float64 `json:"bidSize,string"`
|
||||
Ask float64 `json:"ask,string"`
|
||||
AskSize float64 `json:"askSize,string"`
|
||||
LastPrice float64 `json:"lastPrice,string"`
|
||||
OpenInterest float64 `json:"openInterest,string"`
|
||||
IndexPrice float64 `json:"indexPrice,string"`
|
||||
MarkPrice float64 `json:"markPrice,string"`
|
||||
Change24h float64 `json:"change24h,string"`
|
||||
High24h float64 `json:"high24h,string"`
|
||||
Low24h float64 `json:"low24h,string"`
|
||||
Volume24h float64 `json:"volume24h,string"`
|
||||
Turnover24h float64 `json:"turnover24h,string"`
|
||||
TotalVolume float64 `json:"totalVolume,string"`
|
||||
TotalTurnover float64 `json:"totalTurnover,string"`
|
||||
FundingRate float64 `json:"fundingRate,string"`
|
||||
PredictedFundingRate float64 `json:"predictedFundingRate,string"`
|
||||
CountdownHour float64 `json:"countdownHour,string"`
|
||||
UnderlyingPrice string `json:"underlyingPrice"`
|
||||
}
|
||||
|
||||
// USDCKlineBase stores Kline Base
|
||||
type USDCKlineBase struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Period string `json:"period"`
|
||||
OpenTime bybitTimeSecStr `json:"openTime"`
|
||||
Open float64 `json:"open,string"`
|
||||
High float64 `json:"high,string"`
|
||||
Low float64 `json:"low,string"`
|
||||
Close float64 `json:"close,string"`
|
||||
}
|
||||
|
||||
// USDCKline stores kline data
|
||||
type USDCKline struct {
|
||||
USDCKlineBase
|
||||
Volume float64 `json:"volume,string"`
|
||||
Turnover float64 `json:"turnover,string"`
|
||||
}
|
||||
|
||||
// USDCOpenInterest stores open interest data
|
||||
type USDCOpenInterest struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Timestamp bybitTimeMilliSecStr `json:"timestamp"`
|
||||
OpenInterest float64 `json:"openInterest,string"`
|
||||
}
|
||||
|
||||
// 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 float64 `json:"orderPrice,string"`
|
||||
OrderQty float64 `json:"orderQty,string"`
|
||||
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 float64 `json:"orderPrice,string"`
|
||||
OrderQty float64 `json:"orderQty,string"`
|
||||
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 float64 `json:"qty,string"`
|
||||
Price float64 `json:"price,string"`
|
||||
TimeInForce string `json:"timeInForce"`
|
||||
TotalOrderValue float64 `json:"cumExecValue,string"`
|
||||
TotalFilledQty float64 `json:"cumExecQty,string"`
|
||||
TotalFee float64 `json:"cumExecFee,string"`
|
||||
InitialMargin string `json:"orderIM"`
|
||||
OrderStatus string `json:"orderStatus"`
|
||||
TakeProfit float64 `json:"takeProfit,string"`
|
||||
StopLoss float64 `json:"stopLoss,string"`
|
||||
TPTriggerBy string `json:"tpTriggerBy"`
|
||||
SLTriggerBy string `json:"slTriggerBy"`
|
||||
LastExecPrice float64 `json:"lastExecPrice"`
|
||||
BasePrice string `json:"basePrice"`
|
||||
TriggerPrice float64 `json:"triggerPrice,string"`
|
||||
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 float64 `json:"leavesQty,string"` // Est. unfilled order qty
|
||||
CashFlow string `json:"cashFlow"`
|
||||
RealisedPnl float64 `json:"realisedPnl,string"`
|
||||
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 float64 `json:"execPrice,string"`
|
||||
ExecQty float64 `json:"execQty,string"`
|
||||
ExecFee float64 `json:"execFee,string"`
|
||||
FeeRate float64 `json:"feeRate,string"`
|
||||
ExecType string `json:"execType"`
|
||||
ExecValue float64 `json:"execValue,string"`
|
||||
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 float64 `json:"qty,string"`
|
||||
Size float64 `json:"size,string"`
|
||||
TradePrice float64 `json:"tradePrice,string"`
|
||||
Funding float64 `json:"funding,string"`
|
||||
Fee float64 `json:"fee,string"`
|
||||
CashFlow string `json:"cashFlow"`
|
||||
Change float64 `json:"change,string"`
|
||||
WalletBalance float64 `json:"walletBalance,string"`
|
||||
FeeRate float64 `json:"feeRate,string"`
|
||||
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 float64 `json:"equity,string"`
|
||||
WalletBalance float64 `json:"walletBalance,string"`
|
||||
AvailableBalance float64 `json:"availableBalance,string"`
|
||||
AccountIM float64 `json:"accountIM,string"`
|
||||
AccountMM float64 `json:"accountMM,string"`
|
||||
TotalRPL float64 `json:"totalRPL,string"`
|
||||
TotalSessionUPL float64 `json:"totalSessionUPL,string"`
|
||||
TotalSessionRPL float64 `json:"totalSessionRPL,string"`
|
||||
}
|
||||
|
||||
// USDCAssetInfo stores USDC asset data
|
||||
type USDCAssetInfo struct {
|
||||
BaseCoin string `json:"baseCoin"`
|
||||
TotalDelta float64 `json:"totalDelta,string"`
|
||||
TotalGamma float64 `json:"totalGamma,string"`
|
||||
TotalVega float64 `json:"totalVega,string"`
|
||||
TotalTheta float64 `json:"totalTheta,string"`
|
||||
TotalRPL float64 `json:"totalRPL,string"`
|
||||
SessionUPL float64 `json:"sessionUPL,string"`
|
||||
SessionRPL float64 `json:"sessionRPL,string"`
|
||||
IM float64 `json:"im,string"`
|
||||
MM float64 `json:"mm,string"`
|
||||
}
|
||||
|
||||
// USDCPosition store USDC position data
|
||||
type USDCPosition struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Leverage float64 `json:"leverage,string"`
|
||||
ClosingFee float64 `json:"occClosingFee,string"`
|
||||
LiquidPrice string `json:"liqPrice"`
|
||||
Position float64 `json:"positionValue"`
|
||||
TakeProfit float64 `json:"takeProfit,string"`
|
||||
RiskID string `json:"riskId"`
|
||||
TrailingStop float64 `json:"trailingStop,string"`
|
||||
UnrealisedPnl float64 `json:"unrealisedPnl,string"`
|
||||
MarkPrice float64 `json:"markPrice,string"`
|
||||
CumRealisedPnl float64 `json:"cumRealisedPnl,string"`
|
||||
PositionMM float64 `json:"positionMM,string"`
|
||||
PositionIM float64 `json:"positionIM,string"`
|
||||
EntryPrice float64 `json:"entryPrice,string"`
|
||||
Size float64 `json:"size,string"`
|
||||
SessionRPL float64 `json:"sessionRPL,string"`
|
||||
SessionUPL float64 `json:"sessionUPL,string"`
|
||||
StopLoss float64 `json:"stopLoss,string"`
|
||||
OrderMargin float64 `json:"orderMargin,string"`
|
||||
SessionAvgPrice float64 `json:"sessionAvgPrice,string"`
|
||||
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 float64 `json:"size,string"`
|
||||
SessionAvgPrice float64 `json:"sessionAvgPrice,string"`
|
||||
MarkPrice float64 `json:"markPrice,string"`
|
||||
SessionRpl float64 `json:"sessionRpl,string"`
|
||||
}
|
||||
|
||||
// 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 float64 `json:"startingMargin,string"`
|
||||
MaintainMargin float64 `json:"maintainMargin,string"`
|
||||
IsLowestRisk bool `json:"isLowestRisk"`
|
||||
MaxLeverage float64 `json:"maxLeverage,string"`
|
||||
}
|
||||
|
||||
// USDCFundingInfo store USDC funding data
|
||||
type USDCFundingInfo struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Time bybitTimeMilliSecStr `json:"fundingRateTimestamp"`
|
||||
Rate float64 `json:"fundingRate,string"`
|
||||
}
|
||||
403
exchanges/bybit/ratelimit.go
Normal file
403
exchanges/bybit/ratelimit.go
Normal file
@@ -0,0 +1,403 @@
|
||||
package bybit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
const (
|
||||
spotInterval = time.Second
|
||||
spotRequestRate = 70
|
||||
futuresPublicInterval = time.Second
|
||||
futuresRequestRate = 50
|
||||
|
||||
spotPrivateRequestRate = 20
|
||||
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
|
||||
)
|
||||
|
||||
const (
|
||||
publicSpotRate request.EndpointLimit = iota
|
||||
publicFuturesRate
|
||||
privateSpotRate
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
// RateLimit implements the request.Limiter interface
|
||||
type RateLimit struct {
|
||||
SpotRate *rate.Limiter
|
||||
FuturesRate *rate.Limiter
|
||||
PrivateSpotRate *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
|
||||
}
|
||||
|
||||
// Limit executes rate limiting functionality for Binance
|
||||
func (r *RateLimit) Limit(ctx context.Context, f request.EndpointLimit) error {
|
||||
var limiter *rate.Limiter
|
||||
var tokens int
|
||||
switch f {
|
||||
case publicSpotRate:
|
||||
limiter, tokens = r.SpotRate, 1
|
||||
case privateSpotRate:
|
||||
limiter, tokens = r.PrivateSpotRate, 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
|
||||
|
||||
default:
|
||||
limiter, tokens = r.SpotRate, 1
|
||||
}
|
||||
|
||||
var finalDelay time.Duration
|
||||
var reserves = make([]*rate.Reservation, tokens)
|
||||
for i := 0; i < tokens; i++ {
|
||||
// Consume tokens 1 at a time as this avoids needing burst capacity in the limiter,
|
||||
// which would otherwise allow the rate limit to be exceeded over short periods
|
||||
reserves[i] = limiter.Reserve()
|
||||
finalDelay = limiter.Reserve().Delay()
|
||||
}
|
||||
|
||||
if dl, ok := ctx.Deadline(); ok && dl.Before(time.Now().Add(finalDelay)) {
|
||||
// Cancel all potential reservations to free up rate limiter if deadline
|
||||
// is exceeded.
|
||||
for x := range reserves {
|
||||
reserves[x].Cancel()
|
||||
}
|
||||
return fmt.Errorf("rate limit delay of %s will exceed deadline: %w",
|
||||
finalDelay,
|
||||
context.DeadlineExceeded)
|
||||
}
|
||||
|
||||
time.Sleep(finalDelay)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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(spotInterval, spotPrivateRequestRate),
|
||||
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),
|
||||
}
|
||||
}
|
||||
@@ -483,7 +483,7 @@ func (c *CoinbasePro) GetFundingHistory(_ context.Context) ([]exchange.FundHisto
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (c *CoinbasePro) GetWithdrawalsHistory(_ context.Context, _ currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (c *CoinbasePro) GetWithdrawalsHistory(_ context.Context, _ currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -540,7 +540,7 @@ func (c *COINUT) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory,
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (c *COINUT) GetWithdrawalsHistory(_ context.Context, _ currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (c *COINUT) GetWithdrawalsHistory(_ context.Context, _ currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -1175,6 +1175,8 @@ func (u URL) String() string {
|
||||
return restCoinMarginedFuturesURL
|
||||
case RestFutures:
|
||||
return restFuturesURL
|
||||
case RestUSDCMargined:
|
||||
return restUSDCMarginedFuturesURL
|
||||
case RestSandbox:
|
||||
return restSandboxURL
|
||||
case RestSwap:
|
||||
@@ -1209,6 +1211,8 @@ func getURLTypeFromString(ep string) (URL, error) {
|
||||
return RestCoinMargined, nil
|
||||
case restFuturesURL:
|
||||
return RestFutures, nil
|
||||
case restUSDCMarginedFuturesURL:
|
||||
return RestUSDCMargined, nil
|
||||
case restSandboxURL:
|
||||
return RestSandbox, nil
|
||||
case restSwapURL:
|
||||
|
||||
@@ -2008,6 +2008,9 @@ func TestString(t *testing.T) {
|
||||
if RestFutures.String() != "RestFuturesURL" {
|
||||
t.Errorf("invalid string conversion")
|
||||
}
|
||||
if RestUSDCMargined.String() != "RestUSDCMarginedFuturesURL" {
|
||||
t.Errorf("invalid string conversion")
|
||||
}
|
||||
if RestSandbox.String() != "RestSandboxURL" {
|
||||
t.Errorf("invalid string conversion")
|
||||
}
|
||||
@@ -2202,6 +2205,7 @@ func TestGetGetURLTypeFromString(t *testing.T) {
|
||||
{Endpoint: "RestUSDTMarginedFuturesURL", Expected: RestUSDTMargined},
|
||||
{Endpoint: "RestCoinMarginedFuturesURL", Expected: RestCoinMargined},
|
||||
{Endpoint: "RestFuturesURL", Expected: RestFutures},
|
||||
{Endpoint: "RestUSDCMarginedFuturesURL", Expected: RestUSDCMargined},
|
||||
{Endpoint: "RestSandboxURL", Expected: RestSandbox},
|
||||
{Endpoint: "RestSwapURL", Expected: RestSwap},
|
||||
{Endpoint: "WebsocketSpotURL", Expected: WebsocketSpot},
|
||||
|
||||
@@ -241,6 +241,7 @@ const (
|
||||
RestUSDTMargined
|
||||
RestCoinMargined
|
||||
RestFutures
|
||||
RestUSDCMargined
|
||||
RestSwap
|
||||
RestSandbox
|
||||
WebsocketSpot
|
||||
@@ -254,6 +255,7 @@ const (
|
||||
restSpotSupplementaryURL = "RestSpotSupplementaryURL"
|
||||
restUSDTMarginedFuturesURL = "RestUSDTMarginedFuturesURL"
|
||||
restCoinMarginedFuturesURL = "RestCoinMarginedFuturesURL"
|
||||
restUSDCMarginedFuturesURL = "RestUSDCMarginedFuturesURL"
|
||||
restFuturesURL = "RestFuturesURL"
|
||||
restSandboxURL = "RestSandboxURL"
|
||||
restSwapURL = "RestSwapURL"
|
||||
@@ -270,6 +272,7 @@ var keyURLs = []URL{RestSpot,
|
||||
RestUSDTMargined,
|
||||
RestCoinMargined,
|
||||
RestFutures,
|
||||
RestUSDCMargined,
|
||||
RestSwap,
|
||||
RestSandbox,
|
||||
WebsocketSpot,
|
||||
|
||||
@@ -415,7 +415,7 @@ func (e *EXMO) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, e
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (e *EXMO) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (e *EXMO) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -571,7 +571,7 @@ func (f *FTX) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, er
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (f *FTX) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (f *FTX) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -458,7 +458,7 @@ func (g *Gateio) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory,
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (g *Gateio) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (g *Gateio) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -467,7 +467,7 @@ func (g *Gemini) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory,
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (g *Gemini) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (g *Gemini) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -486,7 +486,7 @@ func (h *HitBTC) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory,
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (h *HitBTC) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (h *HitBTC) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -838,7 +838,7 @@ func (h *HUOBI) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory,
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (h *HUOBI) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (h *HUOBI) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ type IBotExchange interface {
|
||||
|
||||
GetDepositAddress(ctx context.Context, cryptocurrency currency.Code, accountID, chain string) (*deposit.Address, error)
|
||||
GetAvailableTransferChains(ctx context.Context, cryptocurrency currency.Code) ([]string, error)
|
||||
GetWithdrawalsHistory(ctx context.Context, code currency.Code) ([]WithdrawalHistory, error)
|
||||
GetWithdrawalsHistory(ctx context.Context, code currency.Code, a asset.Item) ([]WithdrawalHistory, error)
|
||||
WithdrawCryptocurrencyFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error)
|
||||
WithdrawFiatFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error)
|
||||
WithdrawFiatFundsToInternationalBank(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error)
|
||||
|
||||
@@ -334,7 +334,7 @@ func (i *ItBit) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory,
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (i *ItBit) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (i *ItBit) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -660,7 +660,7 @@ func (k *Kraken) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory,
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (k *Kraken) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (k *Kraken) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
withdrawals, err := k.WithdrawStatus(ctx, c, "")
|
||||
for i := range withdrawals {
|
||||
resp = append(resp, exchange.WithdrawalHistory{
|
||||
|
||||
@@ -384,7 +384,7 @@ func (l *Lbank) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory,
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (l *Lbank) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (l *Lbank) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -332,7 +332,7 @@ func (l *LocalBitcoins) GetFundingHistory(ctx context.Context) ([]exchange.FundH
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (l *LocalBitcoins) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (l *LocalBitcoins) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -500,7 +500,7 @@ func (o *OKGroup) WithdrawFiatFundsToInternationalBank(_ context.Context, _ *wit
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (o *OKGroup) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (o *OKGroup) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -459,7 +459,7 @@ func (p *Poloniex) GetFundingHistory(ctx context.Context) ([]exchange.FundHistor
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (p *Poloniex) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (p *Poloniex) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ func (c *CustomEx) GetOrderHistory(ctx context.Context, getOrdersRequest *order.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *CustomEx) GetWithdrawalsHistory(ctx context.Context, code currency.Code) ([]exchange.WithdrawalHistory, error) {
|
||||
func (c *CustomEx) GetWithdrawalsHistory(ctx context.Context, code currency.Code, a asset.Item) ([]exchange.WithdrawalHistory, error) {
|
||||
return []exchange.WithdrawalHistory{}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ var Exchanges = []string{
|
||||
"bittrex",
|
||||
"btc markets",
|
||||
"btse",
|
||||
"bybit",
|
||||
"coinbasepro",
|
||||
"coinut",
|
||||
"exmo",
|
||||
|
||||
@@ -69,6 +69,7 @@ _b in this context is an `IBotExchange` implemented struct_
|
||||
| Bittrex | Yes | Yes | No |
|
||||
| BTCMarkets | Yes | Yes | No |
|
||||
| BTSE | Yes | Yes | No |
|
||||
| Bybit | Yes | Yes | Yes |
|
||||
| CoinbasePro | Yes | Yes | No|
|
||||
| COINUT | Yes | Yes | No |
|
||||
| Exmo | Yes | NA | No |
|
||||
|
||||
@@ -360,7 +360,7 @@ func (y *Yobit) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory,
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (y *Yobit) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (y *Yobit) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -428,7 +428,7 @@ func (z *ZB) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, err
|
||||
}
|
||||
|
||||
// GetWithdrawalsHistory returns previous withdrawals data
|
||||
func (z *ZB) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) {
|
||||
func (z *ZB) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user