futures: Implement GetLatestFundingRates across exchanges (#1339)

* adds funding rate implementations and improvements

* merge fixes x1

* lint

* kucoin funding rates func make

* migrate sync-manager to keys

* some kucoin work

* adds some kucoin wrapper funcs

* ehhh, todo

* kucoin position

* start of orders

* adds the kucoin tests yay

* multiplier

* nits, EWS includes order limits

* NotYetImplemented, IsPerp improvements, cleaning

* lint, test fix, huobi time

* fixes issues, improves testing

* fixes linters I WRECKED

* local lint but remote lint, lint, lint, lint

* fixes err

* skip CI

* lint

* Supported rates, binance endpoints

* fixes weird mocktest problems

* no, CZ is invalid

* fixes some new EWS test errors
This commit is contained in:
Scott
2023-11-03 10:01:32 +10:00
committed by GitHub
parent f9437dbd08
commit 70690d9a04
78 changed files with 4088 additions and 527 deletions

View File

@@ -123,6 +123,7 @@ func (b *Bitmex) SetDefaults() {
CryptoWithdrawal: true,
TradeFee: true,
CryptoWithdrawalFee: true,
FundingRateFetching: true,
},
WebsocketCapabilities: protocol.Features{
TradeFetching: true,
@@ -134,6 +135,16 @@ func (b *Bitmex) SetDefaults() {
DeadMansSwitch: true,
GetOrders: true,
GetOrder: true,
FundingRateFetching: false, // supported but not implemented // TODO when multi-websocket support added
},
FuturesCapabilities: exchange.FuturesCapabilities{
FundingRates: true,
SupportedFundingRateFrequencies: map[kline.Interval]bool{
kline.EightHour: true,
},
FundingRateBatching: map[asset.Item]bool{
asset.PerpetualContract: true,
},
},
WithdrawPermissions: exchange.AutoWithdrawCryptoWithAPIPermission |
exchange.WithdrawCryptoWithEmail |
@@ -402,12 +413,9 @@ instruments:
pair, enabled, err = b.MatchSymbolCheckEnabled(tick[j].Symbol, a, false)
}
if err != nil {
if !errors.Is(err, currency.ErrPairNotFound) {
return err
}
if err != nil && !errors.Is(err, currency.ErrPairNotFound) {
return err
}
if !enabled {
continue
}
@@ -1241,3 +1249,86 @@ func (b *Bitmex) GetFuturesContractDetails(ctx context.Context, item asset.Item)
}
return resp, nil
}
// GetLatestFundingRates returns the latest funding rates data
func (b *Bitmex) GetLatestFundingRates(ctx context.Context, r *fundingrate.LatestRateRequest) ([]fundingrate.LatestRateResponse, error) {
if r == nil {
return nil, fmt.Errorf("%w LatestRateRequest", common.ErrNilPointer)
}
if r.IncludePredictedRate {
return nil, fmt.Errorf("%w IncludePredictedRate", common.ErrFunctionNotSupported)
}
count := "1"
if r.Pair.IsEmpty() {
count = "500"
} else {
isPerp, err := b.IsPerpetualFutureCurrency(r.Asset, r.Pair)
if err != nil {
return nil, err
}
if !isPerp {
return nil, fmt.Errorf("%w %v %v", futures.ErrNotPerpetualFuture, r.Asset, r.Pair)
}
}
format, err := b.GetPairFormat(r.Asset, true)
if err != nil {
return nil, err
}
fPair := format.Format(r.Pair)
rates, err := b.GetFullFundingHistory(ctx, fPair, count, "", "", "", true, time.Time{}, time.Time{})
if err != nil {
return nil, err
}
resp := make([]fundingrate.LatestRateResponse, 0, len(rates))
// Bitmex returns historical rates from this endpoint, we only want the latest
latestRateSymbol := make(map[string]bool)
for i := range rates {
if _, ok := latestRateSymbol[rates[i].Symbol]; ok {
continue
}
latestRateSymbol[rates[i].Symbol] = true
var nr time.Time
nr, err = time.Parse(time.RFC3339, rates[i].FundingInterval)
if err != nil {
return nil, err
}
var cp currency.Pair
var isEnabled bool
cp, isEnabled, err = b.MatchSymbolCheckEnabled(rates[i].Symbol, r.Asset, false)
if err != nil && !errors.Is(err, currency.ErrPairNotFound) {
return nil, err
}
if !isEnabled {
continue
}
var isPerp bool
isPerp, err = b.IsPerpetualFutureCurrency(r.Asset, cp)
if err != nil {
return nil, err
}
if !isPerp {
continue
}
resp = append(resp, fundingrate.LatestRateResponse{
Exchange: b.Name,
Asset: r.Asset,
Pair: cp,
LatestRate: fundingrate.Rate{
Time: rates[i].Timestamp,
Rate: decimal.NewFromFloat(rates[i].FundingRate),
},
TimeOfNextRate: rates[i].Timestamp.Add(time.Duration(nr.Hour()) * time.Hour),
TimeChecked: time.Now(),
})
}
return resp, nil
}
// IsPerpetualFutureCurrency ensures a given asset and currency is a perpetual future
func (b *Bitmex) IsPerpetualFutureCurrency(a asset.Item, _ currency.Pair) (bool, error) {
return a == asset.PerpetualContract, nil
}