mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-28 15:10:32 +00:00
exchanges: API coverage improvements and helper functions (#681)
* improved functions and new helper functions * bitfinex margin info func * small rate change * rate changes * adding some currencies for margin funding translation * adding index candles * added test * slight improvement in params * time func * orderbook helper avgprice func * broken test + removing some tlogs and prints * adding test cases * error fix * remove unused * another unused * shazbert changes * wip * bitfinex func and more nits * final shazzy nits * most shazzy nits * few prior requested changes * shazbert nits final WIP * shazbert changes * minor linter issue * unused val * glorious changes * more verbositiy improvements * quick changes * unused remaining amount oops * thrasher changes * reverting changes that were only for testing purposes and bymistake pushed up * bfx shadow dec + huobi fetch tradable pairs formatted so as to return config format for ease of comparison and requests * more linters * glorious final nits wip * formatting tradable pairs for different asset types + remove println * glorious changes
This commit is contained in:
@@ -1540,7 +1540,9 @@ var (
|
||||
PDX = NewCode("PDX")
|
||||
SLT = NewCode("SLT")
|
||||
HPY = NewCode("HPY")
|
||||
XXRP = NewCode("XXRP") // XRP
|
||||
XXBT = NewCode("XXBT") // BTC, but XXBT instead
|
||||
XXDG = NewCode("XXDG") // DOGE
|
||||
XDG = NewCode("XDG") // DOGE
|
||||
HKD = NewCode("HKD") // Hong Kong Dollar
|
||||
AUD = NewCode("AUD") // Australian Dollar
|
||||
|
||||
@@ -43,6 +43,14 @@ func setFeeBuilder() *exchange.FeeBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUServerTime(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.UServerTime()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateTicker(t *testing.T) {
|
||||
t.Parallel()
|
||||
spotPairs, err := b.FetchTradablePairs(asset.Spot)
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
const (
|
||||
|
||||
// Unauth
|
||||
ufuturesServerTime = "/fapi/v1/time"
|
||||
ufuturesExchangeInfo = "/fapi/v1/exchangeInfo?"
|
||||
ufuturesOrderbook = "/fapi/v1/depth?"
|
||||
ufuturesRecentTrades = "/fapi/v1/trades?"
|
||||
@@ -62,6 +63,18 @@ const (
|
||||
ufuturesADLQuantile = "/fapi/v1/adlQuantile"
|
||||
)
|
||||
|
||||
// UServerTime gets the server time
|
||||
func (b *Binance) UServerTime() (time.Time, error) {
|
||||
var data struct {
|
||||
ServerTime int64 `json:"serverTime"`
|
||||
}
|
||||
err := b.SendHTTPRequest(exchange.RestUSDTMargined, ufuturesServerTime, uFuturesDefaultRate, &data)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
return time.Unix(0, data.ServerTime*1000000), nil
|
||||
}
|
||||
|
||||
// UExchangeInfo stores usdt margined futures data
|
||||
func (b *Binance) UExchangeInfo() (UFuturesExchangeInfo, error) {
|
||||
var resp UFuturesExchangeInfo
|
||||
|
||||
@@ -345,6 +345,10 @@ func (b *Binance) FetchTradablePairs(a asset.Item) ([]string, error) {
|
||||
if !b.SupportsAsset(a) {
|
||||
return nil, fmt.Errorf("asset type of %s is not supported by %s", a, b.Name)
|
||||
}
|
||||
format, err := b.GetPairFormat(a, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pairs []string
|
||||
switch a {
|
||||
case asset.Spot, asset.Margin:
|
||||
@@ -352,10 +356,6 @@ func (b *Binance) FetchTradablePairs(a asset.Item) ([]string, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
format, err := b.GetPairFormat(a, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for x := range info.Symbols {
|
||||
if info.Symbols[x].Status == "TRADING" {
|
||||
pair := info.Symbols[x].BaseAsset +
|
||||
@@ -376,7 +376,11 @@ func (b *Binance) FetchTradablePairs(a asset.Item) ([]string, error) {
|
||||
}
|
||||
for z := range cInfo.Symbols {
|
||||
if cInfo.Symbols[z].ContractStatus == "TRADING" {
|
||||
pairs = append(pairs, cInfo.Symbols[z].Symbol)
|
||||
curr, err := currency.NewPairFromString(cInfo.Symbols[z].Symbol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pairs = append(pairs, format.Format(curr))
|
||||
}
|
||||
}
|
||||
case asset.USDTMarginedFutures:
|
||||
@@ -386,7 +390,11 @@ func (b *Binance) FetchTradablePairs(a asset.Item) ([]string, error) {
|
||||
}
|
||||
for u := range uInfo.Symbols {
|
||||
if uInfo.Symbols[u].Status == "TRADING" {
|
||||
pairs = append(pairs, uInfo.Symbols[u].Symbol)
|
||||
curr, err := currency.NewPairFromString(uInfo.Symbols[u].Symbol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pairs = append(pairs, format.Format(curr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,11 +13,6 @@ var (
|
||||
"ALL", "CURRENT_QUARTER", "NEXT_QUARTER",
|
||||
}
|
||||
|
||||
validOrderType = []string{
|
||||
"LIMIT", "MARKET", "STOP", "TAKE_PROFIT",
|
||||
"STOP_MARKET", "TAKE_PROFIT_MARKET", "TRAILING_STOP_MARKET",
|
||||
}
|
||||
|
||||
validNewOrderRespType = []string{"ACK", "RESULT"}
|
||||
|
||||
validWorkingType = []string{"MARK_PRICE", "CONTRACT_TYPE"}
|
||||
|
||||
@@ -66,6 +66,7 @@ const (
|
||||
bitfinexV2MarginFunding = "calc/trade/avg?"
|
||||
bitfinexV2Balances = "auth/r/wallets"
|
||||
bitfinexV2AccountInfo = "auth/r/info/user"
|
||||
bitfinexV2MarginInfo = "auth/r/info/margin/"
|
||||
bitfinexV2FundingInfo = "auth/r/info/funding/%s"
|
||||
bitfinexDerivativeData = "status/deriv?"
|
||||
bitfinexPlatformStatus = "platform/status"
|
||||
@@ -118,6 +119,141 @@ func (b *Bitfinex) GetPlatformStatus() (int, error) {
|
||||
return -1, fmt.Errorf("unexpected platform status value %d", response[0])
|
||||
}
|
||||
|
||||
func baseMarginInfo(data []interface{}) (MarginInfoV2, error) {
|
||||
var resp MarginInfoV2
|
||||
tempData, ok := data[1].([]interface{})
|
||||
if !ok {
|
||||
return resp, fmt.Errorf("%w", errTypeAssert)
|
||||
}
|
||||
resp.UserPNL, ok = tempData[0].(float64)
|
||||
if !ok {
|
||||
return resp, fmt.Errorf("%w for UserPNL", errTypeAssert)
|
||||
}
|
||||
resp.UserSwaps, ok = tempData[1].(float64)
|
||||
if !ok {
|
||||
return resp, fmt.Errorf("%w for UserSwaps", errTypeAssert)
|
||||
}
|
||||
resp.MarginBalance, ok = tempData[2].(float64)
|
||||
if !ok {
|
||||
return resp, fmt.Errorf("%w for MarginBalance", errTypeAssert)
|
||||
}
|
||||
resp.MarginNet, ok = tempData[3].(float64)
|
||||
if !ok {
|
||||
return resp, fmt.Errorf("%w for MarginNet", errTypeAssert)
|
||||
}
|
||||
resp.MarginMin, ok = tempData[4].(float64)
|
||||
if !ok {
|
||||
return resp, fmt.Errorf("%w for MarginMin", errTypeAssert)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func symbolMarginInfo(data []interface{}) ([]MarginInfoV2, error) {
|
||||
var resp []MarginInfoV2
|
||||
for x := range data {
|
||||
var tempResp MarginInfoV2
|
||||
tempData, ok := data[x].([]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w for all sym", errTypeAssert)
|
||||
}
|
||||
var check bool
|
||||
tempResp.Symbol, check = tempData[1].(string)
|
||||
if !check {
|
||||
return nil, fmt.Errorf("%w for symbol data", errTypeAssert)
|
||||
}
|
||||
tempFloatData, check := tempData[2].([]interface{})
|
||||
if !check {
|
||||
return nil, fmt.Errorf("%w for symbol data", errTypeAssert)
|
||||
}
|
||||
if len(tempFloatData) < 4 {
|
||||
return nil, errors.New("invalid data received")
|
||||
}
|
||||
tempResp.TradableBalance, ok = tempFloatData[0].(float64)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w for TradableBalance", errTypeAssert)
|
||||
}
|
||||
tempResp.GrossBalance, ok = tempFloatData[1].(float64)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w for GrossBalance", errTypeAssert)
|
||||
}
|
||||
tempResp.BestAskAmount, ok = tempFloatData[2].(float64)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w for BestAskAmount", errTypeAssert)
|
||||
}
|
||||
tempResp.BestBidAmount, ok = tempFloatData[3].(float64)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w for BestBidAmount", errTypeAssert)
|
||||
}
|
||||
resp = append(resp, tempResp)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func defaultMarginV2Info(data []interface{}) (MarginInfoV2, error) {
|
||||
var resp MarginInfoV2
|
||||
var ok bool
|
||||
resp.Symbol, ok = data[1].(string)
|
||||
if !ok {
|
||||
return resp, fmt.Errorf("%w for symbol", errTypeAssert)
|
||||
}
|
||||
tempData, check := data[2].([]interface{})
|
||||
if !check {
|
||||
return resp, fmt.Errorf("%w for symbol data", errTypeAssert)
|
||||
}
|
||||
if len(tempData) < 4 {
|
||||
return resp, errors.New("invalid data received")
|
||||
}
|
||||
resp.TradableBalance, ok = tempData[0].(float64)
|
||||
if !ok {
|
||||
return resp, fmt.Errorf("%w for TradableBalance", errTypeAssert)
|
||||
}
|
||||
resp.GrossBalance, ok = tempData[1].(float64)
|
||||
if !ok {
|
||||
return resp, fmt.Errorf("%w for GrossBalance", errTypeAssert)
|
||||
}
|
||||
resp.BestAskAmount, ok = tempData[2].(float64)
|
||||
if !ok {
|
||||
return resp, fmt.Errorf("%w for BestAskAmount", errTypeAssert)
|
||||
}
|
||||
resp.BestBidAmount, ok = tempData[3].(float64)
|
||||
if !ok {
|
||||
return resp, fmt.Errorf("%w for BestBidAmount", errTypeAssert)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetV2MarginInfo gets v2 margin info for a symbol provided
|
||||
// symbol: base, sym_all, any other trading symbol example tBTCUSD
|
||||
func (b *Bitfinex) GetV2MarginInfo(symbol string) ([]MarginInfoV2, error) {
|
||||
var data []interface{}
|
||||
err := b.SendAuthenticatedHTTPRequestV2(exchange.RestSpot, http.MethodPost,
|
||||
bitfinexV2MarginInfo+symbol,
|
||||
nil,
|
||||
&data,
|
||||
getMarginInfoRate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var tempResp MarginInfoV2
|
||||
switch symbol {
|
||||
case "base":
|
||||
tempResp, err = baseMarginInfo(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%v - %s: %w", b.Name, symbol, err)
|
||||
}
|
||||
case "sym_all":
|
||||
var resp []MarginInfoV2
|
||||
resp, err = symbolMarginInfo(data)
|
||||
return resp, err
|
||||
default:
|
||||
tempResp, err = defaultMarginV2Info(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%v - %s: %w", b.Name, symbol, err)
|
||||
}
|
||||
}
|
||||
return []MarginInfoV2{tempResp}, nil
|
||||
}
|
||||
|
||||
// GetV2MarginFunding gets borrowing rates for margin trading
|
||||
func (b *Bitfinex) GetV2MarginFunding(symbol, amount string, period int32) (MarginV2FundingData, error) {
|
||||
var resp []interface{}
|
||||
@@ -130,7 +266,7 @@ func (b *Bitfinex) GetV2MarginFunding(symbol, amount string, period int32) (Marg
|
||||
bitfinexV2MarginFunding,
|
||||
params,
|
||||
&resp,
|
||||
getAccountFees)
|
||||
getMarginInfoRate)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
@@ -139,11 +275,11 @@ func (b *Bitfinex) GetV2MarginFunding(symbol, amount string, period int32) (Marg
|
||||
}
|
||||
avgRate, ok := resp[0].(float64)
|
||||
if !ok {
|
||||
return response, errors.New("failed type assertion for rate")
|
||||
return response, fmt.Errorf("%v - %v: %w for rate", b.Name, symbol, errTypeAssert)
|
||||
}
|
||||
avgAmount, ok := resp[1].(float64)
|
||||
if !ok {
|
||||
return response, errors.New("failed type assertion for amount")
|
||||
return response, fmt.Errorf("%v - %v: %w for amount", b.Name, symbol, errTypeAssert)
|
||||
}
|
||||
response.Symbol = symbol
|
||||
response.RateAverage = avgRate
|
||||
@@ -168,20 +304,20 @@ func (b *Bitfinex) GetV2FundingInfo(key string) (MarginFundingDataV2, error) {
|
||||
}
|
||||
sym, ok := resp[0].(string)
|
||||
if !ok {
|
||||
return response, errors.New("failed type assertion for sym")
|
||||
return response, fmt.Errorf("%v GetV2FundingInfo: %w for sym", b.Name, errTypeAssert)
|
||||
}
|
||||
symbol, ok := resp[1].(string)
|
||||
if !ok {
|
||||
return response, errors.New("failed type assertion for symbol")
|
||||
return response, fmt.Errorf("%v GetV2FundingInfo: %w for symbol", b.Name, errTypeAssert)
|
||||
}
|
||||
fundingData, ok := resp[2].([]interface{})
|
||||
if !ok {
|
||||
return response, errors.New("failed type assertion for fundingData")
|
||||
return response, fmt.Errorf("%v GetV2FundingInfo: %w for fundingData", b.Name, errTypeAssert)
|
||||
}
|
||||
response.Sym = sym
|
||||
response.Symbol = symbol
|
||||
if len(fundingData) < 4 {
|
||||
return response, errors.New("invalid length of fundingData")
|
||||
return response, fmt.Errorf("%v GetV2FundingInfo: invalid length of fundingData", b.Name)
|
||||
}
|
||||
for x := 0; x < 3; x++ {
|
||||
_, ok := fundingData[x].(float64)
|
||||
@@ -209,33 +345,33 @@ func (b *Bitfinex) GetAccountInfoV2() (AccountV2Data, error) {
|
||||
return resp, err
|
||||
}
|
||||
if len(data) < 8 {
|
||||
return resp, errors.New("invalid length of data")
|
||||
return resp, fmt.Errorf("%v GetAccountInfoV2: invalid length of data", b.Name)
|
||||
}
|
||||
var ok bool
|
||||
var tempString string
|
||||
var tempFloat float64
|
||||
if tempFloat, ok = data[0].(float64); !ok {
|
||||
return resp, errors.New("type assertion failed for id, check for api updates")
|
||||
return resp, fmt.Errorf("%v GetAccountInfoV2: %w for id", b.Name, errTypeAssert)
|
||||
}
|
||||
resp.ID = int64(tempFloat)
|
||||
if tempString, ok = data[1].(string); !ok {
|
||||
return resp, errors.New("type assertion failed for email, check for api updates")
|
||||
return resp, fmt.Errorf("%v GetAccountInfoV2: %w for email", b.Name, errTypeAssert)
|
||||
}
|
||||
resp.Email = tempString
|
||||
if tempString, ok = data[2].(string); !ok {
|
||||
return resp, errors.New("type assertion failed for username, check for api updates")
|
||||
return resp, fmt.Errorf("%v GetAccountInfoV2: %w for username", b.Name, errTypeAssert)
|
||||
}
|
||||
resp.Username = tempString
|
||||
if tempFloat, ok = data[3].(float64); !ok {
|
||||
return resp, errors.New("type assertion failed for accountcreate, check for api updates")
|
||||
return resp, fmt.Errorf("%v GetAccountInfoV2: %w for accountcreate", b.Name, errTypeAssert)
|
||||
}
|
||||
resp.MTSAccountCreate = int64(tempFloat)
|
||||
if tempFloat, ok = data[4].(float64); !ok {
|
||||
return resp, errors.New("type assertion failed for verified, check for api updates")
|
||||
return resp, fmt.Errorf("%v GetAccountInfoV2: %w failed for verified", b.Name, errTypeAssert)
|
||||
}
|
||||
resp.Verified = int64(tempFloat)
|
||||
if tempString, ok = data[7].(string); !ok {
|
||||
return resp, errors.New("type assertion failed for timezone, check for api updates")
|
||||
return resp, fmt.Errorf("%v GetAccountInfoV2: %w for timezone", b.Name, errTypeAssert)
|
||||
}
|
||||
resp.Timezone = tempString
|
||||
return resp, nil
|
||||
@@ -256,19 +392,19 @@ func (b *Bitfinex) GetV2Balances() ([]WalletDataV2, error) {
|
||||
for x := range data {
|
||||
wType, ok := data[x][0].(string)
|
||||
if !ok {
|
||||
return resp, errors.New("type assertion failed for walletType, check for api updates")
|
||||
return resp, fmt.Errorf("%v GetV2Balances: %w for walletType", b.Name, errTypeAssert)
|
||||
}
|
||||
curr, ok := data[x][1].(string)
|
||||
if !ok {
|
||||
return resp, errors.New("type assertion failed for currency, check for api updates")
|
||||
return resp, fmt.Errorf("%v GetV2Balances: %w for currency", b.Name, errTypeAssert)
|
||||
}
|
||||
bal, ok := data[x][2].(float64)
|
||||
if !ok {
|
||||
return resp, errors.New("type assertion failed for balance, check for api updates")
|
||||
return resp, fmt.Errorf("%v GetV2Balances: %w for balance", b.Name, errTypeAssert)
|
||||
}
|
||||
unsettledInterest, ok := data[x][3].(float64)
|
||||
if !ok {
|
||||
return resp, errors.New("type assertion failed for unsettledInterest, check for api updates")
|
||||
return resp, fmt.Errorf("%v GetV2Balances: %w for unsettledInterest", b.Name, errTypeAssert)
|
||||
}
|
||||
resp = append(resp, WalletDataV2{
|
||||
WalletType: wType,
|
||||
@@ -294,10 +430,10 @@ func (b *Bitfinex) GetMarginPairs() ([]string, error) {
|
||||
return resp[0], nil
|
||||
}
|
||||
|
||||
// GetDerivativeData gets data for the queried derivative
|
||||
func (b *Bitfinex) GetDerivativeData(keys, startTime, endTime string, sort, limit int64) (DerivativeDataResponse, error) {
|
||||
var result [][19]interface{}
|
||||
var response DerivativeDataResponse
|
||||
// GetDerivativeStatusInfo gets status data for the queried derivative
|
||||
func (b *Bitfinex) GetDerivativeStatusInfo(keys, startTime, endTime string, sort, limit int64) ([]DerivativeDataResponse, error) {
|
||||
var result [][]interface{}
|
||||
var finalResp []DerivativeDataResponse
|
||||
|
||||
params := url.Values{}
|
||||
params.Set("keys", keys)
|
||||
@@ -317,62 +453,51 @@ func (b *Bitfinex) GetDerivativeData(keys, startTime, endTime string, sort, limi
|
||||
params.Encode()
|
||||
err := b.SendHTTPRequest(exchange.RestSpot, path, &result, status)
|
||||
if err != nil {
|
||||
return response, err
|
||||
return finalResp, err
|
||||
}
|
||||
if len(result) < 1 {
|
||||
return response, errors.New("invalid response, array length too small, check api docs for updates")
|
||||
for z := range result {
|
||||
if len(result[z]) < 19 {
|
||||
return finalResp, fmt.Errorf("%v GetDerivativeStatusInfo: invalid response, array length too small, check api docs for updates", b.Name)
|
||||
}
|
||||
var response DerivativeDataResponse
|
||||
var ok bool
|
||||
if response.Key, ok = result[z][0].(string); !ok {
|
||||
return finalResp, fmt.Errorf("%v GetDerivativeStatusInfo: %w for Key", b.Name, errTypeAssert)
|
||||
}
|
||||
if response.MTS, ok = result[z][1].(float64); !ok {
|
||||
return finalResp, fmt.Errorf("%v GetDerivativeStatusInfo: %w for MTS", b.Name, errTypeAssert)
|
||||
}
|
||||
if response.DerivPrice, ok = result[z][3].(float64); !ok {
|
||||
return finalResp, fmt.Errorf("%v GetDerivativeStatusInfo: %w for DerivPrice", b.Name, errTypeAssert)
|
||||
}
|
||||
if response.SpotPrice, ok = result[z][4].(float64); !ok {
|
||||
return finalResp, fmt.Errorf("%v GetDerivativeStatusInfo: %w for SpotPrice", b.Name, errTypeAssert)
|
||||
}
|
||||
if response.InsuranceFundBalance, ok = result[z][6].(float64); !ok {
|
||||
return finalResp, fmt.Errorf("%v GetDerivativeStatusInfo: %w for Insurance fund balance", b.Name, errTypeAssert)
|
||||
}
|
||||
if response.NextFundingEventTS, ok = result[z][8].(float64); !ok {
|
||||
return finalResp, fmt.Errorf("%v GetDerivativeStatusInfo: %w for NextFundingEventTS", b.Name, errTypeAssert)
|
||||
}
|
||||
if response.NextFundingAccured, ok = result[z][9].(float64); !ok {
|
||||
return finalResp, fmt.Errorf("%v GetDerivativeStatusInfo: %w for NextFundingAccrued", b.Name, errTypeAssert)
|
||||
}
|
||||
if response.NextFundingStep, ok = result[z][10].(float64); !ok {
|
||||
return finalResp, fmt.Errorf("%v GetDerivativeStatusInfo: %w for NextFundingStep", b.Name, errTypeAssert)
|
||||
}
|
||||
if response.CurrentFunding, ok = result[z][12].(float64); !ok {
|
||||
return finalResp, fmt.Errorf("%v GetDerivativeStatusInfo: %w for CurrentFunding", b.Name, errTypeAssert)
|
||||
}
|
||||
if response.MarkPrice, ok = result[z][15].(float64); !ok {
|
||||
return finalResp, fmt.Errorf("%v GetDerivativeStatusInfo: %w for MarkPrice", b.Name, errTypeAssert)
|
||||
}
|
||||
|
||||
if response.OpenInterest, ok = result[z][18].(float64); !ok {
|
||||
return finalResp, fmt.Errorf("%v GetDerivativeStatusInfo: %w for OpenInterest", b.Name, errTypeAssert)
|
||||
}
|
||||
finalResp = append(finalResp, response)
|
||||
}
|
||||
if len(result[0]) < 19 {
|
||||
return response, errors.New("invalid response, array length too small, check api docs for updates")
|
||||
}
|
||||
var floatData float64
|
||||
var stringData string
|
||||
var ok bool
|
||||
if stringData, ok = result[0][0].(string); !ok {
|
||||
return response, errors.New("type assertion failed, check for api updates")
|
||||
}
|
||||
response.Key = stringData
|
||||
if floatData, ok = result[0][1].(float64); !ok {
|
||||
return response, errors.New("type assertion failed, check for api updates")
|
||||
}
|
||||
response.MTS = floatData
|
||||
if floatData, ok = result[0][3].(float64); !ok {
|
||||
return response, errors.New("type assertion failed, check for api updates")
|
||||
}
|
||||
response.DerivPrice = floatData
|
||||
if floatData, ok = result[0][4].(float64); !ok {
|
||||
return response, errors.New("type assertion failed, check for api updates")
|
||||
}
|
||||
response.SpotPrice = floatData
|
||||
if floatData, ok = result[0][6].(float64); !ok {
|
||||
return response, errors.New("type assertion failed, check for api updates")
|
||||
}
|
||||
response.InsuranceFundBalance = floatData
|
||||
if floatData, ok = result[0][8].(float64); !ok {
|
||||
return response, errors.New("type assertion failed, check for api updates")
|
||||
}
|
||||
response.NextFundingEventTS = floatData
|
||||
if floatData, ok = result[0][9].(float64); !ok {
|
||||
return response, errors.New("type assertion failed, check for api updates")
|
||||
}
|
||||
response.NextFundingAccured = floatData
|
||||
if floatData, ok = result[0][10].(float64); !ok {
|
||||
return response, errors.New("type assertion failed, check for api updates")
|
||||
}
|
||||
response.NextFundingStep = floatData
|
||||
if floatData, ok = result[0][12].(float64); !ok {
|
||||
return response, errors.New("type assertion failed, check for api updates")
|
||||
}
|
||||
response.CurrentFunding = floatData
|
||||
if floatData, ok = result[0][15].(float64); !ok {
|
||||
return response, errors.New("type assertion failed, check for api updates")
|
||||
}
|
||||
response.MarkPrice = floatData
|
||||
if floatData, ok = result[0][18].(float64); !ok {
|
||||
return response, errors.New("type assertion failed, check for api updates")
|
||||
}
|
||||
response.OpenInterest = floatData
|
||||
return response, nil
|
||||
return finalResp, nil
|
||||
}
|
||||
|
||||
// GetTickerBatch returns all supported ticker information
|
||||
@@ -545,7 +670,6 @@ func (b *Bitfinex) GetOrderbook(symbol, precision string, limit int64) (Orderboo
|
||||
u.Set("len", strconv.FormatInt(limit, 10))
|
||||
}
|
||||
path := bitfinexAPIVersion2 + bitfinexOrderbook + symbol + "/" + precision + "?" + u.Encode()
|
||||
|
||||
var response [][]interface{}
|
||||
err := b.SendHTTPRequest(exchange.RestSpot, path, &response, orderbookFunction)
|
||||
if err != nil {
|
||||
|
||||
@@ -63,7 +63,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
func TestGetV2MarginFunding(t *testing.T) {
|
||||
if !areTestAPIKeysSet() {
|
||||
t.SkipNow()
|
||||
t.Skip("api keys are not set or invalid")
|
||||
}
|
||||
_, err := b.GetV2MarginFunding("fUSD", "2", 2)
|
||||
if err != nil {
|
||||
@@ -71,10 +71,28 @@ func TestGetV2MarginFunding(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetV2MarginInfo(t *testing.T) {
|
||||
if !areTestAPIKeysSet() {
|
||||
t.Skip("api keys are not set or invalid")
|
||||
}
|
||||
_, err := b.GetV2MarginInfo("base")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
_, err = b.GetV2MarginInfo("tBTCUSD")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
_, err = b.GetV2MarginInfo("sym_all")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAccountInfoV2(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() {
|
||||
t.SkipNow()
|
||||
t.Skip("api keys are not set or invalid")
|
||||
}
|
||||
_, err := b.GetAccountInfoV2()
|
||||
if err != nil {
|
||||
@@ -84,9 +102,9 @@ func TestGetAccountInfoV2(t *testing.T) {
|
||||
|
||||
func TestGetV2FundingInfo(t *testing.T) {
|
||||
if !areTestAPIKeysSet() {
|
||||
t.SkipNow()
|
||||
t.Skip("api keys are not set or invalid")
|
||||
}
|
||||
_, err := b.GetV2FundingInfo("fUSD")
|
||||
_, err := b.GetV2FundingInfo("fUST")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -95,7 +113,7 @@ func TestGetV2FundingInfo(t *testing.T) {
|
||||
func TestGetV2Balances(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() {
|
||||
t.SkipNow()
|
||||
t.Skip("api keys are not set or invalid")
|
||||
}
|
||||
_, err := b.GetV2Balances()
|
||||
if err != nil {
|
||||
@@ -103,9 +121,9 @@ func TestGetV2Balances(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDerivativeData(t *testing.T) {
|
||||
func TestGetDerivativeStatusInfo(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetDerivativeData("tBTCF0:USTF0", "", "", 0, 0)
|
||||
_, err := b.GetDerivativeStatusInfo("ALL", "", "", 0, 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -205,6 +223,11 @@ func TestGetOrderbook(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = b.GetOrderbook("tLINK:UST", "P0", 1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetStats(t *testing.T) {
|
||||
@@ -1363,7 +1386,7 @@ func TestFixCasing(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ret != "fBTCUSD" {
|
||||
if ret != "tBTC:USD" {
|
||||
t.Errorf("unexpected result: %v", ret)
|
||||
}
|
||||
pair, err = currency.NewPairFromString("BTCUSD")
|
||||
@@ -1415,7 +1438,7 @@ func TestFixCasing(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ret, err = b.fixCasing(pair, asset.Margin)
|
||||
ret, err = b.fixCasing(pair, asset.MarginFunding)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -1426,7 +1449,7 @@ func TestFixCasing(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ret, err = b.fixCasing(pair, asset.Margin)
|
||||
ret, err = b.fixCasing(pair, asset.MarginFunding)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -1438,7 +1461,7 @@ func TestFixCasing(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ret, err = b.fixCasing(pair, asset.Margin)
|
||||
ret, err = b.fixCasing(pair, asset.MarginFunding)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package bitfinex
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
)
|
||||
|
||||
var errTypeAssert = errors.New("type assertion failed")
|
||||
|
||||
// AccountV2Data stores account v2 data
|
||||
type AccountV2Data struct {
|
||||
ID int64
|
||||
@@ -16,6 +19,20 @@ type AccountV2Data struct {
|
||||
Timezone string
|
||||
}
|
||||
|
||||
// MarginInfoV2 stores V2 margin data
|
||||
type MarginInfoV2 struct {
|
||||
Symbol string
|
||||
UserPNL float64
|
||||
UserSwaps float64
|
||||
MarginBalance float64
|
||||
MarginNet float64
|
||||
MarginMin float64
|
||||
TradableBalance float64
|
||||
GrossBalance float64
|
||||
BestAskAmount float64
|
||||
BestBidAmount float64
|
||||
}
|
||||
|
||||
// WalletDataV2 stores wallet data for v2
|
||||
type WalletDataV2 struct {
|
||||
WalletType string
|
||||
|
||||
@@ -66,7 +66,7 @@ func (b *Bitfinex) SetDefaults() {
|
||||
}
|
||||
|
||||
fmt2 := currency.PairStore{
|
||||
RequestFormat: ¤cy.PairFormat{Uppercase: true},
|
||||
RequestFormat: ¤cy.PairFormat{Uppercase: true, Delimiter: ":"},
|
||||
ConfigFormat: ¤cy.PairFormat{Uppercase: true, Delimiter: ":"},
|
||||
}
|
||||
|
||||
@@ -1063,10 +1063,10 @@ func (b *Bitfinex) GetHistoricCandlesExtended(pair currency.Pair, a asset.Item,
|
||||
|
||||
func (b *Bitfinex) fixCasing(in currency.Pair, a asset.Item) (string, error) {
|
||||
var checkString [2]byte
|
||||
if a == asset.Spot {
|
||||
if a == asset.Spot || a == asset.Margin {
|
||||
checkString[0] = 't'
|
||||
checkString[1] = 'T'
|
||||
} else if a == asset.Margin {
|
||||
} else if a == asset.MarginFunding {
|
||||
checkString[0] = 'f'
|
||||
checkString[1] = 'F'
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ const (
|
||||
getPositionAuditReqRate = 45
|
||||
updateCollateralOnPositionReqRate = 45 // This is not specified just inputed above
|
||||
// Margin funding -
|
||||
getMarginInfoRate = 90
|
||||
getActiveFundingOffersReqRate = 45
|
||||
submitFundingOfferReqRate = 45 // This is not specified just inputed above
|
||||
cancelFundingOfferReqRate = 45
|
||||
|
||||
@@ -41,6 +41,7 @@ const (
|
||||
getFundingRates = "/funding_rates"
|
||||
getIndexWeights = "/indexes/%s/weights"
|
||||
getAllWalletBalances = "/wallet/all_balances"
|
||||
getIndexCandles = "/indexes/%s/candles"
|
||||
|
||||
// Authenticated endpoints
|
||||
getAccountInfo = "/account"
|
||||
@@ -133,8 +134,45 @@ var (
|
||||
errCoinMustBeSpecified = errors.New("a coin must be specified")
|
||||
errSubaccountTransferSizeGreaterThanZero = errors.New("transfer size must be greater than 0")
|
||||
errSubaccountTransferSourceDestinationMustNotBeEqual = errors.New("subaccount transfer source and destination must not be the same value")
|
||||
|
||||
validResolutionData = []int64{15, 60, 300, 900, 3600, 14400, 86400}
|
||||
)
|
||||
|
||||
// GetHistoricalIndex gets historical index data
|
||||
func (f *FTX) GetHistoricalIndex(indexName string, resolution int64, startTime, endTime time.Time) ([]OHLCVData, error) {
|
||||
params := url.Values{}
|
||||
if indexName == "" {
|
||||
return nil, errors.New("indexName is a mandatory field")
|
||||
}
|
||||
params.Set("index_name", indexName)
|
||||
err := checkResolution(resolution)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params.Set("resolution", strconv.FormatInt(resolution, 10))
|
||||
if !startTime.IsZero() && !endTime.IsZero() {
|
||||
if startTime.After(endTime) {
|
||||
return nil, errStartTimeCannotBeAfterEndTime
|
||||
}
|
||||
params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10))
|
||||
params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10))
|
||||
}
|
||||
resp := struct {
|
||||
Data []OHLCVData `json:"result"`
|
||||
}{}
|
||||
endpoint := common.EncodeURLValues(fmt.Sprintf(getIndexCandles, indexName), params)
|
||||
return resp.Data, f.SendHTTPRequest(exchange.RestSpot, endpoint, &resp)
|
||||
}
|
||||
|
||||
func checkResolution(res int64) error {
|
||||
for x := range validResolutionData {
|
||||
if validResolutionData[x] == res {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("resolution data is a mandatory field and the data provided is invalid")
|
||||
}
|
||||
|
||||
// GetMarkets gets market data
|
||||
func (f *FTX) GetMarkets() ([]MarketData, error) {
|
||||
resp := struct {
|
||||
@@ -210,19 +248,20 @@ func (f *FTX) GetTrades(marketName string, startTime, endTime, limit int64) ([]T
|
||||
}
|
||||
|
||||
// GetHistoricalData gets historical OHLCV data for a given market pair
|
||||
func (f *FTX) GetHistoricalData(marketName, timeInterval, limit string, startTime, endTime time.Time) ([]OHLCVData, error) {
|
||||
func (f *FTX) GetHistoricalData(marketName string, timeInterval, limit int64, startTime, endTime time.Time) ([]OHLCVData, error) {
|
||||
if marketName == "" {
|
||||
return nil, errors.New("a market pair must be specified")
|
||||
}
|
||||
|
||||
if timeInterval == "" {
|
||||
return nil, errors.New("a time interval must be specified")
|
||||
err := checkResolution(timeInterval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params := url.Values{}
|
||||
params.Set("resolution", timeInterval)
|
||||
if limit != "" {
|
||||
params.Set("limit", limit)
|
||||
params.Set("resolution", strconv.FormatInt(timeInterval, 10))
|
||||
if limit != 0 {
|
||||
params.Set("limit", strconv.FormatInt(limit, 10))
|
||||
}
|
||||
if !startTime.IsZero() && !endTime.IsZero() {
|
||||
if startTime.After(endTime) {
|
||||
|
||||
@@ -81,6 +81,18 @@ func TestGetMarkets(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetHistoricalIndex(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := f.GetHistoricalIndex("BTC", 3600, time.Now().Add(-time.Hour*2), time.Now().Add(-time.Hour*1))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
_, err = f.GetHistoricalIndex("BTC", 3600, time.Time{}, time.Time{})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMarket(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := f.GetMarket(spotPair)
|
||||
@@ -136,28 +148,28 @@ func TestGetTrades(t *testing.T) {
|
||||
func TestGetHistoricalData(t *testing.T) {
|
||||
t.Parallel()
|
||||
// test empty market
|
||||
_, err := f.GetHistoricalData("", "86400", "5", time.Time{}, time.Time{})
|
||||
_, 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{})
|
||||
_, err = f.GetHistoricalData(spotPair, 0, 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))
|
||||
_, 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{})
|
||||
o, err = f.GetHistoricalData(spotPair, 86400, 5, time.Time{}, time.Time{})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
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))
|
||||
o, err = f.GetHistoricalData(spotPair, 86400, 5, time.Unix(invalidFTTBTCStartTime, 0), time.Unix(invalidFTTBTCEndTime, 0))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
@@ -239,18 +239,30 @@ func (f *FTX) FetchTradablePairs(a asset.Item) ([]string, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
format, err := f.GetPairFormat(a, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pairs []string
|
||||
switch a {
|
||||
case asset.Spot:
|
||||
for x := range markets {
|
||||
if markets[x].MarketType == spotString {
|
||||
pairs = append(pairs, markets[x].Name)
|
||||
curr, err := currency.NewPairFromString(markets[x].Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pairs = append(pairs, format.Format(curr))
|
||||
}
|
||||
}
|
||||
case asset.Futures:
|
||||
for x := range markets {
|
||||
if markets[x].MarketType == futuresString {
|
||||
pairs = append(pairs, markets[x].Name)
|
||||
curr, err := currency.NewPairFromString(markets[x].Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pairs = append(pairs, format.Format(curr))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1021,8 +1033,8 @@ func (f *FTX) GetHistoricCandles(p currency.Pair, a asset.Item, start, end time.
|
||||
}
|
||||
|
||||
ohlcData, err := f.GetHistoricalData(formattedPair.String(),
|
||||
f.FormatExchangeKlineInterval(interval),
|
||||
strconv.FormatInt(int64(f.Features.Enabled.Kline.ResultLimit), 10),
|
||||
int64(interval.Duration().Seconds()),
|
||||
int64(f.Features.Enabled.Kline.ResultLimit),
|
||||
start, end)
|
||||
if err != nil {
|
||||
return kline.Item{}, err
|
||||
@@ -1071,8 +1083,8 @@ func (f *FTX) GetHistoricCandlesExtended(p currency.Pair, a asset.Item, start, e
|
||||
for x := range dates.Ranges {
|
||||
var ohlcData []OHLCVData
|
||||
ohlcData, err = f.GetHistoricalData(formattedPair.String(),
|
||||
f.FormatExchangeKlineInterval(interval),
|
||||
strconv.FormatInt(int64(f.Features.Enabled.Kline.ResultLimit), 10),
|
||||
int64(interval.Duration().Seconds()),
|
||||
int64(f.Features.Enabled.Kline.ResultLimit),
|
||||
dates.Ranges[x].Start.Time, dates.Ranges[x].End.Time)
|
||||
if err != nil {
|
||||
return kline.Item{}, err
|
||||
|
||||
@@ -698,7 +698,7 @@ func TestUpdateOrderbook(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
cp1, err := currency.NewPairFromString("BTC-USD")
|
||||
cp1, err := currency.NewPairFromString("BTC_USD")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -721,6 +721,21 @@ func TestUpdateOrderbook(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
tradablePairs, err = h.FetchTradablePairs(asset.CoinMarginedFutures)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if len(tradablePairs) == 0 {
|
||||
t.Fatal("no tradable pairs")
|
||||
}
|
||||
cp2, err = currency.NewPairFromString(tradablePairs[0])
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
_, err = h.UpdateOrderbook(cp2, asset.Futures)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateAccountInfo(t *testing.T) {
|
||||
|
||||
@@ -336,6 +336,11 @@ func (h *HUOBI) FetchTradablePairs(a asset.Item) ([]string, error) {
|
||||
|
||||
var pairs []string
|
||||
|
||||
format, err := h.GetPairFormat(a, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch a {
|
||||
case asset.Spot:
|
||||
symbols, err := h.GetSymbols()
|
||||
@@ -343,11 +348,6 @@ func (h *HUOBI) FetchTradablePairs(a asset.Item) ([]string, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
format, err := h.GetPairFormat(a, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for x := range symbols {
|
||||
if symbols[x].State != "online" {
|
||||
continue
|
||||
@@ -365,10 +365,13 @@ func (h *HUOBI) FetchTradablePairs(a asset.Item) ([]string, error) {
|
||||
|
||||
for z := range symbols {
|
||||
if symbols[z].ContractStatus == 1 {
|
||||
pairs = append(pairs, symbols[z].ContractCode)
|
||||
curr, err := currency.NewPairFromString(symbols[z].ContractCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pairs = append(pairs, format.Format(curr))
|
||||
}
|
||||
}
|
||||
|
||||
case asset.Futures:
|
||||
symbols, err := h.FGetContractInfo("", "", currency.Pair{})
|
||||
if err != nil {
|
||||
@@ -377,7 +380,11 @@ func (h *HUOBI) FetchTradablePairs(a asset.Item) ([]string, error) {
|
||||
|
||||
for c := range symbols.Data {
|
||||
if symbols.Data[c].ContractStatus == 1 {
|
||||
pairs = append(pairs, symbols.Data[c].ContractCode)
|
||||
curr, err := currency.NewPairFromString(symbols.Data[c].ContractCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pairs = append(pairs, format.Format(curr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,7 +330,10 @@ func (k *Kraken) Run() {
|
||||
// FetchTradablePairs returns a list of the exchanges tradable pairs
|
||||
func (k *Kraken) FetchTradablePairs(assetType asset.Item) ([]string, error) {
|
||||
var products []string
|
||||
|
||||
format, err := k.GetPairFormat(assetType, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch assetType {
|
||||
case asset.Spot:
|
||||
if !assetTranslator.Seeded() {
|
||||
@@ -342,10 +345,6 @@ func (k *Kraken) FetchTradablePairs(assetType asset.Item) ([]string, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
format, err := k.GetPairFormat(assetType, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range pairs {
|
||||
if strings.Contains(pairs[i].Altname, ".d") {
|
||||
continue
|
||||
@@ -375,7 +374,11 @@ func (k *Kraken) FetchTradablePairs(assetType asset.Item) ([]string, error) {
|
||||
}
|
||||
for x := range pairs.Instruments {
|
||||
if pairs.Instruments[x].Tradable {
|
||||
products = append(products, pairs.Instruments[x].Symbol)
|
||||
curr, err := currency.NewPairFromString(pairs.Instruments[x].Symbol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
products = append(products, format.Format(curr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ const (
|
||||
okGroupPerpTickers = "instruments/ticker"
|
||||
okGroupMarginPairData = "accounts/%s/availability"
|
||||
okGroupMarginPairsData = "accounts/availability"
|
||||
okGroupSpotPairs = "instruments"
|
||||
okGroupInstruments = "instruments"
|
||||
)
|
||||
|
||||
// OKEX bases all account, spot and margin methods off okgroup implementation
|
||||
@@ -68,6 +68,14 @@ func (o *OKEX) GetSwapMarkets() ([]okgroup.SwapInstrumentsData, error) {
|
||||
nil, &resp, false)
|
||||
}
|
||||
|
||||
// GetSwapInstruments gets perpetual swap instruments data
|
||||
func (o *OKEX) GetSwapInstruments() ([]okgroup.PerpSwapInstrumentData, error) {
|
||||
var resp []okgroup.PerpSwapInstrumentData
|
||||
return resp, o.SendHTTPRequest(exchange.RestSpot, http.MethodGet, okGroupSwapSubsection,
|
||||
okGroupInstruments,
|
||||
nil, &resp, false)
|
||||
}
|
||||
|
||||
// GetAllMarginRates gets interest rates for all margin currencies on OKEX
|
||||
func (o *OKEX) GetAllMarginRates() ([]okgroup.MarginCurrencyData, error) {
|
||||
var resp []okgroup.MarginCurrencyData
|
||||
@@ -172,7 +180,7 @@ func (o *OKEX) GetMarginRates(instrumentID currency.Pair) (okgroup.MarginCurrenc
|
||||
// GetSpotMarkets gets perpetual swap markets' data
|
||||
func (o *OKEX) GetSpotMarkets() ([]okgroup.TradingPairData, error) {
|
||||
var resp []okgroup.TradingPairData
|
||||
return resp, o.SendHTTPRequest(exchange.RestSpot, http.MethodGet, okGroupSpotSubsection, okGroupSpotPairs, nil, &resp, false)
|
||||
return resp, o.SendHTTPRequest(exchange.RestSpot, http.MethodGet, okGroupSpotSubsection, okGroupInstruments, nil, &resp, false)
|
||||
}
|
||||
|
||||
// GetFundingRate gets funding rate of a given currency
|
||||
|
||||
@@ -154,6 +154,14 @@ func TestGetSpotMarkets(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSwapInstruments(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := o.GetSwapInstruments()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSwapMarkets(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := o.GetSwapMarkets()
|
||||
|
||||
@@ -15,6 +15,25 @@ const (
|
||||
ImmediateOrCancelOrder
|
||||
)
|
||||
|
||||
// PerpSwapInstrumentData stores instrument data for perpetual swap contracts
|
||||
type PerpSwapInstrumentData struct {
|
||||
InstrumentID string `json:"instrument_id"`
|
||||
UnderlyingIndex string `json:"underlying_index"`
|
||||
QuoteCurrency string `json:"quote_currency"`
|
||||
Coin string `json:"coin"`
|
||||
ContractValue float64 `json:"contract_val,string"`
|
||||
Listing string `json:"listing"`
|
||||
Delivery string `json:"delivery"`
|
||||
SizeIncrement float64 `json:"size_increment,string"`
|
||||
TickSize float64 `json:"tick_size,string"`
|
||||
BaseCurrency string `json:"base_currency"`
|
||||
Underlying string `json:"underlying"`
|
||||
SettlementCurrency string `json:"settlement_currency"`
|
||||
IsInverse bool `json:"is_inverse,string"`
|
||||
Category float64 `json:"category,string"`
|
||||
ContractValCurrency string `json:"contract_val_currency"`
|
||||
}
|
||||
|
||||
// TradingPairData stores data about a trading pair
|
||||
type TradingPairData struct {
|
||||
BaseCurrency string `json:"base_currency"`
|
||||
|
||||
@@ -224,3 +224,40 @@ func (b *Base) sell(amount float64) (orders orderSummary, quoteAmount float64) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetAveragePrice finds the average buy or sell price of a specified amount.
|
||||
// It finds the nominal amount spent on the total purchase or sell and uses it
|
||||
// to find the average price for an individual unit bought or sold
|
||||
func (b *Base) GetAveragePrice(buy bool, amount float64) (float64, error) {
|
||||
if amount <= 0 {
|
||||
return 0, errAmountInvalid
|
||||
}
|
||||
var aggNominalAmount, remainingAmount float64
|
||||
if buy {
|
||||
aggNominalAmount, remainingAmount = b.Asks.FindNominalAmount(amount)
|
||||
} else {
|
||||
aggNominalAmount, remainingAmount = b.Bids.FindNominalAmount(amount)
|
||||
}
|
||||
if remainingAmount != 0 {
|
||||
return 0, fmt.Errorf("%w for %v on exchange %v to support a buy amount of %v", errNotEnoughLiquidity, b.Pair, b.Exchange, amount)
|
||||
}
|
||||
return aggNominalAmount / amount, nil
|
||||
}
|
||||
|
||||
// FindNominalAmount finds the nominal amount spent in terms of the quote
|
||||
// If the orderbook doesn't have enough liquidity it returns a non zero
|
||||
// remaining amount value
|
||||
func (elem Items) FindNominalAmount(amount float64) (aggNominalAmount, remainingAmount float64) {
|
||||
remainingAmount = amount
|
||||
for x := range elem {
|
||||
if remainingAmount <= elem[x].Amount {
|
||||
aggNominalAmount += elem[x].Price * remainingAmount
|
||||
remainingAmount = 0
|
||||
break
|
||||
} else {
|
||||
aggNominalAmount += elem[x].Price * elem[x].Amount
|
||||
remainingAmount -= elem[x].Amount
|
||||
}
|
||||
}
|
||||
return aggNominalAmount, remainingAmount
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package orderbook
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
@@ -90,3 +91,66 @@ func TestOrderSummary(t *testing.T) {
|
||||
|
||||
o.Print()
|
||||
}
|
||||
|
||||
func TestGetAveragePrice(t *testing.T) {
|
||||
var b Base
|
||||
b.Exchange = "Binance"
|
||||
cp, err := currency.NewPairFromString("ETH-USDT")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
b.Pair = cp
|
||||
b.Bids = []Item{}
|
||||
_, err = b.GetAveragePrice(false, 5)
|
||||
if errors.Is(errNotEnoughLiquidity, err) {
|
||||
t.Error("expected: %w, received %w", errNotEnoughLiquidity, err)
|
||||
}
|
||||
b = Base{}
|
||||
b.Pair = cp
|
||||
b.Asks = []Item{
|
||||
{Amount: 5, Price: 1},
|
||||
{Amount: 5, Price: 2},
|
||||
{Amount: 5, Price: 3},
|
||||
{Amount: 5, Price: 4},
|
||||
}
|
||||
_, err = b.GetAveragePrice(true, -2)
|
||||
if !errors.Is(err, errAmountInvalid) {
|
||||
t.Errorf("expected: %v, received %v", errAmountInvalid, err)
|
||||
}
|
||||
avgPrice, err := b.GetAveragePrice(true, 15)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if avgPrice != 2 {
|
||||
t.Errorf("avg price calculation failed: expected 2, received %f", avgPrice)
|
||||
}
|
||||
avgPrice, err = b.GetAveragePrice(true, 18)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if math.Round(avgPrice*1000)/1000 != 2.333 {
|
||||
t.Errorf("avg price calculation failed: expected 2.333, received %f", math.Round(avgPrice*1000)/1000)
|
||||
}
|
||||
_, err = b.GetAveragePrice(true, 25)
|
||||
if !errors.Is(err, errNotEnoughLiquidity) {
|
||||
t.Errorf("expected: %v, received %v", errNotEnoughLiquidity, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindNominalAmount(t *testing.T) {
|
||||
b := Items{
|
||||
{Amount: 5, Price: 1},
|
||||
{Amount: 5, Price: 2},
|
||||
{Amount: 5, Price: 3},
|
||||
{Amount: 5, Price: 4},
|
||||
}
|
||||
nomAmt, remainingAmt := b.FindNominalAmount(15)
|
||||
if nomAmt != 30 && remainingAmt != 0 {
|
||||
t.Errorf("invalid return")
|
||||
}
|
||||
b = Items{}
|
||||
nomAmt, remainingAmt = b.FindNominalAmount(15)
|
||||
if nomAmt != 0 && remainingAmt != 30 {
|
||||
t.Errorf("invalid return")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ var (
|
||||
errDuplication = errors.New("price duplication")
|
||||
errIDDuplication = errors.New("id duplication")
|
||||
errPeriodUnset = errors.New("funding rate period is unset")
|
||||
errNotEnoughLiquidity = errors.New("not enough liquidity")
|
||||
)
|
||||
|
||||
var service = Service{
|
||||
|
||||
12
testdata/http_mock/binance/binance.json
vendored
12
testdata/http_mock/binance/binance.json
vendored
@@ -255890,6 +255890,18 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"/fapi/v1/time": {
|
||||
"GET": [
|
||||
{
|
||||
"data": {
|
||||
"serverTime": 1620099477465
|
||||
},
|
||||
"queryString": "",
|
||||
"bodyParams": "",
|
||||
"headers": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
"/fapi/v1/trades": {
|
||||
"GET": [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user