Gateio: Implement GetHistoricalFundingRates wrapper func (#1385)

* 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

* gateio: Add func GetHistoricalFundingRates

* glorious: nits

* rm: print call

* glorious: nits fix test

---------

Co-authored-by: Scott <scott.grant@thrasher.io>
Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>
This commit is contained in:
Ryan O'Hara-Reid
2023-11-22 10:52:26 +11:00
committed by GitHub
parent 0fd433e865
commit f23cc004fd
3 changed files with 174 additions and 2 deletions

View File

@@ -9,8 +9,14 @@ import (
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
)
// ErrFundingRateOutsideLimits is returned when a funding rate is outside the allowed date range
var ErrFundingRateOutsideLimits = errors.New("funding rate outside limits")
var (
// ErrFundingRateOutsideLimits is returned when a funding rate is outside the allowed date range
ErrFundingRateOutsideLimits = errors.New("funding rate outside limits")
// ErrPaymentCurrencyCannotBeEmpty is returned when a payment currency is not set
ErrPaymentCurrencyCannotBeEmpty = errors.New("payment currency cannot be empty")
// ErrNoFundingRatesFound is returned when no funding rates are found
ErrNoFundingRatesFound = errors.New("no funding rates found")
)
// HistoricalRatesRequest is used to request funding rate details for a position
type HistoricalRatesRequest struct {

View File

@@ -11,6 +11,7 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/core"
@@ -3455,3 +3456,85 @@ func TestGetLatestFundingRates(t *testing.T) {
t.Error(err)
}
}
func TestGetHistoricalFundingRates(t *testing.T) {
t.Parallel()
_, err := g.GetHistoricalFundingRates(context.Background(), nil)
if !errors.Is(err, common.ErrNilPointer) {
t.Fatalf("received: %v, expected: %v", err, common.ErrNilPointer)
}
_, err = g.GetHistoricalFundingRates(context.Background(), &fundingrate.HistoricalRatesRequest{})
if !errors.Is(err, asset.ErrNotSupported) {
t.Fatalf("received: %v, expected: %v", err, asset.ErrNotSupported)
}
_, err = g.GetHistoricalFundingRates(context.Background(), &fundingrate.HistoricalRatesRequest{
Asset: asset.Futures,
})
if !errors.Is(err, currency.ErrCurrencyPairEmpty) {
t.Fatalf("received: %v, expected: %v", err, currency.ErrCurrencyPairEmpty)
}
_, err = g.GetHistoricalFundingRates(context.Background(), &fundingrate.HistoricalRatesRequest{
Asset: asset.Futures,
Pair: currency.NewPair(currency.ENJ, currency.USDT),
})
if !errors.Is(err, fundingrate.ErrPaymentCurrencyCannotBeEmpty) {
t.Fatalf("received: %v, expected: %v", err, fundingrate.ErrPaymentCurrencyCannotBeEmpty)
}
_, err = g.GetHistoricalFundingRates(context.Background(), &fundingrate.HistoricalRatesRequest{
Asset: asset.Futures,
Pair: currency.NewPair(currency.ENJ, currency.USDT),
PaymentCurrency: currency.USDT,
IncludePayments: true,
IncludePredictedRate: true,
})
if !errors.Is(err, common.ErrNotYetImplemented) {
t.Fatalf("received: %v, expected: %v", err, common.ErrNotYetImplemented)
}
_, err = g.GetHistoricalFundingRates(context.Background(), &fundingrate.HistoricalRatesRequest{
Asset: asset.Futures,
Pair: currency.NewPair(currency.ENJ, currency.USDT),
PaymentCurrency: currency.USDT,
IncludePredictedRate: true,
})
if !errors.Is(err, common.ErrNotYetImplemented) {
t.Fatalf("received: %v, expected: %v", err, common.ErrNotYetImplemented)
}
_, err = g.GetHistoricalFundingRates(context.Background(), &fundingrate.HistoricalRatesRequest{
Asset: asset.Futures,
Pair: currency.NewPair(currency.ENJ, currency.USDT),
PaymentCurrency: currency.USDT,
StartDate: time.Now().Add(time.Hour * 16),
EndDate: time.Now(),
})
if !errors.Is(err, common.ErrStartAfterEnd) {
t.Fatalf("received: %v, expected: %v", err, common.ErrStartAfterEnd)
}
_, err = g.GetHistoricalFundingRates(context.Background(), &fundingrate.HistoricalRatesRequest{
Asset: asset.Futures,
Pair: currency.NewPair(currency.ENJ, currency.USDT),
PaymentCurrency: currency.USDT,
StartDate: time.Now().Add(-time.Hour * 8008),
EndDate: time.Now(),
})
if !errors.Is(err, fundingrate.ErrFundingRateOutsideLimits) {
t.Fatalf("received: %v, expected: %v", err, fundingrate.ErrFundingRateOutsideLimits)
}
history, err := g.GetHistoricalFundingRates(context.Background(), &fundingrate.HistoricalRatesRequest{
Asset: asset.Futures,
Pair: currency.NewPair(currency.ENJ, currency.USDT),
PaymentCurrency: currency.USDT,
})
if !errors.Is(err, nil) {
t.Fatalf("received: %v, expected: %v", err, nil)
}
assert.NotEmpty(t, history, "should return values")
}

View File

@@ -11,6 +11,7 @@ import (
"sync"
"time"
"github.com/shopspring/decimal"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/convert"
"github.com/thrasher-corp/gocryptotrader/config"
@@ -2168,6 +2169,88 @@ func (g *Gateio) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) e
return g.LoadLimits(limits)
}
// GetHistoricalFundingRates returns historical funding rates for a futures contract
func (g *Gateio) GetHistoricalFundingRates(ctx context.Context, r *fundingrate.HistoricalRatesRequest) (*fundingrate.HistoricalRates, 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() {
return nil, currency.ErrCurrencyPairEmpty
}
if !r.StartDate.IsZero() && !r.EndDate.IsZero() {
err := common.StartEndTimeCheck(r.StartDate, r.EndDate)
if err != nil {
return nil, err
}
}
// NOTE: Opted to fail here as a misconfigured request will result in
// {"label":"CONTRACT_NOT_FOUND"} and rather not mutate request using
// quote currency as the settlement currency.
if r.PaymentCurrency.IsEmpty() {
return nil, fundingrate.ErrPaymentCurrencyCannotBeEmpty
}
if r.IncludePayments {
return nil, fmt.Errorf("include payments %w", common.ErrNotYetImplemented)
}
if r.IncludePredictedRate {
return nil, fmt.Errorf("include predicted rate %w", common.ErrNotYetImplemented)
}
fPair, err := g.FormatExchangeCurrency(r.Pair, r.Asset)
if err != nil {
return nil, err
}
records, err := g.GetFutureFundingRates(ctx, r.PaymentCurrency.String(), fPair, 1000)
if err != nil {
return nil, err
}
if len(records) == 0 {
return nil, fundingrate.ErrNoFundingRatesFound
}
if !r.StartDate.IsZero() && !r.RespectHistoryLimits && r.StartDate.Before(records[len(records)-1].Timestamp.Time()) {
return nil, fmt.Errorf("%w start date requested: %v last returned record: %v", fundingrate.ErrFundingRateOutsideLimits, r.StartDate, records[len(records)-1].Timestamp.Time())
}
fundingRates := make([]fundingrate.Rate, 0, len(records))
for i := range records {
if (!r.EndDate.IsZero() && r.EndDate.Before(records[i].Timestamp.Time())) ||
(!r.StartDate.IsZero() && r.StartDate.After(records[i].Timestamp.Time())) {
continue
}
fundingRates = append(fundingRates, fundingrate.Rate{
Rate: decimal.NewFromFloat(records[i].Rate.Float64()),
Time: records[i].Timestamp.Time(),
})
}
if len(fundingRates) == 0 {
return nil, fundingrate.ErrNoFundingRatesFound
}
return &fundingrate.HistoricalRates{
Exchange: g.Name,
Asset: r.Asset,
Pair: r.Pair,
FundingRates: fundingRates,
StartDate: fundingRates[len(fundingRates)-1].Time,
EndDate: fundingRates[0].Time,
LatestRate: fundingRates[0],
PaymentCurrency: r.PaymentCurrency,
}, nil
}
// GetLatestFundingRates returns the latest funding rates data
func (g *Gateio) GetLatestFundingRates(ctx context.Context, r *fundingrate.LatestRateRequest) ([]fundingrate.LatestRateResponse, error) {
if r == nil {