From e80091de354ab731639acd958c00234ef55b8556 Mon Sep 17 00:00:00 2001 From: Adrian Gallagher Date: Tue, 16 Feb 2021 10:17:47 +1100 Subject: [PATCH] FTX: Various bug fixes (#631) * FTX: Various bug fixes * ImPrOvE CaSe SenSitVity * Shazbert nitterinos * "Rm newline" * Switch from API specified timestamp values to valid FTTBTC ones for auth requests * Move comment to specific test --- exchanges/ftx/ftx.go | 160 ++++++++++++++++++++------------- exchanges/ftx/ftx_test.go | 179 ++++++++++++++++++++++++++----------- exchanges/ftx/ftx_types.go | 18 ++-- 3 files changed, 236 insertions(+), 121 deletions(-) diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index 9ce3b0953ca..ae242131e13 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -13,6 +13,7 @@ import ( "strings" "time" + "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/crypto" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/order" @@ -31,12 +32,12 @@ const ( getMarkets = "/markets" getMarket = "/markets/" getOrderbook = "/markets/%s/orderbook?depth=%s" - getTrades = "/markets/%s/trades?" - getHistoricalData = "/markets/%s/candles?" + getTrades = "/markets/%s/trades" + getHistoricalData = "/markets/%s/candles" getFutures = "/futures" getFuture = "/futures/" getFutureStats = "/futures/%s/stats" - getFundingRates = "/funding_rates?" + getFundingRates = "/funding_rates" getIndexWeights = "/indexes/%s/weights" getAllWalletBalances = "/wallet/all_balances" @@ -50,11 +51,11 @@ const ( getDepositHistory = "/wallet/deposits" getWithdrawalHistory = "/wallet/withdrawals" withdrawRequest = "/wallet/withdrawals" - getOpenOrders = "/orders?" - getOrderHistory = "/orders/history?" - getOpenTriggerOrders = "/conditional_orders?" + getOpenOrders = "/orders" + getOrderHistory = "/orders/history" + getOpenTriggerOrders = "/conditional_orders" getTriggerOrderTriggers = "/conditional_orders/%s/triggers" - getTriggerOrderHistory = "/conditional_orders/history?" + getTriggerOrderHistory = "/conditional_orders/history" placeOrder = "/orders" placeTriggerOrder = "/conditional_orders" modifyOrder = "/orders/%s/modify" @@ -65,8 +66,8 @@ const ( deleteOrder = "/orders/" deleteOrderByClientID = "/orders/by_client_id/" cancelTriggerOrder = "/conditional_orders/" - getFills = "/fills?" - getFundingPayments = "/funding_payments?" + getFills = "/fills" + getFundingPayments = "/funding_payments" getLeveragedTokens = "/lt/tokens" getTokenInfo = "/lt/" getLTBalances = "/lt/balances" @@ -92,7 +93,7 @@ const ( // Margin Endpoints marginBorrowRates = "/spot_margin/borrow_rates" - maringLendingRates = "/spot_margin/lending_rates" + marginLendingRates = "/spot_margin/lending_rates" dailyBorrowedAmounts = "/spot_margin/borrow_summary" marginMarketInfo = "/spot_margin/market_info?market=%s" marginBorrowHistory = "/spot_margin/borrow_history" @@ -112,6 +113,10 @@ const ( rateLimit = 30 ) +var ( + errStartTimeCannotBeAfterEndTime = errors.New("start timestamp cannot be after end timestamp") +) + // GetMarkets gets market data func (f *FTX) GetMarkets() ([]MarketData, error) { resp := struct { @@ -164,28 +169,38 @@ func (f *FTX) GetOrderbook(marketName string, depth int64) (OrderbookData, error // GetTrades gets trades based on the conditions specified func (f *FTX) GetTrades(marketName string, startTime, endTime, limit int64) ([]TradeData, error) { - strLimit := strconv.FormatInt(limit, 10) + if marketName == "" { + return nil, errors.New("a market pair must be specified") + } + params := url.Values{} - params.Set("limit", strLimit) - resp := struct { - Data []TradeData `json:"result"` - }{} + if limit != 0 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } if startTime > 0 && endTime > 0 { if startTime >= (endTime) { - return resp.Data, errors.New("startTime cannot be after endTime") + return nil, errStartTimeCannotBeAfterEndTime } params.Set("start_time", strconv.FormatInt(startTime, 10)) params.Set("end_time", strconv.FormatInt(endTime, 10)) } - return resp.Data, f.SendHTTPRequest(exchange.RestSpot, fmt.Sprintf(getTrades, marketName)+params.Encode(), - &resp) + resp := struct { + Data []TradeData `json:"result"` + }{} + endpoint := common.EncodeURLValues(fmt.Sprintf(getTrades, marketName), params) + return resp.Data, f.SendHTTPRequest(exchange.RestSpot, endpoint, &resp) } // GetHistoricalData gets historical OHLCV data for a given market pair func (f *FTX) GetHistoricalData(marketName, timeInterval, limit string, startTime, endTime time.Time) ([]OHLCVData, error) { - resp := struct { - Data []OHLCVData `json:"result"` - }{} + if marketName == "" { + return nil, errors.New("a market pair must be specified") + } + + if timeInterval == "" { + return nil, errors.New("a time interval must be specified") + } + params := url.Values{} params.Set("resolution", timeInterval) if limit != "" { @@ -193,12 +208,16 @@ func (f *FTX) GetHistoricalData(marketName, timeInterval, limit string, startTim } if !startTime.IsZero() && !endTime.IsZero() { if startTime.After(endTime) { - return resp.Data, errors.New("startTime cannot be after endTime") + return nil, errStartTimeCannotBeAfterEndTime } params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10)) params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10)) } - return resp.Data, f.SendHTTPRequest(exchange.RestSpot, fmt.Sprintf(getHistoricalData, marketName)+params.Encode(), &resp) + resp := struct { + Data []OHLCVData `json:"result"` + }{} + endpoint := common.EncodeURLValues(fmt.Sprintf(getHistoricalData, marketName), params) + return resp.Data, f.SendHTTPRequest(exchange.RestSpot, endpoint, &resp) } // GetFutures gets data on futures @@ -233,7 +252,7 @@ func (f *FTX) GetFundingRates(startTime, endTime time.Time, future string) ([]Fu params := url.Values{} if !startTime.IsZero() && !endTime.IsZero() { if startTime.After(endTime) { - return resp.Data, errors.New("startTime cannot be after endTime") + return resp.Data, errStartTimeCannotBeAfterEndTime } params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10)) params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10)) @@ -241,7 +260,8 @@ func (f *FTX) GetFundingRates(startTime, endTime time.Time, future string) ([]Fu if future != "" { params.Set("future", future) } - return resp.Data, f.SendHTTPRequest(exchange.RestSpot, getFundingRates+params.Encode(), &resp) + endpoint := common.EncodeURLValues(getFundingRates, params) + return resp.Data, f.SendHTTPRequest(exchange.RestSpot, endpoint, &resp) } // GetIndexWeights gets index weights @@ -279,15 +299,15 @@ func (f *FTX) GetMarginLendingRates() ([]MarginFundingData, error) { r := struct { Data []MarginFundingData `json:"result"` }{} - return r.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, maringLendingRates, nil, &r) + return r.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, marginLendingRates, nil, &r) } // MarginDailyBorrowedAmounts gets daily borrowed amounts for margin -func (f *FTX) MarginDailyBorrowedAmounts() ([]MarginMarketInfo, error) { +func (f *FTX) MarginDailyBorrowedAmounts() ([]MarginDailyBorrowStats, error) { r := struct { - Data []MarginMarketInfo `json:"result"` + Data []MarginDailyBorrowStats `json:"result"` }{} - return r.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, dailyBorrowedAmounts, nil, &r) + return r.Data, f.SendHTTPRequest(exchange.RestSpot, dailyBorrowedAmounts, &r) } // GetMarginMarketInfo gets margin market data @@ -331,13 +351,24 @@ func (f *FTX) GetLendingInfo() ([]LendingInfoData, error) { } // SubmitLendingOffer submits an offer for margin lending -func (f *FTX) SubmitLendingOffer(coin string, size, rate float64) (interface{}, error) { - var resp interface{} +func (f *FTX) SubmitLendingOffer(coin string, size, rate float64) error { + resp := struct { + Result string `json:"result"` + Success bool `json:"success"` + }{} req := make(map[string]interface{}) - req["coin"] = coin + req["coin"] = strings.ToUpper(coin) req["size"] = size req["rate"] = rate - return resp, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, marginLendingInfo, req, &resp) + + if err := f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost, marginLendingOffers, req, &resp); err != nil { + return err + } + + if !resp.Success { + return errors.New(resp.Result) + } + return nil } // GetAccountInfo gets account info @@ -392,7 +423,7 @@ func (f *FTX) FetchDepositAddress(coin string) (DepositData, error) { resp := struct { Data DepositData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getDepositAddress+coin, nil, &resp) + return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getDepositAddress+strings.ToUpper(coin), nil, &resp) } // FetchDepositHistory gets deposit history @@ -414,7 +445,7 @@ func (f *FTX) FetchWithdrawalHistory() ([]TransactionData, error) { // Withdraw sends a withdrawal request func (f *FTX) Withdraw(coin, address, tag, password, code string, size float64) (TransactionData, error) { req := make(map[string]interface{}) - req["coin"] = coin + req["coin"] = strings.ToUpper(coin) req["address"] = address req["size"] = size if code != "" { @@ -441,7 +472,8 @@ func (f *FTX) GetOpenOrders(marketName string) ([]OrderData, error) { resp := struct { Data []OrderData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getOpenOrders+params.Encode(), nil, &resp) + endpoint := common.EncodeURLValues(getOpenOrders, params) + return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, endpoint, nil, &resp) } // FetchOrderHistory gets order history @@ -455,7 +487,7 @@ func (f *FTX) FetchOrderHistory(marketName string, startTime, endTime time.Time, } if !startTime.IsZero() && !endTime.IsZero() { if startTime.After(endTime) { - return resp.Data, errors.New("startTime cannot be after endTime") + return resp.Data, errStartTimeCannotBeAfterEndTime } params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10)) params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10)) @@ -463,7 +495,8 @@ func (f *FTX) FetchOrderHistory(marketName string, startTime, endTime time.Time, if limit != "" { params.Set("limit", limit) } - return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getOrderHistory+params.Encode(), nil, &resp) + endpoint := common.EncodeURLValues(getOrderHistory, params) + return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, endpoint, nil, &resp) } // GetOpenTriggerOrders gets trigger orders that are currently open @@ -478,7 +511,8 @@ func (f *FTX) GetOpenTriggerOrders(marketName, orderType string) ([]TriggerOrder resp := struct { Data []TriggerOrderData `json:"result"` }{} - return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getOpenTriggerOrders+params.Encode(), nil, &resp) + endpoint := common.EncodeURLValues(getOpenTriggerOrders, params) + return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, endpoint, nil, &resp) } // GetTriggerOrderTriggers gets trigger orders that are currently open @@ -491,16 +525,13 @@ func (f *FTX) GetTriggerOrderTriggers(orderID string) ([]TriggerData, error) { // GetTriggerOrderHistory gets trigger orders that are currently open func (f *FTX) GetTriggerOrderHistory(marketName string, startTime, endTime time.Time, side, orderType, limit string) ([]TriggerOrderData, error) { - resp := struct { - Data []TriggerOrderData `json:"result"` - }{} params := url.Values{} if marketName != "" { params.Set("market", marketName) } if !startTime.IsZero() && !endTime.IsZero() { if startTime.After(endTime) { - return resp.Data, errors.New("startTime cannot be after endTime") + return nil, errStartTimeCannotBeAfterEndTime } params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10)) params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10)) @@ -514,7 +545,11 @@ func (f *FTX) GetTriggerOrderHistory(marketName string, startTime, endTime time. if limit != "" { params.Set("limit", limit) } - return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getTriggerOrderHistory+params.Encode(), nil, &resp) + resp := struct { + Data []TriggerOrderData `json:"result"` + }{} + endpoint := common.EncodeURLValues(getTriggerOrderHistory, params) + return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, endpoint, nil, &resp) } // Order places an order @@ -700,12 +735,13 @@ func (f *FTX) GetFills(market, limit string, startTime, endTime time.Time) ([]Fi } if !startTime.IsZero() && !endTime.IsZero() { if startTime.After(endTime) { - return resp.Data, errors.New("startTime cannot be after endTime") + return resp.Data, errStartTimeCannotBeAfterEndTime } params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10)) params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10)) } - return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getFills+params.Encode(), nil, &resp) + endpoint := common.EncodeURLValues(getFills, params) + return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, endpoint, nil, &resp) } // GetFundingPayments gets funding payments @@ -716,7 +752,7 @@ func (f *FTX) GetFundingPayments(startTime, endTime time.Time, future string) ([ params := url.Values{} if !startTime.IsZero() && !endTime.IsZero() { if startTime.After(endTime) { - return resp.Data, errors.New("startTime cannot be after endTime") + return resp.Data, errStartTimeCannotBeAfterEndTime } params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10)) params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10)) @@ -724,7 +760,8 @@ func (f *FTX) GetFundingPayments(startTime, endTime time.Time, future string) ([ if future != "" { params.Set("future", future) } - return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getFundingPayments+params.Encode(), nil, &resp) + endpoint := common.EncodeURLValues(getFundingPayments, params) + return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, endpoint, nil, &resp) } // ListLeveragedTokens lists leveraged tokens @@ -806,7 +843,7 @@ func (f *FTX) GetYourQuoteRequests() ([]PersonalQuotesData, error) { // CreateQuoteRequest sends a request to create a quote func (f *FTX) CreateQuoteRequest(underlying, optionType, side string, expiry int64, requestExpiry string, strike, size, limitPrice, counterParyID float64, hideLimitPrice bool) (CreateQuoteRequestData, error) { req := make(map[string]interface{}) - req["underlying"] = underlying + req["underlying"] = strings.ToUpper(underlying) req["type"] = optionType req["side"] = side req["strike"] = strike @@ -894,21 +931,22 @@ func (f *FTX) GetOptionsPositions() ([]OptionsPositionsData, error) { // GetPublicOptionsTrades gets options' trades from public func (f *FTX) GetPublicOptionsTrades(startTime, endTime time.Time, limit string) ([]OptionsTradesData, error) { - resp := struct { - Data []OptionsTradesData `json:"result"` - }{} - req := make(map[string]interface{}) + params := url.Values{} if !startTime.IsZero() && !endTime.IsZero() { - req["start_time"] = strconv.FormatInt(startTime.Unix(), 10) - req["end_time"] = strconv.FormatInt(endTime.Unix(), 10) if startTime.After(endTime) { - return resp.Data, errors.New("startTime cannot be after endTime") + return nil, errStartTimeCannotBeAfterEndTime } + params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10)) + params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10)) } if limit != "" { - req["limit"] = limit + params.Set("limit", limit) } - return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getPublicOptionsTrades, req, &resp) + resp := struct { + Data []OptionsTradesData `json:"result"` + }{} + endpoint := common.EncodeURLValues(getPublicOptionsTrades, params) + return resp.Data, f.SendHTTPRequest(exchange.RestSpot, endpoint, &resp) } // GetOptionsFills gets fills data for options @@ -921,7 +959,7 @@ func (f *FTX) GetOptionsFills(startTime, endTime time.Time, limit string) ([]Opt req["start_time"] = strconv.FormatInt(startTime.Unix(), 10) req["end_time"] = strconv.FormatInt(endTime.Unix(), 10) if startTime.After(endTime) { - return resp.Data, errors.New("startTime cannot be after endTime") + return resp.Data, errStartTimeCannotBeAfterEndTime } } if limit != "" { @@ -1047,8 +1085,8 @@ func (f *FTX) RequestForQuotes(base, quote string, amount float64) (RequestQuote Data RequestQuoteData `json:"result"` }{} req := make(map[string]interface{}) - req["fromCoin"] = base - req["toCoin"] = quote + req["fromCoin"] = strings.ToUpper(base) + req["toCoin"] = strings.ToUpper(quote) req["size"] = amount return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost, requestOTCQuote, req, &resp) } diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index 7c5d111905b..0f259bec4ba 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -4,7 +4,6 @@ import ( "log" "os" "reflect" - "strings" "testing" "time" @@ -26,8 +25,14 @@ const ( canManipulateRealOrders = false spotPair = "FTT/BTC" futuresPair = "DOGE-PERP" - testToken = "ADAMOON" - btcusd = "BTC/USD" + testLeverageToken = "ADAMOON" + + validFTTBTCStartTime = 1565445600 // Sat Aug 10 2019 14:00:00 GMT+0000 + validFTTBTCEndTime = 1565532000 // Sat Aug 10 2019 14:00:00 GMT+0000 + invalidFTTBTCStartTime = 1559881511 // Fri Jun 07 2019 04:25:11 GMT+0000 + invalidFTTBTCEndTime = 1559901511 // Fri Jun 07 2019 09:58:31 GMT+0000 + authStartTime = validFTTBTCStartTime // Adjust these to test auth requests + authEndTime = validFTTBTCEndTime ) var f FTX @@ -91,33 +96,70 @@ func TestGetOrderbook(t *testing.T) { func TestGetTrades(t *testing.T) { t.Parallel() - _, err := f.GetTrades(spotPair, 0, 0, 200) + // test empty market + _, err := f.GetTrades("", 0, 0, 200) + if err == nil { + t.Error("empty market should return an error") + } + _, err = f.GetTrades(spotPair, validFTTBTCEndTime, validFTTBTCStartTime, 5) + if err != errStartTimeCannotBeAfterEndTime { + t.Errorf("should have thrown errStartTimeCannotBeAfterEndTime, got %v", err) + } + // test optional params + var trades []TradeData + trades, err = f.GetTrades(spotPair, 0, 0, 0) if err != nil { t.Error(err) } - _, err = f.GetTrades(spotPair, 0, 0, 5) + if len(trades) != 20 { + t.Error("default limit should return 20 items") + } + trades, err = f.GetTrades(spotPair, validFTTBTCStartTime, validFTTBTCEndTime, 5) if err != nil { t.Error(err) } - _, err = f.GetTrades(spotPair, 1559901511, 1559881511, 5) - if err == nil { + if len(trades) != 5 { + t.Error("limit of 5 should return 5 items") + } + trades, err = f.GetTrades(spotPair, invalidFTTBTCStartTime, invalidFTTBTCEndTime, 5) + if err != nil { t.Error(err) } + if len(trades) != 0 { + t.Error("invalid time range should return 0 items") + } } func TestGetHistoricalData(t *testing.T) { t.Parallel() - _, err := f.GetHistoricalData(spotPair, "86400", "5", time.Time{}, time.Time{}) + // test empty market + _, err := f.GetHistoricalData("", "86400", "5", time.Time{}, time.Time{}) + if err == nil { + t.Error("empty market should return an error") + } + // test empty resolution + _, err = f.GetHistoricalData(spotPair, "", "5", time.Time{}, time.Time{}) + if err == nil { + t.Error("empty resolution should return an error") + } + _, err = f.GetHistoricalData(spotPair, "86400", "5", time.Unix(validFTTBTCEndTime, 0), time.Unix(validFTTBTCStartTime, 0)) + if err != errStartTimeCannotBeAfterEndTime { + t.Errorf("should have thrown errStartTimeCannotBeAfterEndTime, got %v", err) + } + var o []OHLCVData + o, err = f.GetHistoricalData(spotPair, "86400", "5", time.Time{}, time.Time{}) if err != nil { t.Error(err) } - _, err = f.GetHistoricalData(spotPair, "86400", "5", time.Unix(1559881511, 0), time.Unix(1559901511, 0)) + if len(o) != 5 { + t.Error("limit of 5 should return 5 items") + } + o, err = f.GetHistoricalData(spotPair, "86400", "5", time.Unix(invalidFTTBTCStartTime, 0), time.Unix(invalidFTTBTCEndTime, 0)) if err != nil { t.Error(err) } - _, err = f.GetHistoricalData(spotPair, "86400", "5", time.Unix(1559901511, 0), time.Unix(1559881511, 0)) - if err == nil { - t.Error(err) + if len(o) != 0 { + t.Error("invalid time range should return 0 items") } } @@ -147,7 +189,12 @@ func TestGetFutureStats(t *testing.T) { func TestGetFundingRates(t *testing.T) { t.Parallel() - _, err := f.GetFundingRates(time.Now().Add(-time.Hour), time.Now(), "BTC-PERP") + // optional params + _, err := f.GetFundingRates(time.Time{}, time.Time{}, "") + if err != nil { + t.Error(err) + } + _, err = f.GetFundingRates(time.Now().Add(-time.Hour), time.Now(), "BTC-PERP") if err != nil { t.Error(err) } @@ -243,9 +290,6 @@ func TestGetMarginLendingRates(t *testing.T) { func TestMarginDailyBorrowedAmounts(t *testing.T) { t.Parallel() - if !areTestAPIKeysSet() { - t.Skip() - } _, err := f.MarginDailyBorrowedAmounts() if err != nil { t.Error(err) @@ -312,8 +356,7 @@ func TestSubmitLendingOffer(t *testing.T) { if !areTestAPIKeysSet() || !canManipulateRealOrders { t.Skip() } - _, err := f.SubmitLendingOffer("btc", 0.1, 500) - if err != nil { + if err := f.SubmitLendingOffer("bTc", 0.1, 500); err != nil { t.Error(err) } } @@ -323,7 +366,7 @@ func TestFetchDepositAddress(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - _, err := f.FetchDepositAddress("TUSD") + _, err := f.FetchDepositAddress("tUsD") if err != nil { t.Error(err) } @@ -356,7 +399,7 @@ func TestWithdraw(t *testing.T) { if !areTestAPIKeysSet() || !canManipulateRealOrders { t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly") } - _, err := f.Withdraw("BTC", core.BitcoinDonationAddress, "", "", "957378", 0.0009) + _, err := f.Withdraw("BtC", core.BitcoinDonationAddress, "", "", "957378", 0.0009) if err != nil { t.Error(err) } @@ -367,7 +410,11 @@ func TestGetOpenOrders(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - _, err := f.GetOpenOrders(spotPair) + _, err := f.GetOpenOrders("") + if err != nil { + t.Error(err) + } + _, err = f.GetOpenOrders(spotPair) if err != nil { t.Error(err) } @@ -378,17 +425,17 @@ func TestFetchOrderHistory(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - _, err := f.FetchOrderHistory(spotPair, time.Time{}, time.Time{}, "2") + _, err := f.FetchOrderHistory("", time.Time{}, time.Time{}, "2") if err != nil { t.Error(err) } - _, err = f.FetchOrderHistory(spotPair, time.Unix(1559881511, 0), time.Unix(1559901511, 0), "2") + _, err = f.FetchOrderHistory(spotPair, time.Unix(authStartTime, 0), time.Unix(authEndTime, 0), "2") if err != nil { t.Error(err) } - _, err = f.FetchOrderHistory(spotPair, time.Unix(1559901511, 0), time.Unix(1559881511, 0), "2") - if err == nil { - t.Error(err) + _, err = f.FetchOrderHistory(spotPair, time.Unix(authEndTime, 0), time.Unix(authStartTime, 0), "2") + if err != errStartTimeCannotBeAfterEndTime { + t.Errorf("should have thrown errStartTimeCannotBeAfterEndTime, got %v", err) } } @@ -397,7 +444,12 @@ func TestGetOpenTriggerOrders(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - _, err := f.GetOpenTriggerOrders(spotPair, "") + // optional params + _, err := f.GetOpenTriggerOrders("", "") + if err != nil { + t.Error(err) + } + _, err = f.GetOpenTriggerOrders(spotPair, "") if err != nil { t.Error(err) } @@ -419,18 +471,22 @@ func TestGetTriggerOrderHistory(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - _, err := f.GetTriggerOrderHistory(spotPair, time.Time{}, time.Time{}, order.Buy.Lower(), "stop", "1") + _, err := f.GetTriggerOrderHistory("", time.Time{}, time.Time{}, "", "", "") if err != nil { t.Error(err) } - _, err = f.GetTriggerOrderHistory(spotPair, time.Unix(1559881511, 0), time.Unix(1559901511, 0), order.Buy.Lower(), "stop", "1") + _, err = f.GetTriggerOrderHistory(spotPair, time.Time{}, time.Time{}, order.Buy.Lower(), "stop", "1") if err != nil { t.Error(err) } - _, err = f.GetTriggerOrderHistory(spotPair, time.Unix(1559901511, 0), time.Unix(1559881511, 0), order.Buy.Lower(), "stop", "1") - if err == nil { + _, err = f.GetTriggerOrderHistory(spotPair, time.Unix(authStartTime, 0), time.Unix(authEndTime, 0), order.Buy.Lower(), "stop", "1") + if err != nil { t.Error(err) } + _, err = f.GetTriggerOrderHistory(spotPair, time.Unix(authEndTime, 0), time.Unix(authStartTime, 0), order.Buy.Lower(), "stop", "1") + if err != errStartTimeCannotBeAfterEndTime { + t.Errorf("should have thrown errStartTimeCannotBeAfterEndTime, got %v", err) + } } func TestOrder(t *testing.T) { @@ -546,18 +602,23 @@ func TestGetFills(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - _, err := f.GetFills(spotPair, "", time.Time{}, time.Time{}) + // optional params + _, err := f.GetFills("", "", time.Time{}, time.Time{}) if err != nil { t.Error(err) } - _, err = f.GetFills(spotPair, "", time.Unix(1559881511, 0), time.Unix(1559901511, 0)) + _, err = f.GetFills(spotPair, "", time.Time{}, time.Time{}) if err != nil { t.Error(err) } - _, err = f.GetFills(spotPair, "", time.Unix(1559901511, 0), time.Unix(1559881511, 0)) - if err == nil { + _, err = f.GetFills(spotPair, "", time.Unix(authStartTime, 0), time.Unix(authEndTime, 0)) + if err != nil { t.Error(err) } + _, err = f.GetFills(spotPair, "", time.Unix(authEndTime, 0), time.Unix(authStartTime, 0)) + if err != errStartTimeCannotBeAfterEndTime { + t.Errorf("should have thrown errStartTimeCannotBeAfterEndTime, got %v", err) + } } func TestGetFundingPayments(t *testing.T) { @@ -565,14 +626,19 @@ func TestGetFundingPayments(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - _, err := f.GetFundingPayments(time.Unix(1559881511, 0), time.Unix(1559901511, 0), "") + // optional params + _, err := f.GetFundingPayments(time.Time{}, time.Time{}, "") if err != nil { t.Error(err) } - _, err = f.GetFundingPayments(time.Unix(1559901511, 0), time.Unix(1559881511, 0), "") - if err == nil { + _, err = f.GetFundingPayments(time.Unix(authStartTime, 0), time.Unix(authEndTime, 0), futuresPair) + if err != nil { t.Error(err) } + _, err = f.GetFundingPayments(time.Unix(authEndTime, 0), time.Unix(authStartTime, 0), futuresPair) + if err != errStartTimeCannotBeAfterEndTime { + t.Errorf("should have thrown errStartTimeCannotBeAfterEndTime, got %v", err) + } } func TestListLeveragedTokens(t *testing.T) { @@ -624,7 +690,7 @@ func TestRequestLTCreation(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - _, err := f.RequestLTCreation(testToken, 1) + _, err := f.RequestLTCreation(testLeverageToken, 1) if err != nil { t.Error(err) } @@ -668,7 +734,7 @@ func TestCreateQuoteRequest(t *testing.T) { if !areTestAPIKeysSet() || !canManipulateRealOrders { t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly") } - _, err := f.CreateQuoteRequest(strings.ToUpper(currency.BTC.String()), "call", order.Buy.Lower(), 1593140400, "", 10, 10, 5, 0, false) + _, err := f.CreateQuoteRequest(currency.BTC.String(), "call", order.Buy.Lower(), 1593140400, "", 10, 10, 5, 0, false) if err != nil { t.Error(err) } @@ -764,20 +830,25 @@ func TestGetOptionsPositions(t *testing.T) { func TestGetPublicOptionsTrades(t *testing.T) { t.Parallel() - if !areTestAPIKeysSet() { - t.Skip() - } - _, err := f.GetPublicOptionsTrades(time.Time{}, time.Time{}, "5") + // test optional params + result, err := f.GetPublicOptionsTrades(time.Time{}, time.Time{}, "") if err != nil { t.Error(err) } - _, err = f.GetPublicOptionsTrades(time.Unix(1559881511, 0), time.Unix(1559901511, 0), "5") + if len(result) != 20 { + t.Error("default limit should have returned 20 items") + } + tmNow := time.Now() + result, err = f.GetPublicOptionsTrades(tmNow.AddDate(0, 0, -1), tmNow, "5") if err != nil { t.Error(err) } - _, err = f.GetPublicOptionsTrades(time.Unix(1559901511, 0), time.Unix(1559881511, 0), "5") - if err == nil { - t.Error(err) + if len(result) != 5 { + t.Error("limit of 5 should return 5 items") + } + _, err = f.GetPublicOptionsTrades(time.Unix(validFTTBTCEndTime, 0), time.Unix(validFTTBTCStartTime, 0), "5") + if err != errStartTimeCannotBeAfterEndTime { + t.Errorf("should have thrown errStartTimeCannotBeAfterEndTime, got %v", err) } } @@ -790,13 +861,13 @@ func TestGetOptionsFills(t *testing.T) { if err != nil { t.Error(err) } - _, err = f.GetOptionsFills(time.Unix(1559881511, 0), time.Unix(1559901511, 0), "5") + _, err = f.GetOptionsFills(time.Unix(authStartTime, 0), time.Unix(authEndTime, 0), "5") if err != nil { t.Error(err) } - _, err = f.GetOptionsFills(time.Unix(1559901511, 0), time.Unix(1559881511, 0), "5") - if err == nil { - t.Error(err) + _, err = f.GetOptionsFills(time.Unix(authEndTime, 0), time.Unix(authStartTime, 0), "5") + if err != errStartTimeCannotBeAfterEndTime { + t.Errorf("should have thrown errStartTimeCannotBeAfterEndTime, got %v", err) } } @@ -1223,7 +1294,7 @@ func TestGetOTCQuoteStatus(t *testing.T) { if !areTestAPIKeysSet() { t.Skip("API keys required but not set, skipping test") } - _, err := f.GetOTCQuoteStatus(btcusd, "1") + _, err := f.GetOTCQuoteStatus(spotPair, "1") if err != nil { t.Error(err) } @@ -1234,7 +1305,7 @@ func TestRequestForQuotes(t *testing.T) { if !areTestAPIKeysSet() || !canManipulateRealOrders { t.Skip("skipping test, either api keys or canManipulateRealOrders isnt set correctly") } - _, err := f.RequestForQuotes("BTC", "USD", 0.5) + _, err := f.RequestForQuotes("BtC", "UsD", 0.5) if err != nil { t.Error(err) } diff --git a/exchanges/ftx/ftx_types.go b/exchanges/ftx/ftx_types.go index 7f2da279f54..f577b933ab0 100644 --- a/exchanges/ftx/ftx_types.go +++ b/exchanges/ftx/ftx_types.go @@ -14,6 +14,12 @@ type MarginFundingData struct { Previous float64 `json:"previous"` } +// MarginDailyBorrowStats stores the daily borrowed amounts +type MarginDailyBorrowStats struct { + Coin string `json:"coin"` + Size float64 `json:"size"` +} + // MarginMarketInfo stores margin market info type MarginMarketInfo struct { Coin string `json:"coin"` @@ -319,13 +325,13 @@ type FillsData struct { Fee float64 `json:"fee"` FeeRate float64 `json:"feeRate"` Future string `json:"future"` - ID string `json:"id"` + ID int64 `json:"id"` Liquidity string `json:"liquidity"` Market string `json:"market"` BaseCurrency string `json:"baseCurrency"` QuoteCurrency string `json:"quoteCurrency"` - OrderID string `json:"orderID"` - TradeID string `json:"tradeID"` + OrderID int64 `json:"orderId"` + TradeID int64 `json:"tradeId"` Price float64 `json:"price"` Side string `json:"side"` Size float64 `json:"size"` @@ -336,7 +342,7 @@ type FillsData struct { // FundingPaymentsData stores funding payments' data type FundingPaymentsData struct { Future string `json:"future"` - ID string `json:"id"` + ID int64 `json:"id"` Payment float64 `json:"payment"` Time time.Time `json:"time"` Rate float64 `json:"rate"` @@ -617,8 +623,8 @@ type WsFills struct { ID int64 `json:"id"` Liquidity string `json:"liquidity"` Market string `json:"market"` - OrderID int64 `json:"int64"` - TradeID int64 `json:"tradeID"` + OrderID int64 `json:"orderId"` + TradeID int64 `json:"tradeId"` Price float64 `json:"price"` Side string `json:"side"` Size float64 `json:"size"`