mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-01 15:10:44 +00:00
Binance,OKx: Implement fetching funding rates (#1239)
* adds basic groundwork for rates on binance * more into rates on binance * rm redudant redundancy, add payments * mini commit before merging with testnet ability branch * changes function signature and fixes resulting build * gets billing data too * funding rates package, features use, testnet reimpl * new endpoint, refinements and tests * cli fix, rpc impl, testing, payments * fixups from looking at code * typo fix * niteroos * merge fixes * adds test, fixes cli issues * woah nelly
This commit is contained in:
@@ -10,8 +10,8 @@ import (
|
||||
var (
|
||||
// ErrNotSupported is an error for an unsupported asset type
|
||||
ErrNotSupported = errors.New("unsupported asset type")
|
||||
// ErrNotEnabled returned when a supported asset type is disabled
|
||||
ErrNotEnabled = errors.New("asset type disabled")
|
||||
// ErrNotEnabled is an error for an asset not enabled
|
||||
ErrNotEnabled = errors.New("asset type not enabled")
|
||||
)
|
||||
|
||||
// Item stores the asset type
|
||||
|
||||
@@ -35,6 +35,9 @@ const (
|
||||
cfuturesAPIURL = "https://dapi.binance.com"
|
||||
ufuturesAPIURL = "https://fapi.binance.com"
|
||||
|
||||
testnetSpotURL = "https://testnet.binance.vision/api"
|
||||
testnetFutures = "https://testnet.binancefuture.com"
|
||||
|
||||
// Public endpoints
|
||||
exchangeInfo = "/api/v3/exchangeInfo"
|
||||
orderBookDepth = "/api/v3/depth"
|
||||
@@ -49,6 +52,9 @@ const (
|
||||
perpExchangeInfo = "/fapi/v1/exchangeInfo"
|
||||
historicalTrades = "/api/v3/historicalTrades"
|
||||
|
||||
// Margin endpoints
|
||||
marginInterestHistory = "/sapi/v1/margin/interestHistory"
|
||||
|
||||
// Authenticated endpoints
|
||||
newOrderTest = "/api/v3/order/test"
|
||||
orderEndpoint = "/api/v3/order"
|
||||
@@ -76,8 +82,8 @@ const (
|
||||
defaultRecvWindow = 5 * time.Second
|
||||
)
|
||||
|
||||
// GetInterestHistory gets interest history for currency/currencies provided
|
||||
func (b *Binance) GetInterestHistory(ctx context.Context) (MarginInfoData, error) {
|
||||
// GetUndocumentedInterestHistory gets interest history for currency/currencies provided
|
||||
func (b *Binance) GetUndocumentedInterestHistory(ctx context.Context) (MarginInfoData, error) {
|
||||
var resp MarginInfoData
|
||||
if err := b.SendHTTPRequest(ctx, exchange.EdgeCase1, undocumentedInterestHistory, spotDefaultRate, &resp); err != nil {
|
||||
return resp, err
|
||||
@@ -212,6 +218,41 @@ func (b *Binance) GetHistoricalTrades(ctx context.Context, symbol string, limit
|
||||
b.SendAPIKeyHTTPRequest(ctx, exchange.RestSpotSupplementary, path, spotDefaultRate, &resp)
|
||||
}
|
||||
|
||||
// GetUserMarginInterestHistory returns margin interest history for the user
|
||||
func (b *Binance) GetUserMarginInterestHistory(ctx context.Context, assetCurrency currency.Code, isolatedSymbol currency.Pair, startTime, endTime time.Time, currentPage, size int64, archived bool) (*UserMarginInterestHistoryResponse, error) {
|
||||
params := url.Values{}
|
||||
|
||||
if !assetCurrency.IsEmpty() {
|
||||
params.Set("asset", assetCurrency.String())
|
||||
}
|
||||
if !isolatedSymbol.IsEmpty() {
|
||||
fPair, err := b.FormatSymbol(isolatedSymbol, asset.Margin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params.Set("isolatedSymbol", fPair)
|
||||
}
|
||||
if !startTime.IsZero() {
|
||||
params.Set("startTime", strconv.FormatInt(startTime.UnixMilli(), 10))
|
||||
}
|
||||
if !endTime.IsZero() {
|
||||
params.Set("endTime", strconv.FormatInt(endTime.UnixMilli(), 10))
|
||||
}
|
||||
if currentPage > 0 {
|
||||
params.Set("current", strconv.FormatInt(currentPage, 10))
|
||||
}
|
||||
if size > 0 {
|
||||
params.Set("size", strconv.FormatInt(size, 10))
|
||||
}
|
||||
if archived {
|
||||
params.Set("archived", "true")
|
||||
}
|
||||
|
||||
path := marginInterestHistory + "?" + params.Encode()
|
||||
var resp UserMarginInterestHistoryResponse
|
||||
return &resp, b.SendAPIKeyHTTPRequest(ctx, exchange.RestSpotSupplementary, path, spotDefaultRate, &resp)
|
||||
}
|
||||
|
||||
// GetAggregatedTrades returns aggregated trade activity.
|
||||
// If more than one hour of data is requested or asked limit is not supported by exchange
|
||||
// then the trades are collected with multiple backend requests.
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/config"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
|
||||
)
|
||||
@@ -32,6 +33,20 @@ func TestMain(m *testing.M) {
|
||||
binanceConfig.API.Credentials.Secret = apiSecret
|
||||
b.SetDefaults()
|
||||
b.Websocket = sharedtestvalues.NewTestWebsocket()
|
||||
if useTestNet {
|
||||
err = b.API.Endpoints.SetRunning(exchange.RestUSDTMargined.String(), testnetFutures)
|
||||
if err != nil {
|
||||
log.Fatal("Binance setup error", err)
|
||||
}
|
||||
err = b.API.Endpoints.SetRunning(exchange.RestCoinMargined.String(), testnetFutures)
|
||||
if err != nil {
|
||||
log.Fatal("Binance setup error", err)
|
||||
}
|
||||
err = b.API.Endpoints.SetRunning(exchange.RestSpot.String(), testnetSpotURL)
|
||||
if err != nil {
|
||||
log.Fatal("Binance setup error", err)
|
||||
}
|
||||
}
|
||||
err = b.Setup(binanceConfig)
|
||||
if err != nil {
|
||||
log.Fatal("Binance setup error", err)
|
||||
|
||||
@@ -21,6 +21,9 @@ const mockfile = "../../testdata/http_mock/binance/binance.json"
|
||||
var mockTests = true
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if useTestNet {
|
||||
log.Fatal("cannot use testnet with mock tests")
|
||||
}
|
||||
cfg := config.GetConfig()
|
||||
err := cfg.LoadConfig("../../testdata/configtest.json", true)
|
||||
if err != nil {
|
||||
|
||||
@@ -15,6 +15,7 @@ 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/fundingrate"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
|
||||
@@ -26,6 +27,7 @@ const (
|
||||
apiKey = ""
|
||||
apiSecret = ""
|
||||
canManipulateRealOrders = false
|
||||
useTestNet = false
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -622,9 +624,9 @@ func TestGetFuturesExchangeInfo(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetInterestHistory(t *testing.T) {
|
||||
func TestGetUndocumentedInterestHistory(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetInterestHistory(context.Background())
|
||||
_, err := b.GetUndocumentedInterestHistory(context.Background())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -638,19 +640,6 @@ func TestGetCrossMarginInterestHistory(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFundingRates(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.FundingRates(context.Background(), currency.NewPair(currency.BTC, currency.USDT), "", time.Time{}, time.Time{})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
start, end := getTime()
|
||||
_, err = b.FundingRates(context.Background(), currency.NewPair(currency.BTC, currency.USDT), "2", start, end)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFuturesOrderbook(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetFuturesOrderbook(context.Background(), currency.NewPairWithDelimiter("BTCUSD", "PERP", "_"), 1000)
|
||||
@@ -2817,3 +2806,135 @@ func TestUpdateOrderExecutionLimits(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFundingRates(t *testing.T) {
|
||||
t.Parallel()
|
||||
s, e := getTime()
|
||||
_, err := b.GetFundingRates(context.Background(), &fundingrate.RatesRequest{
|
||||
Asset: asset.USDTMarginedFutures,
|
||||
Pair: currency.NewPair(currency.BTC, currency.USDT),
|
||||
StartDate: s,
|
||||
EndDate: e,
|
||||
IncludePayments: true,
|
||||
IncludePredictedRate: true,
|
||||
})
|
||||
if !errors.Is(err, common.ErrFunctionNotSupported) {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = b.GetFundingRates(context.Background(), &fundingrate.RatesRequest{
|
||||
Asset: asset.USDTMarginedFutures,
|
||||
Pair: currency.NewPair(currency.BTC, currency.USDT),
|
||||
StartDate: s,
|
||||
EndDate: e,
|
||||
PaymentCurrency: currency.DOGE,
|
||||
})
|
||||
if !errors.Is(err, common.ErrFunctionNotSupported) {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
r := &fundingrate.RatesRequest{
|
||||
Asset: asset.USDTMarginedFutures,
|
||||
Pair: currency.NewPair(currency.BTC, currency.USDT),
|
||||
StartDate: s,
|
||||
EndDate: e,
|
||||
}
|
||||
if sharedtestvalues.AreAPICredentialsSet(b) {
|
||||
r.IncludePayments = true
|
||||
}
|
||||
_, err = b.GetFundingRates(context.Background(), r)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
r.Asset = asset.CoinMarginedFutures
|
||||
r.Pair, err = currency.NewPairFromString("BTCUSD_PERP")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = b.GetFundingRates(context.Background(), r)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLatestFundingRate(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetLatestFundingRate(context.Background(), &fundingrate.LatestRateRequest{
|
||||
Asset: asset.USDTMarginedFutures,
|
||||
Pair: currency.NewPair(currency.BTC, currency.USDT),
|
||||
IncludePredictedRate: true,
|
||||
})
|
||||
if !errors.Is(err, common.ErrFunctionNotSupported) {
|
||||
t.Error(err)
|
||||
}
|
||||
_, err = b.GetLatestFundingRate(context.Background(), &fundingrate.LatestRateRequest{
|
||||
Asset: asset.USDTMarginedFutures,
|
||||
Pair: currency.NewPair(currency.BTC, currency.USDT),
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
cp, err := currency.NewPairFromString("BTCUSD_PERP")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
_, err = b.GetLatestFundingRate(context.Background(), &fundingrate.LatestRateRequest{
|
||||
Asset: asset.CoinMarginedFutures,
|
||||
Pair: cp,
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsPerpetualFutureCurrency(t *testing.T) {
|
||||
t.Parallel()
|
||||
is, err := b.IsPerpetualFutureCurrency(asset.Binary, currency.NewPair(currency.BTC, currency.USDT))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if is {
|
||||
t.Error("expected false")
|
||||
}
|
||||
|
||||
is, err = b.IsPerpetualFutureCurrency(asset.CoinMarginedFutures, currency.NewPair(currency.BTC, currency.USDT))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if is {
|
||||
t.Error("expected false")
|
||||
}
|
||||
is, err = b.IsPerpetualFutureCurrency(asset.CoinMarginedFutures, currency.NewPair(currency.BTC, currency.PERP))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !is {
|
||||
t.Error("expected true")
|
||||
}
|
||||
|
||||
is, err = b.IsPerpetualFutureCurrency(asset.USDTMarginedFutures, currency.NewPair(currency.BTC, currency.USDT))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !is {
|
||||
t.Error("expected true")
|
||||
}
|
||||
is, err = b.IsPerpetualFutureCurrency(asset.USDTMarginedFutures, currency.NewPair(currency.BTC, currency.PERP))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if is {
|
||||
t.Error("expected false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUserMarginInterestHistory(t *testing.T) {
|
||||
t.Parallel()
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, b)
|
||||
_, err := b.GetUserMarginInterestHistory(context.Background(), currency.USDT, currency.NewPair(currency.BTC, currency.USDT), time.Now().Add(-time.Hour*24), time.Now(), 1, 10, false)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/convert"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
)
|
||||
@@ -294,14 +295,14 @@ type AggregatedTrade struct {
|
||||
|
||||
// IndexMarkPrice stores data for index and mark prices
|
||||
type IndexMarkPrice struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Pair string `json:"pair"`
|
||||
MarkPrice float64 `json:"markPrice,string"`
|
||||
IndexPrice float64 `json:"indexPrice,string"`
|
||||
EstimatedSettlePrice float64 `json:"estimatedSettlePrice,string"`
|
||||
LastFundingRate string `json:"lastFundingRate"`
|
||||
NextFundingTime int64 `json:"nextFundingTime"`
|
||||
Time int64 `json:"time"`
|
||||
Symbol string `json:"symbol"`
|
||||
Pair string `json:"pair"`
|
||||
MarkPrice convert.StringToFloat64 `json:"markPrice"`
|
||||
IndexPrice convert.StringToFloat64 `json:"indexPrice"`
|
||||
EstimatedSettlePrice convert.StringToFloat64 `json:"estimatedSettlePrice"`
|
||||
LastFundingRate convert.StringToFloat64 `json:"lastFundingRate"`
|
||||
NextFundingTime int64 `json:"nextFundingTime"`
|
||||
Time int64 `json:"time"`
|
||||
}
|
||||
|
||||
// CandleStick holds kline data
|
||||
@@ -917,3 +918,22 @@ type update struct {
|
||||
type job struct {
|
||||
Pair currency.Pair
|
||||
}
|
||||
|
||||
// UserMarginInterestHistoryResponse user margin interest history response
|
||||
type UserMarginInterestHistoryResponse struct {
|
||||
Rows []UserMarginInterestHistory `json:"rows"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
// UserMarginInterestHistory user margin interest history row
|
||||
type UserMarginInterestHistory struct {
|
||||
TxID int64 `json:"txId"`
|
||||
InterestAccruedTime binanceTime `json:"interestAccuredTime"` // typo in docs, cannot verify due to API restrictions
|
||||
Asset string `json:"asset"`
|
||||
RawAsset string `json:"rawAsset"`
|
||||
Principal float64 `json:"principal,string"`
|
||||
Interest float64 `json:"interest,string"`
|
||||
InterestRate float64 `json:"interestRate,string"`
|
||||
Type string `json:"type"`
|
||||
IsolatedSymbol string `json:"isolatedSymbol"`
|
||||
}
|
||||
|
||||
@@ -388,7 +388,7 @@ func (b *Binance) UGetFundingHistory(ctx context.Context, symbol currency.Pair,
|
||||
}
|
||||
params.Set("symbol", symbolValue)
|
||||
}
|
||||
if limit > 0 && limit < 1000 {
|
||||
if limit > 0 {
|
||||
params.Set("limit", strconv.FormatInt(limit, 10))
|
||||
}
|
||||
if !startTime.IsZero() && !endTime.IsZero() {
|
||||
@@ -1098,27 +1098,6 @@ func (b *Binance) GetPerpMarkets(ctx context.Context) (PerpsExchangeInfo, error)
|
||||
return resp, b.SendHTTPRequest(ctx, exchange.RestUSDTMargined, perpExchangeInfo, uFuturesDefaultRate, &resp)
|
||||
}
|
||||
|
||||
// FundingRates gets funding rate history for perpetual contracts
|
||||
func (b *Binance) FundingRates(ctx context.Context, symbol currency.Pair, limit string, startTime, endTime time.Time) ([]FundingRateData, error) {
|
||||
var resp []FundingRateData
|
||||
params := url.Values{}
|
||||
symbolValue, err := b.FormatSymbol(symbol, asset.USDTMarginedFutures)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
params.Set("symbol", symbolValue)
|
||||
if limit != "" {
|
||||
params.Set("limit", limit)
|
||||
}
|
||||
if !startTime.IsZero() {
|
||||
params.Set("startTime", strconv.FormatInt(startTime.UnixMilli(), 10))
|
||||
}
|
||||
if !endTime.IsZero() {
|
||||
params.Set("endTime", strconv.FormatInt(endTime.UnixMilli(), 10))
|
||||
}
|
||||
return resp, b.SendHTTPRequest(ctx, exchange.RestUSDTMargined, fundingRate+params.Encode(), uFuturesDefaultRate, &resp)
|
||||
}
|
||||
|
||||
// FetchUSDTMarginExchangeLimits fetches USDT margined order execution limits
|
||||
func (b *Binance) FetchUSDTMarginExchangeLimits(ctx context.Context) ([]order.MinMaxLevel, error) {
|
||||
usdtFutures, err := b.UExchangeInfo(ctx)
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/config"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/deposit"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/fundingrate"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
@@ -162,6 +164,10 @@ func (b *Binance) SetDefaults() {
|
||||
DateRanges: true,
|
||||
Intervals: true,
|
||||
},
|
||||
FuturesCapabilities: exchange.FuturesCapabilities{
|
||||
FundingRates: true,
|
||||
FundingRateFrequency: kline.EightHour.Duration(),
|
||||
},
|
||||
},
|
||||
Enabled: exchange.FeaturesEnabled{
|
||||
AutoPairUpdates: true,
|
||||
@@ -2057,3 +2063,202 @@ func (b *Binance) GetServerTime(ctx context.Context, ai asset.Item) (time.Time,
|
||||
}
|
||||
return time.Time{}, fmt.Errorf("%s %w", ai, asset.ErrNotSupported)
|
||||
}
|
||||
|
||||
// GetLatestFundingRate returns the latest funding rate for a given asset and currency
|
||||
func (b *Binance) GetLatestFundingRate(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)
|
||||
}
|
||||
format, err := b.GetPairFormat(r.Asset, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fPair := r.Pair.Format(format)
|
||||
pairRate := fundingrate.LatestRateResponse{
|
||||
Exchange: b.Name,
|
||||
Asset: r.Asset,
|
||||
Pair: fPair,
|
||||
}
|
||||
switch r.Asset {
|
||||
case asset.USDTMarginedFutures:
|
||||
var mp []UMarkPrice
|
||||
mp, err = b.UGetMarkPrice(ctx, r.Pair)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pairRate.TimeOfNextRate = time.UnixMilli(mp[len(mp)-1].NextFundingTime)
|
||||
pairRate.LatestRate = fundingrate.Rate{
|
||||
Time: time.UnixMilli(mp[len(mp)-1].Time).Truncate(b.Features.Supports.FuturesCapabilities.FundingRateFrequency),
|
||||
Rate: decimal.NewFromFloat(mp[len(mp)-1].LastFundingRate),
|
||||
}
|
||||
case asset.CoinMarginedFutures:
|
||||
var mp []IndexMarkPrice
|
||||
mp, err = b.GetIndexAndMarkPrice(ctx, fPair.String(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pairRate.TimeOfNextRate = time.UnixMilli(mp[len(mp)-1].NextFundingTime)
|
||||
pairRate.LatestRate = fundingrate.Rate{
|
||||
Time: time.UnixMilli(mp[len(mp)-1].Time).Truncate(b.Features.Supports.FuturesCapabilities.FundingRateFrequency),
|
||||
Rate: mp[len(mp)-1].LastFundingRate.Decimal(),
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("%s %w", r.Asset, asset.ErrNotSupported)
|
||||
}
|
||||
return &pairRate, nil
|
||||
}
|
||||
|
||||
// GetFundingRates returns funding rates for a given asset and currency for a time period
|
||||
func (b *Binance) GetFundingRates(ctx context.Context, r *fundingrate.RatesRequest) (*fundingrate.Rates, error) {
|
||||
if r == nil {
|
||||
return nil, fmt.Errorf("%w RatesRequest", common.ErrNilPointer)
|
||||
}
|
||||
if r.IncludePredictedRate {
|
||||
return nil, fmt.Errorf("%w GetFundingRates IncludePredictedRate", common.ErrFunctionNotSupported)
|
||||
}
|
||||
if !r.PaymentCurrency.IsEmpty() {
|
||||
return nil, fmt.Errorf("%w GetFundingRates PaymentCurrency", common.ErrFunctionNotSupported)
|
||||
}
|
||||
if err := common.StartEndTimeCheck(r.StartDate, r.EndDate); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
format, err := b.GetPairFormat(r.Asset, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fPair := r.Pair.Format(format)
|
||||
pairRate := fundingrate.Rates{
|
||||
Exchange: b.Name,
|
||||
Asset: r.Asset,
|
||||
Pair: fPair,
|
||||
StartDate: r.StartDate,
|
||||
EndDate: r.EndDate,
|
||||
}
|
||||
switch r.Asset {
|
||||
case asset.USDTMarginedFutures:
|
||||
requestLimit := 1000
|
||||
sd := r.StartDate
|
||||
for {
|
||||
var frh []FundingRateHistory
|
||||
frh, err = b.UGetFundingHistory(ctx, fPair, int64(requestLimit), sd, r.EndDate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for j := range frh {
|
||||
pairRate.FundingRates = append(pairRate.FundingRates, fundingrate.Rate{
|
||||
Time: time.UnixMilli(frh[j].FundingTime),
|
||||
Rate: decimal.NewFromFloat(frh[j].FundingRate),
|
||||
})
|
||||
}
|
||||
if len(frh) < requestLimit {
|
||||
break
|
||||
}
|
||||
sd = time.UnixMilli(frh[len(frh)-1].FundingTime)
|
||||
}
|
||||
var mp []UMarkPrice
|
||||
mp, err = b.UGetMarkPrice(ctx, fPair)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pairRate.LatestRate = fundingrate.Rate{
|
||||
Time: time.UnixMilli(mp[len(mp)-1].Time).Truncate(b.Features.Supports.FuturesCapabilities.FundingRateFrequency),
|
||||
Rate: decimal.NewFromFloat(mp[len(mp)-1].LastFundingRate),
|
||||
}
|
||||
pairRate.TimeOfNextRate = time.UnixMilli(mp[len(mp)-1].NextFundingTime)
|
||||
if r.IncludePayments {
|
||||
var income []UAccountIncomeHistory
|
||||
income, err = b.UAccountIncomeHistory(ctx, fPair, "FUNDING_FEE", int64(requestLimit), r.StartDate, r.EndDate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for j := range income {
|
||||
for x := range pairRate.FundingRates {
|
||||
tt := time.UnixMilli(income[j].Time)
|
||||
tt = tt.Truncate(b.Features.Supports.FuturesCapabilities.FundingRateFrequency)
|
||||
if !tt.Equal(pairRate.FundingRates[x].Time) {
|
||||
continue
|
||||
}
|
||||
if pairRate.PaymentCurrency.IsEmpty() {
|
||||
pairRate.PaymentCurrency = currency.NewCode(income[j].Asset)
|
||||
}
|
||||
pairRate.FundingRates[x].Payment = decimal.NewFromFloat(income[j].Income)
|
||||
pairRate.PaymentSum = pairRate.PaymentSum.Add(pairRate.FundingRates[x].Payment)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
case asset.CoinMarginedFutures:
|
||||
requestLimit := 1000
|
||||
sd := r.StartDate
|
||||
for {
|
||||
var frh []FundingRateHistory
|
||||
frh, err = b.FuturesGetFundingHistory(ctx, fPair, int64(requestLimit), sd, r.EndDate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for j := range frh {
|
||||
pairRate.FundingRates = append(pairRate.FundingRates, fundingrate.Rate{
|
||||
Time: time.UnixMilli(frh[j].FundingTime),
|
||||
Rate: decimal.NewFromFloat(frh[j].FundingRate),
|
||||
})
|
||||
}
|
||||
if len(frh) < requestLimit {
|
||||
break
|
||||
}
|
||||
sd = time.UnixMilli(frh[len(frh)-1].FundingTime)
|
||||
}
|
||||
var mp []IndexMarkPrice
|
||||
mp, err = b.GetIndexAndMarkPrice(ctx, fPair.String(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pairRate.LatestRate = fundingrate.Rate{
|
||||
Time: time.UnixMilli(mp[len(mp)-1].Time).Truncate(b.Features.Supports.FuturesCapabilities.FundingRateFrequency),
|
||||
Rate: mp[len(mp)-1].LastFundingRate.Decimal(),
|
||||
}
|
||||
pairRate.TimeOfNextRate = time.UnixMilli(mp[len(mp)-1].NextFundingTime)
|
||||
if r.IncludePayments {
|
||||
var income []FuturesIncomeHistoryData
|
||||
income, err = b.FuturesIncomeHistory(ctx, fPair, "FUNDING_FEE", r.StartDate, r.EndDate, int64(requestLimit))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for j := range income {
|
||||
for x := range pairRate.FundingRates {
|
||||
tt := time.UnixMilli(income[j].Timestamp)
|
||||
tt = tt.Truncate(b.Features.Supports.FuturesCapabilities.FundingRateFrequency)
|
||||
if !tt.Equal(pairRate.FundingRates[x].Time) {
|
||||
continue
|
||||
}
|
||||
if pairRate.PaymentCurrency.IsEmpty() {
|
||||
pairRate.PaymentCurrency = currency.NewCode(income[j].Asset)
|
||||
}
|
||||
pairRate.FundingRates[x].Payment = decimal.NewFromFloat(income[j].Income)
|
||||
pairRate.PaymentSum = pairRate.PaymentSum.Add(pairRate.FundingRates[x].Payment)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("%s %w", r.Asset, asset.ErrNotSupported)
|
||||
}
|
||||
return &pairRate, nil
|
||||
}
|
||||
|
||||
// IsPerpetualFutureCurrency ensures a given asset and currency is a perpetual future
|
||||
func (b *Binance) IsPerpetualFutureCurrency(a asset.Item, cp currency.Pair) (bool, error) {
|
||||
if a == asset.CoinMarginedFutures {
|
||||
if cp.Quote.Equal(currency.PERP) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
if a == asset.USDTMarginedFutures {
|
||||
if cp.Quote.Equal(currency.USDT) || cp.Quote.Equal(currency.BUSD) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -72,12 +72,13 @@ type UCompressedTradeData struct {
|
||||
|
||||
// UMarkPrice stores mark price data
|
||||
type UMarkPrice struct {
|
||||
Symbol string `json:"symbol"`
|
||||
MarkPrice float64 `json:"markPrice,string"`
|
||||
IndexPrice float64 `json:"indexPrice,string"`
|
||||
LastFundingRate float64 `json:"lastFundingRate,string"`
|
||||
NextFundingTime int64 `json:"nextFundingTime"`
|
||||
Time int64 `json:"time"`
|
||||
Symbol string `json:"symbol"`
|
||||
MarkPrice float64 `json:"markPrice,string"`
|
||||
IndexPrice float64 `json:"indexPrice,string"`
|
||||
LastFundingRate float64 `json:"lastFundingRate,string"`
|
||||
EstimatedSettlePrice float64 `json:"estimatedSettlePrice,string"`
|
||||
NextFundingTime int64 `json:"nextFundingTime"`
|
||||
Time int64 `json:"time"`
|
||||
}
|
||||
|
||||
// FundingRateHistory stores funding rate history
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/currencystate"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/fundingrate"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/margin"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
@@ -1505,7 +1506,7 @@ func (b *Base) GetPositionSummary(context.Context, *order.PositionSummaryRequest
|
||||
}
|
||||
|
||||
// GetFundingPaymentDetails returns funding payment details for a future for a specific time period
|
||||
func (b *Base) GetFundingPaymentDetails(context.Context, *order.FundingRatesRequest) (*order.FundingRates, error) {
|
||||
func (b *Base) GetFundingPaymentDetails(context.Context, *fundingrate.RatesRequest) (*fundingrate.Rates, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
@@ -1514,8 +1515,13 @@ func (b *Base) GetFuturesPositions(context.Context, *order.PositionsRequest) ([]
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
// GetLatestFundingRate returns the latest funding rate based on request data
|
||||
func (b *Base) GetLatestFundingRate(context.Context, *fundingrate.LatestRateRequest) (*fundingrate.LatestRateResponse, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
// GetFundingRates returns funding rates based on request data
|
||||
func (b *Base) GetFundingRates(context.Context, *order.FundingRatesRequest) ([]order.FundingRates, error) {
|
||||
func (b *Base) GetFundingRates(context.Context, *fundingrate.RatesRequest) (*fundingrate.Rates, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -2494,7 +2494,7 @@ func TestSetFillsFeedStatus(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFundingRateHistory(t *testing.T) {
|
||||
func TestGetMarginRateHistory(t *testing.T) {
|
||||
t.Parallel()
|
||||
var b Base
|
||||
if _, err := b.GetMarginRatesHistory(context.Background(), nil); !errors.Is(err, common.ErrNotYetImplemented) {
|
||||
@@ -2526,6 +2526,14 @@ func TestGetFundingPaymentDetails(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFundingRate(t *testing.T) {
|
||||
t.Parallel()
|
||||
var b Base
|
||||
if _, err := b.GetLatestFundingRate(context.Background(), nil); !errors.Is(err, common.ErrNotYetImplemented) {
|
||||
t.Errorf("received: %v, expected: %v", err, common.ErrNotYetImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFundingRates(t *testing.T) {
|
||||
t.Parallel()
|
||||
var b Base
|
||||
|
||||
@@ -164,12 +164,27 @@ type FeaturesEnabled struct {
|
||||
|
||||
// FeaturesSupported stores the exchanges supported features
|
||||
type FeaturesSupported struct {
|
||||
REST bool
|
||||
RESTCapabilities protocol.Features
|
||||
Websocket bool
|
||||
WebsocketCapabilities protocol.Features
|
||||
WithdrawPermissions uint32
|
||||
Kline kline.ExchangeCapabilitiesSupported
|
||||
REST bool
|
||||
RESTCapabilities protocol.Features
|
||||
Websocket bool
|
||||
WebsocketCapabilities protocol.Features
|
||||
WithdrawPermissions uint32
|
||||
Kline kline.ExchangeCapabilitiesSupported
|
||||
MaximumOrderHistory time.Duration
|
||||
FuturesCapabilities FuturesCapabilities
|
||||
OfflineFuturesCapabilities FuturesCapabilities
|
||||
}
|
||||
|
||||
// FuturesCapabilities stores the exchange's futures capabilities
|
||||
type FuturesCapabilities struct {
|
||||
FundingRates bool
|
||||
MaximumFundingRateHistory time.Duration
|
||||
FundingRateFrequency time.Duration
|
||||
Positions bool
|
||||
OrderManagerPositionTracking bool
|
||||
Collateral bool
|
||||
CollateralMode bool
|
||||
Leverage bool
|
||||
}
|
||||
|
||||
// Endpoints stores running url endpoints for exchanges
|
||||
|
||||
70
exchanges/fundingrate/fundingrate_types.go
Normal file
70
exchanges/fundingrate/fundingrate_types.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package fundingrate
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"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")
|
||||
|
||||
// RatesRequest is used to request funding rate details for a position
|
||||
type RatesRequest struct {
|
||||
Asset asset.Item
|
||||
Pair currency.Pair
|
||||
// PaymentCurrency is an optional parameter depending on exchange API
|
||||
// if you are paid in a currency that isn't easily inferred from the Pair,
|
||||
// eg BTCUSD-PERP use this field
|
||||
PaymentCurrency currency.Code
|
||||
StartDate time.Time
|
||||
EndDate time.Time
|
||||
IncludePayments bool
|
||||
IncludePredictedRate bool
|
||||
// RespectHistoryLimits if an exchange has a limit on rate history lookup
|
||||
// and your start date is beyond that time, this will set your start date
|
||||
// to the maximum allowed date rather than give you errors
|
||||
RespectHistoryLimits bool
|
||||
}
|
||||
|
||||
// Rates is used to return funding rate details for a position
|
||||
type Rates struct {
|
||||
Exchange string
|
||||
Asset asset.Item
|
||||
Pair currency.Pair
|
||||
StartDate time.Time
|
||||
EndDate time.Time
|
||||
LatestRate Rate
|
||||
PredictedUpcomingRate Rate
|
||||
FundingRates []Rate
|
||||
PaymentSum decimal.Decimal
|
||||
PaymentCurrency currency.Code
|
||||
TimeOfNextRate time.Time
|
||||
}
|
||||
|
||||
// LatestRateRequest is used to request the latest funding rate
|
||||
type LatestRateRequest struct {
|
||||
Asset asset.Item
|
||||
Pair currency.Pair
|
||||
IncludePredictedRate bool
|
||||
}
|
||||
|
||||
// LatestRateResponse for when you just want the latest rate
|
||||
type LatestRateResponse struct {
|
||||
Exchange string
|
||||
Asset asset.Item
|
||||
Pair currency.Pair
|
||||
LatestRate Rate
|
||||
PredictedUpcomingRate Rate
|
||||
TimeOfNextRate time.Time
|
||||
}
|
||||
|
||||
// Rate holds details for an individual funding rate
|
||||
type Rate struct {
|
||||
Time time.Time
|
||||
Rate decimal.Decimal
|
||||
Payment decimal.Decimal
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/currencystate"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/deposit"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/fundingrate"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/margin"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
@@ -144,7 +145,8 @@ type FuturesManagement interface {
|
||||
ScaleCollateral(ctx context.Context, calculator *order.CollateralCalculator) (*order.CollateralByCurrency, error)
|
||||
CalculateTotalCollateral(context.Context, *order.TotalCollateralCalculator) (*order.TotalCollateralResponse, error)
|
||||
GetFuturesPositions(context.Context, *order.PositionsRequest) ([]order.PositionDetails, error)
|
||||
GetFundingRates(context.Context, *order.FundingRatesRequest) ([]order.FundingRates, error)
|
||||
GetFundingRates(context.Context, *fundingrate.RatesRequest) (*fundingrate.Rates, error)
|
||||
GetLatestFundingRate(context.Context, *fundingrate.LatestRateRequest) (*fundingrate.LatestRateResponse, error)
|
||||
IsPerpetualFutureCurrency(asset.Item, currency.Pair) (bool, error)
|
||||
GetCollateralCurrencyForContract(asset.Item, currency.Pair) (currency.Code, asset.Item, error)
|
||||
GetMarginRatesHistory(context.Context, *margin.RateHistoryRequest) (*margin.RateHistoryResponse, error)
|
||||
|
||||
@@ -10,12 +10,14 @@ import (
|
||||
|
||||
// RateHistoryRequest is used to request a funding rate
|
||||
type RateHistoryRequest struct {
|
||||
Exchange string
|
||||
Asset asset.Item
|
||||
Currency currency.Code
|
||||
StartDate time.Time
|
||||
EndDate time.Time
|
||||
GetPredictedRate bool
|
||||
Exchange string
|
||||
Asset asset.Item
|
||||
Currency currency.Code
|
||||
Pair currency.Pair
|
||||
StartDate time.Time
|
||||
EndDate time.Time
|
||||
GetPredictedRate bool
|
||||
|
||||
GetLendingPayments bool
|
||||
GetBorrowRates bool
|
||||
GetBorrowCosts bool
|
||||
|
||||
@@ -3430,8 +3430,8 @@ func (ok *Okx) GetOpenInterest(ctx context.Context, instType, uly, instID string
|
||||
return resp, ok.SendHTTPRequest(ctx, exchange.RestSpot, getOpenInterestEPL, http.MethodGet, common.EncodeURLValues(publicOpenInterestValues, params), nil, &resp, false)
|
||||
}
|
||||
|
||||
// GetFundingRate Retrieve funding rate.
|
||||
func (ok *Okx) GetFundingRate(ctx context.Context, instrumentID string) (*FundingRateResponse, error) {
|
||||
// GetSingleFundingRate returns the latest funding rate
|
||||
func (ok *Okx) GetSingleFundingRate(ctx context.Context, instrumentID string) (*FundingRateResponse, error) {
|
||||
params := url.Values{}
|
||||
if instrumentID == "" {
|
||||
return nil, errMissingInstrumentID
|
||||
@@ -4244,6 +4244,9 @@ func (ok *Okx) SendHTTPRequest(ctx context.Context, ep exchange.URL, f request.E
|
||||
path := endpoint + requestPath
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/json"
|
||||
if _, okay := ctx.Value(testNetVal).(bool); okay {
|
||||
headers["x-simulated-trading"] = "1"
|
||||
}
|
||||
if authenticated {
|
||||
var creds *account.Credentials
|
||||
creds, err = ok.GetCredentials(ctx)
|
||||
|
||||
@@ -18,6 +18,7 @@ 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/fundingrate"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
@@ -32,6 +33,7 @@ const (
|
||||
apiSecret = ""
|
||||
passphrase = ""
|
||||
canManipulateRealOrders = false
|
||||
useTestNet = false
|
||||
)
|
||||
|
||||
var ok = &Okx{}
|
||||
@@ -54,18 +56,36 @@ func TestMain(m *testing.M) {
|
||||
exchCfg.API.AuthenticatedSupport = true
|
||||
exchCfg.API.AuthenticatedWebsocketSupport = true
|
||||
}
|
||||
ok.Websocket = sharedtestvalues.NewTestWebsocket()
|
||||
if !useTestNet {
|
||||
ok.Websocket = sharedtestvalues.NewTestWebsocket()
|
||||
}
|
||||
err = ok.Setup(exchCfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
request.MaxRequestJobs = 200
|
||||
ok.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
|
||||
ok.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride()
|
||||
setupWS()
|
||||
if !useTestNet {
|
||||
ok.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
|
||||
ok.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride()
|
||||
setupWS()
|
||||
}
|
||||
err = ok.UpdateTradablePairs(contextGenerate(), true)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
// contextGenerate sends an optional value to allow test requests
|
||||
// named this way, so it shows up in auto-complete and reminds you to use it
|
||||
func contextGenerate() context.Context {
|
||||
ctx := context.Background()
|
||||
if useTestNet {
|
||||
ctx = context.WithValue(ctx, testNetKey("testnet"), useTestNet)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
func TestStart(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := ok.Start(context.Background(), nil)
|
||||
@@ -228,10 +248,10 @@ func TestGetOpenInterest(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFundingRate(t *testing.T) {
|
||||
func TestGetSingleFundingRate(t *testing.T) {
|
||||
t.Parallel()
|
||||
if _, err := ok.GetFundingRate(context.Background(), "BTC-USD-SWAP"); err != nil {
|
||||
t.Error("okx GetFundingRate() error", err)
|
||||
if _, err := ok.GetSingleFundingRate(context.Background(), "BTC-USD-SWAP"); err != nil {
|
||||
t.Error("okx GetSingleFundingRate() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3203,6 +3223,80 @@ func TestInstrument(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLatestFundingRate(t *testing.T) {
|
||||
t.Parallel()
|
||||
cp, err := currency.NewPairFromString("BTC-USD-SWAP")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
_, err = ok.GetLatestFundingRate(contextGenerate(), &fundingrate.LatestRateRequest{
|
||||
Asset: asset.PerpetualSwap,
|
||||
Pair: cp,
|
||||
IncludePredictedRate: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFundingRates(t *testing.T) {
|
||||
t.Parallel()
|
||||
cp, err := currency.NewPairFromString("BTC-USD-SWAP")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
r := &fundingrate.RatesRequest{
|
||||
Asset: asset.PerpetualSwap,
|
||||
Pair: cp,
|
||||
PaymentCurrency: currency.USDT,
|
||||
StartDate: time.Now().Add(-time.Hour * 24 * 7),
|
||||
EndDate: time.Now(),
|
||||
IncludePredictedRate: true,
|
||||
}
|
||||
if sharedtestvalues.AreAPICredentialsSet(ok) {
|
||||
r.IncludePayments = true
|
||||
}
|
||||
_, err = ok.GetFundingRates(contextGenerate(), r)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
r.StartDate = time.Now().Add(-time.Hour * 24 * 120)
|
||||
_, err = ok.GetFundingRates(contextGenerate(), r)
|
||||
if !errors.Is(err, fundingrate.ErrFundingRateOutsideLimits) {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
r.RespectHistoryLimits = true
|
||||
_, err = ok.GetFundingRates(contextGenerate(), r)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsPerpetualFutureCurrency(t *testing.T) {
|
||||
t.Parallel()
|
||||
is, err := ok.IsPerpetualFutureCurrency(asset.Binary, currency.NewPair(currency.BTC, currency.USDT))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if is {
|
||||
t.Error("expected false")
|
||||
}
|
||||
|
||||
cp, err := currency.NewPairFromString("BTC-USD-SWAP")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
is, err = ok.IsPerpetualFutureCurrency(asset.PerpetualSwap, cp)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !is {
|
||||
t.Error("expected true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAssetsFromInstrumentTypeOrID(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := ok.GetAssetsFromInstrumentTypeOrID("", "")
|
||||
|
||||
@@ -160,22 +160,6 @@ func (a *OpenInterest) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON deserializes JSON, and timestamp information.
|
||||
func (a *FundingRateResponse) UnmarshalJSON(data []byte) error {
|
||||
type Alias FundingRateResponse
|
||||
chil := &struct {
|
||||
*Alias
|
||||
FundingRate string `json:"fundingRate"`
|
||||
}{
|
||||
Alias: (*Alias)(a),
|
||||
}
|
||||
err := json.Unmarshal(data, chil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON deserializes JSON, and timestamp information.
|
||||
func (a *LimitPriceResponse) UnmarshalJSON(data []byte) error {
|
||||
type Alias LimitPriceResponse
|
||||
|
||||
@@ -64,6 +64,13 @@ const (
|
||||
operationLogin = "login"
|
||||
)
|
||||
|
||||
// testNetKey this key is designed for using the testnet endpoints
|
||||
// setting context.WithValue(ctx, testNetKey("testnet"), useTestNet)
|
||||
// will ensure the appropriate headers are sent to OKx to use the testnet
|
||||
type testNetKey string
|
||||
|
||||
var testNetVal = testNetKey("testnet")
|
||||
|
||||
// Market Data Endpoints
|
||||
|
||||
// TickerResponse represents the market data endpoint ticker detail
|
||||
@@ -329,12 +336,13 @@ type OpenInterest struct {
|
||||
|
||||
// FundingRateResponse response data for the Funding Rate for an instruction type
|
||||
type FundingRateResponse struct {
|
||||
FundingRate okxNumericalValue `json:"fundingRate"`
|
||||
FundingTime okxUnixMilliTime `json:"fundingTime"`
|
||||
InstrumentID string `json:"instId"`
|
||||
InstrumentType string `json:"instType"`
|
||||
NextFundingRate okxNumericalValue `json:"nextFundingRate"`
|
||||
NextFundingTime okxUnixMilliTime `json:"nextFundingTime"`
|
||||
FundingRate convert.StringToFloat64 `json:"fundingRate"`
|
||||
RealisedRate convert.StringToFloat64 `json:"realizedRate"`
|
||||
FundingTime okxUnixMilliTime `json:"fundingTime"`
|
||||
InstrumentID string `json:"instId"`
|
||||
InstrumentType string `json:"instType"`
|
||||
NextFundingRate convert.StringToFloat64 `json:"nextFundingRate"`
|
||||
NextFundingTime okxUnixMilliTime `json:"nextFundingTime"`
|
||||
}
|
||||
|
||||
// LimitPriceResponse hold an information for
|
||||
@@ -1371,26 +1379,26 @@ type BillsDetailQueryParameter struct {
|
||||
|
||||
// BillsDetailResponse represents account bills information.
|
||||
type BillsDetailResponse struct {
|
||||
Balance string `json:"bal"`
|
||||
BalanceChange string `json:"balChg"`
|
||||
BillID string `json:"billId"`
|
||||
Currency string `json:"ccy"`
|
||||
ExecType string `json:"execType"` // Order flow type, T:taker M:maker
|
||||
Fee string `json:"fee"` // Fee Negative number represents the user transaction fee charged by the platform. Positive number represents rebate.
|
||||
From string `json:"from"` // The remitting account 6: FUNDING 18: Trading account When bill type is not transfer, the field returns "".
|
||||
InstrumentID string `json:"instId"`
|
||||
InstrumentType string `json:"instType"`
|
||||
MarginMode string `json:"mgnMode"`
|
||||
Notes string `json:"notes"` // notes When bill type is not transfer, the field returns "".
|
||||
OrderID string `json:"ordId"`
|
||||
ProfitAndLoss string `json:"pnl"`
|
||||
PositionLevelBalance string `json:"posBal"`
|
||||
PositionLevelBalanceChange string `json:"posBalChg"`
|
||||
SubType string `json:"subType"`
|
||||
Size string `json:"sz"`
|
||||
To string `json:"to"`
|
||||
Timestamp okxUnixMilliTime `json:"ts"`
|
||||
Type string `json:"type"`
|
||||
Balance okxNumericalValue `json:"bal"`
|
||||
BalanceChange string `json:"balChg"`
|
||||
BillID string `json:"billId"`
|
||||
Currency string `json:"ccy"`
|
||||
ExecType string `json:"execType"` // Order flow type, T:taker M:maker
|
||||
Fee convert.StringToFloat64 `json:"fee"` // Fee Negative number represents the user transaction fee charged by the platform. Positive number represents rebate.
|
||||
From string `json:"from"` // The remitting account 6: FUNDING 18: Trading account When bill type is not transfer, the field returns "".
|
||||
InstrumentID string `json:"instId"`
|
||||
InstrumentType asset.Item `json:"instType"`
|
||||
MarginMode string `json:"mgnMode"`
|
||||
Notes string `json:"notes"` // notes When bill type is not transfer, the field returns "".
|
||||
OrderID string `json:"ordId"`
|
||||
ProfitAndLoss convert.StringToFloat64 `json:"pnl"`
|
||||
PositionLevelBalance convert.StringToFloat64 `json:"posBal"`
|
||||
PositionLevelBalanceChange convert.StringToFloat64 `json:"posBalChg"`
|
||||
SubType string `json:"subType"`
|
||||
Size convert.StringToFloat64 `json:"sz"`
|
||||
To string `json:"to"`
|
||||
Timestamp okxUnixMilliTime `json:"ts"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// AccountConfigurationResponse represents account configuration response.
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/deposit"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/fundingrate"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
@@ -121,6 +122,11 @@ func (ok *Okx) SetDefaults() {
|
||||
ModifyOrder: true,
|
||||
},
|
||||
WithdrawPermissions: exchange.AutoWithdrawCrypto,
|
||||
FuturesCapabilities: exchange.FuturesCapabilities{
|
||||
FundingRates: true,
|
||||
MaximumFundingRateHistory: kline.ThreeMonth.Duration(),
|
||||
FundingRateFrequency: kline.EightHour.Duration(),
|
||||
},
|
||||
},
|
||||
Enabled: exchange.FeaturesEnabled{
|
||||
AutoPairUpdates: true,
|
||||
@@ -1546,3 +1552,164 @@ func (ok *Okx) getInstrumentsForAsset(ctx context.Context, a asset.Item) ([]Inst
|
||||
InstrumentType: instType,
|
||||
})
|
||||
}
|
||||
|
||||
// GetLatestFundingRate returns the latest funding rate for a given asset and currency
|
||||
func (ok *Okx) GetLatestFundingRate(ctx context.Context, r *fundingrate.LatestRateRequest) (*fundingrate.LatestRateResponse, error) {
|
||||
if r == nil {
|
||||
return nil, fmt.Errorf("%w LatestRateRequest", common.ErrNilPointer)
|
||||
}
|
||||
format, err := ok.GetPairFormat(r.Asset, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fPair := r.Pair.Format(format)
|
||||
pairRate := fundingrate.LatestRateResponse{
|
||||
Exchange: ok.Name,
|
||||
Asset: r.Asset,
|
||||
Pair: fPair,
|
||||
}
|
||||
fr, err := ok.GetSingleFundingRate(ctx, fPair.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pairRate.LatestRate = fundingrate.Rate{
|
||||
Time: fr.FundingTime.Time(),
|
||||
Rate: fr.FundingRate.Decimal(),
|
||||
}
|
||||
if r.IncludePredictedRate {
|
||||
pairRate.TimeOfNextRate = fr.NextFundingTime.Time()
|
||||
pairRate.PredictedUpcomingRate = fundingrate.Rate{
|
||||
Time: fr.NextFundingTime.Time(),
|
||||
Rate: fr.NextFundingRate.Decimal(),
|
||||
}
|
||||
}
|
||||
return &pairRate, nil
|
||||
}
|
||||
|
||||
// GetFundingRates returns funding rates for a given asset and currency for a time period
|
||||
func (ok *Okx) GetFundingRates(ctx context.Context, r *fundingrate.RatesRequest) (*fundingrate.Rates, error) {
|
||||
if r == nil {
|
||||
return nil, fmt.Errorf("%w RatesRequest", common.ErrNilPointer)
|
||||
}
|
||||
requestLimit := 100
|
||||
sd := r.StartDate
|
||||
maxLookback := time.Now().Add(-ok.Features.Supports.FuturesCapabilities.MaximumFundingRateHistory)
|
||||
if r.StartDate.Before(maxLookback) {
|
||||
if r.RespectHistoryLimits {
|
||||
r.StartDate = maxLookback
|
||||
} else {
|
||||
return nil, fmt.Errorf("%w earliest date is %v", fundingrate.ErrFundingRateOutsideLimits, maxLookback)
|
||||
}
|
||||
if r.EndDate.Before(maxLookback) {
|
||||
return nil, order.ErrGetFundingDataRequired
|
||||
}
|
||||
r.StartDate = maxLookback
|
||||
}
|
||||
format, err := ok.GetPairFormat(r.Asset, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fPair := r.Pair.Format(format)
|
||||
pairRate := fundingrate.Rates{
|
||||
Exchange: ok.Name,
|
||||
Asset: r.Asset,
|
||||
Pair: fPair,
|
||||
StartDate: r.StartDate,
|
||||
EndDate: r.EndDate,
|
||||
}
|
||||
// map of time indexes, allowing for easy lookup of slice index from unix time data
|
||||
mti := make(map[int64]int)
|
||||
for {
|
||||
if sd.Equal(r.EndDate) || sd.After(r.EndDate) {
|
||||
break
|
||||
}
|
||||
var frh []FundingRateResponse
|
||||
frh, err = ok.GetFundingRateHistory(ctx, fPair.String(), sd, r.EndDate, int64(requestLimit))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(frh) == 0 {
|
||||
break
|
||||
}
|
||||
for i := range frh {
|
||||
if r.IncludePayments {
|
||||
mti[frh[i].FundingTime.Time().Unix()] = i
|
||||
}
|
||||
pairRate.FundingRates = append(pairRate.FundingRates, fundingrate.Rate{
|
||||
Time: frh[i].FundingTime.Time(),
|
||||
Rate: frh[i].RealisedRate.Decimal(),
|
||||
})
|
||||
}
|
||||
if len(frh) < requestLimit {
|
||||
break
|
||||
}
|
||||
sd = frh[len(frh)-1].FundingTime.Time()
|
||||
}
|
||||
var fr *FundingRateResponse
|
||||
fr, err = ok.GetSingleFundingRate(ctx, fPair.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fr == nil {
|
||||
return nil, fmt.Errorf("%w GetSingleFundingRate", common.ErrNilPointer)
|
||||
}
|
||||
pairRate.LatestRate = fundingrate.Rate{
|
||||
Time: fr.FundingTime.Time(),
|
||||
Rate: fr.FundingRate.Decimal(),
|
||||
}
|
||||
pairRate.TimeOfNextRate = fr.NextFundingTime.Time()
|
||||
if r.IncludePredictedRate {
|
||||
pairRate.PredictedUpcomingRate = fundingrate.Rate{
|
||||
Time: fr.NextFundingTime.Time(),
|
||||
Rate: fr.NextFundingRate.Decimal(),
|
||||
}
|
||||
}
|
||||
if r.IncludePayments {
|
||||
pairRate.PaymentCurrency = r.Pair.Base
|
||||
if !r.PaymentCurrency.IsEmpty() {
|
||||
pairRate.PaymentCurrency = r.PaymentCurrency
|
||||
}
|
||||
sd = r.StartDate
|
||||
billDetailsFunc := ok.GetBillsDetail3Months
|
||||
if time.Since(r.StartDate) < kline.OneWeek.Duration() {
|
||||
billDetailsFunc = ok.GetBillsDetailLast7Days
|
||||
}
|
||||
for {
|
||||
if sd.Equal(r.EndDate) || sd.After(r.EndDate) {
|
||||
break
|
||||
}
|
||||
var billDetails []BillsDetailResponse
|
||||
billDetails, err = billDetailsFunc(ctx, &BillsDetailQueryParameter{
|
||||
InstrumentType: ok.GetInstrumentTypeFromAssetItem(r.Asset),
|
||||
Currency: pairRate.PaymentCurrency.String(),
|
||||
BillType: 137,
|
||||
BeginTime: sd,
|
||||
EndTime: r.EndDate,
|
||||
Limit: int64(requestLimit),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range billDetails {
|
||||
if index, okay := mti[billDetails[i].Timestamp.Time().Truncate(ok.Features.Supports.FuturesCapabilities.FundingRateFrequency).Unix()]; okay {
|
||||
pairRate.FundingRates[index].Payment = billDetails[i].ProfitAndLoss.Decimal()
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(billDetails) < requestLimit {
|
||||
break
|
||||
}
|
||||
sd = billDetails[len(billDetails)-1].Timestamp.Time()
|
||||
}
|
||||
|
||||
for i := range pairRate.FundingRates {
|
||||
pairRate.PaymentSum = pairRate.PaymentSum.Add(pairRate.FundingRates[i].Payment)
|
||||
}
|
||||
}
|
||||
return &pairRate, nil
|
||||
}
|
||||
|
||||
// IsPerpetualFutureCurrency ensures a given asset and currency is a perpetual future
|
||||
func (ok *Okx) IsPerpetualFutureCurrency(a asset.Item, _ currency.Pair) (bool, error) {
|
||||
return a == asset.PerpetualSwap, nil
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/fundingrate"
|
||||
)
|
||||
|
||||
// SetupPositionController creates a position controller
|
||||
@@ -126,7 +127,7 @@ func (c *PositionController) GetPositionsForExchange(exch string, item asset.Ite
|
||||
}
|
||||
|
||||
// TrackFundingDetails applies funding rate details to a tracked position
|
||||
func (c *PositionController) TrackFundingDetails(d *FundingRates) error {
|
||||
func (c *PositionController) TrackFundingDetails(d *fundingrate.Rates) error {
|
||||
if c == nil {
|
||||
return fmt.Errorf("position controller %w", common.ErrNilPointer)
|
||||
}
|
||||
@@ -432,7 +433,7 @@ func (m *MultiPositionTracker) TrackNewOrder(d *Detail) error {
|
||||
}
|
||||
|
||||
// TrackFundingDetails applies funding rate details to a tracked position
|
||||
func (m *MultiPositionTracker) TrackFundingDetails(d *FundingRates) error {
|
||||
func (m *MultiPositionTracker) TrackFundingDetails(d *fundingrate.Rates) error {
|
||||
if m == nil {
|
||||
return fmt.Errorf("multi-position tracker %w", common.ErrNilPointer)
|
||||
}
|
||||
@@ -550,9 +551,9 @@ func (p *PositionTracker) GetStats() *Position {
|
||||
}
|
||||
|
||||
if p.fundingRateDetails != nil {
|
||||
frs := make([]FundingRate, len(p.fundingRateDetails.FundingRates))
|
||||
frs := make([]fundingrate.Rate, len(p.fundingRateDetails.FundingRates))
|
||||
copy(frs, p.fundingRateDetails.FundingRates)
|
||||
pos.FundingRates = FundingRates{
|
||||
pos.FundingRates = fundingrate.Rates{
|
||||
Exchange: p.fundingRateDetails.Exchange,
|
||||
Asset: p.fundingRateDetails.Asset,
|
||||
Pair: p.fundingRateDetails.Pair,
|
||||
@@ -660,7 +661,7 @@ func (p *PositionTracker) GetLatestPNLSnapshot() (PNLResult, error) {
|
||||
}
|
||||
|
||||
// TrackFundingDetails sets funding rates to a position
|
||||
func (p *PositionTracker) TrackFundingDetails(d *FundingRates) error {
|
||||
func (p *PositionTracker) TrackFundingDetails(d *fundingrate.Rates) error {
|
||||
if p == nil {
|
||||
return fmt.Errorf("position tracker %w", common.ErrNilPointer)
|
||||
}
|
||||
@@ -688,7 +689,7 @@ func (p *PositionTracker) TrackFundingDetails(d *FundingRates) error {
|
||||
return fmt.Errorf("%w for timeframe %v %v %v %v-%v", ErrNoPositionsFound, p.exchange, p.asset, p.contractPair, d.StartDate, d.EndDate)
|
||||
}
|
||||
if p.fundingRateDetails == nil {
|
||||
p.fundingRateDetails = &FundingRates{
|
||||
p.fundingRateDetails = &fundingrate.Rates{
|
||||
Exchange: d.Exchange,
|
||||
Asset: d.Asset,
|
||||
Pair: d.Pair,
|
||||
@@ -699,7 +700,7 @@ func (p *PositionTracker) TrackFundingDetails(d *FundingRates) error {
|
||||
PaymentSum: d.PaymentSum,
|
||||
}
|
||||
}
|
||||
rates := make([]FundingRate, 0, len(d.FundingRates))
|
||||
rates := make([]fundingrate.Rate, 0, len(d.FundingRates))
|
||||
fundingRates:
|
||||
for i := range d.FundingRates {
|
||||
if d.FundingRates[i].Time.Before(p.openingDate) ||
|
||||
@@ -823,12 +824,12 @@ func (p *PositionTracker) TrackNewOrder(d *Detail, isInitialOrder bool) error {
|
||||
p.longPositions = append(p.longPositions, d.Copy())
|
||||
}
|
||||
}
|
||||
var shortSide, longSide decimal.Decimal
|
||||
var shortSideAmount, longSideAmount decimal.Decimal
|
||||
for i := range p.shortPositions {
|
||||
shortSide = shortSide.Add(decimal.NewFromFloat(p.shortPositions[i].Amount))
|
||||
shortSideAmount = shortSideAmount.Add(decimal.NewFromFloat(p.shortPositions[i].Amount))
|
||||
}
|
||||
for i := range p.longPositions {
|
||||
longSide = longSide.Add(decimal.NewFromFloat(p.longPositions[i].Amount))
|
||||
longSideAmount = longSideAmount.Add(decimal.NewFromFloat(p.longPositions[i].Amount))
|
||||
}
|
||||
|
||||
if isInitialOrder {
|
||||
@@ -927,18 +928,18 @@ func (p *PositionTracker) TrackNewOrder(d *Detail, isInitialOrder bool) error {
|
||||
p.unrealisedPNL = result.UnrealisedPNL
|
||||
|
||||
switch {
|
||||
case longSide.GreaterThan(shortSide):
|
||||
case longSideAmount.GreaterThan(shortSideAmount):
|
||||
p.latestDirection = Long
|
||||
case shortSide.GreaterThan(longSide):
|
||||
case shortSideAmount.GreaterThan(longSideAmount):
|
||||
p.latestDirection = Short
|
||||
default:
|
||||
p.latestDirection = ClosePosition
|
||||
}
|
||||
|
||||
if p.latestDirection.IsLong() {
|
||||
p.exposure = longSide.Sub(shortSide)
|
||||
p.exposure = longSideAmount.Sub(shortSideAmount)
|
||||
} else {
|
||||
p.exposure = shortSide.Sub(longSide)
|
||||
p.exposure = shortSideAmount.Sub(longSideAmount)
|
||||
}
|
||||
|
||||
if p.exposure.Equal(decimal.Zero) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/fundingrate"
|
||||
)
|
||||
|
||||
const testExchange = "test"
|
||||
@@ -538,8 +539,8 @@ func TestGetStats(t *testing.T) {
|
||||
}
|
||||
|
||||
p.exchange = testExchange
|
||||
p.fundingRateDetails = &FundingRates{
|
||||
FundingRates: []FundingRate{
|
||||
p.fundingRateDetails = &fundingrate.Rates{
|
||||
FundingRates: []fundingrate.Rate{
|
||||
{},
|
||||
},
|
||||
}
|
||||
@@ -1264,7 +1265,7 @@ func TestPCTrackFundingDetails(t *testing.T) {
|
||||
}
|
||||
|
||||
p := currency.NewPair(currency.BTC, currency.PERP)
|
||||
rates := &FundingRates{
|
||||
rates := &fundingrate.Rates{
|
||||
Asset: asset.Futures,
|
||||
Pair: p,
|
||||
}
|
||||
@@ -1296,7 +1297,7 @@ func TestPCTrackFundingDetails(t *testing.T) {
|
||||
|
||||
rates.StartDate = tn.Add(-time.Hour)
|
||||
rates.EndDate = tn
|
||||
rates.FundingRates = []FundingRate{
|
||||
rates.FundingRates = []fundingrate.Rate{
|
||||
{
|
||||
Time: tn,
|
||||
Rate: decimal.NewFromInt(1337),
|
||||
@@ -1323,7 +1324,7 @@ func TestMPTTrackFundingDetails(t *testing.T) {
|
||||
}
|
||||
|
||||
cp := currency.NewPair(currency.BTC, currency.PERP)
|
||||
rates := &FundingRates{
|
||||
rates := &fundingrate.Rates{
|
||||
Asset: asset.Futures,
|
||||
Pair: cp,
|
||||
}
|
||||
@@ -1333,7 +1334,7 @@ func TestMPTTrackFundingDetails(t *testing.T) {
|
||||
}
|
||||
|
||||
mpt.exchange = testExchange
|
||||
rates = &FundingRates{
|
||||
rates = &fundingrate.Rates{
|
||||
Exchange: testExchange,
|
||||
Asset: asset.Futures,
|
||||
Pair: cp,
|
||||
@@ -1367,7 +1368,7 @@ func TestMPTTrackFundingDetails(t *testing.T) {
|
||||
|
||||
rates.StartDate = tn.Add(-time.Hour)
|
||||
rates.EndDate = tn
|
||||
rates.FundingRates = []FundingRate{
|
||||
rates.FundingRates = []fundingrate.Rate{
|
||||
{
|
||||
Time: tn,
|
||||
Rate: decimal.NewFromInt(1337),
|
||||
@@ -1392,7 +1393,7 @@ func TestPTTrackFundingDetails(t *testing.T) {
|
||||
}
|
||||
|
||||
cp := currency.NewPair(currency.BTC, currency.PERP)
|
||||
rates := &FundingRates{
|
||||
rates := &fundingrate.Rates{
|
||||
Exchange: testExchange,
|
||||
Asset: asset.Futures,
|
||||
Pair: cp,
|
||||
@@ -1431,7 +1432,7 @@ func TestPTTrackFundingDetails(t *testing.T) {
|
||||
t.Errorf("received '%v' expected '%v", err, nil)
|
||||
}
|
||||
|
||||
rates.FundingRates = []FundingRate{
|
||||
rates.FundingRates = []fundingrate.Rate{
|
||||
{
|
||||
Time: rates.StartDate,
|
||||
Rate: decimal.NewFromInt(1337),
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/fundingrate"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -194,7 +195,7 @@ type PositionTracker struct {
|
||||
shortPositions []Detail
|
||||
longPositions []Detail
|
||||
pnlHistory []PNLResult
|
||||
fundingRateDetails *FundingRates
|
||||
fundingRateDetails *fundingrate.Rates
|
||||
}
|
||||
|
||||
// PositionTrackerSetup contains all required fields to
|
||||
@@ -302,7 +303,7 @@ type Position struct {
|
||||
CloseDate time.Time
|
||||
Orders []Detail
|
||||
PNLHistory []PNLResult
|
||||
FundingRates FundingRates
|
||||
FundingRates fundingrate.Rates
|
||||
}
|
||||
|
||||
// PositionSummaryRequest is used to request a summary of an open position
|
||||
@@ -343,36 +344,6 @@ type PositionSummary struct {
|
||||
TotalCollateral decimal.Decimal
|
||||
}
|
||||
|
||||
// FundingRatesRequest is used to request funding rate details for a position
|
||||
type FundingRatesRequest struct {
|
||||
Asset asset.Item
|
||||
Pairs currency.Pairs
|
||||
StartDate time.Time
|
||||
EndDate time.Time
|
||||
IncludePayments bool
|
||||
IncludePredictedRate bool
|
||||
}
|
||||
|
||||
// FundingRates is used to return funding rate details for a position
|
||||
type FundingRates struct {
|
||||
Exchange string
|
||||
Asset asset.Item
|
||||
Pair currency.Pair
|
||||
StartDate time.Time
|
||||
EndDate time.Time
|
||||
LatestRate FundingRate
|
||||
PredictedUpcomingRate FundingRate
|
||||
FundingRates []FundingRate
|
||||
PaymentSum decimal.Decimal
|
||||
}
|
||||
|
||||
// FundingRate holds details for an individual funding rate
|
||||
type FundingRate struct {
|
||||
Time time.Time
|
||||
Rate decimal.Decimal
|
||||
Payment decimal.Decimal
|
||||
}
|
||||
|
||||
// PositionDetails are used to track open positions
|
||||
// in the order manager
|
||||
type PositionDetails struct {
|
||||
|
||||
Reference in New Issue
Block a user