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

@@ -16,6 +16,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/core"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/fundingrate"
"github.com/thrasher-corp/gocryptotrader/exchanges/futures"
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
@@ -3154,7 +3155,17 @@ func getFirstTradablePairOfAssets() {
if err != nil {
log.Fatalf("GateIO %v, trying to get %v enabled pairs error", err, asset.Futures)
}
futuresTradablePair = enabledPairs[len(enabledPairs)-1]
if len(enabledPairs) == 0 {
var availPairs currency.Pairs
availPairs, err = g.GetAvailablePairs(asset.Futures)
if err != nil {
log.Fatalf("GateIO %v, trying to get %v enabled pairs error", err, asset.Futures)
}
futuresTradablePair = availPairs[len(availPairs)-1]
} else {
futuresTradablePair = enabledPairs[len(enabledPairs)-1]
}
enabledPairs, err = g.GetEnabledPairs(asset.Options)
if err != nil {
log.Fatalf("GateIO %v, trying to get %v enabled pairs error", err, asset.Options)
@@ -3419,3 +3430,28 @@ func TestGetFuturesContractDetails(t *testing.T) {
t.Error(err)
}
}
func TestGetLatestFundingRates(t *testing.T) {
t.Parallel()
_, err := g.GetLatestFundingRates(context.Background(), &fundingrate.LatestRateRequest{
Asset: asset.USDTMarginedFutures,
Pair: currency.NewPair(currency.BTC, currency.USDT),
IncludePredictedRate: true,
})
if !errors.Is(err, asset.ErrNotSupported) {
t.Error(err)
}
_, err = g.GetLatestFundingRates(context.Background(), &fundingrate.LatestRateRequest{
Asset: asset.Futures,
Pair: currency.NewPair(currency.BTC, currency.USD),
})
if err != nil {
t.Error(err)
}
_, err = g.GetLatestFundingRates(context.Background(), &fundingrate.LatestRateRequest{
Asset: asset.Futures,
})
if err != nil {
t.Error(err)
}
}

View File

@@ -94,6 +94,8 @@ func (g *Gateio) SetDefaults() {
CryptoWithdrawalFee: true,
MultiChainDeposits: true,
MultiChainWithdrawals: true,
PredictedFundingRate: true,
FundingRateFetching: true,
},
WebsocketCapabilities: protocol.Features{
TickerFetching: true,
@@ -112,6 +114,16 @@ func (g *Gateio) SetDefaults() {
Kline: kline.ExchangeCapabilitiesSupported{
Intervals: true,
},
FuturesCapabilities: exchange.FuturesCapabilities{
FundingRates: true,
SupportedFundingRateFrequencies: map[kline.Interval]bool{
kline.FourHour: true,
kline.EightHour: true,
},
FundingRateBatching: map[asset.Item]bool{
asset.Futures: true,
},
},
},
Enabled: exchange.FeaturesEnabled{
AutoPairUpdates: true,
@@ -2155,3 +2167,96 @@ func (g *Gateio) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) e
return g.LoadLimits(limits)
}
// GetLatestFundingRates returns the latest funding rates data
func (g *Gateio) GetLatestFundingRates(ctx context.Context, r *fundingrate.LatestRateRequest) ([]fundingrate.LatestRateResponse, error) {
if r == nil {
return nil, fmt.Errorf("%w LatestRateRequest", common.ErrNilPointer)
}
if r.Asset != asset.Futures {
return nil, fmt.Errorf("%w %v", asset.ErrNotSupported, r.Asset)
}
if !r.Pair.IsEmpty() {
resp := make([]fundingrate.LatestRateResponse, 1)
fPair, err := g.FormatExchangeCurrency(r.Pair, r.Asset)
if err != nil {
return nil, err
}
var settle string
settle, err = g.getSettlementFromCurrency(fPair, true)
if err != nil {
return nil, err
}
contract, err := g.GetSingleContract(ctx, settle, fPair.String())
if err != nil {
return nil, err
}
resp[0] = contractToFundingRate(g.Name, r.Asset, fPair, contract, r.IncludePredictedRate)
return resp, nil
}
var resp []fundingrate.LatestRateResponse
settleCurrencies := []string{"btc", "usdt", "usd"}
pairs, err := g.GetEnabledPairs(asset.Futures)
if err != nil {
return nil, err
}
for i := range settleCurrencies {
contracts, err := g.GetAllFutureContracts(ctx, settleCurrencies[i])
if err != nil {
return nil, err
}
for j := range contracts {
p := strings.ToUpper(contracts[j].Name)
if !g.IsValidPairString(p) {
continue
}
cp, err := currency.NewPairFromString(p)
if err != nil {
return nil, err
}
if !pairs.Contains(cp, false) {
continue
}
var isPerp bool
isPerp, err = g.IsPerpetualFutureCurrency(r.Asset, cp)
if err != nil {
return nil, err
}
if !isPerp {
continue
}
resp = append(resp, contractToFundingRate(g.Name, r.Asset, cp, &contracts[j], r.IncludePredictedRate))
}
}
return resp, nil
}
func contractToFundingRate(name string, item asset.Item, fPair currency.Pair, contract *FuturesContract, includeUpcomingRate bool) fundingrate.LatestRateResponse {
resp := fundingrate.LatestRateResponse{
Exchange: name,
Asset: item,
Pair: fPair,
LatestRate: fundingrate.Rate{
Time: contract.FundingNextApply.Time().Add(-time.Duration(contract.FundingInterval) * time.Second),
Rate: contract.FundingRate.Decimal(),
},
TimeOfNextRate: contract.FundingNextApply.Time(),
TimeChecked: time.Now(),
}
if includeUpcomingRate {
resp.PredictedUpcomingRate = fundingrate.Rate{
Time: contract.FundingNextApply.Time(),
Rate: contract.FundingRateIndicative.Decimal(),
}
}
return resp
}
// IsPerpetualFutureCurrency ensures a given asset and currency is a perpetual future
func (g *Gateio) IsPerpetualFutureCurrency(a asset.Item, _ currency.Pair) (bool, error) {
return a == asset.Futures, nil
}