exchanges: Kucoin Update (#1438)

* types and orderbook handling fix

* Minor types and endpoints update

* Minor fixes on Kucoin

* Add benchmarking test

* Unit tests update and minor endpoints update

* Adding updates and newly added endpoints

* Add and correct funding, withdrawal, and deposit endpoints

* linter and codespell fix

* Adding and correcting spot trading endpoints

* Completed Spot HF endpoints

* Minor fix

* Added OCO and Margin HF trade and other endpoints

* Adding missing endpoints

* Updating Isolated margin, margin v3, and v3market lending endpoints

* minor codespell fix

* Completed adding and fixing futures endpoints

* wrapper update and fix unit tests

* Updating endpoint ratelimits

* Complete ratelimiter setup and endpoint functions update

* Unit test configuration and update

* fix linter issue

* Added a ratelimiter test and heavy update on unit tests

* Adding websocket update based on ChangeLog

* Added newly added Earn General, Kucoin Earn and Staking endpoints

* Added VIP lending endpoints

* Minor linter and endpoints fix

* Added unit tests, publicised functions, and minor updates

* Update on wrapper funcs, unit tests, and other methods

* Enexport exchange specific websocket methods

* remove deprecated topic

* Update wrapper based on Type, add and fix unit tests

* Added a margin configuration endpoint and unit test

* Update methods, types, and unit tests

* Update error declaration and handling unit tests

* Update method parameters and error handling

* Updating unit tests and error handling

* Update methods arguments, added and update unit tests

* Fix unit tests and wrapper methods

* Resolving unit test issues and fix faulty endpoints

* Fix on unit tests and working on passphrase errors

* Minor fixed on websocket and endpoint url

* comment and wrapper filters issue fix

* Unit tests and other minor updates

* Update wrapper functions, endpoint methods, and unit tests

* change require to change on two unit tests

* Update unit tests, types, and endpoints

* Refine and update wrapper tempo for minor adjustments

* Remove code that enabled logging

* Update wrapper functions, missing endpoints, response and parameter values, and unit tests

* removed High-frequency orders from wrapper functions, and updated unit tests and types

* Added missing fields and minor update on wrapper

* Update types

* Update tests and websocket channels

* Update unit tests and methods error returns
This commit is contained in:
Samuael A.
2024-09-13 12:52:39 +03:00
committed by GitHub
parent b461c32a5e
commit d94b8af3e1
12 changed files with 7828 additions and 3382 deletions

1
.gitignore vendored
View File

@@ -50,3 +50,4 @@ __debug_bin
# Coverage reports
coverage.txt
wrapperconfig.json
.history/

View File

@@ -64,6 +64,7 @@ var (
ErrStartEqualsEnd = errors.New("start date equals end date")
ErrStartAfterTimeNow = errors.New("start date is after current time")
ErrNilPointer = errors.New("nil pointer")
ErrEmptyParams = errors.New("empty parameters")
ErrCannotCalculateOffline = errors.New("cannot calculate offline, unsupported")
ErrNoResponse = errors.New("no response")
ErrTypeAssertFailure = errors.New("type assert failure")

File diff suppressed because it is too large Load Diff

View File

@@ -16,89 +16,43 @@ import (
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/types"
)
const (
kucoinFuturesAPIURL = "https://api-futures.kucoin.com/api"
kucoinWebsocketURL = "wss://ws-api.kucoin.com/endpoint"
// Public market endpoints
kucoinFuturesOpenContracts = "/v1/contracts/active"
kucoinFuturesContract = "/v1/contracts/"
kucoinFuturesTicker = "/v1/ticker"
kucoinFuturesFullOrderbook = "/v1/level2/snapshot"
kucoinFuturesPartOrderbook20 = "/v1/level2/depth20"
kucoinFuturesPartOrderbook100 = "/v1/level2/depth100"
kucoinFuturesTradeHistory = "/v1/trade/history"
kucoinFuturesInterestRate = "/v1/interest/query"
kucoinFuturesIndex = "/v1/index/query"
kucoinFuturesMarkPrice = "/v1/mark-price/%s/current"
kucoinFuturesPremiumIndex = "/v1/premium/query"
kucoinFuturesFundingRate = "/v1/funding-rate/%s/current"
kucoinFuturesServerTime = "/v1/timestamp"
kucoinFuturesServiceStatus = "/v1/status"
kucoinFuturesKline = "/v1/kline/query"
// Authenticated endpoints
kucoinFuturesOrder = "/v1/orders"
kucoinFuturesCancelOrder = "/v1/orders/"
kucoinFuturesStopOrder = "/v1/stopOrders"
kucoinFuturesRecentCompletedOrder = "/v1/recentDoneOrders"
kucoinFuturesGetOrderDetails = "/v1/orders/"
kucoinFuturesGetOrderDetailsByClientID = "/v1/orders/byClientOid"
kucoinFuturesFills = "/v1/fills"
kucoinFuturesRecentFills = "/v1/recentFills"
kucoinFuturesOpenOrderStats = "/v1/openOrderStatistics"
kucoinFuturesPosition = "/v1/position"
kucoinFuturesPositionList = "/v1/positions"
kucoinFuturesSetAutoDeposit = "/v1/position/margin/auto-deposit-status"
kucoinFuturesAddMargin = "/v1/position/margin/deposit-margin"
kucoinFuturesRiskLimitLevel = "/v1/contracts/risk-limit/"
kucoinFuturesUpdateRiskLmitLevel = "/v1/position/risk-limit-level/change"
kucoinFuturesFundingHistory = "/v1/funding-history"
kucoinFuturesAccountOverview = "/v1/account-overview"
kucoinFuturesTransactionHistory = "/v1/transaction-history"
kucoinFuturesSubAccountAPI = "/v1/sub/api-key"
kucoinFuturesDepositAddress = "/v1/deposit-address"
kucoinFuturesDepositsList = "/v1/deposit-list"
kucoinFuturesWithdrawalLimit = "/v1/withdrawals/quotas"
kucoinFuturesWithdrawalList = "/v1/withdrawal-list"
kucoinFuturesCancelWithdrawal = "/v1/withdrawals/"
kucoinFuturesTransferFundtoMainAccount = "/v3/transfer-out"
kucoinFuturesTransferFundtoFuturesAccount = "/v1/transfer-in"
kucoinFuturesTransferOutList = "/v1/transfer-list"
kucoinFuturesCancelTransferOut = "/v1/cancel/transfer-out"
kucoinFuturesOrder = "/v1/orders"
kucoinFuturesStopOrder = "/v1/stopOrders"
)
// GetFuturesOpenContracts gets all open futures contract with its details
func (ku *Kucoin) GetFuturesOpenContracts(ctx context.Context) ([]Contract, error) {
var resp []Contract
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, kucoinFuturesOpenContracts, &resp)
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, futuresOpenContractsEPL, "/v1/contracts/active", &resp)
}
// GetFuturesContract get contract details
func (ku *Kucoin) GetFuturesContract(ctx context.Context, symbol string) (*Contract, error) {
if symbol == "" {
return nil, errors.New("symbol can't be empty")
return nil, currency.ErrSymbolStringEmpty
}
var resp *Contract
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, kucoinFuturesContract+symbol, &resp)
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, futuresContractEPL, "/v1/contracts/"+symbol, &resp)
}
// GetFuturesTicker get real time ticker
func (ku *Kucoin) GetFuturesTicker(ctx context.Context, symbol string) (*FuturesTicker, error) {
if symbol == "" {
return nil, errors.New("symbol can't be empty")
return nil, currency.ErrSymbolStringEmpty
}
params := url.Values{}
params.Set("symbol", symbol)
var resp *FuturesTicker
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, common.EncodeURLValues(kucoinFuturesTicker, params), &resp)
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, futuresTickerEPL, "/v1/ticker?symbol="+symbol, &resp)
}
// GetFuturesTickers does n * REST requests based on enabled pairs of the futures asset type
@@ -162,12 +116,12 @@ func (ku *Kucoin) GetFuturesTickers(ctx context.Context) ([]*ticker.Price, error
// GetFuturesOrderbook gets full orderbook for a specified symbol
func (ku *Kucoin) GetFuturesOrderbook(ctx context.Context, symbol string) (*Orderbook, error) {
if symbol == "" {
return nil, errors.New("symbol can't be empty")
return nil, currency.ErrSymbolStringEmpty
}
params := url.Values{}
params.Set("symbol", symbol)
var o futuresOrderbookResponse
err := ku.SendHTTPRequest(ctx, exchange.RestFutures, futuresRetrieveFullOrderbookLevel2EPL, common.EncodeURLValues(kucoinFuturesFullOrderbook, params), &o)
err := ku.SendHTTPRequest(ctx, exchange.RestFutures, futuresOrderbookEPL, common.EncodeURLValues("/v1/level2/snapshot", params), &o)
if err != nil {
return nil, err
}
@@ -177,12 +131,12 @@ func (ku *Kucoin) GetFuturesOrderbook(ctx context.Context, symbol string) (*Orde
// GetFuturesPartOrderbook20 gets orderbook for a specified symbol with depth 20
func (ku *Kucoin) GetFuturesPartOrderbook20(ctx context.Context, symbol string) (*Orderbook, error) {
if symbol == "" {
return nil, errors.New("symbol can't be empty")
return nil, currency.ErrSymbolStringEmpty
}
params := url.Values{}
params.Set("symbol", symbol)
var o futuresOrderbookResponse
err := ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, common.EncodeURLValues(kucoinFuturesPartOrderbook20, params), &o)
err := ku.SendHTTPRequest(ctx, exchange.RestFutures, futuresPartOrderbookDepth20EPL, common.EncodeURLValues("/v1/level2/depth20", params), &o)
if err != nil {
return nil, err
}
@@ -192,12 +146,12 @@ func (ku *Kucoin) GetFuturesPartOrderbook20(ctx context.Context, symbol string)
// GetFuturesPartOrderbook100 gets orderbook for a specified symbol with depth 100
func (ku *Kucoin) GetFuturesPartOrderbook100(ctx context.Context, symbol string) (*Orderbook, error) {
if symbol == "" {
return nil, errors.New("symbol can't be empty")
return nil, currency.ErrSymbolStringEmpty
}
params := url.Values{}
params.Set("symbol", symbol)
var o futuresOrderbookResponse
err := ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, common.EncodeURLValues(kucoinFuturesPartOrderbook100, params), &o)
err := ku.SendHTTPRequest(ctx, exchange.RestFutures, futuresPartOrderbookDepth100EPL, common.EncodeURLValues("/v1/level2/depth100", params), &o)
if err != nil {
return nil, err
}
@@ -207,22 +161,19 @@ func (ku *Kucoin) GetFuturesPartOrderbook100(ctx context.Context, symbol string)
// GetFuturesTradeHistory get last 100 trades for symbol
func (ku *Kucoin) GetFuturesTradeHistory(ctx context.Context, symbol string) ([]FuturesTrade, error) {
if symbol == "" {
return nil, errors.New("symbol can't be empty")
return nil, currency.ErrSymbolStringEmpty
}
params := url.Values{}
params.Set("symbol", symbol)
var resp []FuturesTrade
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, common.EncodeURLValues(kucoinFuturesTradeHistory, params), &resp)
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, futuresTransactionHistoryEPL, "/v1/trade/history?symbol="+symbol, &resp)
}
// GetFuturesInterestRate get interest rate
func (ku *Kucoin) GetFuturesInterestRate(ctx context.Context, symbol string, startAt, endAt time.Time, reverse, forward bool, offset, maxCount int64) (*FundingInterestRateResponse, error) {
if symbol == "" {
return nil, errors.New("symbol can't be empty")
return nil, currency.ErrSymbolStringEmpty
}
params := url.Values{}
params.Set("symbol", symbol)
if !startAt.IsZero() {
params.Set("startAt", strconv.FormatInt(startAt.UnixMilli(), 10))
}
@@ -238,13 +189,13 @@ func (ku *Kucoin) GetFuturesInterestRate(ctx context.Context, symbol string, sta
params.Set("maxCount", strconv.FormatInt(maxCount, 10))
}
var resp *FundingInterestRateResponse
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, common.EncodeURLValues(kucoinFuturesInterestRate, params), &resp)
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, futuresInterestRateEPL, common.EncodeURLValues("/v1/interest/query", params), &resp)
}
// GetFuturesIndexList retrieves futures index information for a symbol
func (ku *Kucoin) GetFuturesIndexList(ctx context.Context, symbol string, startAt, endAt time.Time, reverse, forward bool, offset, maxCount int64) (*FuturesIndexResponse, error) {
if symbol == "" {
return nil, errors.New("symbol can't be empty")
return nil, currency.ErrSymbolStringEmpty
}
params := url.Values{}
params.Set("symbol", symbol)
@@ -263,22 +214,22 @@ func (ku *Kucoin) GetFuturesIndexList(ctx context.Context, symbol string, startA
params.Set("maxCount", strconv.FormatInt(maxCount, 10))
}
var resp *FuturesIndexResponse
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, common.EncodeURLValues(kucoinFuturesIndex, params), &resp)
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, futuresIndexListEPL, common.EncodeURLValues("/v1/index/query", params), &resp)
}
// GetFuturesCurrentMarkPrice get current mark price
func (ku *Kucoin) GetFuturesCurrentMarkPrice(ctx context.Context, symbol string) (*FuturesMarkPrice, error) {
if symbol == "" {
return nil, errors.New("symbol can't be empty")
return nil, currency.ErrSymbolStringEmpty
}
var resp *FuturesMarkPrice
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, fmt.Sprintf(kucoinFuturesMarkPrice, symbol), &resp)
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, futuresCurrentMarkPriceEPL, "/v1/mark-price/"+symbol+"/current", &resp)
}
// GetFuturesPremiumIndex get premium index
func (ku *Kucoin) GetFuturesPremiumIndex(ctx context.Context, symbol string, startAt, endAt time.Time, reverse, forward bool, offset, maxCount int64) (*FuturesInterestRateResponse, error) {
if symbol == "" {
return nil, errors.New("symbol can't be empty")
return nil, currency.ErrSymbolStringEmpty
}
params := url.Values{}
params.Set("symbol", symbol)
@@ -297,16 +248,39 @@ func (ku *Kucoin) GetFuturesPremiumIndex(ctx context.Context, symbol string, sta
params.Set("maxCount", strconv.FormatInt(maxCount, 10))
}
var resp *FuturesInterestRateResponse
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, common.EncodeURLValues(kucoinFuturesPremiumIndex, params), &resp)
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, futuresPremiumIndexEPL, common.EncodeURLValues("/v1/premium/query", params), &resp)
}
// Get24HourFuturesTransactionVolume retrieves a 24 hour transaction volume
func (ku *Kucoin) Get24HourFuturesTransactionVolume(ctx context.Context) (*TransactionVolume, error) {
var resp *TransactionVolume
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresTransactionVolumeEPL, http.MethodGet, "/v1/trade-statistics", nil, &resp)
}
// GetFuturesCurrentFundingRate get current funding rate
func (ku *Kucoin) GetFuturesCurrentFundingRate(ctx context.Context, symbol string) (*FuturesFundingRate, error) {
if symbol == "" {
return nil, errors.New("symbol can't be empty")
return nil, currency.ErrSymbolStringEmpty
}
var resp *FuturesFundingRate
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, fmt.Sprintf(kucoinFuturesFundingRate, symbol), &resp)
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, futuresCurrentFundingRateEPL, "/v1/funding-rate/"+symbol+"/current", &resp)
}
// GetPublicFundingRate query the funding rate at each settlement time point within a certain time range of the corresponding contract
func (ku *Kucoin) GetPublicFundingRate(ctx context.Context, symbol string, from, to time.Time) ([]FundingHistoryItem, error) {
if symbol == "" {
return nil, currency.ErrSymbolStringEmpty
}
err := common.StartEndTimeCheck(from, to)
if err != nil {
return nil, err
}
params := url.Values{}
params.Set("symbol", symbol)
params.Set("from", strconv.FormatInt(from.UnixMilli(), 10))
params.Set("to", strconv.FormatInt(to.UnixMilli(), 10))
var resp []FundingHistoryItem
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, futuresPublicFundingRateEPL, common.EncodeURLValues("/v1/contract/funding-rates", params), &resp)
}
// GetFuturesServerTime get server time
@@ -315,7 +289,7 @@ func (ku *Kucoin) GetFuturesServerTime(ctx context.Context) (time.Time, error) {
Data convert.ExchangeTime `json:"data"`
Error
}{}
err := ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, kucoinFuturesServerTime, &resp)
err := ku.SendHTTPRequest(ctx, exchange.RestFutures, futuresServerTimeEPL, "/v1/timestamp", &resp)
if err != nil {
return time.Time{}, err
}
@@ -325,23 +299,23 @@ func (ku *Kucoin) GetFuturesServerTime(ctx context.Context) (time.Time, error) {
// GetFuturesServiceStatus get service status
func (ku *Kucoin) GetFuturesServiceStatus(ctx context.Context) (*FuturesServiceStatus, error) {
var resp *FuturesServiceStatus
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, kucoinFuturesServiceStatus, &resp)
return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, futuresServiceStatusEPL, "/v1/status", &resp)
}
// GetFuturesKline get contract's kline data
func (ku *Kucoin) GetFuturesKline(ctx context.Context, granularity int64, symbol string, from, to time.Time) ([]FuturesKline, error) {
if granularity == 0 {
return nil, errors.New("granularity can not be empty")
return nil, kline.ErrInvalidInterval
}
if !slices.Contains(validGranularity, strconv.FormatInt(granularity, 10)) {
return nil, errors.New("invalid granularity")
return nil, fmt.Errorf("%w, invalid granularity", kline.ErrUnsupportedInterval)
}
if symbol == "" {
return nil, currency.ErrSymbolStringEmpty
}
params := url.Values{}
// The granularity (granularity parameter of K-line) represents the number of minutes, the available granularity scope is: 1,5,15,30,60,120,240,480,720,1440,10080. Requests beyond the above range will be rejected.
// The granularity (granularity parameter of K-line) represents the number of minutes, the available granularity scope is: 1,5,15,30,60,120,240,480,720,1440,10080. Requests beyond the above range will be rejected
params.Set("granularity", strconv.FormatInt(granularity, 10))
if symbol == "" {
return nil, errors.New("symbol can't be empty")
}
params.Set("symbol", symbol)
if !from.IsZero() {
params.Set("from", strconv.FormatInt(from.UnixMilli(), 10))
@@ -350,7 +324,7 @@ func (ku *Kucoin) GetFuturesKline(ctx context.Context, granularity int64, symbol
params.Set("to", strconv.FormatInt(to.UnixMilli(), 10))
}
var resp [][6]float64
err := ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, common.EncodeURLValues(kucoinFuturesKline, params), &resp)
err := ku.SendHTTPRequest(ctx, exchange.RestFutures, futuresKlineEPL, common.EncodeURLValues("/v1/kline/query", params), &resp)
if err != nil {
return nil, err
}
@@ -370,43 +344,9 @@ func (ku *Kucoin) GetFuturesKline(ctx context.Context, granularity int64, symbol
// PostFuturesOrder used to place two types of futures orders: limit and market
func (ku *Kucoin) PostFuturesOrder(ctx context.Context, arg *FuturesOrderParam) (string, error) {
if arg.Leverage < 0.01 {
return "", fmt.Errorf("%w must be greater than 0.01", errInvalidLeverage)
}
if arg.ClientOrderID == "" {
return "", errInvalidClientOrderID
}
if arg.Side == "" {
return "", fmt.Errorf("%w, empty order side", order.ErrSideIsInvalid)
}
if arg.Symbol.IsEmpty() {
return "", currency.ErrCurrencyPairEmpty
}
if arg.Stop != "" {
if arg.StopPriceType == "" {
return "", errInvalidStopPriceType
}
if arg.StopPrice <= 0 {
return "", fmt.Errorf("%w, stopPrice is required", errInvalidPrice)
}
}
switch arg.OrderType {
case "limit", "":
if arg.Price <= 0 {
return "", fmt.Errorf("%w %f", errInvalidPrice, arg.Price)
}
if arg.Size <= 0 {
return "", fmt.Errorf("%w, must be non-zero positive value", errInvalidSize)
}
if arg.VisibleSize < 0 {
return "", fmt.Errorf("%w, visible size must be non-zero positive value", errInvalidSize)
}
case "market":
if arg.Size <= 0 {
return "", fmt.Errorf("%w, market size must be > 0", errInvalidSize)
}
default:
return "", fmt.Errorf("%w, order type= %s", order.ErrTypeIsInvalid, arg.OrderType)
err := ku.FillFuturesPostOrderArgumentFilter(arg)
if err != nil {
return "", err
}
resp := struct {
OrderID string `json:"orderId"`
@@ -414,20 +354,110 @@ func (ku *Kucoin) PostFuturesOrder(ctx context.Context, arg *FuturesOrderParam)
return resp.OrderID, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresPlaceOrderEPL, http.MethodPost, kucoinFuturesOrder, &arg, &resp)
}
// CancelFuturesOrder used to cancel single order previously placed including a stop order
func (ku *Kucoin) CancelFuturesOrder(ctx context.Context, orderID string) ([]string, error) {
// PostFuturesOrderTest a test endpoint to place a single futures order
func (ku *Kucoin) PostFuturesOrderTest(ctx context.Context, arg *FuturesOrderParam) (string, error) {
err := ku.FillFuturesPostOrderArgumentFilter(arg)
if err != nil {
return "", err
}
resp := struct {
OrderID string `json:"orderId"`
}{}
return resp.OrderID, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresPlaceOrderEPL, http.MethodPost, kucoinFuturesOrder+"/test", &arg, &resp)
}
// FillFuturesPostOrderArgumentFilter verifies futures order request parameters
func (ku *Kucoin) FillFuturesPostOrderArgumentFilter(arg *FuturesOrderParam) error {
if *arg == (FuturesOrderParam{}) {
return common.ErrNilPointer
}
if arg.Leverage <= 0 {
return errInvalidLeverage
}
if arg.ClientOrderID == "" {
return order.ErrClientOrderIDMustBeSet
}
if arg.Side == "" {
return fmt.Errorf("%w, empty order side", order.ErrSideIsInvalid)
}
if arg.Symbol.IsEmpty() {
return currency.ErrCurrencyPairEmpty
}
if arg.Stop != "" {
if arg.StopPriceType == "" {
return errInvalidStopPriceType
}
if arg.StopPrice <= 0 {
return fmt.Errorf("%w, stopPrice is required", order.ErrPriceBelowMin)
}
}
switch arg.OrderType {
case "limit", "":
if arg.Price <= 0 {
return fmt.Errorf("%w %f", order.ErrPriceBelowMin, arg.Price)
}
if arg.Size <= 0 {
return fmt.Errorf("%w, must be non-zero positive value", order.ErrAmountBelowMin)
}
if arg.VisibleSize < 0 {
return fmt.Errorf("%w, visible size must be non-zero positive value", order.ErrAmountBelowMin)
}
case "market":
if arg.Size <= 0 {
return fmt.Errorf("%w, market size must be > 0", order.ErrAmountBelowMin)
}
default:
return fmt.Errorf("%w, order type= %s", order.ErrTypeIsInvalid, arg.OrderType)
}
return nil
}
// PlaceMultipleFuturesOrders used to place multiple futures orders
// The maximum limit orders for a single contract is 100 per account, and the maximum stop orders for a single contract is 50 per account
func (ku *Kucoin) PlaceMultipleFuturesOrders(ctx context.Context, args []FuturesOrderParam) ([]FuturesOrderRespItem, error) {
if len(args) == 0 {
return nil, fmt.Errorf("%w, not order to place", common.ErrEmptyParams)
}
var err error
for x := range args {
err = ku.FillFuturesPostOrderArgumentFilter(&args[x])
if err != nil {
return nil, err
}
}
var resp []FuturesOrderRespItem
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, multipleFuturesOrdersEPL, http.MethodPost, "/v1/orders/multi", args, &resp)
}
// CancelFuturesOrderByOrderID used to cancel single order previously placed including a stop order
func (ku *Kucoin) CancelFuturesOrderByOrderID(ctx context.Context, orderID string) ([]string, error) {
return ku.cancelFuturesOrderByID(ctx, orderID, "/v1/orders/", "")
}
// CancelFuturesOrderByClientOrderID cancels a futures order by using client order ID
func (ku *Kucoin) CancelFuturesOrderByClientOrderID(ctx context.Context, symbol, clientOrderID string) ([]string, error) {
if symbol == "" {
return nil, currency.ErrSymbolStringEmpty
}
return ku.cancelFuturesOrderByID(ctx, clientOrderID, "/v1/orders/client-order/", symbol)
}
func (ku *Kucoin) cancelFuturesOrderByID(ctx context.Context, id, path, symbol string) ([]string, error) {
if id == "" {
return nil, order.ErrOrderIDNotSet
}
params := url.Values{}
if symbol != "" {
params.Set("symbol", symbol)
}
resp := struct {
CancelledOrderIDs []string `json:"cancelledOrderIds"`
}{}
if orderID == "" {
return resp.CancelledOrderIDs, errors.New("orderID can't be empty")
}
return resp.CancelledOrderIDs, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresCancelAnOrderEPL, http.MethodDelete, kucoinFuturesCancelOrder+orderID, nil, &resp)
return resp.CancelledOrderIDs, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresCancelAnOrderEPL, http.MethodDelete, common.EncodeURLValues(path+id, params), nil, &resp)
}
// CancelAllFuturesOpenOrders used to cancel all futures order excluding stop orders
func (ku *Kucoin) CancelAllFuturesOpenOrders(ctx context.Context, symbol string) ([]string, error) {
// CancelMultipleFuturesLimitOrders used to cancel all futures order excluding stop orders
func (ku *Kucoin) CancelMultipleFuturesLimitOrders(ctx context.Context, symbol string) ([]string, error) {
params := url.Values{}
if symbol != "" {
params.Set("symbol", symbol)
@@ -447,7 +477,7 @@ func (ku *Kucoin) CancelAllFuturesStopOrders(ctx context.Context, symbol string)
resp := struct {
CancelledOrderIDs []string `json:"cancelledOrderIds"`
}{}
return resp.CancelledOrderIDs, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodDelete, common.EncodeURLValues(kucoinFuturesStopOrder, params), nil, &resp)
return resp.CancelledOrderIDs, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresCancelMultipleLimitOrdersEPL, http.MethodDelete, common.EncodeURLValues(kucoinFuturesStopOrder, params), nil, &resp)
}
// GetFuturesOrders gets the user current futures order list
@@ -494,30 +524,43 @@ func (ku *Kucoin) GetUntriggeredFuturesStopOrders(ctx context.Context, symbol, s
params.Set("endAt", strconv.FormatInt(endAt.UnixMilli(), 10))
}
var resp *FutureOrdersResponse
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesStopOrder, params), nil, &resp)
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, cancelUntriggeredFuturesStopOrdersEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesStopOrder, params), nil, &resp)
}
// GetFuturesRecentCompletedOrders gets list of recent 1000 orders in the last 24 hours
func (ku *Kucoin) GetFuturesRecentCompletedOrders(ctx context.Context) ([]FuturesOrder, error) {
func (ku *Kucoin) GetFuturesRecentCompletedOrders(ctx context.Context, symbol string) ([]FuturesOrder, error) {
params := url.Values{}
if symbol != "" {
params.Set("symbol", symbol)
}
var resp []FuturesOrder
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, kucoinFuturesRecentCompletedOrder, nil, &resp)
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresRecentCompletedOrdersEPL, http.MethodGet, common.EncodeURLValues("/v1/recentDoneOrders", params), nil, &resp)
}
// GetFuturesOrderDetails gets single order details by order ID
func (ku *Kucoin) GetFuturesOrderDetails(ctx context.Context, orderID string) (*FuturesOrder, error) {
var resp *FuturesOrder
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, kucoinFuturesGetOrderDetails+orderID, nil, &resp)
}
// GetFuturesOrderDetailsByClientID gets single order details by client ID
func (ku *Kucoin) GetFuturesOrderDetailsByClientID(ctx context.Context, clientID string) (*FuturesOrder, error) {
if clientID == "" {
return nil, errors.New("clientID can't be empty")
func (ku *Kucoin) GetFuturesOrderDetails(ctx context.Context, orderID, clientOrderID string) (*FuturesOrder, error) {
path := "/v1/orders/"
if orderID == "" && clientOrderID == "" {
return nil, fmt.Errorf("%w either client order ID or order id required", order.ErrOrderIDNotSet)
}
if orderID == "" {
path = "/v1/orders/byClientOid"
}
params := url.Values{}
params.Set("clientOid", clientID)
if clientOrderID != "" {
params.Set("clientOid", clientOrderID)
}
var resp *FuturesOrder
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesGetOrderDetailsByClientID, params), nil, &resp)
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresOrdersByIDEPL, http.MethodGet, common.EncodeURLValues(path+orderID, params), nil, &resp)
}
// GetFuturesOrderDetailsByClientOrderID gets single order details by client ID
func (ku *Kucoin) GetFuturesOrderDetailsByClientOrderID(ctx context.Context, clientOrderID string) (*FuturesOrder, error) {
if clientOrderID == "" {
return nil, order.ErrClientOrderIDMustBeSet
}
var resp *FuturesOrder
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresOrderDetailsByClientOrderIDEPL, http.MethodGet, "/v1/orders/byClientOid?clientOid="+clientOrderID, nil, &resp)
}
// GetFuturesFills gets list of recent fills
@@ -542,96 +585,119 @@ func (ku *Kucoin) GetFuturesFills(ctx context.Context, orderID, symbol, side, or
params.Set("endAt", strconv.FormatInt(endAt.UnixMilli(), 10))
}
var resp *FutureFillsResponse
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresRetrieveFillsEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesFills, params), nil, &resp)
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresRetrieveFillsEPL, http.MethodGet, common.EncodeURLValues("/v1/fills", params), nil, &resp)
}
// GetFuturesRecentFills gets list of 1000 recent fills in the last 24 hrs
func (ku *Kucoin) GetFuturesRecentFills(ctx context.Context) ([]FuturesFill, error) {
var resp []FuturesFill
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresRecentFillsEPL, http.MethodGet, kucoinFuturesRecentFills, nil, &resp)
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresRecentFillsEPL, http.MethodGet, "/v1/recentFills", nil, &resp)
}
// GetFuturesOpenOrderStats gets the total number and value of the all your active orders
func (ku *Kucoin) GetFuturesOpenOrderStats(ctx context.Context, symbol string) (*FuturesOpenOrderStats, error) {
if symbol == "" {
return nil, errors.New("symbol can't be empty")
return nil, currency.ErrSymbolStringEmpty
}
params := url.Values{}
params.Set("symbol", symbol)
var resp *FuturesOpenOrderStats
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesOpenOrderStats, params), nil, &resp)
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresOpenOrderStatsEPL, http.MethodGet, "/v1/openOrderStatistics?symbol="+symbol, nil, &resp)
}
// GetFuturesPosition gets the position details of a specified position
func (ku *Kucoin) GetFuturesPosition(ctx context.Context, symbol string) (*FuturesPosition, error) {
if symbol == "" {
return nil, errors.New("symbol can't be empty")
return nil, currency.ErrSymbolStringEmpty
}
params := url.Values{}
params.Set("symbol", symbol)
var resp *FuturesPosition
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesPosition, params), nil, &resp)
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresPositionEPL, http.MethodGet, "/v1/position?symbol="+symbol, nil, &resp)
}
// GetFuturesPositionList gets the list of position with details
func (ku *Kucoin) GetFuturesPositionList(ctx context.Context) ([]FuturesPosition, error) {
var resp []FuturesPosition
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresRetrievePositionListEPL, http.MethodGet, kucoinFuturesPositionList, nil, &resp)
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresPositionListEPL, http.MethodGet, "/v1/positions", nil, &resp)
}
// SetAutoDepositMargin enable/disable of auto-deposit margin
func (ku *Kucoin) SetAutoDepositMargin(ctx context.Context, symbol string, status bool) (bool, error) {
params := make(map[string]interface{})
if symbol == "" {
return false, errors.New("symbol can't be empty")
return false, currency.ErrSymbolStringEmpty
}
params := make(map[string]interface{})
params["symbol"] = symbol
params["status"] = status
var resp bool
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodPost, kucoinFuturesSetAutoDeposit, params, &resp)
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, setAutoDepositMarginEPL, http.MethodPost, "/v1/position/margin/auto-deposit-status", params, &resp)
}
// GetMaxWithdrawMargin query the maximum amount of margin that the current position supports withdrawal
func (ku *Kucoin) GetMaxWithdrawMargin(ctx context.Context, symbol string) (float64, error) {
if symbol == "" {
return 0, currency.ErrSymbolStringEmpty
}
var resp types.Number
return resp.Float64(), ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, maxWithdrawMarginEPL, http.MethodGet, "/v1/margin/maxWithdrawMargin?symbol="+symbol, nil, &resp)
}
// RemoveMarginManually removes a margin manually
func (ku *Kucoin) RemoveMarginManually(ctx context.Context, arg *WithdrawMarginResponse) (*MarginRemovingResponse, error) {
if *arg == (WithdrawMarginResponse{}) {
return nil, common.ErrNilPointer
}
if arg.Symbol == "" {
return nil, currency.ErrSymbolStringEmpty
}
if arg.WithdrawAmount <= 0 {
return nil, fmt.Errorf("%w, withdrawAmount must be greater than 0", order.ErrAmountBelowMin)
}
var resp *MarginRemovingResponse
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestSpot, removeMarginManuallyEPL, http.MethodPost, "/v1/margin/withdrawMargin", arg, &resp)
}
// AddMargin is used to add margin manually
func (ku *Kucoin) AddMargin(ctx context.Context, symbol, uniqueID string, margin float64) (*FuturesPosition, error) {
params := make(map[string]interface{})
if symbol == "" {
return nil, errors.New("symbol can't be empty")
return nil, currency.ErrSymbolStringEmpty
}
params := make(map[string]interface{})
params["symbol"] = symbol
if uniqueID == "" {
return nil, errors.New("uniqueID can't be empty")
return nil, errors.New("uniqueID cannot be empty")
}
params["bizNo"] = uniqueID
if margin <= 0 {
return nil, errors.New("margin can't be zero or negative")
return nil, errors.New("margin cannot be zero or negative")
}
params["margin"] = strconv.FormatFloat(margin, 'f', -1, 64)
var resp *FuturesPosition
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodPost, kucoinFuturesAddMargin, params, &resp)
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresAddMarginManuallyEPL, http.MethodPost, "/v1/position/margin/deposit-margin", params, &resp)
}
// GetFuturesRiskLimitLevel gets information about risk limit level of a specific contract
func (ku *Kucoin) GetFuturesRiskLimitLevel(ctx context.Context, symbol string) ([]FuturesRiskLimitLevel, error) {
if symbol == "" {
return nil, currency.ErrSymbolStringEmpty
}
var resp []FuturesRiskLimitLevel
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, kucoinFuturesRiskLimitLevel+symbol, nil, &resp)
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresRiskLimitLevelEPL, http.MethodGet, "/v1/contracts/risk-limit/"+symbol, nil, &resp)
}
// FuturesUpdateRiskLmitLevel is used to adjustment the risk limit level
func (ku *Kucoin) FuturesUpdateRiskLmitLevel(ctx context.Context, symbol string, level int64) (bool, error) {
params := make(map[string]interface{})
if symbol == "" {
return false, errors.New("symbol can't be empty")
return false, currency.ErrSymbolStringEmpty
}
params := make(map[string]interface{})
params["symbol"] = symbol
params["level"] = strconv.FormatInt(level, 10)
var resp bool
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodPost, kucoinFuturesUpdateRiskLmitLevel, params, &resp)
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresUpdateRiskLimitLevelEPL, http.MethodPost, "/v1/position/risk-limit-level/change", params, &resp)
}
// GetFuturesFundingHistory gets information about funding history
func (ku *Kucoin) GetFuturesFundingHistory(ctx context.Context, symbol string, offset, maxCount int64, reverse, forward bool, startAt, endAt time.Time) (*FuturesFundingHistoryResponse, error) {
if symbol == "" {
return nil, errors.New("symbol can't be empty")
return nil, currency.ErrSymbolStringEmpty
}
params := url.Values{}
params.Set("symbol", symbol)
@@ -650,24 +716,24 @@ func (ku *Kucoin) GetFuturesFundingHistory(ctx context.Context, symbol string, o
params.Set("maxCount", strconv.FormatInt(maxCount, 10))
}
var resp *FuturesFundingHistoryResponse
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresRetrieveFundingHistoryEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesFundingHistory, params), nil, &resp)
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresFundingHistoryEPL, http.MethodGet, common.EncodeURLValues("/v1/funding-history", params), nil, &resp)
}
// GetFuturesAccountOverview gets future account overview
func (ku *Kucoin) GetFuturesAccountOverview(ctx context.Context, currency string) (FuturesAccount, error) {
func (ku *Kucoin) GetFuturesAccountOverview(ctx context.Context, ccy string) (*FuturesAccount, error) {
params := url.Values{}
if currency != "" {
params.Set("currency", currency)
if ccy != "" {
params.Set("currency", ccy)
}
resp := FuturesAccount{}
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresRetrieveAccountOverviewEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesAccountOverview, params), nil, &resp)
var resp *FuturesAccount
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresAccountOverviewEPL, http.MethodGet, common.EncodeURLValues("/v1/account-overview", params), nil, &resp)
}
// GetFuturesTransactionHistory gets future transaction history
func (ku *Kucoin) GetFuturesTransactionHistory(ctx context.Context, currency, txType string, offset, maxCount int64, forward bool, startAt, endAt time.Time) (*FuturesTransactionHistoryResponse, error) {
func (ku *Kucoin) GetFuturesTransactionHistory(ctx context.Context, ccy currency.Code, txType string, offset, maxCount int64, forward bool, startAt, endAt time.Time) (*FuturesTransactionHistoryResponse, error) {
params := url.Values{}
if currency != "" {
params.Set("currency", currency)
if !ccy.IsEmpty() {
params.Set("currency", ccy.String())
}
if txType != "" {
params.Set("type", txType)
@@ -686,147 +752,81 @@ func (ku *Kucoin) GetFuturesTransactionHistory(ctx context.Context, currency, tx
params.Set("maxCount", strconv.FormatInt(maxCount, 10))
}
var resp *FuturesTransactionHistoryResponse
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresRetrieveTransactionHistoryEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesTransactionHistory, params), nil, &resp)
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresRetrieveTransactionHistoryEPL, http.MethodGet, common.EncodeURLValues("/v1/transaction-history", params), nil, &resp)
}
// CreateFuturesSubAccountAPIKey is used to create Futures APIs for sub-accounts
func (ku *Kucoin) CreateFuturesSubAccountAPIKey(ctx context.Context, ipWhitelist, passphrase, permission, remark, subName string) (*APIKeyDetail, error) {
if remark == "" {
return nil, errRemarkIsRequired
}
if subName == "" {
return nil, errInvalidSubAccountName
}
if passphrase == "" {
return nil, errInvalidPassPhraseInstance
}
params := make(map[string]interface{})
params["passphrase"] = passphrase
params["remark"] = remark
params["subName"] = subName
if ipWhitelist != "" {
params["ipWhitelist"] = ipWhitelist
}
if passphrase == "" {
return nil, errors.New("passphrase can't be empty")
}
params["passphrase"] = passphrase
if permission != "" {
params["permission"] = permission
}
if remark == "" {
return nil, errors.New("remark can't be empty")
}
params["remark"] = remark
if subName == "" {
return nil, errors.New("subName can't be empty")
}
params["subName"] = subName
var resp *APIKeyDetail
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodPost, kucoinFuturesSubAccountAPI, params, &resp)
}
// GetFuturesDepositAddress gets deposit address for currency
func (ku *Kucoin) GetFuturesDepositAddress(ctx context.Context, currency string) (*DepositAddress, error) {
if currency == "" {
return nil, errors.New("currency can't be empty")
}
params := url.Values{}
params.Set("currency", currency)
var resp *DepositAddress
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesDepositAddress, params), nil, &resp)
}
// GetFuturesDepositsList gets deposits list
func (ku *Kucoin) GetFuturesDepositsList(ctx context.Context, currency, status string, startAt, endAt time.Time) (*FuturesDepositDetailsResponse, error) {
params := url.Values{}
if currency != "" {
params.Set("currency", currency)
}
if status != "" {
params.Set("status", status)
}
if !startAt.IsZero() {
params.Set("startAt", strconv.FormatInt(startAt.UnixMilli(), 10))
}
if !endAt.IsZero() {
params.Set("endAt", strconv.FormatInt(endAt.UnixMilli(), 10))
}
var resp *FuturesDepositDetailsResponse
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesDepositsList, params), nil, &resp)
}
// GetFuturesWithdrawalLimit gets withdrawal limits for currency
func (ku *Kucoin) GetFuturesWithdrawalLimit(ctx context.Context, currency string) (*FuturesWithdrawalLimit, error) {
if currency == "" {
return nil, errors.New("currency can't be empty")
}
params := url.Values{}
params.Set("currency", currency)
var resp *FuturesWithdrawalLimit
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesWithdrawalLimit, params), nil, &resp)
}
// GetFuturesWithdrawalList gets withdrawal list
func (ku *Kucoin) GetFuturesWithdrawalList(ctx context.Context, currency, status string, startAt, endAt time.Time) (*FuturesWithdrawalsListResponse, error) {
params := url.Values{}
if currency != "" {
params.Set("currency", currency)
}
if status != "" {
params.Set("status", status)
}
if !startAt.IsZero() {
params.Set("startAt", strconv.FormatInt(startAt.UnixMilli(), 10))
}
if !endAt.IsZero() {
params.Set("endAt", strconv.FormatInt(endAt.UnixMilli(), 10))
}
var resp *FuturesWithdrawalsListResponse
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesWithdrawalList, params), nil, &resp)
}
// CancelFuturesWithdrawal is used to cancel withdrawal request of only PROCESSING status
func (ku *Kucoin) CancelFuturesWithdrawal(ctx context.Context, withdrawalID string) (bool, error) {
var resp bool
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodDelete, kucoinFuturesCancelWithdrawal+withdrawalID, nil, &resp)
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, createSubAccountAPIKeyEPL, http.MethodPost, "/v1/sub/api-key", params, &resp)
}
// TransferFuturesFundsToMainAccount helps in transferring funds from futures to main/trade account
func (ku *Kucoin) TransferFuturesFundsToMainAccount(ctx context.Context, amount float64, currency, recAccountType string) (*TransferRes, error) {
params := make(map[string]interface{})
func (ku *Kucoin) TransferFuturesFundsToMainAccount(ctx context.Context, amount float64, ccy currency.Code, recAccountType string) (*TransferRes, error) {
if amount <= 0 {
return nil, errors.New("amount can't be zero or negative")
return nil, order.ErrAmountBelowMin
}
params["amount"] = amount
if currency == "" {
return nil, errors.New("currency can't be empty")
if ccy.IsEmpty() {
return nil, currency.ErrCurrencyCodeEmpty
}
params["currency"] = currency
if recAccountType == "" {
return nil, errors.New("recAccountType can't be empty")
return nil, fmt.Errorf("%w, invalid receive account type", errAccountTypeMissing)
}
params := make(map[string]interface{})
params["amount"] = amount
params["currency"] = ccy.String()
params["recAccountType"] = recAccountType
var resp *TransferRes
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodPost, kucoinFuturesTransferFundtoMainAccount, params, &resp)
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, transferOutToMainEPL, http.MethodPost, "/v3/transfer-out", params, &resp)
}
// TransferFundsToFuturesAccount helps in transferring funds from payee account to futures account
func (ku *Kucoin) TransferFundsToFuturesAccount(ctx context.Context, amount float64, currency, payAccountType string) error {
params := make(map[string]interface{})
func (ku *Kucoin) TransferFundsToFuturesAccount(ctx context.Context, amount float64, ccy currency.Code, payAccountType string) error {
if amount <= 0 {
return errors.New("amount can't be zero or negative")
return order.ErrAmountBelowMin
}
params["amount"] = amount
if currency == "" {
return errors.New("currency can't be empty")
if ccy.IsEmpty() {
return currency.ErrCurrencyCodeEmpty
}
params["currency"] = currency
if payAccountType == "" {
return errors.New("payAccountType can't be empty")
return fmt.Errorf("%w, payAccountType cannot be empty", errAccountTypeMissing)
}
params := make(map[string]interface{})
params["amount"] = amount
params["currency"] = ccy.String()
params["payAccountType"] = payAccountType
resp := struct {
Error
}{}
return ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodPost, kucoinFuturesTransferFundtoFuturesAccount, params, &resp)
return ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, transferFundToFuturesAccountEPL, http.MethodPost, "/v1/transfer-in", params, &resp)
}
// GetFuturesTransferOutList gets list of transfer out
func (ku *Kucoin) GetFuturesTransferOutList(ctx context.Context, currency, status string, startAt, endAt time.Time) (*TransferListsResponse, error) {
if currency == "" {
return nil, errors.New("currency can't be empty")
func (ku *Kucoin) GetFuturesTransferOutList(ctx context.Context, ccy currency.Code, status string, startAt, endAt time.Time) (*TransferListsResponse, error) {
if ccy.IsEmpty() {
return nil, currency.ErrCurrencyCodeEmpty
}
params := url.Values{}
params.Set("currency", currency)
params.Set("currency", ccy.String())
if status != "" {
params.Set("status", status)
}
@@ -837,20 +837,7 @@ func (ku *Kucoin) GetFuturesTransferOutList(ctx context.Context, currency, statu
params.Set("endAt", strconv.FormatInt(endAt.UnixMilli(), 10))
}
var resp *TransferListsResponse
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodGet, common.EncodeURLValues(kucoinFuturesTransferOutList, params), nil, &resp)
}
// CancelFuturesTransferOut is used to cancel transfer out request of only PROCESSING status
func (ku *Kucoin) CancelFuturesTransferOut(ctx context.Context, applyID string) error {
if applyID == "" {
return errors.New("applyID can't be empty")
}
params := url.Values{}
params.Set("applyId", applyID)
resp := struct {
Error
}{}
return ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodDelete, common.EncodeURLValues(kucoinFuturesCancelTransferOut, params), nil, &resp)
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresTransferOutListEPL, http.MethodGet, common.EncodeURLValues("/v1/transfer-list", params), nil, &resp)
}
func processFuturesOB(ob [][2]float64) []orderbook.Tranche {
@@ -872,3 +859,63 @@ func constructFuturesOrderbook(o *futuresOrderbookResponse) *Orderbook {
Time: o.Time.Time(),
}
}
// GetFuturesTradingPairsActualFees retrieves the actual fee rate of the trading pair. The fee rate of your sub-account is the same as that of the master account
func (ku *Kucoin) GetFuturesTradingPairsActualFees(ctx context.Context, symbol string) (*TradingPairFee, error) {
if symbol == "" {
return nil, currency.ErrSymbolStringEmpty
}
params := url.Values{}
params.Set("symbol", symbol)
var resp *TradingPairFee
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresTradingPairFeeEPL, http.MethodGet, common.EncodeURLValues("/v1/trade-fees", params), nil, &resp)
}
// GetPositionHistory query position history information records
func (ku *Kucoin) GetPositionHistory(ctx context.Context, symbol string, from, to time.Time, limit, pageID int64) (*FuturesPositionHistory, error) {
params := url.Values{}
if !from.IsZero() && !to.IsZero() {
err := common.StartEndTimeCheck(from, to)
if err != nil {
return nil, err
}
params.Set("from", strconv.FormatInt(from.UnixMilli(), 10))
params.Set("to", strconv.FormatInt(to.UnixMilli(), 10))
}
if symbol != "" {
params.Set("symbol", symbol)
}
if limit > 0 {
params.Set("limit", strconv.FormatInt(limit, 10))
}
if pageID > 0 {
params.Set("pageId", strconv.FormatInt(pageID, 10))
}
var resp *FuturesPositionHistory
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresPositionHistoryEPL, http.MethodGet, common.EncodeURLValues("/v1/history-positions", params), nil, &resp)
}
// GetMaximumOpenPositionSize retrieves a maximum open position size
func (ku *Kucoin) GetMaximumOpenPositionSize(ctx context.Context, symbol string, price float64, leverage int64) (*FuturesMaxOpenPositionSize, error) {
if symbol == "" {
return nil, currency.ErrSymbolStringEmpty
}
if price <= 0 {
return nil, order.ErrPriceBelowMin
}
if leverage <= 0 {
return nil, fmt.Errorf("%w, leverage is required", errInvalidLeverage)
}
params := url.Values{}
params.Set("symbol", symbol)
params.Set("price", strconv.FormatFloat(price, 'f', -1, 64))
params.Set("leverage", strconv.FormatInt(leverage, 10))
var resp *FuturesMaxOpenPositionSize
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresMaxOpenPositionsSizeEPL, http.MethodGet, common.EncodeURLValues("/v2/getMaxOpenSize", params), nil, &resp)
}
// GetLatestTickersForAllContracts retrieves all futures instruments ticker information
func (ku *Kucoin) GetLatestTickersForAllContracts(ctx context.Context) ([]WsFuturesTicker, error) {
var resp []WsFuturesTicker
return resp, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresAllTickersInfoEPL, http.MethodGet, "/v1/allTickers", nil, &resp)
}

