exchanges/request: abstract and consolidate rate limiting code to request package (#1477)

* initial consolidation of rate limiting code to request package to reduce bespoke code implementation

* continued

* finish abstraction

* lint

* exchanges: fix tests

* linter: fix

* poloniex: fix auth rate limit not being set

* ratelimiter: convert from token to weight

* glorious: nits addressed with fire

* linter: rip

* change func name set -> get

* fix test

* derbit: impl

---------

Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>
This commit is contained in:
Ryan O'Hara-Reid
2024-06-03 11:57:31 +10:00
committed by GitHub
parent aeb4a87913
commit f6a95da536
64 changed files with 780 additions and 2577 deletions

View File

@@ -178,7 +178,7 @@ func (by *Bybit) SetDefaults() {
by.Requester, err = request.New(by.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
request.WithLimiter(SetRateLimit()))
request.WithLimiter(GetRateLimit()))
if err != nil {
log.Errorln(log.ExchangeSys, err)
}

View File

@@ -1,17 +1,9 @@
package bybit
import (
"context"
"fmt"
"time"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
"golang.org/x/time/rate"
)
const (
// See: https://bybit-exchange.github.io/docs/v5/rate-limit
spotInterval = time.Second * 5
)
const (
@@ -76,281 +68,67 @@ const (
spotCrossMarginTradeSwitchEPL
)
// RateLimit implements the request.Limiter interface
type RateLimit struct {
SpotRate *rate.Limiter
CreateOrderRate *rate.Limiter
CreateSpotOrderRate *rate.Limiter
AmendOrderRate *rate.Limiter
CancelOrderRate *rate.Limiter
CancelSpotRate *rate.Limiter
CancelAllRate *rate.Limiter
CancelAllSpotRate *rate.Limiter
CreateBatchOrderRate *rate.Limiter
AmendBatchOrderRate *rate.Limiter
CancelBatchOrderRate *rate.Limiter
GetOrderRate *rate.Limiter
GetOrderHistoryRate *rate.Limiter
GetPositionListRate *rate.Limiter
GetExecutionListRate *rate.Limiter
GetPositionClosedPNLRate *rate.Limiter
PostPositionSetLeverageRate *rate.Limiter
SetPositionTPLSModeRate *rate.Limiter
SetPositionRiskLimitRate *rate.Limiter
StopTradingPositionRate *rate.Limiter
GetAccountWalletBalanceRate *rate.Limiter
GetAccountFeeRate *rate.Limiter
GetAssetTransferQueryInfoRate *rate.Limiter
GetAssetTransferQueryTransferCoinListRate *rate.Limiter
GetAssetTransferCoinListRate *rate.Limiter
GetAssetInterTransferListRate *rate.Limiter
GetSubMemberListRate *rate.Limiter
GetAssetUniversalTransferListRate *rate.Limiter
GetAssetAccountCoinBalanceRate *rate.Limiter
GetAssetDepositRecordsRate *rate.Limiter
GetAssetDepositSubMemberRecordsRate *rate.Limiter
GetAssetDepositSubMemberAddressRate *rate.Limiter
GetWithdrawRecordsRate *rate.Limiter
GetAssetCoinInfoRate *rate.Limiter
GetExchangeOrderRecordRate *rate.Limiter
InterTransferRate *rate.Limiter
SaveTransferSubMemberRate *rate.Limiter
UniversalTransferRate *rate.Limiter
CreateWithdrawalRate *rate.Limiter
CancelWithdrawalRate *rate.Limiter
UserCreateSubMemberRate *rate.Limiter
UserCreateSubAPIKeyRate *rate.Limiter
UserFrozenSubMemberRate *rate.Limiter
UserUpdateAPIRate *rate.Limiter
UserUpdateSubAPIRate *rate.Limiter
UserDeleteAPIRate *rate.Limiter
UserDeleteSubAPIRate *rate.Limiter
UserQuerySubMembersRate *rate.Limiter
UserQueryAPIRate *rate.Limiter
GetSpotLeverageTokenOrderRecordsRate *rate.Limiter
SpotLeverageTokenPurchaseRate *rate.Limiter
SpotLeverTokenRedeemRate *rate.Limiter
GetSpotCrossMarginTradeLoanInfoRate *rate.Limiter
GetSpotCrossMarginTradeAccountRate *rate.Limiter
GetSpotCrossMarginTradeOrdersRate *rate.Limiter
GetSpotCrossMarginTradeRepayHistoryRate *rate.Limiter
SpotCrossMarginTradeLoanRate *rate.Limiter
SpotCrossMarginTradeRepayRate *rate.Limiter
SpotCrossMarginTradeSwitchRate *rate.Limiter
}
// Limit executes rate limiting functionality for Binance
func (r *RateLimit) Limit(ctx context.Context, f request.EndpointLimit) error {
var limiter *rate.Limiter
var tokens int
switch f {
case defaultEPL:
limiter, tokens = r.SpotRate, 1
case createOrderEPL:
limiter, tokens = r.CreateOrderRate, 10
case createSpotOrderEPL:
limiter, tokens = r.CreateSpotOrderRate, 20
case amendOrderEPL:
limiter, tokens = r.AmendOrderRate, 10
case cancelOrderEPL:
limiter, tokens = r.CancelOrderRate, 10
case cancelSpotEPL:
limiter, tokens = r.CancelSpotRate, 20
case cancelAllEPL:
limiter, tokens = r.CancelAllRate, 1
case cancelAllSpotEPL:
limiter, tokens = r.CancelAllSpotRate, 20
case createBatchOrderEPL:
limiter, tokens = r.CreateBatchOrderRate, 10
case amendBatchOrderEPL:
limiter, tokens = r.AmendBatchOrderRate, 10
case cancelBatchOrderEPL:
limiter, tokens = r.CancelBatchOrderRate, 10
case getOrderEPL:
limiter, tokens = r.GetOrderRate, 10
case getOrderHistoryEPL:
limiter, tokens = r.GetOrderHistoryRate, 10
case getPositionListEPL:
limiter, tokens = r.GetPositionListRate, 10
case getExecutionListEPL:
limiter, tokens = r.GetExecutionListRate, 10
case getPositionClosedPNLEPL:
limiter, tokens = r.GetPositionClosedPNLRate, 10
case postPositionSetLeverageEPL:
limiter, tokens = r.PostPositionSetLeverageRate, 10
case setPositionTPLSModeEPL:
limiter, tokens = r.SetPositionTPLSModeRate, 10
case setPositionRiskLimitEPL:
limiter, tokens = r.SetPositionRiskLimitRate, 10
case stopTradingPositionEPL:
limiter, tokens = r.StopTradingPositionRate, 10
case getAccountWalletBalanceEPL:
limiter, tokens = r.GetAccountWalletBalanceRate, 10
case getAccountFeeEPL:
limiter, tokens = r.GetAccountFeeRate, 10
case getAssetTransferQueryInfoEPL:
limiter, tokens = r.GetAssetTransferQueryInfoRate, 1
case getAssetTransferQueryTransferCoinListEPL:
limiter, tokens = r.GetAssetTransferQueryTransferCoinListRate, 1
case getAssetTransferCoinListEPL:
limiter, tokens = r.GetAssetTransferCoinListRate, 1
case getAssetInterTransferListEPL:
limiter, tokens = r.GetAssetInterTransferListRate, 1
case getSubMemberListEPL:
limiter, tokens = r.GetSubMemberListRate, 1
case getAssetUniversalTransferListEPL:
limiter, tokens = r.GetAssetUniversalTransferListRate, 2
case getAssetAccountCoinBalanceEPL:
limiter, tokens = r.GetAssetAccountCoinBalanceRate, 2
case getAssetDepositRecordsEPL:
limiter, tokens = r.GetAssetDepositRecordsRate, 1
case getAssetDepositSubMemberRecordsEPL:
limiter, tokens = r.GetAssetDepositSubMemberRecordsRate, 1
case getAssetDepositSubMemberAddressEPL:
limiter, tokens = r.GetAssetDepositSubMemberAddressRate, 1
case getWithdrawRecordsEPL:
limiter, tokens = r.GetWithdrawRecordsRate, 1
case getAssetCoinInfoEPL:
limiter, tokens = r.GetAssetCoinInfoRate, 1
case getExchangeOrderRecordEPL:
limiter, tokens = r.GetExchangeOrderRecordRate, 1
case interTransferEPL:
limiter, tokens = r.InterTransferRate, 1
case saveTransferSubMemberEPL:
limiter, tokens = r.SaveTransferSubMemberRate, 1
case universalTransferEPL:
limiter, tokens = r.UniversalTransferRate, 5
case createWithdrawalEPL:
limiter, tokens = r.CreateWithdrawalRate, 1
case cancelWithdrawalEPL:
limiter, tokens = r.CancelWithdrawalRate, 1
case userCreateSubMemberEPL:
limiter, tokens = r.UserCreateSubMemberRate, 5
case userCreateSubAPIKeyEPL:
limiter, tokens = r.UserCreateSubAPIKeyRate, 5
case userFrozenSubMemberEPL:
limiter, tokens = r.UserFrozenSubMemberRate, 5
case userUpdateAPIEPL:
limiter, tokens = r.UserUpdateAPIRate, 5
case userUpdateSubAPIEPL:
limiter, tokens = r.UserUpdateSubAPIRate, 5
case userDeleteAPIEPL:
limiter, tokens = r.UserDeleteAPIRate, 5
case userDeleteSubAPIEPL:
limiter, tokens = r.UserDeleteSubAPIRate, 5
case userQuerySubMembersEPL:
limiter, tokens = r.UserQuerySubMembersRate, 10
case userQueryAPIEPL:
limiter, tokens = r.UserQueryAPIRate, 10
case getSpotLeverageTokenOrderRecordsEPL:
limiter, tokens = r.GetSpotLeverageTokenOrderRecordsRate, 50
case spotLeverageTokenPurchaseEPL:
limiter, tokens = r.SpotLeverageTokenPurchaseRate, 20
case spotLeverTokenRedeemEPL:
limiter, tokens = r.SpotLeverTokenRedeemRate, 20
case getSpotCrossMarginTradeLoanInfoEPL:
limiter, tokens = r.GetSpotCrossMarginTradeLoanInfoRate, 50
case getSpotCrossMarginTradeAccountEPL:
limiter, tokens = r.GetSpotCrossMarginTradeAccountRate, 50
case getSpotCrossMarginTradeOrdersEPL:
limiter, tokens = r.GetSpotCrossMarginTradeOrdersRate, 50
case getSpotCrossMarginTradeRepayHistoryEPL:
limiter, tokens = r.GetSpotCrossMarginTradeRepayHistoryRate, 50
case spotCrossMarginTradeLoanEPL:
limiter, tokens = r.SpotCrossMarginTradeLoanRate, 50
case spotCrossMarginTradeRepayEPL:
limiter, tokens = r.SpotCrossMarginTradeRepayRate, 50
case spotCrossMarginTradeSwitchEPL:
limiter, tokens = r.SpotCrossMarginTradeSwitchRate, 50
default:
limiter, tokens = r.SpotRate, 1
}
var finalDelay time.Duration
var reserves = make([]*rate.Reservation, tokens)
for i := 0; i < tokens; i++ {
// Consume tokens 1 at a time as this avoids needing burst capacity in the limiter,
// which would otherwise allow the rate limit to be exceeded over short periods
reserves[i] = limiter.Reserve()
finalDelay = limiter.Reserve().Delay()
}
if dl, ok := ctx.Deadline(); ok && dl.Before(time.Now().Add(finalDelay)) {
// Cancel all potential reservations to free up rate limiter if deadline
// is exceeded.
for x := range reserves {
reserves[x].Cancel()
}
return fmt.Errorf("rate limit delay of %s will exceed deadline: %w",
finalDelay,
context.DeadlineExceeded)
}
time.Sleep(finalDelay)
return nil
}
// SetRateLimit returns the rate limit for the exchange
func SetRateLimit() *RateLimit {
return &RateLimit{
SpotRate: request.NewRateLimit(spotInterval, 120),
CreateOrderRate: request.NewRateLimit(time.Second, 10),
CreateSpotOrderRate: request.NewRateLimit(time.Second, 20),
AmendOrderRate: request.NewRateLimit(time.Second, 10),
CancelOrderRate: request.NewRateLimit(time.Second, 10),
CancelSpotRate: request.NewRateLimit(time.Second, 20),
CancelAllRate: request.NewRateLimit(time.Second, 1),
CancelAllSpotRate: request.NewRateLimit(time.Second, 20),
CreateBatchOrderRate: request.NewRateLimit(time.Second, 10),
AmendBatchOrderRate: request.NewRateLimit(time.Second, 10),
CancelBatchOrderRate: request.NewRateLimit(time.Second, 10),
GetOrderRate: request.NewRateLimit(time.Second, 10),
GetOrderHistoryRate: request.NewRateLimit(time.Second, 10),
GetPositionListRate: request.NewRateLimit(time.Second, 10),
GetExecutionListRate: request.NewRateLimit(time.Second, 10),
GetPositionClosedPNLRate: request.NewRateLimit(time.Second, 10),
PostPositionSetLeverageRate: request.NewRateLimit(time.Second, 10),
SetPositionTPLSModeRate: request.NewRateLimit(time.Second, 10),
SetPositionRiskLimitRate: request.NewRateLimit(time.Second, 10),
StopTradingPositionRate: request.NewRateLimit(time.Second, 10),
GetAccountWalletBalanceRate: request.NewRateLimit(time.Second, 10),
GetAccountFeeRate: request.NewRateLimit(time.Second, 10),
GetAssetTransferQueryInfoRate: request.NewRateLimit(time.Minute, 60),
GetAssetTransferQueryTransferCoinListRate: request.NewRateLimit(time.Minute, 60),
GetAssetTransferCoinListRate: request.NewRateLimit(time.Minute, 60),
GetAssetInterTransferListRate: request.NewRateLimit(time.Minute, 60),
GetSubMemberListRate: request.NewRateLimit(time.Minute, 60),
GetAssetUniversalTransferListRate: request.NewRateLimit(time.Second, 2),
GetAssetAccountCoinBalanceRate: request.NewRateLimit(time.Second, 2),
GetAssetDepositRecordsRate: request.NewRateLimit(time.Minute, 300),
GetAssetDepositSubMemberRecordsRate: request.NewRateLimit(time.Minute, 300),
GetAssetDepositSubMemberAddressRate: request.NewRateLimit(time.Minute, 300),
GetWithdrawRecordsRate: request.NewRateLimit(time.Minute, 300),
GetAssetCoinInfoRate: request.NewRateLimit(time.Minute, 300),
GetExchangeOrderRecordRate: request.NewRateLimit(time.Minute, 300),
InterTransferRate: request.NewRateLimit(time.Minute, 20),
SaveTransferSubMemberRate: request.NewRateLimit(time.Minute, 20),
UniversalTransferRate: request.NewRateLimit(time.Second, 5),
CreateWithdrawalRate: request.NewRateLimit(time.Second, 1),
CancelWithdrawalRate: request.NewRateLimit(time.Minute, 60),
UserCreateSubMemberRate: request.NewRateLimit(time.Second, 5),
UserCreateSubAPIKeyRate: request.NewRateLimit(time.Second, 5),
UserFrozenSubMemberRate: request.NewRateLimit(time.Second, 5),
UserUpdateAPIRate: request.NewRateLimit(time.Second, 5),
UserUpdateSubAPIRate: request.NewRateLimit(time.Second, 5),
UserDeleteAPIRate: request.NewRateLimit(time.Second, 5),
UserDeleteSubAPIRate: request.NewRateLimit(time.Second, 5),
UserQuerySubMembersRate: request.NewRateLimit(time.Second, 10),
UserQueryAPIRate: request.NewRateLimit(time.Second, 10),
GetSpotLeverageTokenOrderRecordsRate: request.NewRateLimit(time.Second, 50),
SpotLeverageTokenPurchaseRate: request.NewRateLimit(time.Second, 20),
SpotLeverTokenRedeemRate: request.NewRateLimit(time.Second, 20),
GetSpotCrossMarginTradeLoanInfoRate: request.NewRateLimit(time.Second, 50),
GetSpotCrossMarginTradeAccountRate: request.NewRateLimit(time.Second, 50),
GetSpotCrossMarginTradeOrdersRate: request.NewRateLimit(time.Second, 50),
GetSpotCrossMarginTradeRepayHistoryRate: request.NewRateLimit(time.Second, 50),
SpotCrossMarginTradeLoanRate: request.NewRateLimit(time.Second, 20),
SpotCrossMarginTradeRepayRate: request.NewRateLimit(time.Second, 20),
SpotCrossMarginTradeSwitchRate: request.NewRateLimit(time.Second, 20),
// GetRateLimit returns the rate limit for the exchange
func GetRateLimit() request.RateLimitDefinitions {
return request.RateLimitDefinitions{
defaultEPL: request.NewRateLimitWithWeight(time.Second*5 /* See: https://bybit-exchange.github.io/docs/v5/rate-limit */, 600, 1),
createOrderEPL: request.NewRateLimitWithWeight(time.Second, 10, 10),
createSpotOrderEPL: request.NewRateLimitWithWeight(time.Second, 20, 20),
amendOrderEPL: request.NewRateLimitWithWeight(time.Second, 10, 10),
cancelOrderEPL: request.NewRateLimitWithWeight(time.Second, 10, 10),
cancelSpotEPL: request.NewRateLimitWithWeight(time.Second, 20, 20),
cancelAllEPL: request.NewRateLimitWithWeight(time.Second, 1, 1),
cancelAllSpotEPL: request.NewRateLimitWithWeight(time.Second, 20, 20),
createBatchOrderEPL: request.NewRateLimitWithWeight(time.Second, 10, 10),
amendBatchOrderEPL: request.NewRateLimitWithWeight(time.Second, 10, 10),
cancelBatchOrderEPL: request.NewRateLimitWithWeight(time.Second, 10, 10),
getOrderEPL: request.NewRateLimitWithWeight(time.Second, 10, 10),
getOrderHistoryEPL: request.NewRateLimitWithWeight(time.Second, 10, 10),
getPositionListEPL: request.NewRateLimitWithWeight(time.Second, 10, 10),
getExecutionListEPL: request.NewRateLimitWithWeight(time.Second, 10, 10),
getPositionClosedPNLEPL: request.NewRateLimitWithWeight(time.Second, 10, 10),
postPositionSetLeverageEPL: request.NewRateLimitWithWeight(time.Second, 10, 10),
setPositionTPLSModeEPL: request.NewRateLimitWithWeight(time.Second, 10, 10),
setPositionRiskLimitEPL: request.NewRateLimitWithWeight(time.Second, 10, 10),
stopTradingPositionEPL: request.NewRateLimitWithWeight(time.Second, 10, 10),
getAccountWalletBalanceEPL: request.NewRateLimitWithWeight(time.Second, 10, 10),
getAccountFeeEPL: request.NewRateLimitWithWeight(time.Second, 10, 10),
getAssetTransferQueryInfoEPL: request.NewRateLimitWithWeight(time.Minute, 60, 1),
getAssetTransferQueryTransferCoinListEPL: request.NewRateLimitWithWeight(time.Minute, 60, 1),
getAssetTransferCoinListEPL: request.NewRateLimitWithWeight(time.Minute, 60, 1),
getAssetInterTransferListEPL: request.NewRateLimitWithWeight(time.Minute, 60, 1),
getSubMemberListEPL: request.NewRateLimitWithWeight(time.Minute, 60, 1),
getAssetUniversalTransferListEPL: request.NewRateLimitWithWeight(time.Second, 2, 2),
getAssetAccountCoinBalanceEPL: request.NewRateLimitWithWeight(time.Second, 2, 2),
getAssetDepositRecordsEPL: request.NewRateLimitWithWeight(time.Minute, 30, 1),
getAssetDepositSubMemberRecordsEPL: request.NewRateLimitWithWeight(time.Minute, 30, 1),
getAssetDepositSubMemberAddressEPL: request.NewRateLimitWithWeight(time.Minute, 30, 1),
getWithdrawRecordsEPL: request.NewRateLimitWithWeight(time.Minute, 30, 1),
getAssetCoinInfoEPL: request.NewRateLimitWithWeight(time.Minute, 30, 1),
getExchangeOrderRecordEPL: request.NewRateLimitWithWeight(time.Minute, 30, 1),
interTransferEPL: request.NewRateLimitWithWeight(time.Minute, 20, 1),
saveTransferSubMemberEPL: request.NewRateLimitWithWeight(time.Minute, 20, 1),
universalTransferEPL: request.NewRateLimitWithWeight(time.Second, 5, 5),
createWithdrawalEPL: request.NewRateLimitWithWeight(time.Second, 1, 1),
cancelWithdrawalEPL: request.NewRateLimitWithWeight(time.Minute, 60, 1),
userCreateSubMemberEPL: request.NewRateLimitWithWeight(time.Second, 5, 5),
userCreateSubAPIKeyEPL: request.NewRateLimitWithWeight(time.Second, 5, 5),
userFrozenSubMemberEPL: request.NewRateLimitWithWeight(time.Second, 5, 5),
userUpdateAPIEPL: request.NewRateLimitWithWeight(time.Second, 5, 5),
userUpdateSubAPIEPL: request.NewRateLimitWithWeight(time.Second, 5, 5),
userDeleteAPIEPL: request.NewRateLimitWithWeight(time.Second, 5, 5),
userDeleteSubAPIEPL: request.NewRateLimitWithWeight(time.Second, 5, 5),
userQuerySubMembersEPL: request.NewRateLimitWithWeight(time.Second, 10, 10),
userQueryAPIEPL: request.NewRateLimitWithWeight(time.Second, 10, 10),
getSpotLeverageTokenOrderRecordsEPL: request.NewRateLimitWithWeight(time.Second, 50, 50),
spotLeverageTokenPurchaseEPL: request.NewRateLimitWithWeight(time.Second, 20, 20),
spotLeverTokenRedeemEPL: request.NewRateLimitWithWeight(time.Second, 20, 20),
getSpotCrossMarginTradeLoanInfoEPL: request.NewRateLimitWithWeight(time.Second, 50, 50),
getSpotCrossMarginTradeAccountEPL: request.NewRateLimitWithWeight(time.Second, 50, 50),
getSpotCrossMarginTradeOrdersEPL: request.NewRateLimitWithWeight(time.Second, 50, 50),
getSpotCrossMarginTradeRepayHistoryEPL: request.NewRateLimitWithWeight(time.Second, 50, 50),
spotCrossMarginTradeLoanEPL: request.NewRateLimitWithWeight(time.Second, 20, 50),
spotCrossMarginTradeRepayEPL: request.NewRateLimitWithWeight(time.Second, 20, 50),
spotCrossMarginTradeSwitchEPL: request.NewRateLimitWithWeight(time.Second, 20, 50),
}
}