Skip to content

Commit

Permalink
FTX: Various bug fixes (thrasher-corp#631)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
thrasher- authored Feb 15, 2021
1 parent 504c2fa commit e80091d
Show file tree
Hide file tree
Showing 3 changed files with 236 additions and 121 deletions.
160 changes: 99 additions & 61 deletions exchanges/ftx/ftx.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"

Expand All @@ -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"
Expand All @@ -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"
Expand All @@ -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"
Expand All @@ -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 {
Expand Down Expand Up @@ -164,41 +169,55 @@ 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 != "" {
params.Set("limit", limit)
}
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
Expand Down Expand Up @@ -233,15 +252,16 @@ 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))
}
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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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 != "" {
Expand All @@ -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
Expand All @@ -455,15 +487,16 @@ 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))
}
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
Expand All @@ -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
Expand All @@ -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))
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -716,15 +752,16 @@ 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))
}
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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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 != "" {
Expand Down Expand Up @@ -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)
}
Expand Down
Loading

0 comments on commit e80091d

Please sign in to comment.