View File

@@ -141,6 +141,13 @@ type FuturesFundingRate struct {
PredictedValue float64 `json:"predictedValue"`
}
// FundingHistoryItem represents funding history item
type FundingHistoryItem struct {
Symbol string `json:"symbol"`
FundingRate float64 `json:"fundingRate"`
Timepoint convert.ExchangeTime `json:"timepoint"`
}
// FuturesKline stores kline data
type FuturesKline struct {
StartTime time.Time
@@ -151,7 +158,7 @@ type FuturesKline struct {
Volume float64
}
// FutureOrdersResponse represents a future order response list detail.
// FutureOrdersResponse represents a future order response list detail
type FutureOrdersResponse struct {
CurrentPage int64 `json:"currentPage"`
PageSize int64 `json:"pageSize"`
@@ -200,7 +207,7 @@ type FuturesOrder struct {
ReduceOnly bool `json:"reduceOnly"`
}
// FutureFillsResponse represents a future fills list response detail.
// FutureFillsResponse represents a future fills list response detail
type FutureFillsResponse struct {
CurrentPage int64 `json:"currentPage"`
PageSize int64 `json:"pageSize"`
@@ -209,7 +216,7 @@ type FutureFillsResponse struct {
Items []FuturesFill `json:"items"`
}
// FuturesFill represents list of recent fills for futures orders.
// FuturesFill represents list of recent fills for futures orders
type FuturesFill struct {
Symbol string `json:"symbol"`
TradeID string `json:"tradeId"`
@@ -232,7 +239,7 @@ type FuturesFill struct {
TradeTime convert.ExchangeTime `json:"tradeTime"`
}
// FuturesOpenOrderStats represents futures open order summary stats information.
// FuturesOpenOrderStats represents futures open order summary stats information
type FuturesOpenOrderStats struct {
OpenOrderBuySize int64 `json:"openOrderBuySize"`
OpenOrderSellSize int64 `json:"openOrderSellSize"`
@@ -241,7 +248,7 @@ type FuturesOpenOrderStats struct {
SettleCurrency string `json:"settleCurrency"`
}
// FuturesPosition represents futures position detailed information.
// FuturesPosition represents futures position detailed information
type FuturesPosition struct {
ID string `json:"id"`
Symbol string `json:"symbol"`
@@ -283,7 +290,19 @@ type FuturesPosition struct {
RiskLimitLevel int64 `json:"riskLimitLevel"`
}
// FuturesRiskLimitLevel represents futures risk limit level information.
// WithdrawMarginResponse represents a response data after withdrawing a margin
type WithdrawMarginResponse struct {
Symbol string `json:"symbol"`
WithdrawAmount float64 `json:"withdrawAmount"`
}
// MarginRemovingResponse represents a response data for margin response
type MarginRemovingResponse struct {
Symbol string `json:"symbol"`
WithdrawAmount float64 `json:"withdrawAmount"`
}
// FuturesRiskLimitLevel represents futures risk limit level information
type FuturesRiskLimitLevel struct {
Symbol string `json:"symbol"`
Level int64 `json:"level"`
@@ -294,7 +313,7 @@ type FuturesRiskLimitLevel struct {
MaintainMargin float64 `json:"maintainMargin"`
}
// FuturesFundingHistory represents futures funding information.
// FuturesFundingHistory represents futures funding information
type FuturesFundingHistory struct {
ID string `json:"id"`
Symbol string `json:"symbol"`
@@ -307,7 +326,7 @@ type FuturesFundingHistory struct {
SettleCurrency string `json:"settleCurrency"`
}
// FuturesAccount holds futures account detail information.
// FuturesAccount holds futures account detail information
type FuturesAccount struct {
AccountEquity float64 `json:"accountEquity"` // marginBalance + Unrealised PNL
UnrealisedPNL float64 `json:"unrealisedPNL"` // unrealised profit and loss
@@ -344,7 +363,7 @@ type APIKeyDetail struct {
CreateAt convert.ExchangeTime `json:"createdAt"`
}
// FuturesDepositDetailsResponse represents a futures deposits list detail response.
// FuturesDepositDetailsResponse represents a futures deposits list detail response
type FuturesDepositDetailsResponse struct {
CurrentPage int64 `json:"currentPage"`
PageSize int64 `json:"pageSize"`
@@ -353,7 +372,7 @@ type FuturesDepositDetailsResponse struct {
Items []FuturesDepositDetail `json:"items"`
}
// FuturesDepositDetail represents futures deposit detail information.
// FuturesDepositDetail represents futures deposit detail information
type FuturesDepositDetail struct {
Currency string `json:"currency"`
Status string `json:"status"`
@@ -365,7 +384,7 @@ type FuturesDepositDetail struct {
CreatedAt convert.ExchangeTime `json:"createdAt"`
}
// FuturesWithdrawalLimit represents withdrawal limit information.
// FuturesWithdrawalLimit represents withdrawal limit information
type FuturesWithdrawalLimit struct {
Currency string `json:"currency"`
ChainID string `json:"chainId"`
@@ -380,7 +399,7 @@ type FuturesWithdrawalLimit struct {
Precision float64 `json:"precision"`
}
// FuturesWithdrawalsListResponse represents a list of futures Withdrawal history instance.
// FuturesWithdrawalsListResponse represents a list of futures Withdrawal history instance
type FuturesWithdrawalsListResponse struct {
CurrentPage int64 `json:"currentPage"`
PageSize int64 `json:"pageSize"`
@@ -389,7 +408,7 @@ type FuturesWithdrawalsListResponse struct {
Items []FuturesWithdrawalHistory `json:"items"`
}
// FuturesWithdrawalHistory represents a list of Futures withdrawal history.
// FuturesWithdrawalHistory represents a list of Futures withdrawal history
type FuturesWithdrawalHistory struct {
WithdrawalID string `json:"withdrawalId"`
Currency string `json:"currency"`
@@ -405,7 +424,7 @@ type FuturesWithdrawalHistory struct {
Reason string `json:"reason"`
}
// TransferBase represents transfer base information.
// TransferBase represents transfer base information
type TransferBase struct {
ApplyID string `json:"applyId"`
Currency string `json:"currency"`
@@ -431,7 +450,7 @@ type TransferRes struct {
UpdatedAt convert.ExchangeTime `json:"updatedAt"`
}
// TransferListsResponse represents a transfer lists detail.
// TransferListsResponse represents a transfer lists detail
type TransferListsResponse struct {
CurrentPage int64 `json:"currentPage"`
PageSize int64 `json:"pageSize"`
@@ -440,13 +459,13 @@ type TransferListsResponse struct {
Items []Transfer `json:"items"`
}
// Transfer represents a transfer detail.
// Transfer represents a transfer detail
type Transfer struct {
TransferBase
Offset int64 `json:"offset"`
}
// FuturesServiceStatus represents service status.
// FuturesServiceStatus represents service status
type FuturesServiceStatus struct {
Status string `json:"status"`
Message string `json:"msg"`

View File

@@ -7,113 +7,417 @@ import (
)
const (
threeSecondsInterval = time.Second * 3
oneMinuteInterval = time.Minute
)
// rate of request per interval
const (
retrieveAccountLedgerRate = 18
masterSubUserTransferRate = 3
retrieveDepositListRate = 6
retrieveV1HistoricalDepositListRate = 6
retrieveWithdrawalListRate = 6
retrieveV1HistoricalWithdrawalListRate = 6
placeOrderRate = 45
placeMarginOrdersRate = 45
placeBulkOrdersRate = 3
cancelOrderRate = 60
cancelAllOrdersRate = 3
listOrdersRate = 30
listFillsRate = 9
retrieveFullOrderbookRate = 30
retrieveMarginAccountRate = 1
futuresRetrieveAccountOverviewRate = 30
futuresRetrieveTransactionHistoryRate = 9
futuresPlaceOrderRate = 30
futuresCancelAnOrderRate = 40
futuresLimitOrderMassCancelationRate = 9
futuresRetrieveOrderListRate = 30
futuresRetrieveFillsRate = 9
futuresRecentFillsRate = 9
futuresRetrievePositionListRate = 9
futuresRetrieveFundingHistoryRate = 9
futuresRetrieveFullOrderbookLevel2Rate = 30
defaultSpotRate = 1200
defaultFuturesRate = 1200
thirtySecondsInterval = time.Second * 30
)
const (
// for spot endpoints
retrieveAccountLedgerEPL request.EndpointLimit = iota
masterSubUserTransferEPL
retrieveDepositListEPL
retrieveV1HistoricalDepositListEPL
retrieveWithdrawalListEPL
accountSummaryInfoEPL request.EndpointLimit = iota
allAccountEPL
accountDetailEPL
accountLedgersEPL
hfAccountLedgersEPL
hfAccountLedgersMarginEPL
futuresAccountLedgersEPL
subAccountInfoV1EPL
allSubAccountsInfoV2EPL
createSubUserEPL
subAccountsEPL
subAccountBalancesEPL
allSubAccountBalancesV2EPL
subAccountSpotAPIListEPL
createSpotAPIForSubAccountEPL
modifySubAccountSpotAPIEPL
deleteSubAccountSpotAPIEPL
marginAccountDetailEPL
crossMarginAccountsDetailEPL
isolatedMarginAccountDetailEPL
futuresAccountsDetailEPL
tradingPairActualFeeEPL
allFuturesSubAccountBalancesEPL
futuresTradingPairFeeEPL
futuresPositionHistoryEPL
futuresMaxOpenPositionsSizeEPL
futuresAllTickersInfoEPL
createDepositAddressEPL
depositAddressesV2EPL
depositAddressesV1EPL
depositListEPL
historicDepositListEPL
withdrawalListEPL
retrieveV1HistoricalWithdrawalListEPL
withdrawalQuotaEPL
applyWithdrawalEPL
cancelWithdrawalsEPL
getTransferablesEPL
flexiTransferEPL
masterSubUserTransferEPL
innerTransferEPL
toMainOrTradeAccountEPL
toFuturesAccountEPL
futuresTransferOutRequestRecordsEPL
basicFeesEPL
tradeFeesEPL
spotCurrenciesV3EPL
spotCurrencyDetailEPL
symbolsEPL
tickersEPL
allTickersEPL
statistics24HrEPL
marketListEPL
partOrderbook20EPL
partOrderbook100EPL
fullOrderbookEPL
tradeHistoryEPL
klinesEPL
fiatPriceEPL
currentServerTimeEPL
serviceStatusEPL
hfPlaceOrderEPL
hfSyncPlaceOrderEPL
hfMultipleOrdersEPL
hfSyncPlaceMultipleHFOrdersEPL
hfModifyOrderEPL
cancelHFOrderEPL
hfSyncCancelOrderEPL
hfCancelOrderByClientOrderIDEPL
cancelSpecifiedNumberHFOrdersByOrderIDEPL
hfCancelAllOrdersBySymbolEPL
hfCancelAllOrdersEPL
hfGetAllActiveOrdersEPL
hfSymbolsWithActiveOrdersEPL
hfCompletedOrderListEPL
hfOrderDetailByOrderIDEPL
autoCancelHFOrderSettingEPL
autoCancelHFOrderSettingQueryEPL
hfFilledListEPL
placeOrderEPL
placeMarginOrdersEPL
placeBulkOrdersEPL
cancelOrderEPL
cancelOrderByClientOrderIDEPL
cancelAllOrdersEPL
listOrdersEPL
recentOrdersEPL
orderDetailByIDEPL
getOrderByClientSuppliedOrderIDEPL
listFillsEPL
retrieveFullOrderbookEPL
retrieveMarginAccountEPL
defaultSpotEPL
// for futures endpoints
futuresRetrieveAccountOverviewEPL
futuresRetrieveTransactionHistoryEPL
futuresPlaceOrderEPL
getRecentFillsEPL
placeStopOrderEPL
cancelStopOrderEPL
cancelStopOrderByClientIDEPL
cancelStopOrdersEPL
listStopOrdersEPL
getStopOrderDetailEPL
getStopOrderByClientIDEPL
placeOCOOrderEPL
cancelOCOOrderByIDEPL
cancelMultipleOCOOrdersEPL
getOCOOrderByIDEPL
getOCOOrderDetailsByOrderIDEPL
getOCOOrdersEPL
placeMarginOrderEPL
cancelMarginHFOrderByIDEPL
getMarginHFOrderDetailByID
cancelAllMarginHFOrdersBySymbolEPL
getActiveMarginHFOrdersEPL
getFilledHFMarginOrdersEPL
getMarginHFOrderDetailByOrderIDEPL
getMarginHFTradeFillsEPL
placeMarginOrdersEPL
leveragedTokenInfoEPL
getMarkPriceEPL
getAllMarginMarkPriceEPL
getMarginConfigurationEPL
crossIsolatedMarginRiskLimitCurrencyConfigEPL
isolatedMarginPairConfigEPL
isolatedMarginAccountInfoEPL
singleIsolatedMarginAccountInfoEPL
postMarginBorrowOrderEPL
postMarginRepaymentEPL
getCrossIsolatedMarginInterestRecordsEPL
marginBorrowingHistoryEPL
marginRepaymentHistoryEPL
lendingCurrencyInfoEPL
interestRateEPL
marginLendingSubscriptionEPL
redemptionEPL
modifySubscriptionEPL
getRedemptionOrdersEPL
getSubscriptionOrdersEPL
futuresOpenContractsEPL
futuresContractEPL
futuresTickerEPL
futuresOrderbookEPL
futuresPartOrderbookDepth20EPL
futuresPartOrderbookDepth100EPL
futuresTransactionHistoryEPL
futuresKlineEPL
futuresInterestRateEPL
futuresIndexListEPL
futuresCurrentMarkPriceEPL
futuresPremiumIndexEPL
futuresTransactionVolumeEPL
futuresServerTimeEPL
futuresServiceStatusEPL
multipleFuturesOrdersEPL
futuresCancelAnOrderEPL
futuresPlaceOrderEPL
futuresLimitOrderMassCancelationEPL
cancelUntriggeredFuturesStopOrdersEPL
futuresCancelMultipleLimitOrdersEPL
futuresRetrieveOrderListEPL
futuresRecentCompletedOrdersEPL
futuresOrdersByIDEPL
futuresRetrieveFillsEPL
futuresRecentFillsEPL
futuresRetrievePositionListEPL
futuresRetrieveFundingHistoryEPL
futuresRetrieveFullOrderbookLevel2EPL
defaultFuturesEPL
futuresOpenOrderStatsEPL
futuresPositionEPL
futuresPositionListEPL
setAutoDepositMarginEPL
maxWithdrawMarginEPL
removeMarginManuallyEPL
futuresAddMarginManuallyEPL
futuresRiskLimitLevelEPL
futuresUpdateRiskLimitLevelEPL
futuresCurrentFundingRateEPL
futuresPublicFundingRateEPL
futuresFundingHistoryEPL
spotAuthenticationEPL
futuresAuthenticationEPL
futuresOrderDetailsByClientOrderIDEPL
modifySubAccountAPIEPL
allSubAccountsBalanceEPL
allUserSubAccountsV2EPL
futuresRetrieveTransactionHistoryEPL
futuresAccountOverviewEPL
createSubAccountAPIKeyEPL
transferOutToMainEPL
transferFundToFuturesAccountEPL
futuresTransferOutListEPL
subscribeToEarnEPL
earnRedemptionEPL
earnRedemptionPreviewEPL
kucoinEarnSavingsProductsEPL
kucoinEarnFixedIncomeCurrentHoldingEPL
earnLimitedTimePromotionProductEPL
earnKCSStakingProductEPL
earnStakingProductEPL
vipLendingEPL
affilateUserRebateInfoEPL
marginPairsConfigurationEPL
modifyLeverageMultiplierEPL
marginActiveHFOrdersEPL
)
// GetRateLimit returns a RateLimit instance, which implements the request.Limiter interface.
func GetRateLimit() request.RateLimitDefinitions {
spotRate := request.NewRateLimit(thirtySecondsInterval, 4000)
futuresRate := request.NewRateLimit(thirtySecondsInterval, 2000)
managementRate := request.NewRateLimit(thirtySecondsInterval, 2000)
publicRate := request.NewRateLimit(thirtySecondsInterval, 2000)
return request.RateLimitDefinitions{
// spot specific rate limiters
retrieveAccountLedgerEPL: request.NewRateLimitWithWeight(threeSecondsInterval, retrieveAccountLedgerRate, 1),
masterSubUserTransferEPL: request.NewRateLimitWithWeight(threeSecondsInterval, masterSubUserTransferRate, 1),
retrieveDepositListEPL: request.NewRateLimitWithWeight(threeSecondsInterval, retrieveDepositListRate, 1),
retrieveV1HistoricalDepositListEPL: request.NewRateLimitWithWeight(threeSecondsInterval, retrieveV1HistoricalDepositListRate, 1),
retrieveWithdrawalListEPL: request.NewRateLimitWithWeight(threeSecondsInterval, retrieveWithdrawalListRate, 1),
retrieveV1HistoricalWithdrawalListEPL: request.NewRateLimitWithWeight(threeSecondsInterval, retrieveV1HistoricalWithdrawalListRate, 1),
placeOrderEPL: request.NewRateLimitWithWeight(threeSecondsInterval, placeOrderRate, 1),
placeMarginOrdersEPL: request.NewRateLimitWithWeight(threeSecondsInterval, placeMarginOrdersRate, 1),
placeBulkOrdersEPL: request.NewRateLimitWithWeight(threeSecondsInterval, placeBulkOrdersRate, 1),
cancelOrderEPL: request.NewRateLimitWithWeight(threeSecondsInterval, cancelOrderRate, 1),
cancelAllOrdersEPL: request.NewRateLimitWithWeight(threeSecondsInterval, cancelAllOrdersRate, 1),
listOrdersEPL: request.NewRateLimitWithWeight(threeSecondsInterval, listOrdersRate, 1),
listFillsEPL: request.NewRateLimitWithWeight(threeSecondsInterval, listFillsRate, 1),
retrieveFullOrderbookEPL: request.NewRateLimitWithWeight(threeSecondsInterval, retrieveFullOrderbookRate, 1),
retrieveMarginAccountEPL: request.NewRateLimitWithWeight(threeSecondsInterval, retrieveMarginAccountRate, 1),
accountSummaryInfoEPL: request.GetRateLimiterWithWeight(managementRate, 20),
allAccountEPL: request.GetRateLimiterWithWeight(managementRate, 5),
accountDetailEPL: request.GetRateLimiterWithWeight(managementRate, 5),
accountLedgersEPL: request.GetRateLimiterWithWeight(managementRate, 2),
hfAccountLedgersEPL: request.GetRateLimiterWithWeight(spotRate, 2),
hfAccountLedgersMarginEPL: request.GetRateLimiterWithWeight(spotRate, 2),
futuresAccountLedgersEPL: request.GetRateLimiterWithWeight(spotRate, 2),
subAccountInfoV1EPL: request.GetRateLimiterWithWeight(managementRate, 20),
allSubAccountsInfoV2EPL: request.GetRateLimiterWithWeight(managementRate, 20),
createSubUserEPL: request.GetRateLimiterWithWeight(managementRate, 20),
subAccountsEPL: request.GetRateLimiterWithWeight(managementRate, 15),
subAccountBalancesEPL: request.GetRateLimiterWithWeight(managementRate, 20),
allSubAccountBalancesV2EPL: request.GetRateLimiterWithWeight(managementRate, 20),
subAccountSpotAPIListEPL: request.GetRateLimiterWithWeight(managementRate, 20),
createSpotAPIForSubAccountEPL: request.GetRateLimiterWithWeight(managementRate, 20),
modifySubAccountSpotAPIEPL: request.GetRateLimiterWithWeight(managementRate, 30),
deleteSubAccountSpotAPIEPL: request.GetRateLimiterWithWeight(managementRate, 30),
marginAccountDetailEPL: request.GetRateLimiterWithWeight(spotRate, 40),
crossMarginAccountsDetailEPL: request.GetRateLimiterWithWeight(spotRate, 15),
isolatedMarginAccountDetailEPL: request.GetRateLimiterWithWeight(spotRate, 15),
futuresAccountsDetailEPL: request.GetRateLimiterWithWeight(futuresRate, 5),
tradingPairActualFeeEPL: request.GetRateLimiterWithWeight(spotRate, 3),
allFuturesSubAccountBalancesEPL: request.GetRateLimiterWithWeight(futuresRate, 6),
futuresTradingPairFeeEPL: request.GetRateLimiterWithWeight(futuresRate, 3),
futuresPositionHistoryEPL: request.GetRateLimiterWithWeight(futuresRate, 2),
futuresMaxOpenPositionsSizeEPL: request.GetRateLimiterWithWeight(futuresRate, 2),
futuresAllTickersInfoEPL: request.GetRateLimiterWithWeight(futuresRate, 5),
createDepositAddressEPL: request.GetRateLimiterWithWeight(managementRate, 20),
depositAddressesV2EPL: request.GetRateLimiterWithWeight(managementRate, 5),
depositAddressesV1EPL: request.GetRateLimiterWithWeight(managementRate, 5),
depositListEPL: request.GetRateLimiterWithWeight(managementRate, 5),
historicDepositListEPL: request.GetRateLimiterWithWeight(managementRate, 5),
withdrawalListEPL: request.GetRateLimiterWithWeight(managementRate, 20),
retrieveV1HistoricalWithdrawalListEPL: request.GetRateLimiterWithWeight(managementRate, 20),
withdrawalQuotaEPL: request.GetRateLimiterWithWeight(managementRate, 20),
applyWithdrawalEPL: request.GetRateLimiterWithWeight(managementRate, 5),
cancelWithdrawalsEPL: request.GetRateLimiterWithWeight(managementRate, 20),
getTransferablesEPL: request.GetRateLimiterWithWeight(managementRate, 20),
flexiTransferEPL: request.GetRateLimiterWithWeight(managementRate, 4),
masterSubUserTransferEPL: request.GetRateLimiterWithWeight(managementRate, 30),
innerTransferEPL: request.GetRateLimiterWithWeight(managementRate, 10),
toMainOrTradeAccountEPL: request.GetRateLimiterWithWeight(managementRate, 20),
toFuturesAccountEPL: request.GetRateLimiterWithWeight(managementRate, 20),
futuresTransferOutRequestRecordsEPL: request.GetRateLimiterWithWeight(managementRate, 20),
basicFeesEPL: request.GetRateLimiterWithWeight(spotRate, 3),
tradeFeesEPL: request.GetRateLimiterWithWeight(spotRate, 3),
spotCurrenciesV3EPL: request.GetRateLimiterWithWeight(publicRate, 3),
spotCurrencyDetailEPL: request.GetRateLimiterWithWeight(publicRate, 3),
symbolsEPL: request.GetRateLimiterWithWeight(publicRate, 4),
tickersEPL: request.GetRateLimiterWithWeight(publicRate, 2),
allTickersEPL: request.GetRateLimiterWithWeight(publicRate, 15),
statistics24HrEPL: request.GetRateLimiterWithWeight(publicRate, 15),
marketListEPL: request.GetRateLimiterWithWeight(publicRate, 3),
partOrderbook20EPL: request.GetRateLimiterWithWeight(publicRate, 2),
partOrderbook100EPL: request.GetRateLimiterWithWeight(publicRate, 4),
fullOrderbookEPL: request.GetRateLimiterWithWeight(spotRate, 3),
tradeHistoryEPL: request.GetRateLimiterWithWeight(publicRate, 3),
klinesEPL: request.GetRateLimiterWithWeight(publicRate, 3),
fiatPriceEPL: request.GetRateLimiterWithWeight(publicRate, 3),
currentServerTimeEPL: request.GetRateLimiterWithWeight(publicRate, 3),
serviceStatusEPL: request.GetRateLimiterWithWeight(publicRate, 3),
hfPlaceOrderEPL: request.GetRateLimiterWithWeight(spotRate, 1),
hfSyncPlaceOrderEPL: request.GetRateLimiterWithWeight(spotRate, 1),
hfMultipleOrdersEPL: request.GetRateLimiterWithWeight(spotRate, 1),
hfSyncPlaceMultipleHFOrdersEPL: request.GetRateLimiterWithWeight(spotRate, 1),
hfModifyOrderEPL: request.GetRateLimiterWithWeight(spotRate, 3),
cancelHFOrderEPL: request.GetRateLimiterWithWeight(spotRate, 1),
hfSyncCancelOrderEPL: request.GetRateLimiterWithWeight(spotRate, 1),
hfCancelOrderByClientOrderIDEPL: request.GetRateLimiterWithWeight(spotRate, 1),
cancelSpecifiedNumberHFOrdersByOrderIDEPL: request.GetRateLimiterWithWeight(spotRate, 2),
hfCancelAllOrdersBySymbolEPL: request.GetRateLimiterWithWeight(spotRate, 2),
hfCancelAllOrdersEPL: request.GetRateLimiterWithWeight(spotRate, 30),
hfGetAllActiveOrdersEPL: request.GetRateLimiterWithWeight(spotRate, 2),
hfSymbolsWithActiveOrdersEPL: request.GetRateLimiterWithWeight(spotRate, 2),
hfCompletedOrderListEPL: request.GetRateLimiterWithWeight(spotRate, 2),
hfOrderDetailByOrderIDEPL: request.GetRateLimiterWithWeight(spotRate, 2),
autoCancelHFOrderSettingEPL: request.GetRateLimiterWithWeight(spotRate, 2),
autoCancelHFOrderSettingQueryEPL: request.GetRateLimiterWithWeight(spotRate, 2),
hfFilledListEPL: request.GetRateLimiterWithWeight(spotRate, 2),
placeOrderEPL: request.GetRateLimiterWithWeight(spotRate, 2),
placeBulkOrdersEPL: request.GetRateLimiterWithWeight(spotRate, 3),
cancelOrderEPL: request.GetRateLimiterWithWeight(spotRate, 3),
cancelOrderByClientOrderIDEPL: request.GetRateLimiterWithWeight(spotRate, 5),
cancelAllOrdersEPL: request.GetRateLimiterWithWeight(spotRate, 20),
listOrdersEPL: request.GetRateLimiterWithWeight(spotRate, 2),
recentOrdersEPL: request.GetRateLimiterWithWeight(spotRate, 3),
orderDetailByIDEPL: request.GetRateLimiterWithWeight(spotRate, 2),
getOrderByClientSuppliedOrderIDEPL: request.GetRateLimiterWithWeight(spotRate, 3),
listFillsEPL: request.GetRateLimiterWithWeight(spotRate, 10),
getRecentFillsEPL: request.GetRateLimiterWithWeight(spotRate, 20),
placeStopOrderEPL: request.GetRateLimiterWithWeight(spotRate, 2),
cancelStopOrderEPL: request.GetRateLimiterWithWeight(spotRate, 3),
cancelStopOrderByClientIDEPL: request.GetRateLimiterWithWeight(spotRate, 5),
cancelStopOrdersEPL: request.GetRateLimiterWithWeight(spotRate, 3),
listStopOrdersEPL: request.GetRateLimiterWithWeight(spotRate, 8),
getStopOrderDetailEPL: request.GetRateLimiterWithWeight(spotRate, 3),
getStopOrderByClientIDEPL: request.GetRateLimiterWithWeight(spotRate, 3),
placeOCOOrderEPL: request.GetRateLimiterWithWeight(spotRate, 2),
cancelOCOOrderByIDEPL: request.GetRateLimiterWithWeight(spotRate, 3),
cancelMultipleOCOOrdersEPL: request.GetRateLimiterWithWeight(spotRate, 3),
getOCOOrderByIDEPL: request.GetRateLimiterWithWeight(spotRate, 2),
getOCOOrderDetailsByOrderIDEPL: request.GetRateLimiterWithWeight(spotRate, 2),
getOCOOrdersEPL: request.GetRateLimiterWithWeight(spotRate, 2),
placeMarginOrderEPL: request.GetRateLimiterWithWeight(spotRate, 5),
cancelMarginHFOrderByIDEPL: request.GetRateLimiterWithWeight(spotRate, 5),
getMarginHFOrderDetailByID: request.GetRateLimiterWithWeight(spotRate, 5),
cancelAllMarginHFOrdersBySymbolEPL: request.GetRateLimiterWithWeight(spotRate, 10),
getActiveMarginHFOrdersEPL: request.GetRateLimiterWithWeight(spotRate, 4),
getFilledHFMarginOrdersEPL: request.GetRateLimiterWithWeight(spotRate, 10),
getMarginHFOrderDetailByOrderIDEPL: request.GetRateLimiterWithWeight(spotRate, 4),
getMarginHFTradeFillsEPL: request.GetRateLimiterWithWeight(spotRate, 5),
placeMarginOrdersEPL: request.GetRateLimiterWithWeight(spotRate, 5),
leveragedTokenInfoEPL: request.GetRateLimiterWithWeight(spotRate, 25),
getMarkPriceEPL: request.GetRateLimiterWithWeight(publicRate, 2),
getAllMarginMarkPriceEPL: request.GetRateLimiterWithWeight(publicRate, 10),
getMarginConfigurationEPL: request.GetRateLimiterWithWeight(spotRate, 25),
crossIsolatedMarginRiskLimitCurrencyConfigEPL: request.GetRateLimiterWithWeight(spotRate, 20),
isolatedMarginPairConfigEPL: request.GetRateLimiterWithWeight(spotRate, 20),
isolatedMarginAccountInfoEPL: request.GetRateLimiterWithWeight(spotRate, 50),
singleIsolatedMarginAccountInfoEPL: request.GetRateLimiterWithWeight(spotRate, 50),
postMarginBorrowOrderEPL: request.GetRateLimiterWithWeight(spotRate, 15),
postMarginRepaymentEPL: request.GetRateLimiterWithWeight(spotRate, 10),
getCrossIsolatedMarginInterestRecordsEPL: request.GetRateLimiterWithWeight(spotRate, 20),
marginBorrowingHistoryEPL: request.GetRateLimiterWithWeight(spotRate, 15),
marginRepaymentHistoryEPL: request.GetRateLimiterWithWeight(spotRate, 15),
lendingCurrencyInfoEPL: request.GetRateLimiterWithWeight(spotRate, 10),
interestRateEPL: request.GetRateLimiterWithWeight(publicRate, 5),
marginLendingSubscriptionEPL: request.GetRateLimiterWithWeight(spotRate, 15),
redemptionEPL: request.GetRateLimiterWithWeight(spotRate, 15),
modifySubscriptionEPL: request.GetRateLimiterWithWeight(spotRate, 10),
getRedemptionOrdersEPL: request.GetRateLimiterWithWeight(spotRate, 10),
getSubscriptionOrdersEPL: request.GetRateLimiterWithWeight(spotRate, 10),
futuresOpenContractsEPL: request.GetRateLimiterWithWeight(publicRate, 3),
futuresContractEPL: request.GetRateLimiterWithWeight(publicRate, 3),
futuresTickerEPL: request.GetRateLimiterWithWeight(publicRate, 2),
futuresOrderbookEPL: request.GetRateLimiterWithWeight(publicRate, 3),
futuresPartOrderbookDepth20EPL: request.GetRateLimiterWithWeight(publicRate, 5),
futuresPartOrderbookDepth100EPL: request.GetRateLimiterWithWeight(publicRate, 10),
futuresTransactionHistoryEPL: request.GetRateLimiterWithWeight(publicRate, 5),
futuresKlineEPL: request.GetRateLimiterWithWeight(publicRate, 3),
futuresInterestRateEPL: request.GetRateLimiterWithWeight(publicRate, 5),
futuresIndexListEPL: request.GetRateLimiterWithWeight(publicRate, 2),
futuresCurrentMarkPriceEPL: request.GetRateLimiterWithWeight(publicRate, 3),
futuresPremiumIndexEPL: request.GetRateLimiterWithWeight(publicRate, 3),
futuresTransactionVolumeEPL: request.GetRateLimiterWithWeight(futuresRate, 3),
futuresServerTimeEPL: request.GetRateLimiterWithWeight(publicRate, 2),
futuresServiceStatusEPL: request.GetRateLimiterWithWeight(publicRate, 4),
multipleFuturesOrdersEPL: request.GetRateLimiterWithWeight(futuresRate, 20),
futuresCancelAnOrderEPL: request.GetRateLimiterWithWeight(futuresRate, 1),
futuresPlaceOrderEPL: request.GetRateLimiterWithWeight(futuresRate, 2),
futuresLimitOrderMassCancelationEPL: request.GetRateLimiterWithWeight(futuresRate, 30),
cancelUntriggeredFuturesStopOrdersEPL: request.GetRateLimiterWithWeight(futuresRate, 15),
futuresCancelMultipleLimitOrdersEPL: request.GetRateLimiterWithWeight(futuresRate, 30),
futuresRetrieveOrderListEPL: request.GetRateLimiterWithWeight(futuresRate, 2),
futuresRecentCompletedOrdersEPL: request.GetRateLimiterWithWeight(futuresRate, 5),
futuresOrdersByIDEPL: request.GetRateLimiterWithWeight(futuresRate, 5),
futuresRetrieveFillsEPL: request.GetRateLimiterWithWeight(futuresRate, 5),
futuresRecentFillsEPL: request.GetRateLimiterWithWeight(futuresRate, 3),
futuresOpenOrderStatsEPL: request.GetRateLimiterWithWeight(futuresRate, 10),
futuresPositionEPL: request.GetRateLimiterWithWeight(futuresRate, 2),
futuresPositionListEPL: request.GetRateLimiterWithWeight(futuresRate, 2),
setAutoDepositMarginEPL: request.GetRateLimiterWithWeight(futuresRate, 4),
maxWithdrawMarginEPL: request.GetRateLimiterWithWeight(futuresRate, 10),
removeMarginManuallyEPL: request.GetRateLimiterWithWeight(futuresRate, 10),
futuresAddMarginManuallyEPL: request.GetRateLimiterWithWeight(futuresRate, 4),
futuresRiskLimitLevelEPL: request.GetRateLimiterWithWeight(futuresRate, 5),
futuresUpdateRiskLimitLevelEPL: request.GetRateLimiterWithWeight(futuresRate, 4),
futuresCurrentFundingRateEPL: request.GetRateLimiterWithWeight(publicRate, 2),
futuresPublicFundingRateEPL: request.GetRateLimiterWithWeight(publicRate, 5),
futuresFundingHistoryEPL: request.GetRateLimiterWithWeight(futuresRate, 5),
spotAuthenticationEPL: request.GetRateLimiterWithWeight(spotRate, 10),
futuresAuthenticationEPL: request.GetRateLimiterWithWeight(futuresRate, 10),
futuresOrderDetailsByClientOrderIDEPL: request.GetRateLimiterWithWeight(futuresRate, 5),
modifySubAccountAPIEPL: request.GetRateLimiterWithWeight(managementRate, 30),
allSubAccountsBalanceEPL: request.GetRateLimiterWithWeight(managementRate, 20),
allUserSubAccountsV2EPL: request.GetRateLimiterWithWeight(managementRate, 20),
futuresRetrieveTransactionHistoryEPL: request.GetRateLimiterWithWeight(managementRate, 2),
futuresAccountOverviewEPL: request.GetRateLimiterWithWeight(futuresRate, 5),
createSubAccountAPIKeyEPL: request.GetRateLimiterWithWeight(managementRate, 20),
transferOutToMainEPL: request.GetRateLimiterWithWeight(managementRate, 20),
transferFundToFuturesAccountEPL: request.GetRateLimiterWithWeight(managementRate, 20),
futuresTransferOutListEPL: request.GetRateLimiterWithWeight(managementRate, 20),
// default spot and futures rates
defaultSpotEPL: request.NewRateLimitWithWeight(oneMinuteInterval, defaultSpotRate, 1),
defaultFuturesEPL: request.NewRateLimitWithWeight(oneMinuteInterval, defaultFuturesRate, 1),
subscribeToEarnEPL: request.GetRateLimiterWithWeight(spotRate, 5),
earnRedemptionEPL: request.GetRateLimiterWithWeight(spotRate, 5),
earnRedemptionPreviewEPL: request.GetRateLimiterWithWeight(spotRate, 5),
// futures specific rate limiters
futuresRetrieveAccountOverviewEPL: request.NewRateLimitWithWeight(threeSecondsInterval, futuresRetrieveAccountOverviewRate, 1),
futuresRetrieveTransactionHistoryEPL: request.NewRateLimitWithWeight(threeSecondsInterval, futuresRetrieveTransactionHistoryRate, 1),
futuresPlaceOrderEPL: request.NewRateLimitWithWeight(threeSecondsInterval, futuresPlaceOrderRate, 1),
futuresCancelAnOrderEPL: request.NewRateLimitWithWeight(threeSecondsInterval, futuresCancelAnOrderRate, 1),
futuresLimitOrderMassCancelationEPL: request.NewRateLimitWithWeight(threeSecondsInterval, futuresLimitOrderMassCancelationRate, 1),
futuresRetrieveOrderListEPL: request.NewRateLimitWithWeight(threeSecondsInterval, futuresRetrieveOrderListRate, 1),
futuresRetrieveFillsEPL: request.NewRateLimitWithWeight(threeSecondsInterval, futuresRetrieveFillsRate, 1),
futuresRecentFillsEPL: request.NewRateLimitWithWeight(threeSecondsInterval, futuresRecentFillsRate, 1),
futuresRetrievePositionListEPL: request.NewRateLimitWithWeight(threeSecondsInterval, futuresRetrievePositionListRate, 1),
futuresRetrieveFundingHistoryEPL: request.NewRateLimitWithWeight(threeSecondsInterval, futuresRetrieveFundingHistoryRate, 1),
futuresRetrieveFullOrderbookLevel2EPL: request.NewRateLimitWithWeight(threeSecondsInterval, futuresRetrieveFullOrderbookLevel2Rate, 1),
kucoinEarnSavingsProductsEPL: request.GetRateLimiterWithWeight(spotRate, 5),
kucoinEarnFixedIncomeCurrentHoldingEPL: request.GetRateLimiterWithWeight(spotRate, 5),
earnLimitedTimePromotionProductEPL: request.GetRateLimiterWithWeight(spotRate, 5),
earnKCSStakingProductEPL: request.GetRateLimiterWithWeight(spotRate, 5),
earnStakingProductEPL: request.GetRateLimiterWithWeight(spotRate, 5),
vipLendingEPL: request.GetRateLimiterWithWeight(spotRate, 1),
affilateUserRebateInfoEPL: request.GetRateLimiterWithWeight(spotRate, 30),
marginPairsConfigurationEPL: request.GetRateLimiterWithWeight(spotRate, 5),
modifyLeverageMultiplierEPL: request.GetRateLimiterWithWeight(spotRate, 5),
marginActiveHFOrdersEPL: request.GetRateLimiterWithWeight(spotRate, 2),
}
}

View File

@@ -0,0 +1,201 @@
package kucoin
import (
"context"
"net/http"
"testing"
"github.com/stretchr/testify/require"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
)
func TestRateLimit_LimitStatic(t *testing.T) {
t.Parallel()
testTable := map[string]request.EndpointLimit{
"all Account": allAccountEPL,
"account Detail": accountDetailEPL,
"account Ledgers": accountLedgersEPL,
"hf Account Ledgers": hfAccountLedgersEPL,
"hf Account Ledgers Margin": hfAccountLedgersMarginEPL,
"futures Account Ledgers": futuresAccountLedgersEPL,
"sub Account Info V1": subAccountInfoV1EPL,
"all Sub Accounts Info V2": allSubAccountsInfoV2EPL,
"create SubUser": createSubUserEPL,
"sub Accounts": subAccountsEPL,
"sub Account Balances": subAccountBalancesEPL,
"all SubAccount Balances V2": allSubAccountBalancesV2EPL,
"sub Account Spot API List": subAccountSpotAPIListEPL,
"create Spot API For Sub Account": createSpotAPIForSubAccountEPL,
"modify Sub Account Spot API": modifySubAccountSpotAPIEPL,
"delete Sub Account Spot API": deleteSubAccountSpotAPIEPL,
"margin Account Detail": marginAccountDetailEPL,
"cross Margin Accounts Detail": crossMarginAccountsDetailEPL,
"isolated Margin Account Detail": isolatedMarginAccountDetailEPL,
"futures Accounts Detail": futuresAccountsDetailEPL,
"all Futures Sub Account Balances": allFuturesSubAccountBalancesEPL,
"create Deposit Address": createDepositAddressEPL,
"deposit Addresses V2": depositAddressesV2EPL,
"deposit Addresses V1": depositAddressesV1EPL,
"deposit List": depositListEPL,
"historic Deposit List": historicDepositListEPL,
"withdrawal List": withdrawalListEPL,
"retrieve V1 Historical Withdrawal List": retrieveV1HistoricalWithdrawalListEPL,
"withdrawal Quota": withdrawalQuotaEPL,
"apply Withdrawal": applyWithdrawalEPL,
"cancel Withdrawals": cancelWithdrawalsEPL,
"get Transferables": getTransferablesEPL,
"flexi Transfer": flexiTransferEPL,
"master Sub User Transfer": masterSubUserTransferEPL,
"inner Transfer": innerTransferEPL,
"to Main Or TradeAccount": toMainOrTradeAccountEPL,
"to Futures Account": toFuturesAccountEPL,
"futures Transfer Out Request Records": futuresTransferOutRequestRecordsEPL,
"basic Fees": basicFeesEPL,
"trade Fees": tradeFeesEPL,
"spot Currencies V3": spotCurrenciesV3EPL,
"spot Currency Detail": spotCurrencyDetailEPL,
"symbols": symbolsEPL,
"tickers": tickersEPL,
"all Tickers": allTickersEPL,
"statistics 24Hr": statistics24HrEPL,
"market List": marketListEPL,
"part Orderbook 20": partOrderbook20EPL,
"part Orderbook 100": partOrderbook100EPL,
"full Orderbook": fullOrderbookEPL,
"trade History": tradeHistoryEPL,
"klines": klinesEPL,
"fiat Price": fiatPriceEPL,
"current Server Time": currentServerTimeEPL,
"service Status": serviceStatusEPL,
"hf Place Order": hfPlaceOrderEPL,
"hf Sync Place Order": hfSyncPlaceOrderEPL,
"hf Multiple Orders": hfMultipleOrdersEPL,
"hf Sync Place Multiple HF Orders": hfSyncPlaceMultipleHFOrdersEPL,
"hf Modify Order": hfModifyOrderEPL,
"cancel HF Order": cancelHFOrderEPL,
"hf Sync Cancel Order": hfSyncCancelOrderEPL,
"hf Cancel Order By Client OrderID": hfCancelOrderByClientOrderIDEPL,
"cancel Specified Number HF Orders By OrderID": cancelSpecifiedNumberHFOrdersByOrderIDEPL,
"hf Cancel All Orders By Symbol": hfCancelAllOrdersBySymbolEPL,
"hf Cancel All Orders": hfCancelAllOrdersEPL,
"hf Get All Active Orders": hfGetAllActiveOrdersEPL,
"hf Symbols With Active Orders": hfSymbolsWithActiveOrdersEPL,
"hf Completed Order List": hfCompletedOrderListEPL,
"hf Order Detail By OrderID": hfOrderDetailByOrderIDEPL,
"auto Cancel HF Order Setting": autoCancelHFOrderSettingEPL,
"auto Cancel HF Order Setting Query": autoCancelHFOrderSettingQueryEPL,
"hf Filled List": hfFilledListEPL,
"place Order": placeOrderEPL,
"place Bulk Orders": placeBulkOrdersEPL,
"cancel Order": cancelOrderEPL,
"cancel Order By Client OrderID": cancelOrderByClientOrderIDEPL,
"cancel All Orders": cancelAllOrdersEPL,
"list Orders": listOrdersEPL,
"recent Orders": recentOrdersEPL,
"order Detail By ID": orderDetailByIDEPL,
"get Order By Client Supplied OrderID": getOrderByClientSuppliedOrderIDEPL,
"list Fills": listFillsEPL,
"get Recent Fills": getRecentFillsEPL,
"place Stop Order": placeStopOrderEPL,
"cancel Stop Order": cancelStopOrderEPL,
"cancel Stop Order By ClientID": cancelStopOrderByClientIDEPL,
"cancel Stop Orders": cancelStopOrdersEPL,
"list Stop Orders": listStopOrdersEPL,
"get Stop Order Detail": getStopOrderDetailEPL,
"get Stop Order By ClientID": getStopOrderByClientIDEPL,
"place OCO Order": placeOCOOrderEPL,
"cancel OCOOrder By ID": cancelOCOOrderByIDEPL,
"cancel Multiple OCO Orders": cancelMultipleOCOOrdersEPL,
"get OCO Order By ID": getOCOOrderByIDEPL,
"get OCO Order Details By OrderID": getOCOOrderDetailsByOrderIDEPL,
"get OCO Orders": getOCOOrdersEPL,
"place Margin Order": placeMarginOrderEPL,
"cancel Margin HF Order By ID": cancelMarginHFOrderByIDEPL,
"get Margin HF Order Detail By ID": getMarginHFOrderDetailByID,
"cancel All Margin HF Orders By Symbol": cancelAllMarginHFOrdersBySymbolEPL,
"get Active Margin HF Orders": getActiveMarginHFOrdersEPL,
"get Filled HF Margin Orders": getFilledHFMarginOrdersEPL,
"get Margin HF Order Detail By Order ID": getMarginHFOrderDetailByOrderIDEPL,
"get Margin HF Trade Fills": getMarginHFTradeFillsEPL,
"place Margin Orders": placeMarginOrdersEPL,
"leveraged Token Info": leveragedTokenInfoEPL,
"get Mark Price": getMarkPriceEPL,
"get Margin Configuration": getMarginConfigurationEPL,
"cross Isolated Margin Risk Limit Currency Config": crossIsolatedMarginRiskLimitCurrencyConfigEPL,
"isolated Margin Pair Config": isolatedMarginPairConfigEPL,
"isolated Margin Account Info": isolatedMarginAccountInfoEPL,
"single Isolated Margin Account Info": singleIsolatedMarginAccountInfoEPL,
"post Margin Borrow Order": postMarginBorrowOrderEPL,
"post Margin Repayment": postMarginRepaymentEPL,
"margin Borrowing History": marginBorrowingHistoryEPL,
"margin Repayment History": marginRepaymentHistoryEPL,
"lending Currency Info": lendingCurrencyInfoEPL,
"interest Rate": interestRateEPL,
"margin Lending Subscription": marginLendingSubscriptionEPL,
"redemption": redemptionEPL,
"modify Subscription": modifySubscriptionEPL,
"get Redemption Orders": getRedemptionOrdersEPL,
"get Subscription Orders": getSubscriptionOrdersEPL,
"futures Open Contracts": futuresOpenContractsEPL,
"futures Contract": futuresContractEPL,
"futures Ticker": futuresTickerEPL,
"futures Orderbook": futuresOrderbookEPL,
"futures Part Orderbook Depth20": futuresPartOrderbookDepth20EPL,
"futures Part Orderbook Depth100": futuresPartOrderbookDepth100EPL,
"futures Transaction History": futuresTransactionHistoryEPL,
"futures Kline": futuresKlineEPL,
"futures Interest Rate": futuresInterestRateEPL,
"futures Index List": futuresIndexListEPL,
"futures Current Mark Price": futuresCurrentMarkPriceEPL,
"futures Premium Index": futuresPremiumIndexEPL,
"futures Transaction Volume": futuresTransactionVolumeEPL,
"futures Server Time": futuresServerTimeEPL,
"futures Service Status": futuresServiceStatusEPL,
"multiple Futures Orders": multipleFuturesOrdersEPL,
"futures Cancel An Order": futuresCancelAnOrderEPL,
"futures Place Order": futuresPlaceOrderEPL,
"futures Limit Order Mass Cancellation": futuresLimitOrderMassCancelationEPL,
"cancel Untriggered Futures Stop Orders": cancelUntriggeredFuturesStopOrdersEPL,
"futures Cancel Multiple Limit Orders": futuresCancelMultipleLimitOrdersEPL,
"futures Retrieve Order List": futuresRetrieveOrderListEPL,
"futures Recent Completed Orders": futuresRecentCompletedOrdersEPL,
"futures Orders By ID": futuresOrdersByIDEPL,
"futures Retrieve Fills": futuresRetrieveFillsEPL,
"futures Recent Fills": futuresRecentFillsEPL,
"futures Open Order Stats": futuresOpenOrderStatsEPL,
"futures Position": futuresPositionEPL,
"futures Position List": futuresPositionListEPL,
"set Auto Deposit Margin": setAutoDepositMarginEPL,
"max Withdraw Margin": maxWithdrawMarginEPL,
"remove Margin Manually": removeMarginManuallyEPL,
"futures Add Margin Manually": futuresAddMarginManuallyEPL,
"futures Risk Limit Level": futuresRiskLimitLevelEPL,
"futures Update Risk Limit Level": futuresUpdateRiskLimitLevelEPL,
"futures Current Funding Rate": futuresCurrentFundingRateEPL,
"futures Public Funding Rate": futuresPublicFundingRateEPL,
"futures Funding History": futuresFundingHistoryEPL,
"spot Authentication": spotAuthenticationEPL,
"futures Authentication": futuresAuthenticationEPL,
"futures Order Details By Client Order ID": futuresOrderDetailsByClientOrderIDEPL,
"modify Sub Account API": modifySubAccountAPIEPL,
"all Sub Accounts Balance": allSubAccountsBalanceEPL,
"all User Sub Accounts V2": allUserSubAccountsV2EPL,
"futures Retrieve Transaction History": futuresRetrieveTransactionHistoryEPL,
"futures Account Overview": futuresAccountOverviewEPL,
"create Sub Account API Key": createSubAccountAPIKeyEPL,
"transfer Out To Main": transferOutToMainEPL,
"transfer Fund To Futures Account": transferFundToFuturesAccountEPL,
"futures Transfer Out List": futuresTransferOutListEPL,
}
rl, err := request.New("rateLimitTest2", http.DefaultClient, request.WithLimiter(GetRateLimit()))
require.NoError(t, err)
for name, tt := range testTable {
t.Run(name, func(t *testing.T) {
t.Parallel()
if err := rl.InitiateRateLimit(context.Background(), tt); err != nil {
t.Fatalf("error applying rate limit: %v", err)
}
})
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -38,7 +38,7 @@ const (
publicBullets = "/v1/bullet-public"
privateBullets = "/v1/bullet-private"
// spot channels
// Spot channels
marketTickerChannel = "/market/ticker" // /market/ticker:{symbol},...
marketSnapshotChannel = "/market/snapshot" // /market/snapshot:{symbol},...
marketOrderbookChannel = "/market/level2" // /market/level2:{symbol},...
@@ -56,21 +56,24 @@ const (
marginLoanChannel = "/margin/loan" // /margin/loan:{currency}
spotMarketAdvancedChannel = "/spotMarket/advancedOrders"
// futures channels
futuresTickerChannel = "/contractMarket/tickerV2" // /contractMarket/tickerV2:{symbol},...
futuresOrderbookChannel = "/contractMarket/level2" // /contractMarket/level2:{symbol},...
futuresOrderbookDepth5Channel = "/contractMarket/level2Depth5" // /contractMarket/level2Depth5:{symbol},...
futuresOrderbookDepth50Channel = "/contractMarket/level2Depth50" // /contractMarket/level2Depth50:{symbol},...
futuresExecutionDataChannel = "/contractMarket/execution" // /contractMarket/execution:{symbol},...
futuresContractMarketDataChannel = "/contract/instrument" // /contract/instrument:{symbol},...
futuresSystemAnnouncementChannel = "/contract/announcement"
futuresTrasactionStatisticsTimerEventChannel = "/contractMarket/snapshot" // /contractMarket/snapshot:{symbol},...
// Futures channels
futuresTransactionStatisticsTimerEventChannel = "/contractMarket/snapshot" // /contractMarket/snapshot:{symbol}
futuresTickerChannel = "/contractMarket/tickerV2" // /contractMarket/tickerV2:{symbol},...
futuresOrderbookChannel = "/contractMarket/level2" // /contractMarket/level2:{symbol},...
futuresOrderbookDepth5Channel = "/contractMarket/level2Depth5" // /contractMarket/level2Depth5:{symbol},...
futuresOrderbookDepth50Channel = "/contractMarket/level2Depth50" // /contractMarket/level2Depth50:{symbol},...
futuresExecutionDataChannel = "/contractMarket/execution" // /contractMarket/execution:{symbol},...
futuresContractMarketDataChannel = "/contract/instrument" // /contract/instrument:{symbol},...
futuresSystemAnnouncementChannel = "/contract/announcement"
futuresTrasactionStatisticsTimerEventChannel = "/contractMarket/snapshot" // /contractMarket/snapshot:{symbol},...
// futures private channels
futuresTradeOrderChannel = "/contractMarket/tradeOrders" // /contractMarket/tradeOrders:{symbol},...
futuresPositionChangeEventChannel = "/contract/position" // /contract/position:{symbol},...
futuresStopOrdersLifecycleEventChannel = "/contractMarket/advancedOrders"
futuresAccountBalanceEventChannel = "/contractAccount/wallet"
futuresLimitCandles = "/contractMarket/limitCandle"
)
var (
@@ -174,9 +177,9 @@ func (ku *Kucoin) GetAuthenticatedInstanceServers(ctx context.Context) (*WSInsta
Data *WSInstanceServers `json:"data"`
Error
}{}
err := ku.SendAuthHTTPRequest(ctx, exchange.RestSpot, defaultSpotEPL, http.MethodPost, privateBullets, nil, &response)
err := ku.SendAuthHTTPRequest(ctx, exchange.RestSpot, spotAuthenticationEPL, http.MethodPost, privateBullets, nil, &response)
if err != nil && strings.Contains(err.Error(), "400003") {
return response.Data, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, http.MethodPost, privateBullets, nil, &response)
return response.Data, ku.SendAuthHTTPRequest(ctx, exchange.RestFutures, futuresAuthenticationEPL, http.MethodPost, privateBullets, nil, &response)
}
return response.Data, err
}
@@ -196,6 +199,7 @@ func (ku *Kucoin) wsReadData() {
}
}
// wsHandleData processes a websocket incoming data.
func (ku *Kucoin) wsHandleData(respData []byte) error {
resp := WsPushData{}
err := json.Unmarshal(respData, &resp)
@@ -268,7 +272,8 @@ func (ku *Kucoin) wsHandleData(respData []byte) error {
return err
}
return ku.processFuturesOrderbookLevel2(resp.Data, topicInfo[1])
case futuresOrderbookDepth5Channel, futuresOrderbookDepth50Channel:
case futuresOrderbookDepth5Channel,
futuresOrderbookDepth50Channel:
if err := ku.ensureFuturesOrderbookSnapshotLoaded(topicInfo[1]); err != nil {
return err
}
@@ -282,7 +287,7 @@ func (ku *Kucoin) wsHandleData(respData []byte) error {
}
case futuresSystemAnnouncementChannel:
return ku.processFuturesSystemAnnouncement(resp.Data, resp.Subject)
case futuresTrasactionStatisticsTimerEventChannel:
case futuresTransactionStatisticsTimerEventChannel:
return ku.processFuturesTransactionStatistics(resp.Data, topicInfo[1])
case futuresTradeOrderChannel:
return ku.processFuturesPrivateTradeOrders(resp.Data)
@@ -312,6 +317,12 @@ func (ku *Kucoin) wsHandleData(respData []byte) error {
var response WsFuturesPositionFundingSettlement
return ku.processData(resp.Data, &response)
}
case futuresLimitCandles:
instrumentInfos := strings.Split(topicInfo[1], "_")
if len(instrumentInfos) != 2 {
return errors.New("invalid instrument information")
}
return ku.processFuturesKline(resp.Data, instrumentInfos[1])
default:
ku.Websocket.DataHandler <- stream.UnhandledMessageWarning{
Message: ku.Name + stream.UnhandledMessage + string(respData),
@@ -321,6 +332,7 @@ func (ku *Kucoin) wsHandleData(respData []byte) error {
return nil
}
// processData used to deserialize and forward the data to DataHandler.
func (ku *Kucoin) processData(respData []byte, resp interface{}) error {
if err := json.Unmarshal(respData, &resp); err != nil {
return err
@@ -329,6 +341,7 @@ func (ku *Kucoin) processData(respData []byte, resp interface{}) error {
return nil
}
// processFuturesAccountBalanceEvent used to process futures account balance change incoming data.
func (ku *Kucoin) processFuturesAccountBalanceEvent(respData []byte) error {
resp := WsFuturesAvailableBalance{}
if err := json.Unmarshal(respData, &resp); err != nil {
@@ -343,6 +356,7 @@ func (ku *Kucoin) processFuturesAccountBalanceEvent(respData []byte) error {
return nil
}
// processFuturesStopOrderLifecycleEvent processes futures stop orders lifecycle events.
func (ku *Kucoin) processFuturesStopOrderLifecycleEvent(respData []byte) error {
resp := WsStopOrderLifecycleEvent{}
err := json.Unmarshal(respData, &resp)
@@ -382,6 +396,7 @@ func (ku *Kucoin) processFuturesStopOrderLifecycleEvent(respData []byte) error {
return nil
}
// processFuturesPrivateTradeOrders processes futures private trade orders updates.
func (ku *Kucoin) processFuturesPrivateTradeOrders(respData []byte) error {
resp := WsFuturesTradeOrder{}
if err := json.Unmarshal(respData, &resp); err != nil {
@@ -391,7 +406,7 @@ func (ku *Kucoin) processFuturesPrivateTradeOrders(respData []byte) error {
if err != nil {
return err
}
oStatus, err := ku.stringToOrderStatus(resp.Status)
oStatus, err := ku.StringToOrderStatus(resp.Status)
if err != nil {
return err
}
@@ -426,6 +441,7 @@ func (ku *Kucoin) processFuturesPrivateTradeOrders(respData []byte) error {
return nil
}
// processFuturesTransactionStatistics processes a futures transaction statistics
func (ku *Kucoin) processFuturesTransactionStatistics(respData []byte, instrument string) error {
resp := WsFuturesTransactionStatisticsTimeEvent{}
if err := json.Unmarshal(respData, &resp); err != nil {
@@ -435,6 +451,7 @@ func (ku *Kucoin) processFuturesTransactionStatistics(respData []byte, instrumen
return nil
}
// processFuturesSystemAnnouncement processes a system announcement.
func (ku *Kucoin) processFuturesSystemAnnouncement(respData []byte, subject string) error {
resp := WsFuturesFundingBegin{}
if err := json.Unmarshal(respData, &resp); err != nil {
@@ -445,6 +462,7 @@ func (ku *Kucoin) processFuturesSystemAnnouncement(respData []byte, subject stri
return nil
}
// processFuturesFundingData processes a futures account funding data.
func (ku *Kucoin) processFuturesFundingData(respData []byte, instrument string) error {
resp := WsFundingRate{}
if err := json.Unmarshal(respData, &resp); err != nil {
@@ -455,6 +473,7 @@ func (ku *Kucoin) processFuturesFundingData(respData []byte, instrument string)
return nil
}
// processFuturesMarkPriceAndIndexPrice processes a futures account mark price and index price changes.
func (ku *Kucoin) processFuturesMarkPriceAndIndexPrice(respData []byte, instrument string) error {
resp := WsFuturesMarkPriceAndIndexPrice{}
if err := json.Unmarshal(respData, &resp); err != nil {
@@ -488,6 +507,7 @@ func (ku *Kucoin) ensureFuturesOrderbookSnapshotLoaded(symbol string) error {
return ku.Websocket.Orderbook.LoadSnapshot(orderbooks)
}
// processFuturesOrderbookSnapshot processes a futures account orderbook websocket update.
func (ku *Kucoin) processFuturesOrderbookSnapshot(respData []byte, instrument string) error {
response := WsOrderbookLevel5Response{}
if err := json.Unmarshal(respData, &response); err != nil {
@@ -512,6 +532,7 @@ func (ku *Kucoin) processFuturesOrderbookSnapshot(respData []byte, instrument st
})
}
// ProcessFuturesOrderbookLevel2 processes a V2 futures account orderbook data.
func (ku *Kucoin) processFuturesOrderbookLevel2(respData []byte, instrument string) error {
resp := WsFuturesOrderbookInfo{}
if err := json.Unmarshal(respData, &resp); err != nil {
@@ -539,6 +560,7 @@ func (ku *Kucoin) processFuturesOrderbookLevel2(respData []byte, instrument stri
return ku.Websocket.Orderbook.Update(&base)
}
// processFuturesTickerV2 processes a futures account ticker data.
func (ku *Kucoin) processFuturesTickerV2(respData []byte) error {
resp := WsFuturesTicker{}
if err := json.Unmarshal(respData, &resp); err != nil {
@@ -554,19 +576,48 @@ func (ku *Kucoin) processFuturesTickerV2(respData []byte) error {
}
ku.Websocket.DataHandler <- &ticker.Price{
AssetType: asset.Futures,
Last: resp.FilledPrice,
Volume: resp.FilledSize,
Last: resp.FilledPrice.Float64(),
Volume: resp.FilledSize.Float64(),
LastUpdated: resp.FilledTime.Time(),
ExchangeName: ku.Name,
Pair: pair,
Ask: resp.BestAskPrice.Float64(),
Bid: resp.BestBidPrice.Float64(),
AskSize: resp.BestAskSize,
BidSize: resp.BestBidSize,
AskSize: resp.BestAskSize.Float64(),
BidSize: resp.BestBidSize.Float64(),
}
return nil
}
// processFuturesKline represents a futures instrument kline data update.
func (ku *Kucoin) processFuturesKline(respData []byte, intervalStr string) error {
resp := WsFuturesKline{}
err := json.Unmarshal(respData, &resp)
if err != nil {
return err
}
var pair currency.Pair
pair, err = currency.NewPairFromString(resp.Symbol)
if err != nil {
return err
}
ku.Websocket.DataHandler <- &stream.KlineData{
Timestamp: resp.Time.Time(),
AssetType: asset.Futures,
Exchange: ku.Name,
StartTime: time.Unix(resp.Candles[0].Int64(), 0),
Interval: intervalStr,
OpenPrice: resp.Candles[1].Float64(),
ClosePrice: resp.Candles[2].Float64(),
HighPrice: resp.Candles[3].Float64(),
LowPrice: resp.Candles[4].Float64(),
Volume: resp.Candles[6].Float64(),
Pair: pair,
}
return nil
}
// processStopOrderEvent represents a stop order update event.
func (ku *Kucoin) processStopOrderEvent(respData []byte) error {
resp := WsStopOrder{}
err := json.Unmarshal(respData, &resp)
@@ -602,6 +653,7 @@ func (ku *Kucoin) processStopOrderEvent(respData []byte) error {
return nil
}
// processMarginLendingTradeOrderEvent represents a margin lending trade order event.
func (ku *Kucoin) processMarginLendingTradeOrderEvent(respData []byte) error {
resp := WsMarginTradeOrderEntersEvent{}
if err := json.Unmarshal(respData, &resp); err != nil {
@@ -611,6 +663,7 @@ func (ku *Kucoin) processMarginLendingTradeOrderEvent(respData []byte) error {
return nil
}
// processAccountBalanceChange processes an account balance change
func (ku *Kucoin) processAccountBalanceChange(respData []byte) error {
response := WsAccountBalance{}
err := json.Unmarshal(respData, &response)
@@ -626,6 +679,7 @@ func (ku *Kucoin) processAccountBalanceChange(respData []byte) error {
return nil
}
// processOrderChangeEvent processes order update events.
func (ku *Kucoin) processOrderChangeEvent(respData []byte, topic string) error {
response := WsTradeOrder{}
err := json.Unmarshal(respData, &response)
@@ -636,7 +690,7 @@ func (ku *Kucoin) processOrderChangeEvent(respData []byte, topic string) error {
if err != nil {
return err
}
oStatus, err := ku.stringToOrderStatus(response.Status)
oStatus, err := ku.StringToOrderStatus(response.Status)
if err != nil {
return err
}
@@ -674,6 +728,7 @@ func (ku *Kucoin) processOrderChangeEvent(respData []byte, topic string) error {
return nil
}
// processTradeData processes a websocket trade data and instruments.
func (ku *Kucoin) processTradeData(respData []byte, instrument, topic string) error {
response := WsTrade{}
err := json.Unmarshal(respData, &response)
@@ -715,6 +770,7 @@ func (ku *Kucoin) processTradeData(respData []byte, instrument, topic string) er
return nil
}
// processTicker processes a ticker data for an instrument.
func (ku *Kucoin) processTicker(respData []byte, instrument, topic string) error {
response := WsTicker{}
err := json.Unmarshal(respData, &response)
@@ -749,6 +805,7 @@ func (ku *Kucoin) processTicker(respData []byte, instrument, topic string) error
return nil
}
// processCandlesticks processes a candlestick data for an instrument with a particular interval
func (ku *Kucoin) processCandlesticks(respData []byte, instrument, intervalString, topic string) error {
pair, err := currency.NewPairFromString(instrument)
if err != nil {
@@ -788,6 +845,7 @@ func (ku *Kucoin) processCandlesticks(respData []byte, instrument, intervalStrin
return nil
}
// processOrderbookWithDepth processes order book data with a specified depth for a particular symbol.
func (ku *Kucoin) processOrderbookWithDepth(respData []byte, instrument, topic string) error {
pair, err := currency.NewPairFromString(instrument)
if err != nil {
@@ -806,7 +864,7 @@ func (ku *Kucoin) processOrderbookWithDepth(respData []byte, instrument, topic s
}
for x := range assets {
var init bool
init, err = ku.UpdateLocalBuffer(result.Result, assets[x])
init, err = ku.updateLocalBuffer(result.Result, assets[x])
if err != nil {
if init {
return nil
@@ -817,9 +875,9 @@ func (ku *Kucoin) processOrderbookWithDepth(respData []byte, instrument, topic s
return nil
}
// UpdateLocalBuffer updates orderbook buffer and checks status if the book is Initial Sync being via the REST
// updateLocalBuffer updates orderbook buffer and checks status if the book is Initial Sync being via the REST
// protocol.
func (ku *Kucoin) UpdateLocalBuffer(wsdp *WsOrderbook, assetType asset.Item) (bool, error) {
func (ku *Kucoin) updateLocalBuffer(wsdp *WsOrderbook, assetType asset.Item) (bool, error) {
enabledPairs, err := ku.GetEnabledPairs(assetType)
if err != nil {
return false, err
@@ -836,9 +894,9 @@ func (ku *Kucoin) UpdateLocalBuffer(wsdp *WsOrderbook, assetType asset.Item) (bo
if err != nil {
return false, err
}
err = ku.obm.stageWsUpdate(wsdp, currencyPair, assetType)
err = ku.obm.StageWsUpdate(wsdp, currencyPair, assetType)
if err != nil {
init, err2 := ku.obm.checkIsInitialSync(currencyPair, assetType)
init, err2 := ku.obm.CheckIsInitialSync(currencyPair, assetType)
if err2 != nil {
return false, err2
}
@@ -847,12 +905,13 @@ func (ku *Kucoin) UpdateLocalBuffer(wsdp *WsOrderbook, assetType asset.Item) (bo
err = ku.applyBufferUpdate(currencyPair, assetType)
if err != nil {
ku.flushAndCleanup(currencyPair, assetType)
ku.FlushAndCleanup(currencyPair, assetType)
}
return false, err
}
// processOrderbook processes orderbook data for a specific symbol.
func (ku *Kucoin) processOrderbook(respData []byte, symbol, topic string) error {
var response Level2Depth5Or20
err := json.Unmarshal(respData, &response)
@@ -882,8 +941,10 @@ func (ku *Kucoin) processOrderbook(respData []byte, symbol, topic string) error
return err
}
lastUpdated := time.UnixMilli(response.Timestamp)
var lastUpdatedTime = response.Timestamp.Time()
if response.Timestamp.Time().IsZero() {
lastUpdatedTime = time.Now()
}
for x := range assets {
err = ku.Websocket.Orderbook.LoadSnapshot(&orderbook.Base{
Exchange: ku.Name,
@@ -891,7 +952,7 @@ func (ku *Kucoin) processOrderbook(respData []byte, symbol, topic string) error
Bids: bids,
Pair: pair,
Asset: assets[x],
LastUpdated: lastUpdated,
LastUpdated: lastUpdatedTime,
})
if err != nil {
return err
@@ -900,6 +961,7 @@ func (ku *Kucoin) processOrderbook(respData []byte, symbol, topic string) error
return nil
}
// processMarketSnapshot processes a price ticker information for a symbol.
func (ku *Kucoin) processMarketSnapshot(respData []byte, topic string) error {
response := WsSnapshot{}
err := json.Unmarshal(respData, &response)
@@ -1038,6 +1100,7 @@ type job struct {
AssetType asset.Item
}
// setupOrderbookManager sets up the orderbook manager for websocket orderbook data handling.
func (ku *Kucoin) setupOrderbookManager() {
locker.Lock()
defer locker.Unlock()
@@ -1066,46 +1129,23 @@ func (ku *Kucoin) setupOrderbookManager() {
}
}
// ProcessUpdate processes the websocket orderbook update
func (ku *Kucoin) ProcessUpdate(cp currency.Pair, a asset.Item, ws *WsOrderbook) error {
// processUpdate processes the websocket orderbook update
func (ku *Kucoin) processUpdate(cp currency.Pair, a asset.Item, ws *WsOrderbook) error {
updateBid := make([]orderbook.Tranche, len(ws.Changes.Bids))
for i := range ws.Changes.Bids {
p, err := strconv.ParseFloat(ws.Changes.Bids[i][0], 64)
if err != nil {
return err
}
a, err := strconv.ParseFloat(ws.Changes.Bids[i][1], 64)
if err != nil {
return err
}
var sequence int64
if len(ws.Changes.Bids[i]) > 2 && ws.Changes.Bids[i][2] != "" {
sequence, err = strconv.ParseInt(ws.Changes.Bids[i][2], 10, 64)
if err != nil {
return err
}
if len(ws.Changes.Bids[i]) > 2 {
sequence = ws.Changes.Bids[i][2].Int64()
}
updateBid[i] = orderbook.Tranche{Price: p, Amount: a, ID: sequence}
updateBid[i] = orderbook.Tranche{Price: ws.Changes.Bids[i][0].Float64(), Amount: ws.Changes.Bids[i][1].Float64(), ID: sequence}
}
updateAsk := make([]orderbook.Tranche, len(ws.Changes.Asks))
for i := range ws.Changes.Asks {
p, err := strconv.ParseFloat(ws.Changes.Asks[i][0], 64)
if err != nil {
return err
}
a, err := strconv.ParseFloat(ws.Changes.Asks[i][1], 64)
if err != nil {
return err
}
var sequence int64
if len(ws.Changes.Asks[i]) > 2 && ws.Changes.Asks[i][2] != "" {
sequence, err = strconv.ParseInt(ws.Changes.Asks[i][2], 10, 64)
if err != nil {
return err
}
if len(ws.Changes.Asks[i]) > 2 {
sequence = ws.Changes.Asks[i][2].Int64()
}
updateAsk[i] = orderbook.Tranche{Price: p, Amount: a, ID: sequence}
updateAsk[i] = orderbook.Tranche{Price: ws.Changes.Asks[i][0].Float64(), Amount: ws.Changes.Asks[i][1].Float64(), ID: sequence}
}
return ku.Websocket.Orderbook.Update(&orderbook.Update{
@@ -1121,7 +1161,7 @@ func (ku *Kucoin) ProcessUpdate(cp currency.Pair, a asset.Item, ws *WsOrderbook)
// applyBufferUpdate applies the buffer to the orderbook or initiates a new
// orderbook sync by the REST protocol which is off handed to go routine.
func (ku *Kucoin) applyBufferUpdate(pair currency.Pair, assetType asset.Item) error {
fetching, needsFetching, err := ku.obm.handleFetchingBook(pair, assetType)
fetching, needsFetching, err := ku.obm.HandleFetchingBook(pair, assetType)
if err != nil {
return err
}
@@ -1132,7 +1172,7 @@ func (ku *Kucoin) applyBufferUpdate(pair currency.Pair, assetType asset.Item) er
if ku.Verbose {
log.Debugf(log.WebsocketMgr, "%s Orderbook: Fetching via REST\n", ku.Name)
}
return ku.obm.fetchBookViaREST(pair, assetType)
return ku.obm.FetchBookViaREST(pair, assetType)
}
recent, err := ku.Websocket.Orderbook.GetOrderbook(pair, assetType)
@@ -1145,7 +1185,7 @@ func (ku *Kucoin) applyBufferUpdate(pair currency.Pair, assetType asset.Item) er
}
if recent != nil {
err = ku.obm.checkAndProcessUpdate(ku.ProcessUpdate, pair, assetType, recent)
err = ku.obm.CheckAndProcessUpdate(ku.processUpdate, pair, assetType, recent)
if err != nil {
log.Errorf(
log.WebsocketMgr,
@@ -1249,7 +1289,7 @@ func (ku *Kucoin) SeedLocalCacheWithBook(p currency.Pair, orderbookNew *Orderboo
func (ku *Kucoin) processJob(p currency.Pair, assetType asset.Item) error {
err := ku.SeedLocalCache(context.TODO(), p, assetType)
if err != nil {
err = ku.obm.stopFetchingBook(p, assetType)
err = ku.obm.StopFetchingBook(p, assetType)
if err != nil {
return err
}
@@ -1257,7 +1297,7 @@ func (ku *Kucoin) processJob(p currency.Pair, assetType asset.Item) error {
p, assetType, err)
}
err = ku.obm.stopFetchingBook(p, assetType)
err = ku.obm.StopFetchingBook(p, assetType)
if err != nil {
return err
}
@@ -1266,14 +1306,14 @@ func (ku *Kucoin) processJob(p currency.Pair, assetType asset.Item) error {
// new update to initiate this.
err = ku.applyBufferUpdate(p, assetType)
if err != nil {
ku.flushAndCleanup(p, assetType)
ku.FlushAndCleanup(p, assetType)
return err
}
return nil
}
// flushAndCleanup flushes orderbook and clean local cache
func (ku *Kucoin) flushAndCleanup(p currency.Pair, assetType asset.Item) {
// FlushAndCleanup flushes orderbook and clean local cache
func (ku *Kucoin) FlushAndCleanup(p currency.Pair, assetType asset.Item) {
errClean := ku.Websocket.Orderbook.FlushOrderbook(p, assetType)
if errClean != nil {
log.Errorf(log.WebsocketMgr,
@@ -1281,7 +1321,7 @@ func (ku *Kucoin) flushAndCleanup(p currency.Pair, assetType asset.Item) {
ku.Name,
errClean)
}
errClean = ku.obm.cleanup(p, assetType)
errClean = ku.obm.Cleanup(p, assetType)
if errClean != nil {
log.Errorf(log.WebsocketMgr, "%s cleanup websocket error: %v",
ku.Name,
@@ -1289,9 +1329,9 @@ func (ku *Kucoin) flushAndCleanup(p currency.Pair, assetType asset.Item) {
}
}
// stageWsUpdate stages websocket update to roll through updates that need to
// StageWsUpdate stages websocket update to roll through updates that need to
// be applied to a fetched orderbook via REST.
func (o *orderbookManager) stageWsUpdate(u *WsOrderbook, pair currency.Pair, a asset.Item) error {
func (o *orderbookManager) StageWsUpdate(u *WsOrderbook, pair currency.Pair, a asset.Item) error {
o.Lock()
defer o.Unlock()
m1, ok := o.state[pair.Base]
@@ -1336,9 +1376,9 @@ func (o *orderbookManager) stageWsUpdate(u *WsOrderbook, pair currency.Pair, a a
}
}
// handleFetchingBook checks if a full book is being fetched or needs to be
// HandleFetchingBook checks if a full book is being fetched or needs to be
// fetched
func (o *orderbookManager) handleFetchingBook(pair currency.Pair, assetType asset.Item) (fetching, needsFetching bool, err error) {
func (o *orderbookManager) HandleFetchingBook(pair currency.Pair, assetType asset.Item) (fetching, needsFetching bool, err error) {
o.Lock()
defer o.Unlock()
state, ok := o.state[pair.Base][pair.Quote][assetType]
@@ -1362,8 +1402,8 @@ func (o *orderbookManager) handleFetchingBook(pair currency.Pair, assetType asse
return false, false, nil
}
// stopFetchingBook completes the book fetching.
func (o *orderbookManager) stopFetchingBook(pair currency.Pair, assetType asset.Item) error {
// StopFetchingBook completes the book fetching.
func (o *orderbookManager) StopFetchingBook(pair currency.Pair, assetType asset.Item) error {
o.Lock()
defer o.Unlock()
state, ok := o.state[pair.Base][pair.Quote][assetType]
@@ -1381,8 +1421,8 @@ func (o *orderbookManager) stopFetchingBook(pair currency.Pair, assetType asset.
return nil
}
// completeInitialSync sets if an asset type has completed its initial sync
func (o *orderbookManager) completeInitialSync(pair currency.Pair, assetType asset.Item) error {
// CompleteInitialSync sets if an asset type has completed its initial sync
func (o *orderbookManager) CompleteInitialSync(pair currency.Pair, assetType asset.Item) error {
o.Lock()
defer o.Unlock()
state, ok := o.state[pair.Base][pair.Quote][assetType]
@@ -1400,9 +1440,9 @@ func (o *orderbookManager) completeInitialSync(pair currency.Pair, assetType ass
return nil
}
// checkIsInitialSync checks status if the book is Initial Sync being via the REST
// CheckIsInitialSync checks status if the book is Initial Sync being via the REST
// protocol.
func (o *orderbookManager) checkIsInitialSync(pair currency.Pair, assetType asset.Item) (bool, error) {
func (o *orderbookManager) CheckIsInitialSync(pair currency.Pair, assetType asset.Item) (bool, error) {
o.Lock()
defer o.Unlock()
state, ok := o.state[pair.Base][pair.Quote][assetType]
@@ -1415,9 +1455,9 @@ func (o *orderbookManager) checkIsInitialSync(pair currency.Pair, assetType asse
return state.initialSync, nil
}
// fetchBookViaREST pushes a job of fetching the orderbook via the REST protocol
// FetchBookViaREST pushes a job of fetching the orderbook via the REST protocol
// to get an initial full book that we can apply our buffered updates too.
func (o *orderbookManager) fetchBookViaREST(pair currency.Pair, assetType asset.Item) error {
func (o *orderbookManager) FetchBookViaREST(pair currency.Pair, assetType asset.Item) error {
o.Lock()
defer o.Unlock()
@@ -1441,7 +1481,7 @@ func (o *orderbookManager) fetchBookViaREST(pair currency.Pair, assetType asset.
}
}
func (o *orderbookManager) checkAndProcessUpdate(processor func(currency.Pair, asset.Item, *WsOrderbook) error, pair currency.Pair, assetType asset.Item, recent *orderbook.Base) error {
func (o *orderbookManager) CheckAndProcessUpdate(processor func(currency.Pair, asset.Item, *WsOrderbook) error, pair currency.Pair, assetType asset.Item, recent *orderbook.Base) error {
o.Lock()
defer o.Unlock()
state, ok := o.state[pair.Base][pair.Quote][assetType]
@@ -1456,7 +1496,7 @@ buffer:
for {
select {
case d := <-state.buffer:
process, err := state.validate(d, recent)
process, err := state.Validate(d, recent)
if err != nil {
return err
}
@@ -1474,8 +1514,8 @@ buffer:
return nil
}
// validate checks for correct update alignment
func (u *update) validate(updt *WsOrderbook, recent *orderbook.Base) (bool, error) {
// Validate checks for correct update alignment
func (u *update) Validate(updt *WsOrderbook, recent *orderbook.Base) (bool, error) {
if updt.SequenceEnd <= recent.LastUpdateID {
// Drop any event where u is <= lastUpdateId in the snapshot.
return false, nil
@@ -1495,8 +1535,8 @@ func (u *update) validate(updt *WsOrderbook, recent *orderbook.Base) (bool, erro
return true, nil
}
// cleanup cleans up buffer and reset fetch and init
func (o *orderbookManager) cleanup(pair currency.Pair, assetType asset.Item) error {
// Cleanup cleans up buffer and reset fetch and init
func (o *orderbookManager) Cleanup(pair currency.Pair, assetType asset.Item) error {
o.Lock()
state, ok := o.state[pair.Base][pair.Quote][assetType]
if !ok {
@@ -1517,14 +1557,14 @@ bufferEmpty:
}
o.Unlock()
// disable rest orderbook synchronisation
_ = o.stopFetchingBook(pair, assetType)
_ = o.completeInitialSync(pair, assetType)
_ = o.stopNeedsFetchingBook(pair, assetType)
_ = o.StopFetchingBook(pair, assetType)
_ = o.CompleteInitialSync(pair, assetType)
_ = o.StopNeedsFetchingBook(pair, assetType)
return nil
}
// stopNeedsFetchingBook completes the book fetching initiation.
func (o *orderbookManager) stopNeedsFetchingBook(pair currency.Pair, assetType asset.Item) error {
// StopNeedsFetchingBook completes the book fetching initiation.
func (o *orderbookManager) StopNeedsFetchingBook(pair currency.Pair, assetType asset.Item) error {
o.Lock()
defer o.Unlock()
state, ok := o.state[pair.Base][pair.Quote][assetType]
@@ -1677,7 +1717,7 @@ func isCurrencyChannel(s *subscription.Subscription) bool {
// channelInterval returns the channel interval if it has one
func channelInterval(s *subscription.Subscription) string {
if channelName(s, s.Asset) == marketCandlesChannel {
if i, err := intervalToString(s.Interval); err == nil {
if i, err := IntervalToString(s.Interval); err == nil {
return i
}
}
@@ -1700,7 +1740,7 @@ func assetCurrencies(s *subscription.Subscription, ap map[asset.Item]currency.Pa
// If the subscription has a viable interval it's appended after each symbol
func joinPairsWithInterval(b currency.Pairs, s *subscription.Subscription) string {
out := make([]string, len(b))
suffix, err := intervalToString(s.Interval)
suffix, err := IntervalToString(s.Interval)
if err == nil {
suffix = "_" + suffix
}

File diff suppressed because it is too large Load Diff

View File

@@ -89,6 +89,9 @@ type Submit struct {
// Hidden when enabled orders not displaying in order book.
Hidden bool
// Iceberg specifies whether or not only visible portions of orders are shown in iceberg orders
Iceberg bool
}
// SubmitResponse is what is returned after submitting an order to an exchange
@@ -301,6 +304,8 @@ type MultiOrderRequest struct {
// FromOrderID for some APIs require order history searching
// from a specific orderID rather than via timestamps
FromOrderID string
MarginType margin.Type
}
// Status defines order status types
@@ -418,7 +423,7 @@ type RiskManagement struct {
TriggerPriceType PriceType
Price float64
// LimitPrice limit order price when stop-los or take-profit risk management method is triggered
// LimitPrice limit order price when stop-loss or take-profit risk management method is triggered
LimitPrice float64
// OrderType order type when stop-loss or take-profit risk management method is triggered.
OrderType Type
@@ -430,6 +435,10 @@ type RiskManagementModes struct {
Mode string
TakeProfit RiskManagement
StopLoss RiskManagement
// StopEntry stop: 'entry': Triggers when the last trade price changes to a value at or above the stopPrice.
// see: https://www.kucoin.com/docs/rest/spot-trading/stop-order/introduction
StopEntry RiskManagement
}
// PriceType enforces a standard for price types used for take-profit and stop-loss trigger types