Feature: Add mock testing to ZB (#569)

* Adds mock testing to ZB

* STEALS improved time validation code from the original STOLEN validation code :D

* Mini fixes from review

* happy fun comment stealing

* Moves the loop checker earlier to ensure no double appendages

* Fixes sneaky test

* Fixes the important part where mock tests work instead of live tests

* Skips authenticated endpoints for mock testing.

* lint

* Updates candle wrapper functions to respect design

* basic linting fix

* Reverts configtest.json, updates readme to be way better, adds coverage to validateCandlesRequest

* Tiniest grammatical fix

* Fixes more outdated code references

* Closing out a high

* Fixes spacing

* Replaces all instances of 4 spaces in tmpl files with a tab

* fixes spacing and tab related readme issues once and for all 🤞

* tidy

* indentation violation identification situation
This commit is contained in:
Scott
2020-10-07 11:59:08 +11:00
committed by GitHub
parent d9e27cd3d8
commit 0da62b7fbf
112 changed files with 4957 additions and 1304 deletions

View File

@@ -31,8 +31,8 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
+ Individual package example below:
```go
// Exchanges will be abstracted out in further updates and examples will be
// supplied then
// Exchanges will be abstracted out in further updates and examples will be
// supplied then
```
### How to do REST public/private calls
@@ -48,9 +48,9 @@ main.go
var z exchange.IBotExchange
for i := range bot.Exchanges {
if bot.Exchanges[i].GetName() == "ZB" {
z = bot.Exchanges[i]
}
if bot.Exchanges[i].GetName() == "ZB" {
z = bot.Exchanges[i]
}
}
// Public calls - wrapper functions
@@ -58,13 +58,13 @@ for i := range bot.Exchanges {
// Fetches current ticker information
tick, err := z.FetchTicker()
if err != nil {
// Handle error
// Handle error
}
// Fetches current orderbook information
ob, err := z.FetchOrderbook()
if err != nil {
// Handle error
// Handle error
}
// Private calls - wrapper functions - make sure your APIKEY and APISECRET are
@@ -73,7 +73,7 @@ if err != nil {
// Fetches current account information
accountInfo, err := z.GetAccountInfo()
if err != nil {
// Handle error
// Handle error
}
```
@@ -85,13 +85,13 @@ if err != nil {
// Fetches current ticker information
ticker, err := z.GetTicker()
if err != nil {
// Handle error
// Handle error
}
// Fetches current orderbook information
ob, err := z.GetOrderBook()
if err != nil {
// Handle error
// Handle error
}
// Private calls - make sure your APIKEY and APISECRET are set and
@@ -100,21 +100,21 @@ if err != nil {
// GetUserInfo returns account info
accountInfo, err := z.GetUserInfo(...)
if err != nil {
// Handle error
// Handle error
}
// Submits an order and the exchange and returns its tradeID
tradeID, err := z.Trade(...)
if err != nil {
// Handle error
// Handle error
}
```
### How to do LongPolling public/private calls
```go
// Exchanges will be abstracted out in further updates and examples will be
// supplied then
// Exchanges will be abstracted out in further updates and examples will be
// supplied then
```
### Please click GoDocs chevron above to view current GoDoc information for this package

View File

@@ -19,10 +19,10 @@ import (
)
const (
zbTradeURL = "http://api.zb.live/data"
zbMarketURL = "https://trade.zb.live/api"
zbAPIVersion = "v1"
zbTradeURL = "http://api.zb.live"
zbMarketURL = "https://trade.zb.live/api"
zbAPIVersion = "v1"
zbData = "data"
zbAccountInfo = "getAccountInfo"
zbMarkets = "markets"
zbKline = "kline"
@@ -136,7 +136,7 @@ func (z *ZB) GetOrders(currency string, pageindex, side int64) ([]Order, error)
// GetMarkets returns market information including pricing, symbols and
// each symbols decimal precision
func (z *ZB) GetMarkets() (map[string]MarketResponseItem, error) {
endpoint := fmt.Sprintf("%s/%s/%s", z.API.Endpoints.URL, zbAPIVersion, zbMarkets)
endpoint := fmt.Sprintf("%s/%s/%s/%s", z.API.Endpoints.URL, zbData, zbAPIVersion, zbMarkets)
var res map[string]MarketResponseItem
err := z.SendHTTPRequest(endpoint, &res, request.UnAuth)
@@ -163,7 +163,7 @@ func (z *ZB) GetLatestSpotPrice(symbol string) (float64, error) {
// GetTicker returns a ticker for a given symbol
func (z *ZB) GetTicker(symbol string) (TickerResponse, error) {
urlPath := fmt.Sprintf("%s/%s/%s?market=%s", z.API.Endpoints.URL, zbAPIVersion, zbTicker, symbol)
urlPath := fmt.Sprintf("%s/%s/%s/%s?market=%s", z.API.Endpoints.URL, zbData, zbAPIVersion, zbTicker, symbol)
var res TickerResponse
err := z.SendHTTPRequest(urlPath, &res, request.UnAuth)
return res, err
@@ -171,7 +171,7 @@ func (z *ZB) GetTicker(symbol string) (TickerResponse, error) {
// GetTickers returns ticker data for all supported symbols
func (z *ZB) GetTickers() (map[string]TickerChildResponse, error) {
urlPath := fmt.Sprintf("%s/%s/%s", z.API.Endpoints.URL, zbAPIVersion, zbTickers)
urlPath := fmt.Sprintf("%s/%s/%s/%s", z.API.Endpoints.URL, zbData, zbAPIVersion, zbTickers)
resp := make(map[string]TickerChildResponse)
err := z.SendHTTPRequest(urlPath, &resp, request.UnAuth)
return resp, err
@@ -179,7 +179,7 @@ func (z *ZB) GetTickers() (map[string]TickerChildResponse, error) {
// GetOrderbook returns the orderbook for a given symbol
func (z *ZB) GetOrderbook(symbol string) (OrderbookResponse, error) {
urlPath := fmt.Sprintf("%s/%s/%s?market=%s", z.API.Endpoints.URL, zbAPIVersion, zbDepth, symbol)
urlPath := fmt.Sprintf("%s/%s/%s/%s?market=%s", z.API.Endpoints.URL, zbData, zbAPIVersion, zbDepth, symbol)
var res OrderbookResponse
err := z.SendHTTPRequest(urlPath, &res, request.UnAuth)
@@ -217,7 +217,7 @@ func (z *ZB) GetSpotKline(arg KlinesRequestParams) (KLineResponse, error) {
vals.Set("size", fmt.Sprintf("%d", arg.Size))
}
urlPath := fmt.Sprintf("%s/%s/%s?%s", z.API.Endpoints.URL, zbAPIVersion, zbKline, vals.Encode())
urlPath := fmt.Sprintf("%s/%s/%s/%s?%s", z.API.Endpoints.URL, zbData, zbAPIVersion, zbKline, vals.Encode())
var res KLineResponse
var rawKlines map[string]interface{}

View File

@@ -0,0 +1,41 @@
//+build mock_test_off
// This will build if build tag mock_test_off is parsed and will do live testing
// using all tests in (exchange)_test.go
package zb
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
var mockTests = false
func TestMain(m *testing.M) {
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
log.Fatal("ZB load config error", err)
}
zbConfig, err := cfg.GetExchangeConfig("ZB")
if err != nil {
log.Fatal("ZB Setup() init error", err)
}
zbConfig.API.AuthenticatedSupport = true
zbConfig.API.Credentials.Key = apiKey
zbConfig.API.Credentials.Secret = apiSecret
z.SetDefaults()
z.Websocket = sharedtestvalues.NewTestWebsocket()
err = z.Setup(zbConfig)
if err != nil {
log.Fatal("ZB setup error", err)
}
log.Printf(sharedtestvalues.LiveTesting, z.Name, z.API.Endpoints.URL)
z.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
z.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride()
os.Exit(m.Run())
}

View File

@@ -0,0 +1,55 @@
//+build !mock_test_off
// This will build if build tag mock_test_off is not parsed and will try to mock
// all tests in _test.go
package zb
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/mock"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
const mockfile = "../../testdata/http_mock/zb/zb.json"
var mockTests = true
func TestMain(m *testing.M) {
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
log.Fatal("ZB load config error", err)
}
var zbConfig *config.ExchangeConfig
zbConfig, err = cfg.GetExchangeConfig("ZB")
if err != nil {
log.Fatal("ZB Setup() init error", err)
}
zbConfig.API.AuthenticatedSupport = true
zbConfig.API.AuthenticatedWebsocketSupport = true
zbConfig.API.Credentials.Key = apiKey
zbConfig.API.Credentials.Secret = apiSecret
z.SkipAuthCheck = true
z.SetDefaults()
z.Websocket = sharedtestvalues.NewTestWebsocket()
err = z.Setup(zbConfig)
if err != nil {
log.Fatal("ZB setup error", err)
}
serverDetails, newClient, err := mock.NewVCRServer(mockfile)
if err != nil {
log.Fatalf("Mock server error %s", err)
}
z.HTTPClient = newClient
z.API.Endpoints.URL = serverDetails
log.Printf(sharedtestvalues.MockTesting,
z.Name,
z.API.Endpoints.URL)
os.Exit(m.Run())
}

View File

@@ -3,23 +3,20 @@ package zb
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"strconv"
"testing"
"time"
"github.com/gorilla/websocket"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/common/convert"
"github.com/thrasher-corp/gocryptotrader/core"
"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/kline"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
"github.com/thrasher-corp/gocryptotrader/exchanges/stream"
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
)
@@ -29,39 +26,20 @@ const (
apiKey = ""
apiSecret = ""
canManipulateRealOrders = false
testCurrency = "btc_usdt"
)
var z ZB
var wsSetupRan bool
func TestMain(m *testing.M) {
z.SetDefaults()
cfg := config.GetConfig()
err := cfg.LoadConfig("../../testdata/configtest.json", true)
if err != nil {
log.Fatal("ZB load config error", err)
}
zbConfig, err := cfg.GetExchangeConfig("ZB")
if err != nil {
log.Fatal("ZB Setup() init error", err)
}
zbConfig.API.AuthenticatedSupport = true
zbConfig.API.AuthenticatedWebsocketSupport = true
zbConfig.API.Credentials.Key = apiKey
zbConfig.API.Credentials.Secret = apiSecret
z.Websocket = sharedtestvalues.NewTestWebsocket()
err = z.Setup(zbConfig)
if err != nil {
log.Fatal("ZB setup error", err)
}
os.Exit(m.Run())
}
func setupWsAuth(t *testing.T) {
if wsSetupRan {
return
}
if !z.Websocket.IsEnabled() && !z.API.AuthenticatedWebsocketSupport || !z.ValidateAPICredentials() || !canManipulateRealOrders {
if !z.Websocket.IsEnabled() &&
!z.API.AuthenticatedWebsocketSupport ||
!z.ValidateAPICredentials() ||
!canManipulateRealOrders {
t.Skip(stream.WebsocketNotEnabled)
}
var dialer websocket.Dialer
@@ -81,7 +59,7 @@ func TestSpotNewOrder(t *testing.T) {
}
arg := SpotNewOrderRequestParams{
Symbol: "btc_usdt",
Symbol: testCurrency,
Type: SpotNewOrderRequestParamsTypeSell,
Amount: 0.01,
Price: 10246.1,
@@ -99,7 +77,7 @@ func TestCancelExistingOrder(t *testing.T) {
t.Skip()
}
err := z.CancelExistingOrder(20180629145864850, "btc_usdt")
err := z.CancelExistingOrder(20180629145864850, testCurrency)
if err != nil {
t.Errorf("ZB CancelExistingOrder: %s", err)
}
@@ -107,7 +85,7 @@ func TestCancelExistingOrder(t *testing.T) {
func TestGetLatestSpotPrice(t *testing.T) {
t.Parallel()
_, err := z.GetLatestSpotPrice("btc_usdt")
_, err := z.GetLatestSpotPrice(testCurrency)
if err != nil {
t.Errorf("ZB GetLatestSpotPrice: %s", err)
}
@@ -115,7 +93,7 @@ func TestGetLatestSpotPrice(t *testing.T) {
func TestGetTicker(t *testing.T) {
t.Parallel()
_, err := z.GetTicker("btc_usdt")
_, err := z.GetTicker(testCurrency)
if err != nil {
t.Errorf("ZB GetTicker: %s", err)
}
@@ -131,7 +109,7 @@ func TestGetTickers(t *testing.T) {
func TestGetOrderbook(t *testing.T) {
t.Parallel()
_, err := z.GetOrderbook("btc_usdt")
_, err := z.GetOrderbook(testCurrency)
if err != nil {
t.Errorf("ZB GetTicker: %s", err)
}
@@ -145,18 +123,6 @@ func TestGetMarkets(t *testing.T) {
}
}
func TestGetSpotKline(t *testing.T) {
arg := KlinesRequestParams{
Symbol: "btc_usdt",
Type: kline.OneMin.Short() + "in",
Size: 10,
}
_, err := z.GetSpotKline(arg)
if err != nil {
t.Errorf("ZB GetSpotKline: %s", err)
}
}
func setFeeBuilder() *exchange.FeeBuilder {
return &exchange.FeeBuilder{
Amount: 1,
@@ -172,6 +138,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
// TestGetFeeByTypeOfflineTradeFee logic test
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
z.GetFeeByType(feeBuilder)
if !z.ValidateAPICredentials() {
@@ -270,6 +237,9 @@ func TestFormatWithdrawPermissions(t *testing.T) {
}
func TestGetActiveOrders(t *testing.T) {
if mockTests {
t.Skip("skipping authenticated function for mock testing")
}
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Pairs: []currency.Pair{currency.NewPair(currency.XRP,
@@ -278,13 +248,16 @@ func TestGetActiveOrders(t *testing.T) {
_, err := z.GetActiveOrders(&getOrdersRequest)
if z.ValidateAPICredentials() && err != nil {
t.Errorf("Could not get open orders: %s", err)
t.Error(err)
} else if !z.ValidateAPICredentials() && err == nil {
t.Error("Expecting an error when no keys are set")
t.Error("expecting an error when no keys are set")
}
}
func TestGetOrderHistory(t *testing.T) {
if mockTests {
t.Skip("skipping authenticated function for mock testing")
}
var getOrdersRequest = order.GetOrdersRequest{
Type: order.AnyType,
Side: order.Buy,
@@ -294,9 +267,9 @@ func TestGetOrderHistory(t *testing.T) {
_, err := z.GetOrderHistory(&getOrdersRequest)
if z.ValidateAPICredentials() && err != nil {
t.Errorf("Could not get order history: %s", err)
t.Error(err)
} else if !z.ValidateAPICredentials() && err == nil {
t.Error("Expecting an error when no keys are set")
t.Error("expecting an error when no keys are set")
}
}
@@ -305,10 +278,12 @@ func TestGetOrderHistory(t *testing.T) {
func TestSubmitOrder(t *testing.T) {
if z.ValidateAPICredentials() && !canManipulateRealOrders {
t.Skip(fmt.Sprintf("ApiKey: %s. Can place orders: %v",
z.API.Credentials.Key,
t.Skip(fmt.Sprintf("Can place orders: %v",
canManipulateRealOrders))
}
if mockTests {
t.Skip("skipping authenticated function for mock testing")
}
var orderSubmission = &order.Submit{
Pair: currency.Pair{
@@ -324,10 +299,13 @@ func TestSubmitOrder(t *testing.T) {
AssetType: asset.Spot,
}
response, err := z.SubmitOrder(orderSubmission)
if z.ValidateAPICredentials() && (err != nil || !response.IsOrderPlaced) {
t.Errorf("Order failed to be placed: %v", err)
if z.ValidateAPICredentials() && err != nil {
t.Error(err)
} else if !z.ValidateAPICredentials() && err == nil {
t.Error("Expecting an error when no keys are set")
t.Error("expecting an error when no keys are set")
}
if z.ValidateAPICredentials() && response.OrderID == "" {
t.Error("expected order id")
}
}
@@ -335,6 +313,9 @@ func TestCancelExchangeOrder(t *testing.T) {
if z.ValidateAPICredentials() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
if mockTests {
t.Skip("skipping authenticated function for mock testing")
}
currencyPair := currency.NewPair(currency.XRP, currency.USDT)
var orderCancellation = &order.Cancel{
@@ -346,11 +327,10 @@ func TestCancelExchangeOrder(t *testing.T) {
}
err := z.CancelOrder(orderCancellation)
if !z.ValidateAPICredentials() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if z.ValidateAPICredentials() && err != nil {
t.Errorf("Could not cancel orders: %v", err)
t.Error(err)
} else if !z.ValidateAPICredentials() && err == nil {
t.Error("expecting an error when no keys are set")
}
}
@@ -358,6 +338,9 @@ func TestCancelAllExchangeOrders(t *testing.T) {
if z.ValidateAPICredentials() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
if mockTests {
t.Skip("skipping authenticated function for mock testing")
}
currencyPair := currency.NewPair(currency.XRP, currency.USDT)
var orderCancellation = &order.Cancel{
@@ -370,19 +353,20 @@ func TestCancelAllExchangeOrders(t *testing.T) {
resp, err := z.CancelAllOrders(orderCancellation)
if !z.ValidateAPICredentials() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if z.ValidateAPICredentials() && err != nil {
t.Errorf("Could not cancel orders: %v", err)
t.Error(err)
} else if !z.ValidateAPICredentials() && err == nil {
t.Error("expecting an error when no keys are set")
}
if len(resp.Status) > 0 {
t.Errorf("%v orders failed to cancel", len(resp.Status))
}
}
func TestGetAccountInfo(t *testing.T) {
if mockTests {
t.Skip("skipping authenticated function for mock testing")
}
if z.ValidateAPICredentials() {
_, err := z.UpdateAccountInfo()
if err != nil {
@@ -397,6 +381,9 @@ func TestGetAccountInfo(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
if mockTests {
t.Skip("skipping authenticated function for mock testing")
}
if z.ValidateAPICredentials() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -407,6 +394,13 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
if mockTests {
t.Skip("skipping authenticated function for mock testing")
}
if z.ValidateAPICredentials() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
withdrawCryptoRequest := withdraw.Request{
Crypto: withdraw.CryptoRequest{
Address: core.BitcoinDonationAddress,
@@ -417,20 +411,18 @@ func TestWithdraw(t *testing.T) {
Description: "WITHDRAW IT ALL",
}
if z.ValidateAPICredentials() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := z.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
if !z.ValidateAPICredentials() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if z.ValidateAPICredentials() && err != nil {
t.Errorf("Withdraw failed to be placed: %v", err)
t.Error(err)
} else if !z.ValidateAPICredentials() && err == nil {
t.Error("expecting an error when no keys are set")
}
}
func TestWithdrawFiat(t *testing.T) {
if mockTests {
t.Skip("skipping authenticated function for mock testing")
}
if z.ValidateAPICredentials() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -443,6 +435,9 @@ func TestWithdrawFiat(t *testing.T) {
}
func TestWithdrawInternationalBank(t *testing.T) {
if mockTests {
t.Skip("skipping authenticated function for mock testing")
}
if z.ValidateAPICredentials() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -455,6 +450,9 @@ func TestWithdrawInternationalBank(t *testing.T) {
}
func TestGetDepositAddress(t *testing.T) {
if mockTests {
t.Skip("skipping authenticated function for mock testing")
}
if z.ValidateAPICredentials() {
_, err := z.GetDepositAddress(currency.BTC, "")
if err != nil {
@@ -833,30 +831,59 @@ func TestWsCreateSubUserResponse(t *testing.T) {
}
}
func TestGetSpotKline(t *testing.T) {
arg := KlinesRequestParams{
Symbol: testCurrency,
Type: kline.OneMin.Short() + "in",
Size: int64(z.Features.Enabled.Kline.ResultLimit),
}
if mockTests {
startTime := time.Date(2020, 9, 1, 0, 0, 0, 0, time.UTC)
arg.Since = convert.UnixMillis(startTime)
arg.Type = "1day"
}
_, err := z.GetSpotKline(arg)
if err != nil {
t.Errorf("ZB GetSpotKline: %s", err)
}
}
func TestGetHistoricCandles(t *testing.T) {
currencyPair, err := currency.NewPairFromString("btc_usdt")
currencyPair, err := currency.NewPairFromString(testCurrency)
if err != nil {
t.Fatal(err)
}
startTime := time.Now().Add(-time.Hour * 1)
_, err = z.GetHistoricCandles(currencyPair, asset.Spot, startTime, time.Now(), kline.OneHour)
endTime := time.Now()
if mockTests {
startTime = time.Date(2020, 9, 1, 0, 0, 0, 0, time.UTC)
endTime = time.Date(2020, 9, 2, 0, 0, 0, 0, time.UTC)
}
_, err = z.GetHistoricCandles(currencyPair, asset.Spot, startTime, endTime, kline.OneDay)
if err != nil {
t.Fatal(err)
}
_, err = z.GetHistoricCandles(currencyPair, asset.Spot, startTime, time.Now(), kline.Interval(time.Hour*7))
_, err = z.GetHistoricCandles(currencyPair, asset.Spot, startTime, endTime, kline.Interval(time.Hour*7))
if err == nil {
t.Fatal("unexpected result")
}
}
func TestGetHistoricCandlesExtended(t *testing.T) {
currencyPair, err := currency.NewPairFromString("btc_usdt")
currencyPair, err := currency.NewPairFromString(testCurrency)
if err != nil {
t.Fatal(err)
}
start := time.Now().AddDate(0, -2, 0)
end := time.Now()
_, err = z.GetHistoricCandlesExtended(currencyPair, asset.Spot, start, end, kline.OneHour)
startTime := time.Now().Add(-time.Hour * 1)
endTime := time.Now()
if mockTests {
startTime = time.Date(2020, 9, 1, 0, 0, 0, 0, time.UTC)
endTime = time.Date(2020, 9, 2, 0, 0, 0, 0, time.UTC)
}
_, err = z.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, endTime, kline.OneDay)
if err != nil {
t.Fatal(err)
}
@@ -912,3 +939,40 @@ func Test_FormatExchangeKlineInterval(t *testing.T) {
})
}
}
func TestValidateCandlesRequest(t *testing.T) {
_, err := z.validateCandlesRequest(currency.Pair{}, "", time.Time{}, time.Time{}, kline.Interval(-1))
if err != nil && err.Error() != "invalid time range supplied. Start: 0001-01-01 00:00:00 +0000 UTC End 0001-01-01 00:00:00 +0000 UTC" {
t.Error(err)
}
_, err = z.validateCandlesRequest(currency.Pair{}, "", time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC), time.Time{}, kline.Interval(-1))
if err != nil && err.Error() != "invalid time range supplied. Start: 2020-01-01 01:01:01.000000001 +0000 UTC End 0001-01-01 00:00:00 +0000 UTC" {
t.Error(err)
}
_, err = z.validateCandlesRequest(currency.Pair{}, asset.Spot, time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC), time.Date(2020, 1, 1, 1, 1, 1, 3, time.UTC), kline.OneHour)
if err != nil && err.Error() != "pair not enabled" {
t.Error(err)
}
var p currency.Pair
p, err = currency.NewPairFromString(testCurrency)
if err != nil {
t.Fatal(err)
}
var item kline.Item
item, err = z.validateCandlesRequest(p, asset.Spot, time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC), time.Date(2020, 1, 1, 1, 1, 1, 3, time.UTC), kline.OneHour)
if err != nil {
t.Error(err)
}
if !item.Pair.Equal(p) {
t.Errorf("unexpected result, expected %v, received %v", p, item.Pair)
}
if item.Asset != asset.Spot {
t.Errorf("unexpected result, expected %v, received %v", asset.Spot, item.Asset)
}
if item.Interval != kline.OneHour {
t.Errorf("unexpected result, expected %v, received %v", kline.OneHour, item.Interval)
}
if item.Exchange != z.Name {
t.Errorf("unexpected result, expected %v, received %v", z.Name, item.Exchange)
}
}

View File

@@ -115,7 +115,7 @@ type KlinesRequestParams struct {
Symbol string // 交易对, zb_qc,zb_usdt,zb_btc...
Type string // K线类型, 1min, 3min, 15min, 30min, 1hour......
Since int64 // 从这个时间戳之后的
Size int // 返回数据的条数限制(默认为1000如果返回数据多于1000条那么只返回1000条)
Size int64 // 返回数据的条数限制(默认为1000如果返回数据多于1000条那么只返回1000条)
}
// KLineResponseData Kline Data

View File

@@ -9,6 +9,7 @@ import (
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/convert"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
@@ -590,7 +591,7 @@ func (z *ZB) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw.Requ
// GetFeeByType returns an estimate of fee based on type of transaction
func (z *ZB) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) {
if !z.AllowAuthenticatedRequest() && // Todo check connection status
if (!z.AllowAuthenticatedRequest() || z.SkipAuthCheck) && // Todo check connection status
feeBuilder.FeeType == exchange.CryptocurrencyTradeFee {
feeBuilder.FeeType = exchange.OfflineTradeFee
}
@@ -770,33 +771,29 @@ func (z *ZB) FormatExchangeKlineInterval(in kline.Interval) string {
}
// GetHistoricCandles returns candles between a time period for a set time interval
func (z *ZB) GetHistoricCandles(pair currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) {
if err := z.ValidateKline(pair, a, interval); err != nil {
func (z *ZB) GetHistoricCandles(p currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) {
ret, err := z.validateCandlesRequest(p, a, start, end, interval)
if err != nil {
return kline.Item{}, err
}
formattedPair, err := z.FormatExchangeCurrency(pair, a)
p, err = z.FormatExchangeCurrency(p, a)
if err != nil {
return kline.Item{}, err
}
klineParams := KlinesRequestParams{
Type: z.FormatExchangeKlineInterval(interval),
Symbol: formattedPair.String(),
Symbol: p.String(),
Since: convert.UnixMillis(start),
Size: int64(z.Features.Enabled.Kline.ResultLimit),
}
candles, err := z.GetSpotKline(klineParams)
var candles KLineResponse
candles, err = z.GetSpotKline(klineParams)
if err != nil {
return kline.Item{}, err
}
ret := kline.Item{
Exchange: z.Name,
Pair: pair,
Asset: a,
Interval: interval,
}
for x := range candles.Data {
if candles.Data[x].KlineTime.Before(start) || candles.Data[x].KlineTime.After(end) {
continue
@@ -804,7 +801,7 @@ func (z *ZB) GetHistoricCandles(pair currency.Pair, a asset.Item, start, end tim
ret.Candles = append(ret.Candles, kline.Candle{
Time: candles.Data[x].KlineTime,
Open: candles.Data[x].Open,
High: candles.Data[x].Close,
High: candles.Data[x].High,
Low: candles.Data[x].Low,
Close: candles.Data[x].Close,
Volume: candles.Data[x].Volume,
@@ -817,5 +814,77 @@ func (z *ZB) GetHistoricCandles(pair currency.Pair, a asset.Item, start, end tim
// GetHistoricCandlesExtended returns candles between a time period for a set time interval
func (z *ZB) GetHistoricCandlesExtended(p currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) {
return z.GetHistoricCandles(p, a, start, end, interval)
ret, err := z.validateCandlesRequest(p, a, start, end, interval)
if err != nil {
return kline.Item{}, err
}
p, err = z.FormatExchangeCurrency(p, a)
if err != nil {
return kline.Item{}, err
}
startTime := start
allKlines:
for {
klineParams := KlinesRequestParams{
Type: z.FormatExchangeKlineInterval(interval),
Symbol: p.String(),
Since: convert.UnixMillis(startTime),
Size: int64(z.Features.Enabled.Kline.ResultLimit),
}
candles, err := z.GetSpotKline(klineParams)
if err != nil {
return kline.Item{}, err
}
for x := range candles.Data {
if candles.Data[x].KlineTime.Before(start) || candles.Data[x].KlineTime.After(end) {
continue
}
if startTime.Equal(candles.Data[x].KlineTime) {
// no new data has been sent
break allKlines
}
ret.Candles = append(ret.Candles, kline.Candle{
Time: candles.Data[x].KlineTime,
Open: candles.Data[x].Open,
High: candles.Data[x].High,
Low: candles.Data[x].Low,
Close: candles.Data[x].Close,
Volume: candles.Data[x].Volume,
})
if x == len(candles.Data)-1 {
startTime = candles.Data[x].KlineTime
}
}
if len(candles.Data) != int(z.Features.Enabled.Kline.ResultLimit) {
break allKlines
}
}
ret.SortCandlesByTimestamp(false)
return ret, nil
}
func (z *ZB) validateCandlesRequest(p currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) {
if start.Equal(end) ||
end.After(time.Now()) ||
end.Before(start) ||
(start.IsZero() && !end.IsZero()) {
return kline.Item{}, fmt.Errorf("invalid time range supplied. Start: %v End %v",
start,
end)
}
if err := z.ValidateKline(p, a, interval); err != nil {
return kline.Item{}, err
}
return kline.Item{
Exchange: z.Name,
Pair: p,
Asset: a,
Interval: interval,
}, nil
}