Feature: Implement funding rates, futures and coin margin (exchange API coverage) (#530)

* ALMOST THERE

* more api wips

* more api thingz

* testing n more api wipz

* more apiz

* more wips

* what is goin on

* more wips

* whip n testing

* testing

* testing

no keys

* remove log

* kraken is broken

ugh

* still broken

* fixing auth funcs + usdtm api docs

* wip

* api stuffs

* whip

* more wips

* whip

* more wip

* api wip n testing

* wip

* wip

* unsaved

* wip n testing

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* whip

* wrapper authenticated functions

* adding asset type and fixing dependencies

* wip

* binance auth wrapper start

* wrapper functionality

* wip

* wip

* wip

* wrapper cancel functions

* order submission for wrappers

* wip

* more error fixing and nits

* websocket beginning n error fix

* wip

* WOW

* glorious n shazzy nits

* useless nits

* wip

* fixing things

* merge stuffs

* crapveyor

* crapveyor rebuild

* probably broke more things than he fixed

* rm lns n other thangs

* hope

* please

* stop it

* done

* ofcourse

* rm vb

* fix lbank

* appveyor please

* float lev

* DONT ASK RYAN FOR HELP EVER

* wip

* wip

* endpoint upgrades continued

* path upgrade

* NeeeNeeeNeeeNeeeNING

* fix stuffs

* fixing time issue

* fixing broken funcs

* glorious nits

* shaz changes

* fixing errors for fundmon

* more error fixing for fundmon

* test running past 30s

* basic changes

* THX AGAIN SHAZBERT

* path system upgrade

* config upgrade

* unsaved stuffs

* broken wip config upgrade

* path system upgrade contd.

* path system upgrade contd

* path upgrade ready for review

* testing verbose removed

* linter stuffs

* appveyor stuffs

* appveyor stuff

* fixed?

* bugfix

* wip

* broken stuff

* fix test

* wierd hack fix

* appveyor pls stop

* error found

* more useless nits

* bitmex err

* broken wip

* broken wip path upgrade change to uint32

* changed url lookups to uint

* WOW

* ready4review

* config fixed HOPEFULLY

* config fix and glorious changes

* efficient way of getting orders and open orders

* binance wrapper logic fixing

* testing, adding tests and fixing lot of errrrrs

* merge master

* appveyor stuffs

* appveyor stuffs

* fmt

* test

* octalLiteral issue fix?

* octalLiteral fix?

* rm vb

* prnt ln to restart

* adding testz

* test fixzzz

* READY FOR REVIEW

* Actually ready now

* FORMATTING

* addressing shazzy n glorious nits

* crapveyor

* rm vb

* small change

* fixing err

* shazbert nits

* review changes

* requested changes

* more requested changes

* noo

* last nit fixes

* restart appveyor

* improving test cov

* Update .golangci.yml

* shazbert changes

* moving pair formatting

* format pair update wip

* path upgrade complete

* error fix

* appveyor linters

* more linters

* remove testexch

* more formatting changes

* changes

* shazbert changes

* checking older requested changes to ensure completion

* wip

* fixing broken code

* error fix

* all fixed

* additional changes

* more changes

* remove commented code

* ftx margin api

* appveyor fixes

* more appveyor issues + test addition

* more appveyor issues + test addition

* remove unnecessary

* testing

* testing, fixing okex api, error fix

* git merge fix

* go sum

* glorious changes and error fix

* rm vb

* more glorious changes and go mod tidy

* fixed now

* okex testing upgrade

* old config migration and batch fetching fix

* added test

* glorious requested changes WIP

* tested and fixed

* go fmted

* go fmt and test fix

* additional funcs and tests for fundingRates

* OKEX tested and fixed

* appveyor fixes

* ineff assign

* 1 glorious change

* error fix

* typo

* shazbert changes

* glorious code changes and path fixing huobi WIP

* adding assetType to accountinfo functions

* fixing panic

* panic fix and updating account info wrappers WIP

* updateaccountinfo updated

* testing WIP binance USDT n Coin Margined and Kraken Futures

* auth functions tested and fixed

* added test

* config reverted

* shazbert and glorious changes

* shazbert and glorious changes

* latest changes and portfolio update

* go fmt change:

* remove commented codes

* improved error checking

* index out of range fix

* rm ln

* critical nit

* glorious latest changes

* appveyor changes

* shazbert change

* easier readability

* latest glorious changes

* shadow dec

* assetstore updated

* last change

* another last change

* merge changes

* go mod tidy

* thrasher requested changes wip

* improving struct layouts

* appveyor go fmt

* remove unnecessary code

* shazbert changes

* small change

* oopsie

* tidy

* configtest reverted

* error fix

* oopsie

* for what

* test patch fix

* insecurities

* fixing tests

* fix config
This commit is contained in:
Adam
2021-02-12 16:19:18 +11:00
committed by GitHub
parent e9bd2ad4d8
commit 504c2fad6d
169 changed files with 227754 additions and 31776 deletions

View File

@@ -7,6 +7,7 @@ import (
"github.com/gofrs/uuid"
"github.com/thrasher-corp/gocryptotrader/dispatch"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
)
func init() {
@@ -45,20 +46,29 @@ func Process(h *Holdings) error {
}
// GetHoldings returns full holdings for an exchange
func GetHoldings(exch string) (Holdings, error) {
func GetHoldings(exch string, assetType asset.Item) (Holdings, error) {
if exch == "" {
return Holdings{}, errors.New("exchange name unset")
}
exch = strings.ToLower(exch)
if !assetType.IsValid() {
return Holdings{}, fmt.Errorf("assetType %v is invalid", assetType)
}
service.Lock()
defer service.Unlock()
h, ok := service.accounts[exch]
if !ok {
return Holdings{}, errors.New("exchange account holdings not found")
}
return *h.h, nil
for y := range h.h.Accounts {
if h.h.Accounts[y].AssetType == assetType {
return *h.h, nil
}
}
return Holdings{}, fmt.Errorf("%v holdings data not found for %s", assetType, exch)
}
// Update updates holdings with new account info

View File

@@ -7,6 +7,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/dispatch"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
)
func TestHoldings(t *testing.T) {
@@ -36,7 +37,8 @@ func TestHoldings(t *testing.T) {
err = Process(&Holdings{
Exchange: "Test",
Accounts: []SubAccount{{
ID: "1337",
AssetType: asset.Spot,
ID: "1337",
Currencies: []Balance{
{
CurrencyName: currency.BTC,
@@ -50,17 +52,22 @@ func TestHoldings(t *testing.T) {
t.Error(err)
}
_, err = GetHoldings("")
_, err = GetHoldings("", asset.Spot)
if err == nil {
t.Error("error cannot be nil")
}
_, err = GetHoldings("bla")
_, err = GetHoldings("bla", asset.Spot)
if err == nil {
t.Error("error cannot be nil")
}
u, err := GetHoldings("Test")
_, err = GetHoldings("bla", asset.Item("hi"))
if err == nil {
t.Error("error cannot be nil since an invalid asset type is provided")
}
u, err := GetHoldings("Test", asset.Spot)
if err != nil {
t.Error(err)
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/gofrs/uuid"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/dispatch"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
)
// Vars for the ticker package
@@ -36,6 +37,7 @@ type Holdings struct {
// SubAccount defines a singular account type with asocciated currency balances
type SubAccount struct {
ID string
AssetType asset.Item
Currencies []Balance
}

View File

@@ -58,7 +58,7 @@ func (a *Alphapoint) GetTicker(currencyPair string) (Ticker, error) {
req["productPair"] = currencyPair
response := Ticker{}
err := a.SendHTTPRequest(http.MethodPost, alphapointTicker, req, &response)
err := a.SendHTTPRequest(exchange.RestSpot, http.MethodPost, alphapointTicker, req, &response)
if err != nil {
return response, err
}
@@ -81,7 +81,7 @@ func (a *Alphapoint) GetTrades(currencyPair string, startIndex, count int) (Trad
req["Count"] = count
response := Trades{}
err := a.SendHTTPRequest(http.MethodPost, alphapointTrades, req, &response)
err := a.SendHTTPRequest(exchange.RestSpot, http.MethodPost, alphapointTrades, req, &response)
if err != nil {
return response, err
}
@@ -102,7 +102,7 @@ func (a *Alphapoint) GetTradesByDate(currencyPair string, startDate, endDate int
req["endDate"] = endDate
response := Trades{}
err := a.SendHTTPRequest(http.MethodPost, alphapointTradesByDate, req, &response)
err := a.SendHTTPRequest(exchange.RestSpot, http.MethodPost, alphapointTradesByDate, req, &response)
if err != nil {
return response, err
}
@@ -119,7 +119,7 @@ func (a *Alphapoint) GetOrderbook(currencyPair string) (Orderbook, error) {
req["productPair"] = currencyPair
response := Orderbook{}
err := a.SendHTTPRequest(http.MethodPost, alphapointOrderbook, req, &response)
err := a.SendHTTPRequest(exchange.RestSpot, http.MethodPost, alphapointOrderbook, req, &response)
if err != nil {
return response, err
}
@@ -133,7 +133,7 @@ func (a *Alphapoint) GetOrderbook(currencyPair string) (Orderbook, error) {
func (a *Alphapoint) GetProductPairs() (ProductPairs, error) {
response := ProductPairs{}
err := a.SendHTTPRequest(http.MethodPost, alphapointProductPairs, nil, &response)
err := a.SendHTTPRequest(exchange.RestSpot, http.MethodPost, alphapointProductPairs, nil, &response)
if err != nil {
return response, err
}
@@ -147,7 +147,7 @@ func (a *Alphapoint) GetProductPairs() (ProductPairs, error) {
func (a *Alphapoint) GetProducts() (Products, error) {
response := Products{}
err := a.SendHTTPRequest(http.MethodPost, alphapointProducts, nil, &response)
err := a.SendHTTPRequest(exchange.RestSpot, http.MethodPost, alphapointProducts, nil, &response)
if err != nil {
return response, err
}
@@ -178,7 +178,7 @@ func (a *Alphapoint) CreateAccount(firstName, lastName, email, phone, password s
req["password"] = password
response := Response{}
err := a.SendAuthenticatedHTTPRequest(http.MethodPost, alphapointCreateAccount, req, &response)
err := a.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, alphapointCreateAccount, req, &response)
if err != nil {
return fmt.Errorf("unable to create account. Reason: %s", err)
}
@@ -192,7 +192,7 @@ func (a *Alphapoint) CreateAccount(firstName, lastName, email, phone, password s
func (a *Alphapoint) GetUserInfo() (UserInfo, error) {
response := UserInfo{}
err := a.SendAuthenticatedHTTPRequest(http.MethodPost, alphapointUserInfo, map[string]interface{}{}, &response)
err := a.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, alphapointUserInfo, map[string]interface{}{}, &response)
if err != nil {
return UserInfo{}, err
}
@@ -245,6 +245,7 @@ func (a *Alphapoint) SetUserInfo(firstName, lastName, cell2FACountryCode, cell2F
req["userInfoKVP"] = userInfoKVPs
err := a.SendAuthenticatedHTTPRequest(
exchange.RestSpot,
http.MethodPost,
alphapointUserInfo,
req,
@@ -264,6 +265,7 @@ func (a *Alphapoint) GetAccountInformation() (AccountInfo, error) {
response := AccountInfo{}
err := a.SendAuthenticatedHTTPRequest(
exchange.RestSpot,
http.MethodPost,
alphapointAccountInfo,
map[string]interface{}{},
@@ -290,6 +292,7 @@ func (a *Alphapoint) GetAccountTrades(currencyPair string, startIndex, count int
response := Trades{}
err := a.SendAuthenticatedHTTPRequest(
exchange.RestSpot,
http.MethodPost,
alphapointAccountTrades,
req,
@@ -308,7 +311,7 @@ func (a *Alphapoint) GetAccountTrades(currencyPair string, startIndex, count int
func (a *Alphapoint) GetDepositAddresses() ([]DepositAddresses, error) {
response := Response{}
err := a.SendAuthenticatedHTTPRequest(http.MethodPost, alphapointDepositAddresses,
err := a.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, alphapointDepositAddresses,
map[string]interface{}{}, &response,
)
if err != nil {
@@ -334,6 +337,7 @@ func (a *Alphapoint) WithdrawCoins(symbol, product, address string, amount float
response := Response{}
err := a.SendAuthenticatedHTTPRequest(
exchange.RestSpot,
http.MethodPost,
alphapointWithdraw,
req,
@@ -373,6 +377,7 @@ func (a *Alphapoint) CreateOrder(symbol, side, orderType string, quantity, price
response := Response{}
err := a.SendAuthenticatedHTTPRequest(
exchange.RestSpot,
http.MethodPost,
alphapointCreateOrder,
req,
@@ -403,6 +408,7 @@ func (a *Alphapoint) ModifyExistingOrder(symbol string, orderID, action int64) (
response := Response{}
err := a.SendAuthenticatedHTTPRequest(
exchange.RestSpot,
http.MethodPost,
alphapointModifyOrder,
req,
@@ -427,6 +433,7 @@ func (a *Alphapoint) CancelExistingOrder(orderID int64, omsid string) (int64, er
response := Response{}
err := a.SendAuthenticatedHTTPRequest(
exchange.RestSpot,
http.MethodPost,
alphapointCancelOrder,
req,
@@ -449,6 +456,7 @@ func (a *Alphapoint) CancelAllExistingOrders(omsid string) error {
response := Response{}
err := a.SendAuthenticatedHTTPRequest(
exchange.RestSpot,
http.MethodPost,
alphapointCancelAllOrders,
req,
@@ -468,6 +476,7 @@ func (a *Alphapoint) GetOrders() ([]OpenOrders, error) {
response := OrderInfo{}
err := a.SendAuthenticatedHTTPRequest(
exchange.RestSpot,
http.MethodPost,
alphapointOpenOrders,
map[string]interface{}{},
@@ -496,6 +505,7 @@ func (a *Alphapoint) GetOrderFee(symbol, side string, quantity, price float64) (
response := Response{}
err := a.SendAuthenticatedHTTPRequest(
exchange.RestSpot,
http.MethodPost,
alphapointOrderFee,
req,
@@ -511,10 +521,14 @@ func (a *Alphapoint) GetOrderFee(symbol, side string, quantity, price float64) (
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (a *Alphapoint) SendHTTPRequest(method, path string, data map[string]interface{}, result interface{}) error {
func (a *Alphapoint) SendHTTPRequest(ep exchange.URL, method, path string, data map[string]interface{}, result interface{}) error {
endpoint, err := a.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
headers := make(map[string]string)
headers["Content-Type"] = "application/json"
path = fmt.Sprintf("%s/ajax/v%s/%s", a.API.Endpoints.URL, alphapointAPIVersion, path)
path = fmt.Sprintf("%s/ajax/v%s/%s", endpoint, alphapointAPIVersion, path)
PayloadJSON, err := json.Marshal(data)
if err != nil {
@@ -533,11 +547,16 @@ func (a *Alphapoint) SendHTTPRequest(method, path string, data map[string]interf
}
// SendAuthenticatedHTTPRequest sends an authenticated request
func (a *Alphapoint) SendAuthenticatedHTTPRequest(method, path string, data map[string]interface{}, result interface{}) error {
func (a *Alphapoint) SendAuthenticatedHTTPRequest(ep exchange.URL, method, path string, data map[string]interface{}, result interface{}) error {
if !a.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, a.Name)
}
endpoint, err := a.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
n := a.Requester.GetNonce(true)
headers := make(map[string]string)
@@ -548,7 +567,7 @@ func (a *Alphapoint) SendAuthenticatedHTTPRequest(method, path string, data map[
[]byte(n.String()+a.API.Credentials.ClientID+a.API.Credentials.Key),
[]byte(a.API.Credentials.Secret))
data["apiSig"] = strings.ToUpper(crypto.HexEncodeToString(hmac))
path = fmt.Sprintf("%s/ajax/v%s/%s", a.API.Endpoints.URL, alphapointAPIVersion, path)
path = fmt.Sprintf("%s/ajax/v%s/%s", endpoint, alphapointAPIVersion, path)
PayloadJSON, err := json.Marshal(data)
if err != nil {

View File

@@ -2,7 +2,6 @@ package alphapoint
import (
"encoding/json"
"log"
"os"
"testing"
"time"
@@ -30,12 +29,6 @@ func TestMain(m *testing.M) {
a.API.Credentials.Key = apiKey
a.API.Credentials.Secret = apiSecret
a.API.AuthenticatedSupport = true
if a.API.Endpoints.URL != "https://sim3.alphapoint.com:8400" {
log.Fatal("SetDefaults: String Incorrect -", a.API.Endpoints.URL)
}
if a.API.Endpoints.WebsocketURL != "wss://sim3.alphapoint.com:8401/v1/GetTicker/" {
log.Fatal("SetDefaults: String Incorrect -", a.API.Endpoints.WebsocketURL)
}
os.Exit(m.Run())
}
@@ -327,7 +320,7 @@ func TestGetAccountInfo(t *testing.T) {
t.Skip("API keys not set, skipping")
}
_, err := a.UpdateAccountInfo()
_, err := a.UpdateAccountInfo(asset.Spot)
if err == nil {
t.Error("GetUserInfo() Expected error")
}
@@ -441,7 +434,8 @@ func TestFormatWithdrawPermissions(t *testing.T) {
func TestGetActiveOrders(t *testing.T) {
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := a.GetActiveOrders(&getOrdersRequest)
@@ -455,7 +449,8 @@ func TestGetActiveOrders(t *testing.T) {
func TestGetOrderHistory(t *testing.T) {
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := a.GetOrderHistory(&getOrdersRequest)

View File

@@ -5,6 +5,7 @@ import (
"net/http"
"github.com/gorilla/websocket"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/log"
)
@@ -18,7 +19,11 @@ func (a *Alphapoint) WebsocketClient() {
var dialer websocket.Dialer
var err error
var httpResp *http.Response
a.WebsocketConn, httpResp, err = dialer.Dial(a.API.Endpoints.WebsocketURL, http.Header{})
endpoint, err := a.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
log.Error(log.WebsocketMgr, err)
}
a.WebsocketConn, httpResp, err = dialer.Dial(endpoint, http.Header{})
httpResp.Body.Close() // not used, so safely free the body
if err != nil {

View File

@@ -17,6 +17,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
"github.com/thrasher-corp/gocryptotrader/exchanges/trade"
"github.com/thrasher-corp/gocryptotrader/log"
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
)
@@ -30,8 +31,14 @@ func (a *Alphapoint) SetDefaults() {
a.Name = "Alphapoint"
a.Enabled = true
a.Verbose = true
a.API.Endpoints.URL = alphapointDefaultAPIURL
a.API.Endpoints.WebsocketURL = alphapointDefaultWebsocketURL
a.API.Endpoints = a.NewEndpoints()
err := a.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: alphapointDefaultAPIURL,
exchange.WebsocketSpot: alphapointDefaultWebsocketURL,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
a.API.CredentialsValidator.RequiresKey = true
a.API.CredentialsValidator.RequiresSecret = true
@@ -82,7 +89,7 @@ func (a *Alphapoint) UpdateTradablePairs(forceUpdate bool) error {
// UpdateAccountInfo retrieves balances for all enabled currencies on the
// Alphapoint exchange
func (a *Alphapoint) UpdateAccountInfo() (account.Holdings, error) {
func (a *Alphapoint) UpdateAccountInfo(assetType asset.Item) (account.Holdings, error) {
var response account.Holdings
response.Exchange = a.Name
acc, err := a.GetAccountInformation()
@@ -114,10 +121,10 @@ func (a *Alphapoint) UpdateAccountInfo() (account.Holdings, error) {
// FetchAccountInfo retrieves balances for all enabled currencies on the
// Alphapoint exchange
func (a *Alphapoint) FetchAccountInfo() (account.Holdings, error) {
acc, err := account.GetHoldings(a.Name)
func (a *Alphapoint) FetchAccountInfo(assetType asset.Item) (account.Holdings, error) {
acc, err := account.GetHoldings(a.Name, assetType)
if err != nil {
return a.UpdateAccountInfo()
return a.UpdateAccountInfo(assetType)
}
return acc, nil
@@ -433,7 +440,7 @@ func (a *Alphapoint) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detai
// ValidateCredentials validates current credentials used for wrapper
// functionality
func (a *Alphapoint) ValidateCredentials() error {
_, err := a.UpdateAccountInfo()
func (a *Alphapoint) ValidateCredentials(assetType asset.Item) error {
_, err := a.UpdateAccountInfo(assetType)
return a.CheckTransientError(err)
}

View File

@@ -23,6 +23,8 @@ const (
Futures = Item("futures")
UpsideProfitContract = Item("upsideprofitcontract")
DownsideProfitContract = Item("downsideprofitcontract")
CoinMarginedFutures = Item("coinmarginedfutures")
USDTMarginedFutures = Item("usdtmarginedfutures")
)
var supported = Items{
@@ -36,6 +38,8 @@ var supported = Items{
Futures,
UpsideProfitContract,
DownsideProfitContract,
CoinMarginedFutures,
USDTMarginedFutures,
}
// Supported returns a list of supported asset types

View File

@@ -17,18 +17,30 @@ import (
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
"github.com/thrasher-corp/gocryptotrader/log"
)
// Binance is the overarching type across the Binance package
type Binance struct {
exchange.Base
// Valid string list that is required by the exchange
validLimits []int
obm *orderbookManager
}
const (
apiURL = "https://api.binance.com"
apiURL = "https://api.binance.com"
spotAPIURL = "https://sapi.binance.com"
cfuturesAPIURL = "https://dapi.binance.com"
ufuturesAPIURL = "https://fapi.binance.com"
// Public endpoints
exchangeInfo = "/api/v3/exchangeInfo"
orderBookDepth = "/api/v3/depth"
recentTrades = "/api/v3/trades"
historicalTrades = "/api/v3/historicalTrades"
aggregatedTrades = "/api/v3/aggTrades"
candleStick = "/api/v3/klines"
averagePrice = "/api/v3/avgPrice"
@@ -37,45 +49,57 @@ const (
bestPrice = "/api/v3/ticker/bookTicker"
accountInfo = "/api/v3/account"
userAccountStream = "/api/v3/userDataStream"
perpExchangeInfo = "/fapi/v1/exchangeInfo"
// Authenticated endpoints
newOrderTest = "/api/v3/order/test"
newOrder = "/api/v3/order"
cancelOrder = "/api/v3/order"
queryOrder = "/api/v3/order"
openOrders = "/api/v3/openOrders"
allOrders = "/api/v3/allOrders"
myTrades = "/api/v3/myTrades"
newOrderTest = "/api/v3/order/test"
orderEndpoint = "/api/v3/order"
openOrders = "/api/v3/openOrders"
allOrders = "/api/v3/allOrders"
// Withdraw API endpoints
withdrawEndpoint = "/wapi/v3/withdraw.html"
depositHistory = "/wapi/v3/depositHistory.html"
withdrawalHistory = "/wapi/v3/withdrawHistory.html"
depositAddress = "/wapi/v3/depositAddress.html"
accountStatus = "/wapi/v3/accountStatus.html"
systemStatus = "/wapi/v3/systemStatus.html"
dustLog = "/wapi/v3/userAssetDribbletLog.html"
tradeFee = "/wapi/v3/tradeFee.html"
assetDetail = "/wapi/v3/assetDetail.html"
withdrawEndpoint = "/wapi/v3/withdraw.html"
depositHistory = "/wapi/v3/depositHistory.html"
withdrawalHistory = "/wapi/v3/withdrawHistory.html"
depositAddress = "/wapi/v3/depositAddress.html"
accountStatus = "/wapi/v3/accountStatus.html"
systemStatus = "/wapi/v3/systemStatus.html"
dustLog = "/wapi/v3/userAssetDribbletLog.html"
tradeFee = "/wapi/v3/tradeFee.html"
assetDetail = "/wapi/v3/assetDetail.html"
undocumentedInterestHistory = "/gateway-api/v1/public/isolated-margin/pair/vip-level"
undocumentedCrossMarginInterestHistory = "/gateway-api/v1/friendly/margin/vip/spec/list-all"
)
// Binance is the overarching type across the Bithumb package
type Binance struct {
exchange.Base
// GetInterestHistory gets interest history for currency/currencies provided
func (b *Binance) GetInterestHistory() (MarginInfoData, error) {
var resp MarginInfoData
if err := b.SendHTTPRequest(exchange.EdgeCase1, undocumentedInterestHistory, limitDefault, &resp); err != nil {
return resp, err
}
return resp, nil
}
// Valid string list that is required by the exchange
validLimits []int
// GetCrossMarginInterestHistory gets cross-margin interest history for currency/currencies provided
func (b *Binance) GetCrossMarginInterestHistory() (CrossMarginInterestData, error) {
var resp CrossMarginInterestData
if err := b.SendHTTPRequest(exchange.EdgeCase1, undocumentedCrossMarginInterestHistory, limitDefault, &resp); err != nil {
return resp, err
}
return resp, nil
}
obm *orderbookManager
// GetMarginMarkets returns exchange information. Check binance_types for more information
func (b *Binance) GetMarginMarkets() (PerpsExchangeInfo, error) {
var resp PerpsExchangeInfo
return resp, b.SendHTTPRequest(exchange.RestSpot, perpExchangeInfo, limitDefault, &resp)
}
// GetExchangeInfo returns exchange information. Check binance_types for more
// information
func (b *Binance) GetExchangeInfo() (ExchangeInfo, error) {
var resp ExchangeInfo
path := b.API.Endpoints.URL + exchangeInfo
return resp, b.SendHTTPRequest(path, limitDefault, &resp)
return resp, b.SendHTTPRequest(exchange.RestSpotSupplementary, exchangeInfo, limitDefault, &resp)
}
// GetOrderBook returns full orderbook information
@@ -90,7 +114,7 @@ func (b *Binance) GetOrderBook(obd OrderBookDataRequestParams) (OrderBook, error
}
params := url.Values{}
symbol, err := b.formatSymbol(obd.Symbol)
symbol, err := b.FormatSymbol(obd.Symbol, asset.Spot)
if err != nil {
return orderbook, err
}
@@ -98,8 +122,7 @@ func (b *Binance) GetOrderBook(obd OrderBookDataRequestParams) (OrderBook, error
params.Set("limit", fmt.Sprintf("%d", obd.Limit))
var resp OrderBookData
path := common.EncodeURLValues(b.API.Endpoints.URL+orderBookDepth, params)
if err := b.SendHTTPRequest(path, orderbookLimit(obd.Limit), &resp); err != nil {
if err := b.SendHTTPRequest(exchange.RestSpotSupplementary, orderBookDepth+"?"+params.Encode(), orderbookLimit(obd.Limit), &resp); err != nil {
return orderbook, err
}
@@ -147,16 +170,16 @@ func (b *Binance) GetMostRecentTrades(rtr RecentTradeRequestParams) ([]RecentTra
var resp []RecentTrade
params := url.Values{}
symbol, err := b.formatSymbol(rtr.Symbol)
symbol, err := b.FormatSymbol(rtr.Symbol, asset.Spot)
if err != nil {
return nil, err
}
params.Set("symbol", symbol)
params.Set("limit", fmt.Sprintf("%d", rtr.Limit))
path := fmt.Sprintf("%s%s?%s", b.API.Endpoints.URL, recentTrades, params.Encode())
path := recentTrades + "?" + params.Encode()
return resp, b.SendHTTPRequest(path, limitDefault, &resp)
return resp, b.SendHTTPRequest(exchange.RestSpotSupplementary, path, limitDefault, &resp)
}
// GetHistoricalTrades returns historical trade activity
@@ -177,7 +200,7 @@ func (b *Binance) GetHistoricalTrades(symbol string, limit int, fromID int64) ([
// https://binance-docs.github.io/apidocs/spot/en/#compressed-aggregate-trades-list
func (b *Binance) GetAggregatedTrades(arg *AggregatedTradeRequestParams) ([]AggregatedTrade, error) {
params := url.Values{}
symbol, err := b.formatSymbol(arg.Symbol)
symbol, err := b.FormatSymbol(arg.Symbol, asset.Spot)
if err != nil {
return nil, err
}
@@ -218,10 +241,9 @@ func (b *Binance) GetAggregatedTrades(arg *AggregatedTradeRequestParams) ([]Aggr
// We would receive {"code":-1128,"msg":"Combination of optional parameters invalid."}
return nil, errors.New("please set StartTime or FromId, but not both")
}
var resp []AggregatedTrade
path := b.API.Endpoints.URL + aggregatedTrades + "?" + params.Encode()
return resp, b.SendHTTPRequest(path, limitDefault, &resp)
path := aggregatedTrades + "?" + params.Encode()
return resp, b.SendHTTPRequest(exchange.RestSpotSupplementary, path, limitDefault, &resp)
}
// batchAggregateTrades fetches trades in multiple requests
@@ -246,8 +268,8 @@ func (b *Binance) batchAggregateTrades(arg *AggregatedTradeRequestParams, params
}
params.Set("startTime", timeString(start))
params.Set("endTime", timeString(start.Add(time.Hour)))
path := b.API.Endpoints.URL + aggregatedTrades + "?" + params.Encode()
err := b.SendHTTPRequest(path, limitDefault, &resp)
path := aggregatedTrades + "?" + params.Encode()
err := b.SendHTTPRequest(exchange.RestSpotSupplementary, path, limitDefault, &resp)
if err != nil {
log.Warn(log.ExchangeSys, err.Error())
return resp, err
@@ -263,9 +285,9 @@ func (b *Binance) batchAggregateTrades(arg *AggregatedTradeRequestParams, params
for ; arg.Limit == 0 || len(resp) < arg.Limit; fromID = resp[len(resp)-1].ATradeID {
// Keep requesting new data after last retrieved trade
params.Set("fromId", strconv.FormatInt(fromID, 10))
path := b.API.Endpoints.URL + aggregatedTrades + "?" + params.Encode()
path := aggregatedTrades + "?" + params.Encode()
var additionalTrades []AggregatedTrade
err := b.SendHTTPRequest(path, limitDefault, &additionalTrades)
err := b.SendHTTPRequest(exchange.RestSpotSupplementary, path, limitDefault, &additionalTrades)
if err != nil {
return resp, err
}
@@ -304,7 +326,7 @@ func (b *Binance) GetSpotKline(arg *KlinesRequestParams) ([]CandleStick, error)
var klineData []CandleStick
params := url.Values{}
symbol, err := b.formatSymbol(arg.Symbol)
symbol, err := b.FormatSymbol(arg.Symbol, asset.Spot)
if err != nil {
return nil, err
}
@@ -320,9 +342,9 @@ func (b *Binance) GetSpotKline(arg *KlinesRequestParams) ([]CandleStick, error)
params.Set("endTime", timeString(arg.EndTime))
}
path := fmt.Sprintf("%s%s?%s", b.API.Endpoints.URL, candleStick, params.Encode())
path := candleStick + "?" + params.Encode()
if err := b.SendHTTPRequest(path, limitDefault, &resp); err != nil {
if err := b.SendHTTPRequest(exchange.RestSpotSupplementary, path, limitDefault, &resp); err != nil {
return klineData, err
}
@@ -375,15 +397,15 @@ func (b *Binance) GetSpotKline(arg *KlinesRequestParams) ([]CandleStick, error)
func (b *Binance) GetAveragePrice(symbol currency.Pair) (AveragePrice, error) {
resp := AveragePrice{}
params := url.Values{}
symbolValue, err := b.formatSymbol(symbol)
symbolValue, err := b.FormatSymbol(symbol, asset.Spot)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
path := fmt.Sprintf("%s%s?%s", b.API.Endpoints.URL, averagePrice, params.Encode())
path := averagePrice + "?" + params.Encode()
return resp, b.SendHTTPRequest(path, limitDefault, &resp)
return resp, b.SendHTTPRequest(exchange.RestSpotSupplementary, path, limitDefault, &resp)
}
// GetPriceChangeStats returns price change statistics for the last 24 hours
@@ -392,22 +414,21 @@ func (b *Binance) GetAveragePrice(symbol currency.Pair) (AveragePrice, error) {
func (b *Binance) GetPriceChangeStats(symbol currency.Pair) (PriceChangeStats, error) {
resp := PriceChangeStats{}
params := url.Values{}
symbolValue, err := b.formatSymbol(symbol)
symbolValue, err := b.FormatSymbol(symbol, asset.Spot)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
path := fmt.Sprintf("%s%s?%s", b.API.Endpoints.URL, priceChange, params.Encode())
path := priceChange + "?" + params.Encode()
return resp, b.SendHTTPRequest(path, limitDefault, &resp)
return resp, b.SendHTTPRequest(exchange.RestSpotSupplementary, path, limitDefault, &resp)
}
// GetTickers returns the ticker data for the last 24 hrs
func (b *Binance) GetTickers() ([]PriceChangeStats, error) {
var resp []PriceChangeStats
path := b.API.Endpoints.URL + priceChange
return resp, b.SendHTTPRequest(path, limitPriceChangeAll, &resp)
return resp, b.SendHTTPRequest(exchange.RestSpotSupplementary, priceChange, limitPriceChangeAll, &resp)
}
// GetLatestSpotPrice returns latest spot price of symbol
@@ -416,15 +437,15 @@ func (b *Binance) GetTickers() ([]PriceChangeStats, error) {
func (b *Binance) GetLatestSpotPrice(symbol currency.Pair) (SymbolPrice, error) {
resp := SymbolPrice{}
params := url.Values{}
symbolValue, err := b.formatSymbol(symbol)
symbolValue, err := b.FormatSymbol(symbol, asset.Spot)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
path := fmt.Sprintf("%s%s?%s", b.API.Endpoints.URL, symbolPrice, params.Encode())
path := symbolPrice + "?" + params.Encode()
return resp, b.SendHTTPRequest(path, symbolPriceLimit(symbolValue), &resp)
return resp, b.SendHTTPRequest(exchange.RestSpotSupplementary, path, symbolPriceLimit(symbolValue), &resp)
}
// GetBestPrice returns the latest best price for symbol
@@ -433,21 +454,21 @@ func (b *Binance) GetLatestSpotPrice(symbol currency.Pair) (SymbolPrice, error)
func (b *Binance) GetBestPrice(symbol currency.Pair) (BestPrice, error) {
resp := BestPrice{}
params := url.Values{}
symbolValue, err := b.formatSymbol(symbol)
symbolValue, err := b.FormatSymbol(symbol, asset.Spot)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
path := fmt.Sprintf("%s%s?%s", b.API.Endpoints.URL, bestPrice, params.Encode())
path := bestPrice + "?" + params.Encode()
return resp, b.SendHTTPRequest(path, bestPriceLimit(symbolValue), &resp)
return resp, b.SendHTTPRequest(exchange.RestSpotSupplementary, path, bestPriceLimit(symbolValue), &resp)
}
// NewOrder sends a new order to Binance
func (b *Binance) NewOrder(o *NewOrderRequest) (NewOrderResponse, error) {
var resp NewOrderResponse
if err := b.newOrder(newOrder, o, &resp); err != nil {
if err := b.newOrder(orderEndpoint, o, &resp); err != nil {
return resp, err
}
@@ -465,10 +486,8 @@ func (b *Binance) NewOrderTest(o *NewOrderRequest) error {
}
func (b *Binance) newOrder(api string, o *NewOrderRequest, resp *NewOrderResponse) error {
path := b.API.Endpoints.URL + api
params := url.Values{}
symbol, err := b.formatSymbol(o.Symbol)
symbol, err := b.FormatSymbol(o.Symbol, asset.Spot)
if err != nil {
return err
}
@@ -502,16 +521,14 @@ func (b *Binance) newOrder(api string, o *NewOrderRequest, resp *NewOrderRespons
if o.NewOrderRespType != "" {
params.Set("newOrderRespType", o.NewOrderRespType)
}
return b.SendAuthHTTPRequest(http.MethodPost, path, params, limitOrder, resp)
return b.SendAuthHTTPRequest(exchange.RestSpotSupplementary, http.MethodPost, api, params, limitOrder, resp)
}
// CancelExistingOrder sends a cancel order to Binance
func (b *Binance) CancelExistingOrder(symbol currency.Pair, orderID int64, origClientOrderID string) (CancelOrderResponse, error) {
var resp CancelOrderResponse
path := b.API.Endpoints.URL + cancelOrder
symbolValue, err := b.formatSymbol(symbol)
symbolValue, err := b.FormatSymbol(symbol, asset.Spot)
if err != nil {
return resp, err
}
@@ -525,8 +542,7 @@ func (b *Binance) CancelExistingOrder(symbol currency.Pair, orderID int64, origC
if origClientOrderID != "" {
params.Set("origClientOrderId", origClientOrderID)
}
return resp, b.SendAuthHTTPRequest(http.MethodDelete, path, params, limitOrder, &resp)
return resp, b.SendAuthHTTPRequest(exchange.RestSpotSupplementary, http.MethodDelete, orderEndpoint, params, limitOrder, &resp)
}
// OpenOrders Current open orders. Get all open orders on a symbol.
@@ -535,14 +551,12 @@ func (b *Binance) CancelExistingOrder(symbol currency.Pair, orderID int64, origC
func (b *Binance) OpenOrders(pair *currency.Pair) ([]QueryOrderData, error) {
var resp []QueryOrderData
path := b.API.Endpoints.URL + openOrders
params := url.Values{}
var symbol string
if pair != nil {
var err error
symbol, err = b.formatSymbol(*pair)
symbol, err = b.FormatSymbol(*pair, asset.Spot)
if err != nil {
return resp, err
}
@@ -551,7 +565,7 @@ func (b *Binance) OpenOrders(pair *currency.Pair) ([]QueryOrderData, error) {
params.Set("symbol", symbol)
}
if err := b.SendAuthHTTPRequest(http.MethodGet, path, params, openOrdersLimit(symbol), &resp); err != nil {
if err := b.SendAuthHTTPRequest(exchange.RestSpotSupplementary, http.MethodGet, openOrders, params, openOrdersLimit(symbol), &resp); err != nil {
return resp, err
}
@@ -564,10 +578,8 @@ func (b *Binance) OpenOrders(pair *currency.Pair) ([]QueryOrderData, error) {
func (b *Binance) AllOrders(symbol currency.Pair, orderID, limit string) ([]QueryOrderData, error) {
var resp []QueryOrderData
path := b.API.Endpoints.URL + allOrders
params := url.Values{}
symbolValue, err := b.formatSymbol(symbol)
symbolValue, err := b.FormatSymbol(symbol, asset.Spot)
if err != nil {
return resp, err
}
@@ -578,7 +590,7 @@ func (b *Binance) AllOrders(symbol currency.Pair, orderID, limit string) ([]Quer
if limit != "" {
params.Set("limit", limit)
}
if err := b.SendAuthHTTPRequest(http.MethodGet, path, params, limitOrdersAll, &resp); err != nil {
if err := b.SendAuthHTTPRequest(exchange.RestSpotSupplementary, http.MethodGet, allOrders, params, limitOrdersAll, &resp); err != nil {
return resp, err
}
@@ -589,10 +601,8 @@ func (b *Binance) AllOrders(symbol currency.Pair, orderID, limit string) ([]Quer
func (b *Binance) QueryOrder(symbol currency.Pair, origClientOrderID string, orderID int64) (QueryOrderData, error) {
var resp QueryOrderData
path := b.API.Endpoints.URL + queryOrder
params := url.Values{}
symbolValue, err := b.formatSymbol(symbol)
symbolValue, err := b.FormatSymbol(symbol, asset.Spot)
if err != nil {
return resp, err
}
@@ -604,7 +614,7 @@ func (b *Binance) QueryOrder(symbol currency.Pair, origClientOrderID string, ord
params.Set("orderId", strconv.FormatInt(orderID, 10))
}
if err := b.SendAuthHTTPRequest(http.MethodGet, path, params, limitOrder, &resp); err != nil {
if err := b.SendAuthHTTPRequest(exchange.RestSpotSupplementary, http.MethodGet, orderEndpoint, params, limitOrder, &resp); err != nil {
return resp, err
}
@@ -622,11 +632,9 @@ func (b *Binance) GetAccount() (*Account, error) {
}
var resp response
path := b.API.Endpoints.URL + accountInfo
params := url.Values{}
if err := b.SendAuthHTTPRequest(http.MethodGet, path, params, request.Unset, &resp); err != nil {
if err := b.SendAuthHTTPRequest(exchange.RestSpotSupplementary, http.MethodGet, accountInfo, params, request.Unset, &resp); err != nil {
return &resp.Account, err
}
@@ -638,10 +646,14 @@ func (b *Binance) GetAccount() (*Account, error) {
}
// SendHTTPRequest sends an unauthenticated request
func (b *Binance) SendHTTPRequest(path string, f request.EndpointLimit, result interface{}) error {
func (b *Binance) SendHTTPRequest(ePath exchange.URL, path string, f request.EndpointLimit, result interface{}) error {
endpointPath, err := b.API.Endpoints.GetURL(ePath)
if err != nil {
return err
}
return b.SendPayload(context.Background(), &request.Item{
Method: http.MethodGet,
Path: path,
Path: endpointPath + path,
Result: result,
Verbose: b.Verbose,
HTTPDebugging: b.HTTPDebugging,
@@ -650,42 +662,41 @@ func (b *Binance) SendHTTPRequest(path string, f request.EndpointLimit, result i
}
// SendAuthHTTPRequest sends an authenticated HTTP request
func (b *Binance) SendAuthHTTPRequest(method, path string, params url.Values, f request.EndpointLimit, result interface{}) error {
func (b *Binance) SendAuthHTTPRequest(ePath exchange.URL, method, path string, params url.Values, f request.EndpointLimit, result interface{}) error {
if !b.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name)
}
endpointPath, err := b.API.Endpoints.GetURL(ePath)
if err != nil {
return err
}
path = endpointPath + path
if params == nil {
params = url.Values{}
}
recvWindow := 5 * time.Second
params.Set("recvWindow", strconv.FormatInt(convert.RecvWindow(recvWindow), 10))
params.Set("timestamp", strconv.FormatInt(time.Now().Unix()*1000, 10))
signature := params.Encode()
hmacSigned := crypto.GetHMAC(crypto.HashSHA256, []byte(signature), []byte(b.API.Credentials.Secret))
hmacSignedStr := crypto.HexEncodeToString(hmacSigned)
headers := make(map[string]string)
headers["X-MBX-APIKEY"] = b.API.Credentials.Key
if b.Verbose {
log.Debugf(log.ExchangeSys, "sent path: %s", path)
}
path = common.EncodeURLValues(path, params)
path += "&signature=" + hmacSignedStr
interim := json.RawMessage{}
errCap := struct {
Success bool `json:"success"`
Message string `json:"msg"`
Code int64 `json:"code"`
}{}
ctx, cancel := context.WithTimeout(context.Background(), recvWindow)
defer cancel()
err := b.SendPayload(ctx, &request.Item{
err = b.SendPayload(ctx, &request.Item{
Method: method,
Path: path,
Headers: headers,
@@ -699,13 +710,11 @@ func (b *Binance) SendAuthHTTPRequest(method, path string, params url.Values, f
if err != nil {
return err
}
if err := json.Unmarshal(interim, &errCap); err == nil {
if !errCap.Success && errCap.Message != "" {
if !errCap.Success && errCap.Message != "" && errCap.Code != 200 {
return errors.New(errCap.Message)
}
}
return json.Unmarshal(interim, result)
}
@@ -779,7 +788,6 @@ func getCryptocurrencyWithdrawalFee(c currency.Code) float64 {
// WithdrawCrypto sends cryptocurrency to the address of your choosing
func (b *Binance) WithdrawCrypto(asset, address, addressTag, name, amount string) (string, error) {
var resp WithdrawResponse
path := b.API.Endpoints.URL + withdrawEndpoint
params := url.Values{}
params.Set("asset", asset)
@@ -792,7 +800,7 @@ func (b *Binance) WithdrawCrypto(asset, address, addressTag, name, amount string
params.Set("addressTag", addressTag)
}
if err := b.SendAuthHTTPRequest(http.MethodPost, path, params, request.Unset, &resp); err != nil {
if err := b.SendAuthHTTPRequest(exchange.RestSpotSupplementary, http.MethodPost, withdrawEndpoint, params, request.Unset, &resp); err != nil {
return "", err
}
@@ -811,7 +819,6 @@ func (b *Binance) WithdrawStatus(c currency.Code, status string, startTime, endT
WithdrawList []WithdrawStatusResponse `json:"withdrawList"`
}
path := b.API.Endpoints.URL + withdrawalHistory
params := url.Values{}
params.Set("asset", c.String())
@@ -838,7 +845,7 @@ func (b *Binance) WithdrawStatus(c currency.Code, status string, startTime, endT
params.Set("endTime", strconv.FormatInt(endTime, 10))
}
if err := b.SendAuthHTTPRequest(http.MethodGet, path, params, request.Unset, &response); err != nil {
if err := b.SendAuthHTTPRequest(exchange.RestSpotSupplementary, http.MethodGet, withdrawalHistory, params, request.Unset, &response); err != nil {
return response.WithdrawList, err
}
@@ -847,8 +854,6 @@ func (b *Binance) WithdrawStatus(c currency.Code, status string, startTime, endT
// GetDepositAddressForCurrency retrieves the wallet address for a given currency
func (b *Binance) GetDepositAddressForCurrency(currency string) (string, error) {
path := b.API.Endpoints.URL + depositAddress
resp := struct {
Address string `json:"address"`
Success bool `json:"success"`
@@ -860,16 +865,20 @@ func (b *Binance) GetDepositAddressForCurrency(currency string) (string, error)
params.Set("status", "true")
return resp.Address,
b.SendAuthHTTPRequest(http.MethodGet, path, params, request.Unset, &resp)
b.SendAuthHTTPRequest(exchange.RestSpotSupplementary, http.MethodGet, depositAddress, params, request.Unset, &resp)
}
// GetWsAuthStreamKey will retrieve a key to use for authorised WS streaming
func (b *Binance) GetWsAuthStreamKey() (string, error) {
endpointPath, err := b.API.Endpoints.GetURL(exchange.RestSpotSupplementary)
if err != nil {
return "", err
}
var resp UserAccountStream
path := b.API.Endpoints.URL + userAccountStream
path := endpointPath + userAccountStream
headers := make(map[string]string)
headers["X-MBX-APIKEY"] = b.API.Credentials.Key
err := b.SendPayload(context.Background(), &request.Item{
err = b.SendPayload(context.Background(), &request.Item{
Method: http.MethodPost,
Path: path,
Headers: headers,
@@ -888,12 +897,15 @@ func (b *Binance) GetWsAuthStreamKey() (string, error) {
// MaintainWsAuthStreamKey will keep the key alive
func (b *Binance) MaintainWsAuthStreamKey() error {
var err error
endpointPath, err := b.API.Endpoints.GetURL(exchange.RestSpotSupplementary)
if err != nil {
return err
}
if listenKey == "" {
listenKey, err = b.GetWsAuthStreamKey()
return err
}
path := b.API.Endpoints.URL + userAccountStream
path := endpointPath + userAccountStream
params := url.Values{}
params.Set("listenKey", listenKey)
path = common.EncodeURLValues(path, params)

File diff suppressed because it is too large Load Diff

View File

@@ -36,6 +36,6 @@ func TestMain(m *testing.M) {
}
b.setupOrderbookManager()
b.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
log.Printf(sharedtestvalues.LiveTesting, b.Name, b.API.Endpoints.URL)
log.Printf(sharedtestvalues.LiveTesting, b.Name)
os.Exit(m.Run())
}

View File

@@ -45,9 +45,14 @@ func TestMain(m *testing.M) {
if err != nil {
log.Fatalf("Mock server error %s", err)
}
b.HTTPClient = newClient
b.API.Endpoints.URL = serverDetails
log.Printf(sharedtestvalues.MockTesting, b.Name, b.API.Endpoints.URL)
endpointMap := b.API.Endpoints.GetURLMap()
for k := range endpointMap {
err = b.API.Endpoints.SetRunning(k, serverDetails)
if err != nil {
log.Fatal(err)
}
}
log.Printf(sharedtestvalues.MockTesting, b.Name)
os.Exit(m.Run())
}

File diff suppressed because it is too large Load Diff

View File

@@ -19,12 +19,6 @@ const (
Completed
)
// Response holds basic binance api response data
type Response struct {
Code int `json:"code"`
Msg string `json:"msg"`
}
// ExchangeInfo holds the full exchange information type
type ExchangeInfo struct {
Code int `json:"code"`
@@ -93,6 +87,7 @@ type OrderBookData struct {
// OrderBook actual structured data that can be used for orderbook
type OrderBook struct {
Symbol string
LastUpdateID int64
Code int
Msg string
@@ -237,6 +232,18 @@ type AggregatedTrade struct {
BestMatchPrice bool `json:"M"`
}
// IndexMarkPrice stores data for index and mark prices
type IndexMarkPrice struct {
Symbol string `json:"symbol"`
Pair string `json:"pair"`
MarkPrice float64 `json:"markPrice,string"`
IndexPrice float64 `json:"indexPrice,string"`
EstimatedSettlePrice float64 `json:"estimatedSettlePrice,string"`
LastFundingRate string `json:"lastFundingRate"`
NextFundingTime int64 `json:"nextFundingTime"`
Time int64 `json:"time"`
}
// CandleStick holds kline data
type CandleStick struct {
OpenTime time.Time
@@ -749,6 +756,22 @@ type WsPayload struct {
ID int64 `json:"id"`
}
// CrossMarginInterestData stores cross margin data for borrowing
type CrossMarginInterestData struct {
Code int64 `json:"code,string"`
Message string `json:"message"`
MessageDetail string `json:"messageDetail"`
Data []struct {
AssetName string `json:"assetName"`
Specs []struct {
VipLevel string `json:"vipLevel"`
DailyInterestRate string `json:"dailyInterestRate"`
BorrowLimit string `json:"borrowLimit"`
} `json:"specs"`
} `json:"data"`
Success bool `json:"success"`
}
// orderbookManager defines a way of managing and maintaining synchronisation
// across connections and assets.
type orderbookManager struct {

File diff suppressed because it is too large Load Diff

View File

@@ -525,20 +525,22 @@ func (b *Binance) GenerateSubscriptions() ([]stream.ChannelSubscription, error)
var subscriptions []stream.ChannelSubscription
assets := b.GetAssetTypes()
for x := range assets {
pairs, err := b.GetEnabledPairs(assets[x])
if err != nil {
return nil, err
}
if assets[x] == asset.Spot {
pairs, err := b.GetEnabledPairs(assets[x])
if err != nil {
return nil, err
}
for y := range pairs {
for z := range channels {
lp := pairs[y].Lower()
lp.Delimiter = ""
subscriptions = append(subscriptions, stream.ChannelSubscription{
Channel: lp.String() + channels[z],
Currency: pairs[y],
Asset: assets[x],
})
for y := range pairs {
for z := range channels {
lp := pairs[y].Lower()
lp.Delimiter = ""
subscriptions = append(subscriptions, stream.ChannelSubscription{
Channel: lp.String() + channels[z],
Currency: pairs[y],
Asset: assets[x],
})
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,608 @@
package binance
import (
"time"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
)
// Response holds basic binance api response data
type Response struct {
Code int `json:"code"`
Msg string `json:"msg"`
}
// FuturesPublicTradesData stores recent public trades for futures
type FuturesPublicTradesData struct {
ID int64 `json:"id"`
Price float64 `json:"price,string"`
Qty float64 `json:"qty,string"`
QuoteQty float64 `json:"quoteQty,string"`
Time int64 `json:"time"`
IsBuyerMaker bool `json:"isBuyerMaker"`
}
// CompressedTradesData stores futures trades data in a compressed format
type CompressedTradesData struct {
TradeID int64 `json:"a"`
Price float64 `json:"p"`
Quantity float64 `json:"q"`
FirstTradeID int64 `json:"f"`
LastTradeID int64 `json:"l"`
Timestamp int64 `json:"t"`
BuyerMaker bool `json:"b"`
}
// MarkPriceData stores mark price data for futures
type MarkPriceData struct {
Symbol string `json:"symbol"`
MarkPrice float64 `json:"markPrice"`
LastFundingRate float64 `json:"lastFundingRate"`
NextFundingTime int64 `json:"nextFundingTime"`
Time int64 `json:"time"`
}
// SymbolPriceTicker stores ticker price stats
type SymbolPriceTicker struct {
Symbol string `json:"symbol"`
Price float64 `json:"price,string"`
Time int64 `json:"time"`
}
// SymbolOrderBookTicker stores orderbook ticker data
type SymbolOrderBookTicker struct {
Symbol string `json:"symbol"`
BidPrice float64 `json:"bidPrice,string"`
AskPrice float64 `json:"askPrice,string"`
BidQty float64 `json:"bidQty,string"`
AskQty float64 `json:"askQty,string"`
Time int64 `json:"time"`
}
// FuturesCandleStick holds kline data
type FuturesCandleStick struct {
OpenTime time.Time
Open float64
High float64
Low float64
Close float64
Volume float64
CloseTime time.Time
BaseAssetVolume float64
NumberOfTrades int64
TakerBuyVolume float64
TakerBuyBaseAssetVolume float64
}
// AllLiquidationOrders gets all liquidation orders
type AllLiquidationOrders struct {
Symbol string `json:"symbol"`
Price float64 `json:"price,string"`
OrigQty float64 `json:"origQty,string"`
ExecutedQty float64 `json:"executedQty,string"`
AveragePrice float64 `json:"averagePrice,string"`
Status string `json:"status"`
TimeInForce string `json:"timeInForce"`
OrderType string `json:"type"`
Side string `json:"side"`
Time int64 `json:"time"`
}
// OpenInterestData stores open interest data
type OpenInterestData struct {
Symbol string `json:"symbol"`
Pair string `json:"pair"`
OpenInterest float64 `json:"openInterest,string"`
ContractType string `json:"contractType"`
Time int64 `json:"time"`
}
// OpenInterestStats stores stats for open interest data
type OpenInterestStats struct {
Pair string `json:"pair"`
ContractType string `json:"contractType"`
SumOpenInterest float64 `json:"sumOpenInterest,string"`
SumOpenInterestValue float64 `json:"sumOpenInterestValue,string"`
Timestamp int64 `json:"timestamp"`
}
// TopTraderAccountRatio stores account ratio data for top traders
type TopTraderAccountRatio struct {
Pair string `json:"pair"`
LongShortRatio float64 `json:"longShortRatio,string"`
LongAccount float64 `json:"longAccount,string"`
ShortAccount float64 `json:"shortAccount,string"`
Timestamp int64 `json:"timestamp"`
}
// TopTraderPositionRatio stores position ratio for top trader accounts
type TopTraderPositionRatio struct {
Pair string `json:"pair"`
LongShortRatio float64 `json:"longShortRatio,string"`
LongPosition float64 `json:"longPosition,string"`
ShortPosition float64 `json:"shortPosition,string"`
Timestamp int64 `json:"timestamp"`
}
// GlobalLongShortRatio stores ratio data of all longs vs shorts
type GlobalLongShortRatio struct {
Symbol string `json:"symbol"`
LongShortRatio float64 `json:"longShortRatio"`
LongAccount float64 `json:"longAccount"`
ShortAccount float64 `json:"shortAccount"`
Timestamp string `json:"timestamp"`
}
// TakerBuySellVolume stores taker buy sell volume
type TakerBuySellVolume struct {
Pair string `json:"pair"`
ContractType string `json:"contractType"`
TakerBuyVolume float64 `json:"takerBuyVol,string"`
BuySellRatio float64 `json:"takerSellVol,string"`
BuyVol float64 `json:"takerBuyVolValue,string"`
SellVol float64 `json:"takerSellVolValue,string"`
Timestamp int64 `json:"timestamp"`
}
// FuturesBasisData gets futures basis data
type FuturesBasisData struct {
Pair string `json:"pair"`
ContractType string `json:"contractType"`
FuturesPrice float64 `json:"futuresPrice,string"`
IndexPrice float64 `json:"indexPrice,string"`
Basis float64 `json:"basis,string"`
BasisRate float64 `json:"basisRate,string"`
Timestamp int64 `json:"timestamp"`
}
// PlaceBatchOrderData stores batch order data for placing
type PlaceBatchOrderData struct {
Symbol string `json:"symbol"`
Side string `json:"side"`
PositionSide string `json:"positionSide,omitempty"`
OrderType string `json:"type"`
TimeInForce string `json:"timeInForce,omitempty"`
Quantity float64 `json:"quantity"`
ReduceOnly string `json:"reduceOnly,omitempty"`
Price float64 `json:"price"`
NewClientOrderID string `json:"newClientOrderId,omitempty"`
StopPrice float64 `json:"stopPrice,omitempty"`
ActivationPrice float64 `json:"activationPrice,omitempty"`
CallbackRate float64 `json:"callbackRate,omitempty"`
WorkingType string `json:"workingType,omitempty"`
PriceProtect string `json:"priceProtect,omitempty"`
NewOrderRespType string `json:"newOrderRespType,omitempty"`
}
// BatchCancelOrderData stores batch cancel order data
type BatchCancelOrderData struct {
ClientOrderID string `json:"clientOrderID"`
CumQty float64 `json:"cumQty,string"`
CumBase float64 `json:"cumBase,string"`
ExecuteQty float64 `json:"executeQty,string"`
OrderID int64 `json:"orderID,string"`
AvgPrice float64 `json:"avgPrice,string"`
OrigQty float64 `json:"origQty,string"`
Price float64 `json:"price,string"`
ReduceOnly bool `json:"reduceOnly"`
Side string `json:"side"`
PositionSide string `json:"positionSide"`
Status string `json:"status"`
StopPrice int64 `json:"stopPrice"`
ClosePosition bool `json:"closePosition"`
Symbol string `json:"symbol"`
Pair string `json:"pair"`
TimeInForce string `json:"TimeInForce"`
OrderType string `json:"type"`
OrigType string `json:"origType"`
ActivatePrice float64 `json:"activatePrice,string"`
PriceRate float64 `json:"priceRate,string"`
UpdateTime int64 `json:"updateTime"`
WorkingType string `json:"workingType"`
PriceProtect bool `json:"priceProtect"`
Code int64 `json:"code"`
Msg string `json:"msg"`
}
// FuturesOrderPlaceData stores futures order data
type FuturesOrderPlaceData struct {
ClientOrderID string `json:"clientOrderID"`
CumQty float64 `json:"cumQty,string"`
CumBase float64 `json:"cumBase,string"`
ExecuteQty float64 `json:"executeQty,string"`
OrderID int64 `json:"orderID,string"`
AvgPrice float64 `json:"avgPrice,string"`
OrigQty float64 `json:"origQty,string"`
Price float64 `json:"price,string"`
ReduceOnly bool `json:"reduceOnly"`
Side string `json:"side"`
PositionSide string `json:"positionSide"`
Status string `json:"status"`
StopPrice int64 `json:"stopPrice"`
ClosePosition bool `json:"closePosition"`
Symbol string `json:"symbol"`
Pair string `json:"pair"`
TimeInForce string `json:"TimeInForce"`
OrderType string `json:"type"`
OrigType string `json:"origType"`
ActivatePrice float64 `json:"activatePrice,string"`
PriceRate float64 `json:"priceRate,string"`
UpdateTime int64 `json:"updateTime"`
WorkingType string `json:"workingType"`
PriceProtect bool `json:"priceProtect"`
}
// FuturesOrderGetData stores futures order data for get requests
type FuturesOrderGetData struct {
AvgPrice float64 `json:"avgPrice,string"`
ClientOrderID string `json:"clientOrderID"`
CumQty float64 `json:"cumQty,string"`
CumBase float64 `json:"cumBase,string"`
ExecutedQty float64 `json:"executedQty,string"`
OrderID int64 `json:"orderId"`
OrigQty float64 `json:"origQty,string"`
OrigType string `json:"origType"`
Price float64 `json:"price,string"`
ReduceOnly bool `json:"reduceOnly"`
Side string `json:"buy"`
PositionSide string `json:"positionSide"`
Status string `json:"status"`
StopPrice float64 `json:"stopPrice,string"`
ClosePosition bool `json:"closePosition"`
Symbol string `json:"symbol"`
Pair string `json:"pair"`
TimeInForce string `json:"timeInForce"`
OrderType string `json:"type"`
ActivatePrice float64 `json:"activatePrice,string"`
PriceRate float64 `json:"priceRate,string"`
UpdateTime int64 `json:"updateTime"`
WorkingType string `json:"workingType"`
PriceProtect bool `json:"priceProtect"`
}
// FuturesOrderData stores order data for futures
type FuturesOrderData struct {
AvgPrice float64 `json:"avgPrice,string"`
ClientOrderID string `json:"clientOrderId"`
CumBase string `json:"cumBase"`
ExecutedQty float64 `json:"executedQty,string"`
OrderID int64 `json:"orderId"`
OrigQty float64 `json:"origQty,string"`
OrigType string `json:"origType"`
Price float64 `json:"price,string"`
ReduceOnly bool `json:"reduceOnly"`
Side string `json:"side"`
PositionSide string `json:"positionSide"`
Status string `json:"status"`
StopPrice float64 `json:"stopPrice,string"`
ClosePosition bool `json:"closePosition"`
Symbol string `json:"symbol"`
Pair string `json:"pair"`
Time int64 `json:"time"`
TimeInForce string `json:"timeInForce"`
OrderType string `json:"type"`
ActivatePrice float64 `json:"activatePrice,string"`
PriceRate float64 `json:"priceRate,string"`
UpdateTime int64 `json:"updateTime"`
WorkingType string `json:"workingType"`
PriceProtect bool `json:"priceProtect"`
}
// OrderVars stores side, status and type for any order/trade
type OrderVars struct {
Side order.Side
Status order.Status
OrderType order.Type
Fee float64
}
// AutoCancelAllOrdersData gives data of auto cancelling all open orders
type AutoCancelAllOrdersData struct {
Symbol string `json:"symbol"`
CountdownTime int64 `json:"countdownTime,string"`
}
// LevelDetail stores level detail data
type LevelDetail struct {
Level string `json:"level"`
MaxBorrowable float64 `json:"maxBorrowable,string"`
InterestRate float64 `json:"interestRate,string"`
}
// MarginInfoData stores margin info data
type MarginInfoData struct {
Data []struct {
MarginRatio string `json:"marginRatio"`
Base struct {
AssetName string `json:"assetName"`
LevelDetails []LevelDetail `json:"levelDetails"`
} `json:"base"`
Quote struct {
AssetName string `json:"assetName"`
LevelDetails []LevelDetail `json:"levelDetails"`
} `json:"quote"`
} `json:"data"`
}
// FuturesAccountBalanceData stores account balance data for futures
type FuturesAccountBalanceData struct {
AccountAlias string `json:"accountAlias"`
Asset string `json:"asset"`
Balance float64 `json:"balance,string"`
WithdrawAvailable float64 `json:"withdrawAvailable,string"`
CrossWalletBalance float64 `json:"crossWalletBalance,string"`
CrossUnPNL float64 `json:"crossUnPNL,string"`
AvailableBalance float64 `json:"availableBalance,string"`
UpdateTime int64 `json:"updateTime"`
}
// FuturesAccountInformation stores account information for futures account
type FuturesAccountInformation struct {
Assets []struct {
Asset string `json:"asset"`
WalletBalance float64 `json:"walletBalance,string"`
UnrealizedProfit float64 `json:"unrealizedProfit,string"`
MarginBalance float64 `json:"marginBalance,string"`
MaintMargin float64 `json:"maintMargin,string"`
InitialMargin float64 `json:"initialMargin,string"`
PositionInitialMargin float64 `json:"positionInitialMargin,string"`
OpenOrderInitialMargin float64 `json:"openOrderInitialMargin,string"`
Leverage float64 `json:"leverage,string"`
Isolated bool `json:"isolated"`
PositionSide string `json:"positionSide"`
EntryPrice float64 `json:"entryPrice,string"`
MaxQty float64 `json:"maxQty,string"`
} `json:"assets"`
Positions []struct {
Symbol string `json:"symbol"`
InitialMargin float64 `json:"initialMargin,string"`
MaintMargin float64 `json:"maintMargin,string"`
UnrealizedProfit float64 `json:"unrealizedProfit,string"`
PositionInitialMargin float64 `json:"positionInitialMargin,string"`
OpenOrderInitialMargin float64 `json:"openOrderInitialMargin,string"`
Leverage float64 `json:"leverage,string"`
Isolated bool `json:"isolated"`
PositionSide string `json:"positionSide"`
EntryPrice float64 `json:"entryPrice,string"`
MaxQty float64 `json:"maxQty,string"`
} `json:"positions"`
CanDeposit bool `json:"canDeposit"`
CanTrade bool `json:"canTrade"`
CanWithdraw bool `json:"canWithdraw"`
FeeTier int64 `json:"feeTier"`
UpdateTime int64 `json:"updateTime"`
}
// GenericAuthResponse is a general data response for a post auth request
type GenericAuthResponse struct {
Code int64 `json:"code"`
Msg string `json:"msg"`
}
// FuturesLeverageData stores leverage data for futures
type FuturesLeverageData struct {
Leverage int64 `json:"leverage"`
MaxQty float64 `json:"maxQty,string"`
Symbol string `json:"symbol"`
}
// ModifyIsolatedMarginData stores margin modification data
type ModifyIsolatedMarginData struct {
Amount float64 `json:"amount"`
Code int64 `json:"code"`
Msg string `json:"msg"`
ModType string `json:"modType"`
}
// GetPositionMarginChangeHistoryData gets margin change history for positions
type GetPositionMarginChangeHistoryData struct {
Amount float64 `json:"amount"`
Asset string `json:"asset"`
Symbol string `json:"symbol"`
Timestamp int64 `json:"time"`
MarginChangeType int64 `json:"type"`
PositionSide string `json:"positionSide"`
}
// FuturesPositionInformation stores futures position info
type FuturesPositionInformation struct {
Symbol string `json:"symbol"`
PositionAmount float64 `json:"positionAmt,string"`
EntryPrice float64 `json:"entryPrice,string"`
MarkPrice float64 `json:"markPrice,string"`
UnrealizedProfit float64 `json:"unRealizedProfit,string"`
LiquidationPrice float64 `json:"liquidation,string"`
Leverage int64 `json:"leverage"`
MaxQty float64 `json:"maxQty"`
MarginType string `json:"marginType"`
IsolatedMargin float64 `json:"isolatedMargin,string"`
IsAutoAddMargin bool `json:"isAutoAddMargin"`
PositionSide string `json:"positionSide"`
}
// FuturesAccountTradeList stores account trade list data
type FuturesAccountTradeList struct {
Symbol string `json:"symbol"`
ID int64 `json:"id"`
OrderID int64 `json:"orderID"`
Pair string `json:"pair"`
Side string `json:"side"`
Price string `json:"price"`
Qty float64 `json:"qty"`
RealizedPNL float64 `json:"realizedPNL"`
MarginAsset string `json:"marginAsset"`
BaseQty float64 `json:"baseQty"`
Commission float64 `json:"commission"`
CommissionAsset string `json:"commissionAsset"`
Timestamp int64 `json:"timestamp"`
PositionSide string `json:"positionSide"`
Buyer bool `json:"buyer"`
Maker bool `json:"maker"`
}
// FuturesIncomeHistoryData stores futures income history data
type FuturesIncomeHistoryData struct {
Symbol string `json:"symbol"`
IncomeType string `json:"incomeType"`
Income float64 `json:"income,string"`
Asset string `json:"asset"`
Info string `json:"info"`
Timestamp int64 `json:"time"`
}
// NotionalBracketData stores notional bracket data
type NotionalBracketData struct {
Pair string `json:"pair"`
Brackets []struct {
Bracket int64 `json:"bracket"`
InitialLeverage float64 `json:"initialLeverage"`
QtyCap float64 `json:"qtyCap"`
QtylFloor float64 `json:"qtyFloor"`
MaintMarginRatio float64 `json:"maintMarginRatio"`
}
}
// ForcedOrdersData stores forced orders data
type ForcedOrdersData struct {
OrderID int64 `json:"orderId"`
Symbol string `json:"symbol"`
Status string `json:"status"`
ClientOrderID string `json:"clientOrderId"`
Price float64 `json:"price,string"`
AvgPrice float64 `json:"avgPrice,string"`
OrigQty float64 `json:"origQty,string"`
ExecutedQty float64 `json:"executedQty,string"`
CumQuote float64 `json:"cumQuote,string"`
TimeInForce string `json:"timeInForce"`
OrderType string `json:"orderType"`
ReduceOnly bool `json:"reduceOnly"`
ClosePosition bool `json:"closePosition"`
Side string `json:"side"`
PositionSide string `json:"positionSide"`
StopPrice float64 `json:"stopPrice,string"`
WorkingType string `json:"workingType"`
PriceProtect float64 `json:"priceProtect,string"`
OrigType string `json:"origType"`
Time int64 `json:"time"`
UpdateTime int64 `json:"updateTime"`
}
// ADLEstimateData stores data for ADL estimates
type ADLEstimateData struct {
Symbol string `json:"symbol"`
ADLQuantile struct {
Long float64 `json:"LONG"`
Short float64 `json:"SHORT"`
Hedge float64 `json:"HEDGE"`
} `json:"adlQuantile"`
}
// InterestHistoryData gets interest history data
type InterestHistoryData struct {
Asset string `json:"asset"`
Interest float64 `json:"interest"`
LendingType string `json:"lendingType"`
ProductName string `json:"productName"`
Time string `json:"time"`
}
// FundingRateData stores funding rates data
type FundingRateData struct {
Symbol string `json:"symbol"`
FundingRate float64 `json:"fundingRate,string"`
FundingTime int64 `json:"fundingTime"`
}
// SymbolsData stores perp futures' symbols
type SymbolsData struct {
Symbol string `json:"symbol"`
}
// PerpsExchangeInfo stores data for perps
type PerpsExchangeInfo struct {
Symbols []SymbolsData `json:"symbols"`
}
// UFuturesExchangeInfo stores exchange info for ufutures
type UFuturesExchangeInfo struct {
RateLimits []struct {
Interval string `json:"interval"`
IntervalNum int64 `json:"intervalNum"`
Limit int64 `json:"limit"`
RateLimitType string `json:"rateLimitType"`
} `json:"rateLimits"`
ServerTime int64 `json:"serverTime"`
Symbols []struct {
Symbol string `json:"symbol"`
Status string `json:"status"`
MaintenanceMarginPercent float64 `json:"maintMarginPercent,string"`
RequiredMarginPercent float64 `json:"requiredMarginPercent,string"`
BaseAsset string `json:"baseAsset"`
QuoteAsset string `json:"quoteAsset"`
PricePrecision int64 `json:"pricePrecision"`
QuantityPrecision int64 `json:"quantityPrecision"`
BaseAssetPrecision int64 `json:"baseAssetPrecision"`
QuotePrecision int64 `json:"quotePrecision"`
Filters []struct {
MinPrice float64 `json:"minPrice,string"`
MaxPrice float64 `json:"maxPrice,string"`
FilterType string `json:"filterType"`
TickSize float64 `json:"tickSize,string"`
StepSize float64 `json:"stepSize,string"`
MaxQty float64 `json:"maxQty,string"`
MinQty float64 `json:"minQty,string"`
Limit int64 `json:"limit"`
MultiplierDown float64 `json:"multiplierDown,string"`
MultiplierUp float64 `json:"multiplierUp,string"`
MultiplierDecimal float64 `json:"multiplierDecimal,string"`
} `json:"filters"`
OrderTypes []string `json:"orderTypes"`
TimeInForce []string `json:"timeInForce"`
} `json:"symbols"`
Timezone string `json:"timezone"`
}
// CExchangeInfo stores exchange info for cfutures
type CExchangeInfo struct {
ExchangeFilters []interface{} `json:"exchangeFilters"`
RateLimits []struct {
Interval string `json:"interval"`
IntervalNum int64 `json:"intervalNul"`
Limit int64 `json:"limit"`
RateLimitType string `json:"rateLimitType"`
} `json:"rateLimits"`
ServerTime int64 `json:"serverTime"`
Symbols []struct {
Filters []struct {
FilterType string `json:"filterType"`
MinPrice float64 `json:"minPrice,string"`
MaxPrice float64 `json:"maxPrice,string"`
StepSize float64 `json:"stepSize,string"`
MaxQty float64 `json:"maxQty,string"`
MinQty float64 `json:"minQty,string"`
Limit int64 `json:"limit"`
MultiplierDown float64 `json:"multiplierDown,string"`
MultiplierUp float64 `json:"multiplierUp,string"`
MultiplierDecimal float64 `json:"multiplierDecimal,string"`
} `json:"filters"`
OrderTypes []string `json:"orderType"`
TimeInForce []string `json:"timeInForce"`
Symbol string `json:"symbol"`
Pair string `json:"pair"`
ContractType string `json:"contractType"`
DeliveryDate int64 `json:"deliveryDate"`
OnboardDate int64 `json:"onboardDate"`
ContractStatus string `json:"contractStatus"`
ContractSize int64 `json:"contractSize"`
QuoteAsset string `json:"quoteAsset"`
BaseAsset string `json:"baseAsset"`
MarginAsset string `json:"marginAsset"`
PricePrecision int64 `json:"pricePrecision"`
QuantityPrecision int64 `json:"quantityPrecision"`
BaseAssetPrecision int64 `json:"baseAssetPrecision"`
QuotePrecision int64 `json:"quotePrecision"`
MaintMarginPercent float64 `json:"maintMarginPercent,string"`
RequiredMarginPercent float64 `json:"requiredMarginPercent,string"`
} `json:"symbols"`
Timezone string `json:"timezone"`
}

View File

@@ -6,8 +6,6 @@ import (
"time"
"github.com/thrasher-corp/gocryptotrader/common/convert"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
)
// binanceTime provides an internal conversion helper
@@ -346,13 +344,3 @@ func (a *wsListStatus) UnmarshalJSON(data []byte) error {
a.Data.TransactionTime = aux.Data.TransactionTime.Time()
return nil
}
// formatSymbol formats the given pair to a string suitable for exchange API requests
// currently applicable to Spot and Margin assets
func (b *Binance) formatSymbol(pair currency.Pair) (string, error) {
pairFmt, err := b.GetPairFormat(asset.Spot, true)
if err != nil {
return pair.String(), err
}
return pairFmt.Format(pair), nil
}

View File

@@ -0,0 +1,411 @@
package binance
var (
validFuturesIntervals = []string{
"1m", "3m", "5m", "15m", "30m",
"1h", "2h", "4h", "6h", "8h",
"12h", "1d", "3d", "1w", "1M",
}
validContractType = []string{
"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"}
validPositionSide = []string{"BOTH", "LONG", "SHORT"}
validMarginType = []string{"ISOLATED", "CROSSED"}
validIncomeType = []string{"TRANSFER", "WELCOME_BONUS", "REALIZED_PNL", "FUNDING_FEE", "COMMISSION", "INSURANCE_CLEAR"}
validAutoCloseTypes = []string{"LIQUIDATION", "ADL"}
validMarginChange = map[string]int64{
"add": 1,
"reduce": 2,
}
uValidOBLimits = []string{"5", "10", "20", "50", "100", "500", "1000"}
uValidPeriods = []string{"5m", "15m", "30m", "1h", "2h", "4h", "6h", "12h", "1d"}
)
// USDT Margined Futures
// OrderbookData stores ob data for umargined and cmargined futures
type OrderbookData struct {
LastUpdateID int64 `json:"lastUpdateID"`
Timestamp int64 `json:"T"`
Bids [][2]string `json:"bids"`
Asks [][2]string `json:"asks"`
}
// UPublicTradesData stores trade data
type UPublicTradesData struct {
ID int64 `json:"id"`
Price float64 `json:"price,string"`
Qty float64 `json:"qty,string"`
QuoteQty float64 `json:"quoteQty,string"`
Time int64 `json:"time"`
IsBuyerMaker bool `json:"isBuyerMaker"`
}
// UCompressedTradeData stores compressed trade data
type UCompressedTradeData struct {
AggregateTradeID int64 `json:"a"`
Price float64 `json:"p,string"`
Quantity float64 `json:"q,string"`
FirstTradeID int64 `json:"f"`
LastTradeID int64 `json:"l"`
Timestamp int64 `json:"t"`
IsBuyerMaker bool `json:"m"`
}
// UMarkPrice stores mark price data
type UMarkPrice struct {
Symbol string `json:"symbol"`
MarkPrice float64 `json:"markPrice,string"`
IndexPrice float64 `json:"indexPrice,string"`
LastFundingRate float64 `json:"lastFundingRate,string"`
NextFundingTime int64 `json:"nextFundingTime"`
Time int64 `json:"time"`
}
// FundingRateHistory stores funding rate history
type FundingRateHistory struct {
Symbol string `json:"symbol"`
FundingRate float64 `json:"fundingRate,string"`
FundingTime int64 `json:"fundingTime"`
}
// U24HrPriceChangeStats stores price change stats data
type U24HrPriceChangeStats struct {
Symbol string `json:"symbol"`
PriceChange float64 `json:"priceChange,string"`
PriceChangePercent float64 `json:"priceChangePercent,string"`
WeightedAvgPrice float64 `json:"weightedAvgPrice,string"`
PrevClosePrice float64 `json:"prevClosePrice,string"`
LastPrice float64 `json:"lastPrice,string"`
LastQty float64 `json:"lastQty,string"`
OpenPrice float64 `json:"openPrice,string"`
HighPrice float64 `json:"highPrice,string"`
LowPrice float64 `json:"lowPrice,string"`
Volume float64 `json:"volume,string"`
QuoteVolume float64 `json:"quoteVolume,string"`
OpenTime int64 `json:"openTime"`
CloseTime int64 `json:"closeTime"`
FirstID int64 `json:"firstId"`
LastID int64 `json:"lastId"`
Count int64 `json:"count"`
}
// USymbolPriceTicker stores symbol price ticker data
type USymbolPriceTicker struct {
Symbol string `json:"symbol"`
Price float64 `json:"price,string"`
Time int64 `json:"time"`
}
// USymbolOrderbookTicker stores symbol orderbook ticker data
type USymbolOrderbookTicker struct {
Symbol string `json:"symbol"`
BidPrice float64 `json:"bidPrice,string"`
BidQty float64 `json:"bidQty,string"`
AskPrice float64 `json:"askPrice,string"`
AskQty float64 `json:"askQty,string"`
Time int64 `json:"time"`
}
// ULiquidationOrdersData stores liquidation orders data
type ULiquidationOrdersData struct {
Symbol string `json:"symbol"`
Price float64 `json:"price,string"`
OrigQty float64 `json:"origQty,string"`
ExecutedQty float64 `json:"executedQty,string"`
AveragePrice float64 `json:"averagePrice,string"`
Status string `json:"status"`
TimeInForce string `json:"timeInForce"`
OrderType string `json:"type"`
Side string `json:"side"`
Time int64 `json:"time"`
}
// UOpenInterestData stores open interest data
type UOpenInterestData struct {
OpenInterest float64 `json:"openInterest,string"`
Symbol string `json:"symbol"`
Time int64 `json:"time"`
}
// UOpenInterestStats stores open interest stats data
type UOpenInterestStats struct {
Symbol string `json:"symbol"`
SumOpenInterest float64 `json:"sumOpenInterest,string"`
SumOpenInterestValue float64 `json:"sumOpenInterestValue,string"`
Timestamp int64 `json:"timestamp"`
}
// ULongShortRatio stores top trader accounts' or positions' or global long/short ratio data
type ULongShortRatio struct {
Symbol string `json:"symbol"`
LongShortRatio float64 `json:"longShortRatio,string"`
LongAccount float64 `json:"longAccount,string"`
ShortAccount float64 `json:"shortAccount,string"`
Timestamp int64 `json:"timestamp"`
}
// UTakerVolumeData stores volume data on buy/sell side from takers
type UTakerVolumeData struct {
BuySellRatio float64 `json:"buySellRatio,string"`
BuyVol float64 `json:"buyVol,string"`
SellVol float64 `json:"sellVol,string"`
Timestamp int64 `json:"timestamp"`
}
// UCompositeIndexInfoData stores composite index data for usdt margined futures
type UCompositeIndexInfoData struct {
Symbol string `json:"symbol"`
Time int64 `json:"time"`
BaseAssetList []struct {
BaseAsset string `json:"baseAsset"`
WeightInQuantity float64 `json:"weightInQuantity,string"`
WeightInPercentage float64 `json:"weightInPercentage,string"`
} `json:"baseAssetList"`
}
// UOrderData stores order data
type UOrderData struct {
ClientOrderID string `json:"clientOrderId"`
CumQty float64 `json:"cumQty,string"`
CumQuote float64 `json:"cumQuote,string"`
ExecutedQty float64 `json:"executedQty,string"`
OrderID int64 `json:"orderId"`
AvgPrice float64 `json:"avgPrice,string"`
OrigQty float64 `json:"origQty,string"`
Price float64 `json:"price,string"`
ReduceOnly bool `json:"reduceOnly"`
Side string `json:"side"`
PositionSide string `json:"positionSide"`
Status string `json:"status"`
StopPrice float64 `json:"stopPrice,string"`
ClosePosition bool `json:"closePosition"`
Symbol string `json:"symbol"`
TimeInForce string `json:"timeInForce"`
OrderType string `json:"type"`
OrigType string `json:"origType"`
ActivatePrice float64 `json:"activatePrice,string"`
PriceRate float64 `json:"priceRate,string"`
UpdateTime int64 `json:"updateTime"`
WorkingType string `json:"workingType"`
Code int64 `json:"code"`
Msg string `json:"msg"`
}
// UFuturesOrderData stores order data for ufutures
type UFuturesOrderData struct {
AvgPrice float64 `json:"avgPrice,string"`
ClientOrderID string `json:"clientOrderId"`
CumQuote string `json:"cumQuote"`
ExecutedQty float64 `json:"executedQty,string"`
OrderID int64 `json:"orderId"`
OrigQty float64 `json:"origQty,string"`
OrigType string `json:"origType"`
Price float64 `json:"price,string"`
ReduceOnly bool `json:"reduceOnly"`
Side string `json:"side"`
PositionSide string `json:"positionSide"`
Status string `json:"status"`
StopPrice float64 `json:"stopPrice,string"`
ClosePosition bool `json:"closePosition"`
Symbol string `json:"symbol"`
Time int64 `json:"time"`
TimeInForce string `json:"timeInForce"`
OrderType string `json:"type"`
ActivatePrice float64 `json:"activatePrice,string"`
PriceRate float64 `json:"priceRate,string"`
UpdateTime int64 `json:"updateTime"`
WorkingType string `json:"workingType"`
}
// UAccountBalanceV2Data stores account balance data for ufutures
type UAccountBalanceV2Data struct {
AccountAlias string `json:"accountAlias"`
Asset string `json:"asset"`
Balance float64 `json:"balance,string"`
CrossWalletBalance float64 `json:"crossWalletBalance,string"`
CrossUnrealizedPNL float64 `json:"crossUnPnl,string"`
AvailableBalance float64 `json:"availableBalance,string"`
MaxWithdrawAmount float64 `json:"maxWithdrawAmount,string"`
}
// UAccountInformationV2Data stores account info for ufutures
type UAccountInformationV2Data struct {
FeeTier int64 `json:"feeTier"`
CanTrade bool `json:"canTrade"`
CanDeposit bool `json:"canDeposit"`
CanWithdraw bool `json:"canWithdraw"`
UpdateTime int64 `json:"updateTime"`
TotalInitialMargin float64 `json:"totalInitialMargin,string"`
TotalMaintenance float64 `json:"totalMaintMargin,string"`
TotalWalletBalance float64 `json:"totalWalletBalance,string"`
TotalUnrealizedProfit float64 `json:"totalUnrealizedProfit,string"`
TotalMarginBalance float64 `json:"totalMarginBalance,string"`
TotalPositionInitialMargin float64 `json:"totalPositionInitialMargin,string"`
TotalOpenOrderInitialMargin float64 `json:"totalOpenOrderInitialMargin,string"`
TotalCrossWalletBalance float64 `json:"totalCrossWalletBalance,string"`
TotalCrossUnrealizedPNL float64 `json:"totalCrossUnPnl,string"`
AvailableBalance float64 `json:"availableBalance,string"`
MaxWithdrawAmount float64 `json:"maxWithdrawAmount,string"`
Assets []struct {
Asset string `json:"asset"`
WalletBalance float64 `json:"walletBalance,string"`
UnrealizedProfit float64 `json:"unrealizedProfit,string"`
MarginBalance float64 `json:"marginBalance,string"`
MaintMargin float64 `json:"maintMargin,string"`
InitialMargin float64 `json:"initialMargin,string"`
PositionInitialMargin float64 `json:"positionInitialMargin,string"`
OpenOrderInitialMargin float64 `json:"openOrderInitialMargin,string"`
CrossWalletBalance float64 `json:"crossWalletBalance,string"`
CrossUnPnl float64 `json:"crossUnPnl,string"`
AvailableBalance float64 `json:"availableBalance,string"`
MaxWithdrawAmount float64 `json:"maxWithdrawAmount,string"`
} `json:"assets"`
Positions []struct {
Symbol string `json:"symbol"`
InitialMargin float64 `json:"initialMargin,string"`
MaintenanceMargin float64 `json:"maintMargin,string"`
UnrealizedProfit float64 `json:"unrealizedProfit,string"`
PositionInitialMargin float64 `json:"positionInitialMargin,string"`
OpenOrderInitialMargin float64 `json:"openOrderInitialMargin,string"`
Leverage float64 `json:"leverage,string"`
Isolated bool `json:"isolated"`
EntryPrice float64 `json:"entryPrice,string"`
MaxNotional float64 `json:"maxNotional,string"`
PositionSide string `json:"positionSide"`
} `json:"positions"`
}
// UChangeInitialLeverage stores leverage change data
type UChangeInitialLeverage struct {
Leverage int64 `json:"leverage"`
MaxNotionalValue float64 `json:"maxNotionalValue,string"`
Symbol string `json:"symbol"`
}
// UModifyIsolatedPosMargin stores modified isolated margin positions' data
type UModifyIsolatedPosMargin struct {
Amount float64 `json:"amount,string"`
MarginType int64 `json:"type"`
}
// UPositionMarginChangeHistoryData gets position margin change history data
type UPositionMarginChangeHistoryData struct {
Amount float64 `json:"amount,string"`
Asset string `json:"asset"`
Symbol string `json:"symbol"`
Time int64 `json:"time"`
MarginType int64 `json:"type"`
PositionSide string `json:"positionSide"`
}
// UPositionInformationV2 stores positions data
type UPositionInformationV2 struct {
EntryPrice float64 `json:"entryPrice,string"`
MarginType string `json:"marginType"`
AutoAddMarginEnabled bool `json:"isAutoAddMargin,string"`
IsolatedMargin float64 `json:"isolatedMargin,string"`
Leverage float64 `json:"leverage,string"`
LiquidationPrice float64 `json:"liquidationPrice,string"`
MarkPrice float64 `json:"markPrice,string"`
MaxNotionalValue float64 `json:"maxNotionalValue,string"`
PositionAmount float64 `json:"positionAmt,string"`
Symbol string `json:"symbol"`
UnrealizedProfit float64 `json:"unrealizedProfit,string"`
PositionSide string `json:"positionSide"`
}
// UAccountTradeHistory stores trade data for the users account
type UAccountTradeHistory struct {
Buyer bool `json:"buyer"`
Commission float64 `json:"commission,string"`
CommissionAsset string `json:"commissionAsset"`
ID int64 `json:"id"`
Maker bool `json:"maker"`
OrderID int64 `json:"orderId"`
Price float64 `json:"price,string"`
Qty float64 `json:"qty,string"`
QuoteQty float64 `json:"quoteQty"`
RealizedPNL float64 `json:"realizedPnl,string"`
Side string `json:"side"`
PositionSide string `json:"positionSide"`
Symbol string `json:"symbol"`
Time int64 `json:"time"`
}
// UAccountIncomeHistory stores income history data
type UAccountIncomeHistory struct {
Symbol string `json:"symbol"`
IncomeType string `json:"incomeType"`
Income float64 `json:"income,string"`
Asset string `json:"asset"`
Info string `json:"info"`
Time int64 `json:"time"`
TranID int64 `json:"tranId"`
TradeID string `json:"tradeId"`
}
// UNotionalLeverageAndBrakcetsData stores notional and leverage brackets data for the account
type UNotionalLeverageAndBrakcetsData struct {
Symbol string `json:"symbol"`
Brackets []struct {
Bracket int64 `json:"bracket"`
InitialLeverage float64 `json:"initialLeverage"`
NotionalCap float64 `json:"notionalCap"`
NotionalFloor float64 `json:"notionalFloor"`
MaintenanceMarginRatio float64 `json:"maintMarginRatio"`
Cumulative float64 `json:"cum"`
} `json:"brackets"`
}
// UPositionADLEstimationData stores ADL estimation data for a position
type UPositionADLEstimationData struct {
Symbol string `json:"symbol"`
ADLQuantile struct {
Long int64 `json:"LONG"`
Short int64 `json:"SHORT"`
Hedge int64 `json:"HEDGE"`
} `json:"adlQuantile"`
}
// UForceOrdersData stores liquidation orders data for the account
type UForceOrdersData struct {
OrderID int64 `json:"orderId"`
Symbol string `json:"symbol"`
Status string `json:"status"`
ClientOrderID string `json:"clientOrderId"`
Price float64 `json:"price,string"`
AvgPrice float64 `json:"avgPrice,string"`
OrigQty float64 `json:"origQty,string"`
ExecutedQty float64 `json:"executedQty,string"`
CumQuote float64 `json:"cumQuote,string"`
TimeInForce string `json:"timeInForce"`
OrderType string `json:"type"`
ReduceOnly bool `json:"reduceOnly"`
ClosePosition bool `json:"closePosition"`
Side string `json:"side"`
PositionSide string `json:"positionSide"`
StopPrice float64 `json:"stopPrice,string"`
WorkingType string `json:"workingType"`
PriceProtect bool `json:"priceProtect,string"`
OrigType string `json:"origType"`
Time int64 `json:"time"`
UpdateTime int64 `json:"updateTime"`
}

View File

@@ -1,10 +1,12 @@
package bitfinex
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
@@ -60,17 +62,23 @@ const (
bitfinexLeaderboard = "rankings"
// Version 2 API endpoints
bitfinexAPIVersion2 = "/v2/"
bitfinexPlatformStatus = "platform/status"
bitfinexTickerBatch = "tickers"
bitfinexTicker = "ticker/"
bitfinexTrades = "trades/"
bitfinexOrderbook = "book/"
bitfinexStatistics = "stats1/"
bitfinexCandles = "candles/trade"
bitfinexKeyPermissions = "key_info"
bitfinexMarginInfo = "margin_infos"
bitfinexDepositMethod = "conf/pub:map:currency:label"
bitfinexAPIVersion2 = "/v2/"
bitfinexV2MarginFunding = "calc/trade/avg?"
bitfinexV2Balances = "auth/r/wallets"
bitfinexV2AccountInfo = "auth/r/info/user"
bitfinexV2FundingInfo = "auth/r/info/funding/%s"
bitfinexDerivativeData = "status/deriv?"
bitfinexPlatformStatus = "platform/status"
bitfinexTickerBatch = "tickers"
bitfinexTicker = "ticker/"
bitfinexTrades = "trades/"
bitfinexOrderbook = "book/"
bitfinexStatistics = "stats1/"
bitfinexCandles = "candles/trade"
bitfinexKeyPermissions = "key_info"
bitfinexMarginInfo = "margin_infos"
bitfinexDepositMethod = "conf/pub:map:currency:label"
bitfinexMarginPairs = "conf/pub:list:pair:margin"
// Bitfinex platform status values
// When the platform is marked in maintenance mode bots should stop trading
@@ -91,9 +99,9 @@ type Bitfinex struct {
// GetPlatformStatus returns the Bifinex platform status
func (b *Bitfinex) GetPlatformStatus() (int, error) {
var response []int
err := b.SendHTTPRequest(b.API.Endpoints.URL+
err := b.SendHTTPRequest(exchange.RestSpot,
bitfinexAPIVersion2+
bitfinexPlatformStatus,
bitfinexPlatformStatus,
&response,
platformStatus)
if err != nil {
@@ -110,16 +118,271 @@ func (b *Bitfinex) GetPlatformStatus() (int, error) {
return -1, fmt.Errorf("unexpected platform status value %d", response[0])
}
// GetV2MarginFunding gets borrowing rates for margin trading
func (b *Bitfinex) GetV2MarginFunding(symbol, amount string, period int32) (MarginV2FundingData, error) {
var resp []interface{}
var response MarginV2FundingData
params := make(map[string]interface{})
params["symbol"] = symbol
params["period"] = period
params["amount"] = amount
err := b.SendAuthenticatedHTTPRequestV2(exchange.RestSpot, http.MethodPost,
bitfinexV2MarginFunding,
params,
&resp,
getAccountFees)
if err != nil {
return response, err
}
if len(resp) != 2 {
return response, errors.New("invalid data received")
}
avgRate, ok := resp[0].(float64)
if !ok {
return response, errors.New("failed type assertion for rate")
}
avgAmount, ok := resp[1].(float64)
if !ok {
return response, errors.New("failed type assertion for amount")
}
response.Symbol = symbol
response.RateAverage = avgRate
response.AmountAverage = avgAmount
return response, nil
}
// GetV2FundingInfo gets funding info for margin pairs
func (b *Bitfinex) GetV2FundingInfo(key string) (MarginFundingDataV2, error) {
var resp []interface{}
var response MarginFundingDataV2
err := b.SendAuthenticatedHTTPRequestV2(exchange.RestSpot, http.MethodPost,
fmt.Sprintf(bitfinexV2FundingInfo, key),
nil,
&resp,
getAccountFees)
if err != nil {
return response, err
}
if len(resp) != 3 {
return response, errors.New("invalid data received")
}
sym, ok := resp[0].(string)
if !ok {
return response, errors.New("failed type assertion for sym")
}
symbol, ok := resp[1].(string)
if !ok {
return response, errors.New("failed type assertion for symbol")
}
fundingData, ok := resp[2].([]interface{})
if !ok {
return response, errors.New("failed type assertion for fundingData")
}
response.Sym = sym
response.Symbol = symbol
if len(fundingData) < 4 {
return response, errors.New("invalid length of fundingData")
}
for x := 0; x < 3; x++ {
_, ok := fundingData[x].(float64)
if !ok {
return response, fmt.Errorf("type conversion failed for x = %d", x)
}
}
response.Data.YieldLoan = fundingData[0].(float64)
response.Data.YieldLend = fundingData[1].(float64)
response.Data.DurationLoan = fundingData[2].(float64)
response.Data.DurationLend = fundingData[3].(float64)
return response, nil
}
// GetAccountInfoV2 gets V2 account data
func (b *Bitfinex) GetAccountInfoV2() (AccountV2Data, error) {
var resp AccountV2Data
var data []interface{}
err := b.SendAuthenticatedHTTPRequestV2(exchange.RestSpot, http.MethodPost,
bitfinexV2AccountInfo,
nil,
&data,
getAccountFees)
if err != nil {
return resp, err
}
if len(data) < 8 {
return resp, errors.New("invalid length of data")
}
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")
}
resp.ID = int64(tempFloat)
if tempString, ok = data[1].(string); !ok {
return resp, errors.New("type assertion failed for email, check for api updates")
}
resp.Email = tempString
if tempString, ok = data[2].(string); !ok {
return resp, errors.New("type assertion failed for username, check for api updates")
}
resp.Username = tempString
if tempFloat, ok = data[3].(float64); !ok {
return resp, errors.New("type assertion failed for accountcreate, check for api updates")
}
resp.MTSAccountCreate = int64(tempFloat)
if tempFloat, ok = data[4].(float64); !ok {
return resp, errors.New("type assertion failed for verified, check for api updates")
}
resp.Verified = int64(tempFloat)
if tempString, ok = data[7].(string); !ok {
return resp, errors.New("type assertion failed for timezone, check for api updates")
}
resp.Timezone = tempString
return resp, nil
}
// GetV2Balances gets v2 balances
func (b *Bitfinex) GetV2Balances() ([]WalletDataV2, error) {
var resp []WalletDataV2
var data [][4]interface{}
err := b.SendAuthenticatedHTTPRequestV2(exchange.RestSpot, http.MethodPost,
bitfinexV2Balances,
nil,
&data,
getAccountFees)
if err != nil {
return resp, err
}
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")
}
curr, ok := data[x][1].(string)
if !ok {
return resp, errors.New("type assertion failed for currency, check for api updates")
}
bal, ok := data[x][2].(float64)
if !ok {
return resp, errors.New("type assertion failed for balance, check for api updates")
}
unsettledInterest, ok := data[x][3].(float64)
if !ok {
return resp, errors.New("type assertion failed for unsettledInterest, check for api updates")
}
resp = append(resp, WalletDataV2{
WalletType: wType,
Currency: curr,
Balance: bal,
UnsettledInterest: unsettledInterest,
})
}
return resp, nil
}
// GetMarginPairs gets pairs that allow margin trading
func (b *Bitfinex) GetMarginPairs() ([]string, error) {
var resp [][]string
path := bitfinexAPIVersion2 + bitfinexMarginPairs
err := b.SendHTTPRequest(exchange.RestSpot, path, &resp, status)
if err != nil {
return nil, err
}
if len(resp) != 1 {
return nil, errors.New("invalid response")
}
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
params := url.Values{}
params.Set("keys", keys)
if startTime != "" {
params.Set("start", startTime)
}
if endTime != "" {
params.Set("end", endTime)
}
if sort != 0 {
params.Set("sort", strconv.FormatInt(sort, 10))
}
if limit != 0 {
params.Set("limit", strconv.FormatInt(limit, 10))
}
path := bitfinexAPIVersion2 + bitfinexDerivativeData +
params.Encode()
err := b.SendHTTPRequest(exchange.RestSpot, path, &result, status)
if err != nil {
return response, err
}
if len(result) < 1 {
return response, errors.New("invalid response, array length too small, check api docs for updates")
}
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
}
// GetTickerBatch returns all supported ticker information
func (b *Bitfinex) GetTickerBatch() (map[string]Ticker, error) {
var response [][]interface{}
path := b.API.Endpoints.URL +
bitfinexAPIVersion2 +
bitfinexTickerBatch +
path := bitfinexAPIVersion2 + bitfinexTickerBatch +
"?symbols=ALL"
err := b.SendHTTPRequest(path, &response, tickerBatch)
err := b.SendHTTPRequest(exchange.RestSpot, path, &response, tickerBatch)
if err != nil {
return nil, err
}
@@ -165,12 +428,9 @@ func (b *Bitfinex) GetTickerBatch() (map[string]Ticker, error) {
func (b *Bitfinex) GetTicker(symbol string) (Ticker, error) {
var response []interface{}
path := b.API.Endpoints.URL +
bitfinexAPIVersion2 +
bitfinexTicker +
symbol
path := bitfinexAPIVersion2 + bitfinexTicker + symbol
err := b.SendHTTPRequest(path, &response, tickerFunction)
err := b.SendHTTPRequest(exchange.RestSpot, path, &response, tickerFunction)
if err != nil {
return Ticker{}, err
}
@@ -232,16 +492,10 @@ func (b *Bitfinex) GetTrades(currencyPair string, limit, timestampStart, timesta
}
v.Set("sort", sortVal)
path := b.API.Endpoints.URL +
bitfinexAPIVersion2 +
bitfinexTrades +
currencyPair +
"/hist" +
"?" +
v.Encode()
path := bitfinexAPIVersion2 + bitfinexTrades + currencyPair + "/hist" + "?" + v.Encode()
var resp [][]interface{}
err := b.SendHTTPRequest(path, &resp, tradeRateLimit)
err := b.SendHTTPRequest(exchange.RestSpot, path, &resp, tradeRateLimit)
if err != nil {
return nil, err
}
@@ -290,17 +544,10 @@ func (b *Bitfinex) GetOrderbook(symbol, precision string, limit int64) (Orderboo
if limit > 0 {
u.Set("len", strconv.FormatInt(limit, 10))
}
path := b.API.Endpoints.URL +
bitfinexAPIVersion2 +
bitfinexOrderbook +
symbol +
"/" +
precision +
"?" +
u.Encode()
path := bitfinexAPIVersion2 + bitfinexOrderbook + symbol + "/" + precision + "?" + u.Encode()
var response [][]interface{}
err := b.SendHTTPRequest(path, &response, orderbookFunction)
err := b.SendHTTPRequest(exchange.RestSpot, path, &response, orderbookFunction)
if err != nil {
return Orderbook{}, err
}
@@ -371,8 +618,8 @@ func (b *Bitfinex) GetOrderbook(symbol, precision string, limit int64) (Orderboo
// GetStats returns various statistics about the requested pair
func (b *Bitfinex) GetStats(symbol string) ([]Stat, error) {
var response []Stat
path := b.API.Endpoints.URL + bitfinexAPIVersion + bitfinexStats + symbol
return response, b.SendHTTPRequest(path, &response, statsV1)
path := bitfinexAPIVersion + bitfinexStats + symbol
return response, b.SendHTTPRequest(exchange.RestSpot, path, &response, statsV1)
}
// GetFundingBook the entire margin funding book for both bids and asks sides
@@ -382,9 +629,9 @@ func (b *Bitfinex) GetStats(symbol string) ([]Stat, error) {
// conversion to full V2 API update is done.
func (b *Bitfinex) GetFundingBook(symbol string) (FundingBook, error) {
response := FundingBook{}
path := b.API.Endpoints.URL + bitfinexAPIVersion + bitfinexLendbook + symbol
path := bitfinexAPIVersion + bitfinexLendbook + symbol
if err := b.SendHTTPRequest(path, &response, fundingbook); err != nil {
if err := b.SendHTTPRequest(exchange.RestSpot, path, &response, fundingbook); err != nil {
return response, err
}
@@ -397,12 +644,11 @@ func (b *Bitfinex) GetFundingBook(symbol string) (FundingBook, error) {
// Symbol - example "USD"
func (b *Bitfinex) GetLends(symbol string, values url.Values) ([]Lends, error) {
var response []Lends
path := common.EncodeURLValues(b.API.Endpoints.URL+
bitfinexAPIVersion+
path := common.EncodeURLValues(bitfinexAPIVersion+
bitfinexLends+
symbol,
values)
return response, b.SendHTTPRequest(path, &response, lends)
return response, b.SendHTTPRequest(exchange.RestSpot, path, &response, lends)
}
// GetCandles returns candle chart data
@@ -415,8 +661,7 @@ func (b *Bitfinex) GetCandles(symbol, timeFrame string, start, end int64, limit
fundingPeriod = ":p30"
}
var path = b.API.Endpoints.URL +
bitfinexAPIVersion2 +
var path = bitfinexAPIVersion2 +
bitfinexCandles +
":" +
timeFrame +
@@ -444,7 +689,7 @@ func (b *Bitfinex) GetCandles(symbol, timeFrame string, start, end int64, limit
}
var response [][]interface{}
err := b.SendHTTPRequest(path, &response, candle)
err := b.SendHTTPRequest(exchange.RestSpot, path, &response, candle)
if err != nil {
return nil, err
}
@@ -467,7 +712,7 @@ func (b *Bitfinex) GetCandles(symbol, timeFrame string, start, end int64, limit
path += "/last"
var response []interface{}
err := b.SendHTTPRequest(path, &response, candle)
err := b.SendHTTPRequest(exchange.RestSpot, path, &response, candle)
if err != nil {
return nil, err
}
@@ -528,7 +773,7 @@ func (b *Bitfinex) GetLeaderboard(key, timeframe, symbol string, sort, limit int
return nil, errors.New("invalid leaderboard key")
}
path := fmt.Sprintf("%s/%s:%s:%s/hist", b.API.Endpoints.URL+bitfinexAPIVersion2+bitfinexLeaderboard,
path := fmt.Sprintf("%s/%s:%s:%s/hist", bitfinexAPIVersion2+bitfinexLeaderboard,
key,
timeframe,
symbol)
@@ -547,7 +792,7 @@ func (b *Bitfinex) GetLeaderboard(key, timeframe, symbol string, sort, limit int
}
path = common.EncodeURLValues(path, vals)
var resp []interface{}
if err := b.SendHTTPRequest(path, &resp, leaderBoardReqRate); err != nil {
if err := b.SendHTTPRequest(exchange.RestSpot, path, &resp, leaderBoardReqRate); err != nil {
return nil, err
}
@@ -587,7 +832,7 @@ func (b *Bitfinex) GetForeignExchangeRate() error {
// GetAccountFees returns information about your account trading fees
func (b *Bitfinex) GetAccountFees() ([]AccountInfo, error) {
var responses []AccountInfo
return responses, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return responses, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexAccountInfo,
nil,
&responses,
@@ -597,7 +842,7 @@ func (b *Bitfinex) GetAccountFees() ([]AccountInfo, error) {
// GetWithdrawalFees - Gets all fee rates for withdrawals
func (b *Bitfinex) GetWithdrawalFees() (AccountFees, error) {
response := AccountFees{}
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexAccountFees,
nil,
&response,
@@ -609,7 +854,7 @@ func (b *Bitfinex) GetWithdrawalFees() (AccountFees, error) {
func (b *Bitfinex) GetAccountSummary() (AccountSummary, error) {
response := AccountSummary{}
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexAccountSummary,
nil,
&response,
@@ -635,7 +880,7 @@ func (b *Bitfinex) NewDeposit(method, walletName string, renew int) (DepositResp
req["wallet_name"] = walletName
req["renew"] = renew
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexDeposit,
req,
&response,
@@ -646,7 +891,7 @@ func (b *Bitfinex) NewDeposit(method, walletName string, renew int) (DepositResp
// this request.
func (b *Bitfinex) GetKeyPermissions() (KeyPermissions, error) {
response := KeyPermissions{}
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexKeyPermissions,
nil,
&response,
@@ -656,7 +901,7 @@ func (b *Bitfinex) GetKeyPermissions() (KeyPermissions, error) {
// GetMarginInfo shows your trading wallet information for margin trading
func (b *Bitfinex) GetMarginInfo() ([]MarginInfo, error) {
var response []MarginInfo
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexMarginInfo,
nil,
&response,
@@ -666,7 +911,7 @@ func (b *Bitfinex) GetMarginInfo() ([]MarginInfo, error) {
// GetAccountBalance returns full wallet balance information
func (b *Bitfinex) GetAccountBalance() ([]Balance, error) {
var response []Balance
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexBalances,
nil,
&response,
@@ -686,7 +931,7 @@ func (b *Bitfinex) WalletTransfer(amount float64, currency, walletFrom, walletTo
req["walletfrom"] = walletFrom
req["walletto"] = walletTo
err := b.SendAuthenticatedHTTPRequest(http.MethodPost,
err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexTransfer,
req,
&response,
@@ -714,7 +959,7 @@ func (b *Bitfinex) WithdrawCryptocurrency(wallet, address, paymentID string, amo
req["payment_id"] = paymentID
}
err := b.SendAuthenticatedHTTPRequest(http.MethodPost,
err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexWithdrawal,
req,
&response,
@@ -759,7 +1004,7 @@ func (b *Bitfinex) WithdrawFIAT(withdrawalType, walletType string, withdrawReque
req["intermediary_bank_swift"] = withdrawRequest.Fiat.IntermediarySwiftCode
}
err := b.SendAuthenticatedHTTPRequest(http.MethodPost,
err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexWithdrawal,
req,
&response,
@@ -794,7 +1039,7 @@ func (b *Bitfinex) NewOrder(currencyPair, orderType string, amount, price float6
req["side"] = order.Buy.Lower()
}
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexOrderNew,
req,
&response,
@@ -807,7 +1052,7 @@ func (b *Bitfinex) NewOrderMulti(orders []PlaceOrder) (OrderMultiResponse, error
req := make(map[string]interface{})
req["orders"] = orders
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexOrderNewMulti,
req,
&response,
@@ -820,7 +1065,7 @@ func (b *Bitfinex) CancelExistingOrder(orderID int64) (Order, error) {
req := make(map[string]interface{})
req["order_id"] = orderID
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexOrderCancel,
req,
&response,
@@ -833,7 +1078,7 @@ func (b *Bitfinex) CancelMultipleOrders(orderIDs []int64) (string, error) {
req := make(map[string]interface{})
req["order_ids"] = orderIDs
return response.Result, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response.Result, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexOrderCancelMulti,
req,
nil,
@@ -844,7 +1089,7 @@ func (b *Bitfinex) CancelMultipleOrders(orderIDs []int64) (string, error) {
func (b *Bitfinex) CancelAllExistingOrders() (string, error) {
response := GenericResponse{}
return response.Result, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response.Result, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexOrderCancelAll,
nil,
nil,
@@ -869,7 +1114,7 @@ func (b *Bitfinex) ReplaceOrder(orderID int64, symbol string, amount, price floa
req["side"] = order.Sell.Lower()
}
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexOrderCancelReplace,
req,
&response,
@@ -882,7 +1127,7 @@ func (b *Bitfinex) GetOrderStatus(orderID int64) (Order, error) {
req := make(map[string]interface{})
req["order_id"] = orderID
return orderStatus, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return orderStatus, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexOrderStatus,
req,
&orderStatus,
@@ -895,7 +1140,7 @@ func (b *Bitfinex) GetInactiveOrders() ([]Order, error) {
req := make(map[string]interface{})
req["limit"] = "100"
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexInactiveOrders,
req,
&response,
@@ -905,7 +1150,7 @@ func (b *Bitfinex) GetInactiveOrders() ([]Order, error) {
// GetOpenOrders returns all active orders and statuses
func (b *Bitfinex) GetOpenOrders() ([]Order, error) {
var response []Order
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexOrders,
nil,
&response,
@@ -916,7 +1161,7 @@ func (b *Bitfinex) GetOpenOrders() ([]Order, error) {
func (b *Bitfinex) GetActivePositions() ([]Position, error) {
var response []Position
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexPositions,
nil,
&response,
@@ -929,7 +1174,7 @@ func (b *Bitfinex) ClaimPosition(positionID int) (Position, error) {
req := make(map[string]interface{})
req["position_id"] = positionID
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexClaimPosition,
nil,
nil,
@@ -955,7 +1200,7 @@ func (b *Bitfinex) GetBalanceHistory(symbol string, timeSince, timeUntil time.Ti
req["wallet"] = wallet
}
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexHistory,
req,
&response,
@@ -981,7 +1226,7 @@ func (b *Bitfinex) GetMovementHistory(symbol, method string, timeSince, timeUnti
req["limit"] = limit
}
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexHistoryMovements,
req,
&response,
@@ -1005,7 +1250,7 @@ func (b *Bitfinex) GetTradeHistory(currencyPair string, timestamp, until time.Ti
req["reverse"] = reverse
}
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexTradeHistory,
req,
&response,
@@ -1022,7 +1267,7 @@ func (b *Bitfinex) NewOffer(symbol string, amount, rate float64, period int64, d
req["period"] = period
req["direction"] = direction
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexOfferNew,
req,
&response,
@@ -1035,7 +1280,7 @@ func (b *Bitfinex) CancelOffer(offerID int64) (Offer, error) {
req := make(map[string]interface{})
req["offer_id"] = offerID
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexOfferCancel,
req,
&response,
@@ -1049,7 +1294,7 @@ func (b *Bitfinex) GetOfferStatus(offerID int64) (Offer, error) {
req := make(map[string]interface{})
req["offer_id"] = offerID
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexOrderStatus,
req,
&response,
@@ -1060,7 +1305,7 @@ func (b *Bitfinex) GetOfferStatus(offerID int64) (Offer, error) {
func (b *Bitfinex) GetActiveCredits() ([]Offer, error) {
var response []Offer
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexActiveCredits,
nil,
&response,
@@ -1071,7 +1316,7 @@ func (b *Bitfinex) GetActiveCredits() ([]Offer, error) {
func (b *Bitfinex) GetActiveOffers() ([]Offer, error) {
var response []Offer
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexOffers,
nil,
&response,
@@ -1082,7 +1327,7 @@ func (b *Bitfinex) GetActiveOffers() ([]Offer, error) {
func (b *Bitfinex) GetActiveMarginFunding() ([]MarginFunds, error) {
var response []MarginFunds
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexMarginActiveFunds,
nil,
&response,
@@ -1094,7 +1339,7 @@ func (b *Bitfinex) GetActiveMarginFunding() ([]MarginFunds, error) {
func (b *Bitfinex) GetUnusedMarginFunds() ([]MarginFunds, error) {
var response []MarginFunds
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexMarginUnusedFunds,
nil,
&response,
@@ -1106,7 +1351,7 @@ func (b *Bitfinex) GetUnusedMarginFunds() ([]MarginFunds, error) {
func (b *Bitfinex) GetMarginTotalTakenFunds() ([]MarginTotalTakenFunds, error) {
var response []MarginTotalTakenFunds
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexMarginTotalFunds,
nil,
&response,
@@ -1119,7 +1364,7 @@ func (b *Bitfinex) CloseMarginFunding(swapID int64) (Offer, error) {
req := make(map[string]interface{})
req["swap_id"] = swapID
return response, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return response, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitfinexMarginClose,
req,
&response,
@@ -1127,10 +1372,14 @@ func (b *Bitfinex) CloseMarginFunding(swapID int64) (Offer, error) {
}
// SendHTTPRequest sends an unauthenticated request
func (b *Bitfinex) SendHTTPRequest(path string, result interface{}, e request.EndpointLimit) error {
func (b *Bitfinex) SendHTTPRequest(ep exchange.URL, path string, result interface{}, e request.EndpointLimit) error {
endpoint, err := b.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
return b.SendPayload(context.Background(), &request.Item{
Method: http.MethodGet,
Path: path,
Path: endpoint + path,
Result: result,
Verbose: b.Verbose,
HTTPDebugging: b.HTTPDebugging,
@@ -1140,12 +1389,17 @@ func (b *Bitfinex) SendHTTPRequest(path string, result interface{}, e request.En
// SendAuthenticatedHTTPRequest sends an autheticated http request and json
// unmarshals result to a supplied variable
func (b *Bitfinex) SendAuthenticatedHTTPRequest(method, path string, params map[string]interface{}, result interface{}, endpoint request.EndpointLimit) error {
func (b *Bitfinex) SendAuthenticatedHTTPRequest(ep exchange.URL, method, path string, params map[string]interface{}, result interface{}, endpoint request.EndpointLimit) error {
if !b.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet,
b.Name)
}
ePoint, err := b.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
n := b.Requester.GetNonce(true)
req := make(map[string]interface{})
@@ -1175,7 +1429,7 @@ func (b *Bitfinex) SendAuthenticatedHTTPRequest(method, path string, params map[
return b.SendPayload(context.Background(), &request.Item{
Method: method,
Path: b.API.Endpoints.URL + bitfinexAPIVersion + path,
Path: ePoint + bitfinexAPIVersion + path,
Headers: headers,
Result: result,
AuthRequest: true,
@@ -1186,6 +1440,58 @@ func (b *Bitfinex) SendAuthenticatedHTTPRequest(method, path string, params map[
Endpoint: endpoint})
}
// SendAuthenticatedHTTPRequestV2 sends an autheticated http request and json
// unmarshals result to a supplied variable
func (b *Bitfinex) SendAuthenticatedHTTPRequestV2(ep exchange.URL, method, path string, params map[string]interface{}, result interface{}, endpoint request.EndpointLimit) error {
if !b.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet,
b.Name)
}
ePoint, err := b.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
var body io.Reader
var payload []byte
if len(params) != 0 {
payload, err = json.Marshal(params)
if err != nil {
return err
}
body = bytes.NewBuffer(payload)
}
// This is done in a weird way because bitfinex doesn't accept unixnano
n := strconv.FormatInt(int64(b.Requester.GetNonce(false))*1e9, 10)
headers := make(map[string]string)
headers["Content-Type"] = "application/json"
headers["Accept"] = "application/json"
headers["bfx-apikey"] = b.API.Credentials.Key
headers["bfx-nonce"] = n
strPath := "/api" + bitfinexAPIVersion2 + path + string(payload)
signStr := strPath + n
hmac := crypto.GetHMAC(
crypto.HashSHA512_384,
[]byte(signStr),
[]byte(b.API.Credentials.Secret),
)
headers["bfx-signature"] = crypto.HexEncodeToString(hmac)
return b.SendPayload(context.Background(), &request.Item{
Method: method,
Path: ePoint + bitfinexAPIVersion2 + path,
Headers: headers,
Body: body,
Result: result,
AuthRequest: true,
NonceEnabled: true,
Verbose: b.Verbose,
HTTPDebugging: b.HTTPDebugging,
HTTPRecording: b.HTTPRecording,
Endpoint: endpoint,
})
}
// GetFee returns an estimate of fee based on type of transaction
func (b *Bitfinex) GetFee(feeBuilder *exchange.FeeBuilder) (float64, error) {
var fee float64
@@ -1343,9 +1649,8 @@ func (b *Bitfinex) ConvertSymbolToDepositMethod(c currency.Code) (string, error)
func (b *Bitfinex) PopulateAcceptableMethods() error {
if len(AcceptableMethods) == 0 {
var response [][][2]string
err := b.SendHTTPRequest(b.API.Endpoints.URL+
bitfinexAPIVersion2+
bitfinexDepositMethod,
err := b.SendHTTPRequest(exchange.RestSpot,
bitfinexAPIVersion2+bitfinexDepositMethod,
&response,
configs)
if err != nil {

View File

@@ -61,6 +61,64 @@ func TestMain(m *testing.M) {
os.Exit(m.Run())
}
func TestGetV2MarginFunding(t *testing.T) {
if !areTestAPIKeysSet() {
t.SkipNow()
}
_, err := b.GetV2MarginFunding("fUSD", "2", 2)
if err != nil {
t.Error(err)
}
}
func TestGetAccountInfoV2(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.SkipNow()
}
_, err := b.GetAccountInfoV2()
if err != nil {
t.Error(err)
}
}
func TestGetV2FundingInfo(t *testing.T) {
if !areTestAPIKeysSet() {
t.SkipNow()
}
_, err := b.GetV2FundingInfo("fUSD")
if err != nil {
t.Error(err)
}
}
func TestGetV2Balances(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.SkipNow()
}
_, err := b.GetV2Balances()
if err != nil {
t.Error(err)
}
}
func TestGetDerivativeData(t *testing.T) {
t.Parallel()
_, err := b.GetDerivativeData("tBTCF0:USTF0", "", "", 0, 0)
if err != nil {
t.Error(err)
}
}
func TestGetMarginPairs(t *testing.T) {
t.Parallel()
_, err := b.GetMarginPairs()
if err != nil {
t.Error(err)
}
}
func TestAppendOptionalDelimiter(t *testing.T) {
t.Parallel()
curr1, err := currency.NewPairFromString("BTCUSD")
@@ -222,7 +280,7 @@ func TestGetAccountFees(t *testing.T) {
}
t.Parallel()
_, err := b.UpdateAccountInfo()
_, err := b.UpdateAccountInfo(asset.Spot)
if err != nil {
t.Error("GetAccountInfo error", err)
}
@@ -233,7 +291,6 @@ func TestGetWithdrawalFee(t *testing.T) {
t.SkipNow()
}
t.Parallel()
_, err := b.GetWithdrawalFees()
if err != nil {
t.Error("GetAccountInfo error", err)
@@ -315,7 +372,7 @@ func TestGetAccountInfo(t *testing.T) {
}
t.Parallel()
_, err := b.FetchAccountInfo()
_, err := b.FetchAccountInfo(asset.Spot)
if err != nil {
t.Error(err)
}
@@ -487,7 +544,6 @@ func TestGetBalanceHistory(t *testing.T) {
t.SkipNow()
}
t.Parallel()
_, err := b.GetBalanceHistory("USD", time.Time{}, time.Time{}, 1, "deposit")
if err == nil {
t.Error("GetBalanceHistory() Expected error")
@@ -511,7 +567,6 @@ func TestGetTradeHistory(t *testing.T) {
t.SkipNow()
}
t.Parallel()
_, err := b.GetTradeHistory("BTCUSD", time.Time{}, time.Time{}, 1, 0)
if err == nil {
t.Error("GetTradeHistory() Expected error")
@@ -734,7 +789,8 @@ func TestFormatWithdrawPermissions(t *testing.T) {
func TestGetActiveOrders(t *testing.T) {
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := b.GetActiveOrders(&getOrdersRequest)
@@ -748,7 +804,8 @@ func TestGetActiveOrders(t *testing.T) {
func TestGetOrderHistory(t *testing.T) {
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := b.GetOrderHistory(&getOrdersRequest)
@@ -1470,7 +1527,7 @@ func TestGetHistoricTrades(t *testing.T) {
}
// longer term test
_, err = b.GetHistoricTrades(currencyPair, asset.Spot, time.Now().Add(-time.Hour*24*100), time.Now().Add(-time.Hour*24*99))
_, err = b.GetHistoricTrades(currencyPair, asset.Spot, time.Now().Add(-time.Hour*100), time.Now().Add(-time.Hour*99))
if err != nil {
t.Error(err)
}

View File

@@ -6,6 +6,24 @@ import (
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
)
// AccountV2Data stores account v2 data
type AccountV2Data struct {
ID int64
Email string
Username string
MTSAccountCreate int64
Verified int64
Timezone string
}
// WalletDataV2 stores wallet data for v2
type WalletDataV2 struct {
WalletType string
Currency string
Balance float64
UnsettledInterest float64
}
// AcceptedOrderType defines the accepted market types, exchange strings denote non-contract order types.
var AcceptedOrderType = []string{"market", "limit", "stop", "trailing-stop",
"fill-or-kill", "exchange market", "exchange limit", "exchange stop",
@@ -18,6 +36,42 @@ var AcceptedWalletNames = []string{"trading", "exchange", "deposit", "margin",
// AcceptableMethods defines a map of currency codes to methods
var AcceptableMethods = make(map[string]string)
// MarginV2FundingData stores margin funding data
type MarginV2FundingData struct {
Symbol string
RateAverage float64
AmountAverage float64
}
// MarginFundingDataV2 stores margin funding data
type MarginFundingDataV2 struct {
Sym string
Symbol string
Data struct {
YieldLoan float64
YieldLend float64
DurationLoan float64
DurationLend float64
}
}
// MarginFundingData stores data for margin funding
type MarginFundingData struct {
ID int64
Symbol string
MTSCreated int64
MTSUpdated int64
Amount float64
AmountOrig float64
OrderType string
OfferStatus string
Active string
Rate float64
Period float64
Notify bool
Renew bool
}
// Ticker holds ticker information
type Ticker struct {
FlashReturnRate float64
@@ -36,6 +90,21 @@ type Ticker struct {
FFRAmountAvailable float64
}
// DerivativeDataResponse stores data for queried derivative
type DerivativeDataResponse struct {
Key string
MTS float64
DerivPrice float64
SpotPrice float64
MarkPrice float64
InsuranceFundBalance float64
NextFundingEventTS float64
NextFundingAccured float64
NextFundingStep float64
CurrentFunding float64
OpenInterest float64
}
// Stat holds individual statistics from exchange
type Stat struct {
Period int64 `json:"period"`

View File

@@ -163,10 +163,14 @@ func (b *Bitfinex) SetDefaults() {
b.Requester = request.New(b.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
request.WithLimiter(SetRateLimit()))
b.API.Endpoints.URLDefault = bitfinexAPIURLBase
b.API.Endpoints.URL = b.API.Endpoints.URLDefault
b.API.Endpoints.WebsocketURL = publicBitfinexWebsocketEndpoint
b.API.Endpoints = b.NewEndpoints()
err = b.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: bitfinexAPIURLBase,
exchange.WebsocketSpot: publicBitfinexWebsocketEndpoint,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
b.Websocket = stream.New()
b.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
b.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
@@ -184,6 +188,10 @@ func (b *Bitfinex) Setup(exch *config.ExchangeConfig) error {
if err != nil {
return err
}
wsEndpoint, err := b.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
return err
}
err = b.Websocket.Setup(&stream.WebsocketSetup{
Enabled: exch.Features.Enabled.Websocket,
@@ -192,7 +200,7 @@ func (b *Bitfinex) Setup(exch *config.ExchangeConfig) error {
WebsocketTimeout: exch.WebsocketTrafficTimeout,
DefaultURL: publicBitfinexWebsocketEndpoint,
ExchangeName: exch.Name,
RunningURL: exch.API.Endpoints.WebsocketURL,
RunningURL: wsEndpoint,
Connector: b.WsConnect,
Subscriber: b.Subscribe,
UnSubscriber: b.Unsubscribe,
@@ -398,17 +406,19 @@ func (b *Bitfinex) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orde
if err != nil {
return o, err
}
if assetType != asset.Spot && assetType != asset.Margin && assetType != asset.MarginFunding {
return o, fmt.Errorf("assetType not supported: %v", assetType)
}
b.appendOptionalDelimiter(&fPair)
var prefix = "t"
if assetType == asset.MarginFunding {
prefix = "f"
}
orderbookNew, err := b.GetOrderbook(prefix+fPair.String(), "R0", 100)
var orderbookNew Orderbook
orderbookNew, err = b.GetOrderbook(prefix+fPair.String(), "R0", 100)
if err != nil {
return o, err
return nil, err
}
if assetType == asset.MarginFunding {
o.IsFundingRate = true
for x := range orderbookNew.Asks {
@@ -443,18 +453,16 @@ func (b *Bitfinex) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orde
})
}
}
err = o.Process()
if err != nil {
return nil, err
}
return orderbook.Get(b.Name, fPair, assetType)
}
// UpdateAccountInfo retrieves balances for all enabled currencies on the
// Bitfinex exchange
func (b *Bitfinex) UpdateAccountInfo() (account.Holdings, error) {
func (b *Bitfinex) UpdateAccountInfo(assetType asset.Item) (account.Holdings, error) {
var response account.Holdings
response.Exchange = b.Name
@@ -494,10 +502,10 @@ func (b *Bitfinex) UpdateAccountInfo() (account.Holdings, error) {
}
// FetchAccountInfo retrieves balances for all enabled currencies
func (b *Bitfinex) FetchAccountInfo() (account.Holdings, error) {
acc, err := account.GetHoldings(b.Name)
func (b *Bitfinex) FetchAccountInfo(assetType asset.Item) (account.Holdings, error) {
acc, err := account.GetHoldings(b.Name, assetType)
if err != nil {
return b.UpdateAccountInfo()
return b.UpdateAccountInfo(assetType)
}
return acc, nil
@@ -945,8 +953,8 @@ func (b *Bitfinex) appendOptionalDelimiter(p *currency.Pair) {
// ValidateCredentials validates current credentials used for wrapper
// functionality
func (b *Bitfinex) ValidateCredentials() error {
_, err := b.UpdateAccountInfo()
func (b *Bitfinex) ValidateCredentials(assetType asset.Item) error {
_, err := b.UpdateAccountInfo(assetType)
return b.CheckTransientError(err)
}

View File

@@ -3,7 +3,6 @@ package bitflyer
import (
"context"
"errors"
"fmt"
"net/http"
"net/url"
"strconv"
@@ -76,51 +75,41 @@ type Bitflyer struct {
// analysis system
func (b *Bitflyer) GetLatestBlockCA() (ChainAnalysisBlock, error) {
var resp ChainAnalysisBlock
path := b.API.Endpoints.URLSecondary + latestBlock
return resp, b.SendHTTPRequest(path, &resp)
return resp, b.SendHTTPRequest(exchange.ChainAnalysis, latestBlock, &resp)
}
// GetBlockCA returns block information by blockhash from bitflyer chain
// analysis system
func (b *Bitflyer) GetBlockCA(blockhash string) (ChainAnalysisBlock, error) {
var resp ChainAnalysisBlock
path := b.API.Endpoints.URLSecondary + blockByBlockHash + blockhash
return resp, b.SendHTTPRequest(path, &resp)
return resp, b.SendHTTPRequest(exchange.ChainAnalysis, blockByBlockHash+blockhash, &resp)
}
// GetBlockbyHeightCA returns the block information by height from bitflyer chain
// analysis system
func (b *Bitflyer) GetBlockbyHeightCA(height int64) (ChainAnalysisBlock, error) {
var resp ChainAnalysisBlock
path := b.API.Endpoints.URLSecondary +
blockByBlockHeight +
strconv.FormatInt(height, 10)
return resp, b.SendHTTPRequest(path, &resp)
return resp, b.SendHTTPRequest(exchange.ChainAnalysis, blockByBlockHeight+strconv.FormatInt(height, 10), &resp)
}
// GetTransactionByHashCA returns transaction information by txHash from
// bitflyer chain analysis system
func (b *Bitflyer) GetTransactionByHashCA(txHash string) (ChainAnalysisTransaction, error) {
var resp ChainAnalysisTransaction
path := b.API.Endpoints.URLSecondary + transaction + txHash
return resp, b.SendHTTPRequest(path, &resp)
return resp, b.SendHTTPRequest(exchange.ChainAnalysis, transaction+txHash, &resp)
}
// GetAddressInfoCA returns balance information for address by addressln string
// from bitflyer chain analysis system
func (b *Bitflyer) GetAddressInfoCA(addressln string) (ChainAnalysisAddress, error) {
var resp ChainAnalysisAddress
path := b.API.Endpoints.URLSecondary + address + addressln
return resp, b.SendHTTPRequest(path, &resp)
return resp, b.SendHTTPRequest(exchange.ChainAnalysis, address+addressln, &resp)
}
// GetMarkets returns market information
func (b *Bitflyer) GetMarkets() ([]MarketInfo, error) {
var resp []MarketInfo
path := b.API.Endpoints.URL + pubGetMarkets
return resp, b.SendHTTPRequest(path, &resp)
return resp, b.SendHTTPRequest(exchange.RestSpot, pubGetMarkets, &resp)
}
// GetOrderBook returns market orderbook depth
@@ -128,9 +117,8 @@ func (b *Bitflyer) GetOrderBook(symbol string) (Orderbook, error) {
var resp Orderbook
v := url.Values{}
v.Set("product_code", symbol)
path := fmt.Sprintf("%s%s?%s", b.API.Endpoints.URL, pubGetBoard, v.Encode())
return resp, b.SendHTTPRequest(path, &resp)
return resp, b.SendHTTPRequest(exchange.RestSpot, pubGetBoard+"?"+v.Encode(), &resp)
}
// GetTicker returns ticker information
@@ -138,8 +126,7 @@ func (b *Bitflyer) GetTicker(symbol string) (Ticker, error) {
var resp Ticker
v := url.Values{}
v.Set("product_code", symbol)
path := fmt.Sprintf("%s%s?%s", b.API.Endpoints.URL, pubGetTicker, v.Encode())
return resp, b.SendHTTPRequest(path, &resp)
return resp, b.SendHTTPRequest(exchange.RestSpot, pubGetTicker+"?"+v.Encode(), &resp)
}
// GetExecutionHistory returns past trades that were executed on the market
@@ -147,18 +134,14 @@ func (b *Bitflyer) GetExecutionHistory(symbol string) ([]ExecutedTrade, error) {
var resp []ExecutedTrade
v := url.Values{}
v.Set("product_code", symbol)
path := fmt.Sprintf("%s%s?%s", b.API.Endpoints.URL, pubGetExecutionHistory, v.Encode())
return resp, b.SendHTTPRequest(path, &resp)
return resp, b.SendHTTPRequest(exchange.RestSpot, pubGetExecutionHistory+"?"+v.Encode(), &resp)
}
// GetExchangeStatus returns exchange status information
func (b *Bitflyer) GetExchangeStatus() (string, error) {
resp := make(map[string]string)
path := b.API.Endpoints.URL + pubGetHealth
err := b.SendHTTPRequest(path, &resp)
err := b.SendHTTPRequest(exchange.RestSpot, pubGetHealth, &resp)
if err != nil {
return "", err
}
@@ -183,8 +166,7 @@ func (b *Bitflyer) GetChats(fromDate string) ([]ChatLog, error) {
var resp []ChatLog
v := url.Values{}
v.Set("from_date", fromDate)
path := fmt.Sprintf("%s%s?%s", b.API.Endpoints.URL, pubGetChats, v.Encode())
return resp, b.SendHTTPRequest(path, &resp)
return resp, b.SendHTTPRequest(exchange.RestSpot, pubGetChats+"?"+v.Encode(), &resp)
}
// GetPermissions returns current permissions for associated with your API
@@ -304,10 +286,14 @@ func (b *Bitflyer) GetTradingCommission() {
}
// SendHTTPRequest sends an unauthenticated request
func (b *Bitflyer) SendHTTPRequest(path string, result interface{}) error {
func (b *Bitflyer) SendHTTPRequest(ep exchange.URL, path string, result interface{}) error {
endpoint, err := b.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
return b.SendPayload(context.Background(), &request.Item{
Method: http.MethodGet,
Path: path,
Path: endpoint + path,
Result: result,
Verbose: b.Verbose,
HTTPDebugging: b.HTTPDebugging,

View File

@@ -283,7 +283,8 @@ func TestFormatWithdrawPermissions(t *testing.T) {
func TestGetActiveOrders(t *testing.T) {
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := b.GetActiveOrders(&getOrdersRequest)
@@ -297,7 +298,8 @@ func TestGetActiveOrders(t *testing.T) {
func TestGetOrderHistory(t *testing.T) {
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := b.GetOrderHistory(&getOrdersRequest)

View File

@@ -94,11 +94,14 @@ func (b *Bitflyer) SetDefaults() {
b.Requester = request.New(b.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
request.WithLimiter(SetRateLimit()))
b.API.Endpoints.URLDefault = japanURL
b.API.Endpoints.URL = b.API.Endpoints.URLDefault
b.API.Endpoints.URLSecondaryDefault = chainAnalysis
b.API.Endpoints.URLSecondary = b.API.Endpoints.URLSecondaryDefault
b.API.Endpoints = b.NewEndpoints()
err = b.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: japanURL,
exchange.ChainAnalysis: chainAnalysis,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
}
// Setup takes in the supplied exchange configuration details and sets params
@@ -289,15 +292,15 @@ func (b *Bitflyer) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orde
// UpdateAccountInfo retrieves balances for all enabled currencies on the
// Bitflyer exchange
func (b *Bitflyer) UpdateAccountInfo() (account.Holdings, error) {
func (b *Bitflyer) UpdateAccountInfo(assetType asset.Item) (account.Holdings, error) {
return account.Holdings{}, common.ErrNotYetImplemented
}
// FetchAccountInfo retrieves balances for all enabled currencies
func (b *Bitflyer) FetchAccountInfo() (account.Holdings, error) {
acc, err := account.GetHoldings(b.Name)
func (b *Bitflyer) FetchAccountInfo(assetType asset.Item) (account.Holdings, error) {
acc, err := account.GetHoldings(b.Name, assetType)
if err != nil {
return b.UpdateAccountInfo()
return b.UpdateAccountInfo(assetType)
}
return acc, nil
@@ -442,8 +445,8 @@ func (b *Bitflyer) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error
// ValidateCredentials validates current credentials used for wrapper
// functionality
func (b *Bitflyer) ValidateCredentials() error {
_, err := b.UpdateAccountInfo()
func (b *Bitflyer) ValidateCredentials(assetType asset.Item) error {
_, err := b.UpdateAccountInfo(assetType)
return b.CheckTransientError(err)
}

View File

@@ -68,10 +68,7 @@ func (b *Bithumb) GetTradablePairs() ([]string, error) {
// symbol e.g. "btc"
func (b *Bithumb) GetTicker(symbol string) (Ticker, error) {
var response TickerResponse
path := b.API.Endpoints.URL +
publicTicker +
strings.ToUpper(symbol)
err := b.SendHTTPRequest(path, &response)
err := b.SendHTTPRequest(exchange.RestSpot, publicTicker+strings.ToUpper(symbol), &response)
if err != nil {
return response.Data, err
}
@@ -86,8 +83,7 @@ func (b *Bithumb) GetTicker(symbol string) (Ticker, error) {
// GetAllTickers returns all ticker information
func (b *Bithumb) GetAllTickers() (map[string]Ticker, error) {
var response TickersResponse
path := b.API.Endpoints.URL + publicTicker + "all"
err := b.SendHTTPRequest(path, &response)
err := b.SendHTTPRequest(exchange.RestSpot, publicTicker+"all", &response)
if err != nil {
return nil, err
}
@@ -116,9 +112,7 @@ func (b *Bithumb) GetAllTickers() (map[string]Ticker, error) {
// symbol e.g. "btc"
func (b *Bithumb) GetOrderBook(symbol string) (Orderbook, error) {
response := Orderbook{}
path := b.API.Endpoints.URL + publicOrderBook + strings.ToUpper(symbol)
err := b.SendHTTPRequest(path, &response)
err := b.SendHTTPRequest(exchange.RestSpot, publicOrderBook+strings.ToUpper(symbol), &response)
if err != nil {
return response, err
}
@@ -135,11 +129,10 @@ func (b *Bithumb) GetOrderBook(symbol string) (Orderbook, error) {
// symbol e.g. "btc"
func (b *Bithumb) GetTransactionHistory(symbol string) (TransactionHistory, error) {
response := TransactionHistory{}
path := b.API.Endpoints.URL +
publicTransactionHistory +
path := publicTransactionHistory +
strings.ToUpper(symbol)
err := b.SendHTTPRequest(path, &response)
err := b.SendHTTPRequest(exchange.RestSpot, path, &response)
if err != nil {
return response, err
}
@@ -166,7 +159,7 @@ func (b *Bithumb) GetAccountInformation(orderCurrency, paymentCurrency string) (
}
return response,
b.SendAuthenticatedHTTPRequest(privateAccInfo, val, &response)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, privateAccInfo, val, &response)
}
// GetAccountBalance returns customer wallet information
@@ -185,7 +178,7 @@ func (b *Bithumb) GetAccountBalance(c string) (FullBalance, error) {
vals.Set("currency", c)
}
err := b.SendAuthenticatedHTTPRequest(privateAccBalance, vals, &response)
err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, privateAccBalance, vals, &response)
if err != nil {
return fullBalance, err
}
@@ -238,7 +231,7 @@ func (b *Bithumb) GetWalletAddress(currency string) (WalletAddressRes, error) {
params := url.Values{}
params.Set("currency", strings.ToUpper(currency))
err := b.SendAuthenticatedHTTPRequest(privateWalletAdd, params, &response)
err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, privateWalletAdd, params, &response)
if err != nil {
return response, err
}
@@ -257,7 +250,7 @@ func (b *Bithumb) GetLastTransaction() (LastTransactionTicker, error) {
response := LastTransactionTicker{}
return response,
b.SendAuthenticatedHTTPRequest(privateTicker, nil, &response)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, privateTicker, nil, &response)
}
// GetOrders returns order list
@@ -292,7 +285,7 @@ func (b *Bithumb) GetOrders(orderID, transactionType, count, after, currency str
}
return response,
b.SendAuthenticatedHTTPRequest(privateOrders, params, &response)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, privateOrders, params, &response)
}
// GetUserTransactions returns customer transactions
@@ -300,7 +293,7 @@ func (b *Bithumb) GetUserTransactions() (UserTransactions, error) {
response := UserTransactions{}
return response,
b.SendAuthenticatedHTTPRequest(privateUserTrans, nil, &response)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, privateUserTrans, nil, &response)
}
// PlaceTrade executes a trade order
@@ -321,7 +314,7 @@ func (b *Bithumb) PlaceTrade(orderCurrency, transactionType string, units float6
params.Set("price", strconv.FormatInt(price, 10))
return response,
b.SendAuthenticatedHTTPRequest(privatePlaceTrade, params, &response)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, privatePlaceTrade, params, &response)
}
// ModifyTrade modifies an order already on the exchange books
@@ -337,7 +330,7 @@ func (b *Bithumb) ModifyTrade(orderID, orderCurrency, transactionType string, un
params.Set("order_id", orderID)
return response,
b.SendAuthenticatedHTTPRequest(privatePlaceTrade, params, &response)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, privatePlaceTrade, params, &response)
}
// GetOrderDetails returns specific order details
@@ -355,7 +348,7 @@ func (b *Bithumb) GetOrderDetails(orderID, transactionType, currency string) (Or
params.Set("currency", strings.ToUpper(currency))
return response,
b.SendAuthenticatedHTTPRequest(privateOrderDetail, params, &response)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, privateOrderDetail, params, &response)
}
// CancelTrade cancels a customer purchase/sales transaction
@@ -372,7 +365,7 @@ func (b *Bithumb) CancelTrade(transactionType, orderID, currency string) (Action
params.Set("currency", strings.ToUpper(currency))
return response,
b.SendAuthenticatedHTTPRequest(privateCancelTrade, nil, &response)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, privateCancelTrade, nil, &response)
}
// WithdrawCrypto withdraws a customer currency to an address
@@ -395,7 +388,7 @@ func (b *Bithumb) WithdrawCrypto(address, destination, currency string, units fl
params.Set("units", strconv.FormatFloat(units, 'f', -1, 64))
return response,
b.SendAuthenticatedHTTPRequest(privateBTCWithdraw, params, &response)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, privateBTCWithdraw, params, &response)
}
// RequestKRWDepositDetails returns Bithumb banking details for deposit
@@ -404,7 +397,7 @@ func (b *Bithumb) RequestKRWDepositDetails() (KRWDeposit, error) {
response := KRWDeposit{}
return response,
b.SendAuthenticatedHTTPRequest(privateKRWDeposit, nil, &response)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, privateKRWDeposit, nil, &response)
}
// RequestKRWWithdraw allows a customer KRW withdrawal request
@@ -421,7 +414,7 @@ func (b *Bithumb) RequestKRWWithdraw(bank, account string, price int64) (ActionS
params.Set("price", strconv.FormatInt(price, 10))
return response,
b.SendAuthenticatedHTTPRequest(privateKRWWithdraw, params, &response)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, privateKRWWithdraw, params, &response)
}
// MarketBuyOrder initiates a buy order through available order books
@@ -437,7 +430,7 @@ func (b *Bithumb) MarketBuyOrder(currency string, units float64) (MarketBuy, err
params.Set("units", strconv.FormatFloat(units, 'f', -1, 64))
return response,
b.SendAuthenticatedHTTPRequest(privateMarketBuy, params, &response)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, privateMarketBuy, params, &response)
}
// MarketSellOrder initiates a sell order through available order books
@@ -453,14 +446,18 @@ func (b *Bithumb) MarketSellOrder(currency string, units float64) (MarketSell, e
params.Set("units", strconv.FormatFloat(units, 'f', -1, 64))
return response,
b.SendAuthenticatedHTTPRequest(privateMarketSell, params, &response)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, privateMarketSell, params, &response)
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (b *Bithumb) SendHTTPRequest(path string, result interface{}) error {
func (b *Bithumb) SendHTTPRequest(ep exchange.URL, path string, result interface{}) error {
endpoint, err := b.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
return b.SendPayload(context.Background(), &request.Item{
Method: http.MethodGet,
Path: path,
Path: endpoint + path,
Result: result,
Verbose: b.Verbose,
HTTPDebugging: b.HTTPDebugging,
@@ -469,11 +466,14 @@ func (b *Bithumb) SendHTTPRequest(path string, result interface{}) error {
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to bithumb
func (b *Bithumb) SendAuthenticatedHTTPRequest(path string, params url.Values, result interface{}) error {
func (b *Bithumb) SendAuthenticatedHTTPRequest(ep exchange.URL, path string, params url.Values, result interface{}) error {
if !b.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name)
}
endpoint, err := b.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
if params == nil {
params = url.Values{}
}
@@ -501,9 +501,9 @@ func (b *Bithumb) SendAuthenticatedHTTPRequest(path string, params url.Values, r
Message string `json:"message"`
}{}
err := b.SendPayload(context.Background(), &request.Item{
err = b.SendPayload(context.Background(), &request.Item{
Method: http.MethodPost,
Path: b.API.Endpoints.URL + path,
Path: endpoint + path,
Headers: headers,
Body: bytes.NewBufferString(payload),
Result: &intermediary,
@@ -608,7 +608,7 @@ var errCode = map[string]string{
// GetCandleStick returns candle stick data for requested pair
func (b *Bithumb) GetCandleStick(symbol, interval string) (resp OHLCVResponse, err error) {
path := b.API.Endpoints.URL + publicCandleStick + symbol + "/" + interval
err = b.SendHTTPRequest(path, &resp)
path := publicCandleStick + symbol + "/" + interval
err = b.SendHTTPRequest(exchange.RestSpot, path, &resp)
return
}

View File

@@ -343,8 +343,9 @@ func TestFormatWithdrawPermissions(t *testing.T) {
func TestGetActiveOrders(t *testing.T) {
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Side: order.Sell,
Type: order.AnyType,
Side: order.Sell,
AssetType: asset.Spot,
}
_, err := b.GetActiveOrders(&getOrdersRequest)
@@ -358,7 +359,8 @@ func TestGetActiveOrders(t *testing.T) {
func TestGetOrderHistory(t *testing.T) {
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := b.GetOrderHistory(&getOrdersRequest)
@@ -457,12 +459,12 @@ func TestCancelAllExchangeOrders(t *testing.T) {
func TestGetAccountInfo(t *testing.T) {
t.Parallel()
if areTestAPIKeysSet() {
_, err := b.UpdateAccountInfo()
_, err := b.UpdateAccountInfo(asset.Spot)
if err != nil {
t.Error("Bithumb GetAccountInfo() error", err)
}
} else {
_, err := b.UpdateAccountInfo()
_, err := b.UpdateAccountInfo(asset.Spot)
if err == nil {
t.Error("Bithumb GetAccountInfo() Expected error")
}

View File

@@ -17,8 +17,8 @@ type Ticker struct {
PreviousClosingPrice float64 `json:"prev_closing_price,string"`
UnitsTraded24Hr float64 `json:"units_traded_24H,string"`
AccumulatedTradeValue24hr float64 `json:"acc_trade_value_24H,string"`
Fluctate24Hr string `json:"fluctate_24H"`
FluctateRate24hr float64 `json:"fluctate_rate_24H,string"`
Fluctuate24Hr float64 `json:"fluctate_24H,string"`
FluctuateRate24hr float64 `json:"fluctate_rate_24H,string"`
Date int64 `json:"date,string"`
}

View File

@@ -116,9 +116,13 @@ func (b *Bithumb) SetDefaults() {
b.Requester = request.New(b.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
request.WithLimiter(SetRateLimit()))
b.API.Endpoints.URLDefault = apiURL
b.API.Endpoints.URL = b.API.Endpoints.URLDefault
b.API.Endpoints = b.NewEndpoints()
err = b.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: apiURL,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
}
// Setup takes in the supplied exchange configuration details and sets params
@@ -204,7 +208,6 @@ func (b *Bithumb) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.P
fmt.Errorf("enabled pair %s [%s] not found in returned ticker map %v",
pairs[i], pairs, tickers)
}
err = ticker.ProcessTicker(&ticker.Price{
High: t.MaxPrice,
Low: t.MinPrice,
@@ -280,7 +283,7 @@ func (b *Bithumb) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*order
// UpdateAccountInfo retrieves balances for all enabled currencies for the
// Bithumb exchange
func (b *Bithumb) UpdateAccountInfo() (account.Holdings, error) {
func (b *Bithumb) UpdateAccountInfo(assetType asset.Item) (account.Holdings, error) {
var info account.Holdings
bal, err := b.GetAccountBalance("ALL")
if err != nil {
@@ -316,10 +319,10 @@ func (b *Bithumb) UpdateAccountInfo() (account.Holdings, error) {
}
// FetchAccountInfo retrieves balances for all enabled currencies
func (b *Bithumb) FetchAccountInfo() (account.Holdings, error) {
acc, err := account.GetHoldings(b.Name)
func (b *Bithumb) FetchAccountInfo(assetType asset.Item) (account.Holdings, error) {
acc, err := account.GetHoldings(b.Name, assetType)
if err != nil {
return b.UpdateAccountInfo()
return b.UpdateAccountInfo(assetType)
}
return acc, nil
@@ -680,8 +683,8 @@ func (b *Bithumb) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail,
// ValidateCredentials validates current credentials used for wrapper
// functionality
func (b *Bithumb) ValidateCredentials() error {
_, err := b.UpdateAccountInfo()
func (b *Bithumb) ValidateCredentials(assetType asset.Item) error {
_, err := b.UpdateAccountInfo(assetType)
return b.CheckTransientError(err)
}

View File

@@ -4,8 +4,10 @@ import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"time"
@@ -33,7 +35,7 @@ const (
bitmexEndpointTrollbox = "/chat"
bitmexEndpointTrollboxChannels = "/chat/channels"
bitmexEndpointTrollboxConnected = "/chat/connected"
bitmexEndpointFundingHistory = "/funding"
bitmexEndpointFundingHistory = "/funding?"
bitmexEndpointInstruments = "/instrument"
bitmexEndpointActiveInstruments = "/instrument/active"
bitmexEndpointActiveAndIndexInstruments = "/instrument/activeAndIndices"
@@ -106,7 +108,7 @@ const (
func (b *Bitmex) GetAnnouncement() ([]Announcement, error) {
var announcement []Announcement
return announcement, b.SendHTTPRequest(bitmexEndpointAnnouncement,
return announcement, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointAnnouncement,
nil,
&announcement)
}
@@ -115,7 +117,7 @@ func (b *Bitmex) GetAnnouncement() ([]Announcement, error) {
func (b *Bitmex) GetUrgentAnnouncement() ([]Announcement, error) {
var announcement []Announcement
return announcement, b.SendAuthenticatedHTTPRequest(http.MethodGet,
return announcement, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
bitmexEndpointAnnouncementUrgent,
nil,
&announcement)
@@ -125,7 +127,7 @@ func (b *Bitmex) GetUrgentAnnouncement() ([]Announcement, error) {
func (b *Bitmex) GetAPIKeys() ([]APIKey, error) {
var keys []APIKey
return keys, b.SendAuthenticatedHTTPRequest(http.MethodGet,
return keys, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
bitmexEndpointAPIkeys,
nil,
&keys)
@@ -135,7 +137,7 @@ func (b *Bitmex) GetAPIKeys() ([]APIKey, error) {
func (b *Bitmex) RemoveAPIKey(params APIKeyParams) (bool, error) {
var keyDeleted bool
return keyDeleted, b.SendAuthenticatedHTTPRequest(http.MethodDelete,
return keyDeleted, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodDelete,
bitmexEndpointAPIkeys,
&params,
&keyDeleted)
@@ -145,7 +147,7 @@ func (b *Bitmex) RemoveAPIKey(params APIKeyParams) (bool, error) {
func (b *Bitmex) DisableAPIKey(params APIKeyParams) (APIKey, error) {
var keyInfo APIKey
return keyInfo, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return keyInfo, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointDisableAPIkey,
&params,
&keyInfo)
@@ -155,7 +157,7 @@ func (b *Bitmex) DisableAPIKey(params APIKeyParams) (APIKey, error) {
func (b *Bitmex) EnableAPIKey(params APIKeyParams) (APIKey, error) {
var keyInfo APIKey
return keyInfo, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return keyInfo, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointEnableAPIkey,
&params,
&keyInfo)
@@ -165,14 +167,14 @@ func (b *Bitmex) EnableAPIKey(params APIKeyParams) (APIKey, error) {
func (b *Bitmex) GetTrollboxMessages(params ChatGetParams) ([]Chat, error) {
var messages []Chat
return messages, b.SendHTTPRequest(bitmexEndpointTrollbox, &params, &messages)
return messages, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointTrollbox, &params, &messages)
}
// SendTrollboxMessage sends a message to the bitmex trollbox
func (b *Bitmex) SendTrollboxMessage(params ChatSendParams) ([]Chat, error) {
var messages []Chat
return messages, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return messages, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointTrollboxSend,
&params,
&messages)
@@ -182,7 +184,7 @@ func (b *Bitmex) SendTrollboxMessage(params ChatSendParams) ([]Chat, error) {
func (b *Bitmex) GetTrollboxChannels() ([]ChatChannel, error) {
var channels []ChatChannel
return channels, b.SendHTTPRequest(bitmexEndpointTrollboxChannels,
return channels, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointTrollboxChannels,
nil,
&channels)
}
@@ -191,7 +193,7 @@ func (b *Bitmex) GetTrollboxChannels() ([]ChatChannel, error) {
func (b *Bitmex) GetTrollboxConnectedUsers() (ConnectedUsers, error) {
var users ConnectedUsers
return users, b.SendHTTPRequest(bitmexEndpointTrollboxConnected, nil, &users)
return users, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointTrollboxConnected, nil, &users)
}
// GetAccountExecutions returns all raw transactions, which includes order
@@ -200,7 +202,7 @@ func (b *Bitmex) GetTrollboxConnectedUsers() (ConnectedUsers, error) {
func (b *Bitmex) GetAccountExecutions(params *GenericRequestParams) ([]Execution, error) {
var executionList []Execution
return executionList, b.SendAuthenticatedHTTPRequest(http.MethodGet,
return executionList, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
bitmexEndpointExecution,
params,
&executionList)
@@ -211,17 +213,33 @@ func (b *Bitmex) GetAccountExecutions(params *GenericRequestParams) ([]Execution
func (b *Bitmex) GetAccountExecutionTradeHistory(params *GenericRequestParams) ([]Execution, error) {
var tradeHistory []Execution
return tradeHistory, b.SendAuthenticatedHTTPRequest(http.MethodGet,
return tradeHistory, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
bitmexEndpointExecutionTradeHistory,
params,
&tradeHistory)
}
// GetFullFundingHistory returns funding history
func (b *Bitmex) GetFullFundingHistory() ([]Funding, error) {
func (b *Bitmex) GetFullFundingHistory(symbol, count, filter, columns, start string, reverse bool, startTime, endTime time.Time) ([]Funding, error) {
var fundingHistory []Funding
return fundingHistory, b.SendHTTPRequest(bitmexEndpointFundingHistory,
params := url.Values{}
params.Set("symbol", symbol)
params.Set("count", count)
params.Set("filter", filter)
params.Set("columns", columns)
params.Set("start", start)
params.Set("reverse", "true")
if !reverse {
params.Set("reverse", "false")
}
if !startTime.IsZero() && !endTime.IsZero() {
if startTime.After(endTime) {
return nil, errors.New("startTime cannot be after endTime")
}
params.Set("startTime", startTime.Format(time.RFC3339))
params.Set("endTime", endTime.Format(time.RFC3339))
}
return fundingHistory, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointFundingHistory+params.Encode(),
nil,
&fundingHistory)
}
@@ -230,7 +248,7 @@ func (b *Bitmex) GetFullFundingHistory() ([]Funding, error) {
func (b *Bitmex) GetInstruments(params *GenericRequestParams) ([]Instrument, error) {
var instruments []Instrument
return instruments, b.SendHTTPRequest(bitmexEndpointInstruments,
return instruments, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointInstruments,
params,
&instruments)
}
@@ -239,7 +257,7 @@ func (b *Bitmex) GetInstruments(params *GenericRequestParams) ([]Instrument, err
func (b *Bitmex) GetActiveInstruments(params *GenericRequestParams) ([]Instrument, error) {
var activeInstruments []Instrument
return activeInstruments, b.SendHTTPRequest(bitmexEndpointActiveInstruments,
return activeInstruments, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointActiveInstruments,
params,
&activeInstruments)
}
@@ -249,7 +267,7 @@ func (b *Bitmex) GetActiveAndIndexInstruments() ([]Instrument, error) {
var activeAndIndices []Instrument
return activeAndIndices,
b.SendHTTPRequest(bitmexEndpointActiveAndIndexInstruments,
b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointActiveAndIndexInstruments,
nil,
&activeAndIndices)
}
@@ -258,17 +276,40 @@ func (b *Bitmex) GetActiveAndIndexInstruments() ([]Instrument, error) {
func (b *Bitmex) GetActiveIntervals() (InstrumentInterval, error) {
var interval InstrumentInterval
return interval, b.SendHTTPRequest(bitmexEndpointActiveIntervals,
return interval, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointActiveIntervals,
nil,
&interval)
}
// GetCompositeIndex returns composite index
func (b *Bitmex) GetCompositeIndex(params *GenericRequestParams) ([]IndexComposite, error) {
func (b *Bitmex) GetCompositeIndex(symbol, count, filter, columns, start, reverse string, startTime, endTime time.Time) ([]IndexComposite, error) {
var compositeIndices []IndexComposite
return compositeIndices, b.SendHTTPRequest(bitmexEndpointCompositeIndex,
params,
params := url.Values{}
params.Set("symbol", symbol)
if count != "" {
params.Set("count", count)
}
if filter != "" {
params.Set("filter", filter)
}
if columns != "" {
params.Set("columns", columns)
}
if start != "" {
params.Set("start", start)
}
if reverse != "" {
params.Set("reverse", "true")
}
if !startTime.IsZero() && !endTime.IsZero() {
if startTime.After(endTime) {
return nil, errors.New("startTime cannot be after endTime")
}
params.Set("startTime", startTime.Format(time.RFC3339))
params.Set("endTime", endTime.Format(time.RFC3339))
}
return compositeIndices, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointCompositeIndex+"?"+params.Encode(),
nil,
&compositeIndices)
}
@@ -276,35 +317,35 @@ func (b *Bitmex) GetCompositeIndex(params *GenericRequestParams) ([]IndexComposi
func (b *Bitmex) GetIndices() ([]Instrument, error) {
var indices []Instrument
return indices, b.SendHTTPRequest(bitmexEndpointIndices, nil, &indices)
return indices, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointIndices, nil, &indices)
}
// GetInsuranceFundHistory returns insurance fund history
func (b *Bitmex) GetInsuranceFundHistory(params *GenericRequestParams) ([]Insurance, error) {
var history []Insurance
return history, b.SendHTTPRequest(bitmexEndpointIndices, params, &history)
return history, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointIndices, params, &history)
}
// GetLeaderboard returns leaderboard information
func (b *Bitmex) GetLeaderboard(params LeaderboardGetParams) ([]Leaderboard, error) {
var leader []Leaderboard
return leader, b.SendHTTPRequest(bitmexEndpointLeader, params, &leader)
return leader, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointLeader, params, &leader)
}
// GetAliasOnLeaderboard returns your alias on the leaderboard
func (b *Bitmex) GetAliasOnLeaderboard() (Alias, error) {
var alias Alias
return alias, b.SendHTTPRequest(bitmexEndpointAlias, nil, &alias)
return alias, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointAlias, nil, &alias)
}
// GetLiquidationOrders returns liquidation orders
func (b *Bitmex) GetLiquidationOrders(params *GenericRequestParams) ([]Liquidation, error) {
var orders []Liquidation
return orders, b.SendHTTPRequest(bitmexEndpointLiquidation,
return orders, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointLiquidation,
params,
&orders)
}
@@ -313,7 +354,7 @@ func (b *Bitmex) GetLiquidationOrders(params *GenericRequestParams) ([]Liquidati
func (b *Bitmex) GetCurrentNotifications() ([]Notification, error) {
var notifications []Notification
return notifications, b.SendAuthenticatedHTTPRequest(http.MethodGet,
return notifications, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
bitmexEndpointNotifications,
nil,
&notifications)
@@ -322,7 +363,7 @@ func (b *Bitmex) GetCurrentNotifications() ([]Notification, error) {
// GetOrders returns all the orders, open and closed
func (b *Bitmex) GetOrders(params *OrdersRequest) ([]Order, error) {
var orders []Order
return orders, b.SendAuthenticatedHTTPRequest(http.MethodGet,
return orders, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
bitmexEndpointOrder,
params,
&orders)
@@ -332,7 +373,7 @@ func (b *Bitmex) GetOrders(params *OrdersRequest) ([]Order, error) {
func (b *Bitmex) AmendOrder(params *OrderAmendParams) (Order, error) {
var order Order
return order, b.SendAuthenticatedHTTPRequest(http.MethodPut,
return order, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPut,
bitmexEndpointOrder,
params,
&order)
@@ -342,7 +383,7 @@ func (b *Bitmex) AmendOrder(params *OrderAmendParams) (Order, error) {
func (b *Bitmex) CreateOrder(params *OrderNewParams) (Order, error) {
var orderInfo Order
return orderInfo, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return orderInfo, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointOrder,
params,
&orderInfo)
@@ -353,7 +394,7 @@ func (b *Bitmex) CreateOrder(params *OrderNewParams) (Order, error) {
func (b *Bitmex) CancelOrders(params *OrderCancelParams) ([]Order, error) {
var cancelledOrders []Order
return cancelledOrders, b.SendAuthenticatedHTTPRequest(http.MethodDelete,
return cancelledOrders, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodDelete,
bitmexEndpointOrder,
params,
&cancelledOrders)
@@ -363,7 +404,7 @@ func (b *Bitmex) CancelOrders(params *OrderCancelParams) ([]Order, error) {
func (b *Bitmex) CancelAllExistingOrders(params OrderCancelAllParams) ([]Order, error) {
var cancelledOrders []Order
return cancelledOrders, b.SendAuthenticatedHTTPRequest(http.MethodDelete,
return cancelledOrders, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodDelete,
bitmexEndpointCancelAllOrders,
params,
&cancelledOrders)
@@ -373,7 +414,7 @@ func (b *Bitmex) CancelAllExistingOrders(params OrderCancelAllParams) ([]Order,
func (b *Bitmex) AmendBulkOrders(params OrderAmendBulkParams) ([]Order, error) {
var amendedOrders []Order
return amendedOrders, b.SendAuthenticatedHTTPRequest(http.MethodPut,
return amendedOrders, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPut,
bitmexEndpointBulk,
params,
&amendedOrders)
@@ -383,7 +424,7 @@ func (b *Bitmex) AmendBulkOrders(params OrderAmendBulkParams) ([]Order, error) {
func (b *Bitmex) CreateBulkOrders(params OrderNewBulkParams) ([]Order, error) {
var orders []Order
return orders, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return orders, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointBulk,
params,
&orders)
@@ -393,7 +434,7 @@ func (b *Bitmex) CreateBulkOrders(params OrderNewBulkParams) ([]Order, error) {
func (b *Bitmex) CancelAllOrdersAfterTime(params OrderCancelAllAfterParams) ([]Order, error) {
var cancelledOrder []Order
return cancelledOrder, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return cancelledOrder, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointCancelOrderAfter,
params,
&cancelledOrder)
@@ -403,7 +444,7 @@ func (b *Bitmex) CancelAllOrdersAfterTime(params OrderCancelAllAfterParams) ([]O
func (b *Bitmex) ClosePosition(params OrderClosePositionParams) ([]Order, error) {
var closedPositions []Order
return closedPositions, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return closedPositions, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointOrder,
params,
&closedPositions)
@@ -413,7 +454,7 @@ func (b *Bitmex) ClosePosition(params OrderClosePositionParams) ([]Order, error)
func (b *Bitmex) GetOrderbook(params OrderBookGetL2Params) ([]OrderBookL2, error) {
var orderBooks []OrderBookL2
return orderBooks, b.SendHTTPRequest(bitmexEndpointOrderbookL2,
return orderBooks, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointOrderbookL2,
params,
&orderBooks)
}
@@ -422,7 +463,7 @@ func (b *Bitmex) GetOrderbook(params OrderBookGetL2Params) ([]OrderBookL2, error
func (b *Bitmex) GetPositions(params PositionGetParams) ([]Position, error) {
var positions []Position
return positions, b.SendAuthenticatedHTTPRequest(http.MethodGet,
return positions, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
bitmexEndpointPosition,
params,
&positions)
@@ -432,7 +473,7 @@ func (b *Bitmex) GetPositions(params PositionGetParams) ([]Position, error) {
func (b *Bitmex) IsolatePosition(params PositionIsolateMarginParams) (Position, error) {
var position Position
return position, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return position, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointIsolatePosition,
params,
&position)
@@ -442,7 +483,7 @@ func (b *Bitmex) IsolatePosition(params PositionIsolateMarginParams) (Position,
func (b *Bitmex) LeveragePosition(params PositionUpdateLeverageParams) (Position, error) {
var position Position
return position, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return position, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointLeveragePosition,
params,
&position)
@@ -452,7 +493,7 @@ func (b *Bitmex) LeveragePosition(params PositionUpdateLeverageParams) (Position
func (b *Bitmex) UpdateRiskLimit(params PositionUpdateRiskLimitParams) (Position, error) {
var position Position
return position, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return position, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointAdjustRiskLimit,
params,
&position)
@@ -462,7 +503,7 @@ func (b *Bitmex) UpdateRiskLimit(params PositionUpdateRiskLimitParams) (Position
func (b *Bitmex) TransferMargin(params PositionTransferIsolatedMarginParams) (Position, error) {
var position Position
return position, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return position, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointTransferMargin,
params,
&position)
@@ -472,7 +513,7 @@ func (b *Bitmex) TransferMargin(params PositionTransferIsolatedMarginParams) (Po
func (b *Bitmex) GetQuotes(params *GenericRequestParams) ([]Quote, error) {
var quotations []Quote
return quotations, b.SendHTTPRequest(bitmexEndpointQuote,
return quotations, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointQuote,
params,
&quotations)
}
@@ -481,7 +522,7 @@ func (b *Bitmex) GetQuotes(params *GenericRequestParams) ([]Quote, error) {
func (b *Bitmex) GetQuotesByBuckets(params *QuoteGetBucketedParams) ([]Quote, error) {
var quotations []Quote
return quotations, b.SendHTTPRequest(bitmexEndpointQuoteBucketed,
return quotations, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointQuoteBucketed,
params,
&quotations)
}
@@ -490,7 +531,7 @@ func (b *Bitmex) GetQuotesByBuckets(params *QuoteGetBucketedParams) ([]Quote, er
func (b *Bitmex) GetSettlementHistory(params *GenericRequestParams) ([]Settlement, error) {
var history []Settlement
return history, b.SendHTTPRequest(bitmexEndpointSettlement,
return history, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointSettlement,
params,
&history)
}
@@ -499,35 +540,35 @@ func (b *Bitmex) GetSettlementHistory(params *GenericRequestParams) ([]Settlemen
func (b *Bitmex) GetStats() ([]Stats, error) {
var stats []Stats
return stats, b.SendHTTPRequest(bitmexEndpointStats, nil, &stats)
return stats, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointStats, nil, &stats)
}
// GetStatsHistorical historic stats
func (b *Bitmex) GetStatsHistorical() ([]StatsHistory, error) {
var history []StatsHistory
return history, b.SendHTTPRequest(bitmexEndpointStatsHistory, nil, &history)
return history, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointStatsHistory, nil, &history)
}
// GetStatSummary returns the stats summary in USD terms
func (b *Bitmex) GetStatSummary() ([]StatsUSD, error) {
var summary []StatsUSD
return summary, b.SendHTTPRequest(bitmexEndpointStatsSummary, nil, &summary)
return summary, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointStatsSummary, nil, &summary)
}
// GetTrade returns executed trades on the desk
func (b *Bitmex) GetTrade(params *GenericRequestParams) ([]Trade, error) {
var trade []Trade
return trade, b.SendHTTPRequest(bitmexEndpointTrade, params, &trade)
return trade, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointTrade, params, &trade)
}
// GetPreviousTrades previous trade history in time buckets
func (b *Bitmex) GetPreviousTrades(params *TradeGetBucketedParams) ([]Trade, error) {
var trade []Trade
return trade, b.SendHTTPRequest(bitmexEndpointTradeBucketed,
return trade, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointTradeBucketed,
params,
&trade)
}
@@ -536,7 +577,7 @@ func (b *Bitmex) GetPreviousTrades(params *TradeGetBucketedParams) ([]Trade, err
func (b *Bitmex) GetUserInfo() (User, error) {
var userInfo User
return userInfo, b.SendAuthenticatedHTTPRequest(http.MethodGet,
return userInfo, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
bitmexEndpointUser,
nil,
&userInfo)
@@ -546,7 +587,7 @@ func (b *Bitmex) GetUserInfo() (User, error) {
func (b *Bitmex) UpdateUserInfo(params *UserUpdateParams) (User, error) {
var userInfo User
return userInfo, b.SendAuthenticatedHTTPRequest(http.MethodPut,
return userInfo, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPut,
bitmexEndpointUser,
params,
&userInfo)
@@ -556,7 +597,7 @@ func (b *Bitmex) UpdateUserInfo(params *UserUpdateParams) (User, error) {
func (b *Bitmex) GetAffiliateStatus() (AffiliateStatus, error) {
var status AffiliateStatus
return status, b.SendAuthenticatedHTTPRequest(http.MethodGet,
return status, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
bitmexEndpointUserAffiliate,
nil,
&status)
@@ -566,7 +607,7 @@ func (b *Bitmex) GetAffiliateStatus() (AffiliateStatus, error) {
func (b *Bitmex) CancelWithdraw(token string) (TransactionInfo, error) {
var info TransactionInfo
return info, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return info, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointUserCancelWithdraw,
UserTokenParams{Token: token},
&info)
@@ -577,7 +618,7 @@ func (b *Bitmex) CancelWithdraw(token string) (TransactionInfo, error) {
func (b *Bitmex) CheckReferalCode(referralCode string) (float64, error) {
var percentage float64
return percentage, b.SendHTTPRequest(bitmexEndpointUserCheckReferralCode,
return percentage, b.SendHTTPRequest(exchange.RestSpot, bitmexEndpointUserCheckReferralCode,
UserCheckReferralCodeParams{ReferralCode: referralCode},
&percentage)
}
@@ -586,7 +627,7 @@ func (b *Bitmex) CheckReferalCode(referralCode string) (float64, error) {
func (b *Bitmex) GetUserCommision() (UserCommission, error) {
var commissionInfo UserCommission
return commissionInfo, b.SendAuthenticatedHTTPRequest(http.MethodGet,
return commissionInfo, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
bitmexEndpointUserCommision,
nil,
&commissionInfo)
@@ -596,7 +637,7 @@ func (b *Bitmex) GetUserCommision() (UserCommission, error) {
func (b *Bitmex) ConfirmEmail(token string) (ConfirmEmail, error) {
var confirmation ConfirmEmail
return confirmation, b.SendAuthenticatedHTTPRequest(http.MethodGet,
return confirmation, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
bitmexEndpointUserConfirmEmail,
UserTokenParams{Token: token},
&confirmation)
@@ -606,7 +647,7 @@ func (b *Bitmex) ConfirmEmail(token string) (ConfirmEmail, error) {
func (b *Bitmex) ConfirmTwoFactorAuth(token, typ string) (bool, error) {
var working bool
return working, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return working, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointUserConfirmTFA,
UserConfirmTFAParams{Token: token, Type: typ},
&working)
@@ -616,7 +657,7 @@ func (b *Bitmex) ConfirmTwoFactorAuth(token, typ string) (bool, error) {
func (b *Bitmex) ConfirmWithdrawal(token string) (TransactionInfo, error) {
var info TransactionInfo
return info, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return info, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointUserCancelWithdraw,
UserTokenParams{Token: token},
&info)
@@ -632,7 +673,7 @@ func (b *Bitmex) GetCryptoDepositAddress(cryptoCurrency string) (string, error)
cryptoCurrency)
}
return address, b.SendAuthenticatedHTTPRequest(http.MethodGet,
return address, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
bitmexEndpointUserDepositAddress,
UserCurrencyParams{Currency: "XBt"},
&address)
@@ -642,7 +683,7 @@ func (b *Bitmex) GetCryptoDepositAddress(cryptoCurrency string) (string, error)
func (b *Bitmex) DisableTFA(token, typ string) (bool, error) {
var disabled bool
return disabled, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return disabled, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointUserDisableTFA,
UserConfirmTFAParams{Token: token, Type: typ},
&disabled)
@@ -650,7 +691,7 @@ func (b *Bitmex) DisableTFA(token, typ string) (bool, error) {
// UserLogOut logs you out of BitMEX
func (b *Bitmex) UserLogOut() error {
return b.SendAuthenticatedHTTPRequest(http.MethodPost,
return b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointUserLogout,
nil,
nil)
@@ -660,7 +701,7 @@ func (b *Bitmex) UserLogOut() error {
func (b *Bitmex) UserLogOutAll() (int64, error) {
var status int64
return status, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return status, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointUserLogoutAll,
nil,
&status)
@@ -670,7 +711,7 @@ func (b *Bitmex) UserLogOutAll() (int64, error) {
func (b *Bitmex) GetUserMargin(currency string) (UserMargin, error) {
var info UserMargin
return info, b.SendAuthenticatedHTTPRequest(http.MethodGet,
return info, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
bitmexEndpointUserMargin,
UserCurrencyParams{Currency: currency},
&info)
@@ -680,7 +721,7 @@ func (b *Bitmex) GetUserMargin(currency string) (UserMargin, error) {
func (b *Bitmex) GetAllUserMargin() ([]UserMargin, error) {
var info []UserMargin
return info, b.SendAuthenticatedHTTPRequest(http.MethodGet,
return info, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
bitmexEndpointUserMargin,
UserCurrencyParams{Currency: "all"},
&info)
@@ -690,7 +731,7 @@ func (b *Bitmex) GetAllUserMargin() ([]UserMargin, error) {
func (b *Bitmex) GetMinimumWithdrawalFee(currency string) (MinWithdrawalFee, error) {
var fee MinWithdrawalFee
return fee, b.SendAuthenticatedHTTPRequest(http.MethodGet,
return fee, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
bitmexEndpointUserMinWithdrawalFee,
UserCurrencyParams{Currency: currency},
&fee)
@@ -700,7 +741,7 @@ func (b *Bitmex) GetMinimumWithdrawalFee(currency string) (MinWithdrawalFee, err
func (b *Bitmex) GetUserPreferences(params UserPreferencesParams) (User, error) {
var userInfo User
return userInfo, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return userInfo, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointUserPreferences,
params,
&userInfo)
@@ -710,7 +751,7 @@ func (b *Bitmex) GetUserPreferences(params UserPreferencesParams) (User, error)
func (b *Bitmex) EnableTFA(typ string) (bool, error) {
var enabled bool
return enabled, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return enabled, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointUserRequestTFA,
UserConfirmTFAParams{Type: typ},
&enabled)
@@ -722,7 +763,7 @@ func (b *Bitmex) EnableTFA(typ string) (bool, error) {
func (b *Bitmex) UserRequestWithdrawal(params UserRequestWithdrawalParams) (TransactionInfo, error) {
var info TransactionInfo
return info, b.SendAuthenticatedHTTPRequest(http.MethodPost,
return info, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
bitmexEndpointUserRequestWithdraw,
params,
&info)
@@ -732,7 +773,7 @@ func (b *Bitmex) UserRequestWithdrawal(params UserRequestWithdrawalParams) (Tran
func (b *Bitmex) GetWalletInfo(currency string) (WalletInfo, error) {
var info WalletInfo
return info, b.SendAuthenticatedHTTPRequest(http.MethodGet,
return info, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
bitmexEndpointUserWallet,
UserCurrencyParams{Currency: currency},
&info)
@@ -742,7 +783,7 @@ func (b *Bitmex) GetWalletInfo(currency string) (WalletInfo, error) {
func (b *Bitmex) GetWalletHistory(currency string) ([]TransactionInfo, error) {
var info []TransactionInfo
return info, b.SendAuthenticatedHTTPRequest(http.MethodGet,
return info, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
bitmexEndpointUserWalletHistory,
UserCurrencyParams{Currency: currency},
&info)
@@ -752,19 +793,24 @@ func (b *Bitmex) GetWalletHistory(currency string) ([]TransactionInfo, error) {
func (b *Bitmex) GetWalletSummary(currency string) ([]TransactionInfo, error) {
var info []TransactionInfo
return info, b.SendAuthenticatedHTTPRequest(http.MethodGet,
return info, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
bitmexEndpointUserWalletSummary,
UserCurrencyParams{Currency: currency},
&info)
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (b *Bitmex) SendHTTPRequest(path string, params Parameter, result interface{}) error {
func (b *Bitmex) SendHTTPRequest(ep exchange.URL, path string, params Parameter, result interface{}) error {
var respCheck interface{}
path = b.API.Endpoints.URL + path
endpoint, err := b.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
path = endpoint + path
if params != nil {
var encodedPath string
if !params.IsNil() {
encodedPath, err := params.ToURLVals(path)
encodedPath, err = params.ToURLVals(path)
if err != nil {
return err
}
@@ -782,7 +828,8 @@ func (b *Bitmex) SendHTTPRequest(path string, params Parameter, result interface
return b.CaptureError(respCheck, result)
}
}
err := b.SendPayload(context.Background(), &request.Item{
err = b.SendPayload(context.Background(), &request.Item{
Method: http.MethodGet,
Path: path,
Result: &respCheck,
@@ -793,16 +840,20 @@ func (b *Bitmex) SendHTTPRequest(path string, params Parameter, result interface
if err != nil {
return err
}
return b.CaptureError(respCheck, result)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to bitmex
func (b *Bitmex) SendAuthenticatedHTTPRequest(verb, path string, params Parameter, result interface{}) error {
func (b *Bitmex) SendAuthenticatedHTTPRequest(ep exchange.URL, verb, path string, params Parameter, result interface{}) error {
if !b.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet,
b.Name)
}
endpoint, err := b.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
expires := time.Now().Add(time.Second * 10)
timestamp := expires.UnixNano()
timestampStr := strconv.FormatInt(timestamp, 10)
@@ -815,11 +866,12 @@ func (b *Bitmex) SendAuthenticatedHTTPRequest(verb, path string, params Paramete
var payload string
if params != nil {
err := params.VerifyData()
err = params.VerifyData()
if err != nil {
return err
}
data, err := json.Marshal(params)
var data []byte
data, err = json.Marshal(params)
if err != nil {
return err
}
@@ -836,9 +888,9 @@ func (b *Bitmex) SendAuthenticatedHTTPRequest(verb, path string, params Paramete
ctx, cancel := context.WithDeadline(context.Background(), expires)
defer cancel()
err := b.SendPayload(ctx, &request.Item{
err = b.SendPayload(ctx, &request.Item{
Method: verb,
Path: b.API.Endpoints.URL + path,
Path: endpoint + path,
Headers: headers,
Body: bytes.NewBuffer([]byte(payload)),
Result: &respCheck,

View File

@@ -56,12 +56,32 @@ func TestMain(m *testing.M) {
}
func TestStart(t *testing.T) {
t.Parallel()
var testWg sync.WaitGroup
b.Start(&testWg)
testWg.Wait()
}
func TestGetFullFundingHistory(t *testing.T) {
t.Parallel()
_, err := b.GetFullFundingHistory("", "", "", "", "", true, time.Time{}, time.Time{})
if err != nil {
t.Error(err)
}
_, err = b.GetFullFundingHistory("", "", "", "", "", true, time.Now().Add(-time.Hour*8), time.Now())
if err != nil {
t.Error(err)
}
_, err = b.GetFullFundingHistory("LTCUSD", "1", "", "", "", true, time.Now().Add(time.Hour*-24), time.Now())
if err != nil {
t.Error(err)
}
}
func TestGetUrgentAnnouncement(t *testing.T) {
t.Parallel()
_, err := b.GetUrgentAnnouncement()
if err == nil {
t.Error("GetUrgentAnnouncement() Expected error")
@@ -69,6 +89,7 @@ func TestGetUrgentAnnouncement(t *testing.T) {
}
func TestGetAPIKeys(t *testing.T) {
t.Parallel()
_, err := b.GetAPIKeys()
if err == nil {
t.Error("GetAPIKeys() Expected error")
@@ -76,6 +97,7 @@ func TestGetAPIKeys(t *testing.T) {
}
func TestRemoveAPIKey(t *testing.T) {
t.Parallel()
_, err := b.RemoveAPIKey(APIKeyParams{APIKeyID: "1337"})
if err == nil {
t.Error("RemoveAPIKey() Expected error")
@@ -83,6 +105,7 @@ func TestRemoveAPIKey(t *testing.T) {
}
func TestDisableAPIKey(t *testing.T) {
t.Parallel()
_, err := b.DisableAPIKey(APIKeyParams{APIKeyID: "1337"})
if err == nil {
t.Error("DisableAPIKey() Expected error")
@@ -90,6 +113,7 @@ func TestDisableAPIKey(t *testing.T) {
}
func TestEnableAPIKey(t *testing.T) {
t.Parallel()
_, err := b.EnableAPIKey(APIKeyParams{APIKeyID: "1337"})
if err == nil {
t.Error("EnableAPIKey() Expected error")
@@ -97,6 +121,7 @@ func TestEnableAPIKey(t *testing.T) {
}
func TestGetTrollboxMessages(t *testing.T) {
t.Parallel()
_, err := b.GetTrollboxMessages(ChatGetParams{Count: 5})
if err != nil {
t.Error("GetTrollboxMessages() error", err)
@@ -104,6 +129,7 @@ func TestGetTrollboxMessages(t *testing.T) {
}
func TestSendTrollboxMessage(t *testing.T) {
t.Parallel()
_, err := b.SendTrollboxMessage(ChatSendParams{
ChannelID: 1337,
Message: "Hello,World!"})
@@ -113,6 +139,7 @@ func TestSendTrollboxMessage(t *testing.T) {
}
func TestGetTrollboxChannels(t *testing.T) {
t.Parallel()
_, err := b.GetTrollboxChannels()
if err != nil {
t.Error("GetTrollboxChannels() error", err)
@@ -120,6 +147,7 @@ func TestGetTrollboxChannels(t *testing.T) {
}
func TestGetTrollboxConnectedUsers(t *testing.T) {
t.Parallel()
_, err := b.GetTrollboxConnectedUsers()
if err == nil {
t.Error("GetTrollboxConnectedUsers() Expected error")
@@ -127,6 +155,7 @@ func TestGetTrollboxConnectedUsers(t *testing.T) {
}
func TestGetAccountExecutions(t *testing.T) {
t.Parallel()
_, err := b.GetAccountExecutions(&GenericRequestParams{})
if err == nil {
t.Error("GetAccountExecutions() Expected error")
@@ -134,6 +163,7 @@ func TestGetAccountExecutions(t *testing.T) {
}
func TestGetAccountExecutionTradeHistory(t *testing.T) {
t.Parallel()
_, err := b.GetAccountExecutionTradeHistory(&GenericRequestParams{})
if err == nil {
t.Error("GetAccountExecutionTradeHistory() Expected error")
@@ -141,6 +171,7 @@ func TestGetAccountExecutionTradeHistory(t *testing.T) {
}
func TestGetFundingHistory(t *testing.T) {
t.Parallel()
_, err := b.GetFundingHistory()
if err == nil {
t.Error("GetFundingHistory() Expected error")
@@ -148,6 +179,7 @@ func TestGetFundingHistory(t *testing.T) {
}
func TestGetInstruments(t *testing.T) {
t.Parallel()
_, err := b.GetInstruments(&GenericRequestParams{})
if err != nil {
t.Error("GetInstruments() error", err)
@@ -155,6 +187,7 @@ func TestGetInstruments(t *testing.T) {
}
func TestGetActiveInstruments(t *testing.T) {
t.Parallel()
_, err := b.GetActiveInstruments(&GenericRequestParams{})
if err != nil {
t.Error("GetActiveInstruments() error", err)
@@ -162,6 +195,7 @@ func TestGetActiveInstruments(t *testing.T) {
}
func TestGetActiveAndIndexInstruments(t *testing.T) {
t.Parallel()
_, err := b.GetActiveAndIndexInstruments()
if err != nil {
t.Error("GetActiveAndIndexInstruments() error", err)
@@ -169,6 +203,7 @@ func TestGetActiveAndIndexInstruments(t *testing.T) {
}
func TestGetActiveIntervals(t *testing.T) {
t.Parallel()
_, err := b.GetActiveIntervals()
if err == nil {
t.Error("GetActiveIntervals() Expected error")
@@ -176,13 +211,15 @@ func TestGetActiveIntervals(t *testing.T) {
}
func TestGetCompositeIndex(t *testing.T) {
_, err := b.GetCompositeIndex(&GenericRequestParams{})
if err == nil {
t.Error("GetCompositeIndex() Expected error")
t.Parallel()
_, err := b.GetCompositeIndex(".XBT", "", "", "", "", "", time.Time{}, time.Time{})
if err != nil {
t.Error("GetCompositeIndex() Expected error", err)
}
}
func TestGetIndices(t *testing.T) {
t.Parallel()
_, err := b.GetIndices()
if err != nil {
t.Error("GetIndices() error", err)
@@ -190,6 +227,7 @@ func TestGetIndices(t *testing.T) {
}
func TestGetInsuranceFundHistory(t *testing.T) {
t.Parallel()
_, err := b.GetInsuranceFundHistory(&GenericRequestParams{})
if err != nil {
t.Error("GetInsuranceFundHistory() error", err)
@@ -197,6 +235,7 @@ func TestGetInsuranceFundHistory(t *testing.T) {
}
func TestGetLeaderboard(t *testing.T) {
t.Parallel()
_, err := b.GetLeaderboard(LeaderboardGetParams{})
if err != nil {
t.Error("GetLeaderboard() error", err)
@@ -204,6 +243,7 @@ func TestGetLeaderboard(t *testing.T) {
}
func TestGetAliasOnLeaderboard(t *testing.T) {
t.Parallel()
_, err := b.GetAliasOnLeaderboard()
if err == nil {
t.Error("GetAliasOnLeaderboard() Expected error")
@@ -211,6 +251,7 @@ func TestGetAliasOnLeaderboard(t *testing.T) {
}
func TestGetLiquidationOrders(t *testing.T) {
t.Parallel()
_, err := b.GetLiquidationOrders(&GenericRequestParams{})
if err != nil {
t.Error("GetLiquidationOrders() error", err)
@@ -218,6 +259,7 @@ func TestGetLiquidationOrders(t *testing.T) {
}
func TestGetCurrentNotifications(t *testing.T) {
t.Parallel()
_, err := b.GetCurrentNotifications()
if err == nil {
t.Error("GetCurrentNotifications() Expected error")
@@ -225,6 +267,7 @@ func TestGetCurrentNotifications(t *testing.T) {
}
func TestAmendOrder(t *testing.T) {
t.Parallel()
_, err := b.AmendOrder(&OrderAmendParams{})
if err == nil {
t.Error("AmendOrder() Expected error")
@@ -232,6 +275,7 @@ func TestAmendOrder(t *testing.T) {
}
func TestCreateOrder(t *testing.T) {
t.Parallel()
_, err := b.CreateOrder(&OrderNewParams{Symbol: "XBTM15",
Price: 219.0,
ClientOrderID: "mm_bitmex_1a/oemUeQ4CAJZgP3fjHsA",
@@ -242,6 +286,7 @@ func TestCreateOrder(t *testing.T) {
}
func TestCancelOrders(t *testing.T) {
t.Parallel()
_, err := b.CancelOrders(&OrderCancelParams{})
if err == nil {
t.Error("CancelOrders() Expected error")
@@ -249,6 +294,7 @@ func TestCancelOrders(t *testing.T) {
}
func TestCancelAllOrders(t *testing.T) {
t.Parallel()
_, err := b.CancelAllExistingOrders(OrderCancelAllParams{})
if err == nil {
t.Error("CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAllResponse, error)", err)
@@ -256,6 +302,7 @@ func TestCancelAllOrders(t *testing.T) {
}
func TestAmendBulkOrders(t *testing.T) {
t.Parallel()
_, err := b.AmendBulkOrders(OrderAmendBulkParams{})
if err == nil {
t.Error("AmendBulkOrders() Expected error")
@@ -263,6 +310,7 @@ func TestAmendBulkOrders(t *testing.T) {
}
func TestCreateBulkOrders(t *testing.T) {
t.Parallel()
_, err := b.CreateBulkOrders(OrderNewBulkParams{})
if err == nil {
t.Error("CreateBulkOrders() Expected error")
@@ -270,6 +318,7 @@ func TestCreateBulkOrders(t *testing.T) {
}
func TestCancelAllOrdersAfterTime(t *testing.T) {
t.Parallel()
_, err := b.CancelAllOrdersAfterTime(OrderCancelAllAfterParams{})
if err == nil {
t.Error("CancelAllOrdersAfterTime() Expected error")
@@ -277,6 +326,7 @@ func TestCancelAllOrdersAfterTime(t *testing.T) {
}
func TestClosePosition(t *testing.T) {
t.Parallel()
_, err := b.ClosePosition(OrderClosePositionParams{})
if err == nil {
t.Error("ClosePosition() Expected error")
@@ -284,6 +334,7 @@ func TestClosePosition(t *testing.T) {
}
func TestGetOrderbook(t *testing.T) {
t.Parallel()
_, err := b.GetOrderbook(OrderBookGetL2Params{Symbol: "XBT"})
if err != nil {
t.Error("GetOrderbook() error", err)
@@ -291,6 +342,7 @@ func TestGetOrderbook(t *testing.T) {
}
func TestGetPositions(t *testing.T) {
t.Parallel()
_, err := b.GetPositions(PositionGetParams{})
if err == nil {
t.Error("GetPositions() Expected error")
@@ -298,6 +350,7 @@ func TestGetPositions(t *testing.T) {
}
func TestIsolatePosition(t *testing.T) {
t.Parallel()
_, err := b.IsolatePosition(PositionIsolateMarginParams{Symbol: "XBT"})
if err == nil {
t.Error("IsolatePosition() Expected error")
@@ -305,6 +358,7 @@ func TestIsolatePosition(t *testing.T) {
}
func TestLeveragePosition(t *testing.T) {
t.Parallel()
_, err := b.LeveragePosition(PositionUpdateLeverageParams{})
if err == nil {
t.Error("LeveragePosition() Expected error")
@@ -312,6 +366,7 @@ func TestLeveragePosition(t *testing.T) {
}
func TestUpdateRiskLimit(t *testing.T) {
t.Parallel()
_, err := b.UpdateRiskLimit(PositionUpdateRiskLimitParams{})
if err == nil {
t.Error("UpdateRiskLimit() Expected error")
@@ -319,6 +374,7 @@ func TestUpdateRiskLimit(t *testing.T) {
}
func TestTransferMargin(t *testing.T) {
t.Parallel()
_, err := b.TransferMargin(PositionTransferIsolatedMarginParams{})
if err == nil {
t.Error("TransferMargin() Expected error")
@@ -326,6 +382,7 @@ func TestTransferMargin(t *testing.T) {
}
func TestGetQuotesByBuckets(t *testing.T) {
t.Parallel()
_, err := b.GetQuotesByBuckets(&QuoteGetBucketedParams{})
if err == nil {
t.Error("GetQuotesByBuckets() Expected error")
@@ -333,6 +390,7 @@ func TestGetQuotesByBuckets(t *testing.T) {
}
func TestGetSettlementHistory(t *testing.T) {
t.Parallel()
_, err := b.GetSettlementHistory(&GenericRequestParams{})
if err != nil {
t.Error("GetSettlementHistory() error", err)
@@ -340,6 +398,7 @@ func TestGetSettlementHistory(t *testing.T) {
}
func TestGetStats(t *testing.T) {
t.Parallel()
_, err := b.GetStats()
if err != nil {
t.Error("GetStats() error", err)
@@ -347,6 +406,7 @@ func TestGetStats(t *testing.T) {
}
func TestGetStatsHistorical(t *testing.T) {
t.Parallel()
_, err := b.GetStatsHistorical()
if err != nil {
t.Error("GetStatsHistorical() error", err)
@@ -354,6 +414,7 @@ func TestGetStatsHistorical(t *testing.T) {
}
func TestGetStatSummary(t *testing.T) {
t.Parallel()
_, err := b.GetStatSummary()
if err != nil {
t.Error("GetStatSummary() error", err)
@@ -361,6 +422,7 @@ func TestGetStatSummary(t *testing.T) {
}
func TestGetTrade(t *testing.T) {
t.Parallel()
_, err := b.GetTrade(&GenericRequestParams{
Symbol: "XBT",
Reverse: false,
@@ -372,6 +434,7 @@ func TestGetTrade(t *testing.T) {
}
func TestGetPreviousTrades(t *testing.T) {
t.Parallel()
_, err := b.GetPreviousTrades(&TradeGetBucketedParams{
Symbol: "XBTBTC",
Start: int32(time.Now().Add(-time.Hour * 24).Unix()),
@@ -393,6 +456,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
// TestGetFeeByTypeOfflineTradeFee logic test
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
b.GetFeeByType(feeBuilder)
if !areTestAPIKeysSet() {
@@ -407,6 +471,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
// CryptocurrencyTradeFee Basic
if resp, err := b.GetFee(feeBuilder); resp != float64(0.00075) || err != nil {
@@ -475,6 +540,7 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
t.Parallel()
expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.WithdrawCryptoWith2FAText +
" & " + exchange.WithdrawCryptoWithEmailText + " & " + exchange.NoFiatWithdrawalsText
withdrawPermissions := b.FormatWithdrawPermissions()
@@ -484,8 +550,10 @@ func TestFormatWithdrawPermissions(t *testing.T) {
}
func TestGetActiveOrders(t *testing.T) {
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := b.GetActiveOrders(&getOrdersRequest)
@@ -497,10 +565,12 @@ func TestGetActiveOrders(t *testing.T) {
}
func TestGetOrderHistory(t *testing.T) {
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Pairs: []currency.Pair{currency.NewPair(currency.LTC,
currency.BTC)},
AssetType: asset.Spot,
}
_, err := b.GetOrderHistory(&getOrdersRequest)
@@ -518,6 +588,7 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -543,6 +614,7 @@ func TestSubmitOrder(t *testing.T) {
}
func TestCancelExchangeOrder(t *testing.T) {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -566,6 +638,7 @@ func TestCancelExchangeOrder(t *testing.T) {
}
func TestCancelAllExchangeOrders(t *testing.T) {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -594,13 +667,14 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestGetAccountInfo(t *testing.T) {
t.Parallel()
if areTestAPIKeysSet() {
_, err := b.UpdateAccountInfo()
_, err := b.UpdateAccountInfo(asset.Spot)
if err != nil {
t.Error("GetAccountInfo() error", err)
}
} else {
_, err := b.UpdateAccountInfo()
_, err := b.UpdateAccountInfo(asset.Spot)
if err == nil {
t.Error("GetAccountInfo() error")
}
@@ -608,6 +682,7 @@ func TestGetAccountInfo(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -618,6 +693,7 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
t.Parallel()
withdrawCryptoRequest := withdraw.Request{
Crypto: withdraw.CryptoRequest{
Address: core.BitcoinDonationAddress,
@@ -642,6 +718,7 @@ func TestWithdraw(t *testing.T) {
}
func TestWithdrawFiat(t *testing.T) {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -654,6 +731,7 @@ func TestWithdrawFiat(t *testing.T) {
}
func TestWithdrawInternationalBank(t *testing.T) {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -666,6 +744,7 @@ func TestWithdrawInternationalBank(t *testing.T) {
}
func TestGetDepositAddress(t *testing.T) {
t.Parallel()
if areTestAPIKeysSet() {
_, err := b.GetDepositAddress(currency.BTC, "")
if err != nil {
@@ -681,6 +760,7 @@ func TestGetDepositAddress(t *testing.T) {
// TestWsAuth dials websocket, sends login request.
func TestWsAuth(t *testing.T) {
t.Parallel()
if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() {
t.Skip(stream.WebsocketNotEnabled)
}
@@ -708,6 +788,7 @@ func TestWsAuth(t *testing.T) {
}
func TestUpdateTradablePairs(t *testing.T) {
t.Parallel()
err := b.UpdateTradablePairs(true)
if err != nil {
t.Fatal(err)
@@ -715,6 +796,7 @@ func TestUpdateTradablePairs(t *testing.T) {
}
func TestWsPositionUpdate(t *testing.T) {
t.Parallel()
pressXToJSON := []byte(`{"table":"position",
"action":"update",
"data":[{
@@ -731,6 +813,7 @@ func TestWsPositionUpdate(t *testing.T) {
}
func TestWsInsertExectuionUpdate(t *testing.T) {
t.Parallel()
pressXToJSON := []byte(`{"table":"execution",
"action":"insert",
"data":[{
@@ -754,6 +837,7 @@ func TestWsInsertExectuionUpdate(t *testing.T) {
}
func TestWSConnectionHandling(t *testing.T) {
t.Parallel()
pressXToJSON := []byte(`{"info":"Welcome to the BitMEX Realtime API.","version":"1.1.0",
"timestamp":"2015-01-18T10:14:06.802Z","docs":"https://www.bitmex.com/app/wsAPI","heartbeatEnabled":false}`)
err := b.wsHandleData(pressXToJSON)
@@ -763,6 +847,7 @@ func TestWSConnectionHandling(t *testing.T) {
}
func TestWSSubscriptionHandling(t *testing.T) {
t.Parallel()
pressXToJSON := []byte(`{"success":true,"subscribe":"trade:ETHUSD",
"request":{"op":"subscribe","args":["trade:ETHUSD","instrument:ETHUSD"]}}`)
err := b.wsHandleData(pressXToJSON)
@@ -772,6 +857,7 @@ func TestWSSubscriptionHandling(t *testing.T) {
}
func TestWSPositionUpdateHandling(t *testing.T) {
t.Parallel()
pressXToJSON := []byte(`{"table":"position",
"action":"update",
"data":[{
@@ -804,6 +890,7 @@ func TestWSPositionUpdateHandling(t *testing.T) {
}
func TestWSOrderbookHandling(t *testing.T) {
t.Parallel()
pressXToJSON := []byte(`{
"table":"orderBookL2_25",
"keys":["symbol","id","side"],
@@ -877,6 +964,7 @@ func TestWSOrderbookHandling(t *testing.T) {
}
func TestWSDeleveragePositionUpdateHandling(t *testing.T) {
t.Parallel()
pressXToJSON := []byte(`{"table":"position",
"action":"update",
"data":[{
@@ -911,6 +999,7 @@ func TestWSDeleveragePositionUpdateHandling(t *testing.T) {
}
func TestWSDeleverageExecutionInsertHandling(t *testing.T) {
t.Parallel()
pressXToJSON := []byte(`{"table":"execution",
"action":"insert",
"data":[{
@@ -934,6 +1023,7 @@ func TestWSDeleverageExecutionInsertHandling(t *testing.T) {
}
func TestWsTrades(t *testing.T) {
t.Parallel()
pressXToJSON := []byte(`{"table":"trade","action":"insert","data":[{"timestamp":"2020-02-17T01:35:36.442Z","symbol":"ETHUSD","side":"Sell","size":100,"price":258.3,"tickDirection":"MinusTick","trdMatchID":"c427f7a0-6b26-1e10-5c4e-1bd74daf2a73","grossValue":2583000,"homeNotional":0.9904912836767037,"foreignNotional":255.84389857369254},{"timestamp":"2020-02-17T01:35:36.442Z","symbol":"ETHUSD","side":"Sell","size":100,"price":258.3,"tickDirection":"ZeroMinusTick","trdMatchID":"95eb9155-b58c-70e9-44b7-34efe50302e0","grossValue":2583000,"homeNotional":0.9904912836767037,"foreignNotional":255.84389857369254},{"timestamp":"2020-02-17T01:35:36.442Z","symbol":"ETHUSD","side":"Sell","size":100,"price":258.3,"tickDirection":"ZeroMinusTick","trdMatchID":"e607c187-f25c-86bc-cb39-8afff7aaf2d9","grossValue":2583000,"homeNotional":0.9904912836767037,"foreignNotional":255.84389857369254},{"timestamp":"2020-02-17T01:35:36.442Z","symbol":"ETHUSD","side":"Sell","size":17,"price":258.3,"tickDirection":"ZeroMinusTick","trdMatchID":"0f076814-a57d-9a59-8063-ad6b823a80ac","grossValue":439110,"homeNotional":0.1683835182250396,"foreignNotional":43.49346275752773},{"timestamp":"2020-02-17T01:35:36.442Z","symbol":"ETHUSD","side":"Sell","size":100,"price":258.25,"tickDirection":"MinusTick","trdMatchID":"f4ef3dfd-51c4-538f-37c1-e5071ba1c75d","grossValue":2582500,"homeNotional":0.9904912836767037,"foreignNotional":255.79437400950872},{"timestamp":"2020-02-17T01:35:36.442Z","symbol":"ETHUSD","side":"Sell","size":100,"price":258.25,"tickDirection":"ZeroMinusTick","trdMatchID":"81ef136b-8f4a-b1cf-78a8-fffbfa89bf40","grossValue":2582500,"homeNotional":0.9904912836767037,"foreignNotional":255.79437400950872},{"timestamp":"2020-02-17T01:35:36.442Z","symbol":"ETHUSD","side":"Sell","size":100,"price":258.25,"tickDirection":"ZeroMinusTick","trdMatchID":"65a87e8c-7563-34a4-d040-94e8513c5401","grossValue":2582500,"homeNotional":0.9904912836767037,"foreignNotional":255.79437400950872},{"timestamp":"2020-02-17T01:35:36.442Z","symbol":"ETHUSD","side":"Sell","size":15,"price":258.25,"tickDirection":"ZeroMinusTick","trdMatchID":"1d11a74e-a157-3f33-036d-35a101fba50b","grossValue":387375,"homeNotional":0.14857369255150554,"foreignNotional":38.369156101426306},{"timestamp":"2020-02-17T01:35:36.442Z","symbol":"ETHUSD","side":"Sell","size":1,"price":258.25,"tickDirection":"ZeroMinusTick","trdMatchID":"40d49df1-f018-f66f-4ca5-31d4997641d7","grossValue":25825,"homeNotional":0.009904912836767036,"foreignNotional":2.5579437400950873},{"timestamp":"2020-02-17T01:35:36.442Z","symbol":"ETHUSD","side":"Sell","size":100,"price":258.2,"tickDirection":"MinusTick","trdMatchID":"36135b51-73e5-c007-362b-a55be5830c6b","grossValue":2582000,"homeNotional":0.9904912836767037,"foreignNotional":255.7448494453249},{"timestamp":"2020-02-17T01:35:36.442Z","symbol":"ETHUSD","side":"Sell","size":100,"price":258.2,"tickDirection":"ZeroMinusTick","trdMatchID":"6ee19edb-99aa-3030-ba63-933ffb347ade","grossValue":2582000,"homeNotional":0.9904912836767037,"foreignNotional":255.7448494453249},{"timestamp":"2020-02-17T01:35:36.442Z","symbol":"ETHUSD","side":"Sell","size":100,"price":258.2,"tickDirection":"ZeroMinusTick","trdMatchID":"d44be603-cdb8-d676-e3e2-f91fb12b2a70","grossValue":2582000,"homeNotional":0.9904912836767037,"foreignNotional":255.7448494453249},{"timestamp":"2020-02-17T01:35:36.442Z","symbol":"ETHUSD","side":"Sell","size":5,"price":258.2,"tickDirection":"ZeroMinusTick","trdMatchID":"a14b43b3-50b4-c075-c54d-dfb0165de33d","grossValue":129100,"homeNotional":0.04952456418383518,"foreignNotional":12.787242472266245},{"timestamp":"2020-02-17T01:35:36.442Z","symbol":"ETHUSD","side":"Sell","size":8,"price":258.2,"tickDirection":"ZeroMinusTick","trdMatchID":"3c30e175-5194-320c-8f8c-01636c2f4a32","grossValue":206560,"homeNotional":0.07923930269413629,"foreignNotional":20.45958795562599},{"timestamp":"2020-02-17T01:35:36.442Z","symbol":"ETHUSD","side":"Sell","size":50,"price":258.2,"tickDirection":"ZeroMinusTick","trdMatchID":"5b803378-760b-4919-21fc-bfb275d39ace","grossValue":1291000,"homeNotional":0.49524564183835185,"foreignNotional":127.87242472266244},{"timestamp":"2020-02-17T01:35:36.442Z","symbol":"ETHUSD","side":"Sell","size":244,"price":258.2,"tickDirection":"ZeroMinusTick","trdMatchID":"cf57fec1-c444-b9e5-5e2d-4fb643f4fdb7","grossValue":6300080,"homeNotional":2.416798732171157,"foreignNotional":624.0174326465927}]}`)
err := b.wsHandleData(pressXToJSON)
if err != nil {

View File

@@ -119,10 +119,14 @@ func (b *Bitmex) SetDefaults() {
b.Requester = request.New(b.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
request.WithLimiter(SetRateLimit()))
b.API.Endpoints.URLDefault = bitmexAPIURL
b.API.Endpoints.URL = b.API.Endpoints.URLDefault
b.API.Endpoints.WebsocketURL = bitmexWSURL
b.API.Endpoints = b.NewEndpoints()
err = b.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: bitmexAPIURL,
exchange.WebsocketSpot: bitmexWSURL,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
b.Websocket = stream.New()
b.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
b.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
@@ -141,6 +145,11 @@ func (b *Bitmex) Setup(exch *config.ExchangeConfig) error {
return err
}
wsEndpoint, err := b.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
return err
}
err = b.Websocket.Setup(&stream.WebsocketSetup{
Enabled: exch.Features.Enabled.Websocket,
Verbose: exch.Verbose,
@@ -148,7 +157,7 @@ func (b *Bitmex) Setup(exch *config.ExchangeConfig) error {
WebsocketTimeout: exch.WebsocketTrafficTimeout,
DefaultURL: bitmexWSURL,
ExchangeName: exch.Name,
RunningURL: exch.API.Endpoints.WebsocketURL,
RunningURL: wsEndpoint,
Connector: b.WsConnect,
Subscriber: b.Subscribe,
UnSubscriber: b.Unsubscribe,
@@ -161,7 +170,6 @@ func (b *Bitmex) Setup(exch *config.ExchangeConfig) error {
if err != nil {
return err
}
return b.Websocket.SetupNewConnection(stream.ConnectionSetup{
ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
@@ -180,11 +188,15 @@ func (b *Bitmex) Start(wg *sync.WaitGroup) {
// Run implements the Bitmex wrapper
func (b *Bitmex) Run() {
if b.Verbose {
wsEndpoint, err := b.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
log.Error(log.ExchangeSys, err)
}
log.Debugf(log.ExchangeSys,
"%s Websocket: %s. (url: %s).\n",
b.Name,
common.IsEnabled(b.Websocket.IsEnabled()),
b.API.Endpoints.WebsocketURL)
wsEndpoint)
b.PrintEnabledPairs()
}
@@ -383,7 +395,7 @@ func (b *Bitmex) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderb
// UpdateAccountInfo retrieves balances for all enabled currencies for the
// Bitmex exchange
func (b *Bitmex) UpdateAccountInfo() (account.Holdings, error) {
func (b *Bitmex) UpdateAccountInfo(assetType asset.Item) (account.Holdings, error) {
var info account.Holdings
bal, err := b.GetAllUserMargin()
@@ -414,10 +426,10 @@ func (b *Bitmex) UpdateAccountInfo() (account.Holdings, error) {
}
// FetchAccountInfo retrieves balances for all enabled currencies
func (b *Bitmex) FetchAccountInfo() (account.Holdings, error) {
acc, err := account.GetHoldings(b.Name)
func (b *Bitmex) FetchAccountInfo(assetType asset.Item) (account.Holdings, error) {
acc, err := account.GetHoldings(b.Name, assetType)
if err != nil {
return b.UpdateAccountInfo()
return b.UpdateAccountInfo(assetType)
}
return acc, nil
@@ -786,8 +798,8 @@ func (b *Bitmex) AuthenticateWebsocket() error {
// ValidateCredentials validates current credentials used for wrapper
// functionality
func (b *Bitmex) ValidateCredentials() error {
_, err := b.UpdateAccountInfo()
func (b *Bitmex) ValidateCredentials(assetType asset.Item) error {
_, err := b.UpdateAccountInfo(assetType)
return b.CheckTransientError(err)
}

View File

@@ -147,15 +147,8 @@ func (b *Bitstamp) GetTicker(currency string, hourly bool) (*Ticker, error) {
if hourly {
tickerEndpoint = bitstampAPITickerHourly
}
path := fmt.Sprintf(
"%s/v%s/%s/%s/",
b.API.Endpoints.URL,
bitstampAPIVersion,
tickerEndpoint,
strings.ToLower(currency),
)
return &response, b.SendHTTPRequest(path, &response)
path := "/v" + bitstampAPIVersion + "/" + tickerEndpoint + "/" + strings.ToLower(currency) + "/"
return &response, b.SendHTTPRequest(exchange.RestSpot, path, &response)
}
// GetOrderbook Returns a JSON dictionary with "bids" and "asks". Each is a list
@@ -168,16 +161,8 @@ func (b *Bitstamp) GetOrderbook(currency string) (Orderbook, error) {
Asks [][]string `json:"asks"`
}
resp := response{}
path := fmt.Sprintf(
"%s/v%s/%s/%s/",
b.API.Endpoints.URL,
bitstampAPIVersion,
bitstampAPIOrderbook,
strings.ToLower(currency),
)
err := b.SendHTTPRequest(path, &resp)
path := "/v" + bitstampAPIVersion + "/" + bitstampAPIOrderbook + "/" + strings.ToLower(currency) + "/"
err := b.SendHTTPRequest(exchange.RestSpot, path, &resp)
if err != nil {
return Orderbook{}, err
}
@@ -220,13 +205,8 @@ func (b *Bitstamp) GetOrderbook(currency string) (Orderbook, error) {
// currently supports
func (b *Bitstamp) GetTradingPairs() ([]TradingPair, error) {
var result []TradingPair
path := fmt.Sprintf("%s/v%s/%s",
b.API.Endpoints.URL,
bitstampAPIVersion,
bitstampAPITradingPairsInfo)
return result, b.SendHTTPRequest(path, &result)
path := "/v" + bitstampAPIVersion + "/" + bitstampAPITradingPairsInfo
return result, b.SendHTTPRequest(exchange.RestSpot, path, &result)
}
// GetTransactions returns transaction information
@@ -234,36 +214,27 @@ func (b *Bitstamp) GetTradingPairs() ([]TradingPair, error) {
// response into time intervals.
func (b *Bitstamp) GetTransactions(currencyPair, timePeriod string) ([]Transactions, error) {
var transactions []Transactions
requestURL := fmt.Sprintf(
"%s/v%s/%s/%s/",
b.API.Endpoints.URL,
bitstampAPIVersion,
bitstampAPITransactions,
strings.ToLower(currencyPair),
)
requestURL := "/v" + bitstampAPIVersion + "/" + bitstampAPITransactions + "/" + strings.ToLower(currencyPair) + "/"
if timePeriod != "" {
requestURL += "?time=" + url.QueryEscape(timePeriod)
}
return transactions, b.SendHTTPRequest(requestURL, &transactions)
return transactions, b.SendHTTPRequest(exchange.RestSpot, requestURL, &transactions)
}
// GetEURUSDConversionRate returns the conversion rate between Euro and USD
func (b *Bitstamp) GetEURUSDConversionRate() (EURUSDConversionRate, error) {
rate := EURUSDConversionRate{}
path := fmt.Sprintf("%s/%s", b.API.Endpoints.URL, bitstampAPIEURUSD)
return rate, b.SendHTTPRequest(path, &rate)
path := "/" + bitstampAPIEURUSD
return rate, b.SendHTTPRequest(exchange.RestSpot, path, &rate)
}
// GetBalance returns full balance of currency held on the exchange
func (b *Bitstamp) GetBalance() (Balances, error) {
var balance map[string]string
err := b.SendAuthenticatedHTTPRequest(bitstampAPIBalance, true, nil, &balance)
err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, bitstampAPIBalance, true, nil, &balance)
if err != nil {
return nil, err
}
balances := make(map[string]Balance)
for k := range balance {
curr := k[0:3]
@@ -317,14 +288,14 @@ func (b *Bitstamp) GetUserTransactions(currencyPair string) ([]UserTransactions,
var response []Response
if currencyPair == "" {
if err := b.SendAuthenticatedHTTPRequest(bitstampAPIUserTransactions,
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, bitstampAPIUserTransactions,
true,
url.Values{},
&response); err != nil {
return nil, err
}
} else {
if err := b.SendAuthenticatedHTTPRequest(bitstampAPIUserTransactions+"/"+currencyPair,
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, bitstampAPIUserTransactions+"/"+currencyPair,
true,
url.Values{},
&response); err != nil {
@@ -366,11 +337,8 @@ func (b *Bitstamp) GetUserTransactions(currencyPair string) ([]UserTransactions,
// GetOpenOrders returns all open orders on the exchange
func (b *Bitstamp) GetOpenOrders(currencyPair string) ([]Order, error) {
var resp []Order
path := fmt.Sprintf(
"%s/%s", bitstampAPIOpenOrders, strings.ToLower(currencyPair),
)
return resp, b.SendAuthenticatedHTTPRequest(path, true, nil, &resp)
path := bitstampAPIOpenOrders + "/" + strings.ToLower(currencyPair)
return resp, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, path, true, nil, &resp)
}
// GetOrderStatus returns an the status of an order by its ID
@@ -380,7 +348,7 @@ func (b *Bitstamp) GetOrderStatus(orderID int64) (OrderStatus, error) {
req.Add("id", strconv.FormatInt(orderID, 10))
return resp,
b.SendAuthenticatedHTTPRequest(bitstampAPIOrderStatus, false, req, &resp)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, bitstampAPIOrderStatus, false, req, &resp)
}
// CancelExistingOrder cancels order by ID
@@ -389,7 +357,7 @@ func (b *Bitstamp) CancelExistingOrder(orderID int64) (CancelOrder, error) {
req.Add("id", strconv.FormatInt(orderID, 10))
var result CancelOrder
err := b.SendAuthenticatedHTTPRequest(bitstampAPICancelOrder, true, req, &result)
err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, bitstampAPICancelOrder, true, req, &result)
if err != nil {
return result, err
}
@@ -402,7 +370,7 @@ func (b *Bitstamp) CancelAllExistingOrders() (bool, error) {
result := false
return result,
b.SendAuthenticatedHTTPRequest(bitstampAPICancelAllOrders, false, nil, &result)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, bitstampAPICancelAllOrders, false, nil, &result)
}
// PlaceOrder places an order on the exchange.
@@ -419,13 +387,13 @@ func (b *Bitstamp) PlaceOrder(currencyPair string, price, amount float64, buy, m
var path string
if market {
path = fmt.Sprintf("%s/%s/%s", orderType, bitstampAPIMarket, strings.ToLower(currencyPair))
path = orderType + "/" + bitstampAPIMarket + strings.ToLower(currencyPair)
} else {
path = fmt.Sprintf("%s/%s", orderType, strings.ToLower(currencyPair))
path = orderType + "/" + orderType + strings.ToLower(currencyPair)
}
return response,
b.SendAuthenticatedHTTPRequest(path, true, req, &response)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, path, true, req, &response)
}
// GetWithdrawalRequests returns withdrawal requests for the account
@@ -445,7 +413,7 @@ func (b *Bitstamp) GetWithdrawalRequests(timedelta int64) ([]WithdrawalRequests,
}
return resp,
b.SendAuthenticatedHTTPRequest(bitstampAPIWithdrawalRequests, false, value, &resp)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, bitstampAPIWithdrawalRequests, false, value, &resp)
}
// CryptoWithdrawal withdraws a cryptocurrency into a supplied wallet, returns ID
@@ -484,7 +452,7 @@ func (b *Bitstamp) CryptoWithdrawal(amount float64, address, symbol, destTag str
return resp, errors.New("incorrect symbol")
}
return resp, b.SendAuthenticatedHTTPRequest(endpoint, false, req, &resp)
return resp, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, endpoint, false, req, &resp)
}
// OpenBankWithdrawal Opens a bank withdrawal request (SEPA or international)
@@ -505,7 +473,7 @@ func (b *Bitstamp) OpenBankWithdrawal(amount float64, currency,
req.Add("comment", comment)
resp := FIATWithdrawalResponse{}
return resp, b.SendAuthenticatedHTTPRequest(bitstampAPIOpenWithdrawal, true, req, &resp)
return resp, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, bitstampAPIOpenWithdrawal, true, req, &resp)
}
// OpenInternationalBankWithdrawal Opens a bank withdrawal request (international)
@@ -533,7 +501,7 @@ func (b *Bitstamp) OpenInternationalBankWithdrawal(amount float64, currency,
req.Add("bank_country", bankCountry)
resp := FIATWithdrawalResponse{}
return resp, b.SendAuthenticatedHTTPRequest(bitstampAPIOpenWithdrawal, true, req, &resp)
return resp, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, bitstampAPIOpenWithdrawal, true, req, &resp)
}
// GetCryptoDepositAddress returns a depositing address by crypto
@@ -547,23 +515,23 @@ func (b *Bitstamp) GetCryptoDepositAddress(crypto currency.Code) (string, error)
switch crypto {
case currency.BTC:
return resp,
b.SendAuthenticatedHTTPRequest(bitstampAPIBitcoinDeposit, false, nil, &resp)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, bitstampAPIBitcoinDeposit, false, nil, &resp)
case currency.LTC:
return v2Resp.Address,
b.SendAuthenticatedHTTPRequest(bitstampAPILitecoinDeposit, true, nil, &v2Resp)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, bitstampAPILitecoinDeposit, true, nil, &v2Resp)
case currency.ETH:
return v2Resp.Address,
b.SendAuthenticatedHTTPRequest(bitstampAPIEthereumDeposit, true, nil, &v2Resp)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, bitstampAPIEthereumDeposit, true, nil, &v2Resp)
case currency.XRP:
return v2Resp.Address,
b.SendAuthenticatedHTTPRequest(bitstampAPIXrpDeposit, true, nil, &v2Resp)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, bitstampAPIXrpDeposit, true, nil, &v2Resp)
case currency.BCH:
return v2Resp.Address,
b.SendAuthenticatedHTTPRequest(bitstampAPIBitcoinCashDeposit, true, nil, &v2Resp)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, bitstampAPIBitcoinCashDeposit, true, nil, &v2Resp)
default:
return resp, fmt.Errorf("unsupported cryptocurrency string %s", crypto)
@@ -575,7 +543,7 @@ func (b *Bitstamp) GetUnconfirmedBitcoinDeposits() ([]UnconfirmedBTCTransactions
var response []UnconfirmedBTCTransactions
return response,
b.SendAuthenticatedHTTPRequest(bitstampAPIUnconfirmedBitcoin, false, nil, &response)
b.SendAuthenticatedHTTPRequest(exchange.RestSpot, bitstampAPIUnconfirmedBitcoin, false, nil, &response)
}
// OHLC returns OHLCV data for step (interval)
@@ -593,7 +561,7 @@ func (b *Bitstamp) OHLC(currency string, start, end time.Time, step, limit strin
if !end.IsZero() {
v.Add("end", strconv.FormatInt(end.Unix(), 10))
}
return resp, b.SendHTTPRequest(common.EncodeURLValues(b.API.Endpoints.URL+"/v"+bitstampAPIVersion+"/"+bitstampOHLC+"/"+currency, v), &resp)
return resp, b.SendHTTPRequest(exchange.RestSpot, common.EncodeURLValues("/v"+bitstampAPIVersion+"/"+bitstampOHLC+"/"+currency, v), &resp)
}
// TransferAccountBalance transfers funds from either a main or sub account
@@ -621,14 +589,18 @@ func (b *Bitstamp) TransferAccountBalance(amount float64, currency, subAccount s
var resp interface{}
return b.SendAuthenticatedHTTPRequest(path, true, req, &resp)
return b.SendAuthenticatedHTTPRequest(exchange.RestSpot, path, true, req, &resp)
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (b *Bitstamp) SendHTTPRequest(path string, result interface{}) error {
func (b *Bitstamp) SendHTTPRequest(ep exchange.URL, path string, result interface{}) error {
endpoint, err := b.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
return b.SendPayload(context.Background(), &request.Item{
Method: http.MethodGet,
Path: path,
Path: endpoint + path,
Result: result,
Verbose: b.Verbose,
HTTPDebugging: b.HTTPDebugging,
@@ -637,11 +609,14 @@ func (b *Bitstamp) SendHTTPRequest(path string, result interface{}) error {
}
// SendAuthenticatedHTTPRequest sends an authenticated request
func (b *Bitstamp) SendAuthenticatedHTTPRequest(path string, v2 bool, values url.Values, result interface{}) error {
func (b *Bitstamp) SendAuthenticatedHTTPRequest(ep exchange.URL, path string, v2 bool, values url.Values, result interface{}) error {
if !b.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name)
}
endpoint, err := b.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
n := b.Requester.GetNonce(true).String()
if values == nil {
@@ -656,9 +631,9 @@ func (b *Bitstamp) SendAuthenticatedHTTPRequest(path string, v2 bool, values url
values.Set("signature", strings.ToUpper(crypto.HexEncodeToString(hmac)))
if v2 {
path = fmt.Sprintf("%s/v%s/%s/", b.API.Endpoints.URL, bitstampAPIVersion, path)
path = endpoint + "/v" + bitstampAPIVersion + "/" + path + "/"
} else {
path = fmt.Sprintf("%s/%s/", b.API.Endpoints.URL, path)
path = endpoint + "/" + path + "/"
}
if b.Verbose {
@@ -679,7 +654,7 @@ func (b *Bitstamp) SendAuthenticatedHTTPRequest(path string, v2 bool, values url
Reason interface{} `json:"reason"`
}{}
err := b.SendPayload(context.Background(), &request.Item{
err = b.SendPayload(context.Background(), &request.Item{
Method: http.MethodPost,
Path: path,
Headers: headers,

View File

@@ -35,6 +35,6 @@ func TestMain(m *testing.M) {
if err != nil {
log.Fatal("Bitstamp setup error", err)
}
log.Printf(sharedtestvalues.LiveTesting, b.Name, b.API.Endpoints.URL)
log.Printf(sharedtestvalues.LiveTesting, b.Name)
os.Exit(m.Run())
}

View File

@@ -46,7 +46,13 @@ func TestMain(m *testing.M) {
}
b.HTTPClient = newClient
b.API.Endpoints.URL = serverDetails + "/api"
log.Printf(sharedtestvalues.MockTesting, b.Name, b.API.Endpoints.URL)
endpointMap := b.API.Endpoints.GetURLMap()
for k := range endpointMap {
err = b.API.Endpoints.SetRunning(k, serverDetails+"/api")
if err != nil {
log.Fatal(err)
}
}
log.Printf(sharedtestvalues.MockTesting, b.Name)
os.Exit(m.Run())
}

View File

@@ -182,7 +182,6 @@ func TestGetTicker(t *testing.T) {
func TestGetOrderbook(t *testing.T) {
t.Parallel()
_, err := b.GetOrderbook(currency.BTC.String() + currency.USD.String())
if err != nil {
t.Error("GetOrderbook() error", err)
@@ -217,7 +216,6 @@ func TestGetEURUSDConversionRate(t *testing.T) {
func TestGetBalance(t *testing.T) {
t.Parallel()
_, err := b.GetBalance()
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
@@ -333,7 +331,8 @@ func TestGetActiveOrders(t *testing.T) {
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := b.GetActiveOrders(&getOrdersRequest)
@@ -351,7 +350,8 @@ func TestGetOrderHistory(t *testing.T) {
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := b.GetOrderHistory(&getOrdersRequest)

View File

@@ -33,7 +33,6 @@ func (b *Bitstamp) GetDefaultConfig() (*config.ExchangeConfig, error) {
exchCfg.Name = b.Name
exchCfg.HTTPTimeout = exchange.DefaultHTTPTimeout
exchCfg.BaseCurrencies = b.BaseCurrencies
err := b.SetupDefaults(exchCfg)
if err != nil {
return nil, err
@@ -57,7 +56,6 @@ func (b *Bitstamp) SetDefaults() {
b.API.CredentialsValidator.RequiresKey = true
b.API.CredentialsValidator.RequiresSecret = true
b.API.CredentialsValidator.RequiresClientID = true
requestFmt := &currency.PairFormat{Uppercase: true}
configFmt := &currency.PairFormat{Uppercase: true}
err := b.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot)
@@ -129,10 +127,14 @@ func (b *Bitstamp) SetDefaults() {
b.Requester = request.New(b.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
request.WithLimiter(request.NewBasicRateLimit(bitstampRateInterval, bitstampRequestRate)))
b.API.Endpoints.URLDefault = bitstampAPIURL
b.API.Endpoints.URL = b.API.Endpoints.URLDefault
b.API.Endpoints.WebsocketURL = bitstampWSURL
b.API.Endpoints = b.NewEndpoints()
err = b.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: bitstampAPIURL,
exchange.WebsocketSpot: bitstampWSURL,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
b.Websocket = stream.New()
b.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
b.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
@@ -151,6 +153,11 @@ func (b *Bitstamp) Setup(exch *config.ExchangeConfig) error {
return err
}
wsURL, err := b.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
return err
}
err = b.Websocket.Setup(&stream.WebsocketSetup{
Enabled: exch.Features.Enabled.Websocket,
Verbose: exch.Verbose,
@@ -158,7 +165,7 @@ func (b *Bitstamp) Setup(exch *config.ExchangeConfig) error {
WebsocketTimeout: exch.WebsocketTrafficTimeout,
DefaultURL: bitstampWSURL,
ExchangeName: exch.Name,
RunningURL: exch.API.Endpoints.WebsocketURL,
RunningURL: wsURL,
Connector: b.WsConnect,
Subscriber: b.Subscribe,
UnSubscriber: b.Unsubscribe,
@@ -354,7 +361,7 @@ func (b *Bitstamp) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orde
// UpdateAccountInfo retrieves balances for all enabled currencies for the
// Bitstamp exchange
func (b *Bitstamp) UpdateAccountInfo() (account.Holdings, error) {
func (b *Bitstamp) UpdateAccountInfo(assetType asset.Item) (account.Holdings, error) {
var response account.Holdings
response.Exchange = b.Name
accountBalance, err := b.GetBalance()
@@ -383,10 +390,10 @@ func (b *Bitstamp) UpdateAccountInfo() (account.Holdings, error) {
}
// FetchAccountInfo retrieves balances for all enabled currencies
func (b *Bitstamp) FetchAccountInfo() (account.Holdings, error) {
acc, err := account.GetHoldings(b.Name)
func (b *Bitstamp) FetchAccountInfo(assetType asset.Item) (account.Holdings, error) {
acc, err := account.GetHoldings(b.Name, assetType)
if err != nil {
return b.UpdateAccountInfo()
return b.UpdateAccountInfo(assetType)
}
return acc, nil
@@ -775,8 +782,8 @@ func (b *Bitstamp) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail,
// ValidateCredentials validates current credentials used for wrapper
// functionality
func (b *Bitstamp) ValidateCredentials() error {
_, err := b.UpdateAccountInfo()
func (b *Bitstamp) ValidateCredentials(assetType asset.Item) error {
_, err := b.UpdateAccountInfo(assetType)
return b.CheckTransientError(err)
}

View File

@@ -17,6 +17,8 @@ import (
)
const (
spotURL = "spotAPIURL"
spotWSURL = "spotWSURL"
bittrexAPIURL = "https://bittrex.com/api/v1.1"
bittrexAPIVersion = "v1.1"
bittrexMaxOpenOrders = 500
@@ -68,9 +70,8 @@ type Bittrex struct {
// along with other meta data.
func (b *Bittrex) GetMarkets() (Market, error) {
var markets Market
path := fmt.Sprintf("%s/%s/", b.API.Endpoints.URL, bittrexAPIGetMarkets)
if err := b.SendHTTPRequest(path, &markets); err != nil {
if err := b.SendHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetMarkets+"/", &markets); err != nil {
return markets, err
}
@@ -83,9 +84,8 @@ func (b *Bittrex) GetMarkets() (Market, error) {
// GetCurrencies is used to get all supported currencies at Bittrex
func (b *Bittrex) GetCurrencies() (Currency, error) {
var currencies Currency
path := fmt.Sprintf("%s/%s/", b.API.Endpoints.URL, bittrexAPIGetCurrencies)
if err := b.SendHTTPRequest(path, &currencies); err != nil {
if err := b.SendHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetCurrencies+"/", &currencies); err != nil {
return currencies, err
}
@@ -99,11 +99,9 @@ func (b *Bittrex) GetCurrencies() (Currency, error) {
// on the supplied currency. Example currency input param "btc-ltc".
func (b *Bittrex) GetTicker(currencyPair string) (Ticker, error) {
tick := Ticker{}
path := fmt.Sprintf("%s/%s?market=%s", b.API.Endpoints.URL, bittrexAPIGetTicker,
strings.ToUpper(currencyPair),
)
path := "/" + bittrexAPIGetTicker + "?market=" + strings.ToUpper(currencyPair)
if err := b.SendHTTPRequest(path, &tick); err != nil {
if err := b.SendHTTPRequest(exchange.RestSpot, path, &tick); err != nil {
return tick, err
}
@@ -117,9 +115,8 @@ func (b *Bittrex) GetTicker(currencyPair string) (Ticker, error) {
// exchanges
func (b *Bittrex) GetMarketSummaries() (MarketSummary, error) {
var summaries MarketSummary
path := fmt.Sprintf("%s/%s/", b.API.Endpoints.URL, bittrexAPIGetMarketSummaries)
if err := b.SendHTTPRequest(path, &summaries); err != nil {
if err := b.SendHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetMarketSummaries+"/", &summaries); err != nil {
return summaries, err
}
@@ -133,11 +130,7 @@ func (b *Bittrex) GetMarketSummaries() (MarketSummary, error) {
// exchanges by currency pair (btc-ltc).
func (b *Bittrex) GetMarketSummary(currencyPair string) (MarketSummary, error) {
var summary MarketSummary
path := fmt.Sprintf("%s/%s?market=%s", b.API.Endpoints.URL,
bittrexAPIGetMarketSummary, strings.ToLower(currencyPair),
)
if err := b.SendHTTPRequest(path, &summary); err != nil {
if err := b.SendHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetMarketSummary+"?market="+strings.ToLower(currencyPair), &summary); err != nil {
return summary, err
}
@@ -156,11 +149,8 @@ func (b *Bittrex) GetMarketSummary(currencyPair string) (MarketSummary, error) {
// it returns full depth. So depth default is 50.
func (b *Bittrex) GetOrderbook(currencyPair string) (OrderBooks, error) {
var orderbooks OrderBooks
path := fmt.Sprintf("%s/%s?market=%s&type=both&depth=50", b.API.Endpoints.URL,
bittrexAPIGetOrderbook, strings.ToUpper(currencyPair),
)
if err := b.SendHTTPRequest(path, &orderbooks); err != nil {
path := "/" + bittrexAPIGetOrderbook + "?market=" + strings.ToLower(currencyPair) + "&type=both&depth=50"
if err := b.SendHTTPRequest(exchange.RestSpot, path, &orderbooks); err != nil {
return orderbooks, err
}
@@ -174,11 +164,8 @@ func (b *Bittrex) GetOrderbook(currencyPair string) (OrderBooks, error) {
// market
func (b *Bittrex) GetMarketHistory(currencyPair string) (MarketHistory, error) {
var marketHistoriae MarketHistory
path := fmt.Sprintf("%s/%s?market=%s", b.API.Endpoints.URL,
bittrexAPIGetMarketHistory, strings.ToUpper(currencyPair),
)
if err := b.SendHTTPRequest(path, &marketHistoriae); err != nil {
path := "/" + bittrexAPIGetMarketHistory + "?market=" + strings.ToUpper(currencyPair)
if err := b.SendHTTPRequest(exchange.RestSpot, path, &marketHistoriae); err != nil {
return marketHistoriae, err
}
@@ -200,9 +187,7 @@ func (b *Bittrex) PlaceBuyLimit(currencyPair string, quantity, rate float64) (UU
values.Set("market", currencyPair)
values.Set("quantity", strconv.FormatFloat(quantity, 'E', -1, 64))
values.Set("rate", strconv.FormatFloat(rate, 'E', -1, 64))
path := fmt.Sprintf("%s/%s", b.API.Endpoints.URL, bittrexAPIBuyLimit)
if err := b.SendAuthenticatedHTTPRequest(path, values, &id); err != nil {
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIBuyLimit, values, &id); err != nil {
return id, err
}
@@ -224,9 +209,7 @@ func (b *Bittrex) PlaceSellLimit(currencyPair string, quantity, rate float64) (U
values.Set("market", currencyPair)
values.Set("quantity", strconv.FormatFloat(quantity, 'E', -1, 64))
values.Set("rate", strconv.FormatFloat(rate, 'E', -1, 64))
path := fmt.Sprintf("%s/%s", b.API.Endpoints.URL, bittrexAPISellLimit)
if err := b.SendAuthenticatedHTTPRequest(path, values, &id); err != nil {
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPISellLimit, values, &id); err != nil {
return id, err
}
@@ -244,9 +227,8 @@ func (b *Bittrex) GetOpenOrders(currencyPair string) (Order, error) {
if !(currencyPair == "" || currencyPair == " ") {
values.Set("market", currencyPair)
}
path := fmt.Sprintf("%s/%s", b.API.Endpoints.URL, bittrexAPIGetOpenOrders)
if err := b.SendAuthenticatedHTTPRequest(path, values, &orders); err != nil {
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetOpenOrders, values, &orders); err != nil {
return orders, err
}
@@ -261,9 +243,8 @@ func (b *Bittrex) CancelExistingOrder(uuid string) (Balances, error) {
var balances Balances
values := url.Values{}
values.Set("uuid", uuid)
path := fmt.Sprintf("%s/%s", b.API.Endpoints.URL, bittrexAPICancel)
if err := b.SendAuthenticatedHTTPRequest(path, values, &balances); err != nil {
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPICancel, values, &balances); err != nil {
return balances, err
}
@@ -276,9 +257,8 @@ func (b *Bittrex) CancelExistingOrder(uuid string) (Balances, error) {
// GetAccountBalances is used to retrieve all balances from your account
func (b *Bittrex) GetAccountBalances() (Balances, error) {
var balances Balances
path := fmt.Sprintf("%s/%s", b.API.Endpoints.URL, bittrexAPIGetBalances)
if err := b.SendAuthenticatedHTTPRequest(path, url.Values{}, &balances); err != nil {
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetBalances, url.Values{}, &balances); err != nil {
return balances, err
}
@@ -294,9 +274,8 @@ func (b *Bittrex) GetAccountBalanceByCurrency(currency string) (Balance, error)
var balance Balance
values := url.Values{}
values.Set("currency", currency)
path := fmt.Sprintf("%s/%s", b.API.Endpoints.URL, bittrexAPIGetBalance)
if err := b.SendAuthenticatedHTTPRequest(path, values, &balance); err != nil {
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetBalance, values, &balance); err != nil {
return balance, err
}
@@ -313,9 +292,8 @@ func (b *Bittrex) GetCryptoDepositAddress(currency string) (DepositAddress, erro
var address DepositAddress
values := url.Values{}
values.Set("currency", currency)
path := fmt.Sprintf("%s/%s", b.API.Endpoints.URL, bittrexAPIGetDepositAddress)
if err := b.SendAuthenticatedHTTPRequest(path, values, &address); err != nil {
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetDepositAddress, values, &address); err != nil {
return address, err
}
@@ -337,9 +315,7 @@ func (b *Bittrex) Withdraw(currency, paymentID, address string, quantity float64
values.Set("paymentid", paymentID)
}
path := fmt.Sprintf("%s/%s", b.API.Endpoints.URL, bittrexAPIWithdraw)
if err := b.SendAuthenticatedHTTPRequest(path, values, &id); err != nil {
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIWithdraw, values, &id); err != nil {
return id, err
}
@@ -354,9 +330,8 @@ func (b *Bittrex) GetOrder(uuid string) (Order, error) {
var order Order
values := url.Values{}
values.Set("uuid", uuid)
path := fmt.Sprintf("%s/%s", b.API.Endpoints.URL, bittrexAPIGetOrder)
if err := b.SendAuthenticatedHTTPRequest(path, values, &order); err != nil {
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetOrder, values, &order); err != nil {
return order, err
}
@@ -375,9 +350,8 @@ func (b *Bittrex) GetOrderHistoryForCurrency(currencyPair string) (Order, error)
if !(currencyPair == "" || currencyPair == " ") {
values.Set("market", currencyPair)
}
path := fmt.Sprintf("%s/%s", b.API.Endpoints.URL, bittrexAPIGetOrderHistory)
if err := b.SendAuthenticatedHTTPRequest(path, values, &orders); err != nil {
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetOrderHistory, values, &orders); err != nil {
return orders, err
}
@@ -396,9 +370,8 @@ func (b *Bittrex) GetWithdrawalHistory(currency string) (WithdrawalHistory, erro
if !(currency == "" || currency == " ") {
values.Set("currency", currency)
}
path := fmt.Sprintf("%s/%s", b.API.Endpoints.URL, bittrexAPIGetWithdrawalHistory)
if err := b.SendAuthenticatedHTTPRequest(path, values, &history); err != nil {
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetWithdrawalHistory, values, &history); err != nil {
return history, err
}
@@ -417,9 +390,8 @@ func (b *Bittrex) GetDepositHistory(currency string) (DepositHistory, error) {
if !(currency == "" || currency == " ") {
values.Set("currency", currency)
}
path := fmt.Sprintf("%s/%s", b.API.Endpoints.URL, bittrexAPIGetDepositHistory)
if err := b.SendAuthenticatedHTTPRequest(path, values, &history); err != nil {
if err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, "/"+bittrexAPIGetDepositHistory, values, &history); err != nil {
return history, err
}
@@ -430,10 +402,14 @@ func (b *Bittrex) GetDepositHistory(currency string) (DepositHistory, error) {
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (b *Bittrex) SendHTTPRequest(path string, result interface{}) error {
func (b *Bittrex) SendHTTPRequest(ep exchange.URL, path string, result interface{}) error {
endpoint, err := b.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
return b.SendPayload(context.Background(), &request.Item{
Method: http.MethodGet,
Path: path,
Path: endpoint + path,
Result: result,
Verbose: b.Verbose,
HTTPDebugging: b.HTTPDebugging,
@@ -443,16 +419,19 @@ func (b *Bittrex) SendHTTPRequest(path string, result interface{}) error {
// SendAuthenticatedHTTPRequest sends an authenticated http request to a desired
// path
func (b *Bittrex) SendAuthenticatedHTTPRequest(path string, values url.Values, result interface{}) (err error) {
func (b *Bittrex) SendAuthenticatedHTTPRequest(ep exchange.URL, path string, values url.Values, result interface{}) (err error) {
if !b.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name)
}
endpoint, err := b.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
n := b.Requester.GetNonce(true).String()
values.Set("apikey", b.API.Credentials.Key)
values.Set("nonce", n)
rawQuery := path + "?" + values.Encode()
rawQuery := endpoint + path + "?" + values.Encode()
hmac := crypto.GetHMAC(
crypto.HashSHA512, []byte(rawQuery), []byte(b.API.Credentials.Secret),
)

View File

@@ -351,8 +351,9 @@ func TestGetActiveOrders(t *testing.T) {
}
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Pairs: []currency.Pair{p},
Type: order.AnyType,
Pairs: []currency.Pair{p},
AssetType: asset.Spot,
}
getOrdersRequest.Pairs[0].Delimiter = "-"
@@ -367,7 +368,8 @@ func TestGetActiveOrders(t *testing.T) {
func TestGetOrderHistory(t *testing.T) {
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := b.GetOrderHistory(&getOrdersRequest)

View File

@@ -96,9 +96,13 @@ func (b *Bittrex) SetDefaults() {
b.Requester = request.New(b.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
request.WithLimiter(request.NewBasicRateLimit(bittrexRateInterval, bittrexRequestRate)))
b.API.Endpoints.URLDefault = bittrexAPIURL
b.API.Endpoints.URL = b.API.Endpoints.URLDefault
b.API.Endpoints = b.NewEndpoints()
err = b.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: bittrexAPIURL,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
}
// Setup method sets current configuration details if enabled
@@ -224,7 +228,7 @@ func (b *Bittrex) UpdateTradablePairs(forceUpdate bool) error {
// UpdateAccountInfo Retrieves balances for all enabled currencies for the
// Bittrex exchange
func (b *Bittrex) UpdateAccountInfo() (account.Holdings, error) {
func (b *Bittrex) UpdateAccountInfo(assetType asset.Item) (account.Holdings, error) {
var response account.Holdings
response.Exchange = b.Name
accountBalance, err := b.GetAccountBalances()
@@ -254,10 +258,10 @@ func (b *Bittrex) UpdateAccountInfo() (account.Holdings, error) {
}
// FetchAccountInfo retrieves balances for all enabled currencies
func (b *Bittrex) FetchAccountInfo() (account.Holdings, error) {
acc, err := account.GetHoldings(b.Name)
func (b *Bittrex) FetchAccountInfo(assetType asset.Item) (account.Holdings, error) {
acc, err := account.GetHoldings(b.Name, assetType)
if err != nil {
return b.UpdateAccountInfo()
return b.UpdateAccountInfo(assetType)
}
return acc, nil
@@ -702,8 +706,8 @@ func (b *Bittrex) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail,
// ValidateCredentials validates current credentials used for wrapper
// functionality
func (b *Bittrex) ValidateCredentials() error {
_, err := b.UpdateAccountInfo()
func (b *Bittrex) ValidateCredentials(assetType asset.Item) error {
_, err := b.UpdateAccountInfo(assetType)
return b.CheckTransientError(err)
}

View File

@@ -49,7 +49,7 @@ func TestMain(m *testing.M) {
if err != nil {
log.Fatal(err)
}
err = b.ValidateCredentials()
err = b.ValidateCredentials(asset.Spot)
if err != nil {
fmt.Println("API credentials are invalid:", err)
b.API.AuthenticatedSupport = false
@@ -456,7 +456,7 @@ func TestFetchAccountInfo(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("API keys required but not set, skipping test")
}
_, err := b.FetchAccountInfo()
_, err := b.FetchAccountInfo(asset.Spot)
if err != nil {
t.Error(err)
}
@@ -469,7 +469,8 @@ func TestGetOrderHistory(t *testing.T) {
}
_, err := b.GetOrderHistory(&order.GetOrdersRequest{
Side: order.Buy,
Side: order.Buy,
AssetType: asset.Spot,
})
if err != nil {
t.Error(err)
@@ -500,7 +501,7 @@ func TestGetActiveOrders(t *testing.T) {
t.Skip("API keys required but not set, skipping test")
}
_, err := b.GetActiveOrders(&order.GetOrdersRequest{})
_, err := b.GetActiveOrders(&order.GetOrdersRequest{AssetType: asset.Spot})
if err != nil {
t.Fatal(err)
}

View File

@@ -58,9 +58,6 @@ func (b *BTCMarkets) SetDefaults() {
b.API.CredentialsValidator.RequiresKey = true
b.API.CredentialsValidator.RequiresSecret = true
b.API.CredentialsValidator.RequiresBase64DecodeSecret = true
b.API.Endpoints.URLDefault = btcMarketsAPIURL
b.API.Endpoints.URL = b.API.Endpoints.URLDefault
requestFmt := &currency.PairFormat{Delimiter: currency.DashDelimiter, Uppercase: true}
configFmt := &currency.PairFormat{Delimiter: currency.DashDelimiter, Uppercase: true}
err := b.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot)
@@ -123,8 +120,14 @@ func (b *BTCMarkets) SetDefaults() {
b.Requester = request.New(b.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
request.WithLimiter(SetRateLimit()))
b.API.Endpoints.WebsocketURL = btcMarketsWSURL
b.API.Endpoints = b.NewEndpoints()
err = b.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: btcMarketsAPIURL,
exchange.WebsocketSpot: btcMarketsWSURL,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
b.Websocket = stream.New()
b.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
b.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
@@ -143,6 +146,11 @@ func (b *BTCMarkets) Setup(exch *config.ExchangeConfig) error {
return err
}
wsURL, err := b.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
return err
}
err = b.Websocket.Setup(&stream.WebsocketSetup{
Enabled: exch.Features.Enabled.Websocket,
Verbose: exch.Verbose,
@@ -150,7 +158,7 @@ func (b *BTCMarkets) Setup(exch *config.ExchangeConfig) error {
WebsocketTimeout: exch.WebsocketTrafficTimeout,
DefaultURL: btcMarketsWSURL,
ExchangeName: exch.Name,
RunningURL: exch.API.Endpoints.WebsocketURL,
RunningURL: wsURL,
Connector: b.WsConnect,
Subscriber: b.Subscribe,
GenerateSubscriptions: b.generateDefaultSubscriptions,
@@ -387,7 +395,7 @@ func (b *BTCMarkets) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*or
}
// UpdateAccountInfo retrieves balances for all enabled currencies
func (b *BTCMarkets) UpdateAccountInfo() (account.Holdings, error) {
func (b *BTCMarkets) UpdateAccountInfo(assetType asset.Item) (account.Holdings, error) {
var resp account.Holdings
data, err := b.GetAccountBalance()
if err != nil {
@@ -415,10 +423,10 @@ func (b *BTCMarkets) UpdateAccountInfo() (account.Holdings, error) {
}
// FetchAccountInfo retrieves balances for all enabled currencies
func (b *BTCMarkets) FetchAccountInfo() (account.Holdings, error) {
acc, err := account.GetHoldings(b.Name)
func (b *BTCMarkets) FetchAccountInfo(assetType asset.Item) (account.Holdings, error) {
acc, err := account.GetHoldings(b.Name, assetType)
if err != nil {
return b.UpdateAccountInfo()
return b.UpdateAccountInfo(assetType)
}
return acc, nil
@@ -856,8 +864,8 @@ func (b *BTCMarkets) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detai
// ValidateCredentials validates current credentials used for wrapper
// functionality
func (b *BTCMarkets) ValidateCredentials() error {
_, err := b.UpdateAccountInfo()
func (b *BTCMarkets) ValidateCredentials(assetType asset.Item) error {
_, err := b.UpdateAccountInfo(assetType)
if err != nil {
if b.CheckTransientError(err) == nil {
return nil

View File

@@ -36,11 +36,13 @@ const (
// Public endpoints
btseMarketOverview = "market_summary"
btseMarkets = "markets"
btseOrderbook = "orderbook"
btseTrades = "trades"
btseTime = "time"
btseOHLCV = "ohlcv"
btsePrice = "price"
btseFuturesFunding = "funding_history"
// Authenticated endpoints
btseWallet = "user/wallet"
@@ -55,6 +57,16 @@ const (
btseCancelAllAfter = "order/cancelAllAfter"
)
// FetchFundingHistory gets funding history
func (b *BTSE) FetchFundingHistory(symbol string) (map[string][]FundingHistoryData, error) {
var resp map[string][]FundingHistoryData
params := url.Values{}
if symbol != "" {
params.Set("symbol", symbol)
}
return resp, b.SendHTTPRequest(exchange.RestFutures, http.MethodGet, btseFuturesFunding+params.Encode(), &resp, false, queryFunc)
}
// GetMarketSummary stores market summary data
func (b *BTSE) GetMarketSummary(symbol string, spot bool) (MarketSummary, error) {
var m MarketSummary
@@ -62,7 +74,7 @@ func (b *BTSE) GetMarketSummary(symbol string, spot bool) (MarketSummary, error)
if symbol != "" {
path += "?symbol=" + url.QueryEscape(symbol)
}
return m, b.SendHTTPRequest(http.MethodGet, path, &m, spot, queryFunc)
return m, b.SendHTTPRequest(exchange.RestSpot, http.MethodGet, path, &m, spot, queryFunc)
}
// FetchOrderBook gets orderbook data for a given pair
@@ -79,7 +91,7 @@ func (b *BTSE) FetchOrderBook(symbol string, group, limitBids, limitAsks int, sp
if group > 0 {
urlValues.Add("group", strconv.Itoa(group))
}
return &o, b.SendHTTPRequest(http.MethodGet,
return &o, b.SendHTTPRequest(exchange.RestSpot, http.MethodGet,
common.EncodeURLValues(btseOrderbook, urlValues), &o, spot, queryFunc)
}
@@ -90,7 +102,7 @@ func (b *BTSE) FetchOrderBookL2(symbol string, depth int) (*Orderbook, error) {
urlValues.Add("symbol", symbol)
urlValues.Add("depth", strconv.FormatInt(int64(depth), 10))
endpoint := common.EncodeURLValues(btseOrderbook+"/L2", urlValues)
return &o, b.SendHTTPRequest(http.MethodGet, endpoint, &o, true, queryFunc)
return &o, b.SendHTTPRequest(exchange.RestSpot, http.MethodGet, endpoint, &o, true, queryFunc)
}
// GetTrades returns a list of trades for the specified symbol
@@ -119,7 +131,7 @@ func (b *BTSE) GetTrades(symbol string, start, end time.Time, beforeSerialID, af
if includeOld {
urlValues.Add("includeOld", "true")
}
return t, b.SendHTTPRequest(http.MethodGet,
return t, b.SendHTTPRequest(exchange.RestSpot, http.MethodGet,
common.EncodeURLValues(btseTrades, urlValues), &t, spot, queryFunc)
}
@@ -142,26 +154,26 @@ func (b *BTSE) OHLCV(symbol string, start, end time.Time, resolution int) (OHLCV
}
urlValues.Add("resolution", strconv.FormatInt(int64(res), 10))
endpoint := common.EncodeURLValues(btseOHLCV, urlValues)
return o, b.SendHTTPRequest(http.MethodGet, endpoint, &o, true, queryFunc)
return o, b.SendHTTPRequest(exchange.RestSpot, http.MethodGet, endpoint, &o, true, queryFunc)
}
// GetPrice get current price for requested symbol
func (b *BTSE) GetPrice(symbol string) (Price, error) {
var p Price
path := btsePrice + "?symbol=" + url.QueryEscape(symbol)
return p, b.SendHTTPRequest(http.MethodGet, path, &p, true, queryFunc)
return p, b.SendHTTPRequest(exchange.RestSpot, http.MethodGet, path, &p, true, queryFunc)
}
// GetServerTime returns the exchanges server time
func (b *BTSE) GetServerTime() (*ServerTime, error) {
var s ServerTime
return &s, b.SendHTTPRequest(http.MethodGet, btseTime, &s, true, queryFunc)
return &s, b.SendHTTPRequest(exchange.RestSpot, http.MethodGet, btseTime, &s, true, queryFunc)
}
// GetWalletInformation returns the users account balance
func (b *BTSE) GetWalletInformation() ([]CurrencyBalance, error) {
var a []CurrencyBalance
return a, b.SendAuthenticatedHTTPRequest(http.MethodGet, btseWallet, true, nil, nil, &a, queryFunc)
return a, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, btseWallet, true, nil, nil, &a, queryFunc)
}
// GetFeeInformation retrieve fee's (maker/taker) for requested symbol
@@ -171,7 +183,7 @@ func (b *BTSE) GetFeeInformation(symbol string) ([]AccountFees, error) {
if symbol != "" {
urlValues.Add("symbol", symbol)
}
return resp, b.SendAuthenticatedHTTPRequest(http.MethodGet, btseUserFee, true, urlValues, nil, &resp, queryFunc)
return resp, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, btseUserFee, true, urlValues, nil, &resp, queryFunc)
}
// GetWalletHistory returns the users account balance
@@ -192,7 +204,7 @@ func (b *BTSE) GetWalletHistory(symbol string, start, end time.Time, count int)
if count > 0 {
urlValues.Add("count", strconv.Itoa(count))
}
return resp, b.SendAuthenticatedHTTPRequest(http.MethodGet, btseWalletHistory, true, urlValues, nil, &resp, queryFunc)
return resp, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, btseWalletHistory, true, urlValues, nil, &resp, queryFunc)
}
// GetWalletAddress returns the users account balance
@@ -204,7 +216,7 @@ func (b *BTSE) GetWalletAddress(currency string) (WalletAddress, error) {
urlValues.Add("currency", currency)
}
return resp, b.SendAuthenticatedHTTPRequest(http.MethodGet, btseWalletAddress, true, urlValues, nil, &resp, queryFunc)
return resp, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, btseWalletAddress, true, urlValues, nil, &resp, queryFunc)
}
// CreateWalletAddress create new deposit address for requested currency
@@ -212,7 +224,7 @@ func (b *BTSE) CreateWalletAddress(currency string) (WalletAddress, error) {
var resp WalletAddress
req := make(map[string]interface{}, 1)
req["currency"] = currency
err := b.SendAuthenticatedHTTPRequest(http.MethodPost, btseWalletAddress, true, nil, req, &resp, queryFunc)
err := b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, btseWalletAddress, true, nil, req, &resp, queryFunc)
if err != nil {
errResp := ErrorResponse{}
errResponseStr := strings.Split(err.Error(), "raw response: ")
@@ -242,7 +254,7 @@ func (b *BTSE) WalletWithdrawal(currency, address, tag, amount string) (Withdraw
req["address"] = address
req["tag"] = tag
req["amount"] = amount
return resp, b.SendAuthenticatedHTTPRequest(http.MethodPost, btseWalletWithdrawal, true, nil, req, &resp, queryFunc)
return resp, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, btseWalletWithdrawal, true, nil, req, &resp, queryFunc)
}
// CreateOrder creates an order
@@ -292,7 +304,7 @@ func (b *BTSE) CreateOrder(clOrderID string, deviation float64, postOnly bool, p
}
var r []Order
return r, b.SendAuthenticatedHTTPRequest(http.MethodPost, btseOrder, true, url.Values{}, req, &r, orderFunc)
return r, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, btseOrder, true, url.Values{}, req, &r, orderFunc)
}
// GetOrders returns all pending orders
@@ -306,7 +318,7 @@ func (b *BTSE) GetOrders(symbol, orderID, clOrderID string) ([]OpenOrder, error)
req.Add("clOrderID", clOrderID)
}
var o []OpenOrder
return o, b.SendAuthenticatedHTTPRequest(http.MethodGet, btsePendingOrders, true, req, nil, &o, orderFunc)
return o, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, btsePendingOrders, true, req, nil, &o, orderFunc)
}
// CancelExistingOrder cancels an order
@@ -321,14 +333,14 @@ func (b *BTSE) CancelExistingOrder(orderID, symbol, clOrderID string) (CancelOrd
req.Add("clOrderID", clOrderID)
}
return c, b.SendAuthenticatedHTTPRequest(http.MethodDelete, btseOrder, true, req, nil, &c, orderFunc)
return c, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodDelete, btseOrder, true, req, nil, &c, orderFunc)
}
// CancelAllAfter cancels all orders after timeout
func (b *BTSE) CancelAllAfter(timeout int) error {
req := make(map[string]interface{})
req["timeout"] = timeout
return b.SendAuthenticatedHTTPRequest(http.MethodPost, btseCancelAllAfter, true, url.Values{}, req, nil, orderFunc)
return b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, btseCancelAllAfter, true, url.Values{}, req, nil, orderFunc)
}
// IndexOrderPeg create peg order that will track a certain percentage above/below the index price
@@ -378,7 +390,7 @@ func (b *BTSE) IndexOrderPeg(clOrderID string, deviation float64, postOnly bool,
req["type"] = orderType
}
return o, b.SendAuthenticatedHTTPRequest(http.MethodPost, btsePegOrder, true, url.Values{}, req, nil, orderFunc)
return o, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, btsePegOrder, true, url.Values{}, req, nil, orderFunc)
}
// TradeHistory returns previous trades on exchange
@@ -413,18 +425,22 @@ func (b *BTSE) TradeHistory(symbol string, start, end time.Time, beforeSerialID,
if orderID != "" {
urlValues.Add("orderID", orderID)
}
return resp, b.SendAuthenticatedHTTPRequest(http.MethodGet, btseExchangeHistory, true, urlValues, nil, &resp, queryFunc)
return resp, b.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, btseExchangeHistory, true, urlValues, nil, &resp, queryFunc)
}
// SendHTTPRequest sends an HTTP request to the desired endpoint
func (b *BTSE) SendHTTPRequest(method, endpoint string, result interface{}, spotEndpoint bool, f request.EndpointLimit) error {
func (b *BTSE) SendHTTPRequest(ep exchange.URL, method, endpoint string, result interface{}, spotEndpoint bool, f request.EndpointLimit) error {
ePoint, err := b.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
p := btseSPOTPath + btseSPOTAPIPath
if !spotEndpoint {
p = btseFuturesPath + btseFuturesAPIPath
}
return b.SendPayload(context.Background(), &request.Item{
Method: method,
Path: b.API.Endpoints.URL + p + endpoint,
Path: ePoint + p + endpoint,
Result: result,
Verbose: b.Verbose,
HTTPDebugging: b.HTTPDebugging,
@@ -434,16 +450,21 @@ func (b *BTSE) SendHTTPRequest(method, endpoint string, result interface{}, spot
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to the desired endpoint
func (b *BTSE) SendAuthenticatedHTTPRequest(method, endpoint string, isSpot bool, values url.Values, req map[string]interface{}, result interface{}, f request.EndpointLimit) error {
func (b *BTSE) SendAuthenticatedHTTPRequest(ep exchange.URL, method, endpoint string, isSpot bool, values url.Values, req map[string]interface{}, result interface{}, f request.EndpointLimit) error {
if !b.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet,
b.Name)
}
ePoint, err := b.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
// The concatenation is done this way because BTSE expect endpoint+nonce or endpoint+nonce+body
// when signing the data but the full path of the request is /spot/api/v3.2/<endpoint>
// its messy but it works and supports futures as well
host := b.API.Endpoints.URL
host := ePoint
if isSpot {
host += btseSPOTPath + btseSPOTAPIPath + endpoint
endpoint = btseSPOTAPIPath + endpoint

View File

@@ -57,6 +57,13 @@ func areTestAPIKeysSet() bool {
return b.ValidateAPICredentials()
}
func TestFetchFundingHistory(t *testing.T) {
_, err := b.FetchFundingHistory("")
if err != nil {
t.Error(err)
}
}
func TestGetMarketsSummary(t *testing.T) {
t.Parallel()
_, err := b.GetMarketSummary("", true)
@@ -103,7 +110,7 @@ func TestUpdateOrderbook(t *testing.T) {
t.Fatal(err)
}
f, err := currency.NewPairFromString("BTC-PFC")
f, err := currency.NewPairFromString(testFUTURESPair)
if err != nil {
t.Fatal(err)
}
@@ -386,7 +393,8 @@ func TestGetActiveOrders(t *testing.T) {
Quote: currency.USD,
},
},
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := b.GetActiveOrders(&getOrdersRequest)
@@ -401,7 +409,8 @@ func TestGetOrderHistory(t *testing.T) {
t.Skip("API keys not set, skipping test")
}
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := b.GetOrderHistory(&getOrdersRequest)
if err != nil {

View File

@@ -13,6 +13,13 @@ const (
orderCancelled = 6
)
// FundingHistoryData stores funding history data
type FundingHistoryData struct {
Time int64 `json:"time"`
Rate float64 `json:"rate"`
Symbol string `json:"symbol"`
}
// MarketSummary response data
type MarketSummary []struct {
Symbol string `json:"symbol"`

View File

@@ -28,6 +28,11 @@ import (
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
)
const (
spotURL = "spotURL"
spotWSURL = "websocketURL"
)
// GetDefaultConfig returns a default exchange config
func (b *BTSE) GetDefaultConfig() (*config.ExchangeConfig, error) {
b.SetDefaults()
@@ -150,9 +155,15 @@ func (b *BTSE) SetDefaults() {
b.Requester = request.New(b.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
request.WithLimiter(SetRateLimit()))
b.API.Endpoints.URLDefault = btseAPIURL
b.API.Endpoints.URL = b.API.Endpoints.URLDefault
b.API.Endpoints = b.NewEndpoints()
err = b.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: btseAPIURL,
exchange.RestFutures: btseAPIURL,
exchange.WebsocketSpot: btseWebsocket,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
b.Websocket = stream.New()
b.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
b.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
@@ -171,6 +182,11 @@ func (b *BTSE) Setup(exch *config.ExchangeConfig) error {
return err
}
wsRunningURL, err := b.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
return err
}
err = b.Websocket.Setup(&stream.WebsocketSetup{
Enabled: exch.Features.Enabled.Websocket,
Verbose: exch.Verbose,
@@ -178,7 +194,7 @@ func (b *BTSE) Setup(exch *config.ExchangeConfig) error {
WebsocketTimeout: exch.WebsocketTrafficTimeout,
DefaultURL: btseWebsocket,
ExchangeName: exch.Name,
RunningURL: exch.API.Endpoints.WebsocketURL,
RunningURL: wsRunningURL,
Connector: b.WsConnect,
Subscriber: b.Subscribe,
UnSubscriber: b.Unsubscribe,
@@ -363,7 +379,7 @@ func (b *BTSE) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderboo
// UpdateAccountInfo retrieves balances for all enabled currencies for the
// BTSE exchange
func (b *BTSE) UpdateAccountInfo() (account.Holdings, error) {
func (b *BTSE) UpdateAccountInfo(assetType asset.Item) (account.Holdings, error) {
var a account.Holdings
balance, err := b.GetWalletInformation()
if err != nil {
@@ -396,10 +412,10 @@ func (b *BTSE) UpdateAccountInfo() (account.Holdings, error) {
}
// FetchAccountInfo retrieves balances for all enabled currencies
func (b *BTSE) FetchAccountInfo() (account.Holdings, error) {
acc, err := account.GetHoldings(b.Name)
func (b *BTSE) FetchAccountInfo(assetType asset.Item) (account.Holdings, error) {
acc, err := account.GetHoldings(b.Name, assetType)
if err != nil {
return b.UpdateAccountInfo()
return b.UpdateAccountInfo(assetType)
}
return acc, nil
@@ -881,8 +897,8 @@ func (b *BTSE) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) {
// ValidateCredentials validates current credentials used for wrapper
// functionality
func (b *BTSE) ValidateCredentials() error {
_, err := b.UpdateAccountInfo()
func (b *BTSE) ValidateCredentials(assetType asset.Item) error {
_, err := b.UpdateAccountInfo(assetType)
return b.CheckTransientError(err)
}

View File

@@ -63,20 +63,20 @@ type CoinbasePro struct {
func (c *CoinbasePro) GetProducts() ([]Product, error) {
var products []Product
return products, c.SendHTTPRequest(c.API.Endpoints.URL+coinbaseproProducts, &products)
return products, c.SendHTTPRequest(exchange.RestSpot, coinbaseproProducts, &products)
}
// GetOrderbook returns orderbook by currency pair and level
func (c *CoinbasePro) GetOrderbook(symbol string, level int) (interface{}, error) {
orderbook := OrderbookResponse{}
path := fmt.Sprintf("%s/%s/%s", c.API.Endpoints.URL+coinbaseproProducts, symbol, coinbaseproOrderbook)
path := fmt.Sprintf("%s/%s/%s", coinbaseproProducts, symbol, coinbaseproOrderbook)
if level > 0 {
levelStr := strconv.Itoa(level)
path = fmt.Sprintf("%s/%s/%s?level=%s", c.API.Endpoints.URL+coinbaseproProducts, symbol, coinbaseproOrderbook, levelStr)
path = fmt.Sprintf("%s/%s/%s?level=%s", coinbaseproProducts, symbol, coinbaseproOrderbook, levelStr)
}
if err := c.SendHTTPRequest(path, &orderbook); err != nil {
if err := c.SendHTTPRequest(exchange.RestSpot, path, &orderbook); err != nil {
return nil, err
}
@@ -143,8 +143,8 @@ func (c *CoinbasePro) GetOrderbook(symbol string, level int) (interface{}, error
func (c *CoinbasePro) GetTicker(currencyPair string) (Ticker, error) {
tick := Ticker{}
path := fmt.Sprintf(
"%s/%s/%s", c.API.Endpoints.URL+coinbaseproProducts, currencyPair, coinbaseproTicker)
return tick, c.SendHTTPRequest(path, &tick)
"%s/%s/%s", coinbaseproProducts, currencyPair, coinbaseproTicker)
return tick, c.SendHTTPRequest(exchange.RestSpot, path, &tick)
}
// GetTrades listd the latest trades for a product
@@ -152,8 +152,8 @@ func (c *CoinbasePro) GetTicker(currencyPair string) (Ticker, error) {
func (c *CoinbasePro) GetTrades(currencyPair string) ([]Trade, error) {
var trades []Trade
path := fmt.Sprintf(
"%s/%s/%s", c.API.Endpoints.URL+coinbaseproProducts, currencyPair, coinbaseproTrades)
return trades, c.SendHTTPRequest(path, &trades)
"%s/%s/%s", coinbaseproProducts, currencyPair, coinbaseproTrades)
return trades, c.SendHTTPRequest(exchange.RestSpot, path, &trades)
}
// GetHistoricRates returns historic rates for a product. Rates are returned in
@@ -185,10 +185,10 @@ func (c *CoinbasePro) GetHistoricRates(currencyPair, start, end string, granular
}
path := common.EncodeURLValues(
fmt.Sprintf("%s/%s/%s", c.API.Endpoints.URL+coinbaseproProducts, currencyPair, coinbaseproHistory),
fmt.Sprintf("%s/%s/%s", coinbaseproProducts, currencyPair, coinbaseproHistory),
values)
if err := c.SendHTTPRequest(path, &resp); err != nil {
if err := c.SendHTTPRequest(exchange.RestSpot, path, &resp); err != nil {
return history, err
}
@@ -217,9 +217,9 @@ func (c *CoinbasePro) GetHistoricRates(currencyPair, start, end string, granular
func (c *CoinbasePro) GetStats(currencyPair string) (Stats, error) {
stats := Stats{}
path := fmt.Sprintf(
"%s/%s/%s", c.API.Endpoints.URL+coinbaseproProducts, currencyPair, coinbaseproStats)
"%s/%s/%s", coinbaseproProducts, currencyPair, coinbaseproStats)
return stats, c.SendHTTPRequest(path, &stats)
return stats, c.SendHTTPRequest(exchange.RestSpot, path, &stats)
}
// GetCurrencies returns a list of supported currency on the exchange
@@ -227,14 +227,14 @@ func (c *CoinbasePro) GetStats(currencyPair string) (Stats, error) {
func (c *CoinbasePro) GetCurrencies() ([]Currency, error) {
var currencies []Currency
return currencies, c.SendHTTPRequest(c.API.Endpoints.URL+coinbaseproCurrencies, &currencies)
return currencies, c.SendHTTPRequest(exchange.RestSpot, coinbaseproCurrencies, &currencies)
}
// GetServerTime returns the API server time
func (c *CoinbasePro) GetServerTime() (ServerTime, error) {
serverTime := ServerTime{}
return serverTime, c.SendHTTPRequest(c.API.Endpoints.URL+coinbaseproTime, &serverTime)
return serverTime, c.SendHTTPRequest(exchange.RestSpot, coinbaseproTime, &serverTime)
}
// GetAccounts returns a list of trading accounts associated with the APIKEYS
@@ -242,7 +242,7 @@ func (c *CoinbasePro) GetAccounts() ([]AccountResponse, error) {
var resp []AccountResponse
return resp,
c.SendAuthenticatedHTTPRequest(http.MethodGet, coinbaseproAccounts, nil, &resp)
c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, coinbaseproAccounts, nil, &resp)
}
// GetAccount returns information for a single account. Use this endpoint when
@@ -251,7 +251,7 @@ func (c *CoinbasePro) GetAccount(accountID string) (AccountResponse, error) {
resp := AccountResponse{}
path := fmt.Sprintf("%s/%s", coinbaseproAccounts, accountID)
return resp, c.SendAuthenticatedHTTPRequest(http.MethodGet, path, nil, &resp)
return resp, c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, path, nil, &resp)
}
// GetAccountHistory returns a list of account activity. Account activity either
@@ -261,7 +261,7 @@ func (c *CoinbasePro) GetAccountHistory(accountID string) ([]AccountLedgerRespon
var resp []AccountLedgerResponse
path := fmt.Sprintf("%s/%s/%s", coinbaseproAccounts, accountID, coinbaseproLedger)
return resp, c.SendAuthenticatedHTTPRequest(http.MethodGet, path, nil, &resp)
return resp, c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, path, nil, &resp)
}
// GetHolds returns the holds that are placed on an account for any active
@@ -272,7 +272,7 @@ func (c *CoinbasePro) GetHolds(accountID string) ([]AccountHolds, error) {
var resp []AccountHolds
path := fmt.Sprintf("%s/%s/%s", coinbaseproAccounts, accountID, coinbaseproHolds)
return resp, c.SendAuthenticatedHTTPRequest(http.MethodGet, path, nil, &resp)
return resp, c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, path, nil, &resp)
}
// PlaceLimitOrder places a new limit order. Orders can only be placed if the
@@ -317,7 +317,7 @@ func (c *CoinbasePro) PlaceLimitOrder(clientRef string, price, amount float64, s
req["post_only"] = postOnly
}
err := c.SendAuthenticatedHTTPRequest(http.MethodPost, coinbaseproOrders, req, &resp)
err := c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, coinbaseproOrders, req, &resp)
if err != nil {
return "", err
}
@@ -361,7 +361,7 @@ func (c *CoinbasePro) PlaceMarketOrder(clientRef string, size, funds float64, si
req["stp"] = stp
}
err := c.SendAuthenticatedHTTPRequest(http.MethodPost, coinbaseproOrders, req, &resp)
err := c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, coinbaseproOrders, req, &resp)
if err != nil {
return "", err
}
@@ -404,7 +404,7 @@ func (c *CoinbasePro) PlaceMarginOrder(clientRef string, size, funds float64, si
req["stp"] = stp
}
err := c.SendAuthenticatedHTTPRequest(http.MethodPost, coinbaseproOrders, req, &resp)
err := c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, coinbaseproOrders, req, &resp)
if err != nil {
return "", err
}
@@ -416,7 +416,7 @@ func (c *CoinbasePro) PlaceMarginOrder(clientRef string, size, funds float64, si
func (c *CoinbasePro) CancelExistingOrder(orderID string) error {
path := fmt.Sprintf("%s/%s", coinbaseproOrders, orderID)
return c.SendAuthenticatedHTTPRequest(http.MethodDelete, path, nil, nil)
return c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodDelete, path, nil, nil)
}
// CancelAllExistingOrders cancels all open orders on the exchange and returns
@@ -430,7 +430,7 @@ func (c *CoinbasePro) CancelAllExistingOrders(currencyPair string) ([]string, er
if len(currencyPair) > 0 {
req["product_id"] = currencyPair
}
return resp, c.SendAuthenticatedHTTPRequest(http.MethodDelete, coinbaseproOrders, req, &resp)
return resp, c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodDelete, coinbaseproOrders, req, &resp)
}
// GetOrders lists current open orders. Only open or un-settled orders are
@@ -449,11 +449,11 @@ func (c *CoinbasePro) GetOrders(status []string, currencyPair string) ([]General
params.Set("product_id", currencyPair)
}
path := common.EncodeURLValues(c.API.Endpoints.URL+coinbaseproOrders, params)
path := common.EncodeURLValues(coinbaseproOrders, params)
path = common.GetURIPath(path)
return resp,
c.SendAuthenticatedHTTPRequest(http.MethodGet, path[1:], nil, &resp)
c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, path[1:], nil, &resp)
}
// GetOrder returns a single order by order id.
@@ -461,7 +461,7 @@ func (c *CoinbasePro) GetOrder(orderID string) (GeneralizedOrderResponse, error)
resp := GeneralizedOrderResponse{}
path := fmt.Sprintf("%s/%s", coinbaseproOrders, orderID)
return resp, c.SendAuthenticatedHTTPRequest(http.MethodGet, path, nil, &resp)
return resp, c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, path, nil, &resp)
}
// GetFills returns a list of recent fills
@@ -479,11 +479,11 @@ func (c *CoinbasePro) GetFills(orderID, currencyPair string) ([]FillResponse, er
return resp, errors.New("no parameters set")
}
path := common.EncodeURLValues(c.API.Endpoints.URL+coinbaseproFills, params)
path := common.EncodeURLValues(coinbaseproFills, params)
uri := common.GetURIPath(path)
return resp,
c.SendAuthenticatedHTTPRequest(http.MethodGet, uri[1:], nil, &resp)
c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, uri[1:], nil, &resp)
}
// MarginTransfer sends funds between a standard/default profile and a margin
@@ -506,7 +506,7 @@ func (c *CoinbasePro) MarginTransfer(amount float64, transferType, profileID, cu
req["margin_profile_id"] = profileID
return resp,
c.SendAuthenticatedHTTPRequest(http.MethodPost, coinbaseproMarginTransfer, req, &resp)
c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, coinbaseproMarginTransfer, req, &resp)
}
// GetPosition returns an overview of account profile.
@@ -514,7 +514,7 @@ func (c *CoinbasePro) GetPosition() (AccountOverview, error) {
resp := AccountOverview{}
return resp,
c.SendAuthenticatedHTTPRequest(http.MethodGet, coinbaseproPosition, nil, &resp)
c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, coinbaseproPosition, nil, &resp)
}
// ClosePosition closes a position and allowing you to repay position as well
@@ -525,7 +525,7 @@ func (c *CoinbasePro) ClosePosition(repayOnly bool) (AccountOverview, error) {
req["repay_only"] = repayOnly
return resp,
c.SendAuthenticatedHTTPRequest(http.MethodPost, coinbaseproPositionClose, req, &resp)
c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, coinbaseproPositionClose, req, &resp)
}
// GetPayMethods returns a full list of payment methods
@@ -533,7 +533,7 @@ func (c *CoinbasePro) GetPayMethods() ([]PaymentMethod, error) {
var resp []PaymentMethod
return resp,
c.SendAuthenticatedHTTPRequest(http.MethodGet, coinbaseproPaymentMethod, nil, &resp)
c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, coinbaseproPaymentMethod, nil, &resp)
}
// DepositViaPaymentMethod deposits funds from a payment method. See the Payment
@@ -550,7 +550,7 @@ func (c *CoinbasePro) DepositViaPaymentMethod(amount float64, currency, paymentI
req["payment_method_id"] = paymentID
return resp,
c.SendAuthenticatedHTTPRequest(http.MethodPost, coinbaseproPaymentMethodDeposit, req, &resp)
c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, coinbaseproPaymentMethodDeposit, req, &resp)
}
// DepositViaCoinbase deposits funds from a coinbase account. Move funds between
@@ -569,7 +569,7 @@ func (c *CoinbasePro) DepositViaCoinbase(amount float64, currency, accountID str
req["coinbase_account_id"] = accountID
return resp,
c.SendAuthenticatedHTTPRequest(http.MethodPost, coinbaseproDepositCoinbase, req, &resp)
c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, coinbaseproDepositCoinbase, req, &resp)
}
// WithdrawViaPaymentMethod withdraws funds to a payment method
@@ -585,7 +585,7 @@ func (c *CoinbasePro) WithdrawViaPaymentMethod(amount float64, currency, payment
req["payment_method_id"] = paymentID
return resp,
c.SendAuthenticatedHTTPRequest(http.MethodPost, coinbaseproWithdrawalPaymentMethod, req, &resp)
c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, coinbaseproWithdrawalPaymentMethod, req, &resp)
}
// /////////////////////// NO ROUTE FOUND ERROR ////////////////////////////////
@@ -618,7 +618,7 @@ func (c *CoinbasePro) WithdrawCrypto(amount float64, currency, cryptoAddress str
req["crypto_address"] = cryptoAddress
return resp,
c.SendAuthenticatedHTTPRequest(http.MethodPost, coinbaseproWithdrawalCrypto, req, &resp)
c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, coinbaseproWithdrawalCrypto, req, &resp)
}
// GetCoinbaseAccounts returns a list of coinbase accounts
@@ -626,7 +626,7 @@ func (c *CoinbasePro) GetCoinbaseAccounts() ([]CoinbaseAccounts, error) {
var resp []CoinbaseAccounts
return resp,
c.SendAuthenticatedHTTPRequest(http.MethodGet, coinbaseproCoinbaseAccounts, nil, &resp)
c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, coinbaseproCoinbaseAccounts, nil, &resp)
}
// GetReport returns batches of historic information about your account in
@@ -663,7 +663,7 @@ func (c *CoinbasePro) GetReport(reportType, startDate, endDate, currencyPair, ac
}
return resp,
c.SendAuthenticatedHTTPRequest(http.MethodPost, coinbaseproReports, req, &resp)
c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, coinbaseproReports, req, &resp)
}
// GetReportStatus once a report request has been accepted for processing, the
@@ -672,7 +672,7 @@ func (c *CoinbasePro) GetReportStatus(reportID string) (Report, error) {
resp := Report{}
path := fmt.Sprintf("%s/%s", coinbaseproReports, reportID)
return resp, c.SendAuthenticatedHTTPRequest(http.MethodGet, path, nil, &resp)
return resp, c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, path, nil, &resp)
}
// GetTrailingVolume this request will return your 30-day trailing volume for
@@ -681,14 +681,18 @@ func (c *CoinbasePro) GetTrailingVolume() ([]Volume, error) {
var resp []Volume
return resp,
c.SendAuthenticatedHTTPRequest(http.MethodGet, coinbaseproTrailingVolume, nil, &resp)
c.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, coinbaseproTrailingVolume, nil, &resp)
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (c *CoinbasePro) SendHTTPRequest(path string, result interface{}) error {
func (c *CoinbasePro) SendHTTPRequest(ep exchange.URL, path string, result interface{}) error {
endpoint, err := c.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
return c.SendPayload(context.Background(), &request.Item{
Method: http.MethodGet,
Path: path,
Path: endpoint + path,
Result: result,
Verbose: c.Verbose,
HTTPDebugging: c.HTTPDebugging,
@@ -697,12 +701,15 @@ func (c *CoinbasePro) SendHTTPRequest(path string, result interface{}) error {
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request
func (c *CoinbasePro) SendAuthenticatedHTTPRequest(method, path string, params map[string]interface{}, result interface{}) (err error) {
func (c *CoinbasePro) SendAuthenticatedHTTPRequest(ep exchange.URL, method, path string, params map[string]interface{}, result interface{}) (err error) {
if !c.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet,
c.Name)
}
endpoint, err := c.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
payload := []byte("")
if params != nil {
@@ -732,7 +739,7 @@ func (c *CoinbasePro) SendAuthenticatedHTTPRequest(method, path string, params m
defer cancel()
return c.SendPayload(ctx, &request.Item{
Method: method,
Path: c.API.Endpoints.URL + path,
Path: endpoint + path,
Headers: headers,
Body: bytes.NewBuffer(payload),
Result: result,

View File

@@ -394,7 +394,8 @@ func TestFormatWithdrawPermissions(t *testing.T) {
func TestGetActiveOrders(t *testing.T) {
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
Pairs: []currency.Pair{currency.NewPair(currency.BTC,
currency.LTC)},
}
@@ -409,7 +410,8 @@ func TestGetActiveOrders(t *testing.T) {
func TestGetOrderHistory(t *testing.T) {
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
Pairs: []currency.Pair{currency.NewPair(currency.BTC,
currency.LTC)},
}

View File

@@ -131,10 +131,15 @@ func (c *CoinbasePro) SetDefaults() {
c.Requester = request.New(c.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
request.WithLimiter(SetRateLimit()))
c.API.Endpoints.URLDefault = coinbaseproAPIURL
c.API.Endpoints.URL = c.API.Endpoints.URLDefault
c.API.Endpoints.WebsocketURL = coinbaseproWebsocketURL
c.API.Endpoints = c.NewEndpoints()
err = c.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: coinbaseproAPIURL,
exchange.RestSandbox: coinbaseproSandboxAPIURL,
exchange.WebsocketSpot: coinbaseproWebsocketURL,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
c.Websocket = stream.New()
c.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
c.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
@@ -153,6 +158,11 @@ func (c *CoinbasePro) Setup(exch *config.ExchangeConfig) error {
return err
}
wsRunningURL, err := c.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
return err
}
err = c.Websocket.Setup(&stream.WebsocketSetup{
Enabled: exch.Features.Enabled.Websocket,
Verbose: exch.Verbose,
@@ -160,7 +170,7 @@ func (c *CoinbasePro) Setup(exch *config.ExchangeConfig) error {
WebsocketTimeout: exch.WebsocketTrafficTimeout,
DefaultURL: coinbaseproWebsocketURL,
ExchangeName: exch.Name,
RunningURL: exch.API.Endpoints.WebsocketURL,
RunningURL: wsRunningURL,
Connector: c.WsConnect,
Subscriber: c.Subscribe,
UnSubscriber: c.Unsubscribe,
@@ -303,7 +313,7 @@ func (c *CoinbasePro) UpdateTradablePairs(forceUpdate bool) error {
// UpdateAccountInfo retrieves balances for all enabled currencies for the
// coinbasepro exchange
func (c *CoinbasePro) UpdateAccountInfo() (account.Holdings, error) {
func (c *CoinbasePro) UpdateAccountInfo(assetType asset.Item) (account.Holdings, error) {
var response account.Holdings
response.Exchange = c.Name
accountBalance, err := c.GetAccounts()
@@ -334,10 +344,10 @@ func (c *CoinbasePro) UpdateAccountInfo() (account.Holdings, error) {
}
// FetchAccountInfo retrieves balances for all enabled currencies
func (c *CoinbasePro) FetchAccountInfo() (account.Holdings, error) {
acc, err := account.GetHoldings(c.Name)
func (c *CoinbasePro) FetchAccountInfo(assetType asset.Item) (account.Holdings, error) {
acc, err := account.GetHoldings(c.Name, assetType)
if err != nil {
return c.UpdateAccountInfo()
return c.UpdateAccountInfo(assetType)
}
return acc, nil
@@ -934,7 +944,7 @@ func (c *CoinbasePro) GetHistoricCandlesExtended(p currency.Pair, a asset.Item,
// ValidateCredentials validates current credentials used for wrapper
// functionality
func (c *CoinbasePro) ValidateCredentials() error {
_, err := c.UpdateAccountInfo()
func (c *CoinbasePro) ValidateCredentials(assetType asset.Item) error {
_, err := c.UpdateAccountInfo(assetType)
return c.CheckTransientError(err)
}

View File

@@ -80,8 +80,8 @@ func (c *Coinbene) GetAllPairs() ([]PairData, error) {
resp := struct {
Data []PairData `json:"data"`
}{}
path := c.API.Endpoints.URL + coinbeneAPIVersion + coinbeneGetAllPairs
return resp.Data, c.SendHTTPRequest(path, spotPairs, &resp)
path := coinbeneAPIVersion + coinbeneGetAllPairs
return resp.Data, c.SendHTTPRequest(exchange.RestSpot, path, spotPairs, &resp)
}
// GetPairInfo gets info about a single pair
@@ -91,8 +91,8 @@ func (c *Coinbene) GetPairInfo(symbol string) (PairData, error) {
}{}
params := url.Values{}
params.Set("symbol", symbol)
path := common.EncodeURLValues(c.API.Endpoints.URL+coinbeneAPIVersion+coinbenePairInfo, params)
return resp.Data, c.SendHTTPRequest(path, spotPairInfo, &resp)
path := common.EncodeURLValues(coinbeneAPIVersion+coinbenePairInfo, params)
return resp.Data, c.SendHTTPRequest(exchange.RestSpot, path, spotPairInfo, &resp)
}
// GetOrderbook gets and stores orderbook data for given pair
@@ -108,8 +108,8 @@ func (c *Coinbene) GetOrderbook(symbol string, size int64) (Orderbook, error) {
params := url.Values{}
params.Set("symbol", symbol)
params.Set("depth", strconv.FormatInt(size, 10))
path := common.EncodeURLValues(c.API.Endpoints.URL+coinbeneAPIVersion+coinbeneGetOrderBook, params)
err := c.SendHTTPRequest(path, spotOrderbook, &resp)
path := common.EncodeURLValues(coinbeneAPIVersion+coinbeneGetOrderBook, params)
err := c.SendHTTPRequest(exchange.RestSpot, path, spotOrderbook, &resp)
if err != nil {
return Orderbook{}, err
}
@@ -154,8 +154,8 @@ func (c *Coinbene) GetTicker(symbol string) (TickerData, error) {
}{}
params := url.Values{}
params.Set("symbol", symbol)
path := common.EncodeURLValues(c.API.Endpoints.URL+coinbeneAPIVersion+coinbeneGetTicker, params)
return resp.TickerData, c.SendHTTPRequest(path, spotSpecificTicker, &resp)
path := common.EncodeURLValues(coinbeneAPIVersion+coinbeneGetTicker, params)
return resp.TickerData, c.SendHTTPRequest(exchange.RestSpot, path, spotSpecificTicker, &resp)
}
// GetTickers gets and all spot tickers supported by the exchange
@@ -164,8 +164,8 @@ func (c *Coinbene) GetTickers() ([]TickerData, error) {
TickerData []TickerData `json:"data"`
}{}
path := c.API.Endpoints.URL + coinbeneAPIVersion + coinbeneGetTickersSpot
return resp.TickerData, c.SendHTTPRequest(path, spotTickerList, &resp)
path := coinbeneAPIVersion + coinbeneGetTickersSpot
return resp.TickerData, c.SendHTTPRequest(exchange.RestSpot, path, spotTickerList, &resp)
}
// GetTrades gets recent trades from the exchange
@@ -177,8 +177,8 @@ func (c *Coinbene) GetTrades(symbol string, limit int64) (Trades, error) {
params := url.Values{}
params.Set("symbol", symbol)
params.Set("limit", strconv.FormatInt(limit, 10))
path := common.EncodeURLValues(c.API.Endpoints.URL+coinbeneAPIVersion+coinbeneGetTrades, params)
err := c.SendHTTPRequest(path, spotMarketTrades, &resp)
path := common.EncodeURLValues(coinbeneAPIVersion+coinbeneGetTrades, params)
err := c.SendHTTPRequest(exchange.RestSpot, path, spotMarketTrades, &resp)
if err != nil {
return nil, err
}
@@ -213,8 +213,8 @@ func (c *Coinbene) GetAccountBalances() ([]UserBalanceData, error) {
resp := struct {
Data []UserBalanceData `json:"data"`
}{}
path := c.API.Endpoints.URL + coinbeneAPIVersion + coinbeneGetUserBalance
err := c.SendAuthHTTPRequest(http.MethodGet,
path := coinbeneAPIVersion + coinbeneGetUserBalance
err := c.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet,
path,
coinbeneGetUserBalance,
false,
@@ -234,8 +234,8 @@ func (c *Coinbene) GetAccountAssetBalance(symbol string) (UserBalanceData, error
resp := struct {
Data UserBalanceData `json:"data"`
}{}
path := c.API.Endpoints.URL + coinbeneAPIVersion + coinbeneAccountBalanceOne
err := c.SendAuthHTTPRequest(http.MethodGet,
path := coinbeneAPIVersion + coinbeneAccountBalanceOne
err := c.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet,
path,
coinbeneAccountBalanceOne,
false,
@@ -289,8 +289,8 @@ func (c *Coinbene) PlaceSpotOrder(price, quantity float64, symbol, direction,
if notional != 0 {
params.Set("notional", strconv.Itoa(notional))
}
path := c.API.Endpoints.URL + coinbeneAPIVersion + coinbenePlaceOrder
err := c.SendAuthHTTPRequest(http.MethodPost,
path := coinbeneAPIVersion + coinbenePlaceOrder
err := c.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost,
path,
coinbenePlaceOrder,
false,
@@ -359,8 +359,8 @@ func (c *Coinbene) PlaceSpotOrders(orders []PlaceOrderRequest) ([]OrderPlacement
resp := struct {
Data []OrderPlacementResponse `json:"data"`
}{}
path := c.API.Endpoints.URL + coinbeneAPIVersion + coinbeneBatchPlaceOrder
err := c.SendAuthHTTPRequest(http.MethodPost,
path := coinbeneAPIVersion + coinbeneBatchPlaceOrder
err := c.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost,
path,
coinbeneBatchPlaceOrder,
false,
@@ -377,14 +377,14 @@ func (c *Coinbene) PlaceSpotOrders(orders []PlaceOrderRequest) ([]OrderPlacement
func (c *Coinbene) FetchOpenSpotOrders(symbol string) (OrdersInfo, error) {
params := url.Values{}
params.Set("symbol", symbol)
path := c.API.Endpoints.URL + coinbeneAPIVersion + coinbeneOpenOrders
path := coinbeneAPIVersion + coinbeneOpenOrders
var orders OrdersInfo
for i := int64(1); ; i++ {
temp := struct {
Data OrdersInfo `json:"data"`
}{}
params.Set("pageNum", strconv.FormatInt(i, 10))
err := c.SendAuthHTTPRequest(http.MethodGet,
err := c.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet,
path,
coinbeneOpenOrders,
false,
@@ -410,14 +410,14 @@ func (c *Coinbene) FetchClosedOrders(symbol, latestID string) (OrdersInfo, error
params := url.Values{}
params.Set("symbol", symbol)
params.Set("latestOrderId", latestID)
path := c.API.Endpoints.URL + coinbeneAPIVersion + coinbeneClosedOrders
path := coinbeneAPIVersion + coinbeneClosedOrders
var orders OrdersInfo
for i := int64(1); ; i++ {
temp := struct {
Data OrdersInfo `json:"data"`
}{}
params.Set("pageNum", strconv.FormatInt(i, 10))
err := c.SendAuthHTTPRequest(http.MethodGet,
err := c.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet,
path,
coinbeneClosedOrders,
false,
@@ -444,8 +444,8 @@ func (c *Coinbene) FetchSpotOrderInfo(orderID string) (OrderInfo, error) {
}{}
params := url.Values{}
params.Set("orderId", orderID)
path := c.API.Endpoints.URL + coinbeneAPIVersion + coinbeneOrderInfo
err := c.SendAuthHTTPRequest(http.MethodGet,
path := coinbeneAPIVersion + coinbeneOrderInfo
err := c.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet,
path,
coinbeneOrderInfo,
false,
@@ -469,8 +469,8 @@ func (c *Coinbene) GetSpotOrderFills(orderID string) ([]OrderFills, error) {
}{}
params := url.Values{}
params.Set("orderId", orderID)
path := c.API.Endpoints.URL + coinbeneAPIVersion + coinbeneTradeFills
err := c.SendAuthHTTPRequest(http.MethodGet,
path := coinbeneAPIVersion + coinbeneTradeFills
err := c.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet,
path,
coinbeneTradeFills,
false,
@@ -490,8 +490,8 @@ func (c *Coinbene) CancelSpotOrder(orderID string) (string, error) {
}{}
req := make(map[string]interface{})
req["orderId"] = orderID
path := c.API.Endpoints.URL + coinbeneAPIVersion + coinbeneCancelOrder
err := c.SendAuthHTTPRequest(http.MethodPost,
path := coinbeneAPIVersion + coinbeneCancelOrder
err := c.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost,
path,
coinbeneCancelOrder,
false,
@@ -513,8 +513,8 @@ func (c *Coinbene) CancelSpotOrders(orderIDs []string) ([]OrderCancellationRespo
}
var r resp
path := c.API.Endpoints.URL + coinbeneAPIVersion + coinbeneBatchCancel
err := c.SendAuthHTTPRequest(http.MethodPost,
path := coinbeneAPIVersion + coinbeneBatchCancel
err := c.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost,
path,
coinbeneBatchCancel,
false,
@@ -533,8 +533,8 @@ func (c *Coinbene) GetSwapTickers() (SwapTickers, error) {
Data SwapTickers `json:"data"`
}
var r resp
path := coinbeneSwapAPIURL + coinbeneAPIVersion + coinbeneGetTickers
err := c.SendHTTPRequest(path, contractTickers, &r)
path := coinbeneAPIVersion + coinbeneGetTickers
err := c.SendHTTPRequest(exchange.RestSwap, path, contractTickers, &r)
if err != nil {
return nil, err
}
@@ -578,8 +578,8 @@ func (c *Coinbene) GetSwapOrderbook(symbol string, size int64) (Orderbook, error
}
var r resp
path := common.EncodeURLValues(coinbeneSwapAPIURL+coinbeneAPIVersion+coinbeneGetOrderBook, v)
err := c.SendHTTPRequest(path, contractOrderbook, &r)
path := common.EncodeURLValues(coinbeneAPIVersion+coinbeneGetOrderBook, v)
err := c.SendHTTPRequest(exchange.RestSwap, path, contractOrderbook, &r)
if err != nil {
return s, err
}
@@ -634,8 +634,8 @@ func (c *Coinbene) GetKlines(pair string, start, end time.Time, period string) (
}
v.Add("period", period)
path := common.EncodeURLValues(coinbeneAPIURL+coinbeneAPIVersion+coinbeneSpotKlines, v)
if err = c.SendHTTPRequest(path, contractKline, &resp); err != nil {
path := common.EncodeURLValues(coinbeneAPIVersion+coinbeneSpotKlines, v)
if err = c.SendHTTPRequest(exchange.RestSpot, path, contractKline, &resp); err != nil {
return
}
@@ -658,8 +658,8 @@ func (c *Coinbene) GetSwapKlines(symbol string, start, end time.Time, resolution
}
v.Set("resolution", resolution)
path := common.EncodeURLValues(coinbeneSwapAPIURL+coinbeneAPIVersion+coinbeneGetKlines, v)
if err = c.SendHTTPRequest(path, contractKline, &resp); err != nil {
path := common.EncodeURLValues(coinbeneAPIVersion+coinbeneGetKlines, v)
if err = c.SendHTTPRequest(exchange.RestSwap, path, contractKline, &resp); err != nil {
return
}
@@ -677,8 +677,8 @@ func (c *Coinbene) GetSwapTrades(symbol string, limit int) (SwapTrades, error) {
Data [][]string `json:"data"`
}
var r resp
path := common.EncodeURLValues(coinbeneSwapAPIURL+coinbeneAPIVersion+coinbeneGetTrades, v)
if err := c.SendHTTPRequest(path, contractTrades, &r); err != nil {
path := common.EncodeURLValues(coinbeneAPIVersion+coinbeneGetTrades, v)
if err := c.SendHTTPRequest(exchange.RestSwap, path, contractTrades, &r); err != nil {
return nil, err
}
@@ -716,8 +716,8 @@ func (c *Coinbene) GetSwapAccountInfo() (SwapAccountInfo, error) {
Data SwapAccountInfo `json:"data"`
}
var r resp
path := coinbeneSwapAPIURL + coinbeneAPIVersion + coinbeneAccountInfo
err := c.SendAuthHTTPRequest(http.MethodGet,
path := coinbeneAPIVersion + coinbeneAccountInfo
err := c.SendAuthHTTPRequest(exchange.RestSwap, http.MethodGet,
path,
coinbeneAccountInfo,
true,
@@ -738,8 +738,8 @@ func (c *Coinbene) GetSwapPositions(symbol string) (SwapPositions, error) {
Data SwapPositions `json:"data"`
}
var r resp
path := coinbeneSwapAPIURL + coinbeneAPIVersion + coinbeneListSwapPositions
err := c.SendAuthHTTPRequest(http.MethodGet,
path := coinbeneAPIVersion + coinbeneListSwapPositions
err := c.SendAuthHTTPRequest(exchange.RestSwap, http.MethodGet,
path,
coinbeneListSwapPositions,
true,
@@ -793,8 +793,8 @@ func (c *Coinbene) PlaceSwapOrder(symbol, direction, orderType, marginMode,
Data SwapPlaceOrderResponse `json:"data"`
}
var r resp
path := coinbeneSwapAPIURL + coinbeneAPIVersion + coinbenePlaceOrder
err := c.SendAuthHTTPRequest(http.MethodPost,
path := coinbeneAPIVersion + coinbenePlaceOrder
err := c.SendAuthHTTPRequest(exchange.RestSwap, http.MethodPost,
path,
coinbenePlaceOrder,
true,
@@ -815,8 +815,8 @@ func (c *Coinbene) CancelSwapOrder(orderID string) (string, error) {
Data string `json:"data"`
}
var r resp
path := coinbeneSwapAPIURL + coinbeneAPIVersion + coinbeneCancelOrder
err := c.SendAuthHTTPRequest(http.MethodPost,
path := coinbeneAPIVersion + coinbeneCancelOrder
err := c.SendAuthHTTPRequest(exchange.RestSwap, http.MethodPost,
path,
coinbeneCancelOrder,
true,
@@ -843,8 +843,8 @@ func (c *Coinbene) GetSwapOpenOrders(symbol string, pageNum, pageSize int) (Swap
Data SwapOrders `json:"data"`
}
var r resp
path := coinbeneSwapAPIURL + coinbeneAPIVersion + coinbeneOpenOrders
err := c.SendAuthHTTPRequest(http.MethodGet,
path := coinbeneAPIVersion + coinbeneOpenOrders
err := c.SendAuthHTTPRequest(exchange.RestSwap, http.MethodGet,
path,
coinbeneOpenOrders,
true,
@@ -870,8 +870,8 @@ func (c *Coinbene) GetSwapOpenOrdersByPage(symbol string, latestOrderID int64) (
Data SwapOrders `json:"data"`
}
var r resp
path := coinbeneSwapAPIURL + coinbeneAPIVersion + coinbeneOpenOrdersByPage
err := c.SendAuthHTTPRequest(http.MethodGet,
path := coinbeneAPIVersion + coinbeneOpenOrdersByPage
err := c.SendAuthHTTPRequest(exchange.RestSwap, http.MethodGet,
path,
coinbeneOpenOrdersByPage,
true,
@@ -892,8 +892,8 @@ func (c *Coinbene) GetSwapOrderInfo(orderID string) (SwapOrder, error) {
Data SwapOrder `json:"data"`
}
var r resp
path := coinbeneSwapAPIURL + coinbeneAPIVersion + coinbeneOrderInfo
err := c.SendAuthHTTPRequest(http.MethodGet,
path := coinbeneAPIVersion + coinbeneOrderInfo
err := c.SendAuthHTTPRequest(exchange.RestSwap, http.MethodGet,
path,
coinbeneOrderInfo,
true,
@@ -935,8 +935,8 @@ func (c *Coinbene) GetSwapOrderHistory(beginTime, endTime, symbol string, pageNu
}
var r resp
path := coinbeneSwapAPIURL + coinbeneAPIVersion + coinbeneClosedOrders
err := c.SendAuthHTTPRequest(http.MethodGet,
path := coinbeneAPIVersion + coinbeneClosedOrders
err := c.SendAuthHTTPRequest(exchange.RestSwap, http.MethodGet,
path,
coinbeneClosedOrders,
true,
@@ -973,8 +973,8 @@ func (c *Coinbene) GetSwapOrderHistoryByOrderID(beginTime, endTime, symbol, stat
}
var r resp
path := coinbeneSwapAPIURL + coinbeneAPIVersion + coinbeneClosedOrdersByPage
err := c.SendAuthHTTPRequest(http.MethodGet,
path := coinbeneAPIVersion + coinbeneClosedOrdersByPage
err := c.SendAuthHTTPRequest(exchange.RestSwap, http.MethodGet,
path,
coinbeneClosedOrdersByPage,
true,
@@ -999,8 +999,8 @@ func (c *Coinbene) CancelSwapOrders(orderIDs []string) ([]OrderCancellationRespo
}
var r resp
path := coinbeneSwapAPIURL + coinbeneAPIVersion + coinbeneBatchCancel
err := c.SendAuthHTTPRequest(http.MethodPost,
path := coinbeneAPIVersion + coinbeneBatchCancel
err := c.SendAuthHTTPRequest(exchange.RestSwap, http.MethodPost,
path,
coinbeneBatchCancel,
true,
@@ -1030,8 +1030,8 @@ func (c *Coinbene) GetSwapOrderFills(symbol, orderID string, lastTradeID int64)
}
var r resp
path := coinbeneSwapAPIURL + coinbeneAPIVersion + coinbeneOrderFills
err := c.SendAuthHTTPRequest(http.MethodGet,
path := coinbeneAPIVersion + coinbeneOrderFills
err := c.SendAuthHTTPRequest(exchange.RestSwap, http.MethodGet,
path,
coinbeneOrderFills,
true,
@@ -1058,8 +1058,8 @@ func (c *Coinbene) GetSwapFundingRates(pageNum, pageSize int) ([]SwapFundingRate
}
var r resp
path := coinbeneSwapAPIURL + coinbeneAPIVersion + coinbenePositionFeeRate
err := c.SendAuthHTTPRequest(http.MethodGet,
path := coinbeneAPIVersion + coinbenePositionFeeRate
err := c.SendAuthHTTPRequest(exchange.RestSwap, http.MethodGet,
path,
coinbenePositionFeeRate,
true,
@@ -1073,7 +1073,11 @@ func (c *Coinbene) GetSwapFundingRates(pageNum, pageSize int) ([]SwapFundingRate
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (c *Coinbene) SendHTTPRequest(path string, f request.EndpointLimit, result interface{}) error {
func (c *Coinbene) SendHTTPRequest(ep exchange.URL, path string, f request.EndpointLimit, result interface{}) error {
endpoint, err := c.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
var resp json.RawMessage
errCap := struct {
Code int `json:"code"`
@@ -1082,7 +1086,7 @@ func (c *Coinbene) SendHTTPRequest(path string, f request.EndpointLimit, result
if err := c.SendPayload(context.Background(), &request.Item{
Method: http.MethodGet,
Path: path,
Path: endpoint + path,
Result: &resp,
Verbose: c.Verbose,
HTTPDebugging: c.HTTPDebugging,
@@ -1101,13 +1105,16 @@ func (c *Coinbene) SendHTTPRequest(path string, f request.EndpointLimit, result
}
// SendAuthHTTPRequest sends an authenticated HTTP request
func (c *Coinbene) SendAuthHTTPRequest(method, path, epPath string, isSwap bool,
func (c *Coinbene) SendAuthHTTPRequest(ep exchange.URL, method, path, epPath string, isSwap bool,
params, result interface{}, f request.EndpointLimit) error {
if !c.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet,
c.Name)
}
endpoint, err := c.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
authPath := coinbeneAuthPath
if isSwap {
authPath = coinbeneSwapAuthPath
@@ -1165,7 +1172,7 @@ func (c *Coinbene) SendAuthHTTPRequest(method, path, epPath string, isSwap bool,
defer cancel()
if err := c.SendPayload(ctx, &request.Item{
Method: method,
Path: path,
Path: endpoint + path,
Headers: headers,
Body: finalBody,
Result: &resp,

View File

@@ -242,7 +242,7 @@ func TestGetAccountInfo(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("API keys required but not set, skipping test")
}
_, err := c.UpdateAccountInfo()
_, err := c.UpdateAccountInfo(asset.Spot)
if err != nil {
t.Error(err)
}

View File

@@ -400,7 +400,7 @@ func (c *Coinbene) wsHandleData(respRaw []byte) error {
Status: oStatus,
AssetType: assetType,
Date: orders.Data[i].OrderTime,
Leverage: strconv.FormatInt(orders.Data[i].Leverage, 10),
Leverage: float64(orders.Data[i].Leverage),
Pair: newPair,
}
}

View File

@@ -146,10 +146,15 @@ func (c *Coinbene) SetDefaults() {
c.Requester = request.New(c.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
request.WithLimiter(SetRateLimit()))
c.API.Endpoints.URLDefault = coinbeneAPIURL
c.API.Endpoints.URL = c.API.Endpoints.URLDefault
c.API.Endpoints.WebsocketURL = wsContractURL
c.API.Endpoints = c.NewEndpoints()
err = c.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: coinbeneAPIURL,
exchange.RestSwap: coinbeneSwapAPIURL,
exchange.WebsocketSpot: wsContractURL,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
c.Websocket = stream.New()
c.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
c.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
@@ -168,6 +173,11 @@ func (c *Coinbene) Setup(exch *config.ExchangeConfig) error {
return err
}
wsRunningURL, err := c.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
return err
}
err = c.Websocket.Setup(&stream.WebsocketSetup{
Enabled: exch.Features.Enabled.Websocket,
Verbose: exch.Verbose,
@@ -175,7 +185,7 @@ func (c *Coinbene) Setup(exch *config.ExchangeConfig) error {
WebsocketTimeout: exch.WebsocketTrafficTimeout,
DefaultURL: wsContractURL,
ExchangeName: exch.Name,
RunningURL: exch.API.Endpoints.WebsocketURL,
RunningURL: wsRunningURL,
Connector: c.WsConnect,
Subscriber: c.Subscribe,
UnSubscriber: c.Unsubscribe,
@@ -464,7 +474,7 @@ func (c *Coinbene) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orde
// UpdateAccountInfo retrieves balances for all enabled currencies for the
// Coinbene exchange
func (c *Coinbene) UpdateAccountInfo() (account.Holdings, error) {
func (c *Coinbene) UpdateAccountInfo(assetType asset.Item) (account.Holdings, error) {
var info account.Holdings
balance, err := c.GetAccountBalances()
if err != nil {
@@ -494,10 +504,10 @@ func (c *Coinbene) UpdateAccountInfo() (account.Holdings, error) {
}
// FetchAccountInfo retrieves balances for all enabled currencies
func (c *Coinbene) FetchAccountInfo() (account.Holdings, error) {
acc, err := account.GetHoldings(c.Name)
func (c *Coinbene) FetchAccountInfo(assetType asset.Item) (account.Holdings, error) {
acc, err := account.GetHoldings(c.Name, assetType)
if err != nil {
return c.UpdateAccountInfo()
return c.UpdateAccountInfo(assetType)
}
return acc, nil
@@ -820,8 +830,8 @@ func (c *Coinbene) AuthenticateWebsocket() error {
// ValidateCredentials validates current credentials used for wrapper
// functionality
func (c *Coinbene) ValidateCredentials() error {
_, err := c.UpdateAccountInfo()
func (c *Coinbene) ValidateCredentials(assetType asset.Item) error {
_, err := c.UpdateAccountInfo(assetType)
return c.CheckTransientError(err)
}

View File

@@ -74,7 +74,7 @@ func (c *COINUT) GetInstruments() (Instruments, error) {
var result Instruments
params := make(map[string]interface{})
params["sec_type"] = strings.ToUpper(asset.Spot.String())
return result, c.SendHTTPRequest(coinutInstruments, params, false, &result)
return result, c.SendHTTPRequest(exchange.RestSpot, coinutInstruments, params, false, &result)
}
// GetInstrumentTicker returns a ticker for a specific instrument
@@ -82,7 +82,7 @@ func (c *COINUT) GetInstrumentTicker(instrumentID int64) (Ticker, error) {
var result Ticker
params := make(map[string]interface{})
params["inst_id"] = instrumentID
return result, c.SendHTTPRequest(coinutTicker, params, false, &result)
return result, c.SendHTTPRequest(exchange.RestSpot, coinutTicker, params, false, &result)
}
// GetInstrumentOrderbook returns the orderbooks for a specific instrument
@@ -94,7 +94,7 @@ func (c *COINUT) GetInstrumentOrderbook(instrumentID, limit int64) (Orderbook, e
params["top_n"] = limit
}
return result, c.SendHTTPRequest(coinutOrderbook, params, false, &result)
return result, c.SendHTTPRequest(exchange.RestSpot, coinutOrderbook, params, false, &result)
}
// GetTrades returns trade information
@@ -103,13 +103,13 @@ func (c *COINUT) GetTrades(instrumentID int64) (Trades, error) {
params := make(map[string]interface{})
params["inst_id"] = instrumentID
return result, c.SendHTTPRequest(coinutTrades, params, false, &result)
return result, c.SendHTTPRequest(exchange.RestSpot, coinutTrades, params, false, &result)
}
// GetUserBalance returns the full user balance
func (c *COINUT) GetUserBalance() (*UserBalance, error) {
var result *UserBalance
return result, c.SendHTTPRequest(coinutBalance, nil, true, &result)
return result, c.SendHTTPRequest(exchange.RestSpot, coinutBalance, nil, true, &result)
}
// NewOrder places a new order on the exchange
@@ -127,7 +127,7 @@ func (c *COINUT) NewOrder(instrumentID int64, quantity, price float64, buy bool,
}
params["client_ord_id"] = orderID
return result, c.SendHTTPRequest(coinutOrder, params, true, &result)
return result, c.SendHTTPRequest(exchange.RestSpot, coinutOrder, params, true, &result)
}
// NewOrders places multiple orders on the exchange
@@ -136,7 +136,7 @@ func (c *COINUT) NewOrders(orders []Order) ([]OrdersBase, error) {
params := make(map[string]interface{})
params["orders"] = orders
return result.Data, c.SendHTTPRequest(coinutOrders, params, true, &result.Data)
return result.Data, c.SendHTTPRequest(exchange.RestSpot, coinutOrders, params, true, &result.Data)
}
// GetOpenOrders returns a list of open order and relevant information
@@ -144,7 +144,7 @@ func (c *COINUT) GetOpenOrders(instrumentID int64) (GetOpenOrdersResponse, error
var result GetOpenOrdersResponse
params := make(map[string]interface{})
params["inst_id"] = instrumentID
return result, c.SendHTTPRequest(coinutOrdersOpen, params, true, &result)
return result, c.SendHTTPRequest(exchange.RestSpot, coinutOrdersOpen, params, true, &result)
}
// CancelExistingOrder cancels a specific order and returns if it was actioned
@@ -164,7 +164,7 @@ func (c *COINUT) CancelExistingOrder(instrumentID, orderID int64) (bool, error)
entries := []Request{entry}
params["entries"] = entries
err := c.SendHTTPRequest(coinutOrdersCancel, params, true, &result)
err := c.SendHTTPRequest(exchange.RestSpot, coinutOrdersCancel, params, true, &result)
if err != nil {
return false, err
}
@@ -184,7 +184,7 @@ func (c *COINUT) CancelOrders(orders []CancelOrders) (CancelOrdersResponse, erro
entries = append(entries, orders...)
params["entries"] = entries
return result, c.SendHTTPRequest(coinutOrdersCancel, params, true, &result)
return result, c.SendHTTPRequest(exchange.RestSpot, coinutOrdersCancel, params, true, &result)
}
// GetTradeHistory returns trade history for a specific instrument.
@@ -199,7 +199,7 @@ func (c *COINUT) GetTradeHistory(instrumentID, start, limit int64) (TradeHistory
params["limit"] = limit
}
return result, c.SendHTTPRequest(coinutTradeHistory, params, true, &result)
return result, c.SendHTTPRequest(exchange.RestSpot, coinutTradeHistory, params, true, &result)
}
// GetIndexTicker returns the index ticker for an asset
@@ -208,7 +208,7 @@ func (c *COINUT) GetIndexTicker(asset string) (IndexTicker, error) {
params := make(map[string]interface{})
params["asset"] = asset
return result, c.SendHTTPRequest(coinutIndexTicker, params, false, &result)
return result, c.SendHTTPRequest(exchange.RestSpot, coinutIndexTicker, params, false, &result)
}
// GetDerivativeInstruments returns a list of derivative instruments
@@ -217,7 +217,7 @@ func (c *COINUT) GetDerivativeInstruments(secType string) (interface{}, error) {
params := make(map[string]interface{})
params["sec_type"] = secType
return result, c.SendHTTPRequest(coinutInstruments, params, false, &result)
return result, c.SendHTTPRequest(exchange.RestSpot, coinutInstruments, params, false, &result)
}
// GetOptionChain returns option chain
@@ -227,7 +227,7 @@ func (c *COINUT) GetOptionChain(asset, secType string) (OptionChainResponse, err
params["asset"] = asset
params["sec_type"] = secType
return result, c.SendHTTPRequest(coinutOptionChain, params, false, &result)
return result, c.SendHTTPRequest(exchange.RestSpot, coinutOptionChain, params, false, &result)
}
// GetPositionHistory returns position history
@@ -242,7 +242,7 @@ func (c *COINUT) GetPositionHistory(secType string, start, limit int) (PositionH
params["limit"] = limit
}
return result, c.SendHTTPRequest(coinutPositionHistory, params, true, &result)
return result, c.SendHTTPRequest(exchange.RestSpot, coinutPositionHistory, params, true, &result)
}
// GetOpenPositions returns all your current opened positions
@@ -255,17 +255,22 @@ func (c *COINUT) GetOpenPositions(instrumentID int) ([]OpenPosition, error) {
params["inst_id"] = instrumentID
return result.Positions,
c.SendHTTPRequest(coinutPositionOpen, params, true, &result)
c.SendHTTPRequest(exchange.RestSpot, coinutPositionOpen, params, true, &result)
}
// to-do: user position update via websocket
// SendHTTPRequest sends either an authenticated or unauthenticated HTTP request
func (c *COINUT) SendHTTPRequest(apiRequest string, params map[string]interface{}, authenticated bool, result interface{}) (err error) {
func (c *COINUT) SendHTTPRequest(ep exchange.URL, apiRequest string, params map[string]interface{}, authenticated bool, result interface{}) (err error) {
if !c.API.AuthenticatedSupport && authenticated {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, c.Name)
}
endpoint, err := c.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
if params == nil {
params = map[string]interface{}{}
}
@@ -293,7 +298,7 @@ func (c *COINUT) SendHTTPRequest(apiRequest string, params map[string]interface{
var rawMsg json.RawMessage
err = c.SendPayload(context.Background(), &request.Item{
Method: http.MethodPost,
Path: c.API.Endpoints.URL,
Path: endpoint,
Headers: headers,
Body: bytes.NewBuffer(payload),
Result: &rawMsg,

View File

@@ -253,7 +253,8 @@ func TestFormatWithdrawPermissions(t *testing.T) {
func TestGetActiveOrders(t *testing.T) {
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := c.GetActiveOrders(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
@@ -264,7 +265,8 @@ func TestGetActiveOrders(t *testing.T) {
func TestGetOrderHistoryWrapper(t *testing.T) {
setupWSTestAuth(t)
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
Pairs: []currency.Pair{currency.NewPair(currency.BTC,
currency.USD)},
}
@@ -358,12 +360,12 @@ func TestCancelAllExchangeOrders(t *testing.T) {
func TestGetAccountInfo(t *testing.T) {
if apiKey != "" || clientID != "" {
_, err := c.UpdateAccountInfo()
_, err := c.UpdateAccountInfo(asset.Spot)
if err != nil {
t.Error("GetAccountInfo() error", err)
}
} else {
_, err := c.UpdateAccountInfo()
_, err := c.UpdateAccountInfo(asset.Spot)
if err == nil {
t.Error("GetAccountInfo() Expected error")
}

View File

@@ -113,10 +113,14 @@ func (c *COINUT) SetDefaults() {
c.Requester = request.New(c.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
c.API.Endpoints.URLDefault = coinutAPIURL
c.API.Endpoints.URL = c.API.Endpoints.URLDefault
c.API.Endpoints.WebsocketURL = coinutWebsocketURL
c.API.Endpoints = c.NewEndpoints()
err = c.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: coinutAPIURL,
exchange.WebsocketSpot: coinutWebsocketURL,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
c.Websocket = stream.New()
c.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
c.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
@@ -136,6 +140,11 @@ func (c *COINUT) Setup(exch *config.ExchangeConfig) error {
return err
}
wsRunningURL, err := c.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
return err
}
err = c.Websocket.Setup(&stream.WebsocketSetup{
Enabled: exch.Features.Enabled.Websocket,
Verbose: exch.Verbose,
@@ -143,7 +152,7 @@ func (c *COINUT) Setup(exch *config.ExchangeConfig) error {
WebsocketTimeout: exch.WebsocketTrafficTimeout,
DefaultURL: coinutWebsocketURL,
ExchangeName: exch.Name,
RunningURL: exch.API.Endpoints.WebsocketURL,
RunningURL: wsRunningURL,
Connector: c.WsConnect,
Subscriber: c.Subscribe,
UnSubscriber: c.Unsubscribe,
@@ -294,7 +303,7 @@ func (c *COINUT) UpdateTradablePairs(forceUpdate bool) error {
// UpdateAccountInfo retrieves balances for all enabled currencies for the
// COINUT exchange
func (c *COINUT) UpdateAccountInfo() (account.Holdings, error) {
func (c *COINUT) UpdateAccountInfo(assetType asset.Item) (account.Holdings, error) {
var info account.Holdings
var bal *UserBalance
var err error
@@ -384,10 +393,10 @@ func (c *COINUT) UpdateAccountInfo() (account.Holdings, error) {
}
// FetchAccountInfo retrieves balances for all enabled currencies
func (c *COINUT) FetchAccountInfo() (account.Holdings, error) {
acc, err := account.GetHoldings(c.Name)
func (c *COINUT) FetchAccountInfo(assetType asset.Item) (account.Holdings, error) {
acc, err := account.GetHoldings(c.Name, assetType)
if err != nil {
return c.UpdateAccountInfo()
return c.UpdateAccountInfo(assetType)
}
return acc, nil
@@ -1054,8 +1063,8 @@ func (c *COINUT) loadInstrumentsIfNotLoaded() error {
// ValidateCredentials validates current credentials used for wrapper
// functionality
func (c *COINUT) ValidateCredentials() error {
_, err := c.UpdateAccountInfo()
func (c *COINUT) ValidateCredentials(assetType asset.Item) error {
_, err := c.UpdateAccountInfo(assetType)
return c.CheckTransientError(err)
}

View File

@@ -11,6 +11,7 @@ import (
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/convert"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
@@ -578,6 +579,11 @@ func (e *Base) SetupDefaults(exch *config.ExchangeConfig) error {
}
e.SetFeatureDefaults()
if e.API.Endpoints == nil {
e.API.Endpoints = e.NewEndpoints()
}
err = e.SetAPIURL()
if err != nil {
return err
@@ -767,7 +773,6 @@ func (e *Base) UpdatePairs(exchangeProducts currency.Pairs, assetType asset.Item
e.Name,
strings.ToUpper(assetType.String()),
remove)
e.Config.CurrencyPairs.StorePairs(assetType, enabledPairs, true)
e.CurrencyPairs.StorePairs(assetType, enabledPairs, true)
}
@@ -778,52 +783,59 @@ func (e *Base) UpdatePairs(exchangeProducts currency.Pairs, assetType asset.Item
// SetAPIURL sets configuration API URL for an exchange
func (e *Base) SetAPIURL() error {
if e.Config.API.Endpoints.URL == "" || e.Config.API.Endpoints.URLSecondary == "" {
return fmt.Errorf("exchange %s: SetAPIURL error. URL vals are empty", e.Name)
}
checkInsecureEndpoint := func(endpoint string) {
if strings.Contains(endpoint, "https") {
if strings.Contains(endpoint, "https") || strings.Contains(endpoint, "wss") {
return
}
log.Warnf(log.ExchangeSys,
"%s is using HTTP instead of HTTPS [%s] for API functionality, an"+
"%s is using HTTP instead of HTTPS or WS instead of WSS [%s] for API functionality, an"+
" attacker could eavesdrop on this connection. Use at your"+
" own risk.",
e.Name, endpoint)
}
if e.Config.API.Endpoints.URL != config.APIURLNonDefaultMessage {
e.API.Endpoints.URL = e.Config.API.Endpoints.URL
checkInsecureEndpoint(e.API.Endpoints.URL)
}
if e.Config.API.Endpoints.URLSecondary != config.APIURLNonDefaultMessage {
e.API.Endpoints.URLSecondary = e.Config.API.Endpoints.URLSecondary
checkInsecureEndpoint(e.API.Endpoints.URLSecondary)
var err error
if e.Config.API.OldEndPoints != nil {
if e.Config.API.OldEndPoints.URL != "" && e.Config.API.OldEndPoints.URL != config.APIURLNonDefaultMessage {
err = e.API.Endpoints.SetRunning(RestSpot.String(), e.Config.API.OldEndPoints.URL)
if err != nil {
return err
}
checkInsecureEndpoint(e.Config.API.OldEndPoints.URL)
}
if e.Config.API.OldEndPoints.URLSecondary != "" && e.Config.API.OldEndPoints.URLSecondary != config.APIURLNonDefaultMessage {
err = e.API.Endpoints.SetRunning(RestSpotSupplementary.String(), e.Config.API.OldEndPoints.URLSecondary)
if err != nil {
return err
}
checkInsecureEndpoint(e.Config.API.OldEndPoints.URLSecondary)
}
if e.Config.API.OldEndPoints.WebsocketURL != "" && e.Config.API.OldEndPoints.WebsocketURL != config.WebsocketURLNonDefaultMessage {
err = e.API.Endpoints.SetRunning(WebsocketSpot.String(), e.Config.API.OldEndPoints.WebsocketURL)
if err != nil {
return err
}
checkInsecureEndpoint(e.Config.API.OldEndPoints.WebsocketURL)
}
e.Config.API.OldEndPoints = nil
} else if e.Config.API.Endpoints != nil {
for key, val := range e.Config.API.Endpoints {
if val == "" ||
val == config.APIURLNonDefaultMessage ||
val == config.WebsocketURLNonDefaultMessage {
continue
}
checkInsecureEndpoint(val)
err = e.API.Endpoints.SetRunning(key, val)
if err != nil {
return err
}
}
}
runningMap := e.API.Endpoints.GetURLMap()
e.Config.API.Endpoints = runningMap
return nil
}
// GetAPIURL returns the set API URL
func (e *Base) GetAPIURL() string {
return e.API.Endpoints.URL
}
// GetSecondaryAPIURL returns the set Secondary API URL
func (e *Base) GetSecondaryAPIURL() string {
return e.API.Endpoints.URLSecondary
}
// GetAPIURLDefault returns exchange default URL
func (e *Base) GetAPIURLDefault() string {
return e.API.Endpoints.URLDefault
}
// GetAPIURLSecondaryDefault returns exchange default secondary URL
func (e *Base) GetAPIURLSecondaryDefault() string {
return e.API.Endpoints.URLSecondaryDefault
}
// SupportsREST returns whether or not the exchange supports
// REST
func (e *Base) SupportsREST() bool {
@@ -946,6 +958,10 @@ func (e *Base) StoreAssetPairFormat(a asset.Item, f currency.PairStore) error {
e.Name)
}
if f.AssetEnabled == nil {
f.AssetEnabled = convert.BoolPtr(true)
}
if f.RequestFormat == nil {
return fmt.Errorf("%s cannot add to pairs manager, request pair format not provided",
e.Name)
@@ -1136,3 +1152,112 @@ func (e *Base) SetSaveTradeDataStatus(enabled bool) {
log.Debugf(log.Trade, "Set %v 'SaveTradeData' to %v", e.Name, enabled)
}
}
// NewEndpoints declares default and running URLs maps
func (e *Base) NewEndpoints() *Endpoints {
return &Endpoints{
Exchange: e.Name,
defaults: make(map[string]string),
}
}
// SetDefaultEndpoints declares and sets the default URLs map
func (e *Endpoints) SetDefaultEndpoints(m map[URL]string) error {
for k, v := range m {
err := e.SetRunning(k.String(), v)
if err != nil {
return err
}
}
return nil
}
// SetRunning populates running URLs map
func (e *Endpoints) SetRunning(key, val string) error {
e.Lock()
defer e.Unlock()
err := validateKey(key)
if err != nil {
return err
}
_, err = url.ParseRequestURI(val)
if err != nil {
log.Warnf(log.ExchangeSys, "Could not set custom URL for %s to %s for exchange %s. invalid URI for request.", key, val, e.Exchange)
return nil
}
e.defaults[key] = val
return nil
}
func validateKey(keyVal string) error {
for x := range keyURLs {
if keyURLs[x].String() == keyVal {
return nil
}
}
return errors.New("keyVal invalid")
}
// GetURL gets default url from URLs map
func (e *Endpoints) GetURL(key URL) (string, error) {
e.RLock()
defer e.RUnlock()
val, ok := e.defaults[key.String()]
if !ok {
return "", fmt.Errorf("no endpoint path found for the given key: %v", key)
}
return val, nil
}
// GetURLMap gets all urls for either running or default map based on the bool value supplied
func (e *Endpoints) GetURLMap() map[string]string {
e.RLock()
var urlMap = make(map[string]string)
for k, v := range e.defaults {
urlMap[k] = v
}
e.RUnlock()
return urlMap
}
// FormatSymbol formats the given pair to a string suitable for exchange API requests
func (e *Base) FormatSymbol(pair currency.Pair, assetType asset.Item) (string, error) {
pairFmt, err := e.GetPairFormat(assetType, true)
if err != nil {
return pair.String(), err
}
return pairFmt.Format(pair), nil
}
func (u URL) String() string {
switch u {
case RestSpot:
return "RestSpotURL"
case RestSpotSupplementary:
return "RestSpotSupplementaryURL"
case RestUSDTMargined:
return "RestUSDTMarginedFuturesURL"
case RestCoinMargined:
return "RestCoinMarginedFuturesURL"
case RestFutures:
return "RestFuturesURL"
case RestSandbox:
return "RestSandboxURL"
case RestSwap:
return "RestSwapURL"
case WebsocketSpot:
return "WebsocketSpotURL"
case WebsocketSpotSupplementary:
return "WebsocketSpotSupplementaryURL"
case ChainAnalysis:
return "ChainAnalysisURL"
case EdgeCase1:
return "EdgeCase1URL"
case EdgeCase2:
return "EdgeCase2URL"
case EdgeCase3:
return "EdgeCase3URL"
default:
return ""
}
}

View File

@@ -56,9 +56,138 @@ func TestSupportsRESTTickerBatchUpdates(t *testing.T) {
}
}
func TestCreateMap(t *testing.T) {
t.Parallel()
b := Base{
Name: "HELOOOOOOOO",
}
b.API.Endpoints = b.NewEndpoints()
err := b.API.Endpoints.SetDefaultEndpoints(map[URL]string{
EdgeCase1: "http://test1url.com/",
EdgeCase2: "http://test2url.com/",
})
if err != nil {
t.Error(err)
}
val, ok := b.API.Endpoints.defaults[EdgeCase1.String()]
if !ok || val != "http://test1url.com/" {
t.Errorf("CreateMap failed, incorrect value received for the given key")
}
}
func TestSet(t *testing.T) {
t.Parallel()
b := Base{
Name: "HELOOOOOOOO",
}
b.API.Endpoints = b.NewEndpoints()
err := b.API.Endpoints.SetDefaultEndpoints(map[URL]string{
EdgeCase1: "http://test1url.com/",
EdgeCase2: "http://test2url.com/",
})
if err != nil {
t.Error(err)
}
err = b.API.Endpoints.SetRunning(EdgeCase2.String(), "http://google.com/")
if err != nil {
t.Error(err)
}
val, ok := b.API.Endpoints.defaults[EdgeCase2.String()]
if !ok {
t.Error("set method or createmap failed")
}
if val != "http://google.com/" {
t.Errorf("vals didnt match. expecting: %s, got: %s\n", "http://google.com/", val)
}
err = b.API.Endpoints.SetRunning(EdgeCase3.String(), "Added Edgecase3")
if err != nil {
t.Errorf("not expecting an error since invalid url val err should be logged but received: %v", err)
}
}
func TestGetURL(t *testing.T) {
t.Parallel()
b := Base{
Name: "HELAAAAAOOOOOOOOO",
}
b.API.Endpoints = b.NewEndpoints()
b.API.Endpoints.SetDefaultEndpoints(map[URL]string{
EdgeCase1: "http://test1.com/",
EdgeCase2: "http://test2.com/",
})
getVal, err := b.API.Endpoints.GetURL(EdgeCase1)
if err != nil {
t.Error(err)
}
if getVal != "http://test1.com/" {
t.Errorf("getVal failed")
}
err = b.API.Endpoints.SetRunning(EdgeCase2.String(), "http://OVERWRITTENBRO.com.au/")
if err != nil {
t.Error(err)
}
getChangedVal, err := b.API.Endpoints.GetURL(EdgeCase2)
if err != nil {
t.Error(err)
}
if getChangedVal != "http://OVERWRITTENBRO.com.au/" {
t.Error("couldnt get changed val")
}
_, err = b.API.Endpoints.GetURL(URL(100))
if err == nil {
t.Error("expecting error due to invalid URL key parsed")
}
}
func TestGetAll(t *testing.T) {
t.Parallel()
b := Base{
Name: "HELLLLLLO",
}
b.API.Endpoints = b.NewEndpoints()
err := b.API.Endpoints.SetDefaultEndpoints(map[URL]string{
EdgeCase1: "http://test1.com.au/",
EdgeCase2: "http://test2.com.au/",
})
if err != nil {
t.Error(err)
}
allRunning := b.API.Endpoints.GetURLMap()
if len(allRunning) != 2 {
t.Error("invalid running map received")
}
}
func TestSetDefaultEndpoints(t *testing.T) {
t.Parallel()
b := Base{
Name: "HELLLLLLO",
}
b.API.Endpoints = b.NewEndpoints()
err := b.API.Endpoints.SetDefaultEndpoints(map[URL]string{
EdgeCase1: "http://test1.com.au/",
EdgeCase2: "http://test2.com.au/",
})
if err != nil {
t.Error(err)
}
b.API.Endpoints = b.NewEndpoints()
err = b.API.Endpoints.SetDefaultEndpoints(map[URL]string{
URL(15): "http://test2.com.au/",
})
if err == nil {
t.Error("expecting an error due to invalid url key")
}
err = b.API.Endpoints.SetDefaultEndpoints(map[URL]string{
EdgeCase1: "",
})
if err != nil {
t.Errorf("expecting a warning due due to invalid url val but got an error: %v", err)
}
}
func TestHTTPClient(t *testing.T) {
t.Parallel()
r := Base{Name: "asdf"}
r.SetHTTPClientTimeout(time.Second * 5)
@@ -1116,14 +1245,21 @@ func TestSetupDefaults(t *testing.T) {
AuthenticatedSupport: true,
},
}
b.SetupDefaults(&cfg)
err := b.SetupDefaults(&cfg)
if err != nil {
t.Fatal(err)
}
if cfg.HTTPTimeout.String() != "15s" {
t.Error("HTTP timeout should be set to 15s")
}
// Test custom HTTP timeout is set
cfg.HTTPTimeout = time.Second * 30
b.SetupDefaults(&cfg)
err = b.SetupDefaults(&cfg)
if err != nil {
t.Fatal(err)
}
if cfg.HTTPTimeout.String() != "30s" {
t.Error("HTTP timeout should be set to 30s")
}
@@ -1458,78 +1594,6 @@ func TestUpdatePairs(t *testing.T) {
}
}
func TestSetAPIURL(t *testing.T) {
t.Parallel()
testURL := "https://api.something.com"
testURLSecondary := "https://api.somethingelse.com"
testURLDefault := "https://api.defaultsomething.com"
testURLSecondaryDefault := "https://api.defaultsomethingelse.com"
tester := Base{Name: "test"}
tester.Config = new(config.ExchangeConfig)
err := tester.SetAPIURL()
if err == nil {
t.Error("setting zero value config")
}
tester.Config.API.Endpoints.URL = testURL
tester.Config.API.Endpoints.URLSecondary = testURLSecondary
tester.API.Endpoints.URLDefault = testURLDefault
tester.API.Endpoints.URLSecondaryDefault = testURLSecondaryDefault
err = tester.SetAPIURL()
if err != nil {
t.Error(err)
}
if tester.GetAPIURL() != testURL {
t.Error("incorrect return URL")
}
if tester.GetSecondaryAPIURL() != testURLSecondary {
t.Error("incorrect return URL")
}
if tester.GetAPIURLDefault() != testURLDefault {
t.Error("incorrect return URL")
}
if tester.GetAPIURLSecondaryDefault() != testURLSecondaryDefault {
t.Error("incorrect return URL")
}
tester.Config.API.Endpoints.URL = "http://insecureino.com"
tester.Config.API.Endpoints.URLSecondary = tester.Config.API.Endpoints.URL
err = tester.SetAPIURL()
if err != nil {
t.Error(err)
}
}
func BenchmarkSetAPIURL(b *testing.B) {
tester := Base{Name: "test"}
test := config.ExchangeConfig{}
test.API.Endpoints.URL = "https://api.something.com"
test.API.Endpoints.URLSecondary = "https://api.somethingelse.com"
tester.API.Endpoints.URLDefault = "https://api.defaultsomething.com"
tester.API.Endpoints.URLDefault = "https://api.defaultsomethingelse.com"
tester.Config = &test
for i := 0; i < b.N; i++ {
err := tester.SetAPIURL()
if err != nil {
b.Errorf("Benchmark failed %v", err)
}
}
}
func TestSupportsWebsocket(t *testing.T) {
t.Parallel()
@@ -2107,3 +2171,155 @@ func TestAddTradesToBuffer(t *testing.T) {
t.Error(err)
}
}
func TestString(t *testing.T) {
if RestSpot.String() != "RestSpotURL" {
t.Errorf("invalid string conversion")
}
if RestSpotSupplementary.String() != "RestSpotSupplementaryURL" {
t.Errorf("invalid string conversion")
}
if RestUSDTMargined.String() != "RestUSDTMarginedFuturesURL" {
t.Errorf("invalid string conversion")
}
if RestCoinMargined.String() != "RestCoinMarginedFuturesURL" {
t.Errorf("invalid string conversion")
}
if RestFutures.String() != "RestFuturesURL" {
t.Errorf("invalid string conversion")
}
if RestSandbox.String() != "RestSandboxURL" {
t.Errorf("invalid string conversion")
}
if RestSwap.String() != "RestSwapURL" {
t.Errorf("invalid string conversion")
}
if WebsocketSpot.String() != "WebsocketSpotURL" {
t.Errorf("invalid string conversion")
}
if WebsocketSpotSupplementary.String() != "WebsocketSpotSupplementaryURL" {
t.Errorf("invalid string conversion")
}
if ChainAnalysis.String() != "ChainAnalysisURL" {
t.Errorf("invalid string conversion")
}
if EdgeCase1.String() != "EdgeCase1URL" {
t.Errorf("invalid string conversion")
}
if EdgeCase2.String() != "EdgeCase2URL" {
t.Errorf("invalid string conversion")
}
if EdgeCase3.String() != "EdgeCase3URL" {
t.Errorf("invalid string conversion")
}
}
func TestFormatSymbol(t *testing.T) {
b := Base{}
spotStore := currency.PairStore{
RequestFormat: &currency.PairFormat{Uppercase: true},
ConfigFormat: &currency.PairFormat{
Delimiter: currency.DashDelimiter,
Uppercase: true,
},
}
err := b.StoreAssetPairFormat(asset.Spot, spotStore)
if err != nil {
t.Error(err)
}
pair, err := currency.NewPairFromString("BTC-USD")
if err != nil {
t.Error(err)
}
sym, err := b.FormatSymbol(pair, asset.Spot)
if err != nil {
t.Error(err)
}
if sym != "BTCUSD" {
t.Error("formatting failed")
}
_, err = b.FormatSymbol(pair, asset.Futures)
if err == nil {
t.Error("expecting an error since asset pair format has not been set")
}
}
func TestSetAPIURL(t *testing.T) {
b := Base{
Name: "SomeExchange",
}
b.Config = &config.ExchangeConfig{}
var mappy struct {
Mappymap map[string]string `json:"urlEndpoints"`
}
mappy.Mappymap = make(map[string]string)
mappy.Mappymap["hi"] = "http://google.com/"
b.Config.API.Endpoints = mappy.Mappymap
b.API.Endpoints = b.NewEndpoints()
err := b.SetAPIURL()
if err == nil {
t.Error("expecting an error since the key provided is invalid")
}
mappy.Mappymap = make(map[string]string)
b.Config.API.Endpoints = mappy.Mappymap
mappy.Mappymap["RestSpotURL"] = "hi"
b.API.Endpoints = b.NewEndpoints()
err = b.SetAPIURL()
if err != nil {
t.Errorf("expecting no error since invalid url value should be logged but received the following error: %v", err)
}
mappy.Mappymap = make(map[string]string)
b.Config.API.Endpoints = mappy.Mappymap
mappy.Mappymap["RestSpotURL"] = "http://google.com/"
b.API.Endpoints = b.NewEndpoints()
err = b.SetAPIURL()
if err != nil {
t.Error(err)
}
mappy.Mappymap = make(map[string]string)
b.Config.API.OldEndPoints = &config.APIEndpointsConfig{}
b.Config.API.Endpoints = mappy.Mappymap
mappy.Mappymap["RestSpotURL"] = "http://google.com/"
b.API.Endpoints = b.NewEndpoints()
b.Config.API.OldEndPoints.URL = "heloo"
err = b.SetAPIURL()
if err != nil {
t.Errorf("expecting a warning since invalid oldendpoints url but got an error: %v", err)
}
mappy.Mappymap = make(map[string]string)
b.Config.API.OldEndPoints = &config.APIEndpointsConfig{}
b.Config.API.Endpoints = mappy.Mappymap
mappy.Mappymap["RestSpotURL"] = "http://google.com/"
b.API.Endpoints = b.NewEndpoints()
b.Config.API.OldEndPoints.URL = "https://www.bitstamp.net/"
b.Config.API.OldEndPoints.URLSecondary = "https://www.secondary.net/"
b.Config.API.OldEndPoints.WebsocketURL = "https://www.websocket.net/"
err = b.SetAPIURL()
if err != nil {
t.Error(err)
}
var urlLookup URL
for x := range keyURLs {
if keyURLs[x].String() == "RestSpotURL" {
urlLookup = keyURLs[x]
}
}
urlData, err := b.API.Endpoints.GetURL(urlLookup)
if err != nil {
t.Error(err)
}
if urlData != "https://www.bitstamp.net/" {
t.Error("oldendpoints url setting failed")
}
}
func TestSetRunning(t *testing.T) {
b := Base{
Name: "HELOOOOOOOO",
}
b.API.Endpoints = b.NewEndpoints()
err := b.API.Endpoints.SetRunning(EdgeCase1.String(), "http://google.com/")
if err != nil {
t.Error(err)
}
}

View File

@@ -166,19 +166,20 @@ type FeaturesSupported struct {
Kline kline.ExchangeCapabilitiesSupported
}
// Endpoints stores running url endpoints for exchanges
type Endpoints struct {
Exchange string
defaults map[string]string
sync.RWMutex
}
// API stores the exchange API settings
type API struct {
AuthenticatedSupport bool
AuthenticatedWebsocketSupport bool
PEMKeySupport bool
Endpoints struct {
URL string
URLDefault string
URLSecondary string
URLSecondaryDefault string
WebsocketURL string
}
Endpoints *Endpoints
Credentials struct {
Key string
@@ -222,3 +223,37 @@ type Base struct {
settingsMutex sync.RWMutex
OrderbookVerificationBypass bool
}
// url lookup consts
const (
RestSpot URL = iota
RestSpotSupplementary
RestUSDTMargined
RestCoinMargined
RestFutures
RestSwap
RestSandbox
WebsocketSpot
WebsocketSpotSupplementary
ChainAnalysis
EdgeCase1
EdgeCase2
EdgeCase3
)
var keyURLs = []URL{RestSpot,
RestSpotSupplementary,
RestUSDTMargined,
RestCoinMargined,
RestFutures,
RestSwap,
RestSandbox,
WebsocketSpot,
WebsocketSpotSupplementary,
ChainAnalysis,
EdgeCase1,
EdgeCase2,
EdgeCase3}
// URL stores uint conversions
type URL uint16

View File

@@ -57,8 +57,8 @@ func (e *EXMO) GetTrades(symbol string) (map[string][]Trades, error) {
v := url.Values{}
v.Set("pair", symbol)
result := make(map[string][]Trades)
urlPath := fmt.Sprintf("%s/v%s/%s", e.API.Endpoints.URL, exmoAPIVersion, exmoTrades)
return result, e.SendHTTPRequest(common.EncodeURLValues(urlPath, v), &result)
urlPath := fmt.Sprintf("/v%s/%s", exmoAPIVersion, exmoTrades)
return result, e.SendHTTPRequest(exchange.RestSpot, common.EncodeURLValues(urlPath, v), &result)
}
// GetOrderbook returns the orderbook for a symbol or symbols
@@ -66,36 +66,36 @@ func (e *EXMO) GetOrderbook(symbol string) (map[string]Orderbook, error) {
v := url.Values{}
v.Set("pair", symbol)
result := make(map[string]Orderbook)
urlPath := fmt.Sprintf("%s/v%s/%s", e.API.Endpoints.URL, exmoAPIVersion, exmoOrderbook)
return result, e.SendHTTPRequest(common.EncodeURLValues(urlPath, v), &result)
urlPath := fmt.Sprintf("/v%s/%s", exmoAPIVersion, exmoOrderbook)
return result, e.SendHTTPRequest(exchange.RestSpot, common.EncodeURLValues(urlPath, v), &result)
}
// GetTicker returns the ticker for a symbol or symbols
func (e *EXMO) GetTicker() (map[string]Ticker, error) {
v := url.Values{}
result := make(map[string]Ticker)
urlPath := fmt.Sprintf("%s/v%s/%s", e.API.Endpoints.URL, exmoAPIVersion, exmoTicker)
return result, e.SendHTTPRequest(common.EncodeURLValues(urlPath, v), &result)
urlPath := fmt.Sprintf("/v%s/%s", exmoAPIVersion, exmoTicker)
return result, e.SendHTTPRequest(exchange.RestSpot, common.EncodeURLValues(urlPath, v), &result)
}
// GetPairSettings returns the pair settings for a symbol or symbols
func (e *EXMO) GetPairSettings() (map[string]PairSettings, error) {
result := make(map[string]PairSettings)
urlPath := fmt.Sprintf("%s/v%s/%s", e.API.Endpoints.URL, exmoAPIVersion, exmoPairSettings)
return result, e.SendHTTPRequest(urlPath, &result)
urlPath := fmt.Sprintf("/v%s/%s", exmoAPIVersion, exmoPairSettings)
return result, e.SendHTTPRequest(exchange.RestSpot, urlPath, &result)
}
// GetCurrency returns a list of currencies
func (e *EXMO) GetCurrency() ([]string, error) {
var result []string
urlPath := fmt.Sprintf("%s/v%s/%s", e.API.Endpoints.URL, exmoAPIVersion, exmoCurrency)
return result, e.SendHTTPRequest(urlPath, &result)
urlPath := fmt.Sprintf("/v%s/%s", exmoAPIVersion, exmoCurrency)
return result, e.SendHTTPRequest(exchange.RestSpot, urlPath, &result)
}
// GetUserInfo returns the user info
func (e *EXMO) GetUserInfo() (UserInfo, error) {
var result UserInfo
err := e.SendAuthenticatedHTTPRequest(http.MethodPost, exmoUserInfo, url.Values{}, &result)
err := e.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, exmoUserInfo, url.Values{}, &result)
return result, err
}
@@ -116,7 +116,7 @@ func (e *EXMO) CreateOrder(pair, orderType string, price, amount float64) (int64
v.Set("quantity", strconv.FormatFloat(amount, 'f', -1, 64))
var resp response
err := e.SendAuthenticatedHTTPRequest(http.MethodPost, exmoOrderCreate, v, &resp)
err := e.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, exmoOrderCreate, v, &resp)
if !resp.Result {
return -1, errors.New(resp.Error)
}
@@ -132,7 +132,7 @@ func (e *EXMO) CancelExistingOrder(orderID int64) error {
Error string `json:"error"`
}
var resp response
err := e.SendAuthenticatedHTTPRequest(http.MethodPost, exmoOrderCancel, v, &resp)
err := e.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, exmoOrderCancel, v, &resp)
if !resp.Result {
return errors.New(resp.Error)
}
@@ -142,7 +142,7 @@ func (e *EXMO) CancelExistingOrder(orderID int64) error {
// GetOpenOrders returns the users open orders
func (e *EXMO) GetOpenOrders() (map[string]OpenOrders, error) {
result := make(map[string]OpenOrders)
err := e.SendAuthenticatedHTTPRequest(http.MethodPost, exmoOpenOrders, url.Values{}, &result)
err := e.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, exmoOpenOrders, url.Values{}, &result)
return result, err
}
@@ -160,7 +160,7 @@ func (e *EXMO) GetUserTrades(pair, offset, limit string) (map[string][]UserTrade
v.Set("limit", limit)
}
err := e.SendAuthenticatedHTTPRequest(http.MethodPost, exmoUserTrades, v, &result)
err := e.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, exmoUserTrades, v, &result)
return result, err
}
@@ -177,7 +177,7 @@ func (e *EXMO) GetCancelledOrders(offset, limit string) ([]CancelledOrder, error
v.Set("limit", limit)
}
err := e.SendAuthenticatedHTTPRequest(http.MethodPost, exmoCancelledOrders, v, &result)
err := e.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, exmoCancelledOrders, v, &result)
return result, err
}
@@ -187,7 +187,7 @@ func (e *EXMO) GetOrderTrades(orderID int64) (OrderTrades, error) {
v := url.Values{}
v.Set("order_id", strconv.FormatInt(orderID, 10))
err := e.SendAuthenticatedHTTPRequest(http.MethodPost, exmoOrderTrades, v, &result)
err := e.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, exmoOrderTrades, v, &result)
return result, err
}
@@ -198,14 +198,14 @@ func (e *EXMO) GetRequiredAmount(pair string, amount float64) (RequiredAmount, e
v.Set("pair", pair)
v.Set("quantity", strconv.FormatFloat(amount, 'f', -1, 64))
var result RequiredAmount
err := e.SendAuthenticatedHTTPRequest(http.MethodPost, exmoRequiredAmount, v, &result)
err := e.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, exmoRequiredAmount, v, &result)
return result, err
}
// GetCryptoDepositAddress returns a list of addresses for cryptocurrency deposits
func (e *EXMO) GetCryptoDepositAddress() (map[string]string, error) {
var result interface{}
err := e.SendAuthenticatedHTTPRequest(http.MethodPost, exmoDepositAddress, url.Values{}, &result)
err := e.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, exmoDepositAddress, url.Values{}, &result)
if err != nil {
return nil, err
}
@@ -243,7 +243,7 @@ func (e *EXMO) WithdrawCryptocurrency(currency, address, invoice string, amount
v.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
var resp response
err := e.SendAuthenticatedHTTPRequest(http.MethodPost, exmoWithdrawCrypt, v, &resp)
err := e.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, exmoWithdrawCrypt, v, &resp)
if err != nil {
return -1, err
}
@@ -264,7 +264,7 @@ func (e *EXMO) GetWithdrawTXID(taskID int64) (string, error) {
v.Set("task_id", strconv.FormatInt(taskID, 10))
var result response
err := e.SendAuthenticatedHTTPRequest(http.MethodPost, exmoGetWithdrawTXID, v, &result)
err := e.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, exmoGetWithdrawTXID, v, &result)
return result.TXID, err
}
@@ -275,7 +275,7 @@ func (e *EXMO) ExcodeCreate(currency string, amount float64) (ExcodeCreate, erro
v.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
var result ExcodeCreate
err := e.SendAuthenticatedHTTPRequest(http.MethodPost, exmoExcodeCreate, v, &result)
err := e.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, exmoExcodeCreate, v, &result)
return result, err
}
@@ -285,7 +285,7 @@ func (e *EXMO) ExcodeLoad(excode string) (ExcodeLoad, error) {
v.Set("code", excode)
var result ExcodeLoad
err := e.SendAuthenticatedHTTPRequest(http.MethodPost, exmoExcodeLoad, v, &result)
err := e.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, exmoExcodeLoad, v, &result)
return result, err
}
@@ -295,15 +295,19 @@ func (e *EXMO) GetWalletHistory(date int64) (WalletHistory, error) {
v.Set("date", strconv.FormatInt(date, 10))
var result WalletHistory
err := e.SendAuthenticatedHTTPRequest(http.MethodPost, exmoWalletHistory, v, &result)
err := e.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, exmoWalletHistory, v, &result)
return result, err
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (e *EXMO) SendHTTPRequest(path string, result interface{}) error {
func (e *EXMO) SendHTTPRequest(endpoint exchange.URL, path string, result interface{}) error {
urlPath, err := e.API.Endpoints.GetURL(endpoint)
if err != nil {
return err
}
return e.SendPayload(context.Background(), &request.Item{
Method: http.MethodGet,
Path: path,
Path: urlPath + path,
Result: result,
Verbose: e.Verbose,
HTTPDebugging: e.HTTPDebugging,
@@ -312,12 +316,17 @@ func (e *EXMO) SendHTTPRequest(path string, result interface{}) error {
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request
func (e *EXMO) SendAuthenticatedHTTPRequest(method, endpoint string, vals url.Values, result interface{}) error {
func (e *EXMO) SendAuthenticatedHTTPRequest(epath exchange.URL, method, endpoint string, vals url.Values, result interface{}) error {
if !e.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet,
e.Name)
}
urlPath, err := e.API.Endpoints.GetURL(epath)
if err != nil {
return err
}
n := e.Requester.GetNonce(true).String()
vals.Set("nonce", n)
@@ -338,11 +347,11 @@ func (e *EXMO) SendAuthenticatedHTTPRequest(method, endpoint string, vals url.Va
headers["Sign"] = crypto.HexEncodeToString(hash)
headers["Content-Type"] = "application/x-www-form-urlencoded"
path := fmt.Sprintf("%s/v%s/%s", e.API.Endpoints.URL, exmoAPIVersion, endpoint)
path := fmt.Sprintf("/v%s/%s", exmoAPIVersion, endpoint)
return e.SendPayload(context.Background(), &request.Item{
Method: method,
Path: path,
Path: urlPath + path,
Headers: headers,
Body: strings.NewReader(payload),
Result: result,

View File

@@ -264,7 +264,8 @@ func TestFormatWithdrawPermissions(t *testing.T) {
func TestGetActiveOrders(t *testing.T) {
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := e.GetActiveOrders(&getOrdersRequest)
@@ -277,7 +278,8 @@ func TestGetActiveOrders(t *testing.T) {
func TestGetOrderHistory(t *testing.T) {
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
currPair := currency.NewPair(currency.BTC, currency.USD)
currPair.Delimiter = "_"

View File

@@ -108,9 +108,13 @@ func (e *EXMO) SetDefaults() {
e.Requester = request.New(e.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
request.WithLimiter(request.NewBasicRateLimit(exmoRateInterval, exmoRequestRate)))
e.API.Endpoints.URLDefault = exmoAPIURL
e.API.Endpoints.URL = e.API.Endpoints.URLDefault
e.API.Endpoints = e.NewEndpoints()
err = e.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: exmoAPIURL,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
}
// Setup takes in the supplied exchange configuration details and sets params
@@ -320,7 +324,7 @@ func (e *EXMO) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderboo
// UpdateAccountInfo retrieves balances for all enabled currencies for the
// Exmo exchange
func (e *EXMO) UpdateAccountInfo() (account.Holdings, error) {
func (e *EXMO) UpdateAccountInfo(assetType asset.Item) (account.Holdings, error) {
var response account.Holdings
response.Exchange = e.Name
result, err := e.GetUserInfo()
@@ -356,10 +360,10 @@ func (e *EXMO) UpdateAccountInfo() (account.Holdings, error) {
}
// FetchAccountInfo retrieves balances for all enabled currencies
func (e *EXMO) FetchAccountInfo() (account.Holdings, error) {
acc, err := account.GetHoldings(e.Name)
func (e *EXMO) FetchAccountInfo(assetType asset.Item) (account.Holdings, error) {
acc, err := account.GetHoldings(e.Name, assetType)
if err != nil {
return e.UpdateAccountInfo()
return e.UpdateAccountInfo(assetType)
}
return acc, nil
@@ -655,8 +659,8 @@ func (e *EXMO) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, err
// ValidateCredentials validates current credentials used for wrapper
// functionality
func (e *EXMO) ValidateCredentials() error {
_, err := e.UpdateAccountInfo()
func (e *EXMO) ValidateCredentials(assetType asset.Item) error {
_, err := e.UpdateAccountInfo(assetType)
return e.CheckTransientError(err)
}

View File

@@ -36,7 +36,7 @@ const (
getFutures = "/futures"
getFuture = "/futures/"
getFutureStats = "/futures/%s/stats"
getFundingRates = "/funding_rates"
getFundingRates = "/funding_rates?"
getIndexWeights = "/indexes/%s/weights"
getAllWalletBalances = "/wallet/all_balances"
@@ -90,6 +90,17 @@ const (
getOTCQuoteStatus = "/otc/quotes/"
acceptOTCQuote = "/otc/quotes/%s/accept"
// Margin Endpoints
marginBorrowRates = "/spot_margin/borrow_rates"
maringLendingRates = "/spot_margin/lending_rates"
dailyBorrowedAmounts = "/spot_margin/borrow_summary"
marginMarketInfo = "/spot_margin/market_info?market=%s"
marginBorrowHistory = "/spot_margin/borrow_history"
marginLendHistory = "/spot_margin/lending_history"
marginLendingOffers = "/spot_margin/offers"
marginLendingInfo = "/spot_margin/lending_info"
submitLendingOrder = "/spot_margin/offers"
// Other Consts
trailingStopOrderType = "trailingStop"
takeProfitOrderType = "takeProfit"
@@ -106,7 +117,7 @@ func (f *FTX) GetMarkets() ([]MarketData, error) {
resp := struct {
Data []MarketData `json:"result"`
}{}
return resp.Data, f.SendHTTPRequest(ftxAPIURL+getMarkets, &resp)
return resp.Data, f.SendHTTPRequest(exchange.RestSpot, getMarkets, &resp)
}
// GetMarket gets market data for a provided asset type
@@ -114,7 +125,7 @@ func (f *FTX) GetMarket(marketName string) (MarketData, error) {
resp := struct {
Data MarketData `json:"result"`
}{}
return resp.Data, f.SendHTTPRequest(ftxAPIURL+getMarket+marketName,
return resp.Data, f.SendHTTPRequest(exchange.RestSpot, getMarket+marketName,
&resp)
}
@@ -131,7 +142,7 @@ func (f *FTX) GetOrderbook(marketName string, depth int64) (OrderbookData, error
}
var resp OrderbookData
err := f.SendHTTPRequest(fmt.Sprintf(ftxAPIURL+getOrderbook, marketName, strDepth), &result)
err := f.SendHTTPRequest(exchange.RestSpot, fmt.Sprintf(getOrderbook, marketName, strDepth), &result)
if err != nil {
return resp, err
}
@@ -166,7 +177,7 @@ func (f *FTX) GetTrades(marketName string, startTime, endTime, limit int64) ([]T
params.Set("start_time", strconv.FormatInt(startTime, 10))
params.Set("end_time", strconv.FormatInt(endTime, 10))
}
return resp.Data, f.SendHTTPRequest(fmt.Sprintf(ftxAPIURL+getTrades, marketName)+params.Encode(),
return resp.Data, f.SendHTTPRequest(exchange.RestSpot, fmt.Sprintf(getTrades, marketName)+params.Encode(),
&resp)
}
@@ -187,7 +198,7 @@ func (f *FTX) GetHistoricalData(marketName, timeInterval, limit string, startTim
params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10))
params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10))
}
return resp.Data, f.SendHTTPRequest(fmt.Sprintf(ftxAPIURL+getHistoricalData, marketName)+params.Encode(), &resp)
return resp.Data, f.SendHTTPRequest(exchange.RestSpot, fmt.Sprintf(getHistoricalData, marketName)+params.Encode(), &resp)
}
// GetFutures gets data on futures
@@ -195,7 +206,7 @@ func (f *FTX) GetFutures() ([]FuturesData, error) {
resp := struct {
Data []FuturesData `json:"result"`
}{}
return resp.Data, f.SendHTTPRequest(ftxAPIURL+getFutures, &resp)
return resp.Data, f.SendHTTPRequest(exchange.RestSpot, getFutures, &resp)
}
// GetFuture gets data on a given future
@@ -203,7 +214,7 @@ func (f *FTX) GetFuture(futureName string) (FuturesData, error) {
resp := struct {
Data FuturesData `json:"result"`
}{}
return resp.Data, f.SendHTTPRequest(ftxAPIURL+getFuture+futureName, &resp)
return resp.Data, f.SendHTTPRequest(exchange.RestSpot, getFuture+futureName, &resp)
}
// GetFutureStats gets data on a given future's stats
@@ -211,28 +222,43 @@ func (f *FTX) GetFutureStats(futureName string) (FutureStatsData, error) {
resp := struct {
Data FutureStatsData `json:"result"`
}{}
return resp.Data, f.SendHTTPRequest(fmt.Sprintf(ftxAPIURL+getFutureStats, futureName), &resp)
return resp.Data, f.SendHTTPRequest(exchange.RestSpot, fmt.Sprintf(getFutureStats, futureName), &resp)
}
// GetFundingRates gets data on funding rates
func (f *FTX) GetFundingRates() ([]FundingRatesData, error) {
func (f *FTX) GetFundingRates(startTime, endTime time.Time, future string) ([]FundingRatesData, error) {
resp := struct {
Data []FundingRatesData `json:"result"`
}{}
return resp.Data, f.SendHTTPRequest(ftxAPIURL+getFundingRates, &resp)
params := url.Values{}
if !startTime.IsZero() && !endTime.IsZero() {
if startTime.After(endTime) {
return resp.Data, errors.New("startTime cannot be after endTime")
}
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)
}
// GetIndexWeights gets index weights
func (f *FTX) GetIndexWeights(index string) (IndexWeights, error) {
var resp IndexWeights
return resp, f.SendHTTPRequest(ftxAPIURL+fmt.Sprintf(getIndexWeights, index), &resp)
return resp, f.SendHTTPRequest(exchange.RestSpot, fmt.Sprintf(getIndexWeights, index), &resp)
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (f *FTX) SendHTTPRequest(path string, result interface{}) error {
func (f *FTX) SendHTTPRequest(ep exchange.URL, path string, result interface{}) error {
endpoint, err := f.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
return f.SendPayload(context.Background(), &request.Item{
Method: http.MethodGet,
Path: path,
Path: endpoint + path,
Result: result,
Verbose: f.Verbose,
HTTPDebugging: f.HTTPDebugging,
@@ -240,12 +266,86 @@ func (f *FTX) SendHTTPRequest(path string, result interface{}) error {
})
}
// GetMarginBorrowRates gets borrowing rates for margin trading
func (f *FTX) GetMarginBorrowRates() ([]MarginFundingData, error) {
r := struct {
Data []MarginFundingData `json:"result"`
}{}
return r.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, marginBorrowRates, nil, &r)
}
// GetMarginLendingRates gets lending rates for margin trading
func (f *FTX) GetMarginLendingRates() ([]MarginFundingData, error) {
r := struct {
Data []MarginFundingData `json:"result"`
}{}
return r.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, maringLendingRates, nil, &r)
}
// MarginDailyBorrowedAmounts gets daily borrowed amounts for margin
func (f *FTX) MarginDailyBorrowedAmounts() ([]MarginMarketInfo, error) {
r := struct {
Data []MarginMarketInfo `json:"result"`
}{}
return r.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, dailyBorrowedAmounts, nil, &r)
}
// GetMarginMarketInfo gets margin market data
func (f *FTX) GetMarginMarketInfo(market string) ([]MarginMarketInfo, error) {
r := struct {
Data []MarginMarketInfo `json:"result"`
}{}
return r.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, fmt.Sprintf(marginMarketInfo, market), nil, &r)
}
// GetMarginBorrowHistory gets margin borrowing history
func (f *FTX) GetMarginBorrowHistory() ([]MarginTransactionHistoryData, error) {
r := struct {
Data []MarginTransactionHistoryData `json:"result"`
}{}
return r.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, marginBorrowHistory, nil, &r)
}
// GetMarginLendingHistory gets margin lending history
func (f *FTX) GetMarginLendingHistory() ([]MarginTransactionHistoryData, error) {
r := struct {
Data []MarginTransactionHistoryData `json:"result"`
}{}
return r.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, marginLendHistory, nil, &r)
}
// GetMarginLendingOffers gets margin lending offers
func (f *FTX) GetMarginLendingOffers() ([]LendingOffersData, error) {
r := struct {
Data []LendingOffersData `json:"result"`
}{}
return r.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, marginLendingOffers, nil, &r)
}
// GetLendingInfo gets margin lending info
func (f *FTX) GetLendingInfo() ([]LendingInfoData, error) {
r := struct {
Data []LendingInfoData `json:"result"`
}{}
return r.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, marginLendingInfo, nil, &r)
}
// SubmitLendingOffer submits an offer for margin lending
func (f *FTX) SubmitLendingOffer(coin string, size, rate float64) (interface{}, error) {
var resp interface{}
req := make(map[string]interface{})
req["coin"] = coin
req["size"] = size
req["rate"] = rate
return resp, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, marginLendingInfo, req, &resp)
}
// GetAccountInfo gets account info
func (f *FTX) GetAccountInfo() (AccountInfoData, error) {
resp := struct {
Data AccountInfoData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getAccountInfo, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getAccountInfo, nil, &resp)
}
// GetPositions gets the users positions
@@ -253,14 +353,14 @@ func (f *FTX) GetPositions() ([]PositionData, error) {
resp := struct {
Data []PositionData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getPositions, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getPositions, nil, &resp)
}
// ChangeAccountLeverage changes default leverage used by account
func (f *FTX) ChangeAccountLeverage(leverage float64) error {
req := make(map[string]interface{})
req["leverage"] = leverage
return f.SendAuthHTTPRequest(http.MethodPost, setLeverage, req, nil)
return f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost, setLeverage, req, nil)
}
// GetCoins gets coins' data in the account wallet
@@ -268,7 +368,7 @@ func (f *FTX) GetCoins() ([]WalletCoinsData, error) {
resp := struct {
Data []WalletCoinsData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getCoins, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getCoins, nil, &resp)
}
// GetBalances gets balances of the account
@@ -276,7 +376,7 @@ func (f *FTX) GetBalances() ([]BalancesData, error) {
resp := struct {
Data []BalancesData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getBalances, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getBalances, nil, &resp)
}
// GetAllWalletBalances gets all wallets' balances
@@ -284,7 +384,7 @@ func (f *FTX) GetAllWalletBalances() (AllWalletAccountData, error) {
resp := struct {
Data AllWalletAccountData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getAllWalletBalances, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getAllWalletBalances, nil, &resp)
}
// FetchDepositAddress gets deposit address for a given coin
@@ -292,7 +392,7 @@ func (f *FTX) FetchDepositAddress(coin string) (DepositData, error) {
resp := struct {
Data DepositData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getDepositAddress+coin, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getDepositAddress+coin, nil, &resp)
}
// FetchDepositHistory gets deposit history
@@ -300,7 +400,7 @@ func (f *FTX) FetchDepositHistory() ([]TransactionData, error) {
resp := struct {
Data []TransactionData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getDepositHistory, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getDepositHistory, nil, &resp)
}
// FetchWithdrawalHistory gets withdrawal history
@@ -308,7 +408,7 @@ func (f *FTX) FetchWithdrawalHistory() ([]TransactionData, error) {
resp := struct {
Data []TransactionData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getWithdrawalHistory, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getWithdrawalHistory, nil, &resp)
}
// Withdraw sends a withdrawal request
@@ -329,7 +429,7 @@ func (f *FTX) Withdraw(coin, address, tag, password, code string, size float64)
resp := struct {
Data TransactionData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, withdrawRequest, req, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost, withdrawRequest, req, &resp)
}
// GetOpenOrders gets open orders
@@ -341,7 +441,7 @@ func (f *FTX) GetOpenOrders(marketName string) ([]OrderData, error) {
resp := struct {
Data []OrderData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOpenOrders+params.Encode(), nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getOpenOrders+params.Encode(), nil, &resp)
}
// FetchOrderHistory gets order history
@@ -363,7 +463,7 @@ func (f *FTX) FetchOrderHistory(marketName string, startTime, endTime time.Time,
if limit != "" {
params.Set("limit", limit)
}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOrderHistory+params.Encode(), nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getOrderHistory+params.Encode(), nil, &resp)
}
// GetOpenTriggerOrders gets trigger orders that are currently open
@@ -378,7 +478,7 @@ func (f *FTX) GetOpenTriggerOrders(marketName, orderType string) ([]TriggerOrder
resp := struct {
Data []TriggerOrderData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOpenTriggerOrders+params.Encode(), nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getOpenTriggerOrders+params.Encode(), nil, &resp)
}
// GetTriggerOrderTriggers gets trigger orders that are currently open
@@ -386,7 +486,7 @@ func (f *FTX) GetTriggerOrderTriggers(orderID string) ([]TriggerData, error) {
resp := struct {
Data []TriggerData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, fmt.Sprintf(getTriggerOrderTriggers, orderID), nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, fmt.Sprintf(getTriggerOrderTriggers, orderID), nil, &resp)
}
// GetTriggerOrderHistory gets trigger orders that are currently open
@@ -414,7 +514,7 @@ func (f *FTX) GetTriggerOrderHistory(marketName string, startTime, endTime time.
if limit != "" {
params.Set("limit", limit)
}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getTriggerOrderHistory+params.Encode(), nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getTriggerOrderHistory+params.Encode(), nil, &resp)
}
// Order places an order
@@ -440,7 +540,7 @@ func (f *FTX) Order(marketName, side, orderType, reduceOnly, ioc, postOnly, clie
resp := struct {
Data OrderData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, placeOrder, req, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost, placeOrder, req, &resp)
}
// TriggerOrder places an order
@@ -470,7 +570,7 @@ func (f *FTX) TriggerOrder(marketName, side, orderType, reduceOnly, retryUntilFi
resp := struct {
Data TriggerOrderData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, placeTriggerOrder, req, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost, placeTriggerOrder, req, &resp)
}
// ModifyPlacedOrder modifies a placed order
@@ -484,7 +584,7 @@ func (f *FTX) ModifyPlacedOrder(orderID, clientID string, price, size float64) (
resp := struct {
Data OrderData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(modifyOrder, orderID), req, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost, fmt.Sprintf(modifyOrder, orderID), req, &resp)
}
// ModifyOrderByClientID modifies a placed order via clientOrderID
@@ -498,7 +598,7 @@ func (f *FTX) ModifyOrderByClientID(clientOrderID, clientID string, price, size
resp := struct {
Data OrderData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(modifyOrderByClientID, clientOrderID), req, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost, fmt.Sprintf(modifyOrderByClientID, clientOrderID), req, &resp)
}
// ModifyTriggerOrder modifies an existing trigger order
@@ -520,7 +620,7 @@ func (f *FTX) ModifyTriggerOrder(orderID, orderType string, size, triggerPrice,
resp := struct {
Data TriggerOrderData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(modifyTriggerOrder, orderID), req, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost, fmt.Sprintf(modifyTriggerOrder, orderID), req, &resp)
}
// GetOrderStatus gets the order status of a given orderID
@@ -528,7 +628,7 @@ func (f *FTX) GetOrderStatus(orderID string) (OrderData, error) {
resp := struct {
Data OrderData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOrderStatus+orderID, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getOrderStatus+orderID, nil, &resp)
}
// GetOrderStatusByClientID gets the order status of a given clientOrderID
@@ -536,7 +636,7 @@ func (f *FTX) GetOrderStatusByClientID(clientOrderID string) (OrderData, error)
resp := struct {
Data OrderData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOrderStatusByClientID+clientOrderID, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getOrderStatusByClientID+clientOrderID, nil, &resp)
}
// DeleteOrder deletes an order
@@ -545,7 +645,7 @@ func (f *FTX) DeleteOrder(orderID string) (string, error) {
Result string `json:"result"`
Success bool `json:"success"`
}{}
if err := f.SendAuthHTTPRequest(http.MethodDelete, deleteOrder+orderID, nil, &resp); err != nil {
if err := f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodDelete, deleteOrder+orderID, nil, &resp); err != nil {
return "", err
}
if !resp.Success {
@@ -561,7 +661,7 @@ func (f *FTX) DeleteOrderByClientID(clientID string) (string, error) {
Success bool `json:"success"`
}{}
if err := f.SendAuthHTTPRequest(http.MethodDelete, deleteOrderByClientID+clientID, nil, &resp); err != nil {
if err := f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodDelete, deleteOrderByClientID+clientID, nil, &resp); err != nil {
return "", err
}
if !resp.Success {
@@ -577,7 +677,7 @@ func (f *FTX) DeleteTriggerOrder(orderID string) (string, error) {
Success bool `json:"success"`
}{}
if err := f.SendAuthHTTPRequest(http.MethodDelete, cancelTriggerOrder+orderID, nil, &resp); err != nil {
if err := f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodDelete, cancelTriggerOrder+orderID, nil, &resp); err != nil {
return "", err
}
if !resp.Success {
@@ -605,7 +705,7 @@ func (f *FTX) GetFills(market, limit string, startTime, endTime time.Time) ([]Fi
params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10))
params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10))
}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getFills+params.Encode(), nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getFills+params.Encode(), nil, &resp)
}
// GetFundingPayments gets funding payments
@@ -624,7 +724,7 @@ func (f *FTX) GetFundingPayments(startTime, endTime time.Time, future string) ([
if future != "" {
params.Set("future", future)
}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getFundingPayments+params.Encode(), nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getFundingPayments+params.Encode(), nil, &resp)
}
// ListLeveragedTokens lists leveraged tokens
@@ -632,7 +732,7 @@ func (f *FTX) ListLeveragedTokens() ([]LeveragedTokensData, error) {
resp := struct {
Data []LeveragedTokensData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getLeveragedTokens, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getLeveragedTokens, nil, &resp)
}
// GetTokenInfo gets token info
@@ -640,7 +740,7 @@ func (f *FTX) GetTokenInfo(tokenName string) ([]LeveragedTokensData, error) {
resp := struct {
Data []LeveragedTokensData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getTokenInfo+tokenName, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getTokenInfo+tokenName, nil, &resp)
}
// ListLTBalances gets leveraged tokens' balances
@@ -648,7 +748,7 @@ func (f *FTX) ListLTBalances() ([]LTBalanceData, error) {
resp := struct {
Data []LTBalanceData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getLTBalances, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getLTBalances, nil, &resp)
}
// ListLTCreations lists the leveraged tokens' creation requests
@@ -656,7 +756,7 @@ func (f *FTX) ListLTCreations() ([]LTCreationData, error) {
resp := struct {
Data []LTCreationData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getLTCreations, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getLTCreations, nil, &resp)
}
// RequestLTCreation sends a request to create a leveraged token
@@ -666,7 +766,7 @@ func (f *FTX) RequestLTCreation(tokenName string, size float64) (RequestTokenCre
resp := struct {
Data RequestTokenCreationData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(requestLTCreation, tokenName), req, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost, fmt.Sprintf(requestLTCreation, tokenName), req, &resp)
}
// ListLTRedemptions lists the leveraged tokens' redemption requests
@@ -674,7 +774,7 @@ func (f *FTX) ListLTRedemptions() ([]LTRedemptionData, error) {
resp := struct {
Data []LTRedemptionData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getLTRedemptions, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getLTRedemptions, nil, &resp)
}
// RequestLTRedemption sends a request to redeem a leveraged token
@@ -684,7 +784,7 @@ func (f *FTX) RequestLTRedemption(tokenName string, size float64) (LTRedemptionR
resp := struct {
Data LTRedemptionRequestData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(requestLTRedemption, tokenName), req, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost, fmt.Sprintf(requestLTRedemption, tokenName), req, &resp)
}
// GetQuoteRequests gets a list of quote requests
@@ -692,7 +792,7 @@ func (f *FTX) GetQuoteRequests() ([]QuoteRequestData, error) {
resp := struct {
Data []QuoteRequestData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getListQuotes, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getListQuotes, nil, &resp)
}
// GetYourQuoteRequests gets a list of your quote requests
@@ -700,7 +800,7 @@ func (f *FTX) GetYourQuoteRequests() ([]PersonalQuotesData, error) {
resp := struct {
Data []PersonalQuotesData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getMyQuotesRequests, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getMyQuotesRequests, nil, &resp)
}
// CreateQuoteRequest sends a request to create a quote
@@ -725,7 +825,7 @@ func (f *FTX) CreateQuoteRequest(underlying, optionType, side string, expiry int
resp := struct {
Data CreateQuoteRequestData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, createQuoteRequest, req, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost, createQuoteRequest, req, &resp)
}
// DeleteQuote sends request to cancel a quote
@@ -733,13 +833,13 @@ func (f *FTX) DeleteQuote(requestID string) (CancelQuoteRequestData, error) {
resp := struct {
Data CancelQuoteRequestData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodDelete, deleteQuote+requestID, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodDelete, deleteQuote+requestID, nil, &resp)
}
// GetQuotesForYourQuote gets a list of quotes for your quote
func (f *FTX) GetQuotesForYourQuote(requestID string) (QuoteForQuoteData, error) {
var resp QuoteForQuoteData
return resp, f.SendAuthHTTPRequest(http.MethodGet, fmt.Sprintf(endpointQuote, requestID), nil, &resp)
return resp, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, fmt.Sprintf(endpointQuote, requestID), nil, &resp)
}
// MakeQuote makes a quote for a quote
@@ -749,7 +849,7 @@ func (f *FTX) MakeQuote(requestID, price string) ([]QuoteForQuoteData, error) {
resp := struct {
Data []QuoteForQuoteData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(endpointQuote, requestID), nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost, fmt.Sprintf(endpointQuote, requestID), nil, &resp)
}
// MyQuotes gets a list of my quotes for quotes
@@ -757,7 +857,7 @@ func (f *FTX) MyQuotes() ([]QuoteForQuoteData, error) {
resp := struct {
Data []QuoteForQuoteData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getMyQuotes, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getMyQuotes, nil, &resp)
}
// DeleteMyQuote deletes my quote for quotes
@@ -765,7 +865,7 @@ func (f *FTX) DeleteMyQuote(quoteID string) ([]QuoteForQuoteData, error) {
resp := struct {
Data []QuoteForQuoteData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodDelete, deleteMyQuote+quoteID, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodDelete, deleteMyQuote+quoteID, nil, &resp)
}
// AcceptQuote accepts the quote for quote
@@ -773,7 +873,7 @@ func (f *FTX) AcceptQuote(quoteID string) ([]QuoteForQuoteData, error) {
resp := struct {
Data []QuoteForQuoteData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(acceptQuote, quoteID), nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost, fmt.Sprintf(acceptQuote, quoteID), nil, &resp)
}
// GetAccountOptionsInfo gets account's options' info
@@ -781,7 +881,7 @@ func (f *FTX) GetAccountOptionsInfo() (AccountOptionsInfoData, error) {
resp := struct {
Data AccountOptionsInfoData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOptionsInfo, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getOptionsInfo, nil, &resp)
}
// GetOptionsPositions gets options' positions
@@ -789,7 +889,7 @@ func (f *FTX) GetOptionsPositions() ([]OptionsPositionsData, error) {
resp := struct {
Data []OptionsPositionsData `json:"result"`
}{}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOptionsPositions, nil, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getOptionsPositions, nil, &resp)
}
// GetPublicOptionsTrades gets options' trades from public
@@ -808,7 +908,7 @@ func (f *FTX) GetPublicOptionsTrades(startTime, endTime time.Time, limit string)
if limit != "" {
req["limit"] = limit
}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getPublicOptionsTrades, req, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getPublicOptionsTrades, req, &resp)
}
// GetOptionsFills gets fills data for options
@@ -827,15 +927,18 @@ func (f *FTX) GetOptionsFills(startTime, endTime time.Time, limit string) ([]Opt
if limit != "" {
req["limit"] = limit
}
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOptionsFills, req, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getOptionsFills, req, &resp)
}
// SendAuthHTTPRequest sends an authenticated request
func (f *FTX) SendAuthHTTPRequest(method, path string, data, result interface{}) error {
func (f *FTX) SendAuthHTTPRequest(ep exchange.URL, method, path string, data, result interface{}) error {
endpoint, err := f.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
ts := strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
var body io.Reader
var hmac, payload []byte
var err error
if data != nil {
payload, err = json.Marshal(data)
if err != nil {
@@ -855,7 +958,7 @@ func (f *FTX) SendAuthHTTPRequest(method, path string, data, result interface{})
headers["Content-Type"] = "application/json"
return f.SendPayload(context.Background(), &request.Item{
Method: method,
Path: ftxAPIURL + path,
Path: endpoint + path,
Headers: headers,
Body: body,
Result: result,
@@ -947,7 +1050,7 @@ func (f *FTX) RequestForQuotes(base, quote string, amount float64) (RequestQuote
req["fromCoin"] = base
req["toCoin"] = quote
req["size"] = amount
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, requestOTCQuote, req, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost, requestOTCQuote, req, &resp)
}
// GetOTCQuoteStatus gets quote status of a quote
@@ -957,10 +1060,10 @@ func (f *FTX) GetOTCQuoteStatus(marketName, quoteID string) ([]QuoteStatusData,
}{}
params := url.Values{}
params.Set("market", marketName)
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOTCQuoteStatus+quoteID, params, &resp)
return resp.Data, f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodGet, getOTCQuoteStatus+quoteID, params, &resp)
}
// AcceptOTCQuote requests for otc quotes
func (f *FTX) AcceptOTCQuote(quoteID string) error {
return f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(acceptOTCQuote, quoteID), nil, nil)
return f.SendAuthHTTPRequest(exchange.RestSpot, http.MethodPost, fmt.Sprintf(acceptOTCQuote, quoteID), nil, nil)
}

View File

@@ -147,7 +147,7 @@ func TestGetFutureStats(t *testing.T) {
func TestGetFundingRates(t *testing.T) {
t.Parallel()
_, err := f.GetFundingRates()
_, err := f.GetFundingRates(time.Now().Add(-time.Hour), time.Now(), "BTC-PERP")
if err != nil {
t.Error(err)
}
@@ -175,6 +175,28 @@ func TestGetPositions(t *testing.T) {
}
}
func TestGetBalances(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip()
}
_, err := f.GetBalances()
if err != nil {
t.Error(err)
}
}
func TestGetAllWalletBalances(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip()
}
_, err := f.GetAllWalletBalances()
if err != nil {
t.Error(err)
}
}
func TestChangeAccountLeverage(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() || !canManipulateRealOrders {
@@ -197,23 +219,100 @@ func TestGetCoins(t *testing.T) {
}
}
func TestGetBalances(t *testing.T) {
func TestGetMarginBorrowRates(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip()
}
_, err := f.GetBalances()
_, err := f.GetMarginBorrowRates()
if err != nil {
t.Error(err)
}
}
func TestGetAllWalletBalances(t *testing.T) {
func TestGetMarginLendingRates(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip()
}
_, err := f.GetAllWalletBalances()
_, err := f.GetMarginLendingRates()
if err != nil {
t.Error(err)
}
}
func TestMarginDailyBorrowedAmounts(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip()
}
_, err := f.MarginDailyBorrowedAmounts()
if err != nil {
t.Error(err)
}
}
func TestGetMarginMarketInfo(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip()
}
_, err := f.GetMarginMarketInfo("BTC_USD")
if err != nil {
t.Error(err)
}
}
func TestGetMarginBorrowHistory(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip()
}
_, err := f.GetMarginBorrowHistory()
if err != nil {
t.Error(err)
}
}
func TestGetMarginLendingHistory(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip()
}
_, err := f.GetMarginLendingHistory()
if err != nil {
t.Error(err)
}
}
func TestGetMarginLendingOffers(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip()
}
_, err := f.GetMarginLendingOffers()
if err != nil {
t.Error(err)
}
}
func TestGetLendingInfo(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip()
}
_, err := f.GetLendingInfo()
if err != nil {
t.Error(err)
}
}
func TestSubmitLendingOffer(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip()
}
_, err := f.SubmitLendingOffer("btc", 0.1, 500)
if err != nil {
t.Error(err)
}
@@ -727,6 +826,7 @@ func TestGetActiveOrders(t *testing.T) {
var orderReq order.GetOrdersRequest
cp := currency.NewPairWithDelimiter(currency.BTC.String(), currency.USDT.String(), "/")
orderReq.Pairs = append(orderReq.Pairs, cp)
orderReq.AssetType = asset.Spot
_, err := f.GetActiveOrders(&orderReq)
if err != nil {
t.Fatal(err)
@@ -741,6 +841,7 @@ func TestGetOrderHistory(t *testing.T) {
var orderReq order.GetOrdersRequest
cp := currency.NewPairWithDelimiter(currency.BTC.String(), currency.USDT.String(), "/")
orderReq.Pairs = append(orderReq.Pairs, cp)
orderReq.AssetType = asset.Spot
_, err := f.GetOrderHistory(&orderReq)
if err != nil {
t.Fatal(err)
@@ -752,7 +853,7 @@ func TestUpdateAccountHoldings(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("API keys required but not set, skipping test")
}
_, err := f.UpdateAccountInfo()
_, err := f.UpdateAccountInfo(asset.Spot)
if err != nil {
t.Error(err)
}
@@ -763,7 +864,7 @@ func TestFetchAccountInfo(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("API keys required but not set, skipping test")
}
_, err := f.FetchAccountInfo()
_, err := f.FetchAccountInfo(asset.Spot)
if err != nil {
t.Error(err)
}

View File

@@ -7,6 +7,47 @@ import (
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
)
// MarginFundingData stores borrowing/lending data for margin trading
type MarginFundingData struct {
Coin string `json:"coin"`
Estimate float64 `json:"estimate"`
Previous float64 `json:"previous"`
}
// MarginMarketInfo stores margin market info
type MarginMarketInfo struct {
Coin string `json:"coin"`
Borrowed float64 `json:"borrowed"`
Free float64 `json:"free"`
EstimatedRate float64 `json:"estimatedRate"`
PreviousRate float64 `json:"previousRate"`
}
// MarginTransactionHistoryData stores margin borrowing/lending history
type MarginTransactionHistoryData struct {
Coin string `json:"coin"`
Cost float64 `json:"cost"`
Rate float64 `json:"rate"`
Size float64 `json:"size"`
Time time.Time `json:"time"`
}
// LendingOffersData stores data for lending offers
type LendingOffersData struct {
Coin string `json:"coin"`
Rate float64 `json:"rate"`
Size float64 `json:"size"`
}
// LendingInfoData stores margin lending info
type LendingInfoData struct {
Coin string `json:"coin"`
Lendable float64 `json:"lendable"`
Locked float64 `json:"locked"`
MinRate float64 `json:"minRate"`
Offered float64 `json:"offered"`
}
// MarketData stores market data
type MarketData struct {
Name string `json:"name"`

View File

@@ -142,9 +142,14 @@ func (f *FTX) SetDefaults() {
f.Requester = request.New(f.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
request.WithLimiter(request.NewBasicRateLimit(ratePeriod, rateLimit)))
f.API.Endpoints.URLDefault = ftxAPIURL
f.API.Endpoints.URL = f.API.Endpoints.URLDefault
f.API.Endpoints = f.NewEndpoints()
err = f.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: ftxAPIURL,
exchange.WebsocketSpot: ftxWSURL,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
f.Websocket = stream.New()
f.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
f.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
@@ -163,6 +168,11 @@ func (f *FTX) Setup(exch *config.ExchangeConfig) error {
return err
}
wsEndpoint, err := f.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
return err
}
err = f.Websocket.Setup(&stream.WebsocketSetup{
Enabled: exch.Features.Enabled.Websocket,
Verbose: exch.Verbose,
@@ -170,7 +180,7 @@ func (f *FTX) Setup(exch *config.ExchangeConfig) error {
WebsocketTimeout: exch.WebsocketTrafficTimeout,
DefaultURL: ftxWSURL,
ExchangeName: exch.Name,
RunningURL: exch.API.Endpoints.WebsocketURL,
RunningURL: wsEndpoint,
Connector: f.WsConnect,
Subscriber: f.Subscribe,
UnSubscriber: f.Unsubscribe,
@@ -365,7 +375,7 @@ func (f *FTX) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook
}
// UpdateAccountInfo retrieves balances for all enabled currencies
func (f *FTX) UpdateAccountInfo() (account.Holdings, error) {
func (f *FTX) UpdateAccountInfo(assetType asset.Item) (account.Holdings, error) {
var resp account.Holdings
data, err := f.GetBalances()
if err != nil {
@@ -393,10 +403,10 @@ func (f *FTX) UpdateAccountInfo() (account.Holdings, error) {
}
// FetchAccountInfo retrieves balances for all enabled currencies
func (f *FTX) FetchAccountInfo() (account.Holdings, error) {
acc, err := account.GetHoldings(f.Name)
func (f *FTX) FetchAccountInfo(assetType asset.Item) (account.Holdings, error) {
acc, err := account.GetHoldings(f.Name, assetType)
if err != nil {
return f.UpdateAccountInfo()
return f.UpdateAccountInfo(assetType)
}
return acc, nil
@@ -651,6 +661,8 @@ func (s *OrderData) GetCompatible(f *FTX) (OrderVars, error) {
resp.Side = order.Buy
case order.Sell.Lower():
resp.Side = order.Sell
default:
resp.Side = order.UnknownSide
}
switch s.Status {
case strings.ToLower(order.New.String()):
@@ -667,6 +679,8 @@ func (s *OrderData) GetCompatible(f *FTX) (OrderVars, error) {
if s.FilledSize == s.Size {
resp.Status = order.Filled
}
default:
resp.Status = order.AnyStatus
}
var feeBuilder exchange.FeeBuilder
feeBuilder.PurchasePrice = s.AvgFillPrice
@@ -991,8 +1005,8 @@ func (f *FTX) AuthenticateWebsocket() error {
// ValidateCredentials validates current credentials used for wrapper
// functionality
func (f *FTX) ValidateCredentials() error {
_, err := f.UpdateAccountInfo()
func (f *FTX) ValidateCredentials(assetType asset.Item) error {
_, err := f.UpdateAccountInfo(assetType)
return f.CheckTransientError(err)
}

View File

@@ -50,8 +50,8 @@ type Gateio struct {
// GetSymbols returns all supported symbols
func (g *Gateio) GetSymbols() ([]string, error) {
var result []string
urlPath := fmt.Sprintf("%s/%s/%s", g.API.Endpoints.URLSecondary, gateioAPIVersion, gateioSymbol)
err := g.SendHTTPRequest(urlPath, &result)
urlPath := fmt.Sprintf("/%s/%s", gateioAPIVersion, gateioSymbol)
err := g.SendHTTPRequest(exchange.RestSpotSupplementary, urlPath, &result)
if err != nil {
return nil, nil
}
@@ -66,10 +66,10 @@ func (g *Gateio) GetMarketInfo() (MarketInfoResponse, error) {
Pairs []interface{} `json:"pairs"`
}
urlPath := fmt.Sprintf("%s/%s/%s", g.API.Endpoints.URLSecondary, gateioAPIVersion, gateioMarketInfo)
urlPath := fmt.Sprintf("/%s/%s", gateioAPIVersion, gateioMarketInfo)
var res response
var result MarketInfoResponse
err := g.SendHTTPRequest(urlPath, &res)
err := g.SendHTTPRequest(exchange.RestSpotSupplementary, urlPath, &res)
if err != nil {
return result, err
}
@@ -106,16 +106,16 @@ func (g *Gateio) GetLatestSpotPrice(symbol string) (float64, error) {
// GetTicker returns a ticker for the supplied symbol
// updated every 10 seconds
func (g *Gateio) GetTicker(symbol string) (TickerResponse, error) {
urlPath := fmt.Sprintf("%s/%s/%s/%s", g.API.Endpoints.URLSecondary, gateioAPIVersion, gateioTicker, symbol)
urlPath := fmt.Sprintf("/%s/%s/%s", gateioAPIVersion, gateioTicker, symbol)
var res TickerResponse
return res, g.SendHTTPRequest(urlPath, &res)
return res, g.SendHTTPRequest(exchange.RestSpotSupplementary, urlPath, &res)
}
// GetTickers returns tickers for all symbols
func (g *Gateio) GetTickers() (map[string]TickerResponse, error) {
urlPath := fmt.Sprintf("%s/%s/%s", g.API.Endpoints.URLSecondary, gateioAPIVersion, gateioTickers)
urlPath := fmt.Sprintf("/%s/%s", gateioAPIVersion, gateioTickers)
resp := make(map[string]TickerResponse)
err := g.SendHTTPRequest(urlPath, &resp)
err := g.SendHTTPRequest(exchange.RestSpotSupplementary, urlPath, &resp)
if err != nil {
return nil, err
}
@@ -124,9 +124,9 @@ func (g *Gateio) GetTickers() (map[string]TickerResponse, error) {
// GetTrades returns trades for symbols
func (g *Gateio) GetTrades(symbol string) (TradeHistory, error) {
urlPath := fmt.Sprintf("%s/%s/%s/%s", g.API.Endpoints.URLSecondary, gateioAPIVersion, gateioTrades, symbol)
urlPath := fmt.Sprintf("/%s/%s/%s", gateioAPIVersion, gateioTrades, symbol)
var resp TradeHistory
err := g.SendHTTPRequest(urlPath, &resp)
err := g.SendHTTPRequest(exchange.RestSpotSupplementary, urlPath, &resp)
if err != nil {
return TradeHistory{}, err
}
@@ -135,9 +135,9 @@ func (g *Gateio) GetTrades(symbol string) (TradeHistory, error) {
// GetOrderbook returns the orderbook data for a suppled symbol
func (g *Gateio) GetOrderbook(symbol string) (Orderbook, error) {
urlPath := fmt.Sprintf("%s/%s/%s/%s", g.API.Endpoints.URLSecondary, gateioAPIVersion, gateioOrderbook, symbol)
urlPath := fmt.Sprintf("/%s/%s/%s", gateioAPIVersion, gateioOrderbook, symbol)
var resp OrderbookResponse
err := g.SendHTTPRequest(urlPath, &resp)
err := g.SendHTTPRequest(exchange.RestSpotSupplementary, urlPath, &resp)
if err != nil {
return Orderbook{}, err
}
@@ -196,8 +196,7 @@ func (g *Gateio) GetOrderbook(symbol string) (Orderbook, error) {
// GetSpotKline returns kline data for the most recent time period
func (g *Gateio) GetSpotKline(arg KlinesRequestParams) (kline.Item, error) {
urlPath := fmt.Sprintf("%s/%s/%s/%s?group_sec=%s&range_hour=%d",
g.API.Endpoints.URLSecondary,
urlPath := fmt.Sprintf("/%s/%s/%s?group_sec=%s&range_hour=%d",
gateioAPIVersion,
gateioKline,
arg.Symbol,
@@ -205,7 +204,7 @@ func (g *Gateio) GetSpotKline(arg KlinesRequestParams) (kline.Item, error) {
arg.HourSize)
var rawKlines map[string]interface{}
err := g.SendHTTPRequest(urlPath, &rawKlines)
err := g.SendHTTPRequest(exchange.RestSpotSupplementary, urlPath, &rawKlines)
if err != nil {
return kline.Item{}, err
}
@@ -270,7 +269,7 @@ func (g *Gateio) GetBalances() (BalancesResponse, error) {
var result BalancesResponse
return result,
g.SendAuthenticatedHTTPRequest(http.MethodPost, gateioBalances, "", &result)
g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, gateioBalances, "", &result)
}
// SpotNewOrder places a new order
@@ -285,7 +284,7 @@ func (g *Gateio) SpotNewOrder(arg SpotNewOrderRequestParams) (SpotNewOrderRespon
)
urlPath := fmt.Sprintf("%s/%s", gateioOrder, arg.Type)
return result, g.SendAuthenticatedHTTPRequest(http.MethodPost, urlPath, params, &result)
return result, g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, urlPath, params, &result)
}
// CancelExistingOrder cancels an order given the supplied orderID and symbol
@@ -304,7 +303,7 @@ func (g *Gateio) CancelExistingOrder(orderID int64, symbol string) (bool, error)
orderID,
symbol,
)
err := g.SendAuthenticatedHTTPRequest(http.MethodPost, gateioCancelOrder, params, &result)
err := g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, gateioCancelOrder, params, &result)
if err != nil {
return false, err
}
@@ -316,10 +315,14 @@ func (g *Gateio) CancelExistingOrder(orderID int64, symbol string) (bool, error)
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (g *Gateio) SendHTTPRequest(path string, result interface{}) error {
func (g *Gateio) SendHTTPRequest(ep exchange.URL, path string, result interface{}) error {
endpoint, err := g.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
return g.SendPayload(context.Background(), &request.Item{
Method: http.MethodGet,
Path: path,
Path: endpoint + path,
Result: result,
Verbose: g.Verbose,
HTTPDebugging: g.HTTPDebugging,
@@ -341,7 +344,7 @@ func (g *Gateio) CancelAllExistingOrders(orderType int64, symbol string) error {
orderType,
symbol,
)
err := g.SendAuthenticatedHTTPRequest(http.MethodPost, gateioCancelAllOrders, params, &result)
err := g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, gateioCancelAllOrders, params, &result)
if err != nil {
return err
}
@@ -362,7 +365,7 @@ func (g *Gateio) GetOpenOrders(symbol string) (OpenOrdersResponse, error) {
params = fmt.Sprintf("currencyPair=%s", symbol)
}
err := g.SendAuthenticatedHTTPRequest(http.MethodPost, gateioOpenOrders, params, &result)
err := g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, gateioOpenOrders, params, &result)
if err != nil {
return result, err
}
@@ -380,7 +383,7 @@ func (g *Gateio) GetTradeHistory(symbol string) (TradHistoryResponse, error) {
var result TradHistoryResponse
params = fmt.Sprintf("currencyPair=%s", symbol)
err := g.SendAuthenticatedHTTPRequest(http.MethodPost, gateioTradeHistory, params, &result)
err := g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, gateioTradeHistory, params, &result)
if err != nil {
return result, err
}
@@ -400,12 +403,15 @@ func (g *Gateio) GenerateSignature(message string) []byte {
// SendAuthenticatedHTTPRequest sends authenticated requests to the Gateio API
// To use this you must setup an APIKey and APISecret from the exchange
func (g *Gateio) SendAuthenticatedHTTPRequest(method, endpoint, param string, result interface{}) error {
func (g *Gateio) SendAuthenticatedHTTPRequest(ep exchange.URL, method, endpoint, param string, result interface{}) error {
if !g.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet,
g.Name)
}
ePoint, err := g.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
headers := make(map[string]string)
headers["Content-Type"] = "application/x-www-form-urlencoded"
headers["key"] = g.API.Credentials.Key
@@ -413,10 +419,10 @@ func (g *Gateio) SendAuthenticatedHTTPRequest(method, endpoint, param string, re
hmac := g.GenerateSignature(param)
headers["sign"] = crypto.HexEncodeToString(hmac)
urlPath := fmt.Sprintf("%s/%s/%s", g.API.Endpoints.URL, gateioAPIVersion, endpoint)
urlPath := fmt.Sprintf("%s/%s/%s", ePoint, gateioAPIVersion, endpoint)
var intermidiary json.RawMessage
err := g.SendPayload(context.Background(), &request.Item{
err = g.SendPayload(context.Background(), &request.Item{
Method: method,
Path: urlPath,
Headers: headers,
@@ -518,7 +524,7 @@ func (g *Gateio) WithdrawCrypto(currency, address string, amount float64) (*with
address,
amount,
)
err := g.SendAuthenticatedHTTPRequest(http.MethodPost, gateioWithdraw, params, &result)
err := g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, gateioWithdraw, params, &result)
if err != nil {
return nil, err
}
@@ -544,7 +550,7 @@ func (g *Gateio) GetCryptoDepositAddress(currency string) (string, error) {
params := fmt.Sprintf("currency=%s",
currency)
err := g.SendAuthenticatedHTTPRequest(http.MethodPost, gateioDepositAddress, params, &result)
err := g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, gateioDepositAddress, params, &result)
if err != nil {
return "", err
}

View File

@@ -285,7 +285,8 @@ func TestFormatWithdrawPermissions(t *testing.T) {
func TestGetActiveOrders(t *testing.T) {
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := g.GetActiveOrders(&getOrdersRequest)
@@ -298,7 +299,8 @@ func TestGetActiveOrders(t *testing.T) {
func TestGetOrderHistory(t *testing.T) {
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
currPair := currency.NewPair(currency.LTC, currency.BTC)
@@ -398,12 +400,12 @@ func TestCancelAllExchangeOrders(t *testing.T) {
func TestGetAccountInfo(t *testing.T) {
if apiSecret == "" || apiKey == "" {
_, err := g.UpdateAccountInfo()
_, err := g.UpdateAccountInfo(asset.Spot)
if err == nil {
t.Error("GetAccountInfo() Expected error")
}
} else {
_, err := g.UpdateAccountInfo()
_, err := g.UpdateAccountInfo(asset.Spot)
if err != nil {
t.Error("GetAccountInfo() error", err)
}

View File

@@ -127,12 +127,15 @@ func (g *Gateio) SetDefaults() {
}
g.Requester = request.New(g.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
g.API.Endpoints.URLDefault = gateioTradeURL
g.API.Endpoints.URL = g.API.Endpoints.URLDefault
g.API.Endpoints.URLSecondaryDefault = gateioMarketURL
g.API.Endpoints.URLSecondary = g.API.Endpoints.URLSecondaryDefault
g.API.Endpoints.WebsocketURL = gateioWebsocketEndpoint
g.API.Endpoints = g.NewEndpoints()
err = g.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: gateioTradeURL,
exchange.RestSpotSupplementary: gateioMarketURL,
exchange.WebsocketSpot: gateioWebsocketEndpoint,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
g.Websocket = stream.New()
g.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
g.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
@@ -151,6 +154,11 @@ func (g *Gateio) Setup(exch *config.ExchangeConfig) error {
return err
}
wsRunningURL, err := g.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
return err
}
err = g.Websocket.Setup(&stream.WebsocketSetup{
Enabled: exch.Features.Enabled.Websocket,
Verbose: exch.Verbose,
@@ -158,7 +166,7 @@ func (g *Gateio) Setup(exch *config.ExchangeConfig) error {
WebsocketTimeout: exch.WebsocketTrafficTimeout,
DefaultURL: gateioWebsocketEndpoint,
ExchangeName: exch.Name,
RunningURL: exch.API.Endpoints.WebsocketURL,
RunningURL: wsRunningURL,
Connector: g.WsConnect,
Subscriber: g.Subscribe,
GenerateSubscriptions: g.GenerateDefaultSubscriptions,
@@ -316,7 +324,7 @@ func (g *Gateio) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderb
// UpdateAccountInfo retrieves balances for all enabled currencies for the
// ZB exchange
func (g *Gateio) UpdateAccountInfo() (account.Holdings, error) {
func (g *Gateio) UpdateAccountInfo(assetType asset.Item) (account.Holdings, error) {
var info account.Holdings
var balances []account.Balance
@@ -401,10 +409,10 @@ func (g *Gateio) UpdateAccountInfo() (account.Holdings, error) {
}
// FetchAccountInfo retrieves balances for all enabled currencies
func (g *Gateio) FetchAccountInfo() (account.Holdings, error) {
acc, err := account.GetHoldings(g.Name)
func (g *Gateio) FetchAccountInfo(assetType asset.Item) (account.Holdings, error) {
acc, err := account.GetHoldings(g.Name, assetType)
if err != nil {
return g.UpdateAccountInfo()
return g.UpdateAccountInfo(assetType)
}
return acc, nil
@@ -802,8 +810,8 @@ func (g *Gateio) AuthenticateWebsocket() error {
// ValidateCredentials validates current credentials used for wrapper
// functionality
func (g *Gateio) ValidateCredentials() error {
_, err := g.UpdateAccountInfo()
func (g *Gateio) ValidateCredentials(assetType asset.Item) error {
_, err := g.UpdateAccountInfo(assetType)
return g.CheckTransientError(err)
}

View File

@@ -67,15 +67,15 @@ type Gemini struct {
// GetSymbols returns all available symbols for trading
func (g *Gemini) GetSymbols() ([]string, error) {
var symbols []string
path := fmt.Sprintf("%s/v%s/%s", g.API.Endpoints.URL, geminiAPIVersion, geminiSymbols)
return symbols, g.SendHTTPRequest(path, &symbols)
path := fmt.Sprintf("/v%s/%s", geminiAPIVersion, geminiSymbols)
return symbols, g.SendHTTPRequest(exchange.RestSpot, path, &symbols)
}
// GetTicker returns information about recent trading activity for the symbol
func (g *Gemini) GetTicker(currencyPair string) (TickerV2, error) {
ticker := TickerV2{}
path := fmt.Sprintf("%s/v2/ticker/%s", g.API.Endpoints.URL, currencyPair)
err := g.SendHTTPRequest(path, &ticker)
path := fmt.Sprintf("/v2/ticker/%s", currencyPair)
err := g.SendHTTPRequest(exchange.RestSpot, path, &ticker)
if err != nil {
return ticker, err
}
@@ -96,15 +96,14 @@ func (g *Gemini) GetTicker(currencyPair string) (TickerV2, error) {
// Type is an integer ie "params.Set("limit_asks", 30)"
func (g *Gemini) GetOrderbook(currencyPair string, params url.Values) (Orderbook, error) {
path := common.EncodeURLValues(
fmt.Sprintf("%s/v%s/%s/%s",
g.API.Endpoints.URL,
fmt.Sprintf("/v%s/%s/%s",
geminiAPIVersion,
geminiOrderbook,
currencyPair),
params)
var orderbook Orderbook
return orderbook, g.SendHTTPRequest(path, &orderbook)
return orderbook, g.SendHTTPRequest(exchange.RestSpot, path, &orderbook)
}
// GetTrades return the trades that have executed since the specified timestamp.
@@ -127,18 +126,18 @@ func (g *Gemini) GetTrades(currencyPair string, since, limit int64, includeBreak
if includeBreaks {
params.Add("include_breaks", strconv.FormatBool(true))
}
path := common.EncodeURLValues(fmt.Sprintf("%s/v%s/%s/%s", g.API.Endpoints.URL, geminiAPIVersion, geminiTrades, currencyPair), params)
path := common.EncodeURLValues(fmt.Sprintf("/v%s/%s/%s", geminiAPIVersion, geminiTrades, currencyPair), params)
var trades []Trade
return trades, g.SendHTTPRequest(path, &trades)
return trades, g.SendHTTPRequest(exchange.RestSpot, path, &trades)
}
// GetAuction returns auction information
func (g *Gemini) GetAuction(currencyPair string) (Auction, error) {
path := fmt.Sprintf("%s/v%s/%s/%s", g.API.Endpoints.URL, geminiAPIVersion, geminiAuction, currencyPair)
path := fmt.Sprintf("/v%s/%s/%s", geminiAPIVersion, geminiAuction, currencyPair)
auction := Auction{}
return auction, g.SendHTTPRequest(path, &auction)
return auction, g.SendHTTPRequest(exchange.RestSpot, path, &auction)
}
// GetAuctionHistory returns the auction events, optionally including
@@ -153,9 +152,9 @@ func (g *Gemini) GetAuction(currencyPair string) (Auction, error) {
// include_indicative - [bool] Whether to include publication of
// indicative prices and quantities.
func (g *Gemini) GetAuctionHistory(currencyPair string, params url.Values) ([]AuctionHistory, error) {
path := common.EncodeURLValues(fmt.Sprintf("%s/v%s/%s/%s/%s", g.API.Endpoints.URL, geminiAPIVersion, geminiAuction, currencyPair, geminiAuctionHistory), params)
path := common.EncodeURLValues(fmt.Sprintf("/v%s/%s/%s/%s", geminiAPIVersion, geminiAuction, currencyPair, geminiAuctionHistory), params)
var auctionHist []AuctionHistory
return auctionHist, g.SendHTTPRequest(path, &auctionHist)
return auctionHist, g.SendHTTPRequest(exchange.RestSpot, path, &auctionHist)
}
// NewOrder Only limit orders are supported through the API at present.
@@ -169,7 +168,7 @@ func (g *Gemini) NewOrder(symbol string, amount, price float64, side, orderType
req["type"] = orderType
response := Order{}
err := g.SendAuthenticatedHTTPRequest(http.MethodPost, geminiOrderNew, req, &response)
err := g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, geminiOrderNew, req, &response)
if err != nil {
return 0, err
}
@@ -183,7 +182,7 @@ func (g *Gemini) CancelExistingOrder(orderID int64) (Order, error) {
req["order_id"] = orderID
response := Order{}
err := g.SendAuthenticatedHTTPRequest(http.MethodPost, geminiOrderCancel, req, &response)
err := g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, geminiOrderCancel, req, &response)
if err != nil {
return Order{}, err
}
@@ -205,7 +204,7 @@ func (g *Gemini) CancelExistingOrders(cancelBySession bool) (OrderResult, error)
}
var response OrderResult
err := g.SendAuthenticatedHTTPRequest(http.MethodPost, path, nil, &response)
err := g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, path, nil, &response)
if err != nil {
return response, err
}
@@ -222,7 +221,7 @@ func (g *Gemini) GetOrderStatus(orderID int64) (Order, error) {
response := Order{}
err := g.SendAuthenticatedHTTPRequest(http.MethodPost, geminiOrderStatus, req, &response)
err := g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, geminiOrderStatus, req, &response)
if err != nil {
return response, err
}
@@ -241,7 +240,7 @@ func (g *Gemini) GetOrders() ([]Order, error) {
orders []Order
}
err := g.SendAuthenticatedHTTPRequest(http.MethodPost, geminiOrders, nil, &response)
err := g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, geminiOrders, nil, &response)
if err != nil {
return nil, err
}
@@ -268,7 +267,7 @@ func (g *Gemini) GetTradeHistory(currencyPair string, timestamp int64) ([]TradeH
}
return response,
g.SendAuthenticatedHTTPRequest(http.MethodPost, geminiMyTrades, req, &response)
g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, geminiMyTrades, req, &response)
}
// GetNotionalVolume returns the volume in price currency that has been traded across all pairs over a period of 30 days
@@ -276,7 +275,7 @@ func (g *Gemini) GetNotionalVolume() (NotionalVolume, error) {
response := NotionalVolume{}
return response,
g.SendAuthenticatedHTTPRequest(http.MethodPost, geminiVolume, nil, &response)
g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, geminiVolume, nil, &response)
}
// GetTradeVolume returns a multi-arrayed volume response
@@ -284,7 +283,7 @@ func (g *Gemini) GetTradeVolume() ([][]TradeVolume, error) {
var response [][]TradeVolume
return response,
g.SendAuthenticatedHTTPRequest(http.MethodPost, geminiTradeVolume, nil, &response)
g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, geminiTradeVolume, nil, &response)
}
// GetBalances returns available balances in the supported currencies
@@ -292,7 +291,7 @@ func (g *Gemini) GetBalances() ([]Balance, error) {
var response []Balance
return response,
g.SendAuthenticatedHTTPRequest(http.MethodPost, geminiBalances, nil, &response)
g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, geminiBalances, nil, &response)
}
// GetCryptoDepositAddress returns a deposit address
@@ -304,7 +303,7 @@ func (g *Gemini) GetCryptoDepositAddress(depositAddlabel, currency string) (Depo
req["label"] = depositAddlabel
}
err := g.SendAuthenticatedHTTPRequest(http.MethodPost, geminiDeposit+"/"+currency+"/"+geminiNewAddress, req, &response)
err := g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, geminiDeposit+"/"+currency+"/"+geminiNewAddress, req, &response)
if err != nil {
return response, err
}
@@ -321,7 +320,7 @@ func (g *Gemini) WithdrawCrypto(address, currency string, amount float64) (Withd
req["address"] = address
req["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
err := g.SendAuthenticatedHTTPRequest(http.MethodPost, geminiWithdraw+strings.ToLower(currency), req, &response)
err := g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, geminiWithdraw+strings.ToLower(currency), req, &response)
if err != nil {
return response, err
}
@@ -340,7 +339,7 @@ func (g *Gemini) PostHeartbeat() (string, error) {
}
response := Response{}
err := g.SendAuthenticatedHTTPRequest(http.MethodPost, geminiHeartbeat, nil, &response)
err := g.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, geminiHeartbeat, nil, &response)
if err != nil {
return response.Result, err
}
@@ -351,10 +350,14 @@ func (g *Gemini) PostHeartbeat() (string, error) {
}
// SendHTTPRequest sends an unauthenticated request
func (g *Gemini) SendHTTPRequest(path string, result interface{}) error {
func (g *Gemini) SendHTTPRequest(ep exchange.URL, path string, result interface{}) error {
endpoint, err := g.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
return g.SendPayload(context.Background(), &request.Item{
Method: http.MethodGet,
Path: path,
Path: endpoint + path,
Result: result,
Verbose: g.Verbose,
HTTPDebugging: g.HTTPDebugging,
@@ -364,11 +367,16 @@ func (g *Gemini) SendHTTPRequest(path string, result interface{}) error {
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to the
// exchange and returns an error
func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[string]interface{}, result interface{}) (err error) {
func (g *Gemini) SendAuthenticatedHTTPRequest(ep exchange.URL, method, path string, params map[string]interface{}, result interface{}) (err error) {
if !g.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, g.Name)
}
endpoint, err := g.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
req := make(map[string]interface{})
req["request"] = fmt.Sprintf("/v%s/%s", geminiAPIVersion, path)
req["nonce"] = g.Requester.GetNonce(true).String()
@@ -399,7 +407,7 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st
return g.SendPayload(context.Background(), &request.Item{
Method: method,
Path: g.API.Endpoints.URL + "/v1/" + path,
Path: endpoint + "/v1/" + path,
Headers: headers,
Result: result,
AuthRequest: true,

View File

@@ -10,6 +10,7 @@ import (
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
@@ -34,7 +35,10 @@ func TestMain(m *testing.M) {
if err != nil {
log.Fatal("Gemini setup error", err)
}
g.API.Endpoints.URL = geminiSandboxAPIURL
log.Printf(sharedtestvalues.LiveTesting, g.Name, g.API.Endpoints.URL)
err = g.API.Endpoints.SetRunning(exchange.RestSpot.String(), geminiSandboxAPIURL)
if err != nil {
log.Fatalf("endpoint setting failed. key: %s, val: %s", exchange.RestSpot.String(), geminiSandboxAPIURL)
}
log.Printf(sharedtestvalues.LiveTesting, g.Name)
os.Exit(m.Run())
}

View File

@@ -45,7 +45,13 @@ func TestMain(m *testing.M) {
}
g.HTTPClient = newClient
g.API.Endpoints.URL = serverDetails
log.Printf(sharedtestvalues.MockTesting, g.Name, g.API.Endpoints.URL)
endpointMap := g.API.Endpoints.GetURLMap()
for k := range endpointMap {
err = g.API.Endpoints.SetRunning(k, serverDetails)
if err != nil {
log.Fatal(err)
}
}
log.Printf(sharedtestvalues.MockTesting, g.Name)
os.Exit(m.Run())
}

View File

@@ -354,6 +354,7 @@ func TestGetActiveOrders(t *testing.T) {
Pairs: []currency.Pair{
currency.NewPair(currency.LTC, currency.BTC),
},
AssetType: asset.Spot,
}
_, err := g.GetActiveOrders(&getOrdersRequest)
@@ -370,8 +371,9 @@ func TestGetActiveOrders(t *testing.T) {
func TestGetOrderHistory(t *testing.T) {
t.Parallel()
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Pairs: []currency.Pair{currency.NewPair(currency.LTC, currency.BTC)},
Type: order.AnyType,
Pairs: []currency.Pair{currency.NewPair(currency.LTC, currency.BTC)},
AssetType: asset.Spot,
}
_, err := g.GetOrderHistory(&getOrdersRequest)
@@ -550,8 +552,10 @@ func TestGetDepositAddress(t *testing.T) {
// TestWsAuth dials websocket, sends login request.
func TestWsAuth(t *testing.T) {
t.Parallel()
g.API.Endpoints.WebsocketURL = geminiWebsocketSandboxEndpoint
err := g.API.Endpoints.SetRunning(exchange.WebsocketSpot.String(), geminiWebsocketSandboxEndpoint)
if err != nil {
t.Error(err)
}
if !g.Websocket.IsEnabled() &&
!g.API.AuthenticatedWebsocketSupport ||
!areTestAPIKeysSet() {
@@ -559,7 +563,7 @@ func TestWsAuth(t *testing.T) {
}
var dialer websocket.Dialer
go g.wsReadData()
err := g.WsSecureSubscribe(&dialer, geminiWsOrderEvents)
err = g.WsSecureSubscribe(&dialer, geminiWsOrderEvents)
if err != nil {
t.Error(err)
}
@@ -1133,8 +1137,8 @@ func TestGetHistoricTrades(t *testing.T) {
tStart := time.Date(2020, 6, 6, 0, 0, 0, 0, time.UTC)
tEnd := time.Date(2020, 6, 7, 0, 0, 0, 0, time.UTC)
if !mockTests {
tStart = time.Date(2020, time.Now().Month(), 6, 0, 0, 0, 0, time.UTC)
tEnd = time.Date(2020, time.Now().Month(), 7, 0, 0, 0, 0, time.UTC)
tStart = time.Date(time.Now().Year(), time.Now().Month(), 6, 0, 0, 0, 0, time.UTC)
tEnd = time.Date(time.Now().Year(), time.Now().Month(), 7, 0, 0, 0, 0, time.UTC)
}
_, err = g.GetHistoricTrades(currencyPair, asset.Spot, tStart, tEnd)
if err != nil {

View File

@@ -72,8 +72,12 @@ func (g *Gemini) WsSubscribe(dialer *websocket.Dialer) error {
val.Set("bids", "true")
val.Set("offers", "true")
val.Set("trades", "true")
wsEndpoint, err := g.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
return err
}
endpoint := fmt.Sprintf("%s%s/%s?%s",
g.API.Endpoints.WebsocketURL,
wsEndpoint,
geminiWsMarketData,
enabledCurrencies[i].String(),
val.Encode())
@@ -85,7 +89,7 @@ func (g *Gemini) WsSubscribe(dialer *websocket.Dialer) error {
Traffic: g.Websocket.TrafficAlert,
Match: g.Websocket.Match,
}
err := connection.Dial(dialer, http.Header{})
err = connection.Dial(dialer, http.Header{})
if err != nil {
return fmt.Errorf("%v Websocket connection %v error. Error %v",
g.Name, endpoint, err)
@@ -109,8 +113,11 @@ func (g *Gemini) WsSecureSubscribe(dialer *websocket.Dialer, url string) error {
if err != nil {
return fmt.Errorf("%v sendAuthenticatedHTTPRequest: Unable to JSON request", g.Name)
}
endpoint := g.API.Endpoints.WebsocketURL + url
wsEndpoint, err := g.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
return err
}
endpoint := wsEndpoint + url
PayloadBase64 := crypto.Base64Encode(PayloadJSON)
hmac := crypto.GetHMAC(crypto.HashSHA512_384, []byte(PayloadBase64), []byte(g.API.Credentials.Secret))
headers := http.Header{}

View File

@@ -106,10 +106,14 @@ func (g *Gemini) SetDefaults() {
g.Requester = request.New(g.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
request.WithLimiter(SetRateLimit()))
g.API.Endpoints.URLDefault = geminiAPIURL
g.API.Endpoints.URL = g.API.Endpoints.URLDefault
g.API.Endpoints.WebsocketURL = geminiWebsocketEndpoint
g.API.Endpoints = g.NewEndpoints()
err = g.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: geminiAPIURL,
exchange.WebsocketSpot: geminiWebsocketEndpoint,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
g.Websocket = stream.New()
g.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
g.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
@@ -129,7 +133,15 @@ func (g *Gemini) Setup(exch *config.ExchangeConfig) error {
}
if exch.UseSandbox {
g.API.Endpoints.URL = geminiSandboxAPIURL
err = g.API.Endpoints.SetRunning(exchange.RestSpot.String(), geminiSandboxAPIURL)
if err != nil {
log.Error(log.ExchangeSys, err)
}
}
wsRunningURL, err := g.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
return err
}
return g.Websocket.Setup(&stream.WebsocketSetup{
@@ -139,7 +151,7 @@ func (g *Gemini) Setup(exch *config.ExchangeConfig) error {
WebsocketTimeout: exch.WebsocketTrafficTimeout,
DefaultURL: geminiWebsocketEndpoint,
ExchangeName: exch.Name,
RunningURL: exch.API.Endpoints.WebsocketURL,
RunningURL: wsRunningURL,
Connector: g.WsConnect,
Features: &g.Features.Supports.WebsocketCapabilities,
OrderbookBufferLimit: exch.OrderbookConfig.WebsocketBufferLimit,
@@ -196,7 +208,7 @@ func (g *Gemini) UpdateTradablePairs(forceUpdate bool) error {
// UpdateAccountInfo Retrieves balances for all enabled currencies for the
// Gemini exchange
func (g *Gemini) UpdateAccountInfo() (account.Holdings, error) {
func (g *Gemini) UpdateAccountInfo(assetType asset.Item) (account.Holdings, error) {
var response account.Holdings
response.Exchange = g.Name
accountBalance, err := g.GetBalances()
@@ -226,10 +238,10 @@ func (g *Gemini) UpdateAccountInfo() (account.Holdings, error) {
}
// FetchAccountInfo retrieves balances for all enabled currencies
func (g *Gemini) FetchAccountInfo() (account.Holdings, error) {
acc, err := account.GetHoldings(g.Name)
func (g *Gemini) FetchAccountInfo(assetType asset.Item) (account.Holdings, error) {
acc, err := account.GetHoldings(g.Name, assetType)
if err != nil {
return g.UpdateAccountInfo()
return g.UpdateAccountInfo(assetType)
}
return acc, nil
@@ -655,8 +667,8 @@ func (g *Gemini) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, e
// ValidateCredentials validates current credentials used for wrapper
// functionality
func (g *Gemini) ValidateCredentials() error {
_, err := g.UpdateAccountInfo()
func (g *Gemini) ValidateCredentials(assetType asset.Item) error {
_, err := g.UpdateAccountInfo(assetType)
return g.CheckTransientError(err)
}

View File

@@ -58,10 +58,10 @@ func (h *HitBTC) GetCurrencies() (map[string]Currencies, error) {
Data []Currencies
}
resp := Response{}
path := fmt.Sprintf("%s/%s", h.API.Endpoints.URL, apiV2Currency)
path := fmt.Sprintf("/%s", apiV2Currency)
ret := make(map[string]Currencies)
err := h.SendHTTPRequest(path, &resp.Data)
err := h.SendHTTPRequest(exchange.RestSpot, path, &resp.Data)
if err != nil {
return ret, err
}
@@ -79,9 +79,9 @@ func (h *HitBTC) GetCurrency(currency string) (Currencies, error) {
Data Currencies
}
resp := Response{}
path := fmt.Sprintf("%s/%s/%s", h.API.Endpoints.URL, apiV2Currency, currency)
path := fmt.Sprintf("/%s/%s", apiV2Currency, currency)
return resp.Data, h.SendHTTPRequest(path, &resp.Data)
return resp.Data, h.SendHTTPRequest(exchange.RestSpot, path, &resp.Data)
}
// GetSymbols Return the actual list of currency symbols (currency pairs) traded
@@ -91,10 +91,10 @@ func (h *HitBTC) GetCurrency(currency string) (Currencies, error) {
// of the base currency.
func (h *HitBTC) GetSymbols(symbol string) ([]string, error) {
var resp []Symbol
path := fmt.Sprintf("%s/%s/%s", h.API.Endpoints.URL, apiV2Symbol, symbol)
path := fmt.Sprintf("/%s/%s", apiV2Symbol, symbol)
ret := make([]string, 0, len(resp))
err := h.SendHTTPRequest(path, &resp)
err := h.SendHTTPRequest(exchange.RestSpot, path, &resp)
if err != nil {
return ret, err
}
@@ -109,22 +109,22 @@ func (h *HitBTC) GetSymbols(symbol string) ([]string, error) {
// all their details.
func (h *HitBTC) GetSymbolsDetailed() ([]Symbol, error) {
var resp []Symbol
path := fmt.Sprintf("%s/%s", h.API.Endpoints.URL, apiV2Symbol)
return resp, h.SendHTTPRequest(path, &resp)
path := fmt.Sprintf("/%s", apiV2Symbol)
return resp, h.SendHTTPRequest(exchange.RestSpot, path, &resp)
}
// GetTicker returns ticker information
func (h *HitBTC) GetTicker(symbol string) (TickerResponse, error) {
var resp TickerResponse
path := fmt.Sprintf("%s/%s/%s", h.API.Endpoints.URL, apiV2Ticker, symbol)
return resp, h.SendHTTPRequest(path, &resp)
path := fmt.Sprintf("/%s/%s", apiV2Ticker, symbol)
return resp, h.SendHTTPRequest(exchange.RestSpot, path, &resp)
}
// GetTickers returns ticker information
func (h *HitBTC) GetTickers() ([]TickerResponse, error) {
var resp []TickerResponse
path := fmt.Sprintf("%s/%s/", h.API.Endpoints.URL, apiV2Ticker)
return resp, h.SendHTTPRequest(path, &resp)
path := fmt.Sprintf("/%s/", apiV2Ticker)
return resp, h.SendHTTPRequest(exchange.RestSpot, path, &resp)
}
// GetTrades returns trades from hitbtc
@@ -150,12 +150,11 @@ func (h *HitBTC) GetTrades(currencyPair, by, sort string, from, till, limit, off
}
var resp []TradeHistory
path := fmt.Sprintf("%s/%s/%s?%s",
h.API.Endpoints.URL,
path := fmt.Sprintf("/%s/%s?%s",
apiV2Trades,
currencyPair,
urlValues.Encode())
return resp, h.SendHTTPRequest(path, &resp)
return resp, h.SendHTTPRequest(exchange.RestSpot, path, &resp)
}
// GetOrderbook an order book is an electronic list of buy and sell orders for a
@@ -169,13 +168,12 @@ func (h *HitBTC) GetOrderbook(currencyPair string, limit int) (Orderbook, error)
}
resp := OrderbookResponse{}
path := fmt.Sprintf("%s/%s/%s?%s",
h.API.Endpoints.URL,
path := fmt.Sprintf("/%s/%s?%s",
apiV2Orderbook,
currencyPair,
vals.Encode())
err := h.SendHTTPRequest(path, &resp)
err := h.SendHTTPRequest(exchange.RestSpot, path, &resp)
if err != nil {
return Orderbook{}, err
}
@@ -213,8 +211,8 @@ func (h *HitBTC) GetCandles(currencyPair, limit, period string, start, end time.
}
var resp []ChartData
path := fmt.Sprintf("%s/%s/%s?%s", h.API.Endpoints.URL, apiV2Candles, currencyPair, vals.Encode())
return resp, h.SendHTTPRequest(path, &resp)
path := fmt.Sprintf("/%s/%s?%s", apiV2Candles, currencyPair, vals.Encode())
return resp, h.SendHTTPRequest(exchange.RestSpot, path, &resp)
}
// Authenticated Market Data
@@ -223,7 +221,7 @@ func (h *HitBTC) GetCandles(currencyPair, limit, period string, start, end time.
// GetBalances returns full balance for your account
func (h *HitBTC) GetBalances() (map[string]Balance, error) {
var result []Balance
err := h.SendAuthenticatedHTTPRequest(http.MethodGet,
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
apiV2Balance,
url.Values{},
otherRequests,
@@ -246,7 +244,7 @@ func (h *HitBTC) GetDepositAddresses(currency string) (DepositCryptoAddresses, e
var resp DepositCryptoAddresses
return resp,
h.SendAuthenticatedHTTPRequest(http.MethodGet,
h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
apiV2CryptoAddress+"/"+currency,
url.Values{},
otherRequests,
@@ -256,7 +254,7 @@ func (h *HitBTC) GetDepositAddresses(currency string) (DepositCryptoAddresses, e
// GenerateNewAddress generates a new deposit address for a currency
func (h *HitBTC) GenerateNewAddress(currency string) (DepositCryptoAddresses, error) {
resp := DepositCryptoAddresses{}
err := h.SendAuthenticatedHTTPRequest(http.MethodPost,
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
apiV2CryptoAddress+"/"+currency,
url.Values{},
otherRequests,
@@ -268,7 +266,7 @@ func (h *HitBTC) GenerateNewAddress(currency string) (DepositCryptoAddresses, er
// GetActiveorders returns all your active orders
func (h *HitBTC) GetActiveorders(currency string) ([]Order, error) {
var resp []Order
err := h.SendAuthenticatedHTTPRequest(http.MethodGet,
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
orders+"?symbol="+currency,
url.Values{},
tradingRequests,
@@ -292,7 +290,7 @@ func (h *HitBTC) GetTradeHistoryForCurrency(currency, start, end string) (Authen
values.Set("currencyPair", currency)
result := AuthenticatedTradeHistoryResponse{}
return result, h.SendAuthenticatedHTTPRequest(http.MethodPost,
return result, h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
apiV2TradeHistory,
values,
otherRequests,
@@ -314,7 +312,7 @@ func (h *HitBTC) GetTradeHistoryForAllCurrencies(start, end string) (Authenticat
values.Set("currencyPair", "all")
result := AuthenticatedTradeHistoryAll{}
return result, h.SendAuthenticatedHTTPRequest(http.MethodPost,
return result, h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
apiV2TradeHistory,
values,
otherRequests,
@@ -327,7 +325,7 @@ func (h *HitBTC) GetOrders(currency string) ([]OrderHistoryResponse, error) {
values.Set("symbol", currency)
var result []OrderHistoryResponse
return result, h.SendAuthenticatedHTTPRequest(http.MethodGet,
return result, h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
apiV2OrderHistory,
values,
tradingRequests,
@@ -340,7 +338,7 @@ func (h *HitBTC) GetOpenOrders(currency string) ([]OrderHistoryResponse, error)
values.Set("symbol", currency)
var result []OrderHistoryResponse
return result, h.SendAuthenticatedHTTPRequest(http.MethodGet,
return result, h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
apiv2OpenOrders,
values,
tradingRequests,
@@ -359,7 +357,7 @@ func (h *HitBTC) PlaceOrder(currency string, rate, amount float64, orderType, si
values.Set("price", strconv.FormatFloat(rate, 'f', -1, 64))
values.Set("type", orderType)
return result, h.SendAuthenticatedHTTPRequest(http.MethodPost,
return result, h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
apiOrder,
values,
tradingRequests,
@@ -371,7 +369,7 @@ func (h *HitBTC) CancelExistingOrder(orderID int64) (bool, error) {
result := GenericResponse{}
values := url.Values{}
err := h.SendAuthenticatedHTTPRequest(http.MethodDelete,
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodDelete,
apiOrder+"/"+strconv.FormatInt(orderID, 10),
values,
tradingRequests,
@@ -392,7 +390,7 @@ func (h *HitBTC) CancelExistingOrder(orderID int64) (bool, error) {
func (h *HitBTC) CancelAllExistingOrders() ([]Order, error) {
var result []Order
values := url.Values{}
return result, h.SendAuthenticatedHTTPRequest(http.MethodDelete,
return result, h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodDelete,
apiOrder,
values,
tradingRequests,
@@ -410,7 +408,7 @@ func (h *HitBTC) MoveOrder(orderID int64, rate, amount float64) (MoveOrderRespon
values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
}
err := h.SendAuthenticatedHTTPRequest(http.MethodPost,
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
orderMove,
values,
tradingRequests,
@@ -436,7 +434,7 @@ func (h *HitBTC) Withdraw(currency, address string, amount float64) (bool, error
values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
values.Set("address", address)
err := h.SendAuthenticatedHTTPRequest(http.MethodPost,
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
apiV2CryptoWithdraw,
values,
otherRequests,
@@ -456,7 +454,7 @@ func (h *HitBTC) Withdraw(currency, address string, amount float64) (bool, error
// GetFeeInfo returns current fee information
func (h *HitBTC) GetFeeInfo(currencyPair string) (Fee, error) {
result := Fee{}
err := h.SendAuthenticatedHTTPRequest(http.MethodGet,
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
apiV2FeeInfo+"/"+currencyPair,
url.Values{},
tradingRequests,
@@ -472,7 +470,7 @@ func (h *HitBTC) GetTradableBalances() (map[string]map[string]float64, error) {
}
result := Response{}
err := h.SendAuthenticatedHTTPRequest(http.MethodPost,
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
tradableBalances,
url.Values{},
tradingRequests,
@@ -504,7 +502,7 @@ func (h *HitBTC) TransferBalance(currency, from, to string, amount float64) (boo
values.Set("fromAccount", from)
values.Set("toAccount", to)
err := h.SendAuthenticatedHTTPRequest(http.MethodPost,
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost,
transferBalance,
values,
otherRequests,
@@ -522,10 +520,14 @@ func (h *HitBTC) TransferBalance(currency, from, to string, amount float64) (boo
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (h *HitBTC) SendHTTPRequest(path string, result interface{}) error {
func (h *HitBTC) SendHTTPRequest(ep exchange.URL, path string, result interface{}) error {
endpoint, err := h.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
return h.SendPayload(context.Background(), &request.Item{
Method: http.MethodGet,
Path: path,
Path: endpoint + path,
Result: result,
Verbose: h.Verbose,
HTTPDebugging: h.HTTPDebugging,
@@ -535,15 +537,19 @@ func (h *HitBTC) SendHTTPRequest(path string, result interface{}) error {
}
// SendAuthenticatedHTTPRequest sends an authenticated http request
func (h *HitBTC) SendAuthenticatedHTTPRequest(method, endpoint string, values url.Values, f request.EndpointLimit, result interface{}) error {
func (h *HitBTC) SendAuthenticatedHTTPRequest(ep exchange.URL, method, endpoint string, values url.Values, f request.EndpointLimit, result interface{}) error {
if !h.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet,
h.Name)
}
ePoint, err := h.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
headers := make(map[string]string)
headers["Authorization"] = "Basic " + crypto.Base64Encode([]byte(h.API.Credentials.Key+":"+h.API.Credentials.Secret))
path := fmt.Sprintf("%s/%s", h.API.Endpoints.URL, endpoint)
path := fmt.Sprintf("%s/%s", ePoint, endpoint)
return h.SendPayload(context.Background(), &request.Item{
Method: method,

View File

@@ -265,8 +265,9 @@ func TestFormatWithdrawPermissions(t *testing.T) {
func TestGetActiveOrders(t *testing.T) {
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Pairs: []currency.Pair{currency.NewPair(currency.ETH, currency.BTC)},
Type: order.AnyType,
Pairs: []currency.Pair{currency.NewPair(currency.ETH, currency.BTC)},
AssetType: asset.Spot,
}
_, err := h.GetActiveOrders(&getOrdersRequest)
@@ -279,8 +280,9 @@ func TestGetActiveOrders(t *testing.T) {
func TestGetOrderHistory(t *testing.T) {
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Pairs: []currency.Pair{currency.NewPair(currency.ETH, currency.BTC)},
Type: order.AnyType,
AssetType: asset.Spot,
Pairs: []currency.Pair{currency.NewPair(currency.ETH, currency.BTC)},
}
_, err := h.GetOrderHistory(&getOrdersRequest)

View File

@@ -130,10 +130,14 @@ func (h *HitBTC) SetDefaults() {
h.Requester = request.New(h.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
request.WithLimiter(SetRateLimit()))
h.API.Endpoints.URLDefault = apiURL
h.API.Endpoints.URL = h.API.Endpoints.URLDefault
h.API.Endpoints.WebsocketURL = hitbtcWebsocketAddress
h.API.Endpoints = h.NewEndpoints()
err = h.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: apiURL,
exchange.WebsocketSpot: hitbtcWebsocketAddress,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
h.Websocket = stream.New()
h.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
h.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
@@ -152,6 +156,11 @@ func (h *HitBTC) Setup(exch *config.ExchangeConfig) error {
return err
}
wsRunningURL, err := h.API.Endpoints.GetURL(exchange.WebsocketSpot)
if err != nil {
return err
}
err = h.Websocket.Setup(&stream.WebsocketSetup{
Enabled: exch.Features.Enabled.Websocket,
Verbose: exch.Verbose,
@@ -159,7 +168,7 @@ func (h *HitBTC) Setup(exch *config.ExchangeConfig) error {
WebsocketTimeout: exch.WebsocketTrafficTimeout,
DefaultURL: hitbtcWebsocketAddress,
ExchangeName: exch.Name,
RunningURL: exch.API.Endpoints.WebsocketURL,
RunningURL: wsRunningURL,
Connector: h.WsConnect,
Subscriber: h.Subscribe,
UnSubscriber: h.Unsubscribe,
@@ -404,7 +413,7 @@ func (h *HitBTC) UpdateOrderbook(c currency.Pair, assetType asset.Item) (*orderb
// UpdateAccountInfo retrieves balances for all enabled currencies for the
// HitBTC exchange
func (h *HitBTC) UpdateAccountInfo() (account.Holdings, error) {
func (h *HitBTC) UpdateAccountInfo(assetType asset.Item) (account.Holdings, error) {
var response account.Holdings
response.Exchange = h.Name
accountBalance, err := h.GetBalances()
@@ -434,10 +443,10 @@ func (h *HitBTC) UpdateAccountInfo() (account.Holdings, error) {
}
// FetchAccountInfo retrieves balances for all enabled currencies
func (h *HitBTC) FetchAccountInfo() (account.Holdings, error) {
acc, err := account.GetHoldings(h.Name)
func (h *HitBTC) FetchAccountInfo(assetType asset.Item) (account.Holdings, error) {
acc, err := account.GetHoldings(h.Name, assetType)
if err != nil {
return h.UpdateAccountInfo()
return h.UpdateAccountInfo(assetType)
}
return acc, nil
@@ -772,8 +781,8 @@ func (h *HitBTC) AuthenticateWebsocket() error {
// ValidateCredentials validates current credentials used for wrapper
// functionality
func (h *HitBTC) ValidateCredentials() error {
_, err := h.UpdateAccountInfo()
func (h *HitBTC) ValidateCredentials(assetType asset.Item) error {
_, err := h.UpdateAccountInfo(assetType)
return h.CheckTransientError(err)
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,845 @@
package huobi
// FContractInfoData gets contract info data for futures
type FContractInfoData struct {
Data []struct {
Symbol string `json:"symbol"`
ContractCode string `json:"contract_code"`
ContractType string `json:"contract_type"`
ContractSize float64 `json:"contract_size"`
PriceTick float64 `json:"price_tick"`
DeliveryDate string `json:"delivery_date"`
CreateDate string `json:"create_date"`
ContractStatus int64 `json:"contract_status"`
}
}
// FContractIndexPriceInfo stores contract index price
type FContractIndexPriceInfo struct {
Data []struct {
Symbol string `json:"symbol"`
IndexPrice float64 `json:"index_price"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FContractPriceLimits gets limits for futures contracts
type FContractPriceLimits struct {
Data struct {
Symbol string `json:"symbol"`
HighLimit float64 `json:"high_limit"`
LowLimit float64 `json:"low_limit"`
ContractCode string `json:"contract_code"`
ContractType string `json:"contract_type"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FContractOIData stores open interest data for futures contracts
type FContractOIData struct {
Data []struct {
Symbol string `json:"symbol"`
ContractType string `json:"contract_type"`
Volume float64 `json:"volume"`
Amount float64 `json:"amount"`
ContractCode string `json:"contract_code"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FEstimatedDeliveryPriceInfo stores estimated delivery price data for futures
type FEstimatedDeliveryPriceInfo struct {
Data struct {
DeliveryPrice float64 `json:"delivery_price"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FMarketDepth gets orderbook data for futures
type FMarketDepth struct {
Ch string `json:"ch"`
Timestamp int64 `json:"ts"`
Tick struct {
MRID int64 `json:"mrid"`
ID int64 `json:"id"`
Bids [][2]float64 `json:"bids"`
Asks [][2]float64 `json:"asks"`
Timestamp int64 `json:"ts"`
Version int64 `json:"version"`
Ch string `json:"ch"`
} `json:"tick"`
}
// OBData stores market depth data
type OBData struct {
Symbol string
Asks []obItem
Bids []obItem
}
type obItem struct {
Price float64
Quantity float64
}
// FKlineData stores kline data for futures
type FKlineData struct {
Ch string `json:"ch"`
Data []struct {
Vol float64 `json:"vol"`
Close float64 `json:"close"`
Count float64 `json:"count"`
High float64 `json:"high"`
ID int64 `json:"id"`
Low float64 `json:"low"`
Open float64 `json:"open"`
Amount float64 `json:"amount"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FMarketOverviewData stores overview data for futures
type FMarketOverviewData struct {
Ch string `json:"ch"`
Tick struct {
Vol float64 `json:"vol,string"`
Ask [2]float64
Bid [2]float64
Close float64 `json:"close,string"`
Count float64 `json:"count"`
High float64 `json:"high,string"`
ID int64 `jso:"id"`
Low float64 `json:"low,string"`
Open float64 `json:"open,string"`
Timestamp int64 `json:"ts"`
Amount float64 `json:"amount,string"`
} `json:"tick"`
Timestamp int64 `json:"ts"`
}
// FLastTradeData stores last trade's data for a contract
type FLastTradeData struct {
Ch string `json:"ch"`
Tick struct {
Data []struct {
Amount float64 `json:"amount,string"`
Direction string `json:"direction"`
ID int64 `json:"id"`
Price float64 `json:"price,string"`
Timestamp int64 `json:"ts"`
} `json:"data"`
ID int64 `json:"id"`
Timestamp int64 `json:"ts"`
} `json:"tick"`
Timestamp int64 `json:"ts"`
}
// FBatchTradesForContractData stores batch of trades data for a contract
type FBatchTradesForContractData struct {
Ch string `json:"ch"`
Timestamp int64 `json:"ts"`
Data []struct {
ID int64 `json:"id"`
Timestamp int64 `json:"ts"`
Data []struct {
Amount float64 `json:"amount"`
Direction string `json:"direction"`
ID int64 `json:"id"`
Price float64 `json:"price"`
Timestamp int64 `json:"ts"`
} `json:"data"`
} `json:"data"`
}
// FClawbackRateAndInsuranceData stores clawback rate and insurance data for futures
type FClawbackRateAndInsuranceData struct {
Timestamp int64 `json:"ts"`
Data []struct {
Symbol string `json:"symbol"`
InsuranceFund float64 `json:"insurance_fund"`
EstimatedClawback float64 `json:"estimated_clawback"`
} `json:"data"`
}
// FHistoricalInsuranceRecordsData stores historical records of insurance fund balances for futures
type FHistoricalInsuranceRecordsData struct {
Timestamp int64 `json:"timestamp"`
Data struct {
Symbol string `json:"symbol"`
Tick []struct {
InsuranceFund float64 `json:"insurance_fund"`
Timestamp int64 `json:"ts"`
} `json:"tick"`
} `json:"data"`
}
// FTieredAdjustmentFactorInfo stores info on adjustment factor for futures
type FTieredAdjustmentFactorInfo struct {
Data []struct {
Symbol string `json:"symbol"`
List []struct {
LeverageRate float64 `json:"lever_rate"`
Ladders []struct {
Ladder int64 `json:"ladder"`
MinSize float64 `json:"min_size"`
MaxSize float64 `json:"max_size"`
AdjustFactor float64 `json:"adjust_factor"`
} `json:"ladders"`
} `json:"list"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FOIData gets oi data on futures
type FOIData struct {
Data struct {
Symbol string `json:"symbol"`
ContractType string `json:"contract_type"`
Tick []struct {
Volume float64 `json:"volume,string"`
AmountType int64 `json:"amount_type"`
Timestamp int64 `json:"ts"`
} `json:"tick"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FInfoSystemStatusData stores system status info for futures
type FInfoSystemStatusData struct {
Data []struct {
Symbol string `json:"symbol"`
Open int64 `json:"open"`
Close int64 `json:"close"`
Cancel int64 `json:"cancel"`
TransferIn int64 `json:"transfer_in"`
TransferOut int64 `json:"transfer_out"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FTopAccountsLongShortRatio stores long/short ratio for top futures accounts
type FTopAccountsLongShortRatio struct {
Data struct {
List []struct {
BuyRatio float64 `json:"buy_ratio"`
SellRatio float64 `json:"sell_ratio"`
LockedRatio float64 `json:"locked_ratio"`
Timestamp int64 `json:"ts"`
} `json:"list"`
Symbol string `json:"symbol"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FTopPositionsLongShortRatio stores long short ratio for top futures positions
type FTopPositionsLongShortRatio struct {
Data struct {
Symbol string `json:"symbol"`
List []struct {
BuyRatio float64 `json:"buy_ratio"`
SellRatio float64 `json:"sell_ratio"`
Timestamp int64 `json:"timestamp"`
} `json:"list"`
} `json:"data"`
Timestamp int64 `json:"timestamp"`
}
// FLiquidationOrdersInfo stores data of futures liquidation orders
type FLiquidationOrdersInfo struct {
Data struct {
Orders []struct {
Symbol string `json:"symbol"`
ContractCode string `json:"contract_code"`
Direction string `json:"direction"`
Offset string `json:"offset"`
Volume float64 `json:"volume"`
Price float64 `json:"price"`
CreatedAt int64 `json:"created_at"`
} `json:"orders"`
TotalPage int64 `json:"total_page"`
CurrentPage int64 `json:"current_page"`
TotalSize int64 `json:"total_size"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FIndexKlineData stores index kline data for futures
type FIndexKlineData struct {
Ch string `json:"ch"`
Data []struct {
Vol float64 `json:"vol"`
Close float64 `json:"close"`
Count float64 `json:"count"`
High float64 `json:"high"`
ID int64 `json:"id"`
Low float64 `json:"low"`
Open float64 `json:"open"`
Amount float64 `json:"amount"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FBasisData stores basis data for futures
type FBasisData struct {
Ch string `json:"ch"`
Data []struct {
Basis float64 `json:"basis,string"`
BasisRate float64 `json:"basis_rate,string"`
ContractPrice float64 `json:"contract_price,string"`
ID int64 `json:"id"`
IndexPrice float64 `json:"index_price,string"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FUserAccountData stores user account data info for futures
type FUserAccountData struct {
AccData []struct {
Symbol string `json:"symbol"`
MarginBalance float64 `json:"margin_balance"`
MarginPosition float64 `json:"margin_position"`
MarginFrozen float64 `json:"margin_frozen"`
MarginAvailable float64 `json:"margin_available"`
ProfitReal float64 `json:"profit_real"`
ProfitUnreal float64 `json:"profit_unreal"`
RiskRate float64 `json:"risk_rate"`
LiquidationPrice float64 `json:"liquidation_price"`
WithdrawAvailable float64 `json:"withdraw_available"`
LeverageRate float64 `json:"lever_rate"`
AdjustFactor float64 `json:"adjust_factor"`
MarginStatic float64 `json:"margin_static"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FUsersPositionsInfo stores positions data for futures
type FUsersPositionsInfo struct {
PosInfo []struct {
Symbol string `json:"symbol"`
ContractCode string `json:"contract_code"`
ContractType string `json:"contract_type"`
Volume float64 `json:"volume"`
Available float64 `json:"available"`
Frozen float64 `json:"frozen"`
CostOpen float64 `json:"cost_open"`
CostHold float64 `json:"cost_hold"`
ProfitUnreal float64 `json:"profit_unreal"`
ProfitRate float64 `json:"profit_rate"`
Profit float64 `json:"profit"`
PositionMargin float64 `json:"position_margin"`
LeverageRate float64 `json:"lever_rate"`
Direction string `json:"direction"`
LastPrice float64 `json:"last_price"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FSubAccountAssetsInfo gets subaccounts asset data
type FSubAccountAssetsInfo struct {
Timestamp int64 `json:"ts"`
Data []struct {
SubUID int64 `json:"sub_uid"`
List []struct {
Symbol string `json:"symbol"`
MarginBalance float64 `json:"margin_balance"`
LiquidationPrice float64 `json:"liquidation_price"`
RiskRate float64 `json:"risk_rate"`
} `json:"list"`
} `json:"data"`
}
// FSingleSubAccountAssetsInfo stores futures assets info for a single subaccount
type FSingleSubAccountAssetsInfo struct {
AssetsData []struct {
Symbol string `json:"symbol"`
MarginBalance float64 `json:"margin_balance"`
MarginPosition float64 `json:"margin_position"`
MarginFrozen float64 `json:"margin_frozen"`
MarginAvailable float64 `json:"margin_available"`
ProfitReal float64 `json:"profit_real"`
ProfitUnreal float64 `json:"profit_unreal"`
WithdrawAvailable float64 `json:"withdraw_available"`
RiskRate float64 `json:"risk_rate"`
LiquidationPrice float64 `json:"liquidation_price"`
AdjustFactor float64 `json:"adjust_factor"`
LeverageRate float64 `json:"lever_rate"`
MarginStatic float64 `json:"margin_static"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FSingleSubAccountPositionsInfo stores futures positions' info for a single subaccount
type FSingleSubAccountPositionsInfo struct {
PositionsData []struct {
Symbol string `json:"symbol"`
ContractCode string `json:"contract_code"`
ContractType string `json:"contract_type"`
Volume float64 `json:"volume"`
Available float64 `json:"available"`
Frozen float64 `json:"frozen"`
CostOpen float64 `json:"cost_open"`
CostHold float64 `json:"cost_hold"`
ProfitUnreal float64 `json:"profit_unreal"`
ProfitRate float64 `json:"profit_rate"`
Profit float64 `json:"profit"`
PositionMargin float64 `json:"position_margin"`
LeverageRate float64 `json:"lever_rate"`
Direction string `json:"direction"`
LastPrice float64 `json:"last_price"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FFinancialRecords stores financial records data for futures
type FFinancialRecords struct {
Data struct {
FinancialRecord []struct {
ID int64 `json:"id"`
Timestamp int64 `json:"ts"`
Symbol string `json:"symbol"`
RecordType int64 `json:"type"`
Amount float64 `json:"amount"`
} `json:"financial_record"`
TotalPage int64 `json:"total_page"`
CurrentPage int64 `json:"current_page"`
TotalSize int64 `json:"total_size"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FSettlementRecords stores user's futures settlement records
type FSettlementRecords struct {
Data struct {
SettlementRecords []struct {
Symbol string `json:"symbol"`
MarginBalanceInit float64 `json:"margin_balance_init"`
MarginBalance int64 `json:"margin_balance"`
SettlementProfitReal float64 `json:"settlement_profit_real"`
SettlementTime int64 `json:"settlement_time"`
Clawback float64 `json:"clawback"`
DeliveryFee float64 `json:"delivery_fee"`
OffsetProfitLoss float64 `json:"offset_profitloss"`
Fee float64 `json:"fee"`
FeeAsset string `json:"fee_asset"`
Positions []struct {
Symbol string `json:"symbol"`
ContractCode string `json:"contract_code"`
Direction string `json:"direction"`
Volume float64 `json:"volume"`
CostOpen float64 `json:"cost_open"`
CostHoldPre float64 `json:"cost_hold_pre"`
CostHold float64 `json:"cost_hold"`
SettlementProfitUnreal float64 `json:"settlement_profit_unreal"`
SettlementPrice float64 `json:"settlement_price"`
SettlmentType string `json:"settlement_type"`
} `json:"positions"`
} `json:"settlement_records"`
CurrentPage int64 `json:"current_page"`
TotalPage int64 `json:"total_page"`
TotalSize int64 `json:"total_size"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FContractInfoOnOrderLimit stores contract info on futures order limit
type FContractInfoOnOrderLimit struct {
ContractData []struct {
OrderPriceType string `json:"order_price_type"`
List []struct {
Symbol string `json:"symbol"`
ContractTypes []struct {
ContractType string `json:"contract_type"`
OpenLimit int64 `json:"open_limit"`
CloseLimit int64 `json:"close_limit"`
} `json:"types"`
} `json:"list"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FContractTradingFeeData stores contract trading fee data
type FContractTradingFeeData struct {
ContractTradingFeeData []struct {
Symbol string `json:"symbol"`
OpenMakerFee float64 `json:"open_maker_fee,string"`
OpenTakerFee float64 `json:"open_taker_fee,string"`
CloseMakerFee float64 `json:"close_maker_fee,string"`
CloseTakerFee float64 `json:"close_taker_fee,string"`
DeliveryFee float64 `json:"delivery_fee,string"`
FeeAsset string `json:"fee_asset"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FTransferLimitData stores transfer limit data for futures
type FTransferLimitData struct {
Data []struct {
Symbol string `json:"symbol"`
MaxTransferIn float64 `json:"transfer_in_max_each"`
MinTransferIn float64 `json:"transfer_in_min_each"`
MaxTransferOut float64 `json:"transfer_out_max_each"`
MinTransferOut float64 `json:"transfer_out_min_each"`
MaxTransferInDaily float64 `json:"transfer_in_max_daily"`
MaxTransferOutDaily float64 `json:"transfer_out_max_daily"`
NetTransferInMaxDaily float64 `json:"net_transfer_in_max_daily"`
NetTransferOutMaxDaily float64 `json:"net_transfer_out_max_daily"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FPositionLimitData stores information on futures positions limit
type FPositionLimitData struct {
Data []struct {
Symbol string `json:"symbol"`
List []struct {
ContractType string `json:"contract_type"`
BuyLimit float64 `json:"buy_limit"`
SellLimit float64 `json:"sell_limit"`
} `json:"list"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FAssetsAndPositionsData stores assets and positions data for futures
type FAssetsAndPositionsData struct {
Data []struct {
Symbol string `json:"symbol"`
MarginBalance float64 `json:"margin_balance"`
MarginPosition float64 `json:"margin_position"`
MarginFrozen float64 `json:"margin_frozen"`
MarginAvailable float64 `json:"margin_available"`
ProfitReal float64 `json:"profit_real"`
ProfitUnreal float64 `json:"profit_unreal"`
RiskRate float64 `json:"risk_rate"`
WithdrawAvailable float64 `json:"withdraw_available"`
} `json:"data"`
}
// FAccountTransferData stores internal transfer data for futures
type FAccountTransferData struct {
Status string `json:"status"`
Timestamp int64 `json:"ts"`
Data struct {
OrderID string `json:"order_id"`
} `json:"data"`
}
// FTransferRecords gets transfer records data
type FTransferRecords struct {
Timestamp int64 `json:"ts"`
Data struct {
TransferRecord []struct {
ID int64 `json:"id"`
Timestamp int64 `json:"ts"`
Symbol string `json:"symbol"`
SubUID int64 `json:"sub_uid"`
SubAccountName string `json:"sub_account_name"`
TransferType int64 `json:"transfer_type"`
Amount float64 `json:"amount"`
} `json:"transfer_record"`
TotalPage int64 `json:"total_page"`
CurrentPage int64 `json:"current_page"`
TotalSize int64 `json:"total_size"`
} `json:"data"`
}
// FAvailableLeverageData stores available leverage data for futures
type FAvailableLeverageData struct {
Data []struct {
Symbol string `json:"symbol"`
AvailableLeverageRate string `json:"available_level_rate"`
} `json:"data"`
Timestamp int64 `json:"timestamp"`
}
// FOrderData stores order data for futures
type FOrderData struct {
Data struct {
OrderID int64 `json:"order_id"`
OrderIDStr string `json:"order_id_str"`
ClientOrderID int64 `json:"client_order_id"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
type fBatchOrderData struct {
Symbol string `json:"symbol"`
ContractType string `json:"contract_type"`
ContractCode string `json:"contract_code"`
ClientOrderID string `json:"client_order_id"`
Price float64 `json:"price"`
Volume float64 `json:"volume"`
Direction string `json:"direction"`
Offset string `json:"offset"`
LeverageRate float64 `json:"leverRate"`
OrderPriceType string `json:"orderPriceType"`
}
// FBatchOrderResponse stores batch order data
type FBatchOrderResponse struct {
OrdersData []FOrderData `json:"orders_data"`
}
// FCancelOrderData stores cancel order data
type FCancelOrderData struct {
Data struct {
Errors []struct {
OrderID int64 `json:"order_id,string"`
ErrCode int64 `json:"err_code"`
ErrMsg string `json:"err_msg"`
} `json:"errors"`
Successes string `json:"successes"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FOrderInfo stores order info
type FOrderInfo struct {
Data []struct {
ClientOrderID int64 `json:"client_order_id"`
ContractCode string `json:"contract_code"`
ContractType string `json:"contract_type"`
CreatedAt int64 `json:"created_at"`
CanceledAt int64 `json:"canceled_at"`
Direction string `json:"direction"`
Fee float64 `json:"fee"`
FeeAsset string `json:"fee_asset"`
LeverRate int64 `json:"lever_rate"`
MarginFrozen float64 `json:"margin_frozen"`
Offset string `json:"offset"`
OrderID int64 `json:"order_id"`
OrderIDString string `json:"order_id_string"`
OrderPriceType string `json:"order_price_type"`
OrderSource string `json:"order_source"`
OrderType int64 `json:"order_type"`
Price float64 `json:"price"`
Profit float64 `json:"profit"`
Status int64 `json:"status"`
Symbol string `json:"symbol"`
TradeAvgPrice float64 `json:"trade_avg_price"`
TradeTurnover float64 `json:"trade_turnover"`
TradeVolume float64 `json:"trade_volume"`
Volume float64 `json:"volume"`
LiquidationType int64 `json:"liquidation_type"`
} `json:"data"`
Timestamp int64 `json:"timestamp"`
}
// FOrderDetailsData stores order details for futures orders
type FOrderDetailsData struct {
Data struct {
Symbol string `json:"symbol"`
ContractType string `json:"contract_type"`
ContractCode string `json:"contract_code"`
Volume float64 `json:"volume"`
Price float64 `json:"price"`
OrderPriceType string `json:"order_price_type"`
Direction string `json:"direction"`
Offset string `json:"offset"`
LeverageRate float64 `json:"lever_rate"`
MarginFrozen float64 `json:"margin_frozen"`
Profit float64 `json:"profit"`
OrderSource string `json:"order_source"`
OrderID int64 `json:"order_id"`
OrderIDString string `json:"order_id_str"`
ClientOrderID int64 `json:"client_order_id"`
OrderType int64 `json:"order_type"`
Status int64 `json:"status"`
TradeVolume float64 `json:"trade_volume"`
TradeTurnover int64 `json:"trade_turnover"`
TradeAvgPrice float64 `json:"trade_avg_price"`
Fee float64 `json:"fee"`
CreatedAt int64 `json:"created_at"`
CanceledAt int64 `json:"canceled_at"`
FinalInterest float64 `json:"final_interest"`
AdjustValue int64 `json:"adjust_value"`
FeeAsset string `json:"fee_asset"`
Trades []struct {
ID string `json:"id"`
TradeID int64 `json:"trade_id"`
TradeVolume float64 `json:"trade_volume"`
TradePrice float64 `json:"trade_price"`
TradeFee float64 `json:"trade_fee"`
TradeTurnover float64 `json:"trade_turnover"`
Role string `json:"role"`
CreatedAt int64 `json:"created_at"`
} `json:"trades"`
TotalPage int64 `json:"total_page"`
TotalSize int64 `json:"total_size"`
CurrentPage int64 `json:"current_page"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FOpenOrdersData stores open orders data for futures
type FOpenOrdersData struct {
Data struct {
Orders []struct {
Symbol string `json:"symbol"`
ContractType string `json:"contract_type"`
ContractCode string `json:"contract_code"`
Volume float64 `json:"volume"`
Price float64 `json:"price"`
OrderPriceType string `json:"order_price_type"`
OrderType int64 `json:"order_type"`
Direction string `json:"direction"`
Offset string `json:"offset"`
LeverageRate float64 `json:"lever_rate"`
OrderID int64 `json:"order_id"`
OrderIDString string `json:"order_id_string"`
ClientOrderID int64 `json:"client_order_id"`
OrderSource string `json:"order_source"`
CreatedAt int64 `json:"created_at"`
TradeVolume float64 `json:"trade_volume"`
Fee float64 `json:"fee"`
TradeAvgPrice float64 `json:"trade_avg_price"`
MarginFrozen float64 `json:"margin_frozen"`
Profit float64 `json:"profit"`
Status int64 `json:"status"`
FeeAsset string `json:"fee_asset"`
} `json:"orders"`
TotalPage int64 `json:"total_page"`
CurrentPage int64 `json:"current_page"`
TotalSize int64 `json:"total_size"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FOrderHistoryData stores order history data
type FOrderHistoryData struct {
Data struct {
Orders []struct {
Symbol string `json:"symbol"`
ContractType string `json:"contract_type"`
ContractCode string `json:"contract_code"`
Volume float64 `json:"volume"`
Price float64 `json:"price"`
OrderPriceType string `json:"order_price_type"`
Direction string `json:"direction"`
Offset string `json:"offset"`
LeverageRate float64 `json:"lever_rate"`
OrderID int64 `json:"order_id"`
OrderIDString string `json:"order_id_str"`
OrderSource string `json:"order_source"`
CreateDate int64 `json:"create_date"`
TradeVolume float64 `json:"trade_volume"`
TradeTurnover float64 `json:"trade_turnover"`
Fee float64 `json:"fee"`
TradeAvgPrice float64 `json:"trade_avg_price"`
MarginFrozen float64 `json:"margin_frozen"`
Profit float64 `json:"profit"`
Status int64 `json:"status"`
OrderType int64 `json:"order_type"`
FeeAsset string `json:"fee_asset"`
LiquidationType int64 `json:"liquidation_type"`
} `json:"orders"`
TotalPage int64 `json:"total_page"`
CurrentPage int64 `json:"current_page"`
TotalSize int64 `json:"total_size"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FTradeHistoryData stores trade history data for futures
type FTradeHistoryData struct {
Data struct {
TotalPage int64 `json:"total_page"`
CurrentPage int64 `json:"current_page"`
TotalSize int64 `json:"total_size"`
Trades []struct {
ID string `json:"id"`
ContractCode string `json:"contract_code"`
ContractType string `json:"contract_type"`
CreateDate int64 `json:"create_date"`
Direction string `json:"direction"`
MatchID int64 `json:"match_id"`
Offset string `json:"offset"`
OffsetPNL float64 `json:"offset_profitloss"`
OrderID int64 `json:"order_id"`
OrderIDString string `json:"order_id_str"`
Symbol string `json:"symbol"`
OrderSource string `json:"order_source"`
TradeFee float64 `json:"trade_fee"`
TradePrice float64 `json:"trade_price"`
TradeTurnover float64 `json:"trade_turnover"`
TradeVolume float64 `json:"trade_volume"`
Role string `json:"role"`
FeeAsset string `json:"fee_asset"`
} `json:"trades"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FTriggerOrderData stores trigger order data
type FTriggerOrderData struct {
Data struct {
OrderID int64 `json:"order_id"`
OrderIDStr string `json:"order_id_str"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FTriggerOpenOrders stores trigger open orders data
type FTriggerOpenOrders struct {
Data struct {
Orders []struct {
Symbol string `json:"symbol"`
ContractCode string `json:"contract_code"`
ContractType string `json:"contract_type"`
TriggerType string `json:"trigger_type"`
Volume float64 `json:"volume"`
OrderType int64 `json:"order_type"`
Direction string `json:"direction"`
Offset string `json:"offset"`
LeverageRate float64 `json:"lever_rate"`
OrderID int64 `json:"order_id"`
OrderIDString string `json:"order_id_str"`
OrderSource string `json:"order_source"`
TriggerPrice float64 `json:"trigger_price"`
OrderPrice float64 `json:"order_price"`
CreatedAt int64 `json:"created_at"`
OrderPriceType string `json:"order_price_type"`
Status int64 `json:"status"`
} `json:"orders"`
TotalPage int64 `json:"total_page"`
CurrentPage int64 `json:"current_page"`
TotalSize int64 `json:"total_size"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}
// FTriggerOrderHistoryData stores trigger order history for futures
type FTriggerOrderHistoryData struct {
Data struct {
Orders []struct {
Symbol string `json:"symbol"`
ContractCode string `json:"contract_code"`
ContractType string `json:"contract_type"`
TriggerType string `json:"trigger_type"`
Volume float64 `json:"volume"`
OrderType int64 `json:"order_type"`
Direction string `json:"direction"`
Offset string `json:"offset"`
LeverageRate float64 `json:"lever_rate"`
OrderID int64 `json:"order_id"`
OrderIDString string `json:"order_id_str"`
RelationOrderID string `json:"relation_order_id"`
OrderPriceType string `json:"order_price_type"`
Status string `json:"status"`
OrderSource string `json:"order_source"`
TriggerPrice int64 `json:"trigger_price"`
TriggeredPrice float64 `json:"triggered_price"`
OrderPrice float64 `json:"order_price"`
CreatedAt int64 `json:"created_at"`
TriggeredAt int64 `json:"triggered_at"`
OrderInsertAt float64 `json:"order_insert_at"`
CancelledAt int64 `json:"canceled_at"`
FailCode int64 `json:"fail_code"`
FailReason string `json:"fail_reason"`
} `json:"orders"`
TotalPage int64 `json:"total_page"`
CurrentPage int64 `json:"current_page"`
TotalSize int64 `json:"total_size"`
} `json:"data"`
Timestamp int64 `json:"ts"`
}

View File

@@ -15,17 +15,22 @@ import (
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
)
const (
huobiAPIURL = "https://api.huobi.pro"
huobiURL = "https://api.hbdm.com/"
huobiFuturesURL = "https://api.hbdm.com"
huobiAPIVersion = "1"
huobiAPIVersion2 = "2"
// Spot endpoints
huobiMarketHistoryKline = "market/history/kline"
huobiMarketDetail = "market/detail"
huobiMarketDetailMerged = "market/detail/merged"
huobi24HrMarketSummary = "/market/detail?"
huobiMarketDepth = "market/depth"
huobiMarketTrade = "market/trade"
huobiMarketTickers = "market/tickers"
@@ -56,6 +61,7 @@ const (
huobiWithdrawCreate = "dw/withdraw/api/create"
huobiWithdrawCancel = "dw/withdraw-virtual/%s/cancel"
huobiStatusError = "error"
huobiMarginRates = "margin/loan-info"
)
// HUOBI is the overarching type across this package
@@ -64,11 +70,29 @@ type HUOBI struct {
AccountID string
}
// GetMarginRates gets margin rates
func (h *HUOBI) GetMarginRates(symbol currency.Pair) (MarginRatesData, error) {
var resp MarginRatesData
vals := url.Values{}
if symbol != (currency.Pair{}) {
symbolValue, err := h.FormatSymbol(symbol, asset.Spot)
if err != nil {
return resp, err
}
vals.Set("symbol", symbolValue)
}
return resp, h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, huobiMarginRates, vals, nil, &resp, false)
}
// GetSpotKline returns kline data
// KlinesRequestParams contains symbol, period and size
// KlinesRequestParams contains symbol currency.Pair, period and size
func (h *HUOBI) GetSpotKline(arg KlinesRequestParams) ([]KlineItem, error) {
vals := url.Values{}
vals.Set("symbol", arg.Symbol)
symbolValue, err := h.FormatSymbol(arg.Symbol, asset.Spot)
if err != nil {
return nil, err
}
vals.Set("symbol", symbolValue)
vals.Set("period", arg.Period)
if arg.Size != 0 {
@@ -81,26 +105,40 @@ func (h *HUOBI) GetSpotKline(arg KlinesRequestParams) ([]KlineItem, error) {
}
var result response
urlPath := fmt.Sprintf("%s/%s", h.API.Endpoints.URL, huobiMarketHistoryKline)
err := h.SendHTTPRequest(common.EncodeURLValues(urlPath, vals), &result)
err = h.SendHTTPRequest(exchange.RestSpot, common.EncodeURLValues("/"+huobiMarketHistoryKline, vals), &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
return result.Data, err
}
// Get24HrMarketSummary returns 24hr market summary for a given market symbol
func (h *HUOBI) Get24HrMarketSummary(symbol currency.Pair) (MarketSummary24Hr, error) {
var result MarketSummary24Hr
params := url.Values{}
symbolValue, err := h.FormatSymbol(symbol, asset.Spot)
if err != nil {
return result, err
}
params.Set("symbol", symbolValue)
return result, h.SendHTTPRequest(exchange.RestSpot, huobi24HrMarketSummary+params.Encode(), &result)
}
// GetTickers returns the ticker for the specified symbol
func (h *HUOBI) GetTickers() (Tickers, error) {
var result Tickers
urlPath := fmt.Sprintf("%s/%s", h.API.Endpoints.URL, huobiMarketTickers)
return result, h.SendHTTPRequest(urlPath, &result)
return result, h.SendHTTPRequest(exchange.RestSpot, "/"+huobiMarketTickers, &result)
}
// GetMarketDetailMerged returns the ticker for the specified symbol
func (h *HUOBI) GetMarketDetailMerged(symbol string) (DetailMerged, error) {
func (h *HUOBI) GetMarketDetailMerged(symbol currency.Pair) (DetailMerged, error) {
vals := url.Values{}
vals.Set("symbol", symbol)
symbolValue, err := h.FormatSymbol(symbol, asset.Spot)
if err != nil {
return DetailMerged{}, err
}
vals.Set("symbol", symbolValue)
type response struct {
Response
@@ -108,9 +146,8 @@ func (h *HUOBI) GetMarketDetailMerged(symbol string) (DetailMerged, error) {
}
var result response
urlPath := fmt.Sprintf("%s/%s", h.API.Endpoints.URL, huobiMarketDetailMerged)
err := h.SendHTTPRequest(common.EncodeURLValues(urlPath, vals), &result)
err = h.SendHTTPRequest(exchange.RestSpot, common.EncodeURLValues("/"+huobiMarketDetailMerged, vals), &result)
if result.ErrorMessage != "" {
return result.Tick, errors.New(result.ErrorMessage)
}
@@ -120,7 +157,11 @@ func (h *HUOBI) GetMarketDetailMerged(symbol string) (DetailMerged, error) {
// GetDepth returns the depth for the specified symbol
func (h *HUOBI) GetDepth(obd OrderBookDataRequestParams) (Orderbook, error) {
vals := url.Values{}
vals.Set("symbol", obd.Symbol)
symbolValue, err := h.FormatSymbol(obd.Symbol, asset.Spot)
if err != nil {
return Orderbook{}, err
}
vals.Set("symbol", symbolValue)
if obd.Type != OrderBookDataRequestParamsTypeNone {
vals.Set("type", string(obd.Type))
@@ -132,9 +173,8 @@ func (h *HUOBI) GetDepth(obd OrderBookDataRequestParams) (Orderbook, error) {
}
var result response
urlPath := fmt.Sprintf("%s/%s", h.API.Endpoints.URL, huobiMarketDepth)
err := h.SendHTTPRequest(common.EncodeURLValues(urlPath, vals), &result)
err = h.SendHTTPRequest(exchange.RestSpot, common.EncodeURLValues("/"+huobiMarketDepth, vals), &result)
if result.ErrorMessage != "" {
return result.Depth, errors.New(result.ErrorMessage)
}
@@ -142,9 +182,13 @@ func (h *HUOBI) GetDepth(obd OrderBookDataRequestParams) (Orderbook, error) {
}
// GetTrades returns the trades for the specified symbol
func (h *HUOBI) GetTrades(symbol string) ([]Trade, error) {
func (h *HUOBI) GetTrades(symbol currency.Pair) ([]Trade, error) {
vals := url.Values{}
vals.Set("symbol", symbol)
symbolValue, err := h.FormatSymbol(symbol, asset.Spot)
if err != nil {
return nil, err
}
vals.Set("symbol", symbolValue)
type response struct {
Response
@@ -154,9 +198,8 @@ func (h *HUOBI) GetTrades(symbol string) ([]Trade, error) {
}
var result response
urlPath := fmt.Sprintf("%s/%s", h.API.Endpoints.URL, huobiMarketTrade)
err := h.SendHTTPRequest(common.EncodeURLValues(urlPath, vals), &result)
err = h.SendHTTPRequest(exchange.RestSpot, common.EncodeURLValues("/"+huobiMarketTrade, vals), &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
@@ -166,7 +209,7 @@ func (h *HUOBI) GetTrades(symbol string) ([]Trade, error) {
// GetLatestSpotPrice returns latest spot price of symbol
//
// symbol: string of currency pair
func (h *HUOBI) GetLatestSpotPrice(symbol string) (float64, error) {
func (h *HUOBI) GetLatestSpotPrice(symbol currency.Pair) (float64, error) {
list, err := h.GetTradeHistory(symbol, 1)
if err != nil {
@@ -180,9 +223,13 @@ func (h *HUOBI) GetLatestSpotPrice(symbol string) (float64, error) {
}
// GetTradeHistory returns the trades for the specified symbol
func (h *HUOBI) GetTradeHistory(symbol string, size int64) ([]TradeHistory, error) {
func (h *HUOBI) GetTradeHistory(symbol currency.Pair, size int64) ([]TradeHistory, error) {
vals := url.Values{}
vals.Set("symbol", symbol)
symbolValue, err := h.FormatSymbol(symbol, asset.Spot)
if err != nil {
return nil, err
}
vals.Set("symbol", symbolValue)
if size > 0 {
vals.Set("size", strconv.FormatInt(size, 10))
@@ -194,9 +241,8 @@ func (h *HUOBI) GetTradeHistory(symbol string, size int64) ([]TradeHistory, erro
}
var result response
urlPath := fmt.Sprintf("%s/%s", h.API.Endpoints.URL, huobiMarketTradeHistory)
err := h.SendHTTPRequest(common.EncodeURLValues(urlPath, vals), &result)
err = h.SendHTTPRequest(exchange.RestSpot, common.EncodeURLValues("/"+huobiMarketTradeHistory, vals), &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
@@ -204,9 +250,13 @@ func (h *HUOBI) GetTradeHistory(symbol string, size int64) ([]TradeHistory, erro
}
// GetMarketDetail returns the ticker for the specified symbol
func (h *HUOBI) GetMarketDetail(symbol string) (Detail, error) {
func (h *HUOBI) GetMarketDetail(symbol currency.Pair) (Detail, error) {
vals := url.Values{}
vals.Set("symbol", symbol)
symbolValue, err := h.FormatSymbol(symbol, asset.Spot)
if err != nil {
return Detail{}, err
}
vals.Set("symbol", symbolValue)
type response struct {
Response
@@ -214,9 +264,8 @@ func (h *HUOBI) GetMarketDetail(symbol string) (Detail, error) {
}
var result response
urlPath := fmt.Sprintf("%s/%s", h.API.Endpoints.URL, huobiMarketDetail)
err := h.SendHTTPRequest(common.EncodeURLValues(urlPath, vals), &result)
err = h.SendHTTPRequest(exchange.RestSpot, common.EncodeURLValues("/"+huobiMarketDetail, vals), &result)
if result.ErrorMessage != "" {
return result.Tick, errors.New(result.ErrorMessage)
}
@@ -231,9 +280,8 @@ func (h *HUOBI) GetSymbols() ([]Symbol, error) {
}
var result response
urlPath := fmt.Sprintf("%s/v%s/%s", h.API.Endpoints.URL, huobiAPIVersion, huobiSymbols)
err := h.SendHTTPRequest(urlPath, &result)
err := h.SendHTTPRequest(exchange.RestSpot, "/v"+huobiAPIVersion+"/"+huobiSymbols, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
@@ -248,9 +296,8 @@ func (h *HUOBI) GetCurrencies() ([]string, error) {
}
var result response
urlPath := fmt.Sprintf("%s/v%s/%s", h.API.Endpoints.URL, huobiAPIVersion, huobiCurrencies)
err := h.SendHTTPRequest(urlPath, &result)
err := h.SendHTTPRequest(exchange.RestSpot, "/v"+huobiAPIVersion+"/"+huobiCurrencies, &result)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
}
@@ -265,9 +312,8 @@ func (h *HUOBI) GetTimestamp() (int64, error) {
}
var result response
urlPath := fmt.Sprintf("%s/v%s/%s", h.API.Endpoints.URL, huobiAPIVersion, huobiTimestamp)
err := h.SendHTTPRequest(urlPath, &result)
err := h.SendHTTPRequest(exchange.RestSpot, "/v"+huobiAPIVersion+"/"+huobiTimestamp, &result)
if result.ErrorMessage != "" {
return 0, errors.New(result.ErrorMessage)
}
@@ -279,7 +325,7 @@ func (h *HUOBI) GetAccounts() ([]Account, error) {
result := struct {
Accounts []Account `json:"data"`
}{}
err := h.SendAuthenticatedHTTPRequest(http.MethodGet, huobiAccounts, url.Values{}, nil, &result, false)
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, huobiAccounts, url.Values{}, nil, &result, false)
return result.Accounts, err
}
@@ -291,7 +337,7 @@ func (h *HUOBI) GetAccountBalance(accountID string) ([]AccountBalanceDetail, err
endpoint := fmt.Sprintf(huobiAccountBalance, accountID)
v := url.Values{}
v.Set("account-id", accountID)
err := h.SendAuthenticatedHTTPRequest(http.MethodGet, endpoint, v, nil, &result, false)
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, endpoint, v, nil, &result, false)
return result.AccountBalanceData.AccountBalanceDetails, err
}
@@ -300,7 +346,7 @@ func (h *HUOBI) GetAggregatedBalance() ([]AggregatedBalance, error) {
result := struct {
AggregatedBalances []AggregatedBalance `json:"data"`
}{}
err := h.SendAuthenticatedHTTPRequest(
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot,
http.MethodGet,
huobiAggregatedBalance,
nil,
@@ -312,7 +358,12 @@ func (h *HUOBI) GetAggregatedBalance() ([]AggregatedBalance, error) {
}
// SpotNewOrder submits an order to Huobi
func (h *HUOBI) SpotNewOrder(arg SpotNewOrderRequestParams) (int64, error) {
func (h *HUOBI) SpotNewOrder(arg *SpotNewOrderRequestParams) (int64, error) {
symbolValue, err := h.FormatSymbol(arg.Symbol, asset.Spot)
if err != nil {
return 0, err
}
data := struct {
AccountID int `json:"account-id,string"`
Amount string `json:"amount"`
@@ -323,7 +374,7 @@ func (h *HUOBI) SpotNewOrder(arg SpotNewOrderRequestParams) (int64, error) {
}{
AccountID: arg.AccountID,
Amount: strconv.FormatFloat(arg.Amount, 'f', -1, 64),
Symbol: arg.Symbol,
Symbol: symbolValue,
Type: string(arg.Type),
}
@@ -339,7 +390,7 @@ func (h *HUOBI) SpotNewOrder(arg SpotNewOrderRequestParams) (int64, error) {
result := struct {
OrderID int64 `json:"data,string"`
}{}
err := h.SendAuthenticatedHTTPRequest(
err = h.SendAuthenticatedHTTPRequest(exchange.RestSpot,
http.MethodPost,
huobiOrderPlace,
nil,
@@ -356,7 +407,7 @@ func (h *HUOBI) CancelExistingOrder(orderID int64) (int64, error) {
OrderID int64 `json:"data,string"`
}{}
endpoint := fmt.Sprintf(huobiOrderCancel, strconv.FormatInt(orderID, 10))
err := h.SendAuthenticatedHTTPRequest(http.MethodPost, endpoint, url.Values{}, nil, &resp, false)
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, endpoint, url.Values{}, nil, &resp, false)
return resp.OrderID, err
}
@@ -368,7 +419,7 @@ func (h *HUOBI) CancelOrderBatch(_ []int64) ([]CancelOrderBatch, error) {
}
var result response
err := h.SendAuthenticatedHTTPRequest(http.MethodPost, huobiOrderCancelBatch, url.Values{}, nil, &result, false)
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, huobiOrderCancelBatch, url.Values{}, nil, &result, false)
if result.ErrorMessage != "" {
return nil, errors.New(result.ErrorMessage)
@@ -377,9 +428,12 @@ func (h *HUOBI) CancelOrderBatch(_ []int64) ([]CancelOrderBatch, error) {
}
// CancelOpenOrdersBatch cancels a batch of orders -- to-do
func (h *HUOBI) CancelOpenOrdersBatch(accountID, symbol string) (CancelOpenOrdersBatch, error) {
func (h *HUOBI) CancelOpenOrdersBatch(accountID string, symbol currency.Pair) (CancelOpenOrdersBatch, error) {
params := url.Values{}
symbolValue, err := h.FormatSymbol(symbol, asset.Spot)
if err != nil {
return CancelOpenOrdersBatch{}, err
}
params.Set("account-id", accountID)
var result CancelOpenOrdersBatch
@@ -388,10 +442,10 @@ func (h *HUOBI) CancelOpenOrdersBatch(accountID, symbol string) (CancelOpenOrder
Symbol string `json:"symbol"`
}{
AccountID: accountID,
Symbol: symbol,
Symbol: symbolValue,
}
err := h.SendAuthenticatedHTTPRequest(http.MethodPost, huobiBatchCancelOpenOrders, url.Values{}, data, &result, false)
err = h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, huobiBatchCancelOpenOrders, url.Values{}, data, &result, false)
if result.Data.FailedCount > 0 {
return result, fmt.Errorf("there were %v failed order cancellations", result.Data.FailedCount)
}
@@ -406,7 +460,7 @@ func (h *HUOBI) GetOrder(orderID int64) (OrderInfo, error) {
}{}
urlVal := url.Values{}
urlVal.Set("clientOrderId", strconv.FormatInt(orderID, 10))
err := h.SendAuthenticatedHTTPRequest(http.MethodGet,
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet,
huobiGetOrder,
urlVal,
nil,
@@ -421,18 +475,22 @@ func (h *HUOBI) GetOrderMatchResults(orderID int64) ([]OrderMatchInfo, error) {
Orders []OrderMatchInfo `json:"data"`
}{}
endpoint := fmt.Sprintf(huobiGetOrderMatch, strconv.FormatInt(orderID, 10))
err := h.SendAuthenticatedHTTPRequest(http.MethodGet, endpoint, url.Values{}, nil, &resp, false)
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, endpoint, url.Values{}, nil, &resp, false)
return resp.Orders, err
}
// GetOrders returns a list of orders
func (h *HUOBI) GetOrders(symbol, types, start, end, states, from, direct, size string) ([]OrderInfo, error) {
func (h *HUOBI) GetOrders(symbol currency.Pair, types, start, end, states, from, direct, size string) ([]OrderInfo, error) {
resp := struct {
Orders []OrderInfo `json:"data"`
}{}
vals := url.Values{}
vals.Set("symbol", symbol)
symbolValue, err := h.FormatSymbol(symbol, asset.Spot)
if err != nil {
return nil, err
}
vals.Set("symbol", symbolValue)
vals.Set("states", states)
if types != "" {
@@ -459,36 +517,44 @@ func (h *HUOBI) GetOrders(symbol, types, start, end, states, from, direct, size
vals.Set("size", size)
}
err := h.SendAuthenticatedHTTPRequest(http.MethodGet, huobiGetOrders, vals, nil, &resp, false)
err = h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, huobiGetOrders, vals, nil, &resp, false)
return resp.Orders, err
}
// GetOpenOrders returns a list of orders
func (h *HUOBI) GetOpenOrders(accountID, symbol, side string, size int64) ([]OrderInfo, error) {
func (h *HUOBI) GetOpenOrders(symbol currency.Pair, accountID, side string, size int64) ([]OrderInfo, error) {
resp := struct {
Orders []OrderInfo `json:"data"`
}{}
vals := url.Values{}
vals.Set("symbol", symbol)
symbolValue, err := h.FormatSymbol(symbol, asset.Spot)
if err != nil {
return nil, err
}
vals.Set("symbol", symbolValue)
vals.Set("accountID", accountID)
if len(side) > 0 {
vals.Set("side", side)
}
vals.Set("size", strconv.FormatInt(size, 10))
err := h.SendAuthenticatedHTTPRequest(http.MethodGet, huobiGetOpenOrders, vals, nil, &resp, false)
err = h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, huobiGetOpenOrders, vals, nil, &resp, false)
return resp.Orders, err
}
// GetOrdersMatch returns a list of matched orders
func (h *HUOBI) GetOrdersMatch(symbol, types, start, end, from, direct, size string) ([]OrderMatchInfo, error) {
func (h *HUOBI) GetOrdersMatch(symbol currency.Pair, types, start, end, from, direct, size string) ([]OrderMatchInfo, error) {
resp := struct {
Orders []OrderMatchInfo `json:"data"`
}{}
vals := url.Values{}
vals.Set("symbol", symbol)
symbolValue, err := h.FormatSymbol(symbol, asset.Spot)
if err != nil {
return nil, err
}
vals.Set("symbol", symbolValue)
if types != "" {
vals.Set("types", types)
@@ -514,18 +580,22 @@ func (h *HUOBI) GetOrdersMatch(symbol, types, start, end, from, direct, size str
vals.Set("size", size)
}
err := h.SendAuthenticatedHTTPRequest(http.MethodGet, huobiGetOrdersMatch, vals, nil, &resp, false)
err = h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, huobiGetOrdersMatch, vals, nil, &resp, false)
return resp.Orders, err
}
// MarginTransfer transfers assets into or out of the margin account
func (h *HUOBI) MarginTransfer(symbol, currency string, amount float64, in bool) (int64, error) {
func (h *HUOBI) MarginTransfer(symbol currency.Pair, currency string, amount float64, in bool) (int64, error) {
symbolValue, err := h.FormatSymbol(symbol, asset.Spot)
if err != nil {
return 0, err
}
data := struct {
Symbol string `json:"symbol"`
Currency string `json:"currency"`
Amount string `json:"amount"`
}{
Symbol: symbol,
Symbol: symbolValue,
Currency: currency,
Amount: strconv.FormatFloat(amount, 'f', -1, 64),
}
@@ -538,18 +608,22 @@ func (h *HUOBI) MarginTransfer(symbol, currency string, amount float64, in bool)
resp := struct {
TransferID int64 `json:"data"`
}{}
err := h.SendAuthenticatedHTTPRequest(http.MethodPost, path, nil, data, &resp, false)
err = h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, path, nil, data, &resp, false)
return resp.TransferID, err
}
// MarginOrder submits a margin order application
func (h *HUOBI) MarginOrder(symbol, currency string, amount float64) (int64, error) {
func (h *HUOBI) MarginOrder(symbol currency.Pair, currency string, amount float64) (int64, error) {
symbolValue, err := h.FormatSymbol(symbol, asset.Spot)
if err != nil {
return 0, err
}
data := struct {
Symbol string `json:"symbol"`
Currency string `json:"currency"`
Amount string `json:"amount"`
}{
Symbol: symbol,
Symbol: symbolValue,
Currency: currency,
Amount: strconv.FormatFloat(amount, 'f', -1, 64),
}
@@ -557,7 +631,7 @@ func (h *HUOBI) MarginOrder(symbol, currency string, amount float64) (int64, err
resp := struct {
MarginOrderID int64 `json:"data"`
}{}
err := h.SendAuthenticatedHTTPRequest(http.MethodPost, huobiMarginOrders, nil, data, &resp, false)
err = h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, huobiMarginOrders, nil, data, &resp, false)
return resp.MarginOrderID, err
}
@@ -574,14 +648,18 @@ func (h *HUOBI) MarginRepayment(orderID int64, amount float64) (int64, error) {
}{}
endpoint := fmt.Sprintf(huobiMarginRepay, strconv.FormatInt(orderID, 10))
err := h.SendAuthenticatedHTTPRequest(http.MethodPost, endpoint, nil, data, &resp, false)
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, endpoint, nil, data, &resp, false)
return resp.MarginOrderID, err
}
// GetMarginLoanOrders returns the margin loan orders
func (h *HUOBI) GetMarginLoanOrders(symbol, currency, start, end, states, from, direct, size string) ([]MarginOrder, error) {
func (h *HUOBI) GetMarginLoanOrders(symbol currency.Pair, currency, start, end, states, from, direct, size string) ([]MarginOrder, error) {
vals := url.Values{}
vals.Set("symbol", symbol)
symbolValue, err := h.FormatSymbol(symbol, asset.Spot)
if err != nil {
return nil, err
}
vals.Set("symbol", symbolValue)
vals.Set("currency", currency)
if start != "" {
@@ -611,20 +689,24 @@ func (h *HUOBI) GetMarginLoanOrders(symbol, currency, start, end, states, from,
resp := struct {
MarginLoanOrders []MarginOrder `json:"data"`
}{}
err := h.SendAuthenticatedHTTPRequest(http.MethodGet, huobiMarginLoanOrders, vals, nil, &resp, false)
err = h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, huobiMarginLoanOrders, vals, nil, &resp, false)
return resp.MarginLoanOrders, err
}
// GetMarginAccountBalance returns the margin account balances
func (h *HUOBI) GetMarginAccountBalance(symbol string) ([]MarginAccountBalance, error) {
func (h *HUOBI) GetMarginAccountBalance(symbol currency.Pair) ([]MarginAccountBalance, error) {
resp := struct {
Balances []MarginAccountBalance `json:"data"`
}{}
vals := url.Values{}
if symbol != "" {
vals.Set("symbol", symbol)
if symbol != (currency.Pair{}) {
symbolValue, err := h.FormatSymbol(symbol, asset.Spot)
if err != nil {
return resp.Balances, err
}
vals.Set("symbol", symbolValue)
}
err := h.SendAuthenticatedHTTPRequest(http.MethodGet, huobiMarginAccountBalance, vals, nil, &resp, false)
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, huobiMarginAccountBalance, vals, nil, &resp, false)
return resp.Balances, err
}
@@ -654,7 +736,7 @@ func (h *HUOBI) Withdraw(c currency.Code, address, addrTag string, amount, fee f
data.AddrTag = addrTag
}
err := h.SendAuthenticatedHTTPRequest(http.MethodPost, huobiWithdrawCreate, nil, data, &resp.WithdrawID, false)
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, huobiWithdrawCreate, nil, data, &resp.WithdrawID, false)
return resp.WithdrawID, err
}
@@ -667,7 +749,7 @@ func (h *HUOBI) CancelWithdraw(withdrawID int64) (int64, error) {
vals.Set("withdraw-id", strconv.FormatInt(withdrawID, 10))
endpoint := fmt.Sprintf(huobiWithdrawCancel, strconv.FormatInt(withdrawID, 10))
err := h.SendAuthenticatedHTTPRequest(http.MethodPost, endpoint, vals, nil, &resp, false)
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, endpoint, vals, nil, &resp, false)
return resp.WithdrawID, err
}
@@ -680,7 +762,7 @@ func (h *HUOBI) QueryDepositAddress(cryptocurrency string) (DepositAddress, erro
vals := url.Values{}
vals.Set("currency", cryptocurrency)
err := h.SendAuthenticatedHTTPRequest(http.MethodGet, huobiAccountDepositAddress, vals, nil, &resp, true)
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, huobiAccountDepositAddress, vals, nil, &resp, true)
if err != nil {
return DepositAddress{}, err
}
@@ -699,7 +781,7 @@ func (h *HUOBI) QueryWithdrawQuotas(cryptocurrency string) (WithdrawQuota, error
vals := url.Values{}
vals.Set("currency", cryptocurrency)
err := h.SendAuthenticatedHTTPRequest(http.MethodGet, huobiAccountWithdrawQuota, vals, nil, &resp, true)
err := h.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, huobiAccountWithdrawQuota, vals, nil, &resp, true)
if err != nil {
return WithdrawQuota{}, err
}
@@ -707,23 +789,41 @@ func (h *HUOBI) QueryWithdrawQuotas(cryptocurrency string) (WithdrawQuota, error
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (h *HUOBI) SendHTTPRequest(path string, result interface{}) error {
return h.SendPayload(context.Background(), &request.Item{
func (h *HUOBI) SendHTTPRequest(ep exchange.URL, path string, result interface{}) error {
endpoint, err := h.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
var tempResp json.RawMessage
var errCap errorCapture
err = h.SendPayload(context.Background(), &request.Item{
Method: http.MethodGet,
Path: path,
Result: result,
Path: endpoint + path,
Result: &tempResp,
Verbose: h.Verbose,
HTTPDebugging: h.HTTPDebugging,
HTTPRecording: h.HTTPRecording,
})
if err != nil {
return err
}
if err := json.Unmarshal(tempResp, &errCap); err == nil {
if errCap.Code != 200 && errCap.ErrMsg != "" {
return errors.New(errCap.ErrMsg)
}
}
return json.Unmarshal(tempResp, result)
}
// SendAuthenticatedHTTPRequest sends authenticated requests to the HUOBI API
func (h *HUOBI) SendAuthenticatedHTTPRequest(method, endpoint string, values url.Values, data, result interface{}, isVersion2API bool) error {
func (h *HUOBI) SendAuthenticatedHTTPRequest(ep exchange.URL, method, endpoint string, values url.Values, data, result interface{}, isVersion2API bool) error {
if !h.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, h.Name)
}
ePoint, err := h.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
if values == nil {
values = url.Values{}
}
@@ -735,9 +835,9 @@ func (h *HUOBI) SendAuthenticatedHTTPRequest(method, endpoint string, values url
values.Set("Timestamp", now.UTC().Format("2006-01-02T15:04:05"))
if isVersion2API {
endpoint = fmt.Sprintf("/v%s/%s", huobiAPIVersion2, endpoint)
endpoint = "/v" + huobiAPIVersion2 + "/" + endpoint
} else {
endpoint = fmt.Sprintf("/v%s/%s", huobiAPIVersion, endpoint)
endpoint = "/v" + huobiAPIVersion + "/" + endpoint
}
payload := fmt.Sprintf("%s\napi.huobi.pro\n%s\n%s",
@@ -753,22 +853,21 @@ func (h *HUOBI) SendAuthenticatedHTTPRequest(method, endpoint string, values url
hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(payload), []byte(h.API.Credentials.Secret))
values.Set("Signature", crypto.Base64Encode(hmac))
urlPath := h.API.Endpoints.URL + common.EncodeURLValues(endpoint, values)
urlPath := ePoint + common.EncodeURLValues(endpoint, values)
var body []byte
if data != nil {
encoded, err := json.Marshal(data)
body, err = json.Marshal(data)
if err != nil {
return err
}
body = encoded
}
// Time difference between your timestamp and standard should be less than 1 minute.
ctx, cancel := context.WithDeadline(context.Background(), now.Add(time.Minute))
defer cancel()
interim := json.RawMessage{}
err := h.SendPayload(ctx, &request.Item{
err = h.SendPayload(ctx, &request.Item{
Method: method,
Path: urlPath,
Headers: headers,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,416 @@
package huobi
import (
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
)
type errorCapture struct {
Status string `json:"status"`
Code int64 `json:"err_code"`
ErrMsg string `json:"err_msg"`
Timestamp int64 `json:"ts"`
}
// MarketSummary24Hr stores past 24hr market summary data of a given symbol
type MarketSummary24Hr struct {
Tick struct {
Amount float64 `json:"amount"`
Open float64 `json:"open"`
Close float64 `json:"close"`
High float64 `json:"high"`
ID int64 `json:"id"`
Count float64 `json:"count"`
Low float64 `json:"low"`
Version int64 `json:"version"`
Volume float64 `json:"vol"`
}
}
// WsKlineData stores kline data for futures and swap websocket
type WsKlineData struct {
Channel string `json:"ch"`
Timestamp int64 `json:"ts"`
Tick struct {
ID int64 `json:"id"`
MRID int64 `json:"mrid"`
Volume float64 `json:"vol"`
Count float64 `json:"count"`
Open float64 `json:"open"`
Close float64 `json:"close"`
Low float64 `json:"low"`
High float64 `json:"high"`
Amount float64 `json:"amount"`
} `json:"tick"`
}
// WsMarketDepth stores market depth data for futures and swap websocket
type WsMarketDepth struct {
Channel string `json:"ch"`
Timestamp int64 `json:"ts"`
Tick struct {
MRID int64 `json:"mrid"`
ID int64 `json:"id"`
Bids [][2]float64 `json:"bids"`
Asks [][2]float64 `json:"asks"`
Timestamp int64 `json:"ts"`
Version int64 `json:"version"`
Channel string `json:"ch"`
} `json:"tick"`
}
// WsIncrementalMarketDepth stores incremental market depth data for swap and futures websocket
type WsIncrementalMarketDepth struct {
Channel string `json:"ch"`
Timestamp int64 `json:"ts"`
Tick struct {
MRID int64 `json:"mrid"`
ID int64 `json:"id"`
Bids [][2]float64 `json:"bids"`
Asks [][2]float64 `json:"asks"`
Timestamp int64 `json:"ts"`
Version int64 `json:"version"`
Channel string `json:"ch"`
Event string `json:"event"`
} `json:"tick"`
}
// WsMarketDetail stores market detail data for futures and swap websocket
type WsMarketDetail struct {
Channel string `json:"ch"`
Timestamp int64 `json:"ts"`
Tick struct {
ID int64 `json:"id"`
MRID int64 `json:"mrid"`
Open float64 `json:"open"`
Close float64 `json:"close"`
High float64 `json:"high"`
Low float64 `json:"low"`
Amount float64 `json:"amount"`
Volume float64 `json:"vol"`
Count float64 `json:"count"`
} `json:"tick"`
}
// WsMarketBBOData stores BBO data for futures and swap websocket
type WsMarketBBOData struct {
Channel string `json:"ch"`
Timestamp int64 `json:"ts"`
Tick struct {
Channel string `json:"ch"`
MRID int64 `json:"mrid"`
ID int64 `json:"id"`
Bid [2]float64 `json:"bid"`
Ask [2]float64 `json:"ask"`
Timestamp int64 `json:"ts"`
Version int64 `json:":version"`
} `json:"tick"`
}
// WsSubTradeDetail stores trade detail data for futures websocket
type WsSubTradeDetail struct {
Channel string `json:"ch"`
Timestamp int64 `json:"ts"`
Tick struct {
ID int64 `json:"id"`
Timestamp int64 `json:"ts"`
Data []struct {
Amount float64 `json:"amount"`
Timestamp int64 `json:"ts"`
ID int64 `json:"id"`
Price float64 `json:"price"`
Direction string `json:"direction"`
} `json:"data"`
} `json:"tick"`
}
//
// Futures
// FWsRequestKline stores requested kline data for futures websocket
type FWsRequestKline struct {
Rep string `json:"rep"`
ID string `json:"id"`
WsID int64 `json:"wsid"`
Tick []struct {
Volume float64 `json:"vol"`
Count float64 `json:"count"`
ID int64 `json:"id"`
Open float64 `json:"open"`
Close float64 `json:"close"`
Low float64 `json:"low"`
High float64 `json:"high"`
Amount float64 `json:"amount"`
} `json:"tick"`
}
// FWsReqTradeDetail stores requested trade detail data for futures websocket
type FWsReqTradeDetail struct {
Rep string `json:"rep"`
ID string `json:"id"`
Timestamp int64 `json:"ts"`
Data []struct {
ID int64 `json:"id"`
Price float64 `json:"price"`
Amount float64 `json:"amount"`
Direction string `json:"direction"`
Timestamp int64 `json:"ts"`
} `json:"data"`
}
// FWsSubKlineIndex stores subscribed kline index data for futures websocket
type FWsSubKlineIndex struct {
Channel string `json:"ch"`
Timestamp int64 `json:"ts"`
Tick struct {
ID string `json:"id"`
Open float64 `json:"open,string"`
Close float64 `json:"close,string"`
High float64 `json:"high,string"`
Low float64 `json:"low,string"`
Amount float64 `json:"amount,string"`
Volume float64 `json:"vol,string"`
Count float64 `json:"count,string"`
} `json:"tick"`
}
// FWsReqKlineIndex stores requested kline index data for futures websocket
type FWsReqKlineIndex struct {
ID string `json:"id"`
Rep string `json:"rep"`
WsID int64 `json:"wsid"`
Timestamp int64 `json:"ts"`
Data []struct {
ID int64 `json:"id"`
Open float64 `json:"open"`
Close float64 `json:"close"`
Low float64 `json:"low"`
High float64 `json:"high"`
Amount float64 `json:"amount"`
Volume float64 `json:"vol"`
Count float64 `json:"count"`
} `json:"data"`
}
// FWsSubBasisData stores subscribed basis data for futures websocket
type FWsSubBasisData struct {
Channel string `json:"ch"`
Timestamp int64 `json:"ts"`
Tick struct {
ID int64 `json:"id"`
IndexPrice float64 `json:"index_price,string"`
ContractPrice float64 `json:"contract_price,string"`
Basis float64 `json:"basis,string"`
BasisRate float64 `json:"basis_rate,string"`
}
}
// FWsReqBasisData stores requested basis data for futures websocket
type FWsReqBasisData struct {
ID string `json:"id"`
Rep string `json:"rep"`
Timestamp int64 `json:"ts"`
WsID int64 `json:"wsid"`
Tick struct {
ID int64 `json:"id"`
IndexPrice float64 `json:"index_price,string"`
ContractPrice float64 `json:"contract_price,string"`
Basis float64 `json:"basis,string"`
BasisRate float64 `json:"basis_rate,string"`
} `json:"tick"`
}
// FWsSubOrderData stores subscribed order data for futures websocket
type FWsSubOrderData struct {
Operation string `json:"op"`
Topic string `json:"topic"`
UID string `json:"uid"`
Timestamp int64 `json:"ts"`
Symbol string `json:"symbol"`
ContractType string `json:"contract_type"`
ContractCode string `json:"contract_code"`
Volume float64 `json:"volume"`
Price float64 `json:"price"`
OrderPriceType string `json:"order_price_type"`
Direction string `json:"direction"`
Offset string `json:"offset"`
Status int64 `json:"status"`
LeverageRate int64 `json:"lever_rate"`
OrderID int64 `json:"order_id"`
OrderIDString string `json:"order_id_string"`
ClientOrderID int64 `json:"client_order_id"`
OrderSource string `json:"order_source"`
OrderType int64 `json:"order_type"`
CreatedAt int64 `json:"created_at"`
TradeVolume float64 `json:"trade_volume"`
TradeTurnover float64 `json:"trade_turnover"`
Fee float64 `json:"fee"`
TradeAvgPrice float64 `json:"trade_avg_price"`
MarginFrozen float64 `json:"margin_frozen"`
Profit float64 `json:"profit"`
FeeAsset string `json:"fee_asset"`
CancelledAt int64 `json:"canceled_at"`
Trade []struct {
ID string `json:"id"`
TradeID int64 `json:"trade_id"`
TradeVolume float64 `json:"trade_volume"`
TradePrice float64 `json:"trade_price"`
TradeFee float64 `json:"trade_fee"`
TradeTurnover float64 `json:"trade_turnover"`
CreatedAt int64 `json:"created_at"`
Role string `json:"role"`
FeeAsset string `json:"fee_asset"`
} `json:"trade"`
}
// FWsSubMatchOrderData stores subscribed match order data for futures websocket
type FWsSubMatchOrderData struct {
Operation string `json:"op"`
Topic string `json:"topic"`
UID string `json:"uid"`
Timestamp int64 `json:"ts"`
Symbol string `json:"symbol"`
ContractType string `json:"contract_type"`
ContractCode string `json:"contract_code"`
Status int64 `json:"status"`
OrderID int64 `json:"order_id"`
OrderIDString string `json:"order_id_string"`
OrderType string `json:"order_type"`
Volume float64 `json:"volume"`
TradeVolume float64 `json:"trade_volume"`
ClientOrderID int64 `json:"client_order_id"`
Trade []struct {
ID string `json:"id"`
TradeID int64 `json:"trade_id"`
TradeVolume float64 `json:"trade_volume"`
TradePrice float64 `json:"trade_price"`
TradeTurnover float64 `json:"trade_turnover"`
CreatedAt int64 `json:"created_at"`
Role string `json:"role"`
}
}
// FWsSubEquityUpdates stores account equity updates data for futures websocket
type FWsSubEquityUpdates struct {
Operation string `json:"op"`
Topic string `json:"topic"`
UID string `json:"uid"`
Timestamp int64 `json:"ts"`
Event string `json:"event"`
Data []struct {
Symbol string `json:"symbol"`
MarginBalance float64 `json:"margin_balance"`
MarginStatic int64 `json:"margin_static"`
MarginPosition float64 `json:"margin_position"`
MarginFrozen float64 `json:"margin_frozen"`
MarginAvailable float64 `json:"margin_available"`
ProfitReal float64 `json:"profit_real"`
ProfitUnreal float64 `json:"profit_unreal"`
WithdrawAvailable float64 `json:"withdraw_available"`
RiskRate float64 `json:"risk_rate"`
LiquidationPrice float64 `json:"liquidation_price"`
LeverageRate float64 `json:"lever_rate"`
AdjustFactor float64 `json:"adjust_factor"`
} `json:"data"`
}
// FWsSubPositionUpdates stores subscribed position updates data for futures websocket
type FWsSubPositionUpdates struct {
Operation string `json:"op"`
Topic string `json:"topic"`
UID string `json:"uid"`
Timestamp int64 `json:"ts"`
Event string `json:"event"`
PositionsData []struct {
Symbol string `json:"symbol"`
ContractCode string `json:"contract_code"`
ContractType string `json:"contract_type"`
Volume float64 `json:"volume"`
Available float64 `json:"available"`
Frozen float64 `json:"frozen"`
CostOpen float64 `json:"cost_open"`
CostHold float64 `json:"cost_hold"`
ProfitUnreal float64 `json:"profit_unreal"`
ProfitRate float64 `json:"profit_rate"`
Profit float64 `json:"profit"`
PositionMargin float64 `json:"position_margin"`
LeverageRate float64 `json:"lever_rate"`
Direction string `json:"direction"`
LastPrice float64 `json:"last_price"`
} `json:"data"`
}
// FWsSubLiquidationOrders stores subscribed liquidation orders data for futures websocket
type FWsSubLiquidationOrders struct {
Operation string `json:"op"`
Topic string `json:"topic"`
Timestamp int64 `json:"ts"`
OrdersData []struct {
Symbol string `json:"symbol"`
ContractCode string `json:"contract_code"`
Direction string `json:"direction"`
Offset string `json:"offset"`
Volume float64 `json:"volume"`
Price float64 `json:"price"`
CreatedAt int64 `json:"created_at"`
} `json:"data"`
}
// FWsSubContractInfo stores contract info data for futures websocket
type FWsSubContractInfo struct {
Operation string `json:"op"`
Topic string `json:"topic"`
Timestamp int64 `json:"ts"`
Event string `json:"event"`
ContractData []struct {
Symbol string `json:"symbol"`
ContractCode string `json:"contract_code"`
ContractType string `json:"contract_type"`
ContractSize float64 `json:"contract_size"`
PriceTick float64 `json:"price_tick"`
DeliveryDate string `json:"delivery_date"`
CreateDate string `json:"create_date"`
ContractStatus int64 `json:"contract_status"`
} `json:"data"`
}
// FWsSubTriggerOrderUpdates stores subscribed trigger order updates data for futures websocket
type FWsSubTriggerOrderUpdates struct {
Operation string `json:"op"`
Topic string `json:"topic"`
UID string `json:"uid"`
Event string `json:"event"`
Data []struct {
Symbol string `json:"symbol"`
ContractCode string `json:"contract_code"`
ContractType string `json:"contract_type"`
TriggerType string `json:"trigger_type"`
Volume float64 `json:"volume"`
OrderType int64 `json:"order_type"`
Direction string `json:"direction"`
Offset string `json:"offset"`
LeverageRate int64 `json:"lever_rate"`
OrderID int64 `json:"order_id"`
OrderIDString string `json:"order_id_str"`
RelationOrderID string `json:"relation_order_id"`
OrderPriceType string `json:"order_price_type"`
Status int64 `json:"status"`
OrderSource string `json:"order_source"`
TriggerPrice float64 `json:"trigger_price"`
TriggeredPrice float64 `json:"triggered_price"`
OrderPrice float64 `json:"order_price"`
CreatedAt int64 `json:"created_at"`
TriggeredAt int64 `json:"triggered_at"`
OrderInsertAt int64 `json:"order_insert_at"`
CancelledAt int64 `json:"canceled_at"`
FailCode int64 `json:"fail_code"`
FailReason string `json:"fail_reason"`
} `json:"data"`
}
// --------------------------------Spot-----------------------------------------
// Response stores the Huobi response information
type Response struct {
Status string `json:"status"`
@@ -9,12 +420,38 @@ type Response struct {
ErrorMessage string `json:"err-msg"`
}
// MarginRatesData stores margin rates data
type MarginRatesData struct {
Data []struct {
Symbol string `json:"symbol"`
Currencies []struct {
Currency string `json:"currency"`
InterestRate float64 `json:"interest-rate,string"`
MinLoanAmount float64 `json:"min-loan-amt,string"`
MaxLoanAmount float64 `json:"max-loan-amt,string"`
LoanableAmount float64 `json:"loanable-amt,string"`
ActualRate float64 `json:"actual-rate,string"`
} `json:"currencies"`
} `json:"data"`
}
// ResponseV2 stores the Huobi generic response info
type ResponseV2 struct {
Code int32 `json:"code"`
Message string `json:"message"`
}
// SwapMarketsData stores market data for swaps
type SwapMarketsData struct {
Symbol string `json:"symbol"`
ContractCode string `json:"contract_code"`
ContractSize float64 `json:"contract_size"`
PriceTick float64 `json:"price_tick"`
SettlementDate string `json:"settlement_date"`
CreateDate string `json:"create_date"`
ContractStatus int64 `json:"contract_status"`
}
// KlineItem stores a kline item
type KlineItem struct {
ID int64 `json:"id"`
@@ -79,8 +516,8 @@ var (
// OrderBookDataRequestParams represents Klines request data.
type OrderBookDataRequestParams struct {
Symbol string `json:"symbol"` // Required; example LTCBTC,BTCUSDT
Type OrderBookDataRequestParamsType `json:"type"` // step0, step1, step2, step3, step4, step5 (combined depth 0-5); when step0, no depth is merged
Symbol currency.Pair // Required; example LTCBTC,BTCUSDT
Type OrderBookDataRequestParamsType `json:"type"` // step0, step1, step2, step3, step4, step5 (combined depth 0-5); when step0, no depth is merged
}
// Orderbook stores the orderbook data
@@ -122,17 +559,25 @@ type Detail struct {
// Symbol stores the symbol data
type Symbol struct {
BaseCurrency string `json:"base-currency"`
QuoteCurrency string `json:"quote-currency"`
PricePrecision int `json:"price-precision"`
AmountPrecision int `json:"amount-precision"`
SymbolPartition string `json:"symbol-partition"`
Innovation string `json:"innovation"`
State string `json:"state"`
ValuePrecision int `json:"value-precision"`
MinimumOrderAmount float64 `json:"min-order-amt"`
MaximumOrderAmount float64 `json:"max-order-amt"`
MinimumOrderValue float64 `json:"min-order-value"`
BaseCurrency string `json:"base-currency"`
QuoteCurrency string `json:"quote-currency"`
PricePrecision float64 `json:"price-precision"`
AmountPrecision float64 `json:"amount-precision"`
SymbolPartition string `json:"symbol-partition"`
Symbol string `json:"symbol"`
State string `json:"state"`
ValuePrecision float64 `json:"value-precision"`
MinOrderAmt float64 `json:"min-order-amt"`
MaxOrderAmt float64 `json:"max-order-amt"`
MinOrderValue float64 `json:"min-order-value"`
LimitOrderMinOrderAmt float64 `json:"limit-order-min-order-amt"`
LimitOrderMaxOrderAmt float64 `json:"limit-order-max-order-amt"`
SellMarketMinOrderAmt float64 `json:"sell-market-min-order-amt"`
SellMarketMaxOrderAmt float64 `json:"sell-market-max-order-amt"`
BuyMarketMaxOrderAmt float64 `json:"buy-market-max-order-amt"`
LeverageRatio float64 `json:"leverage-ratio"`
SuperMarginLeverageRatio float64 `json:"super-margin-leverage-ratio"`
FundingLeverageRatio float64 `json:"funding-leverage-ratio"`
}
// Account stores the account data
@@ -248,7 +693,7 @@ type SpotNewOrderRequestParams struct {
Amount float64 `json:"amount"` // The limit price indicates the quantity of the order, the market price indicates how much to buy when the order is paid, and the market price indicates how much the coin is sold when the order is sold.
Price float64 `json:"price"` // Order price, market price does not use this parameter
Source string `json:"source"` // Order source, api: API call, margin-api: loan asset transaction
Symbol string `json:"symbol"` // The symbol to use; example btcusdt, bccbtc......
Symbol currency.Pair `json:"symbol"` // The symbol to use; example btcusdt, bccbtc......
Type SpotNewOrderRequestParamsType `json:"type"` // 订单类型, buy-market: 市价买, sell-market: 市价卖, buy-limit: 限价买, sell-limit: 限价卖
}
@@ -299,9 +744,9 @@ var (
// KlinesRequestParams represents Klines request data.
type KlinesRequestParams struct {
Symbol string // Symbol to be used; example btcusdt, bccbtc......
Period string // Kline time interval; 1min, 5min, 15min......
Size int // Size; [1-2000]
Symbol currency.Pair // Symbol to be used; example btcusdt, bccbtc......
Period string // Kline time interval; 1min, 5min, 15min......
Size int // Size; [1-2000]
}
// WsRequest defines a request data structure
@@ -597,3 +1042,167 @@ type authenticationPing struct {
OP string `json:"op"`
TS int64 `json:"ts"`
}
// OrderVars stores side, status and type for any order/trade
type OrderVars struct {
Side order.Side
Status order.Status
OrderType order.Type
Fee float64
}
// Variables below are used to check api requests being sent out
var (
validPeriods = []string{"5min", "15min", "30min", "60min", "4hour", "1day"}
validBasisPriceTypes = []string{"open", "close", "high", "low", "average"}
validAmountType = map[string]int64{
"cont": 1,
"cryptocurrency": 2,
}
validTransferType = []string{
"master_to_sub", "sub_to_master",
}
validTradeTypes = map[string]int64{
"filled": 0,
"closed": 5,
"open": 6,
}
validOrderType = map[string]int64{
"quotation": 1,
"cancelledOrder": 2,
"forcedLiquidation": 3,
"deliveryOrder": 4,
}
validOrderTypes = []string{
"limit", "opponent", "lightning", "optimal_5", "optimal_10", "optimal_20",
"fok", "ioc", "opponent_ioc", "lightning_ioc", "optimal_5_ioc",
"optimal_10_ioc", "optimal_20_ioc", "opponent_fok", "optimal_20_fok",
}
validTriggerType = map[string]string{
"greaterOrEqual": "ge",
"smallerOrEqual": "le",
}
validOrderPriceType = []string{
"limit", "optimal_5", "optimal_10", "optimal_20",
}
validLightningOrderPriceType = []string{
"lightning", "lightning_fok", "lightning_ioc",
}
validTradeType = map[string]int64{
"all": 0,
"openLong": 1,
"openShort": 2,
"closeShort": 3,
"closeLong": 4,
"liquidateLong": 5,
"liquidateShort": 6,
}
validFuturesTradeType = map[string]int64{
"all": 0,
"openLong": 1,
"openShort": 2,
"closeShort": 3,
"closeLong": 4,
"liquidateLong": 5,
"liquidateShort": 6,
"deliveryLong": 7,
"deliveryShort": 8,
"reduceLong": 11,
"reduceShort": 12,
}
validContractTypes = []string{
"this_week", "next_week", "quarter", "next_quarter",
}
validFuturesPeriods = []string{
"1min", "5min", "15min", "30min", "60min", "1hour", "4hour", "1day",
}
validFuturesOrderPriceTypes = []string{
"limit", "opponent", "lightning", "optimal_5", "optimal_10",
"optimal_20", "fok", "ioc", "opponent_ioc", "lightning_ioc",
"optimal_5_ioc", "optimal_10_ioc", "optimal_20_ioc", "opponent_fok",
"lightning_fok", "optimal_5_fok", "optimal_10_fok", "optimal_20_fok",
}
validFuturesRecordTypes = map[string]string{
"closeLong": "3",
"closeShort": "4",
"openOpenPositionsTakerFees": "5",
"openPositionsMakerFees": "6",
"closePositionsTakerFees": "7",
"closePositionsMakerFees": "8",
"closeLongDelivery": "9",
"closeShortDelivery": "10",
"deliveryFee": "11",
"longLiquidationClose": "12",
"shortLiquidationClose": "13",
"transferFromSpotToContracts": "14",
"transferFromContractsToSpot": "15",
"settleUnrealizedLongPNL": "16",
"settleUnrealizedShortPNL": "17",
"clawback": "19",
"system": "26",
"activityPrizeRewards": "28",
"rebate": "29",
"transferToSub": "34",
"transferFromSub": "35",
"transferToMaster": "36",
"transferFromMaster": "37",
}
validOffsetTypes = []string{
"open", "close",
}
validOPTypes = []string{
"lightning", "lightning_fok", "lightning_ioc",
}
validFuturesReqType = map[string]int64{
"all": 1,
"finishedStatus": 2,
}
validFuturesOrderTypes = map[string]int64{
"limit": 1,
"opponent": 3,
"lightning": 4,
"triggerOrder": 5,
"postOnly": 6,
"optimal_5": 7,
"optimal_10": 8,
"optimal_20": 9,
"fok": 10,
"ioc": 11,
}
validOrderStatus = map[order.Status]int64{
order.AnyStatus: 0,
order.Active: 3,
order.PartiallyFilled: 4,
order.PartiallyCancelled: 5,
order.Filled: 6,
order.Cancelled: 7,
}
validStatusTypes = map[string]int64{
"all": 0,
"success": 4,
"failed": 5,
"cancelled": 6,
}
)

View File

@@ -25,7 +25,8 @@ import (
)
const (
baseWSURL = "wss://api.huobi.pro"
baseWSURL = "wss://api.huobi.pro"
futuresWSURL = "wss://api.hbdm.com/"
wsMarketURL = baseWSURL + "/ws"
wsMarketKline = "market.%s.kline.1min"

File diff suppressed because it is too large Load Diff

View File

@@ -26,7 +26,7 @@ type IBotExchange interface {
GetName() string
IsEnabled() bool
SetEnabled(bool)
ValidateCredentials() error
ValidateCredentials(a asset.Item) error
FetchTicker(p currency.Pair, a asset.Item) (*ticker.Price, error)
UpdateTicker(p currency.Pair, a asset.Item) (*ticker.Price, error)
FetchOrderbook(p currency.Pair, a asset.Item) (*orderbook.Base, error)
@@ -35,8 +35,8 @@ type IBotExchange interface {
UpdateTradablePairs(forceUpdate bool) error
GetEnabledPairs(a asset.Item) (currency.Pairs, error)
GetAvailablePairs(a asset.Item) (currency.Pairs, error)
FetchAccountInfo() (account.Holdings, error)
UpdateAccountInfo() (account.Holdings, error)
FetchAccountInfo(a asset.Item) (account.Holdings, error)
UpdateAccountInfo(a asset.Item) (account.Holdings, error)
GetAuthenticatedAPISupport(endpoint uint8) bool
SetPairs(pairs currency.Pairs, a asset.Item, enabled bool) error
GetAssetTypes() asset.Items

View File

@@ -43,18 +43,18 @@ type ItBit struct {
// currencyPair - example "XBTUSD" "XBTSGD" "XBTEUR"
func (i *ItBit) GetTicker(currencyPair string) (Ticker, error) {
var response Ticker
path := fmt.Sprintf("%s/%s/%s/%s", i.API.Endpoints.URL, itbitMarkets, currencyPair, itbitTicker)
path := fmt.Sprintf("/%s/%s/%s", itbitMarkets, currencyPair, itbitTicker)
return response, i.SendHTTPRequest(path, &response)
return response, i.SendHTTPRequest(exchange.RestSpot, path, &response)
}
// GetOrderbook returns full order book for the specified market.
// currencyPair - example "XBTUSD" "XBTSGD" "XBTEUR"
func (i *ItBit) GetOrderbook(currencyPair string) (OrderbookResponse, error) {
response := OrderbookResponse{}
path := fmt.Sprintf("%s/%s/%s/%s", i.API.Endpoints.URL, itbitMarkets, currencyPair, itbitOrderbook)
path := fmt.Sprintf("/%s/%s/%s", itbitMarkets, currencyPair, itbitOrderbook)
return response, i.SendHTTPRequest(path, &response)
return response, i.SendHTTPRequest(exchange.RestSpot, path, &response)
}
// GetTradeHistory returns recent trades for a specified market.
@@ -68,9 +68,9 @@ func (i *ItBit) GetTradeHistory(currencyPair, tradeID string) (Trades, error) {
if tradeID != "" {
req += "?since=" + tradeID
}
path := fmt.Sprintf("%s/%s/%s/%s", i.API.Endpoints.URL, itbitMarkets, currencyPair, req)
path := fmt.Sprintf("/%s/%s/%s", itbitMarkets, currencyPair, req)
return response, i.SendHTTPRequest(path, &response)
return response, i.SendHTTPRequest(exchange.RestSpot, path, &response)
}
// GetWallets returns information about all wallets associated with the account.
@@ -82,7 +82,7 @@ func (i *ItBit) GetWallets(params url.Values) ([]Wallet, error) {
var resp []Wallet
params.Set("userId", i.API.Credentials.ClientID)
path := fmt.Sprintf("/%s?%s", itbitWallets, params.Encode())
return resp, i.SendAuthenticatedHTTPRequest(http.MethodGet, path, nil, &resp)
return resp, i.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, path, nil, &resp)
}
// CreateWallet creates a new wallet with a specified name.
@@ -92,7 +92,7 @@ func (i *ItBit) CreateWallet(walletName string) (Wallet, error) {
params["userId"] = i.API.Credentials.ClientID
params["name"] = walletName
err := i.SendAuthenticatedHTTPRequest(http.MethodPost, "/"+itbitWallets, params, &resp)
err := i.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, "/"+itbitWallets, params, &resp)
if err != nil {
return resp, err
}
@@ -107,7 +107,7 @@ func (i *ItBit) GetWallet(walletID string) (Wallet, error) {
resp := Wallet{}
path := fmt.Sprintf("/%s/%s", itbitWallets, walletID)
err := i.SendAuthenticatedHTTPRequest(http.MethodGet, path, nil, &resp)
err := i.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, path, nil, &resp)
if err != nil {
return resp, err
}
@@ -123,7 +123,7 @@ func (i *ItBit) GetWalletBalance(walletID, currency string) (Balance, error) {
resp := Balance{}
path := fmt.Sprintf("/%s/%s/%s/%s", itbitWallets, walletID, itbitBalances, currency)
err := i.SendAuthenticatedHTTPRequest(http.MethodGet, path, nil, &resp)
err := i.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, path, nil, &resp)
if err != nil {
return resp, err
}
@@ -153,7 +153,7 @@ func (i *ItBit) GetOrders(walletID, symbol, status string, page, perPage int64)
params["perPage"] = strconv.FormatInt(perPage, 10)
}
return resp, i.SendAuthenticatedHTTPRequest(http.MethodGet, itbitOrders, params, &resp)
return resp, i.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, itbitOrders, params, &resp)
}
// GetWalletTrades returns all trades for a specified wallet.
@@ -162,7 +162,7 @@ func (i *ItBit) GetWalletTrades(walletID string, params url.Values) (Records, er
urlPath := fmt.Sprintf("/%s/%s/%s", itbitWallets, walletID, itbitTrades)
path := common.EncodeURLValues(urlPath, params)
err := i.SendAuthenticatedHTTPRequest(http.MethodGet, path, nil, &resp)
err := i.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, path, nil, &resp)
if err != nil {
return resp, err
}
@@ -178,7 +178,7 @@ func (i *ItBit) GetFundingHistoryForWallet(walletID string, params url.Values) (
urlPath := fmt.Sprintf("/%s/%s/%s", itbitWallets, walletID, itbitFundingHistory)
path := common.EncodeURLValues(urlPath, params)
err := i.SendAuthenticatedHTTPRequest(http.MethodGet, path, nil, &resp)
err := i.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, path, nil, &resp)
if err != nil {
return resp, err
}
@@ -205,7 +205,7 @@ func (i *ItBit) PlaceOrder(walletID, side, orderType, currency string, amount, p
params["clientOrderIdentifier"] = clientRef
}
err := i.SendAuthenticatedHTTPRequest(http.MethodPost, path, params, &resp)
err := i.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, path, params, &resp)
if err != nil {
return resp, err
}
@@ -221,7 +221,7 @@ func (i *ItBit) GetOrder(walletID string, params url.Values) (Order, error) {
urlPath := fmt.Sprintf("/%s/%s/%s", itbitWallets, walletID, itbitOrders)
path := common.EncodeURLValues(urlPath, params)
err := i.SendAuthenticatedHTTPRequest(http.MethodGet, path, nil, &resp)
err := i.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodGet, path, nil, &resp)
if err != nil {
return resp, err
}
@@ -236,7 +236,7 @@ func (i *ItBit) GetOrder(walletID string, params url.Values) (Order, error) {
func (i *ItBit) CancelExistingOrder(walletID, orderID string) error {
path := fmt.Sprintf("/%s/%s/%s/%s", itbitWallets, walletID, itbitOrders, orderID)
return i.SendAuthenticatedHTTPRequest(http.MethodDelete, path, nil, nil)
return i.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodDelete, path, nil, nil)
}
// GetCryptoDepositAddress returns a deposit address to send cryptocurrency to.
@@ -246,7 +246,7 @@ func (i *ItBit) GetCryptoDepositAddress(walletID, currency string) (CryptoCurren
params := make(map[string]interface{})
params["currency"] = currency
err := i.SendAuthenticatedHTTPRequest(http.MethodPost, path, params, &resp)
err := i.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, path, params, &resp)
if err != nil {
return resp, err
}
@@ -267,7 +267,7 @@ func (i *ItBit) WalletTransfer(walletID, sourceWallet, destWallet string, amount
params["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
params["currencyCode"] = currency
err := i.SendAuthenticatedHTTPRequest(http.MethodPost, path, params, &resp)
err := i.SendAuthenticatedHTTPRequest(exchange.RestSpot, http.MethodPost, path, params, &resp)
if err != nil {
return resp, err
}
@@ -278,10 +278,14 @@ func (i *ItBit) WalletTransfer(walletID, sourceWallet, destWallet string, amount
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (i *ItBit) SendHTTPRequest(path string, result interface{}) error {
func (i *ItBit) SendHTTPRequest(ep exchange.URL, path string, result interface{}) error {
endpoint, err := i.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
return i.SendPayload(context.Background(), &request.Item{
Method: http.MethodGet,
Path: path,
Path: endpoint + path,
Result: result,
Verbose: i.Verbose,
HTTPDebugging: i.HTTPDebugging,
@@ -290,20 +294,22 @@ func (i *ItBit) SendHTTPRequest(path string, result interface{}) error {
}
// SendAuthenticatedHTTPRequest sends an authenticated request to itBit
func (i *ItBit) SendAuthenticatedHTTPRequest(method, path string, params map[string]interface{}, result interface{}) error {
func (i *ItBit) SendAuthenticatedHTTPRequest(ep exchange.URL, method, path string, params map[string]interface{}, result interface{}) error {
if !i.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, i.Name)
}
endpoint, err := i.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
req := make(map[string]interface{})
urlPath := i.API.Endpoints.URL + path
urlPath := endpoint + path
for key, value := range params {
req[key] = value
}
PayloadJSON := []byte("")
var err error
if params != nil {
PayloadJSON, err = json.Marshal(req)

View File

@@ -269,7 +269,8 @@ func TestFormatWithdrawPermissions(t *testing.T) {
func TestGetActiveOrders(t *testing.T) {
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := i.GetActiveOrders(&getOrdersRequest)
@@ -282,7 +283,8 @@ func TestGetActiveOrders(t *testing.T) {
func TestGetOrderHistory(t *testing.T) {
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := i.GetOrderHistory(&getOrdersRequest)
@@ -378,7 +380,7 @@ func TestCancelAllExchangeOrders(t *testing.T) {
func TestGetAccountInfo(t *testing.T) {
if areTestAPIKeysSet() {
_, err := i.UpdateAccountInfo()
_, err := i.UpdateAccountInfo(asset.Spot)
if err == nil {
t.Error("GetAccountInfo() Expected error")
}

View File

@@ -94,9 +94,13 @@ func (i *ItBit) SetDefaults() {
i.Requester = request.New(i.Name,
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout))
i.API.Endpoints.URLDefault = itbitAPIURL
i.API.Endpoints.URL = i.API.Endpoints.URLDefault
i.API.Endpoints = i.NewEndpoints()
err = i.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{
exchange.RestSpot: itbitAPIURL,
})
if err != nil {
log.Errorln(log.ExchangeSys, err)
}
}
// Setup sets the exchange parameters from exchange config
@@ -244,7 +248,7 @@ func (i *ItBit) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbo
}
// UpdateAccountInfo retrieves balances for all enabled currencies
func (i *ItBit) UpdateAccountInfo() (account.Holdings, error) {
func (i *ItBit) UpdateAccountInfo(assetType asset.Item) (account.Holdings, error) {
var info account.Holdings
info.Exchange = i.Name
@@ -293,10 +297,10 @@ func (i *ItBit) UpdateAccountInfo() (account.Holdings, error) {
}
// FetchAccountInfo retrieves balances for all enabled currencies
func (i *ItBit) FetchAccountInfo() (account.Holdings, error) {
acc, err := account.GetHoldings(i.Name)
func (i *ItBit) FetchAccountInfo(assetType asset.Item) (account.Holdings, error) {
acc, err := account.GetHoldings(i.Name, assetType)
if err != nil {
return i.UpdateAccountInfo()
return i.UpdateAccountInfo(assetType)
}
return acc, nil
@@ -625,8 +629,8 @@ func (i *ItBit) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, er
// ValidateCredentials validates current credentials used for wrapper
// functionality
func (i *ItBit) ValidateCredentials() error {
_, err := i.UpdateAccountInfo()
func (i *ItBit) ValidateCredentials(assetType asset.Item) error {
_, err := i.UpdateAccountInfo(assetType)
return i.CheckTransientError(err)
}

View File

@@ -0,0 +1,576 @@
package kraken
import (
"sync"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
)
var (
validOrderTypes = map[order.Type]string{
order.ImmediateOrCancel: "ioc",
order.Limit: "lmt",
order.Stop: "stp",
order.PostOnly: "post",
order.TakeProfit: "take_profit",
}
validSide = []string{"buy", "sell"}
validTriggerSignal = []string{"mark", "index", "last"}
validReduceOnly = []string{"true", "false"}
validBatchOrderType = []string{
"edit", "cancel", "send",
}
)
// WSFuturesTickerData stores ws ticker data for futures websocket
type WSFuturesTickerData struct {
Time int64 `json:"time"`
Feed string `json:"feed"`
ProductID string `json:"product_id"`
Bid float64 `json:"bid"`
Ask float64 `json:"ask"`
BidSize float64 `json:"bid_size"`
AskSize float64 `json:"ask_size"`
Volume float64 `json:"volume"`
DTM float64 `json:"dtm"`
Leverage string `json:"leverage"`
Index float64 `json:"index"`
Premium float64 `json:"premium"`
Last float64 `json:"last"`
Change float64 `json:"change"`
Suspended bool `json:"suspended"`
Tag string `json:"tag"`
Pair string `json:"pair"`
OpenInterest float64 `json:"openinterest"`
MarkPrice float64 `json:"markPrice"`
MaturityTime int64 `json:"maturityTime"`
FundingRate float64 `json:"funding_rate"`
FundingRatePrediction float64 `json:"funding_rate_prediction"`
RelativeFundingRate float64 `json:"relative_funding_rate"`
RelativeFundingRatePrediction float64 `json:"relative_funding_rate_prediction"`
NextFundingRateTime int64 `json:"next_funding_rate_time"`
}
// WsFuturesTradeData stores public trade data for futures websocket
type WsFuturesTradeData struct {
Feed string `json:"feed"`
ProductID string `json:"product_id"`
Trades []struct {
Feed string `json:"feed"`
ProductID string `json:"product_id"`
Side string `json:"side"`
ProductType string `json:"type"`
Seq int64 `json:"seq"`
Time int64 `json:"time"`
Quantity float64 `json:"qty"`
Price float64 `json:"price"`
} `json:"trades"`
}
// WsFuturesTickerLite stores ticker lite data for futures websocket
type WsFuturesTickerLite struct {
Feed string `json:"feed"`
ProductID string `json:"product_id"`
Bid float64 `json:"bid"`
Ask float64 `json:"ask"`
Change float64 `json:"change"`
Premium float64 `json:"premium"`
Volume float64 `json:"volume"`
Tag string `json:"tag"`
Pair string `json:"pair"`
DTM float64 `json:"dtm"`
}
// WsFuturesOB stores orderbook data for futures websocket
type WsFuturesOB struct {
Feed string `json:"feed"`
ProductID string `json:"product_id"`
Seq int64 `json:"seq"`
Bids []wsOBItem `json:"bids"`
Asks []wsOBItem `json:"asks"`
}
type wsOBItem struct {
Price float64 `json:"price"`
Quantity float64 `json:"qty"`
}
// WsVerboseOpenOrders stores verbose open orders data for futures websocket
type WsVerboseOpenOrders struct {
Feed string `json:"feed"`
Account string `json:"account"`
Orders []struct {
Instrument string `json:"instrument"`
Time int64 `json:"time"`
LastUpdateTime int64 `json:"last_update_time"`
Qty float64 `json:"qty"`
Filled float64 `json:"filled"`
LimitPrice float64 `json:"limit_price"`
StopPrice float64 `json:"stop_price"`
OrderType string `json:"type"`
OrderID string `json:"order_id"`
Direction int64 `json:"direction"`
ReduceOnly bool `json:"reduce_only"`
} `json:"orders"`
}
// WsOpenPositions stores open positions data for futures websocket
type WsOpenPositions struct {
Feed string `json:"feed"`
Account string `json:"account"`
Positions []struct {
Instrument string `json:"instrument"`
Balance float64 `json:"balance"`
EntryPrice float64 `json:"entry_price"`
MarkPrice float64 `json:"mark_price"`
IndexPrice float64 `json:"index_price"`
ProfitAndLoss float64 `json:"pnl"`
} `json:"positions"`
}
// WsFuturesAccountLog stores account log data for futures websocket
type WsFuturesAccountLog struct {
Feed string `json:"feed"`
Logs []struct {
ID int64 `json:"id"`
Date string `json:"date"`
Asset string `json:"asset"`
Info string `json:"info"`
BookingUID string `json:"booking_uid"`
MarginAccount string `json:"margin_account"`
OldBalance float64 `json:"old_balance"`
NewBalance float64 `json:"new_balance"`
OldAverageEntry float64 `json:"old_average_entry"`
NewAverageEntry float64 `json:"new_average_entry"`
TradePrice float64 `json:"trade_price"`
MarkPrice float64 `json:"mark_price"`
RealizedPNL float64 `json:"realized_pnl"`
Fee float64 `json:"fee"`
Execution string `json:"execution"`
Collateral string `json:"collateral"`
FundingRate float64 `json:"funding_rate"`
RealizedFunding float64 `json:"realized_funding"`
} `json:"logs"`
}
// WsFuturesFillsData stores fills data for futures websocket
type WsFuturesFillsData struct {
Feed string `json:"feed"`
Account string `json:"account"`
Fills []struct {
Instrument string `json:"instrument"`
Time int64 `json:"time"`
Price float64 `json:"price"`
Seq int64 `json:"seq"`
Buy bool `json:"buy"`
Quantity float64 `json:"qty"`
OrderID string `json:"order_id"`
ClientOrderID string `json:"cli_order_id"`
FillID string `json:"fill_id"`
FillType string `json:"fill_type"`
} `json:"fills"`
}
// WsFuturesOpenOrders stores open orders data for futures websocket
type WsFuturesOpenOrders struct {
Feed string `json:"feed"`
Account string `json:"account"`
Orders []struct {
Instrument string `json:"instrument"`
Time int64 `json:"time"`
LastUpdateTime int64 `json:"last_update_time"`
Qty float64 `json:"qty"`
Filled float64 `json:"filled"`
LimitPrice float64 `json:"limit_price"`
StopPrice float64 `json:"stop_price"`
OrderType string `json:"order_type"`
OrderID string `json:"order_id"`
Direction string `json:"direction"`
ReduceOnly bool `json:"reduce_only"`
} `json:"orders"`
}
// WsAccountBalancesAndMargin stores account balances and margin data for futures websocket
type WsAccountBalancesAndMargin struct {
Seq int64 `json:"seq"`
Feed string `json:"feed"`
Account string `json:"account"`
MarginAccounts []struct {
Name string `json:"name"`
PortfolioValue float64 `json:"pv"`
Balance float64 `json:"balance"`
Funding float64 `json:"funding"`
MaintenanceMargin float64 `json:"mm"`
ProfitAndLoss float64 `json:"pnl"`
InitialMargin float64 `json:"im"`
AM float64 `json:"am"`
} `json:"margin_accounts"`
}
// WsFuturesNotifications stores notifications data for futures websocket
type WsFuturesNotifications struct {
Feed string `json:"feed"`
Notifications []struct {
ID int64 `json:"id"`
NotificationType string `json:"notificationType"`
Priority string `json:"priority"`
Note string `json:"note"`
EffectiveTime int64 `json:"effective_time"`
}
}
type assetTranslatorStore struct {
l sync.RWMutex
Assets map[string]string
}
// FuturesOrderbookData stores orderbook data for futures
type FuturesOrderbookData struct {
ServerTime string `json:"serverTime"`
Orderbook struct {
Bids [][2]float64 `json:"bids"`
Asks [][2]float64 `json:"asks"`
} `json:"orderBook"`
}
// TimeResponse type
type TimeResponse struct {
Unixtime int64 `json:"unixtime"`
Rfc1123 string `json:"rfc1123"`
}
// FuturesInstrumentData stores info for futures market
type FuturesInstrumentData struct {
Instruments []struct {
Symbol string `json:"symbol"`
FutureType string `json:"type"`
Underlying string `json:"underlying"`
LastTradingTime string `json:"lastTradingTime"`
TickSize float64 `json:"tickSize"`
ContractSize float64 `json:"contractSize"`
Tradable bool `json:"tradeable"`
MarginLevels []struct {
Contracts float64 `json:"contracts"`
InitialMargin float64 `json:"initialMargin"`
MaintenanceMargin float64 `json:"maintenanceMargin"`
} `json:"marginLevels"`
} `json:"instruments"`
}
// FuturesTradeHistoryData stores trade history data for futures
type FuturesTradeHistoryData struct {
History []struct {
Time string `json:"time"`
TradeID int64 `json:"trade_id"`
Price float64 `json:"price"`
Size float64 `json:"size"`
Side string `json:"side"`
TradeType string `json:"type"`
} `json:"history"`
}
// FuturesTickerData stores info for futures ticker
type FuturesTickerData struct {
Tickers []struct {
Tag string `json:"tag"`
Pair string `json:"pair"`
Symbol string `json:"symbol"`
MarkPrice float64 `json:"markPrice"`
Bid float64 `json:"bid"`
BidSize float64 `json:"bidSize"`
Ask float64 `json:"ask"`
AskSize float64 `json:"askSize"`
Vol24h float64 `json:"vol24h"`
OpenInterest float64 `json:"openInterest"`
Open24H float64 `json:"open24h"`
Last float64 `json:"last"`
LastTime string `json:"lastTime"`
LastSize float64 `json:"lastSize"`
Suspended bool `json:"suspended"`
FundingRate float64 `json:"fundingRate"`
FundingRatePrediction float64 `json:"fundingRatePrediction"`
} `json:"tickers"`
ServerTime string `json:"serverTime"`
}
// FuturesEditedOrderData stores an edited order's data
type FuturesEditedOrderData struct {
ServerTime string `json:"serverTime"`
EditStatus struct {
Status string `json:"status"`
OrderID string `json:"orderId"`
ReceivedTime string `json:"receivedTime"`
OrderEvents []struct {
Old FuturesOrderData `json:"old"`
New FuturesOrderData `json:"new"`
} `json:"orderEvents"`
ReduceQuantity string `json:"reduceQuantity"`
DataType string `json:"type"`
} `json:"editStatus"`
}
// FuturesSendOrderData stores send order data
type FuturesSendOrderData struct {
SendStatus struct {
OrderID string `json:"orderId"`
Status string `json:"status"`
ReceivedTime string `json:"receivedTime"`
OrderEvents []struct {
UID string `json:"uid"`
Order FuturesOrderData `json:"order"`
Reason string `json:"reason"`
DataType string `json:"type"`
} `json:"orderEvents"`
} `json:"sendStatus"`
ServerTime string `json:"serverTime"`
}
// FuturesOrderData stores order data
type FuturesOrderData struct {
OrderID string `json:"orderId"`
ClientOrderID string `json:"cliOrderId"`
OrderType string `json:"type"`
Symbol string `json:"symbol"`
Side string `json:"side"`
Quantity float64 `json:"quantity"`
Filled float64 `json:"filled"`
LimitPrice float64 `json:"limitPrice"`
ReduceOnly bool `json:"reduceOnly"`
Timestamp string `json:"timestamp"`
LastUpdateTimestamp string `json:"lastUpdateTimestamp"`
}
// FuturesCancelOrderData stores cancel order data for futures
type FuturesCancelOrderData struct {
CancelStatus struct {
Status string `json:"status"`
OrderID string `json:"order_id"`
ReceivedTime string `json:"receivedTime"`
OrderEvents []struct {
UID string `json:"uid"`
Order FuturesOrderData `json:"order"`
DataType string `json:"type"`
} `json:"orderEvents"`
} `json:"cancelStatus"`
ServerTime string `json:"serverTime"`
}
// FuturesFillsData stores fills data
type FuturesFillsData struct {
Fills []struct {
FillID string `json:"fill_id"`
Symbol string `json:"symbol"`
Side string `json:"buy"`
OrderID string `json:"order_id"`
Size float64 `json:"size"`
Price float64 `json:"price"`
FillTime string `json:"fillTime"`
FillType string `json:"fillType"`
} `json:"fills"`
ServerTime string `json:"serverTime"`
}
// FuturesTransferData stores transfer data
type FuturesTransferData struct {
Result string `json:"result"`
ServerTime string `json:"serverTime"`
}
// FuturesOpenPositions stores open positions data for futures
type FuturesOpenPositions struct {
OpenPositions []struct {
Side string `json:"side"`
Symbol string `json:"symbol"`
Price float64 `json:"price"`
FillTime string `json:"fillTime"`
Size float64 `json:"size"`
UnrealizedFunding float64 `json:"unrealizedFunding"`
} `json:"openPositions"`
ServerTime string `json:"serverTime"`
}
// FuturesNotificationData stores notification data
type FuturesNotificationData struct {
Notifications []struct {
NotificationType string `json:"type"`
Priority string `json:"priority"`
Note string `json:"note"`
EffectiveTime string `json:"effectiveTime"`
} `json:"notifcations"`
ServerTime string `json:"serverTime"`
}
// FuturesAccountsData stores account data
type FuturesAccountsData struct {
ServerTime string `json:"serverTime"`
Accounts map[string]AccountsData `json:"accounts"`
}
// AccountsData stores data of an account
type AccountsData struct {
AccType string `json:"type"`
Currency string `json:"currency"`
Balances map[string]float64 `json:"balances"`
Auxiliary struct {
AvailableFunds float64 `json:"af"`
ProfitAndLoss float64 `json:"pnl"`
PortfolioValue float64 `json:"pv"`
} `json:"auxiliary"`
MarginRequirements struct {
InitialMargin float64 `json:"im"`
MaintenanceMargin float64 `json:"mm"`
LiquidationThreshold float64 `json:"lt"`
TerminationThreshold float64 `json:"tt"`
} `json:"marginRequirements"`
TriggerEstimates struct {
InitialMargin float64 `json:"im"`
MaintenanceMargin float64 `json:"mm"`
LiquidationThreshold float64 `json:"lt"`
TerminationThreshold float64 `json:"tt"`
} `json:"triggerEstimates"`
}
// CancelAllOrdersData stores order data for all cancelled orders
type CancelAllOrdersData struct {
CancelStatus struct {
ReceivedTime string `json:"receivedTime"`
CancelOnly string `json:"cancelOnly"`
Status string `json:"status"`
CancelledOrders []struct {
OrderID string `json:"order_id"`
} `json:"cancelledOrders"`
OrderEvents []struct {
UID string `json:"uid"`
} `json:"uid"`
Order FuturesOrderData `json:"order"`
DataType string `json:"type"`
} `json:"cancelStatus"`
ServerTime string `json:"serverTime"`
}
// CancelOrdersAfterData stores data of all orders after a certain time that are cancelled
type CancelOrdersAfterData struct {
Result string `json:"result"`
Status struct {
CurrentTime string `json:"currentTime"`
TriggerTime string `json:"triggerTime"`
} `json:"status"`
ServerTime string `json:"serverTime"`
}
// RecentOrderData stores order data of a recent order
type RecentOrderData struct {
UID string `json:"uid"`
AccountID string `json:"accountId"`
Tradeable string `json:"tradeable"`
Direction string `json:"direction"`
Quantity float64 `json:"quantity,string"`
Filled float64 `json:"filled,string"`
Timestamp string `json:"timestamp"`
LimitPrice float64 `json:"limitPrice,string"`
OrderType string `json:"orderType"`
ClientID string `json:"clientId"`
StopPrice float64 `json:"stopPrice,string"`
}
// FOpenOrdersData stores open orders data for futures
type FOpenOrdersData struct {
OrderID string `json:"order_id"`
ClientOrderID string `json:"cliOrdId"`
Symbol string `json:"symbol"`
Side string `json:"side"`
OrderType string `json:"orderType"`
LimitPrice float64 `json:"limitPrice"`
StopPrice float64 `json:"stopPrice"`
UnfilledSize float64 `json:"unfilledSize"`
ReceivedTime string `json:"receivedTime"`
Status string `json:"status"`
FilledSize float64 `json:"filledSize"`
ReduceOnly bool `json:"reduceOnly"`
TriggerSignal string `json:"triggerSignal"`
LastUpdateTime string `json:"lastUpdateTime"`
}
// FuturesRecentOrdersData stores recent orders data
type FuturesRecentOrdersData struct {
OrderEvents []struct {
Timestamp int64 `json:"timestamp"`
Event struct {
Timestamp string `json:"timestamp"`
UID string `json:"uid"`
OrderPlaced struct {
RecentOrder RecentOrderData `json:"order"`
Reason string `json:"reason"`
} `json:"orderPlaced"`
OrderCancelled struct {
RecentOrder RecentOrderData `json:"order"`
Reason string `json:"reason"`
} `json:"orderCancelled"`
OrderRejected struct {
RecentOrder RecentOrderData `json:"order"`
Reason string `json:"reason"`
} `json:"orderRejected"`
ExecutionEvent struct {
Execution ExecutionData `json:"execution"`
Timestamp string `json:"timestamp"`
Quantity float64 `json:"quantity"`
Price float64 `json:"price"`
MarkPrice float64 `json:"markPrice"`
LimitFilled bool `json:"limitFilled"`
} `json:"executionEvent"`
} `json:"event"`
} `json:"orderEvents"`
}
// BatchOrderData stores batch order data
type BatchOrderData struct {
Result bool `json:"result"`
ServerTime string `json:"serverTime"`
BatchStatus []struct {
Status string `json:"status"`
OrderTag string `json:"order_tag"`
OrderID string `json:"order_id"`
DateTimeReceived string `json:"dateTimeReceieved"`
OrderEvents []struct {
OrderPlaced FuturesOrderData `json:"orderPlaced"`
ReduceOnly bool `json:"reduceOnly"`
Timestamp string `json:"timestamp"`
OldEditedOrder FuturesOrderData `json:"old"`
NewEditedOrder FuturesOrderData `json:"new"`
UID string `json:"uid"`
RequestType string `json:"requestType"`
} `json:"orderEvents"`
} `json:"batchStatus"`
}
// PlaceBatchOrderData stores data required to place a batch order
type PlaceBatchOrderData struct {
PlaceOrderType string `json:"order,omitempty"`
OrderType string `json:"orderType,omitempty"`
OrderTag string `json:"order_tag,omitempty"`
Symbol string `json:"symbol,omitempty"`
Side string `json:"side,omitempty"`
Size float64 `json:"size,omitempty"`
LimitPrice float64 `json:"limitPrice,omitempty"`
StopPrice float64 `json:"stopPrice,omitempty"`
ClientOrderID int64 `json:"cliOrdId,omitempty"`
ReduceOnly string `json:"reduceOnly,omitempty"`
OrderID string `json:"order_id,omitempty"`
}
// ExecutionData stores execution data
type ExecutionData struct {
UID string `json:"uid"`
TakerOrder RecentOrderData `json:"takerOrder"`
}
// FuturesOpenOrdersData stores open orders data for futures
type FuturesOpenOrdersData struct {
Result string `json:"result"`
OpenOrders []FOpenOrdersData `json:"openOrders"`
ServerTime string `json:"serverTime"`
}

View File

@@ -2,6 +2,7 @@ package kraken
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
@@ -15,53 +16,18 @@ import (
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
"github.com/thrasher-corp/gocryptotrader/log"
)
const (
krakenAPIURL = "https://api.kraken.com"
krakenAPIVersion = "0"
krakenServerTime = "Time"
krakenAssets = "Assets"
krakenAssetPairs = "AssetPairs"
krakenTicker = "Ticker"
krakenOHLC = "OHLC"
krakenDepth = "Depth"
krakenTrades = "Trades"
krakenSpread = "Spread"
krakenBalance = "Balance"
krakenTradeBalance = "TradeBalance"
krakenOpenOrders = "OpenOrders"
krakenClosedOrders = "ClosedOrders"
krakenQueryOrders = "QueryOrders"
krakenTradeHistory = "TradesHistory"
krakenQueryTrades = "QueryTrades"
krakenOpenPositions = "OpenPositions"
krakenLedgers = "Ledgers"
krakenQueryLedgers = "QueryLedgers"
krakenTradeVolume = "TradeVolume"
krakenOrderCancel = "CancelOrder"
krakenOrderPlace = "AddOrder"
krakenWithdrawInfo = "WithdrawInfo"
krakenWithdraw = "Withdraw"
krakenDepositMethods = "DepositMethods"
krakenDepositAddresses = "DepositAddresses"
krakenWithdrawStatus = "WithdrawStatus"
krakenWithdrawCancel = "WithdrawCancel"
krakenWebsocketToken = "GetWebSocketsToken"
// Rate limit consts
krakenRateInterval = time.Second
krakenRequestRate = 1
// Status consts
statusOpen = "open"
)
var (
assetTranslator assetTranslatorStore
krakenAPIURL = "https://api.kraken.com"
krakenFuturesURL = "https://futures.kraken.com"
futuresURL = "https://futures.kraken.com/derivatives"
krakenSpotVersion = "0"
krakenFuturesVersion = "3"
)
// Kraken is the overarching type across the alphapoint package
@@ -72,14 +38,14 @@ type Kraken struct {
// GetServerTime returns current server time
func (k *Kraken) GetServerTime() (TimeResponse, error) {
path := fmt.Sprintf("%s/%s/public/%s", k.API.Endpoints.URL, krakenAPIVersion, krakenServerTime)
path := fmt.Sprintf("/%s/public/%s", krakenAPIVersion, krakenServerTime)
var response struct {
Error []string `json:"error"`
Result TimeResponse `json:"result"`
}
if err := k.SendHTTPRequest(path, &response); err != nil {
if err := k.SendHTTPRequest(exchange.RestSpot, path, &response); err != nil {
return response.Result, err
}
@@ -93,55 +59,70 @@ func (k *Kraken) SeedAssets() error {
if err != nil {
return err
}
for k, v := range assets {
assetTranslator.Seed(k, v.Altname)
for orig, val := range assets {
assetTranslator.Seed(orig, val.Altname)
}
assetPairs, err := k.GetAssetPairs()
assetPairs, err := k.GetAssetPairs([]string{}, "")
if err != nil {
return err
}
for k, v := range assetPairs {
assetTranslator.Seed(k, v.Altname)
for k := range assetPairs {
assetTranslator.Seed(k, assetPairs[k].Altname)
}
return nil
}
// GetAssets returns a full asset list
func (k *Kraken) GetAssets() (map[string]*Asset, error) {
path := fmt.Sprintf("%s/%s/public/%s", k.API.Endpoints.URL, krakenAPIVersion, krakenAssets)
path := fmt.Sprintf("/%s/public/%s", krakenAPIVersion, krakenAssets)
var response struct {
Error []string `json:"error"`
Result map[string]*Asset `json:"result"`
}
if err := k.SendHTTPRequest(path, &response); err != nil {
if err := k.SendHTTPRequest(exchange.RestSpot, path, &response); err != nil {
return response.Result, err
}
return response.Result, GetError(response.Error)
}
// GetAssetPairs returns a full asset pair list
func (k *Kraken) GetAssetPairs() (map[string]*AssetPairs, error) {
path := fmt.Sprintf("%s/%s/public/%s", k.API.Endpoints.URL, krakenAPIVersion, krakenAssetPairs)
var response struct {
Error []string `json:"error"`
Result map[string]*AssetPairs `json:"result"`
// Parameter 'info' only supports 4 strings: "fees", "leverage", "margin", "info" <- (default)
func (k *Kraken) GetAssetPairs(assetPairs []string, info string) (map[string]AssetPairs, error) {
path := fmt.Sprintf("/%s/public/%s", krakenAPIVersion, krakenAssetPairs)
params := url.Values{}
var assets string
if len(assetPairs) != 0 {
assets = strings.Join(assetPairs, ",")
params.Set("pair", assets)
}
if err := k.SendHTTPRequest(path, &response); err != nil {
var response struct {
Error []string `json:"error"`
Result map[string]AssetPairs `json:"result"`
}
if info != "" {
if info != "margin" && info != "leverage" && info != "fees" && info != "info" {
return response.Result, errors.New("parameter info can only be 'asset', 'margin', 'fees' or 'leverage'")
}
params.Set("info", info)
}
if err := k.SendHTTPRequest(exchange.RestSpot, path+params.Encode(), &response); err != nil {
return response.Result, err
}
return response.Result, GetError(response.Error)
}
// GetTicker returns ticker information from kraken
func (k *Kraken) GetTicker(symbol string) (Ticker, error) {
func (k *Kraken) GetTicker(symbol currency.Pair) (Ticker, error) {
tick := Ticker{}
values := url.Values{}
values.Set("pair", symbol)
symbolValue, err := k.FormatSymbol(symbol, asset.Spot)
if err != nil {
return tick, err
}
values.Set("pair", symbolValue)
type Response struct {
Error []interface{} `json:"error"`
@@ -149,9 +130,9 @@ func (k *Kraken) GetTicker(symbol string) (Ticker, error) {
}
resp := Response{}
path := fmt.Sprintf("%s/%s/public/%s?%s", k.API.Endpoints.URL, krakenAPIVersion, krakenTicker, values.Encode())
path := fmt.Sprintf("/%s/public/%s?%s", krakenAPIVersion, krakenTicker, values.Encode())
err := k.SendHTTPRequest(path, &resp)
err = k.SendHTTPRequest(exchange.RestSpot, path, &resp)
if err != nil {
return tick, err
}
@@ -187,9 +168,9 @@ func (k *Kraken) GetTickers(pairList string) (map[string]Ticker, error) {
}
resp := Response{}
path := fmt.Sprintf("%s/%s/public/%s?%s", k.API.Endpoints.URL, krakenAPIVersion, krakenTicker, values.Encode())
path := fmt.Sprintf("/%s/public/%s?%s", krakenAPIVersion, krakenTicker, values.Encode())
err := k.SendHTTPRequest(path, &resp)
err := k.SendHTTPRequest(exchange.RestSpot, path, &resp)
if err != nil {
return nil, err
}
@@ -217,9 +198,17 @@ func (k *Kraken) GetTickers(pairList string) (map[string]Ticker, error) {
}
// GetOHLC returns an array of open high low close values of a currency pair
func (k *Kraken) GetOHLC(symbol, interval string) ([]OpenHighLowClose, error) {
func (k *Kraken) GetOHLC(symbol currency.Pair, interval string) ([]OpenHighLowClose, error) {
values := url.Values{}
values.Set("pair", symbol)
symbolValue, err := k.FormatSymbol(symbol, asset.Spot)
if err != nil {
return nil, err
}
translatedAsset := assetTranslator.LookupCurrency(symbolValue)
if translatedAsset == "" {
translatedAsset = symbolValue
}
values.Set("pair", translatedAsset)
values.Set("interval", interval)
type Response struct {
Error []interface{} `json:"error"`
@@ -229,9 +218,9 @@ func (k *Kraken) GetOHLC(symbol, interval string) ([]OpenHighLowClose, error) {
var OHLC []OpenHighLowClose
var result Response
path := fmt.Sprintf("%s/%s/public/%s?%s", k.API.Endpoints.URL, krakenAPIVersion, krakenOHLC, values.Encode())
path := fmt.Sprintf("/%s/public/%s?%s", krakenAPIVersion, krakenOHLC, values.Encode())
err := k.SendHTTPRequest(path, &result)
err = k.SendHTTPRequest(exchange.RestSpot, path, &result)
if err != nil {
return OHLC, err
}
@@ -240,12 +229,12 @@ func (k *Kraken) GetOHLC(symbol, interval string) ([]OpenHighLowClose, error) {
return OHLC, fmt.Errorf("getOHLC error: %s", result.Error)
}
_, ok := result.Data[symbol].([]interface{})
_, ok := result.Data[translatedAsset].([]interface{})
if !ok {
return nil, errors.New("invalid data returned")
}
for _, y := range result.Data[symbol].([]interface{}) {
for _, y := range result.Data[translatedAsset].([]interface{}) {
o := OpenHighLowClose{}
for i, x := range y.([]interface{}) {
switch i {
@@ -273,15 +262,17 @@ func (k *Kraken) GetOHLC(symbol, interval string) ([]OpenHighLowClose, error) {
}
// GetDepth returns the orderbook for a particular currency
func (k *Kraken) GetDepth(symbol string) (Orderbook, error) {
values := url.Values{}
values.Set("pair", symbol)
func (k *Kraken) GetDepth(symbol currency.Pair) (Orderbook, error) {
var result interface{}
var orderBook Orderbook
path := fmt.Sprintf("%s/%s/public/%s?%s", k.API.Endpoints.URL, krakenAPIVersion, krakenDepth, values.Encode())
err := k.SendHTTPRequest(path, &result)
values := url.Values{}
symbolValue, err := k.FormatSymbol(symbol, asset.Spot)
if err != nil {
return orderBook, err
}
values.Set("pair", symbolValue)
path := fmt.Sprintf("/%s/public/%s?%s", krakenAPIVersion, krakenDepth, values.Encode())
err = k.SendHTTPRequest(exchange.RestSpot, path, &result)
if err != nil {
return orderBook, err
}
@@ -334,16 +325,21 @@ func (k *Kraken) GetDepth(symbol string) (Orderbook, error) {
}
// GetTrades returns current trades on Kraken
func (k *Kraken) GetTrades(symbol string) ([]RecentTrades, error) {
func (k *Kraken) GetTrades(symbol currency.Pair) ([]RecentTrades, error) {
values := url.Values{}
values.Set("pair", symbol)
symbolValue, err := k.FormatSymbol(symbol, asset.Spot)
if err != nil {
return nil, err
}
translatedAsset := assetTranslator.LookupCurrency(symbolValue)
values.Set("pair", translatedAsset)
var recentTrades []RecentTrades
var result interface{}
path := fmt.Sprintf("%s/%s/public/%s?%s", k.API.Endpoints.URL, krakenAPIVersion, krakenTrades, values.Encode())
path := fmt.Sprintf("/%s/public/%s?%s", krakenAPIVersion, krakenTrades, values.Encode())
err := k.SendHTTPRequest(path, &result)
err = k.SendHTTPRequest(exchange.RestSpot, path, &result)
if err != nil {
return nil, err
}
@@ -390,7 +386,7 @@ func (k *Kraken) GetTrades(symbol string) ([]RecentTrades, error) {
var trades []interface{}
var tradesForSymbol interface{}
tradesForSymbol, ok = tradeInfo[symbol]
tradesForSymbol, ok = tradeInfo[translatedAsset]
if !ok {
return nil, fmt.Errorf("no data returned for symbol %v", symbol)
}
@@ -440,16 +436,20 @@ func (k *Kraken) GetTrades(symbol string) ([]RecentTrades, error) {
}
// GetSpread returns the full spread on Kraken
func (k *Kraken) GetSpread(symbol string) ([]Spread, error) {
func (k *Kraken) GetSpread(symbol currency.Pair) ([]Spread, error) {
values := url.Values{}
values.Set("pair", symbol)
symbolValue, err := k.FormatSymbol(symbol, asset.Spot)
if err != nil {
return nil, err
}
values.Set("pair", symbolValue)
var peanutButter []Spread
var response interface{}
path := fmt.Sprintf("%s/%s/public/%s?%s", k.API.Endpoints.URL, krakenAPIVersion, krakenSpread, values.Encode())
path := fmt.Sprintf("/%s/public/%s?%s", krakenAPIVersion, krakenSpread, values.Encode())
err := k.SendHTTPRequest(path, &response)
err = k.SendHTTPRequest(exchange.RestSpot, path, &response)
if err != nil {
return peanutButter, err
}
@@ -457,7 +457,7 @@ func (k *Kraken) GetSpread(symbol string) ([]Spread, error) {
data := response.(map[string]interface{})
result := data["result"].(map[string]interface{})
for _, x := range result[symbol].([]interface{}) {
for _, x := range result[symbolValue].([]interface{}) {
s := Spread{}
for i, y := range x.([]interface{}) {
switch i {
@@ -481,7 +481,7 @@ func (k *Kraken) GetBalance() (map[string]float64, error) {
Result map[string]string `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenBalance, url.Values{}, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenBalance, url.Values{}, &response); err != nil {
return nil, err
}
@@ -507,7 +507,7 @@ func (k *Kraken) GetWithdrawInfo(currency string, amount float64) (WithdrawInfor
params.Set("key", "")
params.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
if err := k.SendAuthenticatedHTTPRequest(krakenWithdrawInfo, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenWithdrawInfo, params, &response); err != nil {
return response.Result, err
}
@@ -525,7 +525,7 @@ func (k *Kraken) Withdraw(asset, key string, amount float64) (string, error) {
params.Set("key", key)
params.Set("amount", fmt.Sprintf("%f", amount))
if err := k.SendAuthenticatedHTTPRequest(krakenWithdraw, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenWithdraw, params, &response); err != nil {
return response.ReferenceID, err
}
@@ -541,7 +541,7 @@ func (k *Kraken) GetDepositMethods(currency string) ([]DepositMethods, error) {
params := url.Values{}
params.Set("asset", currency)
err := k.SendAuthenticatedHTTPRequest(krakenDepositMethods, params, &response)
err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenDepositMethods, params, &response)
if err != nil {
return response.Result, err
}
@@ -568,7 +568,7 @@ func (k *Kraken) GetTradeBalance(args ...TradeBalanceOptions) (TradeBalanceInfo,
Result TradeBalanceInfo `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenTradeBalance, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenTradeBalance, params, &response); err != nil {
return response.Result, err
}
@@ -592,7 +592,7 @@ func (k *Kraken) GetOpenOrders(args OrderInfoOptions) (OpenOrders, error) {
Result OpenOrders `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenOpenOrders, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenOpenOrders, params, &response); err != nil {
return response.Result, err
}
@@ -632,7 +632,7 @@ func (k *Kraken) GetClosedOrders(args GetClosedOrdersOptions) (ClosedOrders, err
Result ClosedOrders `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenClosedOrders, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenClosedOrders, params, &response); err != nil {
return response.Result, err
}
@@ -662,7 +662,7 @@ func (k *Kraken) QueryOrdersInfo(args OrderInfoOptions, txid string, txids ...st
Result map[string]OrderInfo `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenQueryOrders, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenQueryOrders, params, &response); err != nil {
return response.Result, err
}
@@ -700,7 +700,7 @@ func (k *Kraken) GetTradesHistory(args ...GetTradesHistoryOptions) (TradesHistor
Result TradesHistory `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenTradeHistory, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenTradeHistory, params, &response); err != nil {
return response.Result, err
}
@@ -726,7 +726,7 @@ func (k *Kraken) QueryTrades(trades bool, txid string, txids ...string) (map[str
Result map[string]TradeInfo `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenQueryTrades, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenQueryTrades, params, &response); err != nil {
return response.Result, err
}
@@ -750,7 +750,7 @@ func (k *Kraken) OpenPositions(docalcs bool, txids ...string) (map[string]Positi
Result map[string]Position `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenOpenPositions, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenOpenPositions, params, &response); err != nil {
return response.Result, err
}
@@ -792,7 +792,7 @@ func (k *Kraken) GetLedgers(args ...GetLedgersOptions) (Ledgers, error) {
Result Ledgers `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenLedgers, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenLedgers, params, &response); err != nil {
return response.Result, err
}
@@ -814,7 +814,7 @@ func (k *Kraken) QueryLedgers(id string, ids ...string) (map[string]LedgerInfo,
Result map[string]LedgerInfo `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenQueryLedgers, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenQueryLedgers, params, &response); err != nil {
return response.Result, err
}
@@ -822,23 +822,29 @@ func (k *Kraken) QueryLedgers(id string, ids ...string) (map[string]LedgerInfo,
}
// GetTradeVolume returns your trade volume by currency
func (k *Kraken) GetTradeVolume(feeinfo bool, symbol ...string) (TradeVolumeResponse, error) {
func (k *Kraken) GetTradeVolume(feeinfo bool, symbol ...currency.Pair) (TradeVolumeResponse, error) {
var response struct {
Error []string `json:"error"`
Result TradeVolumeResponse `json:"result"`
}
params := url.Values{}
var formattedPairs []string
for x := range symbol {
symbolValue, err := k.FormatSymbol(symbol[x], asset.Spot)
if err != nil {
return response.Result, err
}
formattedPairs = append(formattedPairs, symbolValue)
}
if symbol != nil {
params.Set("pair", strings.Join(symbol, ","))
params.Set("pair", strings.Join(formattedPairs, ","))
}
if feeinfo {
params.Set("fee-info", "true")
}
var response struct {
Error []string `json:"error"`
Result TradeVolumeResponse `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenTradeVolume, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenTradeVolume, params, &response); err != nil {
return response.Result, err
}
@@ -846,9 +852,17 @@ func (k *Kraken) GetTradeVolume(feeinfo bool, symbol ...string) (TradeVolumeResp
}
// AddOrder adds a new order for Kraken exchange
func (k *Kraken) AddOrder(symbol, side, orderType string, volume, price, price2, leverage float64, args *AddOrderOptions) (AddOrderResponse, error) {
func (k *Kraken) AddOrder(symbol currency.Pair, side, orderType string, volume, price, price2, leverage float64, args *AddOrderOptions) (AddOrderResponse, error) {
var response struct {
Error []string `json:"error"`
Result AddOrderResponse `json:"result"`
}
symbolValue, err := k.FormatSymbol(symbol, asset.Spot)
if err != nil {
return response.Result, err
}
params := url.Values{
"pair": {symbol},
"pair": {symbolValue},
"type": {strings.ToLower(side)},
"ordertype": {strings.ToLower(orderType)},
"volume": {strconv.FormatFloat(volume, 'f', -1, 64)},
@@ -894,12 +908,7 @@ func (k *Kraken) AddOrder(symbol, side, orderType string, volume, price, price2,
params.Set("validate", "true")
}
var response struct {
Error []string `json:"error"`
Result AddOrderResponse `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenOrderPlace, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenOrderPlace, params, &response); err != nil {
return response.Result, err
}
@@ -917,7 +926,7 @@ func (k *Kraken) CancelExistingOrder(txid string) (CancelOrderResponse, error) {
Result CancelOrderResponse `json:"result"`
}
if err := k.SendAuthenticatedHTTPRequest(krakenOrderCancel, values, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenOrderCancel, values, &response); err != nil {
return response.Result, err
}
@@ -944,10 +953,14 @@ func GetError(apiErrors []string) error {
}
// SendHTTPRequest sends an unauthenticated HTTP requests
func (k *Kraken) SendHTTPRequest(path string, result interface{}) error {
func (k *Kraken) SendHTTPRequest(ep exchange.URL, path string, result interface{}) error {
endpoint, err := k.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
return k.SendPayload(context.Background(), &request.Item{
Method: http.MethodGet,
Path: path,
Path: endpoint + path,
Result: result,
Verbose: k.Verbose,
HTTPDebugging: k.HTTPDebugging,
@@ -956,12 +969,15 @@ func (k *Kraken) SendHTTPRequest(path string, result interface{}) error {
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request
func (k *Kraken) SendAuthenticatedHTTPRequest(method string, params url.Values, result interface{}) (err error) {
func (k *Kraken) SendAuthenticatedHTTPRequest(ep exchange.URL, method string, params url.Values, result interface{}) error {
if !k.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet,
k.Name)
}
endpoint, err := k.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
path := fmt.Sprintf("/%s/private/%s", krakenAPIVersion, method)
params.Set("nonce", k.Requester.GetNonce(true).String())
@@ -972,7 +988,7 @@ func (k *Kraken) SendAuthenticatedHTTPRequest(method string, params url.Values,
if k.Verbose {
log.Debugf(log.ExchangeSys, "Sending POST request to %s, path: %s, params: %s",
k.API.Endpoints.URL,
endpoint,
path,
encoded)
}
@@ -981,40 +997,49 @@ func (k *Kraken) SendAuthenticatedHTTPRequest(method string, params url.Values,
headers["API-Key"] = k.API.Credentials.Key
headers["API-Sign"] = signature
return k.SendPayload(context.Background(), &request.Item{
interim := json.RawMessage{}
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Minute))
defer cancel()
err = k.SendPayload(ctx, &request.Item{
Method: http.MethodPost,
Path: k.API.Endpoints.URL + path,
Path: endpoint + path,
Headers: headers,
Body: strings.NewReader(encoded),
Result: result,
Result: &interim,
AuthRequest: true,
NonceEnabled: true,
Verbose: k.Verbose,
HTTPDebugging: k.HTTPDebugging,
HTTPRecording: k.HTTPRecording,
})
if err != nil {
return err
}
var errCap SpotAuthError
if err := json.Unmarshal(interim, &errCap); err == nil {
if len(errCap.Error) != 0 {
return errors.New(errCap.Error[0])
}
}
return json.Unmarshal(interim, result)
}
// GetFee returns an estimate of fee based on type of transaction
func (k *Kraken) GetFee(feeBuilder *exchange.FeeBuilder) (float64, error) {
var fee float64
c := feeBuilder.Pair.Base.String() +
feeBuilder.Pair.Delimiter +
feeBuilder.Pair.Quote.String()
switch feeBuilder.FeeType {
case exchange.CryptocurrencyTradeFee:
feePair, err := k.GetTradeVolume(true, c)
feePair, err := k.GetTradeVolume(true, feeBuilder.Pair)
if err != nil {
return 0, err
}
if feeBuilder.IsMaker {
fee = calculateTradingFee(c,
fee = calculateTradingFee(feePair.Currency,
feePair.FeesMaker,
feeBuilder.PurchasePrice,
feeBuilder.Amount)
} else {
fee = calculateTradingFee(c,
fee = calculateTradingFee(feePair.Currency,
feePair.Fees,
feeBuilder.PurchasePrice,
feeBuilder.Amount)
@@ -1078,7 +1103,7 @@ func (k *Kraken) GetCryptoDepositAddress(method, code string) (string, error) {
values.Set("asset", code)
values.Set("method", method)
err := k.SendAuthenticatedHTTPRequest(krakenDepositAddresses, values, &resp)
err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenDepositAddresses, values, &resp)
if err != nil {
return "", err
}
@@ -1103,7 +1128,7 @@ func (k *Kraken) WithdrawStatus(c currency.Code, method string) ([]WithdrawStatu
params.Set("method", method)
}
if err := k.SendAuthenticatedHTTPRequest(krakenWithdrawStatus, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenWithdrawStatus, params, &response); err != nil {
return response.Result, err
}
@@ -1121,7 +1146,7 @@ func (k *Kraken) WithdrawCancel(c currency.Code, refID string) (bool, error) {
params.Set("asset", c.String())
params.Set("refid", refID)
if err := k.SendAuthenticatedHTTPRequest(krakenWithdrawCancel, params, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenWithdrawCancel, params, &response); err != nil {
return response.Result, err
}
@@ -1131,7 +1156,7 @@ func (k *Kraken) WithdrawCancel(c currency.Code, refID string) (bool, error) {
// GetWebsocketToken returns a websocket token
func (k *Kraken) GetWebsocketToken() (string, error) {
var response WsTokenResponse
if err := k.SendAuthenticatedHTTPRequest(krakenWebsocketToken, url.Values{}, &response); err != nil {
if err := k.SendAuthenticatedHTTPRequest(exchange.RestSpot, krakenWebsocketToken, url.Values{}, &response); err != nil {
return "", err
}
if len(response.Error) > 0 {

View File

@@ -0,0 +1,307 @@
package kraken
import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"strconv"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
)
// GetFuturesOrderbook gets orderbook data for futures
func (k *Kraken) GetFuturesOrderbook(symbol currency.Pair) (FuturesOrderbookData, error) {
var resp FuturesOrderbookData
params := url.Values{}
symbolValue, err := k.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
return resp, k.SendHTTPRequest(exchange.RestFutures, futuresOrderbook+"?"+params.Encode(), &resp)
}
// GetFuturesMarkets gets a list of futures markets and their data
func (k *Kraken) GetFuturesMarkets() (FuturesInstrumentData, error) {
var resp FuturesInstrumentData
return resp, k.SendHTTPRequest(exchange.RestFutures, futuresInstruments, &resp)
}
// GetFuturesTickers gets a list of futures tickers and their data
func (k *Kraken) GetFuturesTickers() (FuturesTickerData, error) {
var resp FuturesTickerData
return resp, k.SendHTTPRequest(exchange.RestFutures, futuresTickers, &resp)
}
// GetFuturesTradeHistory gets public trade history data for futures
func (k *Kraken) GetFuturesTradeHistory(symbol currency.Pair, lastTime time.Time) (FuturesTradeHistoryData, error) {
var resp FuturesTradeHistoryData
params := url.Values{}
symbolValue, err := k.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
if !lastTime.IsZero() {
params.Set("lastTime", lastTime.Format("2006-01-02T15:04:05.070Z"))
}
return resp, k.SendHTTPRequest(exchange.RestFutures, futuresTradeHistory+"?"+params.Encode(), &resp)
}
// FuturesBatchOrder places a batch order for futures
func (k *Kraken) FuturesBatchOrder(data []PlaceBatchOrderData) (FuturesAccountsData, error) {
var resp FuturesAccountsData
for x := range data {
unformattedPair, err := currency.NewPairFromString(data[x].Symbol)
if err != nil {
return resp, err
}
formattedPair, err := k.FormatExchangeCurrency(unformattedPair, asset.Futures)
if err != nil {
return resp, err
}
data[x].Symbol = formattedPair.String()
}
req := make(map[string]interface{})
req["batchOrder"] = data
return resp, k.SendFuturesAuthRequest(http.MethodPost, futuresBatchOrder, nil, req, &resp)
}
// FuturesEditOrder edits a futures order
func (k *Kraken) FuturesEditOrder(orderID, clientOrderID string, size, limitPrice, stopPrice float64) (FuturesAccountsData, error) {
var resp FuturesAccountsData
params := url.Values{}
if orderID != "" {
params.Set("orderId", orderID)
}
if clientOrderID != "" {
params.Set("cliOrderId", clientOrderID)
}
params.Set("size", strconv.FormatFloat(size, 'f', -1, 64))
params.Set("limitPrice", strconv.FormatFloat(limitPrice, 'f', -1, 64))
params.Set("stopPrice", strconv.FormatFloat(stopPrice, 'f', -1, 64))
return resp, k.SendFuturesAuthRequest(http.MethodPost, futuresEditOrder, params, nil, &resp)
}
// FuturesSendOrder sends a futures order
func (k *Kraken) FuturesSendOrder(orderType order.Type, symbol currency.Pair, side, triggerSignal, clientOrderID, reduceOnly string,
size, limitPrice, stopPrice float64) (FuturesSendOrderData, error) {
var resp FuturesSendOrderData
oType, ok := validOrderTypes[orderType]
if !ok {
return resp, errors.New("invalid orderType")
}
params := url.Values{}
params.Set("orderType", oType)
symbolValue, err := k.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
if !common.StringDataCompare(validSide, side) {
return resp, errors.New("invalid side")
}
params.Set("side", side)
if triggerSignal != "" {
if !common.StringDataCompare(validTriggerSignal, triggerSignal) {
return resp, errors.New("invalid triggerSignal")
}
params.Set("triggerSignal", triggerSignal)
}
if clientOrderID != "" {
params.Set("cliOrdId", clientOrderID)
}
if reduceOnly != "" {
if !common.StringDataCompare(validReduceOnly, reduceOnly) {
return resp, errors.New("invalid reduceOnly")
}
params.Set("reduceOnly", reduceOnly)
}
params.Set("size", strconv.FormatFloat(size, 'f', -1, 64))
params.Set("limitPrice", strconv.FormatFloat(limitPrice, 'f', -1, 64))
if stopPrice != 0 {
params.Set("stopPrice", strconv.FormatFloat(stopPrice, 'f', -1, 64))
}
return resp, k.SendFuturesAuthRequest(http.MethodPost, futuresSendOrder, params, nil, &resp)
}
// FuturesCancelOrder cancels an order
func (k *Kraken) FuturesCancelOrder(orderID, clientOrderID string) (FuturesCancelOrderData, error) {
var resp FuturesCancelOrderData
params := url.Values{}
if orderID != "" {
params.Set("order_id", orderID)
}
if clientOrderID != "" {
params.Set("cliOrdId", clientOrderID)
}
return resp, k.SendFuturesAuthRequest(http.MethodPost, futuresCancelOrder, params, nil, &resp)
}
// FuturesGetFills gets order fills for futures
func (k *Kraken) FuturesGetFills(lastFillTime time.Time) (FuturesFillsData, error) {
var resp FuturesFillsData
params := url.Values{}
if !lastFillTime.IsZero() {
params.Set("lastFillTime", lastFillTime.UTC().Format("2006-01-02T15:04:05.999Z"))
}
return resp, k.SendFuturesAuthRequest(http.MethodGet, futuresOrderFills, params, nil, &resp)
}
// FuturesTransfer transfers funds between accounts
func (k *Kraken) FuturesTransfer(fromAccount, toAccount, unit string, amount float64) (FuturesTransferData, error) {
var resp FuturesTransferData
req := make(map[string]interface{})
req["fromAccount"] = fromAccount
req["toAccount"] = toAccount
req["unit"] = unit
req["amount"] = amount
return resp, k.SendFuturesAuthRequest(http.MethodPost, futuresTransfer, nil, nil, &resp)
}
// FuturesGetOpenPositions gets futures platform's notifications
func (k *Kraken) FuturesGetOpenPositions() (FuturesOpenPositions, error) {
var resp FuturesOpenPositions
return resp, k.SendFuturesAuthRequest(http.MethodGet, futuresOpenPositions, nil, nil, &resp)
}
// FuturesNotifications gets futures notifications
func (k *Kraken) FuturesNotifications() (FuturesNotificationData, error) {
var resp FuturesNotificationData
return resp, k.SendFuturesAuthRequest(http.MethodGet, futuresNotifications, nil, nil, &resp)
}
// FuturesCancelAllOrders cancels all futures orders for a given symbol or all symbols
func (k *Kraken) FuturesCancelAllOrders(symbol currency.Pair) (CancelAllOrdersData, error) {
var resp CancelAllOrdersData
params := url.Values{}
if symbol != (currency.Pair{}) {
symbolValue, err := k.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
}
return resp, k.SendFuturesAuthRequest(http.MethodPost, futuresCancelAllOrders, params, nil, &resp)
}
// FuturesCancelAllOrdersAfter cancels all futures orders for all symbols after a period of time (timeout measured in seconds)
func (k *Kraken) FuturesCancelAllOrdersAfter(timeout int64) (CancelOrdersAfterData, error) {
var resp CancelOrdersAfterData
params := url.Values{}
params.Set("timeout", strconv.FormatInt(timeout, 10))
return resp, k.SendFuturesAuthRequest(http.MethodPost, futuresCancelOrdersAfter, params, nil, &resp)
}
// FuturesOpenOrders gets all futures open orders
func (k *Kraken) FuturesOpenOrders() (FuturesOpenOrdersData, error) {
var resp FuturesOpenOrdersData
return resp, k.SendFuturesAuthRequest(http.MethodGet, futuresOpenOrders, nil, nil, &resp)
}
// FuturesRecentOrders gets recent futures orders for a symbol or all symbols
func (k *Kraken) FuturesRecentOrders(symbol currency.Pair) (FuturesRecentOrdersData, error) {
var resp FuturesRecentOrdersData
params := url.Values{}
if symbol != (currency.Pair{}) {
symbolValue, err := k.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
}
return resp, k.SendFuturesAuthRequest(http.MethodGet, futuresRecentOrders, nil, nil, &resp)
}
// FuturesWithdrawToSpotWallet withdraws currencies from futures wallet to spot wallet
func (k *Kraken) FuturesWithdrawToSpotWallet(currency string, amount float64) (GenericResponse, error) {
var resp GenericResponse
params := url.Values{}
params.Set("currency", currency)
params.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
return resp, k.SendFuturesAuthRequest(http.MethodPost, futuresWithdraw, params, nil, &resp)
}
// FuturesGetTransfers withdraws currencies from futures wallet to spot wallet
func (k *Kraken) FuturesGetTransfers(lastTransferTime time.Time) (GenericResponse, error) {
var resp GenericResponse
params := url.Values{}
if !lastTransferTime.IsZero() {
params.Set("lastTransferTime", lastTransferTime.UTC().Format(time.RFC3339))
}
return resp, k.SendFuturesAuthRequest(http.MethodGet, futuresTransfers, params, nil, &resp)
}
// GetFuturesAccountData gets account data for futures
func (k *Kraken) GetFuturesAccountData() (FuturesAccountsData, error) {
var resp FuturesAccountsData
return resp, k.SendFuturesAuthRequest(http.MethodGet, futuresAccountData, nil, nil, &resp)
}
func (k *Kraken) signFuturesRequest(endpoint, nonce, data string) string {
message := data + nonce + endpoint
hash := crypto.GetSHA256([]byte(message))
hc := crypto.GetHMAC(crypto.HashSHA512, hash, []byte(k.API.Credentials.Secret))
return base64.StdEncoding.EncodeToString(hc)
}
// SendFuturesAuthRequest will send an auth req
func (k *Kraken) SendFuturesAuthRequest(method, path string, postData url.Values, data map[string]interface{}, result interface{}) error {
if !k.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet,
k.Name)
}
if postData == nil {
postData = url.Values{}
}
nonce := strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
reqData := ""
if len(data) > 0 {
temp, err := json.Marshal(data)
if err != nil {
return err
}
postData.Add("json", string(temp))
reqData = "json=" + string(temp)
}
sig := k.signFuturesRequest(path, nonce, reqData)
headers := map[string]string{
"APIKey": k.API.Credentials.Key,
"Authent": sig,
"Nonce": nonce,
}
interim := json.RawMessage{}
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Minute))
defer cancel()
err := k.SendPayload(ctx, &request.Item{
Method: method,
Path: futuresURL + common.EncodeURLValues(path, postData),
Headers: headers,
Result: &interim,
AuthRequest: true,
Verbose: k.Verbose,
HTTPDebugging: k.HTTPDebugging,
HTTPRecording: k.HTTPRecording,
})
if err != nil {
return err
}
var errCap AuthErrorData
if err := json.Unmarshal(interim, &errCap); err == nil {
if errCap.Result != "success" && errCap.Error != "" {
return errors.New(errCap.Error)
}
}
return json.Unmarshal(interim, result)
}

View File

@@ -49,7 +49,6 @@ func TestMain(m *testing.M) {
krakenConfig.API.AuthenticatedSupport = true
krakenConfig.API.Credentials.Key = apiKey
krakenConfig.API.Credentials.Secret = apiSecret
krakenConfig.API.Endpoints.WebsocketURL = k.API.Endpoints.WebsocketURL
k.Websocket = sharedtestvalues.NewTestWebsocket()
err = k.Setup(krakenConfig)
if err != nil {
@@ -67,6 +66,301 @@ func TestGetServerTime(t *testing.T) {
}
}
func TestFetchTradablePairs(t *testing.T) {
t.Parallel()
_, err := k.FetchTradablePairs(asset.Futures)
if err != nil {
t.Error(err)
}
}
func TestUpdateTicker(t *testing.T) {
t.Parallel()
sp, err := currency.NewPairFromString("XBTUSD")
if err != nil {
t.Error(err)
}
_, err = k.UpdateTicker(sp, asset.Spot)
if err != nil {
t.Error(err)
}
fp, err := currency.NewPairFromString("pi_xbtusd")
if err != nil {
t.Error(err)
}
_, err = k.UpdateTicker(fp, asset.Futures)
if err != nil {
t.Error(err)
}
}
func TestUpdateOrderbook(t *testing.T) {
t.Parallel()
sp, err := currency.NewPairFromString("BTCEUR")
if err != nil {
t.Error(err)
}
_, err = k.UpdateOrderbook(sp, asset.Spot)
if err != nil {
t.Error(err)
}
fp, err := currency.NewPairFromString("pi_xbtusd")
if err != nil {
t.Error(err)
}
_, err = k.UpdateOrderbook(fp, asset.Futures)
if err != nil {
t.Error(err)
}
}
func TestUpdateAccountInfo(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
_, err := k.UpdateAccountInfo(asset.Spot)
if err != nil {
t.Error(err)
}
}
func TestWrapperGetOrderInfo(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := k.GetOrderInfo("123", currency.Pair{}, asset.Futures)
if err != nil {
t.Error(err)
}
}
func TestFuturesBatchOrder(t *testing.T) {
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test: api keys not set or canManipulateRealOrders")
}
t.Parallel()
var data []PlaceBatchOrderData
var tempData PlaceBatchOrderData
tempData.PlaceOrderType = "cancel"
tempData.OrderID = "test123"
tempData.Symbol = "pi_xbtusd"
data = append(data, tempData)
_, err := k.FuturesBatchOrder(data)
if err != nil {
t.Error(err)
}
}
func TestFuturesEditOrder(t *testing.T) {
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test: api keys not set or canManipulateRealOrders")
}
t.Parallel()
_, err := k.FuturesEditOrder("test123", "", 5.2, 1, 0)
if err != nil {
t.Error(err)
}
}
func TestFuturesSendOrder(t *testing.T) {
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test: api keys not set or canManipulateRealOrders")
}
t.Parallel()
cp, err := currency.NewPairFromString("PI_XBTUSD")
if err != nil {
t.Error(err)
}
_, err = k.FuturesSendOrder(order.Limit, cp, "buy", "", "", "", 1, 1, 0.9)
if err != nil {
t.Error(err)
}
}
func TestFuturesCancelOrder(t *testing.T) {
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test: api keys not set or canManipulateRealOrders")
}
t.Parallel()
_, err := k.FuturesCancelOrder("test123", "")
if err != nil {
t.Error(err)
}
}
func TestFuturesGetFills(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := k.FuturesGetFills(time.Now().Add(-time.Hour * 24))
if err != nil {
t.Error(err)
}
}
func TestFuturesTransfer(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := k.FuturesTransfer("cash", "futures", "btc", 2)
if err != nil {
t.Error(err)
}
}
func TestFuturesGetOpenPositions(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := k.FuturesGetOpenPositions()
if err != nil {
t.Error(err)
}
}
func TestFuturesNotifications(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := k.FuturesNotifications()
if err != nil {
t.Error(err)
}
}
func TestFuturesCancelAllOrders(t *testing.T) {
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test: api keys not set or canManipulateRealOrders")
}
t.Parallel()
cp, err := currency.NewPairFromString("PI_XBTUSD")
if err != nil {
t.Error(err)
}
_, err = k.FuturesCancelAllOrders(cp)
if err != nil {
t.Error(err)
}
}
func TestGetFuturesAccountData(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := k.GetFuturesAccountData()
if err != nil {
t.Error(err)
}
}
func TestFuturesCancelAllOrdersAfter(t *testing.T) {
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test: api keys not set or canManipulateRealOrders")
}
t.Parallel()
_, err := k.FuturesCancelAllOrdersAfter(50)
if err != nil {
t.Error(err)
}
}
func TestFuturesOpenOrders(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := k.FuturesOpenOrders()
if err != nil {
t.Error(err)
}
}
func TestFuturesRecentOrders(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
cp, err := currency.NewPairFromString("PI_XBTUSD")
if err != nil {
t.Error(err)
}
_, err = k.FuturesRecentOrders(cp)
if err != nil {
t.Error(err)
}
}
func TestFuturesWithdrawToSpotWallet(t *testing.T) {
if !areTestAPIKeysSet() || !canManipulateRealOrders {
t.Skip("skipping test: api keys not set or canManipulateRealOrders")
}
t.Parallel()
_, err := k.FuturesWithdrawToSpotWallet("xbt", 5)
if err != nil {
t.Error(err)
}
}
func TestFuturesGetTransfers(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
t.Parallel()
_, err := k.FuturesGetTransfers(time.Now().Add(-time.Hour * 24))
if err != nil {
t.Error(err)
}
}
func TestGetFuturesOrderbook(t *testing.T) {
t.Parallel()
cp, err := currency.NewPairFromString("FI_xbtusd_200925")
if err != nil {
t.Error(err)
}
_, err = k.GetFuturesOrderbook(cp)
if err != nil {
t.Error(err)
}
}
func TestGetFuturesMarkets(t *testing.T) {
t.Parallel()
_, err := k.GetFuturesMarkets()
if err != nil {
t.Error(err)
}
}
func TestGetFuturesTickers(t *testing.T) {
t.Parallel()
_, err := k.GetFuturesTickers()
if err != nil {
t.Error(err)
}
}
func TestGetFuturesTradeHistory(t *testing.T) {
t.Parallel()
cp, err := currency.NewPairFromString("pi_xbtusd")
if err != nil {
t.Error(err)
}
_, err = k.GetFuturesTradeHistory(cp, time.Now().Add(-time.Hour*24))
if err != nil {
t.Error(err)
}
}
// TestGetAssets API endpoint test
func TestGetAssets(t *testing.T) {
t.Parallel()
@@ -137,7 +431,19 @@ func TestLookupCurrency(t *testing.T) {
// TestGetAssetPairs API endpoint test
func TestGetAssetPairs(t *testing.T) {
t.Parallel()
_, err := k.GetAssetPairs()
_, err := k.GetAssetPairs([]string{}, "fees")
if err != nil {
t.Error("GetAssetPairs() error", err)
}
_, err = k.GetAssetPairs([]string{}, "leverage")
if err != nil {
t.Error("GetAssetPairs() error", err)
}
_, err = k.GetAssetPairs([]string{}, "margin")
if err != nil {
t.Error("GetAssetPairs() error", err)
}
_, err = k.GetAssetPairs([]string{}, "")
if err != nil {
t.Error("GetAssetPairs() error", err)
}
@@ -146,7 +452,11 @@ func TestGetAssetPairs(t *testing.T) {
// TestGetTicker API endpoint test
func TestGetTicker(t *testing.T) {
t.Parallel()
_, err := k.GetTicker("BCHEUR")
cp, err := currency.NewPairFromString("BCHEUR")
if err != nil {
t.Error(err)
}
_, err = k.GetTicker(cp)
if err != nil {
t.Error("GetTicker() error", err)
}
@@ -164,7 +474,11 @@ func TestGetTickers(t *testing.T) {
// TestGetOHLC API endpoint test
func TestGetOHLC(t *testing.T) {
t.Parallel()
_, err := k.GetOHLC("XXBTZUSD", "1440")
cp, err := currency.NewPairFromString("XXBTZUSD")
if err != nil {
t.Error(err)
}
_, err = k.GetOHLC(cp, "1440")
if err != nil {
t.Error("GetOHLC() error", err)
}
@@ -173,7 +487,11 @@ func TestGetOHLC(t *testing.T) {
// TestGetDepth API endpoint test
func TestGetDepth(t *testing.T) {
t.Parallel()
_, err := k.GetDepth("BCHEUR")
cp, err := currency.NewPairFromString("BCHEUR")
if err != nil {
t.Error(err)
}
_, err = k.GetDepth(cp)
if err != nil {
t.Error("GetDepth() error", err)
}
@@ -182,12 +500,19 @@ func TestGetDepth(t *testing.T) {
// TestGetTrades API endpoint test
func TestGetTrades(t *testing.T) {
t.Parallel()
_, err := k.GetTrades("BCHEUR")
cp, err := currency.NewPairFromString("BCHEUR")
if err != nil {
t.Error(err)
}
_, err = k.GetTrades(cp)
if err != nil {
t.Error("GetTrades() error", err)
}
_, err = k.GetTrades("MADEUP")
cp2, err := currency.NewPairFromString("MADEUP")
if err != nil {
t.Error(err)
}
_, err = k.GetTrades(cp2)
if err == nil {
t.Error("expected error")
}
@@ -196,7 +521,11 @@ func TestGetTrades(t *testing.T) {
// TestGetSpread API endpoint test
func TestGetSpread(t *testing.T) {
t.Parallel()
_, err := k.GetSpread("BCHEUR")
cp, err := currency.NewPairFromString("BCHEUR")
if err != nil {
t.Error(err)
}
_, err = k.GetSpread(cp)
if err != nil {
t.Error("GetSpread() error", err)
}
@@ -301,7 +630,11 @@ func TestQueryLedgers(t *testing.T) {
// TestGetTradeVolume API endpoint test
func TestGetTradeVolume(t *testing.T) {
t.Parallel()
_, err := k.GetTradeVolume(true, "OAVY7T-MV5VK-KHDF5X")
cp, err := currency.NewPairFromString("OAVY7T-MV5VK-KHDF5X")
if err != nil {
t.Error(err)
}
_, err = k.GetTradeVolume(true, cp)
if err == nil {
t.Error("GetTradeVolume() Expected error")
}
@@ -311,7 +644,11 @@ func TestGetTradeVolume(t *testing.T) {
func TestAddOrder(t *testing.T) {
t.Parallel()
args := AddOrderOptions{OrderFlags: "fcib"}
_, err := k.AddOrder("XXBTZUSD",
cp, err := currency.NewPairFromString("XXBTZUSD")
if err != nil {
t.Error(err)
}
_, err = k.AddOrder(cp,
order.Sell.Lower(), order.Limit.Lower(),
0.00000001, 0, 0, 0, &args)
if err == nil {
@@ -447,22 +784,31 @@ func TestFormatWithdrawPermissions(t *testing.T) {
// TestGetActiveOrders wrapper test
func TestGetActiveOrders(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip("skipping test: api keys not set")
}
pair, err := currency.NewPairFromString("LTC_USDT")
if err != nil {
t.Error(err)
}
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
Pairs: currency.Pairs{pair},
}
_, err := k.GetActiveOrders(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not get open orders: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
_, err = k.GetActiveOrders(&getOrdersRequest)
if err != nil {
t.Error(err)
}
}
// TestGetOrderHistory wrapper test
func TestGetOrderHistory(t *testing.T) {
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Type: order.AnyType,
AssetType: asset.Spot,
}
_, err := k.GetOrderHistory(&getOrdersRequest)
@@ -594,18 +940,30 @@ func TestCancelAllExchangeOrders(t *testing.T) {
// TestGetAccountInfo wrapper test
func TestGetAccountInfo(t *testing.T) {
if areTestAPIKeysSet() {
_, err := k.UpdateAccountInfo()
_, err := k.UpdateAccountInfo(asset.Spot)
if err != nil {
// Spot and Futures have separate api keys. Please ensure that the correct one is provided
t.Error("GetAccountInfo() error", err)
}
} else {
_, err := k.UpdateAccountInfo()
_, err := k.UpdateAccountInfo(asset.Spot)
if err == nil {
t.Error("GetAccountInfo() Expected error")
}
}
}
func TestUpdateFuturesAccountInfo(t *testing.T) {
if !areTestAPIKeysSet() {
t.Skip("API keys not set. Skipping the test")
}
_, err := k.UpdateAccountInfo(asset.Futures)
if err != nil {
// Spot and Futures have separate api keys. Please ensure that the correct one is provided
t.Error(err)
}
}
// TestModifyOrder wrapper test
func TestModifyOrder(t *testing.T) {
if areTestAPIKeysSet() && !canManipulateRealOrders {
@@ -620,6 +978,7 @@ func TestModifyOrder(t *testing.T) {
// TestWithdraw wrapper test
func TestWithdraw(t *testing.T) {
withdrawCryptoRequest := withdraw.Request{
Exchange: k.Name,
Crypto: withdraw.CryptoRequest{
Address: core.BitcoinDonationAddress,
},
@@ -1335,7 +1694,7 @@ func TestWsOpenOrders(t *testing.T) {
"cost": "0.00000",
"descr": {
"close": "",
"leverage": "0:1",
"leverage": "0.1",
"order": "sell 10.00345345 XBT/USD @ limit 34.50000 with 0:1 leverage",
"ordertype": "limit",
"pair": "XBT/USD",
@@ -1364,7 +1723,7 @@ func TestWsOpenOrders(t *testing.T) {
"cost": "0.00000",
"descr": {
"close": "",
"leverage": "0:1",
"leverage": "0.1",
"order": "sell 0.00000010 XBT/USD @ limit 5334.60000 with 0:1 leverage",
"ordertype": "limit",
"pair": "XBT/USD",
@@ -1393,7 +1752,7 @@ func TestWsOpenOrders(t *testing.T) {
"cost": "0.00000",
"descr": {
"close": "",
"leverage": "0:1",
"leverage": "0.1",
"order": "sell 0.00001000 XBT/USD @ limit 90.40000 with 0:1 leverage",
"ordertype": "limit",
"pair": "XBT/USD",
@@ -1422,7 +1781,7 @@ func TestWsOpenOrders(t *testing.T) {
"cost": "0.00000",
"descr": {
"close": "",
"leverage": "0:1",
"leverage": "0.1",
"order": "sell 0.00001000 XBT/USD @ limit 9.00000 with 0:1 leverage",
"ordertype": "limit",
"pair": "XBT/USD",

View File

@@ -1,22 +1,96 @@
package kraken
import (
"sync"
"time"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/stream"
)
type assetTranslatorStore struct {
l sync.RWMutex
Assets map[string]string
const (
krakenAPIVersion = "0"
krakenServerTime = "Time"
krakenAssets = "Assets"
krakenAssetPairs = "AssetPairs?"
krakenTicker = "Ticker"
krakenOHLC = "OHLC"
krakenDepth = "Depth"
krakenTrades = "Trades"
krakenSpread = "Spread"
krakenBalance = "Balance"
krakenTradeBalance = "TradeBalance"
krakenOpenOrders = "OpenOrders"
krakenClosedOrders = "ClosedOrders"
krakenQueryOrders = "QueryOrders"
krakenTradeHistory = "TradesHistory"
krakenQueryTrades = "QueryTrades"
krakenOpenPositions = "OpenPositions"
krakenLedgers = "Ledgers"
krakenQueryLedgers = "QueryLedgers"
krakenTradeVolume = "TradeVolume"
krakenOrderCancel = "CancelOrder"
krakenOrderPlace = "AddOrder"
krakenWithdrawInfo = "WithdrawInfo"
krakenWithdraw = "Withdraw"
krakenDepositMethods = "DepositMethods"
krakenDepositAddresses = "DepositAddresses"
krakenWithdrawStatus = "WithdrawStatus"
krakenWithdrawCancel = "WithdrawCancel"
krakenWebsocketToken = "GetWebSocketsToken"
// Futures
futuresTickers = "/api/v3/tickers"
futuresOrderbook = "/api/v3/orderbook"
futuresInstruments = "/api/v3/instruments"
futuresTradeHistory = "/api/v3/history"
futuresSendOrder = "/api/v3/sendorder"
futuresCancelOrder = "/api/v3/cancelorder"
futuresOrderFills = "/api/v3/fills"
futuresTransfer = "/api/v3/transfer"
futuresOpenPositions = "/api/v3/openpositions"
futuresBatchOrder = "/api/v3/batchorder"
futuresNotifications = "/api/v3/notifications"
futuresAccountData = "/api/v3/accounts"
futuresCancelAllOrders = "/api/v3/cancelallorders"
futuresCancelOrdersAfter = "/api/v3/cancelallordersafter"
futuresOpenOrders = "/api/v3/openorders"
futuresRecentOrders = "/api/v3/recentorders"
futuresWithdraw = "/api/v3/withdrawal"
futuresTransfers = "/api/v3/transfers"
futuresEditOrder = "/api/v3/editorder"
// Rate limit consts
krakenRateInterval = time.Second
krakenRequestRate = 1
// Status consts
statusOpen = "open"
krakenFormat = "2006-01-02T15:04:05.000Z"
)
var (
assetTranslator assetTranslatorStore
)
// GenericResponse stores general response data for functions that only return success
type GenericResponse struct {
Timestamp string `json:"timestamp"`
Result string `json:"result"`
}
// TimeResponse type
type TimeResponse struct {
Unixtime int64 `json:"unixtime"`
Rfc1123 string `json:"rfc1123"`
// AuthErrorData stores authenticated error messages
type AuthErrorData struct {
Result string `json:"result"`
ServerTime string `json:"serverTime"`
Error string `json:"error"`
}
// SpotAuthError stores authenticated error messages
type SpotAuthError struct {
Error []string `json:"error"`
}
// Asset holds asset information
@@ -30,6 +104,7 @@ type Asset struct {
// AssetPairs holds asset pair information
type AssetPairs struct {
Altname string `json:"altname"`
Wsname string `json:"wsname"`
AclassBase string `json:"aclass_base"`
Base string `json:"base"`
AclassQuote string `json:"aclass_quote"`
@@ -45,6 +120,7 @@ type AssetPairs struct {
FeeVolumeCurrency string `json:"fee_volume_currency"`
MarginCall int `json:"margin_call"`
MarginStop int `json:"margin_stop"`
Ordermin string `json:"ordermin"`
}
// Ticker is a standard ticker type
@@ -520,7 +596,7 @@ type WsOpenOrder struct {
Close string `json:"close"`
Price float64 `json:"price,string"`
Price2 float64 `json:"price2,string"`
Leverage string `json:"leverage"`
Leverage float64 `json:"leverage,string"`
Order string `json:"order"`
OrderType string `json:"ordertype"`
Pair string `json:"pair"`
@@ -623,3 +699,11 @@ type WsCancelOrderResponse struct {
RequestID int64 `json:"reqid"`
Count int64 `json:"count"`
}
// OrderVars stores side, status and type for any order/trade
type OrderVars struct {
Side order.Side
Status order.Status
OrderType order.Type
Fee float64
}

View File

@@ -599,7 +599,7 @@ func (k *Kraken) wsProcessOpenOrders(ownOrders interface{}) error {
// allowing correlation between subscriptions and returned data
func (k *Kraken) addNewSubscriptionChannelData(response *wsSubscription) {
// We change the / to - to maintain compatibility with REST/config
var pair currency.Pair
var pair, fPair currency.Pair
if response.Pair != "" {
var err error
pair, err = currency.NewPairFromString(response.Pair)
@@ -607,11 +607,15 @@ func (k *Kraken) addNewSubscriptionChannelData(response *wsSubscription) {
log.Errorf(log.ExchangeSys, "%s exchange error: %s", k.Name, err)
return
}
pair.Delimiter = k.CurrencyPairs.RequestFormat.Delimiter
fPair, err = k.FormatExchangeCurrency(pair, asset.Spot)
if err != nil {
log.Errorf(log.ExchangeSys, "%s exchange error: %s", k.Name, err)
return
}
}
subscriptionChannelPair = append(subscriptionChannelPair, WebsocketChannelData{
Subscription: response.Subscription.Name,
Pair: pair,
Pair: fPair,
ChannelID: response.ChannelID,
})
}

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More