From 578dd0bafe1221f5d1a168be6f8ab1fc2c4a9e99 Mon Sep 17 00:00:00 2001 From: Adrian Gallagher Date: Fri, 1 Sep 2023 16:46:53 +1000 Subject: [PATCH] Binance: Add crypto/flexible loan endpoints (#1331) * Binance: Add crypto/flexible loan endpoints * Address nitters * Address shazbert's nitters --- exchanges/binance/binance.go | 506 +++++++++++++++++++++++++++++ exchanges/binance/binance_test.go | 237 ++++++++++++++ exchanges/binance/binance_types.go | 297 +++++++++++++++++ exchanges/binance/type_convert.go | 19 +- 4 files changed, 1056 insertions(+), 3 deletions(-) diff --git a/exchanges/binance/binance.go b/exchanges/binance/binance.go index d8b23842..7f95c5f7 100644 --- a/exchanges/binance/binance.go +++ b/exchanges/binance/binance.go @@ -79,9 +79,43 @@ const ( withdrawHistory = "/sapi/v1/capital/withdraw/history" depositAddress = "/sapi/v1/capital/deposit/address" + // Crypto loan endpoints + loanIncomeHistory = "/sapi/v1/loan/income" + loanBorrow = "/sapi/v1/loan/borrow" + loanBorrowHistory = "/sapi/v1/loan/borrow/history" + loanOngoingOrders = "/sapi/v1/loan/ongoing/orders" + loanRepay = "/sapi/v1/loan/repay" + loanRepaymentHistory = "/sapi/v1/loan/repay/history" + loanAdjustLTV = "/sapi/v1/loan/adjust/ltv" + loanLTVAdjustmentHistory = "/sapi/v1/loan/ltv/adjustment/history" + loanableAssetsData = "/sapi/v1/loan/loanable/data" + loanCollateralAssetsData = "/sapi/v1/loan/collateral/data" + loanCheckCollateralRepayRate = "/sapi/v1/loan/repay/collateral/rate" + loanCustomiseMarginCall = "/sapi/v1/loan/customize/margin_call" + + // Flexible loan endpoints + flexibleLoanBorrow = "/sapi/v1/loan/flexible/borrow" + flexibleLoanOngoingOrders = "/sapi/v1/loan/flexible/ongoing/orders" + flexibleLoanBorrowHistory = "/sapi/v1/loan/flexible/borrow/history" + flexibleLoanRepay = "/sapi/v1/loan/flexible/repay" + flexibleLoanRepayHistory = "/sapi/v1/loan/flexible/repay/history" + flexibleLoanAdjustLTV = "/sapi/v1/loan/flexible/adjust/ltv" + flexibleLoanLTVHistory = "/sapi/v1/loan/flexible/ltv/adjustment/history" + flexibleLoanAssetsData = "/sapi/v1/loan/flexible/loanable/data" + flexibleLoanCollateralAssetsData = "/sapi/v1/loan/flexible/collateral/data" + defaultRecvWindow = 5 * time.Second ) +var ( + errLoanCoinMustBeSet = errors.New("loan coin must bet set") + errLoanTermMustBeSet = errors.New("loan term must be set") + errCollateralCoinMustBeSet = errors.New("collateral coin must be set") + errOrderIDMustBeSet = errors.New("orderID must be set") + errAmountMustBeSet = errors.New("amount must not be <= 0") + errEitherLoanOrCollateralAmountsMustBeSet = errors.New("either loan or collateral amounts must be set") +) + // GetUndocumentedInterestHistory gets interest history for currency/currencies provided func (b *Binance) GetUndocumentedInterestHistory(ctx context.Context) (MarginInfoData, error) { var resp MarginInfoData @@ -1267,3 +1301,475 @@ func (b *Binance) FetchSpotExchangeLimits(ctx context.Context) ([]order.MinMaxLe } return limits, nil } + +// CryptoLoanIncomeHistory returns crypto loan income history +func (b *Binance) CryptoLoanIncomeHistory(ctx context.Context, curr currency.Code, loanType string, startTime, endTime time.Time, limit int64) ([]CryptoLoansIncomeHistory, error) { + params := url.Values{} + if !curr.IsEmpty() { + params.Set("asset", curr.String()) + } + if loanType != "" { + params.Set("type", loanType) + } + if !startTime.IsZero() { + params.Set("startTime", strconv.FormatInt(startTime.UnixMilli(), 10)) + } + if !endTime.IsZero() { + params.Set("endTime", strconv.FormatInt(endTime.UnixMilli(), 10)) + } + if limit != 0 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + var resp []CryptoLoansIncomeHistory + return resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodGet, loanIncomeHistory, params, spotDefaultRate, &resp) +} + +// CryptoLoanBorrow borrows crypto +func (b *Binance) CryptoLoanBorrow(ctx context.Context, loanCoin currency.Code, loanAmount float64, collateralCoin currency.Code, collateralAmount float64, loanTerm int64) ([]CryptoLoanBorrow, error) { + if loanCoin.IsEmpty() { + return nil, errLoanCoinMustBeSet + } + if collateralCoin.IsEmpty() { + return nil, errCollateralCoinMustBeSet + } + if loanTerm <= 0 { + return nil, errLoanTermMustBeSet + } + if loanAmount == 0 && collateralAmount == 0 { + return nil, errEitherLoanOrCollateralAmountsMustBeSet + } + + params := url.Values{} + params.Set("loanCoin", loanCoin.String()) + if loanAmount != 0 { + params.Set("loanAmount", strconv.FormatFloat(loanAmount, 'f', -1, 64)) + } + params.Set("collateralCoin", collateralCoin.String()) + if collateralAmount != 0 { + params.Set("collateralAmount", strconv.FormatFloat(collateralAmount, 'f', -1, 64)) + } + params.Set("loanTerm", strconv.FormatInt(loanTerm, 10)) + + var resp []CryptoLoanBorrow + return resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodPost, loanBorrow, params, spotDefaultRate, &resp) +} + +// CryptoLoanBorrowHistory gets loan borrow history +func (b *Binance) CryptoLoanBorrowHistory(ctx context.Context, orderID int64, loanCoin, collateralCoin currency.Code, startTime, endTime time.Time, current, limit int64) (*LoanBorrowHistory, error) { + params := url.Values{} + if orderID != 0 { + params.Set("orderId", strconv.FormatInt(orderID, 10)) + } + if !loanCoin.IsEmpty() { + params.Set("loanCoin", loanCoin.String()) + } + if !collateralCoin.IsEmpty() { + params.Set("collateralCoin", collateralCoin.String()) + } + if !startTime.IsZero() { + params.Set("startTime", strconv.FormatInt(startTime.UnixMilli(), 10)) + } + if !endTime.IsZero() { + params.Set("endTime", strconv.FormatInt(endTime.UnixMilli(), 10)) + } + if current != 0 { + params.Set("current", strconv.FormatInt(current, 10)) + } + if limit != 0 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + var resp LoanBorrowHistory + return &resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodGet, loanBorrowHistory, params, spotDefaultRate, &resp) +} + +// CryptoLoanOngoingOrders obtains ongoing loan orders +func (b *Binance) CryptoLoanOngoingOrders(ctx context.Context, orderID int64, loanCoin, collateralCoin currency.Code, current, limit int64) (*CryptoLoanOngoingOrder, error) { + params := url.Values{} + if orderID != 0 { + params.Set("orderId", strconv.FormatInt(orderID, 10)) + } + if !loanCoin.IsEmpty() { + params.Set("loanCoin", loanCoin.String()) + } + if !collateralCoin.IsEmpty() { + params.Set("collateralCoin", collateralCoin.String()) + } + if current != 0 { + params.Set("current", strconv.FormatInt(current, 10)) + } + if limit != 0 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + var resp CryptoLoanOngoingOrder + return &resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodGet, loanOngoingOrders, params, spotDefaultRate, &resp) +} + +// CryptoLoanRepay repays a crypto loan +func (b *Binance) CryptoLoanRepay(ctx context.Context, orderID int64, amount float64, repayType int64, collateralReturn bool) ([]CryptoLoanRepay, error) { + if orderID <= 0 { + return nil, errOrderIDMustBeSet + } + if amount <= 0 { + return nil, errAmountMustBeSet + } + + params := url.Values{} + params.Set("orderId", strconv.FormatInt(orderID, 10)) + params.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64)) + if repayType != 0 { + params.Set("type", strconv.FormatInt(repayType, 10)) + } + params.Set("collateralReturn", strconv.FormatBool(collateralReturn)) + + var resp []CryptoLoanRepay + return resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodPost, loanRepay, params, spotDefaultRate, &resp) +} + +// CryptoLoanRepaymentHistory gets the crypto loan repayment history +func (b *Binance) CryptoLoanRepaymentHistory(ctx context.Context, orderID int64, loanCoin, collateralCoin currency.Code, startTime, endTime time.Time, current, limit int64) (*CryptoLoanRepayHistory, error) { + params := url.Values{} + if orderID != 0 { + params.Set("orderId", strconv.FormatInt(orderID, 10)) + } + if !loanCoin.IsEmpty() { + params.Set("loanCoin", loanCoin.String()) + } + if !collateralCoin.IsEmpty() { + params.Set("collateralCoin", collateralCoin.String()) + } + if !startTime.IsZero() { + params.Set("startTime", strconv.FormatInt(startTime.UnixMilli(), 10)) + } + if !endTime.IsZero() { + params.Set("endTime", strconv.FormatInt(endTime.UnixMilli(), 10)) + } + if current != 0 { + params.Set("current", strconv.FormatInt(current, 10)) + } + if limit != 0 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + var resp CryptoLoanRepayHistory + return &resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodGet, loanRepaymentHistory, params, spotDefaultRate, &resp) +} + +// CryptoLoanAdjustLTV adjusts the LTV of a crypto loan +func (b *Binance) CryptoLoanAdjustLTV(ctx context.Context, orderID int64, reduce bool, amount float64) (*CryptoLoanAdjustLTV, error) { + if orderID <= 0 { + return nil, errOrderIDMustBeSet + } + if amount <= 0 { + return nil, errAmountMustBeSet + } + + params := url.Values{} + params.Set("orderId", strconv.FormatInt(orderID, 10)) + params.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64)) + direction := "ADDITIONAL" + if reduce { + direction = "REDUCED" + } + params.Set("direction", direction) + + var resp CryptoLoanAdjustLTV + return &resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodPost, loanAdjustLTV, params, spotDefaultRate, &resp) +} + +// CryptoLoanLTVAdjustmentHistory gets the crypto loan LTV adjustment history +func (b *Binance) CryptoLoanLTVAdjustmentHistory(ctx context.Context, orderID int64, loanCoin, collateralCoin currency.Code, startTime, endTime time.Time, current, limit int64) (*CryptoLoanLTVAdjustmentHistory, error) { + params := url.Values{} + if orderID != 0 { + params.Set("orderId", strconv.FormatInt(orderID, 10)) + } + if !loanCoin.IsEmpty() { + params.Set("loanCoin", loanCoin.String()) + } + if !collateralCoin.IsEmpty() { + params.Set("collateralCoin", collateralCoin.String()) + } + if !startTime.IsZero() { + params.Set("startTime", strconv.FormatInt(startTime.UnixMilli(), 10)) + } + if !endTime.IsZero() { + params.Set("endTime", strconv.FormatInt(endTime.UnixMilli(), 10)) + } + if current != 0 { + params.Set("current", strconv.FormatInt(current, 10)) + } + if limit != 0 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + var resp CryptoLoanLTVAdjustmentHistory + return &resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodGet, loanLTVAdjustmentHistory, params, spotDefaultRate, &resp) +} + +// CryptoLoanAssetsData gets the loanable assets data +func (b *Binance) CryptoLoanAssetsData(ctx context.Context, loanCoin currency.Code, vipLevel int64) (*LoanableAssetsData, error) { + params := url.Values{} + if !loanCoin.IsEmpty() { + params.Set("loanCoin", loanCoin.String()) + } + if vipLevel != 0 { + params.Set("vipLevel", strconv.FormatInt(vipLevel, 10)) + } + + var resp LoanableAssetsData + return &resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodGet, loanableAssetsData, params, spotDefaultRate, &resp) +} + +// CryptoLoanCollateralAssetsData gets the collateral assets data +func (b *Binance) CryptoLoanCollateralAssetsData(ctx context.Context, collateralCoin currency.Code, vipLevel int64) (*CollateralAssetData, error) { + params := url.Values{} + if !collateralCoin.IsEmpty() { + params.Set("collateralCoin", collateralCoin.String()) + } + if vipLevel != 0 { + params.Set("vipLevel", strconv.FormatInt(vipLevel, 10)) + } + + var resp CollateralAssetData + return &resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodGet, loanCollateralAssetsData, params, spotDefaultRate, &resp) +} + +// CryptoLoanCheckCollateralRepayRate checks the collateral repay rate +func (b *Binance) CryptoLoanCheckCollateralRepayRate(ctx context.Context, loanCoin, collateralCoin currency.Code, amount float64) (*CollateralRepayRate, error) { + if loanCoin.IsEmpty() { + return nil, errLoanCoinMustBeSet + } + if collateralCoin.IsEmpty() { + return nil, errCollateralCoinMustBeSet + } + if amount <= 0 { + return nil, errAmountMustBeSet + } + + params := url.Values{} + params.Set("loanCoin", loanCoin.String()) + params.Set("collateralCoin", collateralCoin.String()) + params.Set("repayAmount", strconv.FormatFloat(amount, 'f', -1, 64)) + + var resp CollateralRepayRate + return &resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodGet, loanCheckCollateralRepayRate, params, spotDefaultRate, &resp) +} + +// CryptoLoanCustomiseMarginCall customises a loan's margin call +func (b *Binance) CryptoLoanCustomiseMarginCall(ctx context.Context, orderID int64, collateralCoin currency.Code, marginCallValue float64) (*CustomiseMarginCall, error) { + if marginCallValue <= 0 { + return nil, errors.New("marginCallValue must not be <= 0") + } + + params := url.Values{} + if orderID != 0 { + params.Set("orderId", strconv.FormatInt(orderID, 10)) + } + if !collateralCoin.IsEmpty() { + params.Set("collateralCoin", collateralCoin.String()) + } + params.Set("marginCall", strconv.FormatFloat(marginCallValue, 'f', -1, 64)) + + var resp CustomiseMarginCall + return &resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodPost, loanCustomiseMarginCall, params, spotDefaultRate, &resp) +} + +// FlexibleLoanBorrow creates a flexible loan +func (b *Binance) FlexibleLoanBorrow(ctx context.Context, loanCoin, collateralCoin currency.Code, loanAmount, collateralAmount float64) (*FlexibleLoanBorrow, error) { + if loanCoin.IsEmpty() { + return nil, errLoanCoinMustBeSet + } + if collateralCoin.IsEmpty() { + return nil, errCollateralCoinMustBeSet + } + if loanAmount == 0 && collateralAmount == 0 { + return nil, errEitherLoanOrCollateralAmountsMustBeSet + } + + params := url.Values{} + params.Set("loanCoin", loanCoin.String()) + if loanAmount != 0 { + params.Set("loanAmount", strconv.FormatFloat(loanAmount, 'f', -1, 64)) + } + params.Set("collateralCoin", collateralCoin.String()) + if collateralAmount != 0 { + params.Set("collateralAmount", strconv.FormatFloat(collateralAmount, 'f', -1, 64)) + } + + var resp FlexibleLoanBorrow + return &resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodPost, flexibleLoanBorrow, params, spotDefaultRate, &resp) +} + +// FlexibleLoanOngoingOrders gets the flexible loan ongoing orders +func (b *Binance) FlexibleLoanOngoingOrders(ctx context.Context, loanCoin, collateralCoin currency.Code, current, limit int64) (*FlexibleLoanOngoingOrder, error) { + params := url.Values{} + if !loanCoin.IsEmpty() { + params.Set("loanCoin", loanCoin.String()) + } + if !collateralCoin.IsEmpty() { + params.Set("collateralCoin", collateralCoin.String()) + } + if current != 0 { + params.Set("current", strconv.FormatInt(current, 10)) + } + if limit != 0 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + var resp FlexibleLoanOngoingOrder + return &resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodGet, flexibleLoanOngoingOrders, params, spotDefaultRate, &resp) +} + +// FlexibleLoanBorrowHistory gets the flexible loan borrow history +func (b *Binance) FlexibleLoanBorrowHistory(ctx context.Context, loanCoin, collateralCoin currency.Code, startTime, endTime time.Time, current, limit int64) (*FlexibleLoanBorrowHistory, error) { + params := url.Values{} + if !loanCoin.IsEmpty() { + params.Set("loanCoin", loanCoin.String()) + } + if !collateralCoin.IsEmpty() { + params.Set("collateralCoin", collateralCoin.String()) + } + if !startTime.IsZero() { + params.Set("startTime", strconv.FormatInt(startTime.UnixMilli(), 10)) + } + if !endTime.IsZero() { + params.Set("endTime", strconv.FormatInt(endTime.UnixMilli(), 10)) + } + if current != 0 { + params.Set("current", strconv.FormatInt(current, 10)) + } + if limit != 0 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + var resp FlexibleLoanBorrowHistory + return &resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodGet, flexibleLoanBorrowHistory, params, spotDefaultRate, &resp) +} + +// FlexibleLoanRepay repays a flexible loan +func (b *Binance) FlexibleLoanRepay(ctx context.Context, loanCoin, collateralCoin currency.Code, amount float64, collateralReturn, fullRepayment bool) (*FlexibleLoanRepay, error) { + if loanCoin.IsEmpty() { + return nil, errLoanCoinMustBeSet + } + if collateralCoin.IsEmpty() { + return nil, errCollateralCoinMustBeSet + } + if amount <= 0 { + return nil, errAmountMustBeSet + } + + params := url.Values{} + params.Set("loanCoin", loanCoin.String()) + params.Set("collateralCoin", collateralCoin.String()) + params.Set("repayAmount", strconv.FormatFloat(amount, 'f', -1, 64)) + params.Set("collateralReturn", strconv.FormatBool(collateralReturn)) + if fullRepayment { + params.Set("fullRepayment", "true") + } + + var resp FlexibleLoanRepay + return &resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodPost, flexibleLoanRepay, params, spotDefaultRate, &resp) +} + +// FlexibleLoanRepayHistory gets the flexible loan repayment history +func (b *Binance) FlexibleLoanRepayHistory(ctx context.Context, loanCoin, collateralCoin currency.Code, startTime, endTime time.Time, current, limit int64) (*FlexibleLoanRepayHistory, error) { + params := url.Values{} + if !loanCoin.IsEmpty() { + params.Set("loanCoin", loanCoin.String()) + } + if !collateralCoin.IsEmpty() { + params.Set("collateralCoin", collateralCoin.String()) + } + if !startTime.IsZero() { + params.Set("startTime", strconv.FormatInt(startTime.UnixMilli(), 10)) + } + if !endTime.IsZero() { + params.Set("endTime", strconv.FormatInt(endTime.UnixMilli(), 10)) + } + if current != 0 { + params.Set("current", strconv.FormatInt(current, 10)) + } + if limit != 0 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + var resp FlexibleLoanRepayHistory + return &resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodGet, flexibleLoanRepayHistory, params, spotDefaultRate, &resp) +} + +// FlexibleLoanAdjustLTV adjusts the LTV of a flexible loan +func (b *Binance) FlexibleLoanAdjustLTV(ctx context.Context, loanCoin, collateralCoin currency.Code, amount float64, reduce bool) (*FlexibleLoanAdjustLTV, error) { + if loanCoin.IsEmpty() { + return nil, errLoanCoinMustBeSet + } + if collateralCoin.IsEmpty() { + return nil, errCollateralCoinMustBeSet + } + if amount <= 0 { + return nil, errAmountMustBeSet + } + + direction := "ADDITIONAL" + if reduce { + direction = "REDUCED" + } + + params := url.Values{} + params.Set("loanCoin", loanCoin.String()) + params.Set("collateralCoin", collateralCoin.String()) + params.Set("adjustmentAmount", strconv.FormatFloat(amount, 'f', -1, 64)) + params.Set("direction", direction) + + var resp FlexibleLoanAdjustLTV + return &resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodPost, flexibleLoanAdjustLTV, params, spotDefaultRate, &resp) +} + +// FlexibleLoanLTVAdjustmentHistory gets the flexible loan LTV adjustment history +func (b *Binance) FlexibleLoanLTVAdjustmentHistory(ctx context.Context, loanCoin, collateralCoin currency.Code, startTime, endTime time.Time, current, limit int64) (*FlexibleLoanLTVAdjustmentHistory, error) { + params := url.Values{} + if !loanCoin.IsEmpty() { + params.Set("loanCoin", loanCoin.String()) + } + if !collateralCoin.IsEmpty() { + params.Set("collateralCoin", collateralCoin.String()) + } + if !startTime.IsZero() { + params.Set("startTime", strconv.FormatInt(startTime.UnixMilli(), 10)) + } + if !endTime.IsZero() { + params.Set("endTime", strconv.FormatInt(endTime.UnixMilli(), 10)) + } + if current != 0 { + params.Set("current", strconv.FormatInt(current, 10)) + } + if limit != 0 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + var resp FlexibleLoanLTVAdjustmentHistory + return &resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodGet, flexibleLoanLTVHistory, params, spotDefaultRate, &resp) +} + +// FlexibleLoanAssetsData gets the flexible loan assets data +func (b *Binance) FlexibleLoanAssetsData(ctx context.Context, loanCoin currency.Code) (*FlexibleLoanAssetsData, error) { + params := url.Values{} + if !loanCoin.IsEmpty() { + params.Set("loanCoin", loanCoin.String()) + } + + var resp FlexibleLoanAssetsData + return &resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodGet, flexibleLoanAssetsData, params, spotDefaultRate, &resp) +} + +// FlexibleCollateralAssetsData gets the flexible loan collateral assets data +func (b *Binance) FlexibleCollateralAssetsData(ctx context.Context, collateralCoin currency.Code) (*FlexibleCollateralAssetsData, error) { + params := url.Values{} + if !collateralCoin.IsEmpty() { + params.Set("collateralCoin", collateralCoin.String()) + } + + var resp FlexibleCollateralAssetsData + return &resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpotSupplementary, http.MethodGet, flexibleLoanCollateralAssetsData, params, spotDefaultRate, &resp) +} diff --git a/exchanges/binance/binance_test.go b/exchanges/binance/binance_test.go index 5058e88e..f096f969 100644 --- a/exchanges/binance/binance_test.go +++ b/exchanges/binance/binance_test.go @@ -2938,3 +2938,240 @@ func TestGetUserMarginInterestHistory(t *testing.T) { t.Error(err) } } + +func TestGetCryptoLoansIncomeHistory(t *testing.T) { + t.Parallel() + sharedtestvalues.SkipTestIfCredentialsUnset(t, b) + if _, err := b.CryptoLoanIncomeHistory(context.Background(), currency.USDT, "", time.Time{}, time.Time{}, 100); err != nil { + t.Error(err) + } +} + +func TestCryptoLoanBorrow(t *testing.T) { + t.Parallel() + if _, err := b.CryptoLoanBorrow(context.Background(), currency.EMPTYCODE, 1000, currency.BTC, 1, 7); !errors.Is(err, errLoanCoinMustBeSet) { + t.Errorf("received %v, expected %v", err, errLoanCoinMustBeSet) + } + if _, err := b.CryptoLoanBorrow(context.Background(), currency.USDT, 1000, currency.EMPTYCODE, 1, 7); !errors.Is(err, errCollateralCoinMustBeSet) { + t.Errorf("received %v, expected %v", err, errCollateralCoinMustBeSet) + } + if _, err := b.CryptoLoanBorrow(context.Background(), currency.USDT, 0, currency.BTC, 1, 0); !errors.Is(err, errLoanTermMustBeSet) { + t.Errorf("received %v, expected %v", err, errLoanTermMustBeSet) + } + if _, err := b.CryptoLoanBorrow(context.Background(), currency.USDT, 0, currency.BTC, 0, 7); !errors.Is(err, errEitherLoanOrCollateralAmountsMustBeSet) { + t.Errorf("received %v, expected %v", err, errEitherLoanOrCollateralAmountsMustBeSet) + } + + sharedtestvalues.SkipTestIfCredentialsUnset(t, b, canManipulateRealOrders) + if _, err := b.CryptoLoanBorrow(context.Background(), currency.USDT, 1000, currency.BTC, 1, 7); err != nil { + t.Error(err) + } +} + +func TestCryptoLoanBorrowHistory(t *testing.T) { + t.Parallel() + sharedtestvalues.SkipTestIfCredentialsUnset(t, b) + if _, err := b.CryptoLoanBorrowHistory(context.Background(), 0, currency.USDT, currency.BTC, time.Time{}, time.Time{}, 0, 0); err != nil { + t.Error(err) + } +} + +func TestCryptoLoanOngoingOrders(t *testing.T) { + t.Parallel() + sharedtestvalues.SkipTestIfCredentialsUnset(t, b) + if _, err := b.CryptoLoanOngoingOrders(context.Background(), 0, currency.USDT, currency.BTC, 0, 0); err != nil { + t.Error(err) + } +} + +func TestCryptoLoanRepay(t *testing.T) { + t.Parallel() + if _, err := b.CryptoLoanRepay(context.Background(), 0, 1000, 1, false); !errors.Is(err, errOrderIDMustBeSet) { + t.Errorf("received %v, expected %v", err, errOrderIDMustBeSet) + } + if _, err := b.CryptoLoanRepay(context.Background(), 42069, 0, 1, false); !errors.Is(err, errAmountMustBeSet) { + t.Errorf("received %v, expected %v", err, errAmountMustBeSet) + } + + sharedtestvalues.SkipTestIfCredentialsUnset(t, b, canManipulateRealOrders) + if _, err := b.CryptoLoanRepay(context.Background(), 42069, 1000, 1, false); err != nil { + t.Error(err) + } +} + +func TestCryptoLoanRepaymentHistory(t *testing.T) { + t.Parallel() + sharedtestvalues.SkipTestIfCredentialsUnset(t, b) + if _, err := b.CryptoLoanRepaymentHistory(context.Background(), 0, currency.USDT, currency.BTC, time.Time{}, time.Time{}, 0, 0); err != nil { + t.Error(err) + } +} + +func TestCryptoLoanAdjustLTV(t *testing.T) { + t.Parallel() + if _, err := b.CryptoLoanAdjustLTV(context.Background(), 0, true, 1); !errors.Is(err, errOrderIDMustBeSet) { + t.Errorf("received %v, expected %v", err, errOrderIDMustBeSet) + } + if _, err := b.CryptoLoanAdjustLTV(context.Background(), 42069, true, 0); !errors.Is(err, errAmountMustBeSet) { + t.Errorf("received %v, expected %v", err, errAmountMustBeSet) + } + + sharedtestvalues.SkipTestIfCredentialsUnset(t, b, canManipulateRealOrders) + if _, err := b.CryptoLoanAdjustLTV(context.Background(), 42069, true, 1); err != nil { + t.Error(err) + } +} + +func TestCryptoLoanLTVAdjustmentHistory(t *testing.T) { + t.Parallel() + sharedtestvalues.SkipTestIfCredentialsUnset(t, b) + if _, err := b.CryptoLoanLTVAdjustmentHistory(context.Background(), 0, currency.USDT, currency.BTC, time.Time{}, time.Time{}, 0, 0); err != nil { + t.Error(err) + } +} + +func TestCryptoLoanAssetsData(t *testing.T) { + t.Parallel() + sharedtestvalues.SkipTestIfCredentialsUnset(t, b) + if _, err := b.CryptoLoanAssetsData(context.Background(), currency.EMPTYCODE, 0); err != nil { + t.Error(err) + } +} + +func TestCryptoLoanCollateralAssetsData(t *testing.T) { + t.Parallel() + sharedtestvalues.SkipTestIfCredentialsUnset(t, b) + if _, err := b.CryptoLoanCollateralAssetsData(context.Background(), currency.EMPTYCODE, 0); err != nil { + t.Error(err) + } +} + +func TestCryptoLoanCheckCollateralRepayRate(t *testing.T) { + t.Parallel() + if _, err := b.CryptoLoanCheckCollateralRepayRate(context.Background(), currency.EMPTYCODE, currency.BNB, 69); !errors.Is(err, errLoanCoinMustBeSet) { + t.Errorf("received %v, expected %v", err, errLoanCoinMustBeSet) + } + if _, err := b.CryptoLoanCheckCollateralRepayRate(context.Background(), currency.BUSD, currency.EMPTYCODE, 69); !errors.Is(err, errCollateralCoinMustBeSet) { + t.Errorf("received %v, expected %v", err, errCollateralCoinMustBeSet) + } + if _, err := b.CryptoLoanCheckCollateralRepayRate(context.Background(), currency.BUSD, currency.BNB, 0); !errors.Is(err, errAmountMustBeSet) { + t.Errorf("received %v, expected %v", err, errAmountMustBeSet) + } + + sharedtestvalues.SkipTestIfCredentialsUnset(t, b) + if _, err := b.CryptoLoanCheckCollateralRepayRate(context.Background(), currency.BUSD, currency.BNB, 69); err != nil { + t.Error(err) + } +} + +func TestCryptoLoanCustomiseMarginCall(t *testing.T) { + t.Parallel() + if _, err := b.CryptoLoanCustomiseMarginCall(context.Background(), 0, currency.BTC, 0); err == nil { + t.Error("expected an error") + } + + sharedtestvalues.SkipTestIfCredentialsUnset(t, b, canManipulateRealOrders) + if _, err := b.CryptoLoanCustomiseMarginCall(context.Background(), 1337, currency.BTC, .70); err != nil { + t.Error(err) + } +} + +func TestFlexibleLoanBorrow(t *testing.T) { + t.Parallel() + if _, err := b.FlexibleLoanBorrow(context.Background(), currency.EMPTYCODE, currency.USDC, 1, 0); !errors.Is(err, errLoanCoinMustBeSet) { + t.Errorf("received %v, expected %v", err, errLoanCoinMustBeSet) + } + if _, err := b.FlexibleLoanBorrow(context.Background(), currency.ATOM, currency.EMPTYCODE, 1, 0); !errors.Is(err, errCollateralCoinMustBeSet) { + t.Errorf("received %v, expected %v", err, errCollateralCoinMustBeSet) + } + if _, err := b.FlexibleLoanBorrow(context.Background(), currency.ATOM, currency.USDC, 0, 0); !errors.Is(err, errEitherLoanOrCollateralAmountsMustBeSet) { + t.Errorf("received %v, expected %v", err, errEitherLoanOrCollateralAmountsMustBeSet) + } + + sharedtestvalues.SkipTestIfCredentialsUnset(t, b, canManipulateRealOrders) + if _, err := b.FlexibleLoanBorrow(context.Background(), currency.ATOM, currency.USDC, 1, 0); err != nil { + t.Error(err) + } +} + +func TestFlexibleLoanOngoingOrders(t *testing.T) { + t.Parallel() + sharedtestvalues.SkipTestIfCredentialsUnset(t, b) + if _, err := b.FlexibleLoanOngoingOrders(context.Background(), currency.EMPTYCODE, currency.EMPTYCODE, 0, 0); err != nil { + t.Error(err) + } +} + +func TestFlexibleLoanBorrowHistory(t *testing.T) { + t.Parallel() + sharedtestvalues.SkipTestIfCredentialsUnset(t, b) + if _, err := b.FlexibleLoanBorrowHistory(context.Background(), currency.EMPTYCODE, currency.EMPTYCODE, time.Time{}, time.Time{}, 0, 0); err != nil { + t.Error(err) + } +} + +func TestFlexibleLoanRepay(t *testing.T) { + t.Parallel() + + if _, err := b.FlexibleLoanRepay(context.Background(), currency.EMPTYCODE, currency.BTC, 1, false, false); !errors.Is(err, errLoanCoinMustBeSet) { + t.Errorf("received %v, expected %v", err, errLoanCoinMustBeSet) + } + if _, err := b.FlexibleLoanRepay(context.Background(), currency.USDT, currency.EMPTYCODE, 1, false, false); !errors.Is(err, errCollateralCoinMustBeSet) { + t.Errorf("received %v, expected %v", err, errCollateralCoinMustBeSet) + } + if _, err := b.FlexibleLoanRepay(context.Background(), currency.USDT, currency.BTC, 0, false, false); !errors.Is(err, errAmountMustBeSet) { + t.Errorf("received %v, expected %v", err, errAmountMustBeSet) + } + + sharedtestvalues.SkipTestIfCredentialsUnset(t, b, canManipulateRealOrders) + if _, err := b.FlexibleLoanRepay(context.Background(), currency.ATOM, currency.USDC, 1, false, false); err != nil { + t.Error(err) + } +} + +func TestFlexibleLoanRepayHistory(t *testing.T) { + t.Parallel() + sharedtestvalues.SkipTestIfCredentialsUnset(t, b) + if _, err := b.FlexibleLoanRepayHistory(context.Background(), currency.EMPTYCODE, currency.EMPTYCODE, time.Time{}, time.Time{}, 0, 0); err != nil { + t.Error(err) + } +} + +func TestFlexibleLoanAdjustLTV(t *testing.T) { + t.Parallel() + if _, err := b.FlexibleLoanAdjustLTV(context.Background(), currency.EMPTYCODE, currency.BTC, 1, true); !errors.Is(err, errLoanCoinMustBeSet) { + t.Errorf("received %v, expected %v", err, errLoanCoinMustBeSet) + } + if _, err := b.FlexibleLoanAdjustLTV(context.Background(), currency.USDT, currency.EMPTYCODE, 1, true); !errors.Is(err, errCollateralCoinMustBeSet) { + t.Errorf("received %v, expected %v", err, errCollateralCoinMustBeSet) + } + + sharedtestvalues.SkipTestIfCredentialsUnset(t, b, canManipulateRealOrders) + if _, err := b.FlexibleLoanAdjustLTV(context.Background(), currency.USDT, currency.BTC, 1, true); err != nil { + t.Error(err) + } +} + +func TestFlexibleLoanLTVAdjustmentHistory(t *testing.T) { + t.Parallel() + sharedtestvalues.SkipTestIfCredentialsUnset(t, b) + if _, err := b.FlexibleLoanLTVAdjustmentHistory(context.Background(), currency.EMPTYCODE, currency.EMPTYCODE, time.Time{}, time.Time{}, 0, 0); err != nil { + t.Error(err) + } +} + +func TestFlexibleLoanAssetsData(t *testing.T) { + t.Parallel() + sharedtestvalues.SkipTestIfCredentialsUnset(t, b) + if _, err := b.FlexibleLoanAssetsData(context.Background(), currency.EMPTYCODE); err != nil { + t.Error(err) + } +} + +func TestFlexibleCollateralAssetsData(t *testing.T) { + t.Parallel() + sharedtestvalues.SkipTestIfCredentialsUnset(t, b) + if _, err := b.FlexibleCollateralAssetsData(context.Background(), currency.EMPTYCODE); err != nil { + t.Error(err) + } +} diff --git a/exchanges/binance/binance_types.go b/exchanges/binance/binance_types.go index c5f19aac..e45981c5 100644 --- a/exchanges/binance/binance_types.go +++ b/exchanges/binance/binance_types.go @@ -937,3 +937,300 @@ type UserMarginInterestHistory struct { Type string `json:"type"` IsolatedSymbol string `json:"isolatedSymbol"` } + +// CryptoLoansIncomeHistory stores crypto loan income history data +type CryptoLoansIncomeHistory struct { + Asset currency.Code `json:"asset"` + Type string `json:"type"` + Amount float64 `json:"amount,string"` + TransactionID int64 `json:"tranId"` +} + +// CryptoLoanBorrow stores crypto loan borrow data +type CryptoLoanBorrow struct { + LoanCoin currency.Code `json:"loanCoin"` + Amount float64 `json:"amount,string"` + CollateralCoin currency.Code `json:"collateralCoin"` + CollateralAmount float64 `json:"collateralAmount,string"` + HourlyInterestRate float64 `json:"hourlyInterestRate,string"` + OrderID int64 `json:"orderId,string"` +} + +// LoanBorrowHistoryItem stores loan borrow history item data +type LoanBorrowHistoryItem struct { + OrderID int64 `json:"orderId"` + LoanCoin currency.Code `json:"loanCoin"` + InitialLoanAmount float64 `json:"initialLoanAmount,string"` + HourlyInterestRate float64 `json:"hourlyInterestRate,string"` + LoanTerm int64 `json:"loanTerm,string"` + CollateralCoin currency.Code `json:"collateralCoin"` + InitialCollateralAmount float64 `json:"initialCollateralAmount,string"` + BorrowTime binanceTime `json:"borrowTime"` + Status string `json:"status"` +} + +// LoanBorrowHistory stores loan borrow history data +type LoanBorrowHistory struct { + Rows []LoanBorrowHistoryItem `json:"rows"` + Total int64 `json:"total"` +} + +// CryptoLoanOngoingOrderItem stores crypto loan ongoing order item data +type CryptoLoanOngoingOrderItem struct { + OrderID int64 `json:"orderId"` + LoanCoin currency.Code `json:"loanCoin"` + TotalDebt float64 `json:"totalDebt,string"` + ResidualInterest float64 `json:"residualInterest,string"` + CollateralCoin currency.Code `json:"collateralCoin"` + CollateralAmount float64 `json:"collateralAmount,string"` + CurrentLTV float64 `json:"currentLTV,string"` + ExpirationTime binanceTime `json:"expirationTime"` +} + +// CryptoLoanOngoingOrder stores crypto loan ongoing order data +type CryptoLoanOngoingOrder struct { + Rows []CryptoLoanOngoingOrderItem `json:"rows"` + Total int64 `json:"total"` +} + +// CryptoLoanRepay stores crypto loan repayment data +type CryptoLoanRepay struct { + LoanCoin currency.Code `json:"loanCoin"` + RemainingPrincipal float64 `json:"remainingPrincipal,string"` + RemainingInterest float64 `json:"remainingInterest,string"` + CollateralCoin currency.Code `json:"collateralCoin"` + RemainingCollateral float64 `json:"remainingCollateral,string"` + CurrentLTV float64 `json:"currentLTV,string"` + RepayStatus string `json:"repayStatus"` +} + +// CryptoLoanRepayHistoryItem stores crypto loan repayment history item data +type CryptoLoanRepayHistoryItem struct { + LoanCoin currency.Code `json:"loanCoin"` + RepayAmount float64 `json:"repayAmount,string"` + CollateralCoin currency.Code `json:"collateralCoin"` + CollateralUsed float64 `json:"collateralUsed,string"` + CollateralReturn float64 `json:"collateralReturn,string"` + RepayType string `json:"repayType"` + RepayTime binanceTime `json:"repayTime"` + OrderID int64 `json:"orderId"` +} + +// CryptoLoanRepayHistory stores crypto loan repayment history data +type CryptoLoanRepayHistory struct { + Rows []CryptoLoanRepayHistoryItem `json:"rows"` + Total int64 `json:"total"` +} + +// CryptoLoanAdjustLTV stores crypto loan LTV adjustment data +type CryptoLoanAdjustLTV struct { + LoanCoin currency.Code `json:"loanCoin"` + CollateralCoin currency.Code `json:"collateralCoin"` + Direction string `json:"direction"` + Amount float64 `json:"amount,string"` + CurrentLTV float64 `json:"currentLTV,string"` +} + +// CryptoLoanLTVAdjustmentItem stores crypto loan LTV adjustment item data +type CryptoLoanLTVAdjustmentItem struct { + LoanCoin currency.Code `json:"loanCoin"` + CollateralCoin currency.Code `json:"collateralCoin"` + Direction string `json:"direction"` + Amount float64 `json:"amount,string"` + PreviousLTV float64 `json:"preLTV,string"` + AfterLTV float64 `json:"afterLTV,string"` + AdjustTime binanceTime `json:"adjustTime"` + OrderID int64 `json:"orderId"` +} + +// CryptoLoanLTVAdjustmentHistory stores crypto loan LTV adjustment history data +type CryptoLoanLTVAdjustmentHistory struct { + Rows []CryptoLoanLTVAdjustmentItem `json:"rows"` + Total int64 `json:"total"` +} + +// LoanableAssetItem stores loanable asset item data +type LoanableAssetItem struct { + LoanCoin currency.Code `json:"loanCoin"` + SevenDayHourlyInterestRate float64 `json:"_7dHourlyInterestRate,string"` + SevenDayDailyInterestRate float64 `json:"_7dDailyInterestRate,string"` + FourteenDayHourlyInterest float64 `json:"_14dHourlyInterestRate,string"` + FourteenDayDailyInterest float64 `json:"_14dDailyInterestRate,string"` + ThirtyDayHourlyInterest float64 `json:"_30dHourlyInterestRate,string"` + ThirtyDayDailyInterest float64 `json:"_30dDailyInterestRate,string"` + NinetyDayHourlyInterest float64 `json:"_90dHourlyInterestRate,string"` + NinetyDayDailyInterest float64 `json:"_90dDailyInterestRate,string"` + OneHundredAndEightyDayHourlyInterest float64 `json:"_180dHourlyInterestRate,string"` + OneHundredAndEightyDayDailyInterest float64 `json:"_180dDailyInterestRate,string"` + MinimumLimit float64 `json:"minLimit,string"` + MaximumLimit float64 `json:"maxLimit,string"` + VIPLevel int64 `json:"vipLevel"` +} + +// LoanableAssetsData stores loanable assets data +type LoanableAssetsData struct { + Rows []LoanableAssetItem `json:"rows"` + Total int64 `json:"total"` +} + +// CollateralAssetItem stores collateral asset item data +type CollateralAssetItem struct { + CollateralCoin currency.Code `json:"collateralCoin"` + InitialLTV float64 `json:"initialLTV,string"` + MarginCallLTV float64 `json:"marginCallLTV,string"` + LiquidationLTV float64 `json:"liquidationLTV,string"` + MaxLimit float64 `json:"maxLimit,string"` + VIPLevel int64 `json:"vipLevel"` +} + +// CollateralAssetData stores collateral asset data +type CollateralAssetData struct { + Rows []CollateralAssetItem `json:"rows"` + Total int64 `json:"total"` +} + +// CollateralRepayRate stores collateral repayment rate data +type CollateralRepayRate struct { + LoanCoin currency.Code `json:"loanCoin"` + CollateralCoin currency.Code `json:"collateralCoin"` + RepayAmount float64 `json:"repayAmount,string"` + Rate float64 `json:"rate,string"` +} + +// CustomiseMarginCallItem stores customise margin call item data +type CustomiseMarginCallItem struct { + OrderID int64 `json:"orderId"` + CollateralCoin currency.Code `json:"collateralCoin"` + PreMarginCall float64 `json:"preMarginCall,string"` + AfterMarginCall float64 `json:"afterMarginCall,string"` + CustomiseTime binanceTime `json:"customizeTime"` +} + +// CustomiseMarginCall stores customise margin call data +type CustomiseMarginCall struct { + Rows []CustomiseMarginCallItem `json:"rows"` + Total int64 `json:"total"` +} + +// FlexibleLoanBorrow stores a flexible loan borrow +type FlexibleLoanBorrow struct { + LoanCoin currency.Code `json:"loanCoin"` + LoanAmount float64 `json:"loanAmount,string"` + CollateralCoin currency.Code `json:"collateralCoin"` + CollateralAmount float64 `json:"collateralAmount,string"` + Status string `json:"status"` +} + +// FlexibleLoanOngoingOrderItem stores a flexible loan ongoing order item +type FlexibleLoanOngoingOrderItem struct { + LoanCoin currency.Code `json:"loanCoin"` + TotalDebt float64 `json:"totalDebt,string"` + CollateralCoin currency.Code `json:"collateralCoin"` + CollateralAmount float64 `json:"collateralAmount,string"` + CurrentLTV float64 `json:"currentLTV,string"` +} + +// FlexibleLoanOngoingOrder stores flexible loan ongoing orders +type FlexibleLoanOngoingOrder struct { + Rows []FlexibleLoanOngoingOrderItem `json:"rows"` + Total int64 `json:"total"` +} + +// FlexibleLoanBorrowHistoryItem stores a flexible loan borrow history item +type FlexibleLoanBorrowHistoryItem struct { + LoanCoin currency.Code `json:"loanCoin"` + InitialLoanAmount float64 `json:"initialLoanAmount,string"` + CollateralCoin currency.Code `json:"collateralCoin"` + InitialCollateralAmount float64 `json:"initialCollateralAmount,string"` + BorrowTime binanceTime `json:"borrowTime"` + Status string `json:"status"` +} + +// FlexibleLoanBorrowHistory stores flexible loan borrow history +type FlexibleLoanBorrowHistory struct { + Rows []FlexibleLoanBorrowHistoryItem `json:"rows"` + Total int64 `json:"total"` +} + +// FlexibleLoanRepay stores a flexible loan repayment +type FlexibleLoanRepay struct { + LoanCoin currency.Code `json:"loanCoin"` + CollateralCoin currency.Code `json:"collateralCoin"` + RemainingDebt float64 `json:"remainingDebt,string"` + RemainingCollateral float64 `json:"remainingCollateral,string"` + FullRepayment bool `json:"fullRepayment"` + CurrentLTV float64 `json:"currentLTV,string"` + RepayStatus string `json:"repayStatus"` +} + +// FlexibleLoanRepayHistoryItem stores a flexible loan repayment history item +type FlexibleLoanRepayHistoryItem struct { + LoanCoin currency.Code `json:"loanCoin"` + RepayAmount float64 `json:"repayAmount,string"` + CollateralCoin currency.Code `json:"collateralCoin"` + CollateralReturn float64 `json:"collateralReturn,string"` + RepayStatus string `json:"repayStatus"` + RepayTime binanceTime `json:"repayTime"` +} + +// FlexibleLoanRepayHistory stores flexible loan repayment history +type FlexibleLoanRepayHistory struct { + Rows []FlexibleLoanRepayHistoryItem `json:"rows"` + Total int64 `json:"total"` +} + +// FlexibleLoanAdjustLTV stores a flexible loan LTV adjustment +type FlexibleLoanAdjustLTV struct { + LoanCoin currency.Code `json:"loanCoin"` + CollateralCoin currency.Code `json:"collateralCoin"` + Direction string `json:"direction"` + Amount float64 `json:"amount,string"` // docs error: API actually returns "amount" instead of "adjustedAmount" + CurrentLTV float64 `json:"currentLTV,string"` + Status string `json:"status"` +} + +// FlexibleLoanLTVAdjustmentHistoryItem stores a flexible loan LTV adjustment history item +type FlexibleLoanLTVAdjustmentHistoryItem struct { + LoanCoin currency.Code `json:"loanCoin"` + CollateralCoin currency.Code `json:"collateralCoin"` + Direction string `json:"direction"` + CollateralAmount float64 `json:"collateralAmount,string"` + PreviousLTV float64 `json:"preLTV,string"` + AfterLTV float64 `json:"afterLTV,string"` + AdjustTime binanceTime `json:"adjustTime"` +} + +// FlexibleLoanLTVAdjustmentHistory stores flexible loan LTV adjustment history +type FlexibleLoanLTVAdjustmentHistory struct { + Rows []FlexibleLoanLTVAdjustmentHistoryItem `json:"rows"` + Total int64 `json:"total"` +} + +// FlexibleLoanAssetsDataItem stores a flexible loan asset data item +type FlexibleLoanAssetsDataItem struct { + LoanCoin currency.Code `json:"loanCoin"` + FlexibleInterestRate float64 `json:"flexibleInterestRate,string"` + FlexibleMinLimit float64 `json:"flexibleMinLimit,string"` + FlexibleMaxLimit float64 `json:"flexibleMaxLimit,string"` +} + +// FlexibleLoanAssetsData stores flexible loan asset data +type FlexibleLoanAssetsData struct { + Rows []FlexibleLoanAssetsDataItem `json:"rows"` + Total int64 `json:"total"` +} + +// FlexibleCollateralAssetsDataItem stores a flexible collateral asset data item +type FlexibleCollateralAssetsDataItem struct { + CollateralCoin currency.Code `json:"collateralCoin"` + InitialLTV float64 `json:"initialLTV,string"` + MarginCallLTV float64 `json:"marginCallLTV,string"` + LiquidationLTV float64 `json:"liquidationLTV,string"` + MaxLimit float64 `json:"maxLimit,string"` +} + +// FlexibleCollateralAssetsData stores flexible collateral asset data +type FlexibleCollateralAssetsData struct { + Rows []FlexibleCollateralAssetsDataItem `json:"rows"` + Total int64 `json:"total"` +} diff --git a/exchanges/binance/type_convert.go b/exchanges/binance/type_convert.go index 10f98d14..6de53fdc 100644 --- a/exchanges/binance/type_convert.go +++ b/exchanges/binance/type_convert.go @@ -2,6 +2,7 @@ package binance import ( "encoding/json" + "errors" "strconv" "time" ) @@ -10,11 +11,23 @@ import ( type binanceTime time.Time func (t *binanceTime) UnmarshalJSON(data []byte) error { - var timestamp int64 - if err := json.Unmarshal(data, ×tamp); err != nil { + var result interface{} + if err := json.Unmarshal(data, &result); err != nil { return err } - *t = binanceTime(time.UnixMilli(timestamp)) + + switch v := result.(type) { + case string: + timestamp, err := strconv.ParseInt(v, 10, 64) + if err != nil { + return err + } + *t = binanceTime(time.UnixMilli(timestamp)) + case float64: + *t = binanceTime(time.UnixMilli(int64(v))) + default: + return errors.New("invalid time format received") + } return nil }